Bug 902762 pt 1 - Pass a 'vertical' flag to font shapers, and support vertical shaping through harfbuzz. r=jdaggett

This commit is contained in:
Jonathan Kew 2014-10-01 20:25:48 +01:00
parent 85d5d1a31d
commit 315fe696fe
18 changed files with 492 additions and 166 deletions

View File

@ -52,6 +52,7 @@ gfxCoreTextShaper::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
// Create a CFAttributedString with text and style info, so we can use CoreText to lay it out.

View File

@ -23,6 +23,7 @@ public:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
// clean up static objects that may have been cached

View File

@ -219,9 +219,10 @@ gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption)
TRUETYPE_TAG('h','h','e','a'));
if (hheaTable) {
uint32_t len;
const HheaTable* hhea =
reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(HheaTable)) {
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mMetrics->maxAdvance =
uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor;
}

View File

@ -48,10 +48,11 @@ gfxFT2Font::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
if (!gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
aShapedText)) {
aVertical, aShapedText)) {
// harfbuzz must have failed(?!), just render raw glyphs
AddRange(aText, aOffset, aLength, aShapedText);
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);

View File

@ -77,6 +77,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd);

View File

@ -2214,6 +2214,7 @@ gfxFont::GetShapedWord(gfxContext *aContext,
uint32_t aLength,
uint32_t aHash,
int32_t aRunScript,
bool aVertical,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags,
gfxTextPerfMetrics *aTextPerf GFX_MAYBE_UNUSED)
@ -2273,7 +2274,7 @@ gfxFont::GetShapedWord(gfxContext *aContext,
}
DebugOnly<bool> ok =
ShapeText(aContext, aText, 0, aLength, aRunScript, sw);
ShapeText(aContext, aText, 0, aLength, aRunScript, aVertical, sw);
NS_WARN_IF_FALSE(ok, "failed to shape word - expect garbled text");
@ -2323,6 +2324,7 @@ gfxFont::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
nsDependentCSubstring ascii((const char*)aText, aLength);
@ -2332,7 +2334,7 @@ gfxFont::ShapeText(gfxContext *aContext,
return false;
}
return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength,
aScript, aShapedText);
aScript, aVertical, aShapedText);
}
bool
@ -2341,17 +2343,20 @@ gfxFont::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
bool ok = false;
if (FontCanSupportGraphite()) {
// XXX Currently, we do all vertical shaping through harfbuzz.
// Vertical graphite support may be wanted as a future enhancement.
if (FontCanSupportGraphite() && !aVertical) {
if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) {
if (!mGraphiteShaper) {
mGraphiteShaper = new gfxGraphiteShaper(this);
}
ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
aScript, aVertical, aShapedText);
}
}
@ -2360,7 +2365,7 @@ gfxFont::ShapeText(gfxContext *aContext,
mHarfBuzzShaper = new gfxHarfBuzzShaper(this);
}
ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText);
aScript, aVertical, aShapedText);
}
NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text");
@ -2397,6 +2402,7 @@ gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxTextRun *aTextRun)
{
aTextRun->SetupClusterBoundaries(aOffset, aText, aLength);
@ -2432,7 +2438,8 @@ gfxFont::ShapeFragmentWithoutWordCache(gfxContext *aContext,
}
}
ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aTextRun);
ok = ShapeText(aContext, aText, aOffset, fragLen, aScript, aVertical,
aTextRun);
aText += fragLen;
aOffset += fragLen;
@ -2460,6 +2467,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxTextRun *aTextRun)
{
uint32_t fragStart = 0;
@ -2478,7 +2486,7 @@ gfxFont::ShapeTextWithoutWordCache(gfxContext *aContext,
if (length > 0) {
ok = ShapeFragmentWithoutWordCache(aContext, aText + fragStart,
aOffset + fragStart, length,
aScript, aTextRun);
aScript, aVertical, aTextRun);
}
if (i == aLength) {
@ -2534,7 +2542,8 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
const T *aString, // text for this font run
uint32_t aRunStart, // position in the textrun
uint32_t aRunLength,
int32_t aRunScript)
int32_t aRunScript,
bool aVertical)
{
if (aRunLength == 0) {
return true;
@ -2569,7 +2578,8 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
HasSpaces(aString, aRunLength)) {
TEXT_PERF_INCR(tp, wordCacheSpaceRules);
return ShapeTextWithoutWordCache(aContext, aString,
aRunStart, aRunLength, aRunScript,
aRunStart, aRunLength,
aRunScript, aVertical,
aTextRun);
}
}
@ -2622,6 +2632,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
aRunStart + wordStart,
length,
aRunScript,
aVertical,
aTextRun);
if (!ok) {
return false;
@ -2638,7 +2649,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
}
gfxShapedWord *sw = GetShapedWord(aContext,
aString + wordStart, length,
hash, aRunScript,
hash, aRunScript, aVertical,
appUnitsPerDevUnit,
wordFlags, tp);
if (sw) {
@ -2652,7 +2663,9 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
// word was terminated by a space: add that to the textrun
uint16_t orientation = flags & gfxTextRunFactory::TEXT_ORIENT_MASK;
if (orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_MIXED) {
orientation = gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
orientation = aVertical ?
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT :
gfxTextRunFactory::TEXT_ORIENT_VERTICAL_SIDEWAYS_RIGHT;
}
if (!aTextRun->SetSpaceGlyphIfSimple(this, aContext,
aRunStart + i, ch,
@ -2662,7 +2675,7 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
gfxShapedWord *sw =
GetShapedWord(aContext,
&space, 1,
gfxShapedWord::HashMix(0, ' '), aRunScript,
gfxShapedWord::HashMix(0, ' '), aRunScript, aVertical,
appUnitsPerDevUnit,
flags | gfxTextRunFactory::TEXT_IS_8BIT, tp);
if (sw) {
@ -2711,15 +2724,17 @@ gfxFont::SplitAndInitTextRun(gfxContext *aContext,
const uint8_t *aString,
uint32_t aRunStart,
uint32_t aRunLength,
int32_t aRunScript);
int32_t aRunScript,
bool aVertical);
template bool
gfxFont::SplitAndInitTextRun(gfxContext *aContext,
gfxTextRun *aTextRun,
const char16_t *aString,
uint32_t aRunStart,
uint32_t aRunLength,
int32_t aRunScript);
int32_t aRunScript,
bool aVertical);
template<>
bool
gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
@ -2745,6 +2760,8 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
RunCaseAction runAction = kNoChange;
uint32_t runStart = 0;
bool vertical =
aOrientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
for (uint32_t i = 0; i <= aLength; ++i) {
uint32_t extraCodeUnits = 0; // Will be set to 1 if we need to consume
@ -2803,7 +2820,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
if (!f->SplitAndInitTextRun(aContext, aTextRun,
aText + runStart,
aOffset + runStart, runLength,
aScript)) {
aScript, vertical)) {
ok = false;
}
break;
@ -2844,7 +2861,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
if (!f->SplitAndInitTextRun(aContext, tempRun,
convertedString.BeginReading(),
0, convertedString.Length(),
aScript)) {
aScript, vertical)) {
ok = false;
} else {
nsAutoPtr<gfxTextRun> mergedRun;
@ -2863,7 +2880,7 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext,
if (!f->SplitAndInitTextRun(aContext, aTextRun,
convertedString.BeginReading(),
aOffset + runStart, runLength,
aScript)) {
aScript, vertical)) {
ok = false;
}
}
@ -3024,9 +3041,10 @@ gfxFont::InitMetricsFromSfntTables(Metrics& aMetrics)
if (!hheaTable) {
return false; // no 'hhea' table -> not an sfnt
}
const HheaTable* hhea =
reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable, &len));
if (len < sizeof(HheaTable)) {
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len < sizeof(MetricsHeader)) {
return false;
}
@ -3272,10 +3290,10 @@ gfxFont::CreateVerticalMetrics()
if (!metrics->aveCharWidth) {
gfxFontEntry::AutoTable hheaTable(mFontEntry, kHheaTableTag);
if (hheaTable) {
const HheaTable* hhea =
reinterpret_cast<const HheaTable*>(hb_blob_get_data(hheaTable,
&len));
if (len >= sizeof(HheaTable)) {
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
SET_SIGNED(aveCharWidth, int16_t(hhea->ascender) -
int16_t(hhea->descender));
metrics->maxAscent = metrics->aveCharWidth / 2;
@ -3288,10 +3306,10 @@ gfxFont::CreateVerticalMetrics()
// Read real vertical metrics if available.
gfxFontEntry::AutoTable vheaTable(mFontEntry, kVheaTableTag);
if (vheaTable) {
const HheaTable* vhea =
reinterpret_cast<const HheaTable*>(hb_blob_get_data(vheaTable,
&len));
if (len >= sizeof(HheaTable)) {
const MetricsHeader* vhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(vheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
SET_UNSIGNED(maxAdvance, vhea->advanceWidthMax);
SET_SIGNED(maxAscent, vhea->ascender);
SET_SIGNED(maxDescent, -int16_t(vhea->descender));

View File

@ -610,6 +610,7 @@ public:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText) = 0;
gfxFont *GetFont() const { return mFont; }
@ -1630,7 +1631,8 @@ public:
const T *aString,
uint32_t aRunStart,
uint32_t aRunLength,
int32_t aRunScript);
int32_t aRunScript,
bool aVertical);
// Get a ShapedWord representing the given text (either 8- or 16-bit)
// for use in setting up a gfxTextRun.
@ -1640,6 +1642,7 @@ public:
uint32_t aLength,
uint32_t aHash,
int32_t aRunScript,
bool aVertical,
int32_t aAppUnitsPerDevUnit,
uint32_t aFlags,
gfxTextPerfMetrics *aTextPerf);
@ -1816,6 +1819,7 @@ protected:
uint32_t aOffset, // dest offset in gfxShapedText
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText); // where to store the result
// Call the appropriate shaper to generate glyphs for aText and store
@ -1825,6 +1829,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
// Helper to adjust for synthetic bold and set character-type flags
@ -1849,6 +1854,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxTextRun *aTextRun);
// Shape a fragment of text (a run that is known to contain only
@ -1862,6 +1868,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxTextRun *aTextRun);
void CheckForFeaturesInvolvingSpace();

