/** Return the number of tables, or if this is a TTC (collection), return the number of tables in the first element of the collection. In either case, if offsetToDir is not-null, set it to the offset to the beginning of the table headers (SkSFNTDirEntry), relative to the start of the stream. On an error, return 0 for number of tables, and ignore offsetToDir */ static int count_tables(SkStream* stream, size_t* offsetToDir = NULL) { SkSharedTTHeader shared; if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { return 0; } // by default, SkSFNTHeader is at the start of the stream size_t offset = 0; // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { if (shared.fCollection.fNumOffsets == 0) { return 0; } // this is the offset to the first local SkSFNTHeader offset = SkEndian_SwapBE32(shared.fCollection.fOffset0); stream->rewind(); if (stream->skip(offset) != offset) { return 0; } if (stream->read(&shared, sizeof(shared)) != sizeof(shared)) { return 0; } } if (offsetToDir) { // add the size of the header, so we will point to the DirEntries *offsetToDir = offset + sizeof(SkSFNTHeader); } return SkEndian_SwapBE16(shared.fSingle.fNumTables); }
bool valid() const { return_if_false(fSize >= kICCHeaderSize, "Size is too small"); uint8_t majorVersion = fVersion >> 24; return_if_false(majorVersion <= 4, "Unsupported version"); // These are the three basic classes of profiles that we might expect to see embedded // in images. Four additional classes exist, but they generally are used as a convenient // way for CMMs to store calculated transforms. const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); return_if_false(fProfileClass == kDisplay_Profile || fProfileClass == kInput_Profile || fProfileClass == kOutput_Profile, "Unsupported profile"); // TODO (msarett): // All the profiles we've tested so far use RGB as the input color space. return_if_false(fInputColorSpace == kRGB_ColorSpace, "Unsupported color space"); // TODO (msarett): // All the profiles we've tested so far use XYZ as the profile connection space. const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); // TODO (msarett): // Should we treat different rendering intents differently? // Valid rendering intents include kPerceptual (0), kRelative (1), // kSaturation (2), and kAbsolute (3). return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); return_if_false(color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[0]), 0.96420f) && color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[1]), 1.00000f) && color_space_almost_equal(SkFixedToFloat(fIlluminantXYZ[2]), 0.82491f), "Illuminant must be D50"); return_if_false(fTagCount <= 100, "Too many tags"); return true; }
bool valid() const { // TODO (msarett): // For now it's nice to fail loudly on invalid inputs. But, can we // recover from some of these errors? return_if_false(fSize >= kICCHeaderSize, "Size is too small"); uint8_t majorVersion = fVersion >> 24; return_if_false(majorVersion <= 4, "Unsupported version"); const uint32_t kDisplay_Profile = SkSetFourByteTag('m', 'n', 't', 'r'); const uint32_t kInput_Profile = SkSetFourByteTag('s', 'c', 'n', 'r'); const uint32_t kOutput_Profile = SkSetFourByteTag('p', 'r', 't', 'r'); // TODO (msarett): // Should we also support DeviceLink, ColorSpace, Abstract, or NamedColor? return_if_false(fClassProfile == kDisplay_Profile || fClassProfile == kInput_Profile || fClassProfile == kOutput_Profile, "Unsupported class profile"); // TODO (msarett): // There are many more color spaces that we could try to support. return_if_false(fColorSpace == kRGB_ColorSpace || fColorSpace == kGray_ColorSpace, "Unsupported color space"); const uint32_t kXYZ_PCSSpace = SkSetFourByteTag('X', 'Y', 'Z', ' '); // TODO (msarett): // Can we support PCS LAB as well? return_if_false(fPCS == kXYZ_PCSSpace, "Unsupported PCS space"); return_if_false(fSignature == SkSetFourByteTag('a', 'c', 's', 'p'), "Bad signature"); // TODO (msarett): // Should we treat different rendering intents differently? // Valid rendering intents include kPerceptual (0), kRelative (1), // kSaturation (2), and kAbsolute (3). return_if_false(fRenderingIntent <= 3, "Bad rendering intent"); return_if_false(fTagCount <= 100, "Too many tags"); return true; }
int SkFontStream::CountTTCEntries(SkStream* stream) { stream->rewind(); SkSharedTTHeader shared; if (!read(stream, &shared, sizeof(shared))) { return 0; } // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(shared.fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { return SkEndian_SwapBE32(shared.fCollection.fNumOffsets); } else { return 1; // normal 'sfnt' has 1 dir entry } }
void onDraw(SkCanvas* canvas) override { SkPaint paint; paint.setAntiAlias(true); paint.setLCDRenderText(true); SkAutoTUnref<SkFontMgr> fontMgr(SkFontMgr::RefDefault()); SkAutoTDelete<SkStreamAsset> distortable(GetResourceAsStream("/fonts/Distortable.ttf")); if (!distortable) { return; } const char* text = "abc"; const size_t textLen = strlen(text); for (int j = 0; j < 2; ++j) { for (int i = 0; i < 5; ++i) { SkScalar x = SkIntToScalar(10); SkScalar y = SkIntToScalar(20); SkFourByteTag tag = SkSetFourByteTag('w','g','h','t'); SkScalar styleValue = SkDoubleToScalar(0.5 + (5*j + i) * ((2.0 - 0.5) / (2 * 5))); SkFontMgr::FontParameters::Axis axes[] = { { tag, styleValue } }; paint.setTypeface(sk_sp<SkTypeface>(fontMgr->createFromStream( distortable->duplicate(), SkFontMgr::FontParameters().setAxes(axes, 1)))); SkAutoCanvasRestore acr(canvas, true); canvas->translate(SkIntToScalar(30 + i * 100), SkIntToScalar(20)); rotate_about(canvas, SkIntToScalar(i * 5), x, y * 10); { SkPaint p; p.setAntiAlias(true); SkRect r; r.set(x - SkIntToScalar(3), SkIntToScalar(15), x - SkIntToScalar(1), SkIntToScalar(280)); canvas->drawRect(r, p); } for (int ps = 6; ps <= 22; ps++) { paint.setTextSize(SkIntToScalar(ps)); canvas->drawText(text, textLen, x, y, paint); y += paint.getFontMetrics(nullptr); } } canvas->translate(0, SkIntToScalar(360)); paint.setSubpixelText(true); } }
/** Return the number of tables, or if this is a TTC (collection), return the number of tables in the first element of the collection. In either case, if offsetToDir is not-null, set it to the offset to the beginning of the table headers (SkSFNTDirEntry), relative to the start of the stream. On an error, return 0 for number of tables, and ignore offsetToDir */ static int count_tables(SkStream* stream, int ttcIndex, size_t* offsetToDir) { SkASSERT(ttcIndex >= 0); SkAutoSMalloc<1024> storage(sizeof(SkSharedTTHeader)); SkSharedTTHeader* header = (SkSharedTTHeader*)storage.get(); if (!read(stream, header, sizeof(SkSharedTTHeader))) { return 0; } // by default, SkSFNTHeader is at the start of the stream size_t offset = 0; // if we're really a collection, the first 4-bytes will be 'ttcf' uint32_t tag = SkEndian_SwapBE32(header->fCollection.fTag); if (SkSetFourByteTag('t', 't', 'c', 'f') == tag) { unsigned count = SkEndian_SwapBE32(header->fCollection.fNumOffsets); if ((unsigned)ttcIndex >= count) { return 0; } if (ttcIndex > 0) { // need to read more of the shared header stream->rewind(); size_t amount = sizeof(SkSharedTTHeader) + ttcIndex * sizeof(uint32_t); header = (SkSharedTTHeader*)storage.reset(amount); if (!read(stream, header, amount)) { return 0; } } // this is the offset to the local SkSFNTHeader offset = SkEndian_SwapBE32((&header->fCollection.fOffset0)[ttcIndex]); stream->rewind(); if (!skip(stream, offset)) { return 0; } if (!read(stream, header, sizeof(SkSFNTHeader))) { return 0; } } if (offsetToDir) { // add the size of the header, so we will point to the DirEntries *offsetToDir = offset + sizeof(SkSFNTHeader); } return SkEndian_SwapBE16(header->fSingle.fNumTables); }
SkOTUtils::LocalizedStrings_NameTable* SkOTUtils::LocalizedStrings_NameTable::CreateForFamilyNames(const SkTypeface& typeface) { static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e'); size_t nameTableSize = typeface.getTableSize(nameTag); if (0 == nameTableSize) { return NULL; } SkAutoTDeleteArray<uint8_t> nameTableData(new uint8_t[nameTableSize]); size_t copied = typeface.getTableData(nameTag, 0, nameTableSize, nameTableData.get()); if (copied != nameTableSize) { return NULL; } return new SkOTUtils::LocalizedStrings_NameTable((SkOTTableName*)nameTableData.detach(), SkOTUtils::LocalizedStrings_NameTable::familyNameTypes, SK_ARRAY_COUNT(SkOTUtils::LocalizedStrings_NameTable::familyNameTypes)); }
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); }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); const SkFontID fontID = m_platformData.uniqueID(); static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; int vdmxAscent, vdmxDescent; bool isVDMXValid = false; size_t vdmxSize = SkFontHost::GetTableSize(fontID, vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize); if (vdmxTable && SkFontHost::GetTableData(fontID, vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; fastFree(vdmxTable); } float ascent; float descent; // Beware those who step here: This code is designed to match Win32 font // metrics *exactly*. if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { SkScalar height = -metrics.fAscent + metrics.fDescent + metrics.fLeading; ascent = SkScalarRound(-metrics.fAscent); descent = SkScalarRound(height) - ascent; } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) xHeight = metrics.fXHeight; else { // hack taken from the Windows port xHeight = ascent * 0.56f; } float lineGap = SkScalarToFloat(metrics.fLeading); m_fontMetrics.setLineGap(lineGap); m_fontMetrics.setXHeight(xHeight); m_fontMetrics.setLineSpacing(lroundf(ascent) + lroundf(descent) + lroundf(lineGap)); if (platformData().orientation() == Vertical && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = SkFontHost::GetTableSize(fontID, vheaTag); size_t vorgSize = SkFontHost::GetTableSize(fontID, 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. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRound(xRange * SkScalarRound(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 x_char = 'x'; const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(x_char).glyph; if (xGlyph) m_avgCharWidth = widthForGlyph(xGlyph); } } }
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); SkTypeface* face = 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 GDI, the metrics will already have taken this into // account (as needed). 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 (!paint.isAutohinted() && (paint.getHinting() == SkPaint::kFull_Hinting || paint.getHinting() == SkPaint::kNormal_Hinting)) { size_t vdmxSize = face->getTableSize(vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*)fastMalloc(vdmxSize); if (vdmxTable && face->getTableData(vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; 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). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { ascent = SkScalarRoundToInt(-metrics.fAscent); descent = SkScalarRoundToInt(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().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; 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)); SkScalar underlineThickness, underlinePosition; if (metrics.hasUnderlineThickness(&underlineThickness) && metrics.hasUnderlinePosition(&underlinePosition)) { m_fontMetrics.setUnderlineThickness(SkScalarToFloat(underlineThickness)); m_fontMetrics.setUnderlinePosition(SkScalarToFloat(-underlinePosition)); } if (platformData().orientation() == Vertical && !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. // FIXME: This seems incorrect and should probably use fMaxCharWidth as // the code path above. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRoundToInt(xRange * SkScalarRoundToInt(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRoundToInt(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 xChar = 'x'; const Glyph xGlyph = glyphPageZero->glyphForCharacter(xChar); if (xGlyph) { // In widthForGlyph(), xGlyph will be compared with // m_zeroWidthSpaceGlyph, which isn't initialized yet here. // Initialize it with zero to make sure widthForGlyph() returns // the right width. m_zeroWidthSpaceGlyph = 0; m_avgCharWidth = widthForGlyph(xGlyph); } } } if (int unitsPerEm = face->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }
static void test_systemfonts(skiatest::Reporter* reporter, bool verbose) { static const SkFontTableTag nameTag = SkSetFourByteTag('n','a','m','e'); sk_sp<SkFontMgr> fm(SkFontMgr::RefDefault()); int count = SkMin32(fm->countFamilies(), MAX_FAMILIES); for (int i = 0; i < count; ++i) { sk_sp<SkFontStyleSet> set(fm->createStyleSet(i)); for (int j = 0; j < set->count(); ++j) { SkString sname; SkFontStyle fs; set->getStyle(j, &fs, &sname); sk_sp<SkTypeface> typeface(set->createTypeface(j)); SkString familyName; typeface->getFamilyName(&familyName); if (verbose) { SkDebugf("[%s]\n", familyName.c_str()); } sk_sp<SkTypeface::LocalizedStrings> familyNamesIter( typeface->createFamilyNameIterator()); SkTypeface::LocalizedString familyNameLocalized; while (familyNamesIter->next(&familyNameLocalized)) { if (verbose) { SkDebugf("(%s) <%s>\n", familyNameLocalized.fString.c_str(), familyNameLocalized.fLanguage.c_str()); } } size_t nameTableSize = typeface->getTableSize(nameTag); if (0 == nameTableSize) { continue; } SkAutoTMalloc<uint8_t> nameTableData(nameTableSize); size_t copied = typeface->getTableData(nameTag, 0, nameTableSize, nameTableData.get()); if (copied != nameTableSize) { continue; } SkOTTableName::Iterator::Record record; SkOTTableName::Iterator familyNameIter(*((SkOTTableName*)nameTableData.get()), SkOTTableName::Record::NameID::Predefined::FontFamilyName); while (familyNameIter.next(record)) { REPORTER_ASSERT_MESSAGE(reporter, SkOTTableName::Record::NameID::Predefined::FontFamilyName == record.type, "Requested family name, got something else." ); if (verbose) { SkDebugf("{%s} <%s>\n", record.name.c_str(), record.language.c_str()); } } SkOTTableName::Iterator styleNameIter(*((SkOTTableName*)nameTableData.get()), SkOTTableName::Record::NameID::Predefined::FontSubfamilyName); while (styleNameIter.next(record)) { REPORTER_ASSERT_MESSAGE(reporter, SkOTTableName::Record::NameID::Predefined::FontSubfamilyName == record.type, "Requested subfamily name, got something else." ); if (verbose) { SkDebugf("{{%s}} <%s>\n", record.name.c_str(), record.language.c_str()); } } if (verbose) { SkDebugf("\n"); } } } }
/** * Use this for bitmapcache and mipmapcache entries. */ uint64_t SkMakeResourceCacheSharedIDForBitmap(uint32_t bitmapGenID) { uint64_t sharedID = SkSetFourByteTag('b', 'm', 'a', 'p'); return (sharedID << 32) | bitmapGenID; }
return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3]; } static int32_t read_big_endian_int(const uint8_t* ptr) { return (int32_t) read_big_endian_uint(ptr); } // This is equal to the header size according to the ICC specification (128) // plus the size of the tag count (4). We include the tag count since we // always require it to be present anyway. static const size_t kICCHeaderSize = 132; // Contains a signature (4), offset (4), and size (4). static const size_t kICCTagTableEntrySize = 12; static const uint32_t kRGB_ColorSpace = SkSetFourByteTag('R', 'G', 'B', ' '); struct ICCProfileHeader { uint32_t fSize; // No reason to care about the preferred color management module (ex: Adobe, Apple, etc.). // We're always going to use this one. uint32_t fCMMType_ignored; uint32_t fVersion; uint32_t fProfileClass; uint32_t fInputColorSpace; uint32_t fPCS; uint32_t fDateTime_ignored[3]; uint32_t fSignature;
void SimpleFontData::platformInit() { if (!m_platformData.size()) { m_fontMetrics.reset(); m_avgCharWidth = 0; m_maxCharWidth = 0; return; } SkPaint paint; SkPaint::FontMetrics metrics; m_platformData.setupPaint(&paint); paint.getFontMetrics(&metrics); const SkFontID fontID = m_platformData.uniqueID(); static const uint32_t vdmxTag = SkSetFourByteTag('V', 'D', 'M', 'X'); int pixelSize = m_platformData.size() + 0.5; int vdmxAscent, vdmxDescent; bool isVDMXValid = false; size_t vdmxSize = SkFontHost::GetTableSize(fontID, vdmxTag); if (vdmxSize && vdmxSize < maxVDMXTableSize) { uint8_t* vdmxTable = (uint8_t*) fastMalloc(vdmxSize); if (vdmxTable && SkFontHost::GetTableData(fontID, vdmxTag, 0, vdmxSize, vdmxTable) == vdmxSize && parseVDMX(&vdmxAscent, &vdmxDescent, vdmxTable, vdmxSize, pixelSize)) isVDMXValid = true; fastFree(vdmxTable); } 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). if (isVDMXValid) { ascent = vdmxAscent; descent = -vdmxDescent; } else { SkScalar height = -metrics.fAscent + metrics.fDescent + metrics.fLeading; ascent = SkScalarRound(-metrics.fAscent); descent = SkScalarRound(height) - ascent; #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().fontRenderStyle().useSubpixelPositioning && descent < SkScalarToFloat(metrics.fDescent) && ascent >= 1) { ++descent; --ascent; } #endif } m_fontMetrics.setAscent(ascent); m_fontMetrics.setDescent(descent); float xHeight; if (metrics.fXHeight) { xHeight = metrics.fXHeight; 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().orientation() == Vertical && !isTextOrientationFallback()) { static const uint32_t vheaTag = SkSetFourByteTag('v', 'h', 'e', 'a'); static const uint32_t vorgTag = SkSetFourByteTag('V', 'O', 'R', 'G'); size_t vheaSize = SkFontHost::GetTableSize(fontID, vheaTag); size_t vorgSize = SkFontHost::GetTableSize(fontID, 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. SkScalar xRange = metrics.fXMax - metrics.fXMin; m_maxCharWidth = SkScalarRound(xRange * SkScalarRound(m_platformData.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); else { m_avgCharWidth = xHeight; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (glyphPageZero) { static const UChar32 xChar = 'x'; const Glyph xGlyph = glyphPageZero->glyphDataForCharacter(xChar).glyph; if (xGlyph) { // In widthForGlyph(), xGlyph will be compared with // m_zeroWidthSpaceGlyph, which isn't initialized yet here. // Initialize it with zero to make sure widthForGlyph() returns // the right width. m_zeroWidthSpaceGlyph = 0; m_avgCharWidth = widthForGlyph(xGlyph); } } } if (int unitsPerEm = paint.getTypeface()->getUnitsPerEm()) m_fontMetrics.setUnitsPerEm(unitsPerEm); }