void SimpleFontData::platformInit() { FS_FIXED ascender = 0; FS_FIXED descender = 0; FS_FIXED leading = 0; FsAscDescLeadSource source; FS_LONG result; if (m_platformData.size() > 0) { // FIXME: hack! FS_get_ascender_descender_leading() returns ERR_NO_CURRENT_SFNT when size is 0, even though we called FS_set_scale and m_platformData.font()->cur_sfnt is not 0 result = FS_get_ascender_descender_leading(m_platformData.font(), &ascender, &descender, &leading, &source); ASSERT_UNUSED(result, result == SUCCESS); } m_fontMetrics.setAscent(FS_ROUND(ascender)); m_fontMetrics.setDescent(FS_ROUND(descender)); m_fontMetrics.setLineGap(iTypeFixedToFloat(leading)); m_fontMetrics.setLineSpacing(lroundf(m_fontMetrics.ascent()) + lroundf(m_fontMetrics.descent()) + lroundf(m_fontMetrics.lineGap())); FONT_METRICS metrics; result = FS_font_metrics(m_platformData.font(), &metrics); ASSERT_UNUSED(result, result == SUCCESS); // m_fontMetrics.setUnitsPerEm(FS_get_design_units(m_platformData().font())); m_fontMetrics.setUnitsPerEm(metrics.unitsPerEm); FS_USHORT xRange = metrics.font_bbox.xMax - metrics.font_bbox.xMin; m_maxCharWidth = roundf((xRange * roundf(m_platformData.size())) / metrics.unitsPerEm); TTF_OS2 os2; if (FS_get_table_structure(m_platformData.font(), TAG_OS2, &os2) == SUCCESS && os2.sxHeight && os2.xAvgCharWidth) { FS_USHORT yppem = m_platformData.font()->lpm; m_fontMetrics.setXHeight(static_cast<float>(os2.sxHeight) * yppem / metrics.unitsPerEm); m_avgCharWidth = static_cast<float>(os2.xAvgCharWidth) * yppem / metrics.unitsPerEm; } else { // HACK m_fontMetrics.setXHeight(m_fontMetrics.ascent() * 0.56); m_fontMetrics.setHasXHeight(false); m_avgCharWidth = m_fontMetrics.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 (m_platformData.orientation() == Vertical && !isTextOrientationFallback()) m_hasVerticalGlyphs = FS_get_table(m_platformData.font(), openTypeTag('v', 'h', 'e', 'a'), TBL_QUERY, 0) || FS_get_table(m_platformData.font(), openTypeTag('V', 'O', 'R', 'G'), TBL_QUERY, 0); }
static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(UChar32 character, NonCJKGlyphOrientation orientation, GlyphData& data, GlyphPage* page, unsigned pageNumber) { if (orientation == NonCJKGlyphOrientationUpright || shouldIgnoreRotation(character)) { RefPtr<SimpleFontData> uprightFontData = data.fontData->uprightOrientationFontData(); GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData.get(), pageNumber); GlyphPage* uprightPage = uprightNode->page(); if (uprightPage) { GlyphData uprightData = uprightPage->glyphDataForCharacter(character); // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright. if (data.glyph == uprightData.glyph) return std::make_pair(data, page); // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that // glyph, so we fall back to the upright data and use the horizontal glyph. if (uprightData.fontData) return std::make_pair(uprightData, uprightPage); } } else if (orientation == NonCJKGlyphOrientationVerticalRight) { RefPtr<SimpleFontData> verticalRightFontData = data.fontData->verticalRightOrientationFontData(); GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData.get(), pageNumber); GlyphPage* verticalRightPage = verticalRightNode->page(); if (verticalRightPage) { GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(character); // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked // into it. if (data.glyph != verticalRightData.glyph) return std::make_pair(data, page); // The glyphs are identical, meaning that we should just use the horizontal glyph. if (verticalRightData.fontData) return std::make_pair(verticalRightData, verticalRightPage); } } return std::make_pair(data, page); }
bool Font::primaryFontHasGlyphForCharacter(UChar32 character) const { unsigned pageNumber = (character / GlyphPage::size); GlyphPageTreeNode* node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); GlyphPage* page = node->page(); return page && page->fontDataForCharacter(character); }
static inline std::pair<GlyphData, GlyphPage*> glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(UChar32 character, GlyphData& data, GlyphPage* page, unsigned pageNumber) { RefPtr<SimpleFontData> nonItalicFontData = data.fontData->nonSyntheticItalicFontData(); GlyphPageTreeNode* nonItalicNode = GlyphPageTreeNode::getRootChild(nonItalicFontData.get(), pageNumber); GlyphPage* nonItalicPage = nonItalicNode->page(); if (nonItalicPage) { GlyphData nonItalicData = nonItalicPage->glyphDataForCharacter(character); if (nonItalicData.fontData) return std::make_pair(nonItalicData, nonItalicPage); } return std::make_pair(data, page); }
void SimpleFontData::platformInit() { m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; if (m_platformData.useGDI()) return initGDIFont(); CGFontRef font = m_platformData.cgFont(); int iAscent = CGFontGetAscent(font); int iDescent = CGFontGetDescent(font); int iLineGap = CGFontGetLeading(font); unsigned unitsPerEm = CGFontGetUnitsPerEm(font); float pointSize = m_platformData.size(); float fAscent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize; float fDescent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize; float fLineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize; if (!isCustomFont()) { HDC dc = GetDC(0); HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); int faceLength = GetTextFace(dc, 0, 0); Vector<WCHAR> faceName(faceLength); GetTextFace(dc, faceLength, faceName.data()); m_isSystemFont = !wcscmp(faceName.data(), L"Lucida Grande"); SelectObject(dc, oldFont); ReleaseDC(0, dc); fAscent = ascentConsideringMacAscentHack(faceName.data(), fAscent, fDescent); } m_fontMetrics.setAscent(fAscent); m_fontMetrics.setDescent(fDescent); m_fontMetrics.setLineGap(fLineGap); m_fontMetrics.setLineSpacing(lroundf(fAscent) + lroundf(fDescent) + lroundf(fLineGap)); GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); Glyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0; if (xGlyph) { // Measure the actual character "x", since it's possible for it to extend below the baseline, and we need the // reported x-height to only include the portion of the glyph that is above the baseline. CGRect xBox; CGFontGetGlyphBBoxes(font, &xGlyph, 1, &xBox); m_fontMetrics.setXHeight(scaleEmToUnits(CGRectGetMaxY(xBox), unitsPerEm) * pointSize); } else { int iXHeight = CGFontGetXHeight(font); m_fontMetrics.setXHeight(scaleEmToUnits(iXHeight, unitsPerEm) * pointSize); } m_fontMetrics.setUnitsPerEm(unitsPerEm); }
void SimpleFontData::platformInit() { SkPaint paint; SkPaint::FontMetrics metrics; m_font.setupPaint(&paint); paint.getFontMetrics(&metrics); // Beware those who step here: This code is designed to match Win32 font // metrics *exactly*. if (metrics.fVDMXMetricsValid) { m_ascent = metrics.fVDMXAscent; m_descent = metrics.fVDMXDescent; } else { m_ascent = SkScalarRound(-metrics.fAscent); m_descent = SkScalarRound(metrics.fHeight) - m_ascent; } if (metrics.fXHeight) m_xHeight = metrics.fXHeight; else { // hack taken from the Windows port m_xHeight = static_cast<float>(m_ascent) * 0.56; } m_lineGap = SkScalarRound(metrics.fLeading); m_lineSpacing = m_ascent + m_descent + m_lineGap; // 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. m_maxCharWidth = SkScalarRound(metrics.fXRange * SkScalarRound(m_font.size())); if (metrics.fAvgCharWidth) m_avgCharWidth = SkScalarRound(metrics.fAvgCharWidth); else { m_avgCharWidth = m_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); } } }
// Estimates of avgCharWidth and maxCharWidth for platforms that don't support accessing these values from the font. void SimpleFontData::initCharWidths() { GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); // Treat the width of a '0' as the avgCharWidth. if (m_avgCharWidth <= 0.f && glyphPageZero) { static const UChar32 digitZeroChar = '0'; Glyph digitZeroGlyph = glyphPageZero->glyphDataForCharacter(digitZeroChar).glyph; if (digitZeroGlyph) m_avgCharWidth = widthForGlyph(digitZeroGlyph); } // If we can't retrieve the width of a '0', fall back to the x height. if (m_avgCharWidth <= 0.f) m_avgCharWidth = m_fontMetrics.xHeight(); if (m_maxCharWidth <= 0.f) m_maxCharWidth = max(m_avgCharWidth, m_fontMetrics.floatAscent()); }
FontData::FontData(const FontPlatformData& f) : m_font(f) , m_treatAsFixedPitch(false) , m_smallCapsFontData(0) { platformInit(); GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (!glyphPageZero) { LOG_ERROR("Failed to get glyph page zero."); m_spaceGlyph = 0; m_spaceWidth = 0; m_adjustedSpaceWidth = 0; determinePitch(); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; return; } // Nasty hack to determine if we should round or ceil space widths. // If the font is monospace or fake monospace we ceil to ensure that // every character and the space are the same width. Otherwise we round. m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph; float width = widthForGlyph(m_spaceGlyph); m_spaceWidth = width; determinePitch(); m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. // See <http://bugs.webkit.org/show_bug.cgi?id=13178> // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, // are mapped to the ZERO WIDTH SPACE glyph. Glyph zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph; if (zeroWidthSpaceGlyph) { if (zeroWidthSpaceGlyph != m_spaceGlyph) m_glyphToWidthMap.setWidthForGlyph(zeroWidthSpaceGlyph, 0); else LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width not overridden."); } m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
void SimpleFontData::platformGlyphInit() { GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (!glyphPageZero) { WTF_LOG_ERROR("Failed to get glyph page zero."); m_spaceGlyph = 0; m_spaceWidth = 0; m_zeroGlyph = 0; m_adjustedSpaceWidth = 0; determinePitch(); m_zeroWidthSpaceGlyph = 0; m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; return; } m_zeroWidthSpaceGlyph = glyphPageZero->glyphForCharacter(0); // Nasty hack to determine if we should round or ceil space widths. // If the font is monospace or fake monospace we ceil to ensure that // every character and the space are the same width. Otherwise we round. m_spaceGlyph = glyphPageZero->glyphForCharacter(' '); float width = widthForGlyph(m_spaceGlyph); m_spaceWidth = width; m_zeroGlyph = glyphPageZero->glyphForCharacter('0'); m_fontMetrics.setZeroWidth(widthForGlyph(m_zeroGlyph)); determinePitch(); m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. // See <http://bugs.webkit.org/show_bug.cgi?id=13178> // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, // are mapped to the ZERO WIDTH SPACE glyph. if (m_zeroWidthSpaceGlyph == m_spaceGlyph) { m_zeroWidthSpaceGlyph = 0; WTF_LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width will not be overridden."); } m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
static bool fillGlyphPage(GlyphPage& pageToFill, UChar* buffer, unsigned bufferLength, const Font* font) { #if ENABLE(SVG_FONTS) if (auto* svgData = font->svgData()) return svgData->fillSVGGlyphPage(&pageToFill, buffer, bufferLength, font); #endif bool hasGlyphs = pageToFill.fill(buffer, bufferLength, font); #if ENABLE(OPENTYPE_VERTICAL) if (hasGlyphs && font->verticalData()) font->verticalData()->substituteWithVerticalGlyphs(font, &pageToFill); #endif return hasGlyphs; }
void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber) { ASSERT(!m_page); ASSERT(fontData); // This function must not be called for the root of the tree, because that // level does not contain any glyphs. ASSERT(m_level > 0 && m_parent); if (m_level == 1) { // Children of the root hold pure pages. These will cover only one // font data's glyphs, and will have glyph index 0 if the font data does not // contain the glyph. initializePurePage(fontData, pageNumber); return; } // The parent's page will be 0 if we are level one or the parent's font data // did not contain any glyphs for that page. GlyphPage* parentPage = m_parent->page(); if (parentPage && parentPage->owner() != m_parent) { // The page we're overriding may not be owned by our parent node. // This happens when our parent node provides no useful overrides // and just copies the pointer to an already-existing page (see // below). // // We want our override to be shared by all nodes that reference // that page to avoid duplication, and so standardize on having the // page's owner collect all the overrides. Call getChild on the // page owner with the desired font data (this will populate // the page) and then reference it. m_page = static_cast<GlyphPageTreeNode*>(parentPage->owner())->getNormalChild(fontData, pageNumber)->page(); return; } initializeOverridePage(fontData, pageNumber); }
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); }
void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber) { ASSERT(!m_page); // This function must not be called for the root of the tree, because that // level does not contain any glyphs. ASSERT(m_level > 0 && m_parent); // The parent's page will be 0 if we are level one or the parent's font data // did not contain any glyphs for that page. GlyphPage* parentPage = m_parent->page(); // NULL FontData means we're being asked for the system fallback font. if (fontData) { if (m_level == 1) { // Children of the root hold pure pages. These will cover only one // font data's glyphs, and will have glyph index 0 if the font data does not // contain the glyph. unsigned start = pageNumber * GlyphPage::size; UChar buffer[GlyphPage::size * 2 + 2]; unsigned bufferLength; unsigned i; // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for. if (start < 0x10000) { bufferLength = GlyphPage::size; for (i = 0; i < GlyphPage::size; i++) buffer[i] = start + i; if (start == 0) { // Control characters must not render at all. for (i = 0; i < 0x20; ++i) buffer[i] = zeroWidthSpace; for (i = 0x7F; i < 0xA0; i++) buffer[i] = zeroWidthSpace; buffer[softHyphen] = zeroWidthSpace; // \n, \t, and nonbreaking space must render as a space. buffer[newlineCharacter] = space; buffer[characterTabulation] = space; buffer[noBreakSpace] = space; } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) { // LRM, RLM, LRE, RLE, ZWNJ, ZWJ, and PDF must not render at all. buffer[leftToRightMark - start] = zeroWidthSpace; buffer[rightToLeftMark - start] = zeroWidthSpace; buffer[leftToRightEmbed - start] = zeroWidthSpace; buffer[rightToLeftEmbed - start] = zeroWidthSpace; buffer[leftToRightOverride - start] = zeroWidthSpace; buffer[rightToLeftOverride - start] = zeroWidthSpace; buffer[zeroWidthNonJoiner - start] = zeroWidthSpace; buffer[zeroWidthJoiner - start] = zeroWidthSpace; buffer[popDirectionalFormatting - start] = zeroWidthSpace; } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) { // Object replacement character must not render at all. buffer[objectReplacementCharacter - start] = zeroWidthSpace; } else if (start == (zeroWidthNoBreakSpace & ~(GlyphPage::size - 1))) { // ZWNBS/BOM must not render at all. buffer[zeroWidthNoBreakSpace - start] = zeroWidthSpace; } } else { bufferLength = GlyphPage::size * 2; for (i = 0; i < GlyphPage::size; i++) { int c = i + start; buffer[i * 2] = U16_LEAD(c); buffer[i * 2 + 1] = U16_TRAIL(c); } } // Now that we have a buffer full of characters, we want to get back an array // of glyph indices. This part involves calling into the platform-specific // routine of our glyph map for actually filling in the page with the glyphs. // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data // for only 128 out of 256 characters. bool haveGlyphs; if (!fontData->isSegmented()) { m_page = GlyphPage::createForSingleFontData(this, toSimpleFontData(fontData)); haveGlyphs = fill(m_page.get(), 0, GlyphPage::size, buffer, bufferLength, toSimpleFontData(fontData)); } else { m_page = GlyphPage::createForMixedFontData(this); haveGlyphs = false; const SegmentedFontData* segmentedFontData = toSegmentedFontData(fontData); for (int i = segmentedFontData->numRanges() - 1; i >= 0; i--) { const FontDataRange& range = segmentedFontData->rangeAt(i); // all this casting is to ensure all the parameters to min and max have the same type, // to avoid ambiguous template parameter errors on Windows int from = max(0, static_cast<int>(range.from()) - static_cast<int>(start)); int to = 1 + min(static_cast<int>(range.to()) - static_cast<int>(start), static_cast<int>(GlyphPage::size) - 1); if (from >= static_cast<int>(GlyphPage::size) || to <= 0) continue; // If this is a custom font needs to be loaded, do not fill // the page so that font fallback is used while loading. RefPtr<CustomFontData> customData = range.fontData()->customFontData(); if (customData && customData->isLoadingFallback()) { for (int j = from; j < to; j++) { m_page->setCustomFontToLoad(j, customData.get()); haveGlyphs = true; } continue; } haveGlyphs |= fill(m_page.get(), from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData().get()); } } if (!haveGlyphs) m_page = nullptr; } else if (parentPage && parentPage->owner() != m_parent) { // The page we're overriding may not be owned by our parent node. // This happens when our parent node provides no useful overrides // and just copies the pointer to an already-existing page (see // below). // // We want our override to be shared by all nodes that reference // that page to avoid duplication, and so standardize on having the // page's owner collect all the overrides. Call getChild on the // page owner with the desired font data (this will populate // the page) and then reference it. m_page = parentPage->owner()->getChild(fontData, pageNumber)->page(); } else { // Get the pure page for the fallback font (at level 1 with no // overrides). getRootChild will always create a page if one // doesn't exist, but the page doesn't necessarily have glyphs // (this pointer may be 0). GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page(); if (!parentPage) { // When the parent has no glyphs for this page, we can easily // override it just by supplying the glyphs from our font. m_page = fallbackPage; } else if (!fallbackPage) { // When our font has no glyphs for this page, we can just reference the // parent page. m_page = parentPage; } else { // Combine the parent's glyphs and ours to form a new more complete page. m_page = GlyphPage::createForMixedFontData(this); // Overlay the parent page on the fallback page. Check if the fallback font // has added anything. bool newGlyphs = false; for (unsigned i = 0; i < GlyphPage::size; i++) { if (parentPage->glyphAt(i)) { m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); } else if (fallbackPage->glyphAt(i)) { m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); newGlyphs = true; } if (parentPage->customFontToLoadAt(i)) { m_page->setCustomFontToLoad(i, parentPage->customFontToLoadAt(i)); } else if (fallbackPage->customFontToLoadAt(i) && !parentPage->glyphAt(i)) { m_page->setCustomFontToLoad(i, fallbackPage->customFontToLoadAt(i)); newGlyphs = true; } } if (!newGlyphs) // We didn't override anything, so our override is just the parent page. m_page = parentPage; } } } else { // System fallback. Initialized with the parent's page here, as individual // entries may use different fonts depending on character. If the Font // ever finds it needs a glyph out of the system fallback page, it will // ask the system for the best font to use and fill that glyph in for us. if (parentPage) m_page = parentPage->createCopiedSystemFallbackPage(this); else m_page = GlyphPage::createForMixedFontData(this); } }
void SimpleFontData::platformInit() { m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; if (m_platformData.useGDI()) return initGDIFont(); CGFontRef font = m_platformData.cgFont(); int iAscent = CGFontGetAscent(font); int iDescent = CGFontGetDescent(font); int iLineGap = CGFontGetLeading(font); unsigned unitsPerEm = CGFontGetUnitsPerEm(font); float pointSize = m_platformData.size(); float fAscent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize; float fDescent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize; float fLineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize; if (!isCustomFont()) { HDC dc = GetDC(0); HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); int faceLength = GetTextFace(dc, 0, 0); Vector<WCHAR> faceName(faceLength); GetTextFace(dc, faceLength, faceName.data()); m_isSystemFont = !wcscmp(faceName.data(), L"Lucida Grande"); SelectObject(dc, oldFont); ReleaseDC(0, dc); if (shouldApplyMacAscentHack()) { // This code comes from FontDataMac.mm. We only ever do this when running regression tests so that our metrics will match Mac. // 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. if (!wcscmp(faceName.data(), L"Times") || !wcscmp(faceName.data(), L"Helvetica") || !wcscmp(faceName.data(), L"Courier")) fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); } } m_fontMetrics.setAscent(fAscent); m_fontMetrics.setDescent(fDescent); m_fontMetrics.setLineGap(fLineGap); m_fontMetrics.setLineSpacing(lroundf(fAscent) + lroundf(fDescent) + lroundf(fLineGap)); // Measure the actual character "x", because AppKit synthesizes X height rather than getting it from the font. // Unfortunately, NSFont will round this for us so we don't quite get the right value. GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); Glyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0; if (xGlyph) { CGRect xBox; CGFontGetGlyphBBoxes(font, &xGlyph, 1, &xBox); // Use the maximum of either width or height because "x" is nearly square // and web pages that foolishly use this metric for width will be laid out // poorly if we return an accurate height. Classic case is Times 13 point, // which has an "x" that is 7x6 pixels. m_fontMetrics.setXHeight(scaleEmToUnits(max(CGRectGetMaxX(xBox), CGRectGetMaxY(xBox)), unitsPerEm) * pointSize); } else { int iXHeight = CGFontGetXHeight(font); m_fontMetrics.setXHeight(scaleEmToUnits(iXHeight, unitsPerEm) * pointSize); } m_fontMetrics.setUnitsPerEm(unitsPerEm); }
// FIXME: It is unfortunate that this function needs to be passed the original cluster. // It is only required for the platform's FontCache::getFontDataForCharacters(), and it means // that this function is not correct if it transforms the character to uppercase and calls // FontCache::getFontDataForCharacters() afterwards. const GlyphData& Font::glyphDataForCharacter(UChar32 c, const UChar* cluster, unsigned clusterLength, bool mirror, bool attemptFontSubstitution) const { bool smallCaps = false; if (m_fontDescription.smallCaps() && !Unicode::isUpper(c)) { // Convert lowercase to upper. UChar32 upperC = Unicode::toUpper(c); if (upperC != c) { c = upperC; smallCaps = true; } } if (mirror) c = mirroredChar(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } if (!attemptFontSubstitution && node->level() != 1) node = GlyphPageTreeNode::getRootChild(primaryFont(), pageNumber); while (true) { GlyphPage* page = node->page(); if (page) { const GlyphData& data = page->glyphDataForCharacter(c); if (data.glyph || !attemptFontSubstitution) { if (!smallCaps) return data; // We have a glyph for the character in question in the current page (or we've been told not to fall back). const FontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription); if (!smallCapsFontData) // This should not happen, but if it does, we will return a big cap. return data; GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); GlyphPage* smallCapsPage = smallCapsNode->page(); if (smallCapsPage) { const GlyphData& data = smallCapsPage->glyphDataForCharacter(c); if (data.glyph || !attemptFontSubstitution) return data; } // Not attempting system fallback off the smallCapsFontData. This is the very unlikely case that // a font has the lowercase character but not its uppercase version. return smallCapsFontData->missingGlyphData(); } } else if (!attemptFontSubstitution) { if (smallCaps) { if (const FontData* smallCapsFontData = primaryFont()->smallCapsFontData(m_fontDescription)) return smallCapsFontData->missingGlyphData(); } return primaryFont()->missingGlyphData(); } if (node->isSystemFallback()) { // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. // Convert characters that shouldn't render to zero width spaces when asking what font is // appropriate. const FontData* characterFontData; if (clusterLength == 1 && Font::treatAsZeroWidthSpace(cluster[0])) #ifdef __OWB__ characterFontData = FontCache::getFontDataForCharacters(*this, &WebCore::zeroWidthSpace, 1); #else characterFontData = FontCache::getFontDataForCharacters(*this, &zeroWidthSpace, 1); #endif //__OWB__ else characterFontData = FontCache::getFontDataForCharacters(*this, cluster, clusterLength); if (smallCaps) characterFontData = characterFontData->smallCapsFontData(m_fontDescription); if (characterFontData) { // Got the fallback font, return the glyph page associated with // it. We also store the FontData for the glyph in the fallback // page for future use (it's lazily populated by us). GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); const GlyphData& data = fallbackPage ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); if (!smallCaps) page->setGlyphDataForCharacter(c, data.glyph, characterFontData); return data; } // Even system fallback can fail. // FIXME: Should the last resort font be used? const GlyphData& data = primaryFont()->missingGlyphData(); if (!smallCaps && page) page->setGlyphDataForCharacter(c, data.glyph, data.fontData); return data; } // Proceed with the fallback list. const FontData* fontData = fontDataAt(node->level()); node = node->getChild(fontData, pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; }
GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, const TextRun& run, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) { const SimpleFontData* primaryFont = font.primaryFont(); ASSERT(primaryFont); pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror); GlyphData glyphData = pair.first; // Check if we have the missing glyph data, in which case we can just return. GlyphData missingGlyphData = primaryFont->missingGlyphData(); if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData) { ASSERT(glyphData.fontData); return glyphData; } // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage. const SimpleFontData* originalFontData = glyphData.fontData; if (glyphData.fontData && !glyphData.fontData->isSVGFont()) { if (TextRun::RenderingContext* renderingContext = run.renderingContext()) { RenderObject* renderObject = static_cast<SVGTextRunRenderingContext*>(renderingContext)->renderer(); RenderObject* parentRenderObject = renderObject->isText() ? renderObject->parent() : renderObject; ASSERT(parentRenderObject); if (Element* parentRenderObjectElement = toElement(parentRenderObject->node())) { if (parentRenderObjectElement->hasTagName(SVGNames::altGlyphTag)) glyphData.fontData = primaryFont; } } } const SimpleFontData* fontData = glyphData.fontData; if (fontData) { if (!fontData->isSVGFont()) return glyphData; SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return glyphData; // If we got here, we're dealing with a glyph defined in a SVG Font. // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table. // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that. if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength)) return glyphData; } GlyphPage* page = pair.second; ASSERT(page); FontFallbackList* fontList = font.fontList(); ASSERT(fontList); // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.) // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly. GlyphPageTreeNode* originalGlyphPageZero = fontList->glyphPageZero(); const FontFallbackList::GlyphPages& originalGlyphPages = fontList->glyphPages(); page->setGlyphDataForCharacter(character, glyphData.glyph, 0); // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before. GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror); ASSERT(fallbackGlyphData.fontData != fontData); // Restore original state of the SVG Font glyph table and the current font fallback list, // to assure the next lookup of the same glyph won't immediately return the fallback glyph. page->setGlyphDataForCharacter(character, glyphData.glyph, originalFontData); fontList->setGlyphPageZero(originalGlyphPageZero); fontList->setGlyphPages(originalGlyphPages); ASSERT(fallbackGlyphData.fontData); return fallbackGlyphData; }
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); } } }
SimpleFontData::SimpleFontData(const FontPlatformData& f, bool customFont, bool loading, SVGFontData* svgFontData) : m_font(f) , m_treatAsFixedPitch(false) #if ENABLE(SVG_FONTS) , m_svgFontData(svgFontData) #endif , m_isCustomFont(customFont) , m_isLoading(loading) , m_smallCapsFontData(0) { #if ENABLE(SVG_FONTS) && !PLATFORM(QT) if (SVGFontFaceElement* svgFontFaceElement = svgFontData ? svgFontData->svgFontFaceElement() : 0) { m_unitsPerEm = svgFontFaceElement->unitsPerEm(); double scale = f.size(); if (m_unitsPerEm) scale /= m_unitsPerEm; m_ascent = static_cast<int>(svgFontFaceElement->ascent() * scale); m_descent = static_cast<int>(svgFontFaceElement->descent() * scale); m_xHeight = static_cast<int>(svgFontFaceElement->xHeight() * scale); m_lineGap = 0.1f * f.size(); m_lineSpacing = m_ascent + m_descent + m_lineGap; m_spaceGlyph = 0; m_spaceWidth = 0; m_adjustedSpaceWidth = 0; determinePitch(); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; return; } #endif platformInit(); GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); if (!glyphPageZero) { LOG_ERROR("Failed to get glyph page zero."); m_spaceGlyph = 0; m_spaceWidth = 0; m_adjustedSpaceWidth = 0; determinePitch(); m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; return; } // Nasty hack to determine if we should round or ceil space widths. // If the font is monospace or fake monospace we ceil to ensure that // every character and the space are the same width. Otherwise we round. m_spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph; float width = widthForGlyph(m_spaceGlyph); m_spaceWidth = width; determinePitch(); m_adjustedSpaceWidth = m_treatAsFixedPitch ? ceilf(width) : roundf(width); // Force the glyph for ZERO WIDTH SPACE to have zero width, unless it is shared with SPACE. // Helvetica is an example of a non-zero width ZERO WIDTH SPACE glyph. // See <http://bugs.webkit.org/show_bug.cgi?id=13178> // Ask for the glyph for 0 to avoid paging in ZERO WIDTH SPACE. Control characters, including 0, // are mapped to the ZERO WIDTH SPACE glyph. Glyph zeroWidthSpaceGlyph = glyphPageZero->glyphDataForCharacter(0).glyph; if (zeroWidthSpaceGlyph) { if (zeroWidthSpaceGlyph != m_spaceGlyph) m_glyphToWidthMap.setWidthForGlyph(zeroWidthSpaceGlyph, 0); else LOG_ERROR("Font maps SPACE and ZERO WIDTH SPACE to the same glyph. Glyph width not overridden."); } m_missingGlyphData.fontData = this; m_missingGlyphData.glyph = 0; }
GlyphData SVGTextRunRenderingContext::glyphDataForCharacter(const Font& font, WidthIterator& iterator, UChar32 character, bool mirror, int currentCharacter, unsigned& advanceLength) { const SimpleFontData* primaryFont = font.primaryFont(); ASSERT(primaryFont); std::pair<GlyphData, GlyphPage*> pair = font.glyphDataAndPageForCharacter(character, mirror, AutoVariant); GlyphData glyphData = pair.first; // Check if we have the missing glyph data, in which case we can just return. GlyphData missingGlyphData = primaryFont->missingGlyphData(); if (glyphData.glyph == missingGlyphData.glyph && glyphData.fontData == missingGlyphData.fontData) { ASSERT(glyphData.fontData); return glyphData; } // Save data fromt he font fallback list because we may modify it later. Do this before the // potential change to glyphData.fontData below. FontGlyphs* glyph = font.glyphs(); ASSERT(glyph); FontGlyphs::GlyphPagesStateSaver glyphPagesSaver(*glyph); // Characters enclosed by an <altGlyph> element, may not be registered in the GlyphPage. const SimpleFontData* originalFontData = glyphData.fontData; if (glyphData.fontData && !glyphData.fontData->isSVGFont()) { auto& elementRenderer = renderer().isRenderElement() ? toRenderElement(renderer()) : *renderer().parent(); if (Element* parentRendererElement = elementRenderer.element()) { if (parentRendererElement->hasTagName(SVGNames::altGlyphTag)) glyphData.fontData = primaryFont; } } const SimpleFontData* fontData = glyphData.fontData; if (fontData) { if (!fontData->isSVGFont()) return glyphData; SVGFontElement* fontElement = 0; SVGFontFaceElement* fontFaceElement = 0; const SVGFontData* svgFontData = svgFontAndFontFaceElementForFontData(fontData, fontFaceElement, fontElement); if (!fontElement || !fontFaceElement) return glyphData; // If we got here, we're dealing with a glyph defined in a SVG Font. // The returned glyph by glyphDataAndPageForCharacter() is a glyph stored in the SVG Font glyph table. // This doesn't necessarily mean the glyph is suitable for rendering/measuring in this context, its // arabic-form/orientation/... may not match, we have to apply SVG Glyph selection to discover that. if (svgFontData->applySVGGlyphSelection(iterator, glyphData, mirror, currentCharacter, advanceLength)) return glyphData; } GlyphPage* page = pair.second; ASSERT(page); // No suitable glyph found that is compatible with the requirments (same language, arabic-form, orientation etc.) // Even though our GlyphPage contains an entry for eg. glyph "a", it's not compatible. So we have to temporarily // remove the glyph data information from the GlyphPage, and retry the lookup, which handles font fallbacks correctly. page->setGlyphDataForCharacter(character, 0, 0); // Assure that the font fallback glyph selection worked, aka. the fallbackGlyphData font data is not the same as before. GlyphData fallbackGlyphData = font.glyphDataForCharacter(character, mirror); ASSERT(fallbackGlyphData.fontData != fontData); // Restore original state of the SVG Font glyph table and the current font fallback list, // to assure the next lookup of the same glyph won't immediately return the fallback glyph. page->setGlyphDataForCharacter(character, glyphData.glyph, originalFontData); ASSERT(fallbackGlyphData.fontData); return fallbackGlyphData; }
GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const { ASSERT(isMainThread()); if (variant == AutoVariant) { if (m_fontDescription.smallCaps()) { UChar32 upperC = toUpper(c); if (upperC != c) { c = upperC; variant = SmallCapsVariant; } else variant = NormalVariant; } else variant = NormalVariant; } if (mirror) c = mirroredChar(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_fontList->m_pages.get(pageNumber) : m_fontList->m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } GlyphPage* page; if (variant == NormalVariant) { // Fastest loop, for the common case (normal variant). while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback())) return data; if (data.fontData) { if (isCJKIdeographOrSymbol(c)) { if (!data.fontData->hasVerticalGlyphs()) { // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs // to make sure you get a square (even for broken glyphs like symbols used for punctuation). const SimpleFontData* brokenIdeographFontData = data.fontData->brokenIdeographFontData(); GlyphPageTreeNode* brokenIdeographNode = GlyphPageTreeNode::getRootChild(brokenIdeographFontData, pageNumber); const GlyphPage* brokenIdeographPage = brokenIdeographNode->page(); if (brokenIdeographPage) { GlyphData brokenIdeographData = brokenIdeographPage->glyphDataForCharacter(c); if (brokenIdeographData.fontData) return brokenIdeographData; } // Shouldn't be possible to even reach this point. ASSERT_NOT_REACHED(); } } else { if (m_fontDescription.textOrientation() == TextOrientationVerticalRight) { const SimpleFontData* verticalRightFontData = data.fontData->verticalRightOrientationFontData(); GlyphPageTreeNode* verticalRightNode = GlyphPageTreeNode::getRootChild(verticalRightFontData, pageNumber); const GlyphPage* verticalRightPage = verticalRightNode->page(); if (verticalRightPage) { GlyphData verticalRightData = verticalRightPage->glyphDataForCharacter(c); // If the glyphs are distinct, we will make the assumption that the font has a vertical-right glyph baked // into it. if (data.glyph != verticalRightData.glyph) return data; // The glyphs are identical, meaning that we should just use the horizontal glyph. if (verticalRightData.fontData) return verticalRightData; } } else if (m_fontDescription.textOrientation() == TextOrientationUpright) { const SimpleFontData* uprightFontData = data.fontData->uprightOrientationFontData(); GlyphPageTreeNode* uprightNode = GlyphPageTreeNode::getRootChild(uprightFontData, pageNumber); const GlyphPage* uprightPage = uprightNode->page(); if (uprightPage) { GlyphData uprightData = uprightPage->glyphDataForCharacter(c); // If the glyphs are the same, then we know we can just use the horizontal glyph rotated vertically to be upright. if (data.glyph == uprightData.glyph) return data; // The glyphs are distinct, meaning that the font has a vertical-right glyph baked into it. We can't use that // glyph, so we fall back to the upright data and use the horizontal glyph. if (uprightData.fontData) return uprightData; } } // Shouldn't be possible to even reach this point. ASSERT_NOT_REACHED(); } return data; } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } } else { while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData) { // The variantFontData function should not normally return 0. // But if it does, we will just render the capital letter big. const SimpleFontData* variantFontData = data.fontData->variantFontData(m_fontDescription, variant); if (!variantFontData) return data; GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData, pageNumber); const GlyphPage* variantPage = variantNode->page(); if (variantPage) { GlyphData data = variantPage->glyphDataForCharacter(c); if (data.fontData) return data; } // Do not attempt system fallback off the variantFontData. This is the very unlikely case that // a font has the lowercase character but the small caps font does not have its uppercase version. return variantFontData->missingGlyphData(); } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } } ASSERT(page); ASSERT(node->isSystemFallback()); // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. UChar codeUnits[2]; int codeUnitsLength; if (c <= 0xFFFF) { codeUnits[0] = Font::normalizeSpaces(c); codeUnitsLength = 1; } else { codeUnits[0] = U16_LEAD(c); codeUnits[1] = U16_TRAIL(c); codeUnitsLength = 2; } const SimpleFontData* characterFontData = fontCache()->getFontDataForCharacters(*this, codeUnits, codeUnitsLength); if (variant != NormalVariant && characterFontData) characterFontData = characterFontData->variantFontData(m_fontDescription, variant); if (characterFontData) { // Got the fallback glyph and font. GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. if (variant == NormalVariant) { #if OS(WINCE) // missingGlyphData returns a null character, which is not suitable for GDI to display. // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still // display the character, probably because the font package is not installed correctly. // So we just always set the glyph to be same as the character, and let GDI solve it. page->setGlyphDataForCharacter(c, c, characterFontData); return page->glyphDataForCharacter(c); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); #endif } return data; } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primaryFont()->missingGlyphData(); if (variant == NormalVariant) { #if OS(WINCE) // See comment about WINCE GDI handling near setGlyphDataForCharacter above. page->setGlyphDataForCharacter(c, c, data.fontData); return page->glyphDataForCharacter(c); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); #endif } return data; }
std::pair<GlyphData, GlyphPage*> Font::glyphDataAndPageForCharacter(UChar32 c, bool mirror, FontDataVariant variant) const { ASSERT(isMainThread()); if (variant == AutoVariant) { if (m_fontDescription.smallCaps() && !primaryFont()->isSVGFont()) { UChar32 upperC = toUpper(c); if (upperC != c) { c = upperC; variant = SmallCapsVariant; } else { variant = NormalVariant; } } else { variant = NormalVariant; } } if (mirror) c = mirroredChar(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_fontFallbackList->m_pages.get(pageNumber) : m_fontFallbackList->m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); if (pageNumber) m_fontFallbackList->m_pages.set(pageNumber, node); else m_fontFallbackList->m_pageZero = node; } GlyphPage* page = 0; if (variant == NormalVariant) { // Fastest loop, for the common case (normal variant). while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback())) return make_pair(data, page); if (data.fontData) { if (Character::isCJKIdeographOrSymbol(c)) { if (!data.fontData->hasVerticalGlyphs()) { // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs // to make sure you get a square (even for broken glyphs like symbols used for punctuation). variant = BrokenIdeographVariant; break; } } else { return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, page, pageNumber); } return make_pair(data, page); } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontFallbackList->m_pages.set(pageNumber, node); else m_fontFallbackList->m_pageZero = node; } } if (variant != NormalVariant) { while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData) { // The variantFontData function should not normally return 0. // But if it does, we will just render the capital letter big. RefPtr<SimpleFontData> variantFontData = data.fontData->variantFontData(m_fontDescription, variant); if (!variantFontData) return make_pair(data, page); GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber); GlyphPage* variantPage = variantNode->page(); if (variantPage) { GlyphData data = variantPage->glyphDataForCharacter(c); if (data.fontData) return make_pair(data, variantPage); } // Do not attempt system fallback off the variantFontData. This is the very unlikely case that // a font has the lowercase character but the small caps font does not have its uppercase version. return make_pair(variantFontData->missingGlyphData(), page); } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontFallbackList->m_pages.set(pageNumber, node); else m_fontFallbackList->m_pageZero = node; } } ASSERT(page); ASSERT(node->isSystemFallback()); // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. // FIXME: Unclear if this should normalizeSpaces above 0xFFFF. // Doing so changes fast/text/international/plane2-diffs.html UChar32 characterToRender = c; if (characterToRender <= 0xFFFF) characterToRender = Character::normalizeSpaces(characterToRender); const SimpleFontData* fontDataToSubstitute = fontDataAt(0)->fontDataForCharacter(characterToRender); RefPtr<SimpleFontData> characterFontData = FontCache::fontCache()->platformFallbackForCharacter(m_fontDescription, characterToRender, fontDataToSubstitute); if (characterFontData) { if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Character::isCJKIdeographOrSymbol(c)) variant = BrokenIdeographVariant; if (variant != NormalVariant) characterFontData = characterFontData->variantFontData(m_fontDescription, variant); } if (characterFontData) { // Got the fallback glyph and font. GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page(); GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. if (variant == NormalVariant) { page->setGlyphDataForCharacter(c, data.glyph, data.fontData); data.fontData->setMaxGlyphPageTreeLevel(max(data.fontData->maxGlyphPageTreeLevel(), node->level())); if (!Character::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback()) return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, m_fontDescription.nonCJKGlyphOrientation(), data, fallbackPage, pageNumber); } return make_pair(data, page); } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primaryFont()->missingGlyphData(); if (variant == NormalVariant) { page->setGlyphDataForCharacter(c, data.glyph, data.fontData); data.fontData->setMaxGlyphPageTreeLevel(max(data.fontData->maxGlyphPageTreeLevel(), node->level())); } return make_pair(data, page); }
const GlyphData& Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const { bool useSmallCapsFont = forceSmallCaps; if (m_fontDescription.smallCaps()) { UChar32 upperC = Unicode::toUpper(c); if (upperC != c) { c = upperC; useSmallCapsFont = true; } } if (mirror) c = mirroredChar(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } GlyphPage* page; if (!useSmallCapsFont) { // Fastest loop, for the common case (not small caps). while (true) { page = node->page(); if (page) { const GlyphData& data = page->glyphDataForCharacter(c); if (data.fontData) return data; if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } } else { while (true) { page = node->page(); if (page) { const GlyphData& data = page->glyphDataForCharacter(c); if (data.fontData) { // The smallCapsFontData function should not normally return 0. // But if it does, we will just render the capital letter big. const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription); if (!smallCapsFontData) return data; GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); const GlyphPage* smallCapsPage = smallCapsNode->page(); if (smallCapsPage) { const GlyphData& data = smallCapsPage->glyphDataForCharacter(c); if (data.fontData) return data; } // Do not attempt system fallback off the smallCapsFontData. This is the very unlikely case that // a font has the lowercase character but the small caps font does not have its uppercase version. return smallCapsFontData->missingGlyphData(); } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } } ASSERT(page); ASSERT(node->isSystemFallback()); // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. UChar codeUnits[2]; int codeUnitsLength; if (c <= 0xFFFF) { UChar c16 = c; if (Font::treatAsSpace(c16)) codeUnits[0] = ' '; else if (Font::treatAsZeroWidthSpace(c16)) codeUnits[0] = zeroWidthSpace; else codeUnits[0] = c16; codeUnitsLength = 1; } else { codeUnits[0] = U16_LEAD(c); codeUnits[1] = U16_TRAIL(c); codeUnitsLength = 2; } const SimpleFontData* characterFontData = FontCache::getFontDataForCharacters(*this, codeUnits, codeUnitsLength); if (useSmallCapsFont) characterFontData = characterFontData->smallCapsFontData(m_fontDescription); if (characterFontData) { // Got the fallback glyph and font. GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); const GlyphData& data = fallbackPage && fallbackPage->glyphDataForCharacter(c).fontData ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. if (!useSmallCapsFont) page->setGlyphDataForCharacter(c, data.glyph, data.fontData); return data; } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. const GlyphData& data = primaryFont()->missingGlyphData(); if (!useSmallCapsFont) page->setGlyphDataForCharacter(c, data.glyph, data.fontData); return data; }
std::pair<GlyphData, GlyphPage*> FontGlyphs::glyphDataAndPageForCharacter(const FontDescription& description, UChar32 c, bool mirror, FontDataVariant variant) const { ASSERT(isMainThread()); if (variant == AutoVariant) { if (description.smallCaps() && !primarySimpleFontData(description)->isSVGFont()) { UChar32 upperC = u_toupper(c); if (upperC != c) { c = upperC; variant = SmallCapsVariant; } else variant = NormalVariant; } else variant = NormalVariant; } if (mirror) c = u_charMirror(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_pages.get(pageNumber) : m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(realizeFontDataAt(description, 0), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } GlyphPage* page = 0; if (variant == NormalVariant) { // Fastest loop, for the common case (normal variant). while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData && (data.fontData->platformData().orientation() == Horizontal || data.fontData->isTextOrientationFallback())) return std::make_pair(data, page); if (data.fontData) { if (Font::isCJKIdeographOrSymbol(c)) { if (!data.fontData->hasVerticalGlyphs()) { // Use the broken ideograph font data. The broken ideograph font will use the horizontal width of glyphs // to make sure you get a square (even for broken glyphs like symbols used for punctuation). variant = BrokenIdeographVariant; break; } #if PLATFORM(COCOA) else if (data.fontData->platformData().syntheticOblique()) return glyphDataAndPageForCJKCharacterWithoutSyntheticItalic(c, data, page, pageNumber); #endif } else return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, page, pageNumber); return std::make_pair(data, page); } if (node->isSystemFallback()) break; } node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } } if (variant != NormalVariant) { while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData) { // The variantFontData function should not normally return 0. // But if it does, we will just render the capital letter big. RefPtr<SimpleFontData> variantFontData = data.fontData->variantFontData(description, variant); if (!variantFontData) return std::make_pair(data, page); GlyphPageTreeNode* variantNode = GlyphPageTreeNode::getRootChild(variantFontData.get(), pageNumber); GlyphPage* variantPage = variantNode->page(); if (variantPage) { GlyphData data = variantPage->glyphDataForCharacter(c); if (data.fontData) return std::make_pair(data, variantPage); } // Do not attempt system fallback off the variantFontData. This is the very unlikely case that // a font has the lowercase character but the small caps font does not have its uppercase version. return std::make_pair(variantFontData->missingGlyphData(), page); } if (node->isSystemFallback()) break; } node = node->getChild(realizeFontDataAt(description, node->level()), pageNumber); if (pageNumber) m_pages.set(pageNumber, node); else m_pageZero = node; } } ASSERT(page); ASSERT(node->isSystemFallback()); // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. UChar codeUnits[2]; int codeUnitsLength; if (c <= 0xFFFF) { codeUnits[0] = Font::normalizeSpaces(c); codeUnitsLength = 1; } else { codeUnits[0] = U16_LEAD(c); codeUnits[1] = U16_TRAIL(c); codeUnitsLength = 2; } const SimpleFontData* originalFontData = primaryFontData(description)->fontDataForCharacter(c); RefPtr<SimpleFontData> characterFontData = fontCache().systemFallbackForCharacters(description, originalFontData, m_isForPlatformFont, codeUnits, codeUnitsLength); if (characterFontData) { if (characterFontData->platformData().orientation() == Vertical && !characterFontData->hasVerticalGlyphs() && Font::isCJKIdeographOrSymbol(c)) variant = BrokenIdeographVariant; if (variant != NormalVariant) characterFontData = characterFontData->variantFontData(description, variant); } if (characterFontData) { // Got the fallback glyph and font. GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData.get(), pageNumber)->page(); GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. if (variant == NormalVariant) { #if OS(WINCE) // missingGlyphData returns a null character, which is not suitable for GDI to display. // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still // display the character, probably because the font package is not installed correctly. // So we just always set the glyph to be same as the character, and let GDI solve it. page->setGlyphDataForCharacter(c, c, characterFontData.get()); characterFontData->setMaxGlyphPageTreeLevel(std::max(characterFontData->maxGlyphPageTreeLevel(), node->level())); return std::make_pair(page->glyphDataForCharacter(c), page); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); if (!Font::isCJKIdeographOrSymbol(c) && data.fontData->platformData().orientation() != Horizontal && !data.fontData->isTextOrientationFallback()) return glyphDataAndPageForNonCJKCharacterWithGlyphOrientation(c, description.nonCJKGlyphOrientation(), data, fallbackPage, pageNumber); #endif } return std::make_pair(data, page); } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primarySimpleFontData(description)->missingGlyphData(); if (variant == NormalVariant) { #if OS(WINCE) // See comment about WINCE GDI handling near setGlyphDataForCharacter above. page->setGlyphDataForCharacter(c, c, data.fontData); data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); return std::make_pair(page->glyphDataForCharacter(c), page); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); data.fontData->setMaxGlyphPageTreeLevel(std::max(data.fontData->maxGlyphPageTreeLevel(), node->level())); #endif } return std::make_pair(data, page); }
void SimpleFontData::platformInit() { m_syntheticBoldOffset = m_platformData.syntheticBold() ? 1.0f : 0.f; m_scriptCache = 0; m_scriptFontProperties = 0; m_isSystemFont = false; if (m_platformData.useGDI()) return initGDIFont(); CGFontRef font = m_platformData.cgFont(); int iAscent = CGFontGetAscent(font); int iDescent = CGFontGetDescent(font); int iLineGap = CGFontGetLeading(font); unsigned unitsPerEm = CGFontGetUnitsPerEm(font); float pointSize = m_platformData.size(); float fAscent = scaleEmToUnits(iAscent, unitsPerEm) * pointSize; float fDescent = -scaleEmToUnits(iDescent, unitsPerEm) * pointSize; float fLineGap = scaleEmToUnits(iLineGap, unitsPerEm) * pointSize; if (!isCustomFont()) { HDC dc = GetDC(0); HGDIOBJ oldFont = SelectObject(dc, m_platformData.hfont()); int faceLength = GetTextFace(dc, 0, 0); Vector<WCHAR> faceName(faceLength); GetTextFace(dc, faceLength, faceName.data()); m_isSystemFont = !wcscmp(faceName.data(), L"Lucida Grande"); SelectObject(dc, oldFont); ReleaseDC(0, dc); if (shouldApplyMacAscentHack()) { // This code comes from FontDataMac.mm. We only ever do this when running regression tests so that our metrics will match Mac. // 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. if (!wcscmp(faceName.data(), L"Times") || !wcscmp(faceName.data(), L"Helvetica") || !wcscmp(faceName.data(), L"Courier")) fAscent += floorf(((fAscent + fDescent) * 0.15f) + 0.5f); } } m_fontMetrics.setAscent(fAscent); m_fontMetrics.setDescent(fDescent); m_fontMetrics.setLineGap(fLineGap); m_fontMetrics.setLineSpacing(lroundf(fAscent) + lroundf(fDescent) + lroundf(fLineGap)); GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(this, 0)->page(); Glyph xGlyph = glyphPageZero ? glyphPageZero->glyphDataForCharacter('x').glyph : 0; if (xGlyph) { // Measure the actual character "x", since it's possible for it to extend below the baseline, and we need the // reported x-height to only include the portion of the glyph that is above the baseline. CGRect xBox; CGFontGetGlyphBBoxes(font, &xGlyph, 1, &xBox); m_fontMetrics.setXHeight(scaleEmToUnits(CGRectGetMaxY(xBox), unitsPerEm) * pointSize); } else { int iXHeight = CGFontGetXHeight(font); m_fontMetrics.setXHeight(scaleEmToUnits(iXHeight, unitsPerEm) * pointSize); } m_fontMetrics.setUnitsPerEm(unitsPerEm); }
void GlyphPageTreeNode::initializeOverridePage(const FontData* fontData, unsigned pageNumber) { GlyphPage* parentPage = m_parent->page(); // Get the pure page for the fallback font (at level 1 with no // overrides). getRootChild will always create a page if one // doesn't exist, but the page doesn't necessarily have glyphs // (this pointer may be 0). GlyphPage* fallbackPage = getNormalRootChild(fontData, pageNumber)->page(); if (!parentPage) { // When the parent has no glyphs for this page, we can easily // override it just by supplying the glyphs from our font. m_page = fallbackPage; return; } if (!fallbackPage) { // When our font has no glyphs for this page, we can just reference the // parent page. m_page = parentPage; return; } // Combine the parent's glyphs and ours to form a new more complete page. m_page = GlyphPage::createForMixedFontData(this); // Overlay the parent page on the fallback page. Check if the fallback font // has added anything. bool newGlyphs = false; for (unsigned i = 0; i < GlyphPage::size; i++) { if (parentPage->glyphAt(i)) { m_page->setGlyphDataForIndex(i, parentPage->glyphDataForIndex(i)); } else if (fallbackPage->glyphAt(i)) { m_page->setGlyphDataForIndex(i, fallbackPage->glyphDataForIndex(i)); newGlyphs = true; } if (parentPage->customFontToLoadAt(i)) { m_page->setCustomFontToLoad(i, parentPage->customFontToLoadAt(i)); } else if (fallbackPage->customFontToLoadAt(i) && !parentPage->glyphAt(i)) { m_page->setCustomFontToLoad(i, fallbackPage->customFontToLoadAt(i)); newGlyphs = true; } } if (!newGlyphs) { // We didn't override anything, so our override is just the parent page. m_page = parentPage; } }
void GlyphPageTreeNode::initializePage(const FontData* fontData, unsigned pageNumber) { ASSERT(!m_page); // This function must not be called for the root of the tree, because that // level does not contain any glyphs. ASSERT(m_level > 0 && m_parent); // The parent's page will be 0 if we are level one or the parent's font data // did not contain any glyphs for that page. GlyphPage* parentPage = m_parent->page(); // NULL FontData means we're being asked for the system fallback font. if (fontData) { if (m_level == 1) { // Children of the root hold pure pages. These will cover only one // font data's glyphs, and will have glyph index 0 if the font data does not // contain the glyph. unsigned start = pageNumber * GlyphPage::size; UChar buffer[GlyphPage::size * 2 + 2]; unsigned bufferLength; unsigned i; // Fill in a buffer with the entire "page" of characters that we want to look up glyphs for. if (start < 0x10000) { bufferLength = GlyphPage::size; for (i = 0; i < GlyphPage::size; i++) buffer[i] = start + i; if (start == 0) { // Control characters must not render at all. for (i = 0; i < 0x20; ++i) buffer[i] = zeroWidthSpace; for (i = 0x7F; i < 0xA0; i++) buffer[i] = zeroWidthSpace; // \n, \t, and nonbreaking space must render as a space. buffer[(int)'\n'] = ' '; buffer[(int)'\t'] = ' '; buffer[noBreakSpace] = ' '; } else if (start == (leftToRightMark & ~(GlyphPage::size - 1))) { // LRM, RLM, LRE, RLE and PDF must not render at all. buffer[leftToRightMark - start] = zeroWidthSpace; buffer[rightToLeftMark - start] = zeroWidthSpace; buffer[leftToRightEmbed - start] = zeroWidthSpace; buffer[rightToLeftEmbed - start] = zeroWidthSpace; buffer[leftToRightOverride - start] = zeroWidthSpace; buffer[rightToLeftOverride - start] = zeroWidthSpace; buffer[popDirectionalFormatting - start] = zeroWidthSpace; } else if (start == (objectReplacementCharacter & ~(GlyphPage::size - 1))) { // Object replacement character must not render at all. buffer[objectReplacementCharacter - start] = zeroWidthSpace; } } else { bufferLength = GlyphPage::size * 2; for (i = 0; i < GlyphPage::size; i++) { int c = i + start; buffer[i * 2] = U16_LEAD(c); buffer[i * 2 + 1] = U16_TRAIL(c); } } m_page = GlyphPage::create(this); // Now that we have a buffer full of characters, we want to get back an array // of glyph indices. This part involves calling into the platform-specific // routine of our glyph map for actually filling in the page with the glyphs. // Success is not guaranteed. For example, Times fails to fill page 260, giving glyph data // for only 128 out of 256 characters. bool haveGlyphs; if (fontData->isSegmented()) { haveGlyphs = false; const SegmentedFontData* segmentedFontData = static_cast<const SegmentedFontData*>(fontData); unsigned numRanges = segmentedFontData->numRanges(); bool zeroFilled = false; RefPtr<GlyphPage> scratchPage; GlyphPage* pageToFill = m_page.get(); for (unsigned i = 0; i < numRanges; i++) { const FontDataRange& range = segmentedFontData->rangeAt(i); int from = max(0, range.from() - static_cast<int>(start)); int to = 1 + min(range.to() - static_cast<int>(start), static_cast<int>(GlyphPage::size) - 1); if (from < static_cast<int>(GlyphPage::size) && to > 0) { if (haveGlyphs && !scratchPage) { scratchPage = GlyphPage::create(this); pageToFill = scratchPage.get(); } if (!zeroFilled) { if (from > 0 || to < static_cast<int>(GlyphPage::size)) { for (unsigned i = 0; i < GlyphPage::size; i++) pageToFill->setGlyphDataForIndex(i, 0, 0); } zeroFilled = true; } haveGlyphs |= pageToFill->fill(from, to - from, buffer + from * (start < 0x10000 ? 1 : 2), (to - from) * (start < 0x10000 ? 1 : 2), range.fontData()); if (scratchPage) { for (int j = from; j < to; j++) { if (!m_page->m_glyphs[j].glyph && pageToFill->m_glyphs[j].glyph) m_page->m_glyphs[j] = pageToFill->m_glyphs[j]; } } } } } else haveGlyphs = m_page->fill(0, GlyphPage::size, buffer, bufferLength, static_cast<const SimpleFontData*>(fontData)); if (!haveGlyphs) m_page = 0; } else if (parentPage && parentPage->owner() != m_parent) { // The page we're overriding may not be owned by our parent node. // This happens when our parent node provides no useful overrides // and just copies the pointer to an already-existing page (see // below). // // We want our override to be shared by all nodes that reference // that page to avoid duplication, and so standardize on having the // page's owner collect all the overrides. Call getChild on the // page owner with the desired font data (this will populate // the page) and then reference it. m_page = parentPage->owner()->getChild(fontData, pageNumber)->page(); } else { // Get the pure page for the fallback font (at level 1 with no // overrides). getRootChild will always create a page if one // doesn't exist, but the page doesn't necessarily have glyphs // (this pointer may be 0). GlyphPage* fallbackPage = getRootChild(fontData, pageNumber)->page(); if (!parentPage) { // When the parent has no glyphs for this page, we can easily // override it just by supplying the glyphs from our font. m_page = fallbackPage; } else if (!fallbackPage) { // When our font has no glyphs for this page, we can just reference the // parent page. m_page = parentPage; } else { // Combine the parent's glyphs and ours to form a new more complete page. m_page = GlyphPage::create(this); // Overlay the parent page on the fallback page. Check if the fallback font // has added anything. bool newGlyphs = false; for (unsigned i = 0; i < GlyphPage::size; i++) { if (parentPage->m_glyphs[i].glyph) m_page->m_glyphs[i] = parentPage->m_glyphs[i]; else if (fallbackPage->m_glyphs[i].glyph) { m_page->m_glyphs[i] = fallbackPage->m_glyphs[i]; newGlyphs = true; } else { const GlyphData data = { 0, 0 }; m_page->m_glyphs[i] = data; } } if (!newGlyphs) // We didn't override anything, so our override is just the parent page. m_page = parentPage; } } } else { m_page = GlyphPage::create(this); // System fallback. Initialized with the parent's page here, as individual // entries may use different fonts depending on character. If the Font // ever finds it needs a glyph out of the system fallback page, it will // ask the system for the best font to use and fill that glyph in for us. if (parentPage) memcpy(m_page->m_glyphs, parentPage->m_glyphs, GlyphPage::size * sizeof(m_page->m_glyphs[0])); else { const GlyphData data = { 0, 0 }; for (unsigned i = 0; i < GlyphPage::size; i++) m_page->m_glyphs[i] = data; } } }
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); }
void SVGFontData::initializeFontData(SimpleFontData* fontData, float fontSize) { ASSERT(fontData); SVGFontFaceElement* svgFontFaceElement = this->svgFontFaceElement(); ASSERT(svgFontFaceElement); SVGFontElement* svgFontElement = svgFontFaceElement->associatedFontElement(); ASSERT(svgFontElement); GlyphData missingGlyphData; missingGlyphData.fontData = fontData; missingGlyphData.glyph = svgFontElement->missingGlyph(); fontData->setMissingGlyphData(missingGlyphData); fontData->setZeroWidthSpaceGlyph(0); fontData->determinePitch(); unsigned unitsPerEm = svgFontFaceElement->unitsPerEm(); float scale = scaleEmToUnits(fontSize, unitsPerEm); float xHeight = svgFontFaceElement->xHeight() * scale; float ascent = svgFontFaceElement->ascent() * scale; float descent = svgFontFaceElement->descent() * scale; float lineGap = 0.1f * fontSize; GlyphPage* glyphPageZero = GlyphPageTreeNode::getRootChild(fontData, 0)->page(); if (!xHeight && glyphPageZero) { // Fallback if x_heightAttr is not specified for the font element. Glyph letterXGlyph = glyphPageZero->glyphDataForCharacter('x').glyph; xHeight = letterXGlyph ? fontData->widthForGlyph(letterXGlyph) : 2 * ascent / 3; } FontMetrics& fontMetrics = fontData->fontMetrics(); fontMetrics.setUnitsPerEm(unitsPerEm); fontMetrics.setAscent(ascent); fontMetrics.setDescent(descent); fontMetrics.setLineGap(lineGap); fontMetrics.setLineSpacing(roundf(ascent) + roundf(descent) + roundf(lineGap)); fontMetrics.setXHeight(xHeight); if (!glyphPageZero) { fontData->setSpaceGlyph(0); fontData->setSpaceWidths(0); fontData->setAvgCharWidth(0); fontData->setMaxCharWidth(ascent); return; } // Calculate space width. Glyph spaceGlyph = glyphPageZero->glyphDataForCharacter(' ').glyph; fontData->setSpaceGlyph(spaceGlyph); fontData->setSpaceWidths(fontData->widthForGlyph(spaceGlyph)); // Estimate average character width. Glyph numeralZeroGlyph = glyphPageZero->glyphDataForCharacter('0').glyph; fontData->setAvgCharWidth(numeralZeroGlyph ? fontData->widthForGlyph(numeralZeroGlyph) : fontData->spaceWidth()); // Estimate maximum character width. Glyph letterWGlyph = glyphPageZero->glyphDataForCharacter('W').glyph; fontData->setMaxCharWidth(letterWGlyph ? fontData->widthForGlyph(letterWGlyph) : ascent); }
GlyphData Font::glyphDataForCharacter(UChar32 c, bool mirror, bool forceSmallCaps) const { ASSERT(isMainThread()); bool useSmallCapsFont = forceSmallCaps; if (m_fontDescription.smallCaps()) { UChar32 upperC = toUpper(c); if (upperC != c) { c = upperC; useSmallCapsFont = true; } } if (mirror) c = mirroredChar(c); unsigned pageNumber = (c / GlyphPage::size); GlyphPageTreeNode* node = pageNumber ? m_fontList->m_pages.get(pageNumber) : m_fontList->m_pageZero; if (!node) { node = GlyphPageTreeNode::getRootChild(fontDataAt(0), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } GlyphPage* page; if (!useSmallCapsFont) { // Fastest loop, for the common case (not small caps). while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData) return data; if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } } else { while (true) { page = node->page(); if (page) { GlyphData data = page->glyphDataForCharacter(c); if (data.fontData) { // The smallCapsFontData function should not normally return 0. // But if it does, we will just render the capital letter big. const SimpleFontData* smallCapsFontData = data.fontData->smallCapsFontData(m_fontDescription); if (!smallCapsFontData) return data; GlyphPageTreeNode* smallCapsNode = GlyphPageTreeNode::getRootChild(smallCapsFontData, pageNumber); const GlyphPage* smallCapsPage = smallCapsNode->page(); if (smallCapsPage) { GlyphData data = smallCapsPage->glyphDataForCharacter(c); if (data.fontData) return data; } // Do not attempt system fallback off the smallCapsFontData. This is the very unlikely case that // a font has the lowercase character but the small caps font does not have its uppercase version. return smallCapsFontData->missingGlyphData(); } if (node->isSystemFallback()) break; } // Proceed with the fallback list. node = node->getChild(fontDataAt(node->level()), pageNumber); if (pageNumber) m_fontList->m_pages.set(pageNumber, node); else m_fontList->m_pageZero = node; } } ASSERT(page); ASSERT(node->isSystemFallback()); // System fallback is character-dependent. When we get here, we // know that the character in question isn't in the system fallback // font's glyph page. Try to lazily create it here. UChar codeUnits[2]; int codeUnitsLength; if (c <= 0xFFFF) { codeUnits[0] = Font::normalizeSpaces(c); codeUnitsLength = 1; } else { codeUnits[0] = U16_LEAD(c); codeUnits[1] = U16_TRAIL(c); codeUnitsLength = 2; } const SimpleFontData* characterFontData = fontCache()->getFontDataForCharacters(*this, codeUnits, codeUnitsLength); if (useSmallCapsFont && characterFontData) characterFontData = characterFontData->smallCapsFontData(m_fontDescription); if (characterFontData) { // Got the fallback glyph and font. GlyphPage* fallbackPage = GlyphPageTreeNode::getRootChild(characterFontData, pageNumber)->page(); GlyphData data = fallbackPage && fallbackPage->fontDataForCharacter(c) ? fallbackPage->glyphDataForCharacter(c) : characterFontData->missingGlyphData(); // Cache it so we don't have to do system fallback again next time. if (!useSmallCapsFont) { #if OS(WINCE) // missingGlyphData returns a null character, which is not suitable for GDI to display. // Also, sometimes we cannot map a font for the character on WINCE, but GDI can still // display the character, probably because the font package is not installed correctly. // So we just always set the glyph to be same as the character, and let GDI solve it. page->setGlyphDataForCharacter(c, c, characterFontData); return page->glyphDataForCharacter(c); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); #endif } return data; } // Even system fallback can fail; use the missing glyph in that case. // FIXME: It would be nicer to use the missing glyph from the last resort font instead. GlyphData data = primaryFont()->missingGlyphData(); if (!useSmallCapsFont) { #if OS(WINCE) // See comment about WINCE GDI handling near setGlyphDataForCharacter above. page->setGlyphDataForCharacter(c, c, data.fontData); return page->glyphDataForCharacter(c); #else page->setGlyphDataForCharacter(c, data.glyph, data.fontData); #endif } return data; }