View File

@ -553,7 +553,10 @@ struct PostTable {
AutoSwap_PRUint32 maxMemType1;
};
struct HheaTable {
// This structure is used for both 'hhea' and 'vhea' tables.
// The field names here are those of the horizontal version; the
// vertical table just exchanges vertical and horizontal coordinates.
struct MetricsHeader {
AutoSwap_PRUint32 version;
AutoSwap_PRInt16 ascender;
AutoSwap_PRInt16 descender;
@ -570,7 +573,7 @@ struct HheaTable {
AutoSwap_PRInt16 reserved3;
AutoSwap_PRInt16 reserved4;
AutoSwap_PRInt16 metricDataFormat;
AutoSwap_PRUint16 numOfLongHorMetrics;
AutoSwap_PRUint16 numOfLongMetrics;
};
struct MaxpTableHeader {

View File

@ -83,6 +83,7 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
if (!mMetrics) {
@ -102,7 +103,7 @@ gfxGDIFont::ShapeText(gfxContext *aContext,
}
return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
aShapedText);
aVertical, aShapedText);
}
const gfxFont::Metrics&

View File

@ -77,6 +77,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
void Initialize(); // creates metrics and Cairo fonts

View File

@ -89,6 +89,7 @@ gfxGraphiteShaper::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
// some font back-ends require this in order to get proper hinted metrics

View File

@ -22,6 +22,7 @@ public:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
static void Shutdown();

View File

@ -39,14 +39,18 @@ gfxHarfBuzzShaper::gfxHarfBuzzShaper(gfxFont *aFont)
mHBFont(nullptr),
mKernTable(nullptr),
mHmtxTable(nullptr),
mNumLongMetrics(0),
mVmtxTable(nullptr),
mVORGTable(nullptr),
mCmapTable(nullptr),
mCmapFormat(-1),
mSubtableOffset(0),
mUVSTableOffset(0),
mNumLongHMetrics(0),
mNumLongVMetrics(0),
mUseFontGetGlyph(aFont->ProvidesGetGlyph()),
mUseFontGlyphWidths(false),
mInitialized(false)
mInitialized(false),
mVerticalInitialized(false)
{
}
@ -166,30 +170,15 @@ HBGetGlyph(hb_font_t *font, void *font_data,
return *glyph != 0;
}
struct HMetricsHeader {
AutoSwap_PRUint32 tableVersionNumber;
AutoSwap_PRInt16 ascender;
AutoSwap_PRInt16 descender;
AutoSwap_PRInt16 lineGap;
AutoSwap_PRUint16 advanceWidthMax;
AutoSwap_PRInt16 minLeftSideBearing;
AutoSwap_PRInt16 minRightSideBearing;
AutoSwap_PRInt16 xMaxExtent;
AutoSwap_PRInt16 caretSlopeRise;
AutoSwap_PRInt16 caretSlopeRun;
AutoSwap_PRInt16 caretOffset;
AutoSwap_PRInt16 reserved[4];
AutoSwap_PRInt16 metricDataFormat;
AutoSwap_PRUint16 numberOfHMetrics;
// Glyph metrics structures, shared (with appropriate reinterpretation of
// field names) by horizontal and vertical metrics tables.
struct LongMetric {
AutoSwap_PRUint16 advanceWidth; // or advanceHeight, when vertical
AutoSwap_PRInt16 lsb; // or tsb, when vertical
};
struct HLongMetric {
AutoSwap_PRUint16 advanceWidth;
AutoSwap_PRInt16 lsb;
};
struct HMetrics {
HLongMetric metrics[1]; // actually numberOfHMetrics
struct GlyphMetrics {
LongMetric metrics[1]; // actually numberOfLongMetrics
// the variable-length metrics[] array is immediately followed by:
// AutoSwap_PRUint16 leftSideBearing[];
};
@ -198,23 +187,51 @@ hb_position_t
gfxHarfBuzzShaper::GetGlyphHAdvance(gfxContext *aContext,
hb_codepoint_t glyph) const
{
// font did not implement GetHintedGlyphWidth, so get an unhinted value
// font did not implement GetGlyphWidth, so get an unhinted value
// directly from the font tables
NS_ASSERTION((mNumLongMetrics > 0) && mHmtxTable != nullptr,
NS_ASSERTION((mNumLongHMetrics > 0) && mHmtxTable != nullptr,
"font is lacking metrics, we shouldn't be here");
if (glyph >= uint32_t(mNumLongMetrics)) {
glyph = mNumLongMetrics - 1;
if (glyph >= uint32_t(mNumLongHMetrics)) {
glyph = mNumLongHMetrics - 1;
}
// glyph must be valid now, because we checked during initialization
// that mNumLongMetrics is > 0, and that the hmtx table is large enough
// to contain mNumLongMetrics records
const HMetrics* hmtx =
reinterpret_cast<const HMetrics*>(hb_blob_get_data(mHmtxTable, nullptr));
// that mNumLongHMetrics is > 0, and that the metrics table is large enough
// to contain mNumLongHMetrics records
const GlyphMetrics* metrics =
reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mHmtxTable,
nullptr));
return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
uint16_t(hmtx->metrics[glyph].advanceWidth));
uint16_t(metrics->metrics[glyph].advanceWidth));
}
hb_position_t
gfxHarfBuzzShaper::GetGlyphVAdvance(gfxContext *aContext,
hb_codepoint_t glyph) const
{
if (!mVmtxTable) {
// Must be a "vertical" font that doesn't actually have vertical metrics;
// use a fixed advance.
return FloatToFixed(mFont->GetMetrics(gfxFont::eVertical).aveCharWidth);
}
NS_ASSERTION(mNumLongVMetrics > 0,
"font is lacking metrics, we shouldn't be here");
if (glyph >= uint32_t(mNumLongVMetrics)) {
glyph = mNumLongVMetrics - 1;
}
// glyph must be valid now, because we checked during initialization
// that mNumLongVMetrics is > 0, and that the metrics table is large enough
// to contain mNumLongVMetrics records
const GlyphMetrics* metrics =
reinterpret_cast<const GlyphMetrics*>(hb_blob_get_data(mVmtxTable,
nullptr));
return FloatToFixed(mFont->FUnitsToDevUnitsFactor() *
uint16_t(metrics->metrics[glyph].advanceWidth));
}
/* static */
@ -232,6 +249,111 @@ gfxHarfBuzzShaper::HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
}
}
/* static */
hb_position_t
gfxHarfBuzzShaper::HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
hb_codepoint_t glyph, void *user_data)
{
const gfxHarfBuzzShaper::FontCallbackData *fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
gfxFont *gfxfont = fcd->mShaper->GetFont();
if (gfxfont->ProvidesGlyphWidths()) {
return gfxfont->GetGlyphWidth(fcd->mContext, glyph);
} else {
return fcd->mShaper->GetGlyphVAdvance(fcd->mContext, glyph);
}
}
/* static */
hb_bool_t
gfxHarfBuzzShaper::HBGetGlyphHOrigin(hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y,
void *user_data)
{
// We work in horizontal coordinates, so no origin adjustment needed here.
return true;
}
struct VORG {
AutoSwap_PRUint16 majorVersion;
AutoSwap_PRUint16 minorVersion;
AutoSwap_PRInt16 defaultVertOriginY;
AutoSwap_PRUint16 numVertOriginYMetrics;
};
struct VORGrec {
AutoSwap_PRUint16 glyphIndex;
AutoSwap_PRInt16 vertOriginY;
};
/* static */
hb_bool_t
gfxHarfBuzzShaper::HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y,
void *user_data)
{
const gfxHarfBuzzShaper::FontCallbackData *fcd =
static_cast<const gfxHarfBuzzShaper::FontCallbackData*>(font_data);
fcd->mShaper->GetGlyphVOrigin(fcd->mContext, glyph, x, y);
return true;
}
void
gfxHarfBuzzShaper::GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
hb_position_t *aX, hb_position_t *aY) const
{
*aX = -0.5 * GetGlyphHAdvance(aContext, aGlyph);
if (mVORGTable) {
// We checked in Initialize() that the VORG table is safely readable,
// so no length/bounds-check needed here.
const VORG* vorg =
reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable, nullptr));
const VORGrec *lo = reinterpret_cast<const VORGrec*>(vorg + 1);
const VORGrec *hi = lo + uint16_t(vorg->numVertOriginYMetrics);
const VORGrec *limit = hi;
while (lo < hi) {
const VORGrec *mid = lo + (hi - lo) / 2;
if (uint16_t(mid->glyphIndex) < aGlyph) {
lo = mid + 1;
} else {
hi = mid;
}
}
if (lo < limit && uint16_t(lo->glyphIndex) == aGlyph) {
*aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
int16_t(lo->vertOriginY));
} else {
*aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
int16_t(vorg->defaultVertOriginY));
}
return;
}
// XXX should we consider using OS/2 sTypo* metrics if available?
gfxFontEntry::AutoTable hheaTable(GetFont()->GetFontEntry(),
TRUETYPE_TAG('h','h','e','a'));
if (hheaTable) {
uint32_t len;
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>(hb_blob_get_data(hheaTable,
&len));
if (len >= sizeof(MetricsHeader)) {
*aY = -FloatToFixed(GetFont()->FUnitsToDevUnitsFactor() *
int16_t(hhea->ascender));
return;
}
}
NS_NOTREACHED("we shouldn't be here!");
*aY = -FloatToFixed(GetFont()->GetAdjustedSize() / 2);
}
static hb_bool_t
HBGetContourPoint(hb_font_t *font, void *font_data,
unsigned int point_index, hb_codepoint_t glyph,
@ -858,6 +980,15 @@ gfxHarfBuzzShaper::Initialize()
hb_font_funcs_set_glyph_h_advance_func(sHBFontFuncs,
HBGetGlyphHAdvance,
nullptr, nullptr);
hb_font_funcs_set_glyph_v_advance_func(sHBFontFuncs,
HBGetGlyphVAdvance,
nullptr, nullptr);
hb_font_funcs_set_glyph_h_origin_func(sHBFontFuncs,
HBGetGlyphHOrigin,
nullptr, nullptr);
hb_font_funcs_set_glyph_v_origin_func(sHBFontFuncs,
HBGetGlyphVOrigin,
nullptr, nullptr);
hb_font_funcs_set_glyph_contour_point_func(sHBFontFuncs,
HBGetContourPoint,
nullptr, nullptr);
@ -910,36 +1041,9 @@ gfxHarfBuzzShaper::Initialize()
}
if (!mUseFontGlyphWidths) {
// if font doesn't implement GetGlyphWidth, we will be reading
// the hmtx table directly;
// read mNumLongMetrics from hhea table without caching its blob,
// and preload/cache the hmtx table
gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
if (hheaTable) {
uint32_t len;
const HMetricsHeader* hhea =
reinterpret_cast<const HMetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(HMetricsHeader)) {
mNumLongMetrics = hhea->numberOfHMetrics;
if (mNumLongMetrics > 0 &&
int16_t(hhea->metricDataFormat) == 0) {
// no point reading hmtx if number of entries is zero!
// in that case, we won't be able to use this font
// (this method will return FALSE below if mHmtx is null)
mHmtxTable =
entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
if (hb_blob_get_length(mHmtxTable) <
mNumLongMetrics * sizeof(HLongMetric)) {
// hmtx table is not large enough for the claimed
// number of entries: invalid, do not use.
hb_blob_destroy(mHmtxTable);
mHmtxTable = nullptr;
}
}
}
}
if (!mHmtxTable) {
// If font doesn't implement GetGlyphWidth, we will be reading
// the metrics table directly, so make sure we can load it.
if (!LoadHmtxTable()) {
return false;
}
}
@ -953,12 +1057,109 @@ gfxHarfBuzzShaper::Initialize()
return true;
}
bool
gfxHarfBuzzShaper::LoadHmtxTable()
{
// Read mNumLongHMetrics from metrics-head table without caching its
// blob, and preload/cache the metrics table.
gfxFontEntry *entry = mFont->GetFontEntry();
gfxFontEntry::AutoTable hheaTable(entry, TRUETYPE_TAG('h','h','e','a'));
if (hheaTable) {
uint32_t len;
const MetricsHeader* hhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(hheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mNumLongHMetrics = hhea->numOfLongMetrics;
if (mNumLongHMetrics > 0 &&
int16_t(hhea->metricDataFormat) == 0) {
// no point reading metrics if number of entries is zero!
// in that case, we won't be able to use this font
// (this method will return FALSE below if mHmtxTable
// is null)
mHmtxTable = entry->GetFontTable(TRUETYPE_TAG('h','m','t','x'));
if (hb_blob_get_length(mHmtxTable) <
mNumLongHMetrics * sizeof(LongMetric)) {
// metrics table is not large enough for the claimed
// number of entries: invalid, do not use.
hb_blob_destroy(mHmtxTable);
mHmtxTable = nullptr;
}
}
}
}
if (!mHmtxTable) {
return false;
}
return true;
}
bool
gfxHarfBuzzShaper::InitializeVertical()
{
if (!mHmtxTable) {
if (!LoadHmtxTable()) {
return false;
}
}
// Load vertical metrics if present in the font; if not, we'll synthesize
// vertical glyph advances based on (horizontal) ascent/descent metrics.
gfxFontEntry *entry = mFont->GetFontEntry();
gfxFontEntry::AutoTable vheaTable(entry, TRUETYPE_TAG('v','h','e','a'));
if (vheaTable) {
uint32_t len;
const MetricsHeader* vhea =
reinterpret_cast<const MetricsHeader*>
(hb_blob_get_data(vheaTable, &len));
if (len >= sizeof(MetricsHeader)) {
mNumLongVMetrics = vhea->numOfLongMetrics;
if (mNumLongVMetrics > 0 &&
int16_t(vhea->metricDataFormat) == 0) {
mVmtxTable = entry->GetFontTable(TRUETYPE_TAG('v','m','t','x'));
if (hb_blob_get_length(mVmtxTable) <
mNumLongVMetrics * sizeof(LongMetric)) {
// metrics table is not large enough for the claimed
// number of entries: invalid, do not use.
hb_blob_destroy(mVmtxTable);
mVmtxTable = nullptr;
}
}
}
}
// For CFF fonts only, load a VORG table if present.
if (entry->HasFontTable(TRUETYPE_TAG('C','F','F',' '))) {
mVORGTable = entry->GetFontTable(TRUETYPE_TAG('V','O','R','G'));
if (mVORGTable) {
uint32_t len;
const VORG* vorg =
reinterpret_cast<const VORG*>(hb_blob_get_data(mVORGTable,
&len));
if (len < sizeof(VORG) ||
uint16_t(vorg->majorVersion) != 1 ||
uint16_t(vorg->minorVersion) != 0 ||
len < sizeof(VORG) + uint16_t(vorg->numVertOriginYMetrics) *
sizeof(VORGrec)) {
// VORG table is an unknown version, or not large enough
// to be valid -- discard it.
NS_WARNING("discarding invalid VORG table");
hb_blob_destroy(mVORGTable);
mVORGTable = nullptr;
}
}
}
return true;
}
bool
gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
const char16_t *aText,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
// some font back-ends require this in order to get proper hinted metrics
@ -972,6 +1173,12 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
return false;
}
if (aVertical) {
if (!InitializeVertical()) {
return false;
}
}
const gfxFontStyle *style = mFont->GetStyle();
nsAutoTArray<hb_feature_t,20> features;
@ -1007,8 +1214,11 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
bool isRightToLeft = aShapedText->IsRightToLeft();
hb_buffer_t *buffer = hb_buffer_create();
hb_buffer_set_unicode_funcs(buffer, sHBUnicodeFuncs);
hb_buffer_set_direction(buffer, isRightToLeft ? HB_DIRECTION_RTL :
HB_DIRECTION_LTR);
hb_buffer_set_direction(buffer,
aVertical ? HB_DIRECTION_TTB :
(isRightToLeft ? HB_DIRECTION_RTL :
HB_DIRECTION_LTR));
hb_script_t scriptTag;
if (aShapedText->GetFlags() & gfxTextRunFactory::TEXT_USE_MATH_SCRIPT) {
scriptTag = sMathScript;
@ -1042,7 +1252,7 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
}
nsresult rv = SetGlyphsFromRun(aContext, aShapedText, aOffset, aLength,
aText, buffer);
aText, buffer, aVertical);
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into gfxShapedWord");
hb_buffer_destroy(buffer);
@ -1055,12 +1265,13 @@ gfxHarfBuzzShaper::ShapeText(gfxContext *aContext,
// for charToGlyphArray
nsresult
gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const char16_t *aText,
hb_buffer_t *aBuffer)
hb_buffer_t *aBuffer,
bool aVertical)
{
uint32_t numGlyphs;
const hb_glyph_info_t *ginfo = hb_buffer_get_glyph_infos(aBuffer, &numGlyphs);
@ -1092,9 +1303,13 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
int32_t glyphStart = 0; // looking for a clump that starts at this glyph
int32_t charStart = 0; // and this char index within the range of the run
bool roundX;
bool roundY;
aContext->GetRoundOffsetsToPixels(&roundX, &roundY);
bool roundI;
bool roundB;
if (aVertical) {
aContext->GetRoundOffsetsToPixels(&roundB, &roundI);
} else {
aContext->GetRoundOffsetsToPixels(&roundI, &roundB);
}
int32_t appUnitsPerDevUnit = aShapedText->GetAppUnitsPerDevUnit();
gfxShapedText::CompressedGlyph *charGlyphs =
@ -1114,10 +1329,10 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
//
// The value of the residual is the part of the desired distance that has
// not been included in integer offsets.
hb_position_t x_residual = 0;
hb_position_t residual = 0;
// keep track of y-position to set glyph offsets if needed
nscoord yPos = 0;
nscoord bPos = 0;
const hb_glyph_position_t *posInfo =
hb_buffer_get_glyph_positions(aBuffer, nullptr);
@ -1212,28 +1427,43 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
continue;
}
hb_position_t x_offset = posInfo[glyphStart].x_offset;
hb_position_t x_advance = posInfo[glyphStart].x_advance;
nscoord xOffset, advance;
if (roundX) {
xOffset =
appUnitsPerDevUnit * FixedToIntRound(x_offset + x_residual);
// Desired distance from the base glyph to the next reference point.
hb_position_t width = x_advance - x_offset;
int intWidth = FixedToIntRound(width);
x_residual = width - FloatToFixed(intWidth);
advance = appUnitsPerDevUnit * intWidth + xOffset;
// HarfBuzz gives us physical x- and y-coordinates, but we will store
// them as logical inline- and block-direction values in the textrun.
hb_position_t i_offset, i_advance; // inline-direction offset/advance
hb_position_t b_offset, b_advance; // block-direction offset/advance
if (aVertical) {
i_offset = posInfo[glyphStart].y_offset;
i_advance = posInfo[glyphStart].y_advance;
b_offset = posInfo[glyphStart].x_offset;
b_advance = posInfo[glyphStart].x_advance;
} else {
xOffset = floor(hb2appUnits * x_offset + 0.5);
advance = floor(hb2appUnits * x_advance + 0.5);
i_offset = posInfo[glyphStart].x_offset;
i_advance = posInfo[glyphStart].x_advance;
b_offset = posInfo[glyphStart].y_offset;
b_advance = posInfo[glyphStart].y_advance;
}
nscoord iOffset, advance;
if (roundI) {
iOffset =
appUnitsPerDevUnit * FixedToIntRound(i_offset + residual);
// Desired distance from the base glyph to the next reference point.
hb_position_t width = i_advance - i_offset;
int intWidth = FixedToIntRound(width);
residual = width - FloatToFixed(intWidth);
advance = appUnitsPerDevUnit * intWidth + iOffset;
} else {
iOffset = floor(hb2appUnits * i_offset + 0.5);
advance = floor(hb2appUnits * i_advance + 0.5);
}
// Check if it's a simple one-to-one mapping
if (glyphsInClump == 1 &&
gfxTextRun::CompressedGlyph::IsSimpleGlyphID(ginfo[glyphStart].codepoint) &&
gfxTextRun::CompressedGlyph::IsSimpleAdvance(advance) &&
charGlyphs[baseCharIndex].IsClusterStart() &&
xOffset == 0 &&
posInfo[glyphStart].y_offset == 0 && yPos == 0)
iOffset == 0 && b_offset == 0 &&
b_advance == 0 && bPos == 0)
{
charGlyphs[baseCharIndex].SetSimpleGlyph(advance,
ginfo[glyphStart].codepoint);
@ -1247,41 +1477,49 @@ gfxHarfBuzzShaper::SetGlyphsFromRun(gfxContext *aContext,
detailedGlyphs.AppendElement();
details->mGlyphID = ginfo[glyphStart].codepoint;
details->mXOffset = xOffset;
details->mXOffset = iOffset;
details->mAdvance = advance;
hb_position_t y_offset = posInfo[glyphStart].y_offset;
details->mYOffset = yPos -
(roundY ? appUnitsPerDevUnit * FixedToIntRound(y_offset)
: floor(hb2appUnits * y_offset + 0.5));
details->mYOffset = bPos -
(roundB ? appUnitsPerDevUnit * FixedToIntRound(b_offset)
: floor(hb2appUnits * b_offset + 0.5));
hb_position_t y_advance = posInfo[glyphStart].y_advance;
if (y_advance != 0) {
yPos -=
roundY ? appUnitsPerDevUnit * FixedToIntRound(y_advance)
: floor(hb2appUnits * y_advance + 0.5);
if (b_advance != 0) {
bPos -=
roundB ? appUnitsPerDevUnit * FixedToIntRound(b_advance)
: floor(hb2appUnits * b_advance + 0.5);
}
if (++glyphStart >= glyphEnd) {
break;
}
x_offset = posInfo[glyphStart].x_offset;
x_advance = posInfo[glyphStart].x_advance;
if (roundX) {
xOffset = appUnitsPerDevUnit *
FixedToIntRound(x_offset + x_residual);
if (aVertical) {
i_offset = posInfo[glyphStart].y_offset;
i_advance = posInfo[glyphStart].y_advance;
b_offset = posInfo[glyphStart].x_offset;
b_advance = posInfo[glyphStart].x_advance;
} else {
i_offset = posInfo[glyphStart].x_offset;
i_advance = posInfo[glyphStart].x_advance;
b_offset = posInfo[glyphStart].y_offset;
b_advance = posInfo[glyphStart].y_advance;
}
if (roundI) {
iOffset = appUnitsPerDevUnit *
FixedToIntRound(i_offset + residual);
// Desired distance to the next reference point. The
// residual is considered here, and includes the residual
// from the base glyph offset and subsequent advances, so
// that the distance from the base glyph is optimized
// rather than the distance from combining marks.
x_advance += x_residual;
int intAdvance = FixedToIntRound(x_advance);
x_residual = x_advance - FloatToFixed(intAdvance);
i_advance += residual;
int intAdvance = FixedToIntRound(i_advance);
residual = i_advance - FloatToFixed(intAdvance);
advance = appUnitsPerDevUnit * intAdvance;
} else {
xOffset = floor(hb2appUnits * x_offset + 0.5);
advance = floor(hb2appUnits * x_advance + 0.5);
iOffset = floor(hb2appUnits * i_offset + 0.5);
advance = floor(hb2appUnits * i_advance + 0.5);
}
}

View File

@ -31,6 +31,7 @@ public:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
// get a given font table in harfbuzz blob form
@ -44,11 +45,33 @@ public:
hb_position_t GetGlyphHAdvance(gfxContext *aContext,
hb_codepoint_t glyph) const;
hb_position_t GetGlyphVAdvance(gfxContext *aContext,
hb_codepoint_t glyph) const;
void GetGlyphVOrigin(gfxContext *aContext, hb_codepoint_t aGlyph,
hb_position_t *aX, hb_position_t *aY) const;
// get harfbuzz horizontal advance in 16.16 fixed point format.
static hb_position_t
HBGetGlyphHAdvance(hb_font_t *font, void *font_data,
hb_codepoint_t glyph, void *user_data);
// get harfbuzz vertical advance in 16.16 fixed point format.
static hb_position_t
HBGetGlyphVAdvance(hb_font_t *font, void *font_data,
hb_codepoint_t glyph, void *user_data);
static hb_bool_t
HBGetGlyphHOrigin(hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y,
void *user_data);
static hb_bool_t
HBGetGlyphVOrigin(hb_font_t *font, void *font_data,
hb_codepoint_t glyph,
hb_position_t *x, hb_position_t *y,
void *user_data);
hb_position_t GetHKerning(uint16_t aFirstGlyph,
uint16_t aSecondGlyph) const;
@ -68,12 +91,13 @@ public:
}
protected:
nsresult SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
nsresult SetGlyphsFromRun(gfxContext *aContext,
gfxShapedText *aShapedText,
uint32_t aOffset,
uint32_t aLength,
const char16_t *aText,
hb_buffer_t *aBuffer);
hb_buffer_t *aBuffer,
bool aVertical);
// retrieve glyph positions, applying advance adjustments and attachments
// returns results in appUnits
@ -82,6 +106,9 @@ protected:
nsTArray<nsPoint>& aPositions,
uint32_t aAppUnitsPerDevUnit);
bool InitializeVertical();
bool LoadHmtxTable();
// harfbuzz face object: we acquire a reference from the font entry
// on shaper creation, and release it in our destructor
hb_face_t *mHBFace;
@ -99,13 +126,12 @@ protected:
// Old-style TrueType kern table, if we're not doing GPOS kerning
mutable hb_blob_t *mKernTable;
// Cached copy of the hmtx table and numLongMetrics field from hhea,
// for use when looking up glyph metrics; initialized to 0 by the
// constructor so we can tell it hasn't been set yet.
// This is a signed value so that we can use -1 to indicate
// an error (if the hhea table was not available).
// Cached copy of the hmtx table.
mutable hb_blob_t *mHmtxTable;
mutable int32_t mNumLongMetrics;
// For vertical fonts, cached vmtx and VORG table, if present.
mutable hb_blob_t *mVmtxTable;
mutable hb_blob_t *mVORGTable;
// Cached pointer to cmap subtable to be used for char-to-glyph mapping.
// This comes from GetFontTablePtr; if it is non-null, our destructor
@ -115,6 +141,15 @@ protected:
mutable uint32_t mSubtableOffset;
mutable uint32_t mUVSTableOffset;
// Cached copy of numLongMetrics field from the hhea table,
// for use when looking up glyph metrics; initialized to 0 by the
// constructor so we can tell it hasn't been set yet.
// This is a signed value so that we can use -1 to indicate
// an error (if the hhea table was not available).
mutable int32_t mNumLongHMetrics;
// Similarly for vhea if it's a vertical font.
mutable int32_t mNumLongVMetrics;
// Whether the font implements GetGlyph, or we should read tables
// directly
bool mUseFontGetGlyph;
@ -123,6 +158,7 @@ protected:
bool mUseFontGlyphWidths;
bool mInitialized;
bool mVerticalInitialized;
};
#endif /* GFX_HARFBUZZSHAPER_H */

