From 4ab24bc9d1a02811ac8599f1fcb35559b853161c Mon Sep 17 00:00:00 2001 From: "vladimir%pobox.com" Date: Fri, 16 Jun 2006 00:42:39 +0000 Subject: [PATCH] b=341694, bring Mac Cocoa+Cairo text layout/font selection/rendering to a useful baseline for further work, r=me --- gfx/cairo/cairo/src/cairo-quartz2-surface.c | 21 +- gfx/thebes/public/gfxAtsuiFonts.h | 20 +- gfx/thebes/src/Makefile.in | 4 +- gfx/thebes/src/gfxAtsuiFonts.cpp | 469 ++++++++++++-------- gfx/thebes/src/gfxQuartzFontCache.h | 67 +++ gfx/thebes/src/gfxQuartzFontCache.mm | 369 +++++++++++++++ 6 files changed, 739 insertions(+), 211 deletions(-) create mode 100644 gfx/thebes/src/gfxQuartzFontCache.h create mode 100644 gfx/thebes/src/gfxQuartzFontCache.mm diff --git a/gfx/cairo/cairo/src/cairo-quartz2-surface.c b/gfx/cairo/cairo/src/cairo-quartz2-surface.c index 5bf67f021049..b5959550b62b 100644 --- a/gfx/cairo/cairo/src/cairo-quartz2-surface.c +++ b/gfx/cairo/cairo/src/cairo-quartz2-surface.c @@ -80,7 +80,7 @@ typedef struct cairo_quartzgl_surface { AGLContext aglContext; CGContextRef cgContext; - cairo_rectangle_t extents; + cairo_rectangle_fixed_t extents; /* These are stored while drawing operations are in place, set up * by quartzgl_setup_source() and quartzgl_finish_source() @@ -438,7 +438,7 @@ SurfacePatternDrawFunc (void *info, CGContextRef context) { cairo_surface_pattern_t *spat = (cairo_surface_pattern_t *) info; cairo_surface_t *pat_surf = spat->surface; - cairo_rectangle_t extents; + cairo_rectangle_fixed_t extents; cairo_status_t status; cairo_quartzgl_surface_t *quartz_surf = NULL; @@ -455,7 +455,7 @@ SurfacePatternDrawFunc (void *info, CGContextRef context) cairo_surface_t *dummy = cairo_quartzgl_surface_create (CAIRO_FORMAT_ARGB32, 1, 1, TRUE); - cairo_rectangle_t rect; + cairo_rectangle_fixed_t rect; _cairo_surface_get_extents (pat_surf, &rect); cairo_surface_t *new_surf = NULL; @@ -506,7 +506,7 @@ _cairo_quartzgl_cairo_repeating_surface_pattern_to_quartz (cairo_quartzgl_surfac { cairo_surface_pattern_t *spat; cairo_surface_t *pat_surf; - cairo_rectangle_t extents; + cairo_rectangle_fixed_t extents; CGRect pbounds; CGAffineTransform ptransform, stransform; @@ -843,9 +843,9 @@ _cairo_quartzgl_surface_acquire_source_image (void *abstract_surface, static cairo_status_t _cairo_quartzgl_surface_acquire_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, + cairo_rectangle_fixed_t *interest_rect, cairo_image_surface_t **image_out, - cairo_rectangle_t *image_rect, + cairo_rectangle_fixed_t *image_rect, void **image_extra) { cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface; @@ -867,9 +867,9 @@ _cairo_quartzgl_surface_acquire_dest_image (void *abstract_surface, static void _cairo_quartzgl_surface_release_dest_image (void *abstract_surface, - cairo_rectangle_t *interest_rect, + cairo_rectangle_fixed_t *interest_rect, cairo_image_surface_t *image, - cairo_rectangle_t *image_rect, + cairo_rectangle_fixed_t *image_rect, void *image_extra) { cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface; @@ -1041,7 +1041,7 @@ _cairo_quartzgl_surface_clone_similar (void *abstract_surface, static cairo_int_status_t _cairo_quartzgl_surface_get_extents (void *abstract_surface, - cairo_rectangle_t *extents) + cairo_rectangle_fixed_t *extents) { cairo_quartzgl_surface_t *surface = (cairo_quartzgl_surface_t *) abstract_surface; @@ -1469,7 +1469,8 @@ _cairo_quartzgl_surface_create_internal (CGContextRef cgContext, memset(surface, 0, sizeof(cairo_quartzgl_surface_t)); - _cairo_surface_init(&surface->base, &cairo_quartzgl_surface_backend); + _cairo_surface_init(&surface->base, &cairo_quartzgl_surface_backend, + CAIRO_CONTENT_COLOR_ALPHA); /* Save our extents */ surface->extents.x = surface->extents.y = 0; diff --git a/gfx/thebes/public/gfxAtsuiFonts.h b/gfx/thebes/public/gfxAtsuiFonts.h index 6a38a38753a8..9c883c0b03b2 100644 --- a/gfx/thebes/public/gfxAtsuiFonts.h +++ b/gfx/thebes/public/gfxAtsuiFonts.h @@ -50,22 +50,26 @@ class gfxAtsuiFont : public gfxFont { public: gfxAtsuiFont(ATSUFontID fontID, - gfxAtsuiFontGroup *fontGroup); + const gfxFontStyle *fontStyle); virtual ~gfxAtsuiFont(); virtual const gfxFont::Metrics& GetMetrics(); + float GetCharWidth (PRUnichar c); + ATSUFontID GetATSUFontID() { return mATSUFontID; } cairo_font_face_t *CairoFontFace() { return mFontFace; } cairo_scaled_font_t *CairoScaledFont() { return mScaledFont; } -protected: - ATSUFontID mATSUFontID; + ATSUStyle GetATSUStyle() { return mATSUStyle; } - const gfxAtsuiFontGroup *mFontGroup; +protected: const gfxFontStyle *mFontStyle; + ATSUFontID mATSUFontID; + ATSUStyle mATSUStyle; + cairo_font_face_t *mFontFace; cairo_scaled_font_t *mScaledFont; @@ -83,7 +87,7 @@ public: return MakeTextRun(NS_ConvertASCIItoUTF16(aCString)); } - ATSUFontFallbacks *GetATSUFontFallbacks() { return &mFallbacks; } + ATSUFontFallbacks *GetATSUFontFallbacksPtr() { return &mFallbacks; } gfxAtsuiFont* GetFontAt(PRInt32 i) { return NS_STATIC_CAST(gfxAtsuiFont*, NS_STATIC_CAST(gfxFont*, mFonts[i])); @@ -91,7 +95,7 @@ public: protected: static PRBool FindATSUFont(const nsAString& aName, - const nsAString& aGenericName, + const nsACString& aGenericName, void *closure); ATSUFontFallbacks mFallbacks; @@ -113,9 +117,9 @@ private: nsString mString; gfxAtsuiFontGroup *mGroup; - ATSUStyle mATSUStyle; - ATSUTextLayout mATSULayout; + + nsTArray mStylesToDispose; }; #endif /* GFX_ATSUIFONTS_H */ diff --git a/gfx/thebes/src/Makefile.in b/gfx/thebes/src/Makefile.in index a279c19668fe..ab960485fba9 100644 --- a/gfx/thebes/src/Makefile.in +++ b/gfx/thebes/src/Makefile.in @@ -84,8 +84,10 @@ ifneq (,$(filter $(MOZ_GFX_TOOLKIT),mac cocoa)) CPPSRCS += gfxQuartzSurface.cpp gfxPlatformMac.cpp gfxAtsuiFonts.cpp #CPPSRCS += gfxPDFSurface.cpp +CMMSRCS = gfxQuartzFontCache.mm + # Always link with OpenGL/AGL -EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL +EXTRA_DSO_LDOPTS += -framework OpenGL -framework AGL -framework Cocoa endif ifdef MOZ_ENABLE_GLITZ diff --git a/gfx/thebes/src/gfxAtsuiFonts.cpp b/gfx/thebes/src/gfxAtsuiFonts.cpp index 0348c617749d..6540c37010e9 100644 --- a/gfx/thebes/src/gfxAtsuiFonts.cpp +++ b/gfx/thebes/src/gfxAtsuiFonts.cpp @@ -37,6 +37,8 @@ #include "prtypes.h" #include "prmem.h" +#include "nsString.h" + #include "gfxTypes.h" #include "nsPromiseFlatString.h" @@ -46,16 +48,71 @@ #include "cairo-atsui.h" +#include "gfxQuartzSurface.h" +#include "gfxQuartzFontCache.h" + +/* We might still need this for fast-pathing, but we'll see */ +#if 0 +OSStatus ATSUGetStyleGroup(ATSUStyle style, void **styleGroup); +OSStatus ATSUDisposeStyleGroup(void *styleGroup); +OSStatus ATSUConvertCharToGlyphs(void *styleGroup, + PRunichar *buffer + unsigned int bufferLength, + void *glyphVector); +OSStatus ATSInitializeGlyphVector(int size, void *glyphVectorPtr); +OSStatus ATSClearGlyphVector(void *glyphVectorPtr); +#endif + THEBES_IMPL_REFCOUNTING(gfxAtsuiFont) gfxAtsuiFont::gfxAtsuiFont(ATSUFontID fontID, - gfxAtsuiFontGroup *fontGroup) - : gfxFont(EmptyString(), fontGroup), - mATSUFontID(fontID), mFontGroup(fontGroup) + const gfxFontStyle *fontStyle) + : gfxFont(EmptyString(), fontStyle), + mFontStyle(fontStyle), mATSUFontID(fontID), mATSUStyle(nsnull) { ATSFontRef fontRef = FMGetATSFontRefFromFont(fontID); - mFontStyle = mFontGroup->GetStyle(); + /* Create the ATSUStyle */ + + ATSUAttributeTag styleTags[] = { + kATSUFontTag, + kATSUSizeTag, + kATSUFontMatrixTag, + kATSUKerningInhibitFactorTag + }; + + ByteCount styleArgSizes[] = { + sizeof(ATSUFontID), + sizeof(Fixed), + sizeof(CGAffineTransform), + sizeof(Fract) + }; + + //fprintf (stderr, "string: '%s', size: %f\n", NS_ConvertUTF16toUTF8(aString).get(), aGroup->GetStyle()->size); + + // fSize is in points (72dpi) + Fixed fSize = FloatToFixed(mStyle->size); + ATSUFontID fid = fontID; + // make the font render right-side up + CGAffineTransform transform = CGAffineTransformMakeScale(1, -1); + // we can't do kerning until layout draws what it measures, instead of splitting things up + Fract inhibitKerningFactor = FloatToFract(1.0); + + ATSUAttributeValuePtr styleArgs[] = { + &fid, + &fSize, + &transform, + &inhibitKerningFactor + }; + + ATSUCreateStyle(&mATSUStyle); + ATSUSetAttributes(mATSUStyle, + sizeof(styleTags)/sizeof(ATSUAttributeTag), + styleTags, + styleArgSizes, + styleArgs); + + /* Now pull out the metrics */ ATSFontMetrics atsMetrics; ATSFontGetHorizontalMetrics(fontRef, kATSOptionFlagsDefault, @@ -82,7 +139,7 @@ gfxAtsuiFont::gfxAtsuiFont(ATSUFontID fontID, if (atsMetrics.avgAdvanceWidth != 0.0) mMetrics.aveCharWidth = atsMetrics.avgAdvanceWidth * size; else - mMetrics.aveCharWidth = mMetrics.maxAdvance; + mMetrics.aveCharWidth = GetCharWidth('a'); mMetrics.underlineOffset = atsMetrics.underlinePosition * size; mMetrics.underlineSize = atsMetrics.underlineThickness * size; @@ -93,7 +150,7 @@ gfxAtsuiFont::gfxAtsuiFont(ATSUFontID fontID, mMetrics.strikeoutOffset = mMetrics.xHeight / 2.0; mMetrics.strikeoutSize = mMetrics.underlineSize; - mMetrics.spaceWidth = mMetrics.aveCharWidth; + mMetrics.spaceWidth = GetCharWidth(' '); #if 0 fprintf (stderr, "Font: %p size: %f", this, size); @@ -115,10 +172,37 @@ gfxAtsuiFont::gfxAtsuiFont(ATSUFontID fontID, cairo_font_options_destroy(fontOptions); } +float +gfxAtsuiFont::GetCharWidth(PRUnichar c) +{ + // this sucks. There is a faster way to go from a char -> glyphs, but it + // requires using oodles of apple private interfaces. If we start caching + // gfxAtsuiFonts, then it might make sense to do that. + ATSUTextLayout layout; + + UniCharCount one = 1; + ATSUCreateTextLayoutWithTextPtr(&c, 0, 1, 1, 1, &one, &mATSUStyle, &layout); + + ATSTrapezoid trap; + ItemCount numBounds; + ATSUGetGlyphBounds(layout, FloatToFixed(0.0), FloatToFixed(0.0), + 0, 1, kATSUseFractionalOrigins, 1, &trap, &numBounds); + + float f = + FixedToFloat(PR_MAX(trap.upperRight.x, trap.lowerRight.x)) - + FixedToFloat(PR_MIN(trap.upperLeft.x, trap.lowerLeft.x)); + + ATSUDisposeTextLayout(layout); + + return f; +} + gfxAtsuiFont::~gfxAtsuiFont() { cairo_scaled_font_destroy(mScaledFont); cairo_font_face_destroy(mFontFace); + + ATSUDisposeStyle(mATSUStyle); } const gfxFont::Metrics& @@ -133,6 +217,21 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(const nsAString& families, { ForEachFont(FindATSUFont, this); + if (mFonts.Length() == 0) { + // XXX this will generate a list of the lang groups for which we have no + // default fonts for on the mac; we should fix this! + // Known: + // ja x-beng x-devanagari x-tamil x-geor x-ethi x-gujr x-mlym x-armn + + //fprintf (stderr, "gfxAtsuiFontGroup: %s [%s] -> %d fonts found\n", NS_ConvertUTF16toUTF8(families).get(), aStyle->langGroup.get(), mFonts.Length()); + + // If we get here, we most likely didn't have a default font for + // a specific langGroup. Let's just pick the default OSX + // user font. + ATSUFontID fontID = gfxQuartzFontCache::SharedFontCache()->GetDefaultATSUFontID (aStyle); + mFonts.AppendElement(new gfxAtsuiFont(fontID, aStyle)); + } + // Create the fallback structure ATSUCreateFontFallbacks(&mFallbacks); @@ -145,7 +244,7 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(const nsAString& families, fids = static_fids; for (unsigned int i = 0; i < mFonts.Length(); i++) { - nsRefPtr atsuiFont = NS_STATIC_CAST(gfxAtsuiFont*, NS_STATIC_CAST(gfxFont*, mFonts[i])); + gfxAtsuiFont* atsuiFont = NS_STATIC_CAST(gfxAtsuiFont*, NS_STATIC_CAST(gfxFont*, mFonts[i])); fids[i] = atsuiFont->GetATSUFontID(); } ATSUSetObjFontFallbacks(mFallbacks, @@ -157,98 +256,22 @@ gfxAtsuiFontGroup::gfxAtsuiFontGroup(const nsAString& families, PR_Free(fids); } -/* - * This function will /really/ want to just keep a hash lookup table - * based on name and style attributes that we compute; doing stuff with - * Pascal strings and whatnot is /so/ 1980's. - */ - -/* -06:19 < AngryLuke> vlad, which method in NSFontManager does what you want? -06:21 < vlad_> ideally fontWithFamily:traits:weight:size:, but I really need to - just iterate through all the fonts in a particular family -06:21 < vlad_> and examine their traits -06:22 < AngryLuke> vlad, NSFontManager is using the private FontObject APIs for - this it seems -06:22 < vlad_> AngryLuke: fun -06:22 < vlad_> AngryLuke: so what's the "right" way of doing this? -06:23 < AngryLuke> vlad, crying ;) I ended up just using the FontManager, - despite the fact it is deprecated. But that won't work for - Unicode fonts with no FOND -06:23 < AngryLuke> ATS has no functions for this -06:23 < AngryLuke> and NSFontManager is the only other way that is public. -*/ - PRBool gfxAtsuiFontGroup::FindATSUFont(const nsAString& aName, - const nsAString& aGenericName, + const nsACString& aGenericName, void *closure) { - OSStatus status; gfxAtsuiFontGroup *fontGroup = (gfxAtsuiFontGroup*) closure; const gfxFontStyle *fontStyle = fontGroup->GetStyle(); - PRInt16 baseWeight, offsetWeight; - fontStyle->ComputeWeightAndOffset(&baseWeight, &offsetWeight); - baseWeight += offsetWeight; - baseWeight = PR_MIN(9, PR_MAX(0, baseWeight)); + gfxQuartzFontCache *fc = gfxQuartzFontCache::SharedFontCache(); + ATSUFontID fontID = fc->FindATSUFontIDForFamilyAndStyle (aName, fontStyle); - Boolean isBold = (baseWeight >= 7) ? TRUE : FALSE; - Boolean isItalic = ((fontStyle->style & FONT_STYLE_ITALIC) != 0) ? TRUE : FALSE; - -#if 0 - - Str255 pascalName; - CopyCStringToPascalString(nsPromiseFlatCString(NS_ConvertUTF16toUTF8(aName)).get(), - pascalName); - - FMFontFamily fmFamily = FMGetFontFamilyFromName(pascalName); - if (fmFamily == kInvalidFontFamily) - return PR_TRUE; - - FMFontFamilyInstanceIterator famFontIterator; - status = FMCreateFontFamilyInstanceIterator(fmFamily, &famFontIterator); - if (status != noErr) { - fprintf(stderr, "FMCreateFontFamilyInstanceIterator returned error %d\n", (int) status); - return PR_TRUE; + if (fontID != kATSUInvalidFontID) { + /*printf ("FindATSUFont! %s %d -> %d\n", NS_ConvertUTF16toUTF8(aName).get(), fontStyle->weight, (int)fontID);*/ + fontGroup->mFonts.AppendElement(new gfxAtsuiFont(fontID, fontStyle)); } - FMFont famFont; - FMFontStyle famFontStyle; - FMFontSize famFontSize; - - while ((status = FMGetNextFontFamilyInstance(famFontIterator, - &famFont, - &famFontStyle, - &famFontSize)) == noErr) - { - - } - - if (status != kFMIterationCompleted) { - fprintf (stderr, "FMGetNextFontFamilyInstance returned error %d\n", (int) status); - return PR_TRUE; - } - - FMDisposeFontFamilyInstanceIterator(&familyFontIterator); -#else - ATSUFontID fontID; - - status = ATSUFindFontFromName(NS_ConvertUTF16toUTF8(aName).get(), aName.Length(), - /* nsPromiseFlatString(aName).get(), - aName.Length() * 2,*/ - kFontFamilyName, - kFontNoPlatformCode /* kFontUnicodePlatform */, - kFontNoScriptCode, - kFontNoLanguageCode, - &fontID); - - //fprintf (stderr, "FindATSUFont: %s -> %d (status: %d)\n", NS_ConvertUTF16toUTF8(aName).get(), (int) fontID, (int) status); - - if (fontID != kATSUInvalidFontID) - fontGroup->mFonts.AppendElement(new gfxAtsuiFont(fontID, fontGroup)); -#endif - return PR_TRUE; } @@ -273,145 +296,194 @@ gfxAtsuiTextRun::gfxAtsuiTextRun(const nsAString& aString, gfxAtsuiFontGroup *aG : mString(aString), mGroup(aGroup) { OSStatus status; - const gfxFontStyle *fontStyle = mGroup->GetStyle(); - - PRInt16 baseWeight, offsetWeight; - fontStyle->ComputeWeightAndOffset(&baseWeight, &offsetWeight); - baseWeight += offsetWeight; - if (baseWeight < 0) baseWeight = 0; - else if (baseWeight > 9) baseWeight = 9; - - // we can't do kerning until layout draws what it measures, instead of splitting things up - ATSUAttributeTag styleTags[] = { - kATSUFontTag, - kATSUSizeTag, - kATSUKerningInhibitFactorTag, - kATSUQDBoldfaceTag, - kATSUQDItalicTag, - }; - - ByteCount styleArgSizes[] = { - sizeof(ATSUFontID), - sizeof(Fixed), - sizeof(Fract), - sizeof(Boolean), - sizeof(Boolean) - }; - - //fprintf (stderr, "string: '%s', size: %f\n", NS_ConvertUTF16toUTF8(aString).get(), aGroup->GetStyle()->size); - - // fSize is in points (72dpi) - Fixed fSize = FloatToFixed(aGroup->GetStyle()->size); - nsRefPtr atsuiFont = mGroup->GetFontAt(0); - ATSUFontID fid = atsuiFont->GetATSUFontID(); - Fract inhibitKerningFactor = FloatToFract(1.0); - /* Why am I even setting these? the fid font will end up being used no matter what; - * need smarter font selection earlier on... - */ - Boolean isBold = (baseWeight >= 7) ? TRUE : FALSE; - Boolean isItalic = ((fontStyle->style & FONT_STYLE_ITALIC) != 0) ? TRUE : FALSE; - - - //fprintf (stderr, " bold: %d italic: %d\n", isBold, isItalic); - - ATSUAttributeValuePtr styleArgs[] = { - &fid, - &fSize, - &inhibitKerningFactor, - &isBold, - &isItalic - }; - - status = ATSUCreateStyle(&mATSUStyle); - status = ATSUSetAttributes(mATSUStyle, - sizeof(styleTags)/sizeof(ATSUAttributeTag), - styleTags, - styleArgSizes, - styleArgs); - if (status != noErr) - fprintf (stderr, "ATUSetAttributes gave error: %d\n", (int) status); - - UniCharCount runLengths = kATSUToTextEnd; + gfxAtsuiFont *atsuiFont = mGroup->GetFontAt(0); + ATSUStyle mainStyle = atsuiFont->GetATSUStyle(); + UniCharCount runLengths = mString.Length(); status = ATSUCreateTextLayoutWithTextPtr (nsPromiseFlatString(mString).get(), - kATSUFromTextBeginning, - kATSUToTextEnd, + 0, + mString.Length(), mString.Length(), 1, &runLengths, - &mATSUStyle, + &mainStyle, &mATSULayout); + // Set up line layout + ATSLineLayoutOptions lineLayoutOptions = kATSLineKeepSpacesOutOfMargin | kATSLineHasNoHangers; + ATSUAttributeTag layoutTags[] = { kATSULineLayoutOptionsTag }; + ByteCount layoutArgSizes[] = { sizeof(ATSLineLayoutOptions) }; + ATSUAttributeValuePtr layoutArgs[] = { &lineLayoutOptions }; + ATSUSetLayoutControls(mATSULayout, + sizeof(layoutTags) / sizeof(ATSUAttributeTag), + layoutTags, + layoutArgSizes, + layoutArgs); + // Set up our font fallbacks ATSUAttributeTag lineTags[] = { kATSULineFontFallbacksTag }; ByteCount lineArgSizes[] = { sizeof(ATSUFontFallbacks) }; - ATSUAttributeValuePtr lineArgs[] = { mGroup->GetATSUFontFallbacks() }; + ATSUAttributeValuePtr lineArgs[] = { mGroup->GetATSUFontFallbacksPtr() }; status = ATSUSetLineControls(mATSULayout, 0, sizeof(lineTags) / sizeof(ATSUAttributeTag), lineTags, lineArgSizes, lineArgs); - if (status != noErr) + if (status != noErr) { fprintf(stderr, "ATSUSetLineControls gave error: %d\n", (int) status); + } - ATSUSetTransientFontMatching(mATSULayout, true); + /* Now go through and update the styles for the text, based on font matching. */ + + UniCharArrayOffset runStart = 0; + UniCharCount runLength = mString.Length(); + while (runStart < runLength) { + ATSUFontID substituteFontID; + UniCharArrayOffset changedOffset; + UniCharCount changedLength; + + OSStatus status = ATSUMatchFontsToText (mATSULayout, runStart, kATSUToTextEnd, + &substituteFontID, &changedOffset, &changedLength); + if (status == noErr) { + // everything's good, finish up + break; + } else if (status == kATSUFontsMatched) { + ATSUStyle subStyle; + ATSUCreateStyle (&subStyle); + ATSUCopyAttributes (mainStyle, subStyle); + + ATSUAttributeTag fontTags[] = { kATSUFontTag }; + ByteCount fontArgSizes[] = { sizeof(ATSUFontID) }; + ATSUAttributeValuePtr fontArgs[] = { &substituteFontID }; + + ATSUSetAttributes (subStyle, 1, fontTags, fontArgSizes, fontArgs); + + ATSUSetRunStyle (mATSULayout, subStyle, changedOffset, changedLength); + + mStylesToDispose.AppendElement(subStyle); + + } else if (status == kATSUFontsNotMatched) { + /* I need to select the last resort font; how the heck do I do that? */ + } + + runStart = changedOffset+changedLength; + } } gfxAtsuiTextRun::~gfxAtsuiTextRun() { ATSUDisposeTextLayout(mATSULayout); - ATSUDisposeStyle(mATSUStyle); + + for (PRUint32 i = 0; i < mStylesToDispose.Length(); i++) { + ATSUStyle s = mStylesToDispose[i]; + ATSUDisposeStyle(s); + } } +struct CairoGlyphBuffer { + CairoGlyphBuffer() { + size = 128; + glyphs = staticGlyphBuf; + } + + ~CairoGlyphBuffer() { + if (glyphs != staticGlyphBuf) + PR_Free(glyphs); + } + + void EnsureSize(PRUint32 numGlyphs) { + if (size < numGlyphs) { + if (glyphs != staticGlyphBuf) + PR_Free (staticGlyphBuf); + glyphs = (cairo_glyph_t*) PR_Malloc(sizeof(cairo_glyph_t) * numGlyphs); + size = numGlyphs; + } + } + + PRUint32 size; + cairo_glyph_t *glyphs; +private: + cairo_glyph_t staticGlyphBuf[128]; +}; + void gfxAtsuiTextRun::Draw(gfxContext *aContext, gfxPoint pt) { - cairo_t *cr = aContext->GetCairo(); - nsRefPtr atsuiFont = mGroup->GetFontAt(0); - cairo_set_font_face(cr, atsuiFont->CairoFontFace()); - cairo_set_font_size(cr, mGroup->GetStyle()->size); + gfxFloat offsetX, offsetY; + nsRefPtr surf = aContext->CurrentSurface (&offsetX, &offsetY); - ItemCount cnt; - ATSLayoutRecord *layoutRecords = nsnull; - OSStatus status = ATSUDirectGetLayoutDataArrayPtrFromTextLayout - (mATSULayout, - kATSUFromTextBeginning, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void**)&layoutRecords, - &cnt); + cairo_t *cr = aContext->GetCairo(); + double fontSize = mGroup->GetStyle()->size; + + cairo_save (cr); + cairo_translate (cr, pt.x, pt.y); + + OSStatus status; + + ByteCount bufferSize = 4096; + ATSUGlyphInfoArray *glyphInfo = (ATSUGlyphInfoArray*) PR_Malloc (bufferSize); + status = ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, glyphInfo); + if (status == buffersTooSmall) { + ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, NULL); + PR_Free(glyphInfo); + glyphInfo = (ATSUGlyphInfoArray*) PR_Malloc (bufferSize); + + status = ATSUGetGlyphInfo (mATSULayout, kATSUFromTextBeginning, kATSUToTextEnd, &bufferSize, glyphInfo); + } if (status != noErr) { - fprintf(stderr, "ATSUDirectGetLayoutDataArrayPtrFromTextLayout failed, %d\n", noErr); + fprintf (stderr, "ATSUGetGlyphInfo returned error %d\n", (int) status); + PR_Free (glyphInfo); return; } - cairo_glyph_t *cglyphs = (cairo_glyph_t *) PR_Malloc (cnt * sizeof(cairo_glyph_t)); - if (!cglyphs) - return; + CairoGlyphBuffer cairoGlyphs; - //fprintf (stderr, "String: '%s'\n", NS_ConvertUTF16toUTF8(mString).get()); + ItemCount i = 0; + while (i < glyphInfo->numGlyphs) { + ATSUStyle runStyle = glyphInfo->glyphs[i].style; - PRUint32 cgindex = 0; - for (PRUint32 i = 0; i < cnt; i++) { - //fprintf(stderr, "[%d 0x%04x %f] ", i, layoutRecords[i].glyphID, FixedToFloat(layoutRecords[i].realPos)); - if (!(layoutRecords[i].flags & kATSGlyphInfoIsWhiteSpace)) { - cglyphs[cgindex].index = layoutRecords[i].glyphID; - cglyphs[cgindex].x = pt.x + FixedToFloat(layoutRecords[i].realPos); - cglyphs[cgindex].y = pt.y; - cgindex++; + ItemCount lastRunGlyph; + for (lastRunGlyph = i+1; lastRunGlyph < glyphInfo->numGlyphs; lastRunGlyph++) { + if (glyphInfo->glyphs[lastRunGlyph].style != runStyle) + break; } + + ItemCount numGlyphs = lastRunGlyph-i; + ATSUFontID runFontID; + ByteCount unused; + status = ATSUGetAttribute (runStyle, kATSUFontTag, sizeof(ATSUFontID), &runFontID, &unused); + if (status != noErr || runFontID == kATSUInvalidFontID) { + i += numGlyphs; + continue; + } + + cairoGlyphs.EnsureSize(numGlyphs); + cairo_glyph_t *runGlyphs = cairoGlyphs.glyphs; + + for (ItemCount k = i; k < lastRunGlyph; k++) { + runGlyphs->index = glyphInfo->glyphs[k].glyphID; + runGlyphs->x = glyphInfo->glyphs[k].idealX; /* screenX */ + runGlyphs->y = glyphInfo->glyphs[k].deltaY; + + runGlyphs++; + } + + cairo_font_face_t *runFace = cairo_atsui_font_face_create_for_atsu_font_id (runFontID); + cairo_set_font_face (cr, runFace); + cairo_set_font_size (cr, fontSize); + + cairo_show_glyphs (cr, cairoGlyphs.glyphs, numGlyphs); + cairo_font_face_destroy (runFace); + + i += numGlyphs; } - //fprintf (stderr, "\n"); - ATSUDirectReleaseLayoutDataArrayPtr(NULL, - kATSUDirectDataLayoutRecordATSLayoutRecordCurrent, - (void**)&layoutRecords); - - cairo_show_glyphs(cr, cglyphs, cgindex); - - PR_Free(cglyphs); + PR_Free (glyphInfo); + + cairo_restore (cr); } gfxFloat @@ -430,19 +502,32 @@ gfxAtsuiTextRun::Measure(gfxContext *aContext) 1, &trap, &numBounds); - if (status != noErr) + if (status != noErr) { fprintf(stderr, "ATSUGetGlyphBounds returned error %d!\n", (int) status); + return 0.0; + } - float f = FixedToFloat(PR_MAX(trap.upperRight.x, trap.lowerRight.x)) - FixedToFloat(PR_MIN(trap.upperLeft.x, trap.lowerLeft.x)); +#if 0 + printf ("Measure: '%s' trap: %f %f %f %f\n", + NS_ConvertUTF16toUTF8(mString).get(), + FixedToFloat(trap.upperLeft.x), FixedToFloat(trap.lowerLeft.x), + FixedToFloat(trap.upperRight.x), FixedToFloat(trap.lowerRight.x)); +#endif + + float f = + FixedToFloat(PR_MAX(trap.upperRight.x, trap.lowerRight.x)) - + FixedToFloat(PR_MIN(trap.upperLeft.x, trap.lowerLeft.x)); - //fprintf (stderr, "measured: %f\n", f); return f; } void gfxAtsuiTextRun::SetSpacing(const nsTArray &spacingArray) { - // XXX implement me! + // We can implement this, but there's really a mismatch between + // the spacings given here and cluster spacings. So we're + // going to do nothing until we figure out what the layout API + // will look like for this. } const nsTArray *const diff --git a/gfx/thebes/src/gfxQuartzFontCache.h b/gfx/thebes/src/gfxQuartzFontCache.h new file mode 100644 index 000000000000..063840a99a72 --- /dev/null +++ b/gfx/thebes/src/gfxQuartzFontCache.h @@ -0,0 +1,67 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is Mozilla Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Corporation. + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#ifndef GFXQUARTZFONTCACHE_H_ +#define GFXQUARTZFONTCACHE_H_ + +#include "gfxAtsuiFonts.h" + +// XXX this is called a Cache because optimistically it will +// cache [family, weight, traits, language] -> fontid so that +// it doesn't have to do the lookup each time. It doesn't +// do this yet. + +class gfxQuartzFontCache { +public: + static gfxQuartzFontCache* SharedFontCache() { + if (!sSharedFontCache) + sSharedFontCache = new gfxQuartzFontCache(); + return sSharedFontCache; + } + + ATSUFontID FindATSUFontIDForFamilyAndStyle (const nsAString& aFamily, + const gfxFontStyle* aStyle); + + ATSUFontID GetDefaultATSUFontID (const gfxFontStyle* aStyle); + +private: + static gfxQuartzFontCache *sSharedFontCache; + + gfxQuartzFontCache(); +}; + +#endif /* MACFONTCACHE_H_ */ diff --git a/gfx/thebes/src/gfxQuartzFontCache.mm b/gfx/thebes/src/gfxQuartzFontCache.mm new file mode 100644 index 000000000000..4d01f284bfe3 --- /dev/null +++ b/gfx/thebes/src/gfxQuartzFontCache.mm @@ -0,0 +1,369 @@ +/* -*- Mode: ObjC; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * ***** BEGIN LICENSE BLOCK ***** + * Version: BSD + * + * Copyright (C) 2006 Mozilla Corporation. All rights reserved. + * + * Contributor(s): + * Vladimir Vukicevic + * + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * ***** END LICENSE BLOCK ***** */ + +#include + +#import + +#include "gfxPlatformMac.h" +#include "gfxQuartzFontCache.h" + +// _atsFontID is private; add it in our new category to NSFont +@interface NSFont (MozillaCategory) +- (ATSUFontID)_atsFontID; +@end + +gfxQuartzFontCache *gfxQuartzFontCache::sSharedFontCache = nsnull; + +gfxQuartzFontCache::gfxQuartzFontCache() { +} + +#define SYNTHESIZED_FONT_TRAITS (NSBoldFontMask | NSItalicFontMask) + + +#define IMPORTANT_FONT_TRAITS (0 \ + | NSBoldFontMask \ + | NSCompressedFontMask \ + | NSCondensedFontMask \ + | NSExpandedFontMask \ + | NSItalicFontMask \ + | NSNarrowFontMask \ + | NSPosterFontMask \ + | NSSmallCapsFontMask \ +) + +#define DESIRED_WEIGHT 5 + +#define APPLE_BOLD_WEIGHT 9 + +#define CSS_NORMAL_WEIGHT_BASE 4 +#define CSS_BOLD_WEIGHT_BASE 7 + +#define INDEX_FONT_POSTSCRIPT_NAME 0 +#define INDEX_FONT_EXTRA_NAME 1 +#define INDEX_FONT_WEIGHT 2 +#define INDEX_FONT_TRAITS 3 + +// see docs for NSFontManager-availableMembersOfFontFamily: +static const int CSSWeightToAppleWeight[] = { + // 0 invalid + 0, + 2, + 3, + 4, + 5, + 6, + 8, + 9, + 10, + 12 +}; + +// from the given CSS Weight, how many steps do we need to take in Apple Weights +// to get to the next CSS weight +static const int AppleWeightStepsToNextCSSWeight[] = { + // 0 is invalid + 0, + 1, + 1, + 1, + 1, + 2, + 1, + 1, + 2, + 2 +}; + +/* + * Return PR_TRUE if the candidate font is acceptable for + * the given desired traits. + */ +static PRBool +acceptableChoice (NSFontTraitMask desiredTraits, int desiredWeight, + NSFontTraitMask candidateTraits, int candidateWeight) +{ + // remove the traits we can synthesize, and compare the others + desiredTraits &= ~SYNTHESIZED_FONT_TRAITS; + return (candidateTraits & desiredTraits) == desiredTraits; +} + +/* + * Return PR_TRUE if the candidate font is better than the chosen font, + * for the given desired traits + */ +static PRBool +betterChoice(NSFontTraitMask desiredTraits, int desiredWeight, + NSFontTraitMask chosenTraits, int chosenWeight, + NSFontTraitMask candidateTraits, int candidateWeight) +{ + if (!acceptableChoice(desiredTraits, desiredWeight, candidateTraits, candidateWeight)) + return PR_FALSE; + + // A list of the traits we care about. + // The top item in the list is the worst trait to mismatch; if a font has this + // and we didn't ask for it, we'd prefer any other font in the family. + const NSFontTraitMask masks[] = { + NSPosterFontMask, + NSSmallCapsFontMask, + NSItalicFontMask, + NSCompressedFontMask, + NSCondensedFontMask, + NSExpandedFontMask, + NSNarrowFontMask, + NSBoldFontMask, + 0 }; + + int i = 0; + NSFontTraitMask mask; + while ((mask = masks[i++])) { + BOOL desired = (desiredTraits & mask) != 0; + BOOL chosenHasUnwantedTrait = (desired != ((chosenTraits & mask) != 0)); + BOOL candidateHasUnwantedTrait = (desired != ((candidateTraits & mask) != 0)); + if (!candidateHasUnwantedTrait && chosenHasUnwantedTrait) + return PR_TRUE; + if (!chosenHasUnwantedTrait && candidateHasUnwantedTrait) + return PR_FALSE; + } + + int chosenWeightDelta = chosenWeight - desiredWeight; + int candidateWeightDelta = candidateWeight - desiredWeight; + + int chosenWeightDeltaMagnitude = ABS(chosenWeightDelta); + int candidateWeightDeltaMagnitude = ABS(candidateWeightDelta); + + // Smaller magnitude wins. + // If both have same magnitude, tie breaker is that the smaller weight wins. + // Otherwise, first font in the array wins (should almost never happen). + if (candidateWeightDeltaMagnitude < chosenWeightDeltaMagnitude) + return PR_TRUE; + + if (candidateWeightDeltaMagnitude == chosenWeightDeltaMagnitude && candidateWeight < chosenWeight) + return PR_TRUE; + + return PR_FALSE; +} + +NSFont* +FindFontWeight (NSFontManager *fontManager, NSFont *font, int desiredWeight, int weightSteps, float size) +{ + NSFont *newFont; + + int currentWeight = [fontManager weightOfFont:font]; + if (currentWeight != desiredWeight) { + //fprintf (stderr, "desiredWeight: %d currentWeight: %d\n", desiredWeight, currentWeight); + newFont = [fontManager fontWithFamily:[font familyName] traits:[fontManager traitsOfFont:font] weight:desiredWeight size:size]; + if (newFont) { + font = newFont; + currentWeight = [fontManager weightOfFont:font]; + //fprintf (stderr, "picked new font, weight: %d\n", currentWeight); + } + } + + if (weightSteps == 0) { + return font; + } else if (weightSteps < 0) { + while (weightSteps != 0) { + newFont = [fontManager convertWeight:NO ofFont:font]; + if (newFont == font) + return font; + font = newFont; + weightSteps++; + } + } else if (weightSteps > 0) { + while (weightSteps != 0) { + newFont = [fontManager convertWeight:YES ofFont:font]; + if (newFont == font) + return font; + font = newFont; + weightSteps--; + } + } + + return font; +} + +ATSUFontID +gfxQuartzFontCache::FindATSUFontIDForFamilyAndStyle (const nsAString& aFamily, + const gfxFontStyle *aStyle) +{ + NSString *desiredFamily = [NSString stringWithCharacters:aFamily.BeginReading() length:aFamily.Length()]; + NSFontTraitMask desiredTraits = 0; + int desiredWeight; + PRInt16 baseCSSWeight, weightOffset; + + //printf ("FindATSUFontIDForFamilyAndStyle: %s\n", [desiredFamily cString]); + + aStyle->ComputeWeightAndOffset(&baseCSSWeight, &weightOffset); + desiredWeight = CSSWeightToAppleWeight[PR_MIN(PR_MAX(baseCSSWeight, 1), 9)]; + + // Oblique should really be synthesized italic; fix that later. + if (aStyle->style & FONT_STYLE_ITALIC || aStyle->style & FONT_STYLE_OBLIQUE) + desiredTraits |= NSItalicFontMask; + + // we should really do the right thing with the offsets here, but that's harder. + if (desiredWeight >= APPLE_BOLD_WEIGHT) + desiredTraits |= NSBoldFontMask; + + NSFontManager *fontManager = [NSFontManager sharedFontManager]; + NSFont *font = nsnull; + + // Look for an exact match first. + NSEnumerator *availableFonts = [[fontManager availableFonts] objectEnumerator]; + NSString *availableFont; + while ((availableFont = [availableFonts nextObject])) { + if ([desiredFamily caseInsensitiveCompare:availableFont] == NSOrderedSame) { + NSFont *nameMatchedFont = [NSFont fontWithName:availableFont size:aStyle->size]; + NSFontTraitMask traits = [fontManager traitsOfFont:nameMatchedFont]; + + if ((traits & desiredTraits) == desiredTraits) { + font = [fontManager convertFont:nameMatchedFont toHaveTrait:desiredTraits]; + font = FindFontWeight (fontManager, font, desiredWeight, weightOffset, aStyle->size); + //fprintf (stderr, "Exact match found; weight: %d\n", [fontManager weightOfFont:font]); + return [font _atsFontID]; + } + + break; + } + } + + // Do a simple case insensitive search for a matching font family. + // NSFontManager requires exact name matches. + // This addresses the problem of matching arial to Arial, etc., but perhaps not all the issues. + NSEnumerator *e = [[fontManager availableFontFamilies] objectEnumerator]; + NSString *availableFamily; + while ((availableFamily = [e nextObject])) { + if ([desiredFamily caseInsensitiveCompare:availableFamily] == NSOrderedSame) { + break; + } + } + + // If we can't find a match for this at all, then bail + if (availableFamily == nsnull) + return kATSUInvalidFontID; + + // Found a family, now figure out what weight and traits to use. + BOOL choseFont = false; + int chosenWeight = 0; + NSFontTraitMask chosenTraits = 0; + + NSArray *fonts = [fontManager availableMembersOfFontFamily:availableFamily]; + unsigned n = [fonts count]; + unsigned i; + for (i = 0; i < n; i++) { + NSArray *fontInfo = [fonts objectAtIndex:i]; + + // Array indices must be hard coded because of lame AppKit API. + int fontWeight = [[fontInfo objectAtIndex:INDEX_FONT_WEIGHT] intValue]; + NSFontTraitMask fontTraits = [[fontInfo objectAtIndex:INDEX_FONT_TRAITS] unsignedIntValue]; + + BOOL newWinner; + if (!choseFont) + newWinner = acceptableChoice(desiredTraits, desiredWeight, fontTraits, fontWeight); + else + newWinner = betterChoice(desiredTraits, desiredWeight, chosenTraits, chosenWeight, fontTraits, fontWeight); + + if (newWinner) { + choseFont = YES; + chosenWeight = fontWeight; + chosenTraits = fontTraits; + + if (chosenWeight == desiredWeight && + (chosenTraits & IMPORTANT_FONT_TRAITS) == (desiredTraits & IMPORTANT_FONT_TRAITS)) + { + // this one is good enough; don't bother looking for something better + break; + } + } + } + + // we couldn't find anything that was valid in the family + if (!choseFont) + return kATSUInvalidFontID; + + // grab the actual font + font = [fontManager fontWithFamily:availableFamily traits:chosenTraits weight:chosenWeight size:aStyle->size]; + if (!font) + return kATSUInvalidFontID; + + //fprintf (stderr, "No exact match found; basic match weight: %d\n", [fontManager weightOfFont:font]); + font = FindFontWeight (fontManager, font, desiredWeight, weightOffset, aStyle->size); + //fprintf (stderr, " after FindFontWeight: %d\n", [fontManager weightOfFont:font]); + chosenWeight = [fontManager weightOfFont:font]; + + // Figure out whether we need to synthesize + NSFontTraitMask actualTraits = 0; + if (desiredTraits & (NSItalicFontMask | NSBoldFontMask)) + actualTraits = [fontManager traitsOfFont:font]; + + bool syntheticBold = (baseCSSWeight >= CSS_BOLD_WEIGHT_BASE && weightOffset <= 0) && (chosenWeight < APPLE_BOLD_WEIGHT); + bool syntheticOblique = (desiredTraits & NSItalicFontMask) && !(actualTraits & NSItalicFontMask); + + // There are some malformed fonts that will be correctly returned + // by -fontWithFamily:traits:weight:size: as a match for a + // particular trait, though -[NSFontManager traitsOfFont:] + // incorrectly claims the font does not have the specified + // trait. This could result in applying synthetic bold on top of + // an already-bold font, as reported in + // . To work + // around this problem, if we got an apparent exact match, but the + // requested traits aren't present in the matched font, we'll try + // to get a font from the same family without those traits (to + // apply the synthetic traits to later). + NSFontTraitMask nonSyntheticTraits = desiredTraits; + + if (syntheticBold) + nonSyntheticTraits &= ~NSBoldFontMask; + + if (syntheticOblique) + nonSyntheticTraits &= ~NSItalicFontMask; + + if (nonSyntheticTraits != desiredTraits) { + NSFont *fontWithoutSyntheticTraits = [fontManager fontWithFamily:availableFamily traits:nonSyntheticTraits weight:chosenWeight size:aStyle->size]; + if (fontWithoutSyntheticTraits) + font = fontWithoutSyntheticTraits; + } + + //printf ("Font: %s -> %d\n", [availableFamily cString], GetNSFontATSUFontId(font)); + return [font _atsFontID]; +} + +ATSUFontID +gfxQuartzFontCache::GetDefaultATSUFontID (const gfxFontStyle* aStyle) +{ + return [[NSFont userFontOfSize:aStyle->size] _atsFontID]; +}