diff --git a/gfx/thebes/gfxCoreTextShaper.cpp b/gfx/thebes/gfxCoreTextShaper.cpp index 93fe5a47797..7ab77f9d944 100644 --- a/gfx/thebes/gfxCoreTextShaper.cpp +++ b/gfx/thebes/gfxCoreTextShaper.cpp @@ -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. diff --git a/gfx/thebes/gfxCoreTextShaper.h b/gfx/thebes/gfxCoreTextShaper.h index 75f341554d5..21f6d4770b5 100644 --- a/gfx/thebes/gfxCoreTextShaper.h +++ b/gfx/thebes/gfxCoreTextShaper.h @@ -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 diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp index c15ada25df8..656795c7bc5 100644 --- a/gfx/thebes/gfxDWriteFonts.cpp +++ b/gfx/thebes/gfxDWriteFonts.cpp @@ -219,9 +219,10 @@ gfxDWriteFont::ComputeMetrics(AntialiasOption anAAOption) TRUETYPE_TAG('h','h','e','a')); if (hheaTable) { uint32_t len; - const HheaTable* hhea = - reinterpret_cast(hb_blob_get_data(hheaTable, &len)); - if (len >= sizeof(HheaTable)) { + const MetricsHeader* hhea = + reinterpret_cast + (hb_blob_get_data(hheaTable, &len)); + if (len >= sizeof(MetricsHeader)) { mMetrics->maxAdvance = uint16_t(hhea->advanceWidthMax) * mFUnitsConvFactor; } diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index f6e875efddf..1ba7cabffe2 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -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); diff --git a/gfx/thebes/gfxFT2Fonts.h b/gfx/thebes/gfxFT2Fonts.h index f0e390c5a60..a0db69946a2 100644 --- a/gfx/thebes/gfxFT2Fonts.h +++ b/gfx/thebes/gfxFT2Fonts.h @@ -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); diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 3a4b81ab107..4988efd6ab9 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -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 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 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(hb_blob_get_data(hheaTable, &len)); - if (len < sizeof(HheaTable)) { + const MetricsHeader* hhea = + reinterpret_cast + (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(hb_blob_get_data(hheaTable, - &len)); - if (len >= sizeof(HheaTable)) { + const MetricsHeader* hhea = + reinterpret_cast + (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(hb_blob_get_data(vheaTable, - &len)); - if (len >= sizeof(HheaTable)) { + const MetricsHeader* vhea = + reinterpret_cast + (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)); diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index f92e4f48ecf..d439bf0d122 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -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(); diff --git a/gfx/thebes/gfxFontUtils.h b/gfx/thebes/gfxFontUtils.h index c783caefae0..439752cae4d 100644 --- a/gfx/thebes/gfxFontUtils.h +++ b/gfx/thebes/gfxFontUtils.h @@ -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 { diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index 70c02a2da3b..238b1eded7a 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -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& diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h index 4b5e15a3226..2e320eccc5f 100644 --- a/gfx/thebes/gfxGDIFont.h +++ b/gfx/thebes/gfxGDIFont.h @@ -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 diff --git a/gfx/thebes/gfxGraphiteShaper.cpp b/gfx/thebes/gfxGraphiteShaper.cpp index 076b3df9255..62c74c24563 100644 --- a/gfx/thebes/gfxGraphiteShaper.cpp +++ b/gfx/thebes/gfxGraphiteShaper.cpp @@ -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 diff --git a/gfx/thebes/gfxGraphiteShaper.h b/gfx/thebes/gfxGraphiteShaper.h index b9941ae4707..8127a840563 100644 --- a/gfx/thebes/gfxGraphiteShaper.h +++ b/gfx/thebes/gfxGraphiteShaper.h @@ -22,6 +22,7 @@ public: uint32_t aOffset, uint32_t aLength, int32_t aScript, + bool aVertical, gfxShapedText *aShapedText); static void Shutdown(); diff --git a/gfx/thebes/gfxHarfBuzzShaper.cpp b/gfx/thebes/gfxHarfBuzzShaper.cpp index bbf4b8a2ead..99a74e83778 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.cpp +++ b/gfx/thebes/gfxHarfBuzzShaper.cpp @@ -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(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(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(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(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(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(hb_blob_get_data(mVORGTable, nullptr)); + + const VORGrec *lo = reinterpret_cast(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(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 - (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 + (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 + (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(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 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); } } diff --git a/gfx/thebes/gfxHarfBuzzShaper.h b/gfx/thebes/gfxHarfBuzzShaper.h index 908d965a9c9..ba726a9bbad 100644 --- a/gfx/thebes/gfxHarfBuzzShaper.h +++ b/gfx/thebes/gfxHarfBuzzShaper.h @@ -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& 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 */ diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp index 0af44bcef4a..44608b9470c 100644 --- a/gfx/thebes/gfxMacFont.cpp +++ b/gfx/thebes/gfxMacFont.cpp @@ -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(GetFontEntry())->RequiresAATLayout()) { + // Currently, we don't support vertical shaping via CoreText, + // so we ignore RequiresAATLayout if vertical is requested. + if (static_cast(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 diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h index 565797636d0..36ec9de140f 100644 --- a/gfx/thebes/gfxMacFont.h +++ b/gfx/thebes/gfxMacFont.h @@ -57,6 +57,7 @@ protected: uint32_t aOffset, uint32_t aLength, int32_t aScript, + bool aVertical, gfxShapedText *aShapedText); void InitMetrics(); diff --git a/gfx/thebes/gfxTextRun.cpp b/gfx/thebes/gfxTextRun.cpp index bbe232f99cc..24c273bf06e 100644 --- a/gfx/thebes/gfxTextRun.cpp +++ b/gfx/thebes/gfxTextRun.cpp @@ -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; } diff --git a/layout/media/symbols.def.in b/layout/media/symbols.def.in index a3f6abed8ef..063e0061abe 100644 --- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -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