void LayoutSVGInlineText::addMetricsFromRun( const TextRun& run, bool& lastCharacterWasWhiteSpace) { Vector<CharacterRange> charRanges = scaledFont().individualCharacterRanges(run); synthesizeGraphemeWidths(run, charRanges); const float cachedFontHeight = scaledFont().getFontMetrics().floatHeight() / m_scalingFactor; const bool preserveWhiteSpace = styleRef().whiteSpace() == PRE; const unsigned runLength = static_cast<unsigned>(run.length()); // TODO(pdr): Character-based iteration is ambiguous and error-prone. It // should be unified under a single concept. See: https://crbug.com/593570 unsigned characterIndex = 0; while (characterIndex < runLength) { bool currentCharacterIsWhiteSpace = run[characterIndex] == ' '; if (!preserveWhiteSpace && lastCharacterWasWhiteSpace && currentCharacterIsWhiteSpace) { m_metrics.append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); characterIndex++; continue; } unsigned length = isValidSurrogatePair(run, characterIndex) ? 2 : 1; float width = charRanges[characterIndex].width() / m_scalingFactor; m_metrics.append(SVGTextMetrics(length, width, cachedFontHeight)); lastCharacterWasWhiteSpace = currentCharacterIsWhiteSpace; characterIndex += length; } }
void SVGTextMetricsBuilder::advanceSimpleText() { #if PLATFORM(QT) && !HAVE(QRAWFONT) ASSERT_NOT_REACHED(); #else GlyphBuffer glyphBuffer; unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer); if (!metricsLength) { m_currentMetrics = SVGTextMetrics(); return; } if (currentCharacterStartsSurrogatePair()) ASSERT(metricsLength == 2); float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth; m_totalWidth = m_simpleWidthIterator->runWidthSoFar(); #if ENABLE(SVG_FONTS) m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, m_simpleWidthIterator->lastGlyphName()); #else m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, emptyString()); #endif #endif }
SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterSimple(unsigned textPosition) { GlyphBuffer glyphBuffer; unsigned metricsLength = m_simpleShaper->advance(textPosition + 1, &glyphBuffer); if (!metricsLength) return SVGTextMetrics(); float currentWidth = m_simpleShaper->runWidthSoFar() - m_totalWidth; m_totalWidth = m_simpleShaper->runWidthSoFar(); return SVGTextMetrics(m_text, textPosition, metricsLength, currentWidth); }
void SVGTextMetricsBuilder::initializeMeasurementWithTextRenderer(RenderSVGInlineText* text) { m_text = text; m_textPosition = 0; m_currentMetrics = SVGTextMetrics(); m_complexStartToCurrentMetrics = SVGTextMetrics(); m_totalWidth = 0; const Font& scaledFont = text->scaledFont(); m_run = SVGTextMetrics::constructTextRun(text, text->characters(), 0, text->textLength()); m_isComplexText = scaledFont.codePath(m_run) == Font::Complex; if (m_isComplexText) m_simpleWidthIterator.clear(); else m_simpleWidthIterator = adoptPtr(new WidthIterator(&scaledFont, m_run)); }
void SVGTextMetricsBuilder::advanceSimpleText() { GlyphBuffer glyphBuffer; unsigned metricsLength = m_simpleWidthIterator->advance(m_textPosition + 1, &glyphBuffer); if (!metricsLength) { m_currentMetrics = SVGTextMetrics(); return; } float currentWidth = m_simpleWidthIterator->runWidthSoFar() - m_totalWidth; m_totalWidth = m_simpleWidthIterator->runWidthSoFar(); #if ENABLE(SVG_FONTS) m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, m_simpleWidthIterator->lastGlyphName()); #else m_currentMetrics = SVGTextMetrics(m_text, m_textPosition, metricsLength, currentWidth, emptyString()); #endif }
static void measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data, bool processRenderer) { ASSERT(text); SVGTextLayoutAttributes* attributes = text->layoutAttributes(); Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues(); if (processRenderer) { if (data->allCharactersMap) attributes->clear(); else textMetricsValues->clear(); } SVGTextMetricsCalculator calculator(text); bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; unsigned surrogatePairCharacters = 0; unsigned skippedCharacters = 0; unsigned textPosition = 0; unsigned textLength = calculator.textLength(); SVGTextMetrics currentMetrics; for (; textPosition < textLength; textPosition += currentMetrics.length()) { currentMetrics = calculator.computeMetricsForCharacter(textPosition); if (!currentMetrics.length()) break; bool characterIsWhiteSpace = calculator.characterIsWhiteSpace(textPosition); if (characterIsWhiteSpace && !preserveWhiteSpace && data->lastCharacterWasWhiteSpace) { if (processRenderer) textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); if (data->allCharactersMap) skippedCharacters += currentMetrics.length(); continue; } if (processRenderer) { if (data->allCharactersMap) { const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + textPosition - skippedCharacters - surrogatePairCharacters + 1); if (it != data->allCharactersMap->end()) attributes->characterDataMap().set(textPosition + 1, it->value); } textMetricsValues->append(currentMetrics); } if (data->allCharactersMap && calculator.characterStartsSurrogatePair(textPosition)) surrogatePairCharacters++; data->lastCharacterWasWhiteSpace = characterIsWhiteSpace; } if (!data->allCharactersMap) return; data->valueListPosition += textPosition - skippedCharacters; }
void SVGTextMetricsBuilder::measureTextRenderer(RenderSVGInlineText* text, MeasureTextData* data) { ASSERT(text); SVGTextLayoutAttributes* attributes = text->layoutAttributes(); Vector<SVGTextMetrics>* textMetricsValues = &attributes->textMetricsValues(); if (data->processRenderer) { if (data->allCharactersMap) attributes->clear(); else textMetricsValues->clear(); } initializeMeasurementWithTextRenderer(text); bool preserveWhiteSpace = text->style()->whiteSpace() == PRE; int surrogatePairCharacters = 0; while (advance()) { const UChar* currentCharacter = m_run.data16(m_textPosition); if (*currentCharacter == ' ' && !preserveWhiteSpace && (!data->lastCharacter || *data->lastCharacter == ' ')) { if (data->processRenderer) textMetricsValues->append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); if (data->allCharactersMap) data->skippedCharacters += m_currentMetrics.length(); continue; } if (data->processRenderer) { if (data->allCharactersMap) { const SVGCharacterDataMap::const_iterator it = data->allCharactersMap->find(data->valueListPosition + m_textPosition - data->skippedCharacters - surrogatePairCharacters + 1); if (it != data->allCharactersMap->end()) attributes->characterDataMap().set(m_textPosition + 1, it->value); } textMetricsValues->append(m_currentMetrics); } if (data->allCharactersMap && currentCharacterStartsSurrogatePair()) surrogatePairCharacters++; data->lastCharacter = currentCharacter; } if (!data->allCharactersMap) return; data->valueListPosition += m_textPosition - data->skippedCharacters; data->skippedCharacters = 0; }
SVGTextMetrics SVGTextMetrics::measureCharacterRange(RenderSVGInlineText* text, unsigned position, unsigned length) { ASSERT(text); return SVGTextMetrics(text, constructTextRun(text, position, length)); }
SVGTextMetrics SVGTextMetrics::measureCharacterRange(LineLayoutSVGInlineText textLayoutItem, unsigned position, unsigned length, TextDirection textDirection) { ASSERT(textLayoutItem); return SVGTextMetrics(textLayoutItem, constructTextRun(textLayoutItem, position, length, textDirection)); }
void SVGTextLayoutAttributesBuilder::propagateLayoutAttributes(RenderObject* start, Vector<SVGTextLayoutAttributes>& allAttributes, unsigned& atCharacter, UChar& lastCharacter) const { for (RenderObject* child = start->firstChild(); child; child = child->nextSibling()) { if (child->isSVGInlineText()) { RenderSVGInlineText* text = toRenderSVGInlineText(child); const UChar* characters = text->characters(); unsigned textLength = text->textLength(); bool preserveWhiteSpace = shouldPreserveAllWhiteSpace(text->style()); SVGTextLayoutAttributes attributes(text); attributes.reserveCapacity(textLength); unsigned valueListPosition = atCharacter; unsigned metricsLength = 1; SVGTextMetrics lastMetrics(SVGTextMetrics::SkippedSpaceMetrics); for (unsigned textPosition = 0; textPosition < textLength; textPosition += metricsLength) { const UChar& currentCharacter = characters[textPosition]; SVGTextMetrics startToCurrentMetrics; SVGTextMetrics currentMetrics; unsigned valueListAdvance = 0; if (U16_IS_LEAD(currentCharacter) && (textPosition + 1) < textLength && U16_IS_TRAIL(characters[textPosition + 1])) { // Handle surrogate pairs. startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 2); currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 2); metricsLength = currentMetrics.length(); valueListAdvance = 1; } else { // Handle BMP characters. startToCurrentMetrics = SVGTextMetrics::measureCharacterRange(text, 0, textPosition + 1); currentMetrics = SVGTextMetrics::measureCharacterRange(text, textPosition, 1); metricsLength = currentMetrics.length(); valueListAdvance = metricsLength; } if (!metricsLength) break; // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken // when rendering the glyph "in context" (with it's surrounding characters) it changes due to shaping. // So whenever runWidthAdvance != currentMetrics.width(), we are processing a text run whose length is // not equal to the sum of the individual lengths of the glyphs, when measuring them isolated. float runWidthAdvance = startToCurrentMetrics.width() - lastMetrics.width(); if (runWidthAdvance != currentMetrics.width()) currentMetrics.setWidth(runWidthAdvance); lastMetrics = startToCurrentMetrics; if (!preserveWhiteSpace && characterIsSpace(currentCharacter) && characterIsSpaceOrNull(lastCharacter)) { attributes.positioningLists().appendEmptyValues(); attributes.textMetricsValues().append(SVGTextMetrics(SVGTextMetrics::SkippedSpaceMetrics)); continue; } SVGTextLayoutAttributes::PositioningLists& positioningLists = attributes.positioningLists(); positioningLists.appendValuesFromPosition(m_positioningLists, valueListPosition); attributes.textMetricsValues().append(currentMetrics); // Pad x/y/dx/dy/rotate value lists with empty values, if the metrics span more than one character. if (metricsLength > 1) { for (unsigned i = 0; i < metricsLength - 1; ++i) positioningLists.appendEmptyValues(); } lastCharacter = currentCharacter; valueListPosition += valueListAdvance; } #if DUMP_TEXT_LAYOUT_ATTRIBUTES > 0 fprintf(stderr, "\nDumping layout attributes for RenderSVGInlineText, renderer=%p, node=%p (atCharacter: %i)\n", text, text->node(), atCharacter); fprintf(stderr, "BiDi properties: unicode-bidi=%i, block direction=%i\n", text->style()->unicodeBidi(), text->style()->direction()); attributes.dump(); #endif text->storeLayoutAttributes(attributes); allAttributes.append(attributes); atCharacter = valueListPosition; continue; } if (!child->isSVGInline()) continue; propagateLayoutAttributes(child, allAttributes, atCharacter, lastCharacter); } }