void SimpleFontData::platformInit(bool subpixelAscentDescent) { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint::FontMetrics metrics; m_platformData.setupPaint(&m_paint); m_paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); m_paint.getFontMetrics(&metrics); SkTypeface* face = m_paint.getTypeface(); ASSERT(face); int vdmxAscent = 0, vdmxDescent = 0; bool isVDMXValid = false; #if OS(LINUX) || OS(ANDROID) // Manually digging up VDMX metrics is only applicable when bytecode hinting // using FreeType. With DirectWrite or CoreText, no bytecode hinting is ever // done. This code should be pushed into FreeType (hinted font metrics). static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; if (!m_paint.isAutohinted() && (m_paint.getHinting() == SkPaint::kFull_Hinting || m_paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)WTF::Partitions::fastMalloc( vdmxSize, WTF_HEAP_PROFILER_TYPE_NAME(SimpleFontData)); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; WTF::Partitions::fastFree(vdmxTable); } } #endif float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly* except: // - the adjustment of ascent/descent on Linux/Android // - metrics.fAscent and .fDesscent are not rounded to int for tiny fonts if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { // For tiny fonts, the rounding of fAscent and fDescent results in equal // baseline for different types of text baselines (crbug.com/338908). // Please see CanvasRenderingContext2D::getFontBaseline for the heuristic. if (subpixelAscentDescent && (-metrics.fAscent < 3 || -metrics.fAscent + metrics.fDescent < 2)) { ascent = -metrics.fAscent; descent = metrics.fDescent; } else { ascent = SkScalarRoundToScalar(-metrics.fAscent); descent = SkScalarRoundToScalar(metrics.fDescent); } #if OS(LINUX) || OS(ANDROID) // When subpixel positioning is enabled, if the descent is rounded down, the // descent part of the glyph may be truncated when displayed in a 'overflow: // hidden' container. To avoid that, borrow 1 unit from the ascent when // possible. // FIXME: This can be removed if sub-pixel ascent/descent is supported. if (platformData().getFontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } #if OS(MACOSX) // We are preserving this ascent hack to match Safari's ascent adjustment // in their SimpleFontDataMac.mm, for details see crbug.com/445830. // We need to adjust Times, Helvetica, and Courier to closely match the // vertical metrics of their Microsoft counterparts that are the de facto // web standard. The AppKit adjustment of 20% is too big and is // incorrectly added to line spacing, so we use a 15% adjustment instead // and add it to the ascent. DEFINE_STATIC_LOCAL(AtomicString, timesName, ("Times")); DEFINE_STATIC_LOCAL(AtomicString, helveticaName, ("Helvetica")); DEFINE_STATIC_LOCAL(AtomicString, courierName, ("Courier")); String familyName = m_platformData.fontFamilyName(); if (familyName == timesName || familyName == helveticaName || familyName == courierName) ascent += floorf(((ascent + descent) * 0.15f) + 0.5f); #endif m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; #if OS(MACOSX) // Mac OS CTFontGetXHeight reports the bounding box height of x, // including parts extending below the baseline and apparently no x-height // value from the OS/2 table. However, the CSS ex unit // expects only parts above the baseline, hence measuring the glyph: // http://www.w3.org/TR/css3-values/#ex-unit const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { FloatRect glyphBounds(boundsForGlyph(xGlyph)); // SkGlyph bounds, y down, based on rendering at (0,0). xHeight = -glyphBounds.y(); } #endif m_fontMetrics.setXHeight(xHeight); } else { xHeight = ascent * 0.56; // Best guess from Windows font metrics. m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setHasXHeight(false); } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (platformData().isVerticalAnyUpright() && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = face->getTableSize(vheaTag); size_t vorgSize = face->getTableSize(vorgTag); if ((vheaSize > 0) || (vorgSize > 0)) m_hasVerticalGlyphs = true; } // In WebKit/WebCore/platform/graphics/SimpleFontData.cpp, m_spaceWidth is // calculated for us, but we need to calculate m_maxCharWidth and // m_avgCharWidth in order for text entry widgets to be sized correctly. #if OS(WIN) m_maxCharWidth = SkScalarRoundToInt(metrics.fMaxCharWidth); // Older version of the DirectWrite API doesn't implement support for max // char width. Fall back on a multiple of the ascent. This is entirely // arbitrary but comes pretty close to the expected value in most cases. if (m_maxCharWidth < 1) m_maxCharWidth = ascent * 2; #elif OS(MACOSX) // FIXME: The current avg/max character width calculation is not ideal, // it should check either the OS2 table or, better yet, query FontMetrics. // Sadly FontMetrics provides incorrect data on Mac at the moment. // https://crbug.com/420901 m_maxCharWidth = std::max(m_avgCharWidth, m_fontMetrics.floatAscent()); #else // Better would be to rely on either fMaxCharWidth or fAveCharWidth. // skbug.com/3087 m_maxCharWidth = SkScalarRoundToInt(metrics.fXMax - metrics.fXMin); #endif #if !OS(MACOSX) if (metrics.fAvgCharWidth) { m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); } else { #endif m_avgCharWidth = xHeight; const Glyph xGlyph = glyphForCharacter('x'); if (xGlyph) { m_avgCharWidth = widthForGlyph(xGlyph); } #if !OS(MACOSX) } #endif if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
bool ConsumeGlyph(int32 index, uint32 charCode, const GlyphCache* glyph, FontCacheEntry* entry, double x, double y, double advanceX, double advanceY) { // "glyphBounds" is the bounds of the glyph transformed // by the x y location of the glyph along the base line, // it is therefor yet "untransformed" in case there is an // embedded transformation. const agg::rect_i& r = glyph->bounds; IntRect glyphBounds(int32(r.x1 + x), int32(r.y1 + y - 1), int32(r.x2 + x + 1), int32(r.y2 + y + 1)); // NOTE: "-1"/"+1" converts the glyph bounding box from pixel // indices to pixel area coordinates // track bounding box if (glyphBounds.IsValid()) fBounds = fBounds | glyphBounds; // render the glyph if this is not a dry run if (!fDryRun) { // init the fontmanager's embedded adaptors // NOTE: The initialization for the "location" of // the glyph is different depending on whether we // deal with non-(rotated/sheared) text, in which // case we have a native FT bitmap. For rotated or // sheared text, we use AGG vector outlines and // a transformation pipeline, which will be applied // _after_ we retrieve the outline, and that's why // we simply pass x and y, which are untransformed. // "glyphBounds" is now transformed into screen coords // in order to stop drawing when we are already outside // of the clipping frame if (glyph->data_type != glyph_data_outline) { // we cannot use the transformation pipeline double transformedX = x + fTransformOffset.x; double transformedY = y + fTransformOffset.y; entry->InitAdaptors(glyph, transformedX, transformedY, fRenderer.fMonoAdaptor, fRenderer.fGray8Adaptor, fRenderer.fPathAdaptor); glyphBounds.OffsetBy(fTransformOffset); } else { entry->InitAdaptors(glyph, x, y, fRenderer.fMonoAdaptor, fRenderer.fGray8Adaptor, fRenderer.fPathAdaptor); int32 falseBoldWidth = (int32)fRenderer.fContour.width(); if (falseBoldWidth != 0) glyphBounds.InsetBy(-falseBoldWidth, -falseBoldWidth); // TODO: not correct! this is later used for clipping, // but it doesn't get the rect right glyphBounds = fTransform.TransformBounds(glyphBounds); } if (fClippingFrame.Intersects(glyphBounds)) { switch (glyph->data_type) { case glyph_data_mono: agg::render_scanlines(fRenderer.fMonoAdaptor, fRenderer.fMonoScanline, fRenderer.fBinRenderer); break; case glyph_data_gray8: if (fRenderer.fMaskedScanline != NULL) { agg::render_scanlines(fRenderer.fGray8Adaptor, *fRenderer.fMaskedScanline, fRenderer.fSolidRenderer); } else { agg::render_scanlines(fRenderer.fGray8Adaptor, fRenderer.fGray8Scanline, fRenderer.fSolidRenderer); } break; case glyph_data_subpix: // TODO: Handle alpha mask (fRenderer.fMaskedScanline) agg::render_scanlines(fRenderer.fGray8Adaptor, fRenderer.fGray8Scanline, fRenderer.fSubpixRenderer); break; case glyph_data_outline: { fVector = true; if (fSubpixelAntiAliased) { if (fRenderer.fContour.width() == 0.0) { fRenderer.fSubpixRasterizer.add_path( fTransformedGlyph); } else { fRenderer.fSubpixRasterizer.add_path( fTransformedContour); } } else { if (fRenderer.fContour.width() == 0.0) { fRenderer.fRasterizer.add_path( fTransformedGlyph); } else { fRenderer.fRasterizer.add_path( fTransformedContour); } } #if SHOW_GLYPH_BOUNDS agg::path_storage p; p.move_to(glyphBounds.left + 0.5, glyphBounds.top + 0.5); p.line_to(glyphBounds.right + 0.5, glyphBounds.top + 0.5); p.line_to(glyphBounds.right + 0.5, glyphBounds.bottom + 0.5); p.line_to(glyphBounds.left + 0.5, glyphBounds.bottom + 0.5); p.close_polygon(); agg::conv_stroke<agg::path_storage> ps(p); ps.width(1.0); if (fSubpixelAntiAliased) { fRenderer.fSubpixRasterizer.add_path(ps); } else { fRenderer.fRasterizer.add_path(ps); } #endif break; } default: break; } } } return true; }