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); } }
void LayoutSVGInlineText::updateMetricsList(bool& lastCharacterWasWhiteSpace) { m_metrics.clear(); if (!textLength()) return; TextRun run = constructTextRun(*this, 0, textLength(), styleRef().direction()); BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bool bidiOverride = isOverride(styleRef().unicodeBidi()); BidiStatus status(LTR, bidiOverride); if (run.is8Bit() || bidiOverride) { WTF::Unicode::CharDirection direction = WTF::Unicode::LeftToRight; // If BiDi override is in effect, use the specified direction. if (bidiOverride && !styleRef().isLeftToRightDirection()) direction = WTF::Unicode::RightToLeft; bidiRuns.addRun(new BidiCharacterRun(0, run.charactersLength(), status.context.get(), direction)); } else { status.last = status.lastStrong = WTF::Unicode::OtherNeutral; bidiResolver.setStatus(status); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); const bool hardLineBreak = false; const bool reorderRuns = false; bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length()), NoVisualOverride, hardLineBreak, reorderRuns); } for (const BidiCharacterRun* bidiRun = bidiRuns.firstRun(); bidiRun; bidiRun = bidiRun->next()) { TextRun subRun = constructTextRun(*this, bidiRun->start(), bidiRun->stop() - bidiRun->start(), bidiRun->direction()); addMetricsFromRun(subRun, lastCharacterWasWhiteSpace); } bidiResolver.runs().deleteRuns(); }
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()); }
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(); }
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); }
float ShapeResultSpacing::computeSpacing(const TextRun& run, size_t index, float& offset) { UChar32 character = run[index]; bool treatAsSpace = (Character::treatAsSpace(character) || (m_normalizeSpace && Character::isNormalizedCanvasSpaceCharacter(character))) && (character != '\t' || !m_allowTabs); if (treatAsSpace && character != noBreakSpaceCharacter) character = spaceCharacter; float spacing = 0; if (m_letterSpacing && !Character::treatAsZeroWidthSpace(character)) spacing += m_letterSpacing; if (treatAsSpace && (index || !isFirstRun(run) || character == noBreakSpaceCharacter)) spacing += m_wordSpacing; if (!hasExpansion()) return spacing; if (treatAsSpace) return spacing + nextExpansion(); if (run.is8Bit() || m_textJustify != TextJustify::TextJustifyAuto) return spacing; // isCJKIdeographOrSymbol() has expansion opportunities both before and // after each character. // http://www.w3.org/TR/jlreq/#line_adjustment if (U16_IS_LEAD(character) && index + 1 < run.length() && U16_IS_TRAIL(run[index + 1])) character = U16_GET_SUPPLEMENTARY(character, run[index + 1]); if (!Character::isCJKIdeographOrSymbol(character)) { m_isAfterExpansion = false; return spacing; } if (!m_isAfterExpansion) { // Take the expansion opportunity before this ideograph. float expandBefore = nextExpansion(); if (expandBefore) { offset += expandBefore; spacing += expandBefore; } if (!hasExpansion()) return spacing; } return spacing + nextExpansion(); }
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); }
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); }
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; }