SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacter(unsigned textPosition) { if (m_bidiRun) { if (textPosition >= static_cast<unsigned>(m_bidiRun->stop())) { m_bidiRun = m_bidiRun->next(); // New BiDi run means new reference position for measurements, so reset |m_totalWidth|. m_totalWidth = 0; } ASSERT(m_bidiRun); ASSERT(static_cast<int>(textPosition) < m_bidiRun->stop()); m_textDirection = m_bidiRun->direction(); } unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1; SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection); ASSERT(metrics.length() == metricsLength); unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0; ASSERT(startPosition <= textPosition); SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection); // Frequent case for Arabic text: when measuring a single character the arabic isolated form is taken // when laying out the glyph "in context" (with it's surrounding characters) it changes due to shaping. // So whenever currentWidth != 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 currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; if (currentWidth != metrics.width()) metrics.setWidth(currentWidth); m_totalWidth = complexStartToCurrentMetrics.width(); return metrics; }
static inline void calculateGlyphBoundaries(SVGTextQuery::Data* queryData, const SVGTextFragment& fragment, int startPosition, FloatRect& extent) { float scalingFactor = queryData->textRenderer->scalingFactor(); ASSERT(scalingFactor); extent.setLocation(FloatPoint(fragment.x, fragment.y - queryData->textRenderer->scaledFont().fontMetrics().floatAscent() / scalingFactor)); if (startPosition) { SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition); if (queryData->isVerticalText) extent.move(0, metrics.height()); else extent.move(metrics.width(), 0); } SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, 1); extent.setSize(FloatSize(metrics.width(), metrics.height())); AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); if (fragmentTransform.isIdentity()) return; extent = fragmentTransform.mapRect(extent); }
bool SVGTextLayoutEngine::currentLogicalCharacterMetrics(SVGTextLayoutAttributes*& logicalAttributes, SVGTextMetrics& logicalMetrics) { const Vector<SVGTextMetrics>* textMetricsValues = &logicalAttributes->textMetricsValues(); unsigned textMetricsSize = textMetricsValues->size(); while (true) { if (m_logicalMetricsListOffset == textMetricsSize) { if (!currentLogicalCharacterAttributes(logicalAttributes)) return false; textMetricsValues = &logicalAttributes->textMetricsValues(); textMetricsSize = textMetricsValues->size(); continue; } ASSERT(textMetricsSize); ASSERT(m_logicalMetricsListOffset < textMetricsSize); logicalMetrics = textMetricsValues->at(m_logicalMetricsListOffset); if (logicalMetrics.isEmpty() || (!logicalMetrics.width() && !logicalMetrics.height())) { advanceToNextLogicalCharacter(logicalMetrics); continue; } // Stop if we found the next valid logical text metrics object. return true; } ASSERT_NOT_REACHED(); return true; }
bool SVGTextQuery::endPositionOfCharacterCallback(Data* queryData, const SVGTextFragment& fragment) const { EndPositionOfCharacterData* data = static_cast<EndPositionOfCharacterData*>(queryData); int startPosition = data->position; int endPosition = startPosition + 1; if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) return false; data->endPosition = FloatPoint(fragment.x, fragment.y); SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset, startPosition + 1); if (queryData->isVerticalText) data->endPosition.move(0, metrics.height()); else data->endPosition.move(metrics.width(), 0); AffineTransform fragmentTransform; fragment.buildFragmentTransform(fragmentTransform, SVGTextFragment::TransformIgnoringTextLength); if (fragmentTransform.isIdentity()) return true; data->endPosition = fragmentTransform.mapPoint(data->endPosition); return true; }
float SVGTextLayoutEngineBaseline::calculateGlyphAdvanceAndOrientation(bool isVerticalText, const SVGTextMetrics& metrics, float angle, float& xOrientationShift, float& yOrientationShift) const { bool orientationIsMultiplyOf180Degrees = glyphOrientationIsMultiplyOf180Degrees(angle); // The function is based on spec requirements: // // Spec: If the 'glyph-orientation-horizontal' results in an orientation angle that is not a multiple of // of 180 degrees, then the current text position is incremented according to the vertical metrics of the glyph. // // Spec: If if the 'glyph-orientation-vertical' results in an orientation angle that is not a multiple of // 180 degrees, then the current text position is incremented according to the horizontal metrics of the glyph. const FontMetrics& fontMetrics = m_font.fontMetrics(); float ascent = fontMetrics.floatAscent() / m_effectiveZoom; float descent = fontMetrics.floatDescent() / m_effectiveZoom; // Vertical orientation handling. if (isVerticalText) { float ascentMinusDescent = ascent - descent; if (!angle) { xOrientationShift = (ascentMinusDescent - metrics.width()) / 2; yOrientationShift = ascent; } else if (angle == 180) { xOrientationShift = (ascentMinusDescent + metrics.width()) / 2; } else if (angle == 270) { yOrientationShift = metrics.width(); xOrientationShift = ascentMinusDescent; } // Vertical advance calculation. if (angle && !orientationIsMultiplyOf180Degrees) return metrics.width(); return metrics.height(); } // Horizontal orientation handling. if (angle == 90) { yOrientationShift = -metrics.width(); } else if (angle == 180) { xOrientationShift = metrics.width(); yOrientationShift = -ascent; } else if (angle == 270) { xOrientationShift = metrics.width(); } // Horizontal advance calculation. if (angle && !orientationIsMultiplyOf180Degrees) return metrics.height(); return metrics.width(); }
SVGTextMetrics SVGTextMetricsCalculator::computeMetricsForCharacterComplex(unsigned textPosition) { unsigned metricsLength = characterStartsSurrogatePair(textPosition) ? 2 : 1; SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(m_text, textPosition, metricsLength, m_textDirection); ASSERT(metrics.length() == metricsLength); unsigned startPosition = m_bidiRun ? m_bidiRun->start() : 0; ASSERT(startPosition <= textPosition); SVGTextMetrics complexStartToCurrentMetrics = SVGTextMetrics::measureCharacterRange(m_text, startPosition, textPosition - startPosition + metricsLength, m_textDirection); // 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 currentWidth != 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 currentWidth = complexStartToCurrentMetrics.width() - m_totalWidth; if (currentWidth != metrics.width()) metrics.setWidth(currentWidth); m_totalWidth = complexStartToCurrentMetrics.width(); return metrics; }
bool SVGTextQuery::subStringLengthCallback(Data* queryData, const SVGTextFragment& fragment) const { SubStringLengthData* data = static_cast<SubStringLengthData*>(queryData); int startPosition = data->startPosition; int endPosition = startPosition + data->length; if (!mapStartEndPositionsIntoFragmentCoordinates(queryData, fragment, startPosition, endPosition)) return false; SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textRenderer, fragment.characterOffset + startPosition, endPosition - startPosition); data->subStringLength += queryData->isVerticalText ? metrics.height() : metrics.width(); return false; }
static FloatPoint calculateGlyphPositionWithoutTransform(const QueryData* queryData, const SVGTextFragment& fragment, int offsetInFragment) { float glyphOffsetInDirection = 0; if (offsetInFragment) { SVGTextMetrics metrics = SVGTextMetrics::measureCharacterRange(queryData->textLayoutObject, fragment.characterOffset, offsetInFragment, queryData->textBox->direction()); if (queryData->isVerticalText) glyphOffsetInDirection = metrics.height(); else glyphOffsetInDirection = metrics.width(); } if (!queryData->textBox->isLeftToRightDirection()) { float fragmentExtent = queryData->isVerticalText ? fragment.height : fragment.width; glyphOffsetInDirection = fragmentExtent - glyphOffsetInDirection; } FloatPoint glyphPosition(fragment.x, fragment.y); if (queryData->isVerticalText) glyphPosition.move(0, glyphOffsetInDirection); else glyphPosition.move(glyphOffsetInDirection, 0); return glyphPosition; }
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); } }