Font::CodePath Font::codePath(const TextRun& run) const { if (s_codePath != Auto) return s_codePath; #if ENABLE(SVG_FONTS) if (run.renderingContext()) return Simple; #endif if (m_fontDescription.featureSettings() && m_fontDescription.featureSettings()->size() > 0) return Complex; if (run.length() > 1 && !WidthIterator::supportsTypesettingFeatures(*this)) return Complex; if (!run.characterScanForCodePath()) return Simple; if (run.is8Bit()) return Simple; // Start from 0 since drawing and highlighting also measure the characters before run->from. return characterRangeCodePath(run.characters16(), run.length()); }
static void normalizeCharacters(const TextRun& run, unsigned length, UChar* destination, unsigned* destinationLength) { unsigned position = 0; bool error = false; const UChar* source; String stringFor8BitRun; if (run.is8Bit()) { stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); source = stringFor8BitRun.characters16(); } else { source = run.characters16(); } *destinationLength = 0; while (position < length) { UChar32 character; U16_NEXT(source, position, length, character); // Don't normalize tabs as they are not treated as spaces for word-end. if (run.normalizeSpace() && Character::isNormalizedCanvasSpaceCharacter(character)) character = spaceCharacter; else if (Character::treatAsSpace(character) && character != noBreakSpaceCharacter) character = spaceCharacter; else if (Character::treatAsZeroWidthSpaceInComplexScript(character)) character = zeroWidthSpaceCharacter; U16_APPEND(destination, *destinationLength, length, character, error); ASSERT_UNUSED(error, !error); } }
int Font::offsetForPositionForComplexText(const TextRun& run, float xFloat, bool includePartialGlyphs) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) return offsetForPositionForSimpleText(run, xFloat, includePartialGlyphs); #endif // FIXME: This truncation is not a problem for HTML, but only affects SVG, which passes floating-point numbers // to Font::offsetForPosition(). Bug http://webkit.org/b/40673 tracks fixing this problem. int x = static_cast<int>(xFloat); PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters16(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); int index, trailing; pango_layout_xy_to_index(layout, x * PANGO_SCALE, 1, &index, &trailing); glong offset = g_utf8_pointer_to_offset(utf8, utf8 + index); if (includePartialGlyphs) offset += trailing; g_free(utf8); g_object_unref(layout); return offset; }
UniscribeHelperTextRun::UniscribeHelperTextRun(const TextRun& run, const Font& font) : UniscribeHelper(0, run.length(), run.rtl(), font.primaryFont()->platformData().hfont(), font.primaryFont()->platformData().scriptCache(), font.primaryFont()->platformData().scriptFontProperties(), font.primaryFont()->spaceGlyph()) , m_font(&font) , m_fontIndex(0) { if (run.is8Bit()) { m_stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); setInput(m_stringFor8BitRun.characters16()); } else { setInput(run.characters16()); } setDirectionalOverride(run.directionalOverride()); setLetterSpacing(font.letterSpacing()); setSpaceWidth(font.spaceWidth()); setWordSpacing(font.wordSpacing()); setAscent(font.fontMetrics().ascent()); setRangeProperties(font.fontDescription().featureSettings()); init(); // Expansion is the amount to add to make justification happen. This // should be done after Init() so all the runs are already measured. if (run.expansion() > 0) justify(run.expansion()); }
static void normalizeCharacters(const TextRun& run, UChar* destination, int length) { int position = 0; bool error = false; const UChar* source; String stringFor8BitRun; if (run.is8Bit()) { stringFor8BitRun = String::make16BitFrom8BitSource(run.characters8(), run.length()); source = stringFor8BitRun.characters16(); } else source = run.characters16(); while (position < length) { UChar32 character; int nextPosition = position; U16_NEXT(source, nextPosition, length, character); // Don't normalize tabs as they are not treated as spaces for word-end. if (Font::treatAsSpace(character) && character != '\t') character = ' '; else if (Font::treatAsZeroWidthSpaceInComplexScript(character)) character = zeroWidthSpace; U16_APPEND(destination, position, length, character, error); ASSERT_UNUSED(error, !error); position = nextPosition; } }
bool ShapeResultSpacing::isFirstRun(const TextRun& run) const { if (&run == &m_textRun) return true; return run.is8Bit() ? run.characters8() == m_textRun.characters8() : run.characters16() == m_textRun.characters16(); }
TextDirection directionForRun(TextRun& run, bool* hasStrongDirectionality) { if (!hasStrongDirectionality) { // 8bit is Latin-1 and therefore is always LTR. if (run.is8Bit()) return LTR; // length == 1 for more than 90% of cases of width() for CJK text. if (run.length() == 1 && U16_IS_SINGLE(run.characters16()[0])) return directionForCharacter(run.characters16()[0]); } BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); return bidiResolver.determineParagraphDirectionality(hasStrongDirectionality); }
void computeNormalizedSpaces(const TextRun& run, bool mirror, String& normalizedSpacesStringCache) { if (normalizedSpacesStringCache.length() == static_cast<unsigned>(run.charactersLength())) return; if (run.is8Bit()) normalizedSpacesStringCache = Font::normalizeSpaces(run.characters8(), run.charactersLength()); else normalizedSpacesStringCache = Font::normalizeSpaces(run.characters16(), run.charactersLength()); if (mirror) normalizedSpacesStringCache = createStringWithMirroredCharacters(normalizedSpacesStringCache); }
FloatRect Font::selectionRectForComplexText(const TextRun& run, const FloatPoint& point, int h, int from, int to) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) return selectionRectForSimpleText(run, point, h, from, to); #endif PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters16(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); if (run.ltr()) { from = start - utf8; to = end - utf8; } else { from = end - utf8; to = start - utf8; } PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); int xPos; xPos = 0; if (from < layoutLine->length) pango_layout_line_index_to_x(layoutLine, from, FALSE, &xPos); float beforeWidth = PANGO_PIXELS_FLOOR(xPos); xPos = 0; if (run.ltr() || to < layoutLine->length) pango_layout_line_index_to_x(layoutLine, to, FALSE, &xPos); float afterWidth = PANGO_PIXELS(xPos); g_free(utf8); g_object_unref(layout); return FloatRect(point.x() + beforeWidth, point.y(), afterWidth - beforeWidth, h); }
float Font::floatWidthForComplexText(const TextRun& run, HashSet<const SimpleFontData*>* fallbackFonts, GlyphOverflow* overflow) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) return floatWidthForSimpleText(run, fallbackFonts, overflow); #endif if (!run.length()) return 0.0f; PangoLayout* layout = getDefaultPangoLayout(run); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters16(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); int width; pango_layout_get_pixel_size(layout, &width, 0); g_free(utf8); g_object_unref(layout); return width; }
float ShapeResult::fillGlyphBufferForTextEmphasisRun(GlyphBuffer* glyphBuffer, const RunInfo* run, const TextRun& textRun, const GlyphData* emphasisData, float initialAdvance, unsigned from, unsigned to, unsigned runOffset) { if (!run) return 0; unsigned graphemesInCluster = 1; float clusterAdvance = 0; FloatPoint glyphCenter = emphasisData->fontData-> boundsForGlyph(emphasisData->glyph).center(); TextDirection direction = textRun.direction(); // A "cluster" in this context means a cluster as it is used by HarfBuzz: // The minimal group of characters and corresponding glyphs, that cannot be broken // down further from a text shaping point of view. // A cluster can contain multiple glyphs and grapheme clusters, with mutually // overlapping boundaries. Below we count grapheme clusters per HarfBuzz clusters, // then linearly split the sum of corresponding glyph advances by the number of // grapheme clusters in order to find positions for emphasis mark drawing. uint16_t clusterStart = direction == RTL ? run->m_startIndex + run->m_numCharacters + runOffset : run->glyphToCharacterIndex(0) + runOffset; float advanceSoFar = initialAdvance; unsigned numGlyphs = run->m_numGlyphs; for (unsigned i = 0; i < numGlyphs; ++i) { const HarfBuzzRunGlyphData& glyphData = run->m_glyphData[i]; uint16_t currentCharacterIndex = run->m_startIndex + glyphData.characterIndex + runOffset; bool isRunEnd = (i + 1 == numGlyphs); bool isClusterEnd = isRunEnd || (run->glyphToCharacterIndex(i + 1) + runOffset != currentCharacterIndex); if ((direction == RTL && currentCharacterIndex >= to) || (direction != RTL && currentCharacterIndex < from)) { advanceSoFar += glyphData.advance; direction == RTL ? --clusterStart : ++clusterStart; continue; } clusterAdvance += glyphData.advance; if (textRun.is8Bit()) { float glyphAdvanceX = glyphData.advance; if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) { addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); } advanceSoFar += glyphAdvanceX; } else if (isClusterEnd) { uint16_t clusterEnd; if (direction == RTL) clusterEnd = currentCharacterIndex; else clusterEnd = isRunEnd ? run->m_startIndex + run->m_numCharacters + runOffset : run->glyphToCharacterIndex(i + 1) + runOffset; graphemesInCluster = countGraphemesInCluster(textRun.characters16(), textRun.charactersLength(), clusterStart, clusterEnd); if (!graphemesInCluster || !clusterAdvance) continue; float glyphAdvanceX = clusterAdvance / graphemesInCluster; for (unsigned j = 0; j < graphemesInCluster; ++j) { // Do not put emphasis marks on space, separator, and control characters. if (Character::canReceiveTextEmphasis(textRun[currentCharacterIndex])) addEmphasisMark(glyphBuffer, emphasisData, glyphCenter, advanceSoFar + glyphAdvanceX / 2); advanceSoFar += glyphAdvanceX; } clusterStart = clusterEnd; clusterAdvance = 0; } } return advanceSoFar - initialAdvance; }
static int generateComponents(TextRunComponents* components, const Font &font, const TextRun &run) { int letterSpacing = font.letterSpacing(); int wordSpacing = font.wordSpacing(); int padding = run.expansion(); int numSpaces = 0; if (padding) { for (int i = 0; i < run.length(); i++) if (Font::treatAsSpace(run[i])) ++numSpaces; } int offset = 0; if (letterSpacing) { // need to draw every letter on it's own int start = 0; if (Font::treatAsSpace(run[0])) { int add = 0; if (numSpaces) { add = padding/numSpaces; padding -= add; --numSpaces; } components->append(TextRunComponent(1, font, offset)); offset += add + letterSpacing + components->last().m_width; start = 1; } for (int i = 1; i < run.length(); ++i) { UChar ch = run[i]; if (U16_IS_LEAD(ch) && U16_IS_TRAIL(run[i-1])) ch = U16_GET_SUPPLEMENTARY(ch, run[i-1]); if (U16_IS_TRAIL(ch) || U_GET_GC_MASK(ch) & U_GC_MN_MASK) continue; if (Font::treatAsSpace(run[i])) { int add = 0; if (i - start > 0) { components->append(TextRunComponent(run.characters16() + start, i - start, run, font, offset)); offset += components->last().m_width + letterSpacing; } if (numSpaces) { add = padding/numSpaces; padding -= add; --numSpaces; } components->append(TextRunComponent(1, font, offset)); offset += wordSpacing + add + components->last().m_width + letterSpacing; start = i + 1; continue; } if (i - start > 0) { components->append(TextRunComponent(run.characters16() + start, i - start, run, font, offset)); offset += components->last().m_width + letterSpacing; } start = i; } if (run.length() - start > 0) { components->append(TextRunComponent(run.characters16() + start, run.length() - start, run, font, offset)); offset += components->last().m_width; } offset += letterSpacing; } else { int start = 0; for (int i = 0; i < run.length(); ++i) { if (Font::treatAsSpace(run[i])) { if (i - start > 0) { components->append(TextRunComponent(run.characters16() + start, i - start, run, font, offset)); offset += components->last().m_width; } int add = 0; if (numSpaces) { add = padding/numSpaces; padding -= add; --numSpaces; } components->append(TextRunComponent(1, font, offset)); offset += add + components->last().m_width; if (i) offset += wordSpacing; start = i + 1; } } if (run.length() - start > 0) { components->append(TextRunComponent(run.characters16() + start, run.length() - start, run, font, offset)); offset += components->last().m_width; } } return offset; }
void Font::drawComplexText(GraphicsContext* context, const TextRun& run, const FloatPoint& point, int from, int to) const { #if USE(FREETYPE) if (!primaryFont()->platformData().m_pattern) { drawSimpleText(context, run, point, from, to); return; } #endif cairo_t* cr = context->platformContext()->cr(); PangoLayout* layout = pango_cairo_create_layout(cr); setPangoAttributes(this, run, layout); gchar* utf8 = convertUniCharToUTF8(run.characters16(), run.length(), 0, run.length()); pango_layout_set_text(layout, utf8, -1); // Our layouts are single line PangoLayoutLine* layoutLine = pango_layout_get_line_readonly(layout, 0); // Get the region where this text will be laid out. We will use it to clip // the Cairo context, for when we are only painting part of the text run and // to calculate the size of the shadow buffer. PangoRegionType partialRegion = 0; char* start = g_utf8_offset_to_pointer(utf8, from); char* end = g_utf8_offset_to_pointer(start, to - from); int ranges[] = {start - utf8, end - utf8}; #if PLATFORM(GTK) partialRegion = gdk_pango_layout_line_get_clip_region(layoutLine, 0, 0, ranges, 1); #else partialRegion = getClipRegionFromPangoLayoutLine(layoutLine, ranges); #endif drawGlyphsShadow(context, point, layoutLine, partialRegion); cairo_save(cr); cairo_translate(cr, point.x(), point.y()); float red, green, blue, alpha; context->fillColor().getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); #if PLATFORM(GTK) gdk_cairo_region(cr, partialRegion); #else appendRegionToCairoContext(cr, partialRegion); #endif cairo_clip(cr); pango_cairo_show_layout_line(cr, layoutLine); if (context->textDrawingMode() & TextModeStroke) { Color strokeColor = context->strokeColor(); strokeColor.getRGBA(red, green, blue, alpha); cairo_set_source_rgba(cr, red, green, blue, alpha); pango_cairo_layout_line_path(cr, layoutLine); cairo_set_line_width(cr, context->strokeThickness()); cairo_stroke(cr); } // Pango sometimes leaves behind paths we don't want cairo_new_path(cr); destroyPangoRegion(partialRegion); g_free(utf8); g_object_unref(layout); cairo_restore(cr); }
SVGTextMetrics::SVGTextMetrics(RenderSVGInlineText* textRenderer, const TextRun& run) { ASSERT(textRenderer); float scalingFactor = textRenderer->scalingFactor(); ASSERT(scalingFactor); const Font& scaledFont = textRenderer->scaledFont(); int length = 0; // Calculate width/height using the scaled font, divide this result by the scalingFactor afterwards. m_width = scaledFont.width(run, length, m_glyph.name) / scalingFactor; m_height = scaledFont.fontMetrics().floatHeight() / scalingFactor; m_glyph.unicodeString = run.is8Bit() ? String(run.characters8(), length) : String(run.characters16(), length); m_glyph.isValid = true; ASSERT(length >= 0); m_length = static_cast<unsigned>(length); }