зеркало из https://github.com/mozilla/gecko-dev.git
b=341694, bring Mac Cocoa+Cairo text layout/font selection/rendering to a useful baseline for further work, r=me
This commit is contained in:
Родитель
c8bfbaca8d
Коммит
4ab24bc9d1
|
@ -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;
|
||||
|
|
|
@ -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<ATSUStyle> mStylesToDispose;
|
||||
};
|
||||
|
||||
#endif /* GFX_ATSUIFONTS_H */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<gfxAtsuiFont> 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<gfxAtsuiFont> 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<gfxAtsuiFont> atsuiFont = mGroup->GetFontAt(0);
|
||||
cairo_set_font_face(cr, atsuiFont->CairoFontFace());
|
||||
cairo_set_font_size(cr, mGroup->GetStyle()->size);
|
||||
gfxFloat offsetX, offsetY;
|
||||
nsRefPtr<gfxASurface> 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<gfxFloat> &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<gfxFloat> *const
|
||||
|
|
|
@ -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 <vladimir@pobox.com>
|
||||
*
|
||||
* 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_ */
|
|
@ -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 <vladimir@pobox.com>
|
||||
*
|
||||
* 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 <Carbon.h>
|
||||
|
||||
#import <AppKit/AppKit.h>
|
||||
|
||||
#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
|
||||
// <http://bugzilla.opendarwin.org/show_bug.cgi?id=6146>. 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];
|
||||
}
|
Загрузка…
Ссылка в новой задаче