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:
vladimir%pobox.com 2006-06-16 00:42:39 +00:00
Родитель c8bfbaca8d
Коммит 4ab24bc9d1
6 изменённых файлов: 739 добавлений и 211 удалений

Просмотреть файл

@ -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];
}