View File

@ -124,6 +124,7 @@ gfxMacFont::ShapeText(gfxContext *aContext,
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText)
{
if (!mIsValid) {
@ -131,19 +132,22 @@ gfxMacFont::ShapeText(gfxContext *aContext,
return false;
}
if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout()) {
// Currently, we don't support vertical shaping via CoreText,
// so we ignore RequiresAATLayout if vertical is requested.
if (static_cast<MacOSFontEntry*>(GetFontEntry())->RequiresAATLayout() &&
!aVertical) {
if (!mCoreTextShaper) {
mCoreTextShaper = new gfxCoreTextShaper(this);
}
if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength,
aScript, aShapedText)) {
aScript, aVertical, aShapedText)) {
PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText);
return true;
}
}
return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript,
aShapedText);
aVertical, aShapedText);
}
bool

View File

@ -57,6 +57,7 @@ protected:
uint32_t aOffset,
uint32_t aLength,
int32_t aScript,
bool aVertical,
gfxShapedText *aShapedText);
void InitMetrics();

View File

@ -1240,10 +1240,13 @@ gfxTextRun::SetSpaceGlyph(gfxFont *aFont, gfxContext *aContext,
gfxTextRunFactory::TEXT_IS_ASCII |
gfxTextRunFactory::TEXT_IS_PERSISTENT |
aOrientation;
bool vertical =
(GetFlags() & gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT) != 0;
gfxShapedWord *sw = aFont->GetShapedWord(aContext,
&space, 1,
gfxShapedWord::HashMix(0, ' '),
MOZ_SCRIPT_LATIN,
vertical,
mAppUnitsPerDevUnit,
flags,
nullptr);
@ -2224,7 +2227,8 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
const gfxTextRange& range = fontRanges[r];
uint32_t matchedLength = range.Length();
gfxFont *matchedFont = range.font;
bool vertical =
range.orientation == gfxTextRunFactory::TEXT_ORIENT_VERTICAL_UPRIGHT;
// create the glyph run for this range
if (matchedFont && mStyle.noFallbackVariantFeatures) {
// common case - just do glyph layout and record the
@ -2236,7 +2240,8 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
aString + runStart,
aOffset + runStart,
matchedLength,
aRunScript)) {
aRunScript,
vertical)) {
// glyph layout failed! treat as missing glyphs
matchedFont = nullptr;
}
@ -2275,7 +2280,8 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
aString + runStart,
aOffset + runStart,
matchedLength,
aRunScript)) {
aRunScript,
vertical)) {
// glyph layout failed! treat as missing glyphs
matchedFont = nullptr;
}
@ -2319,7 +2325,8 @@ gfxFontGroup::InitScriptRun(gfxContext *aContext,
aString + runStart,
aOffset + runStart,
matchedLength,
aRunScript)) {
aRunScript,
vertical)) {
// glyph layout failed! treat as missing glyphs
matchedFont = nullptr;
}

View File

@ -570,6 +570,9 @@ hb_font_funcs_set_glyph_contour_point_func
hb_font_funcs_set_glyph_func
hb_font_funcs_set_glyph_h_advance_func
hb_font_funcs_set_glyph_h_kerning_func
hb_font_funcs_set_glyph_h_origin_func
hb_font_funcs_set_glyph_v_origin_func
hb_font_funcs_set_glyph_v_advance_func
hb_font_set_funcs
hb_font_set_ppem
hb_font_set_scale