void GraphicsContext::drawBidiText(const FontCascade& font, const TextRun& run, const FloatPoint& point, FontCascade::CustomFontNotReadyAction customFontNotReadyAction) { if (paintingDisabled()) return; BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiRuns.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); bool isRTL = bidiRun->level() % 2; subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); float width = font.drawText(*this, subrun, currPoint, 0, -1, customFontNotReadyAction); currPoint.move(width, 0); bidiRun = bidiRun->next(); } bidiRuns.deleteRuns(); }
void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) { if (paintingDisabled()) return; BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPosition(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiRuns.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { TextRun subrun = run; subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start()); bool isRTL = bidiRun->level() % 2; subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); font.drawText(this, subrun, currPoint); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) currPoint.move(font.width(subrun), 0); } bidiRuns.deleteRuns(); }
TextDirection directionForRun(TextRun& run, bool& hasStrongDirectionality) { BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); return bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); }
TextDirection determineParagraphDirectionality(const TextRun& textRun, bool* hasStrongDirectionality = 0) { BidiResolver<TextRunIterator, BidiCharacterRun> resolver; resolver.setStatus(BidiStatus(LTR, false)); resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); return resolver.determineParagraphDirectionality(hasStrongDirectionality); }
// Almost the same as determineDirectionality in core/html/HTMLElement.cpp, but // that one uses a "plain" TextRunIterator (which only checks for '\n'). static TextDirection determineDirectionality(const String& value, bool& hasStrongDirectionality) { TextRun run(value); BidiResolver<VTTTextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(LTR, false)); bidiResolver.setPositionIgnoringNestedIsolates(VTTTextRunIterator(&run, 0)); return bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); }
TEST(BidiResolver, Basic) { bool hasStrongDirectionality; String value("foo"); TextRun run(value); BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); TextDirection direction = bidiResolver.determineParagraphDirectionality(&hasStrongDirectionality); EXPECT_TRUE(hasStrongDirectionality); EXPECT_EQ(LTR, direction); }
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 SVGTextMetricsCalculator::setupBidiRuns() { RenderStyle* style = m_text->style(); m_textDirection = style->direction(); if (isOverride(style->unicodeBidi())) return; BidiStatus status(LTR, false); status.last = status.lastStrong = WTF::Unicode::OtherNeutral; m_bidiResolver.setStatus(status); m_bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&m_run, 0)); const bool hardLineBreak = false; const bool reorderRuns = false; m_bidiResolver.createBidiRunsForLine(TextRunIterator(&m_run, m_run.length()), NoVisualOverride, hardLineBreak, reorderRuns); BidiRunList<BidiCharacterRun>& bidiRuns = m_bidiResolver.runs(); m_bidiRun = bidiRuns.firstRun(); }
void GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) { if (paintingDisabled()) return; BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, new BidiContext(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride()))); bidiResolver.setPosition(TextRunIterator(&run, 0)); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); if (!bidiResolver.runCount()) return; FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiResolver.firstRun(); while (bidiRun) { TextRun subrun = run; subrun.setText(run.data(bidiRun->start()), bidiRun->stop() - bidiRun->start()); subrun.setRTL(bidiRun->level() % 2); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); font.drawText(this, subrun, currPoint); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) currPoint.move(font.floatWidth(subrun), 0.f); } bidiResolver.deleteRuns(); }
// ReverseBidi is a trimmed-down version of GraphicsContext::drawBidiText() void ReverseBidi(UChar* chars, int len) { using namespace WTF::Unicode; WTF::Vector<UChar> result; result.reserveCapacity(len); TextRun run(chars, len); BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); bidiResolver.setStatus(BidiStatus(LeftToRight, LeftToRight, LeftToRight, BidiContext::create(0, LeftToRight, false))); bidiResolver.setPosition(TextRunIterator(&run, 0)); bidiResolver.createBidiRunsForLine(TextRunIterator(&run, len)); if (!bidiRuns.runCount()) return; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { int bidiStart = bidiRun->start(); int bidiStop = bidiRun->stop(); int size = result.size(); int bidiCount = bidiStop - bidiStart; result.append(chars + bidiStart, bidiCount); if (bidiRun->level() % 2) { UChar* start = &result[size]; UChar* end = start + bidiCount; // reverse the order of any RTL substrings while (start < end) { UChar temp = *start; *start++ = *--end; *end = temp; } start = &result[size]; end = start + bidiCount - 1; // if the RTL substring had a surrogate pair, restore its order while (start < end) { UChar trail = *start++; if (!U16_IS_SURROGATE(trail)) continue; start[-1] = *start; // lead *start++ = trail; } } bidiRun = bidiRun->next(); } bidiRuns.deleteRuns(); memcpy(chars, &result[0], len * sizeof(UChar)); }
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(); }
float GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point, Font::CustomFontNotReadyAction customFontNotReadyAction, BidiStatus* status, int length) #endif { if (paintingDisabled()) #if !PLATFORM(IOS) return; #else return 0; #endif BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; #if !PLATFORM(IOS) bidiResolver.setStatus(BidiStatus(run.direction(), run.directionalOverride())); #else bidiResolver.setStatus(status ? *status : BidiStatus(run.direction(), run.directionalOverride())); #endif bidiResolver.setPositionIgnoringNestedIsolates(TextRunIterator(&run, 0)); // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); #if !PLATFORM(IOS) bidiResolver.createBidiRunsForLine(TextRunIterator(&run, run.length())); #else bidiResolver.createBidiRunsForLine(TextRunIterator(&run, length < 0 ? run.length() : length)); #endif if (!bidiRuns.runCount()) #if !PLATFORM(IOS) return; #else return 0; #endif FloatPoint currPoint = point; BidiCharacterRun* bidiRun = bidiRuns.firstRun(); while (bidiRun) { TextRun subrun = run.subRun(bidiRun->start(), bidiRun->stop() - bidiRun->start()); bool isRTL = bidiRun->level() % 2; subrun.setDirection(isRTL ? RTL : LTR); subrun.setDirectionalOverride(bidiRun->dirOverride(false)); #if !PLATFORM(IOS) font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); bidiRun = bidiRun->next(); // FIXME: Have Font::drawText return the width of what it drew so that we don't have to re-measure here. if (bidiRun) currPoint.move(font.width(subrun), 0); #else float width = font.drawText(this, subrun, currPoint, 0, -1, customFontNotReadyAction); currPoint.move(width, 0); bidiRun = bidiRun->next(); #endif } #if PLATFORM(IOS) if (status) *status = bidiResolver.status(); #endif bidiRuns.deleteRuns(); #if PLATFORM(IOS) return currPoint.x() - static_cast<float>(point.x()); #endif }
void BidiTestRunner::runTest(const std::basic_string<UChar>& input, const std::vector<int>& expectedOrder, const std::vector<int>& expectedLevels, bidi_test::ParagraphDirection paragraphDirection, const std::string& line, size_t lineNumber) { if (!m_skippedCodePoints.empty()) { for (size_t i = 0; i < input.size(); i++) { if (m_skippedCodePoints.count(input[i])) { m_testsSkipped++; return; } } } m_testsRun++; TextRun textRun(input.data(), input.size()); switch (paragraphDirection) { case bidi_test::DirectionAutoLTR: textRun.setDirection(determineParagraphDirectionality(textRun)); break; case bidi_test::DirectionLTR: textRun.setDirection(LTR); break; case bidi_test::DirectionRTL: textRun.setDirection(RTL); break; } BidiResolver<TextRunIterator, BidiCharacterRun> resolver; resolver.setStatus(BidiStatus(textRun.direction(), textRun.directionalOverride())); resolver.setPositionIgnoringNestedIsolates(TextRunIterator(&textRun, 0)); BidiRunList<BidiCharacterRun>& runs = resolver.runs(); resolver.createBidiRunsForLine(TextRunIterator(&textRun, textRun.length())); std::ostringstream errorContext; errorContext << ", line " << lineNumber << " \"" << line << "\""; errorContext << " context: " << bidi_test::nameFromParagraphDirection(paragraphDirection); std::vector<int> actualOrder; std::vector<int> actualLevels; actualLevels.assign(input.size(), -1); BidiCharacterRun* run = runs.firstRun(); while (run) { // Blink's UBA just makes runs, the actual ordering of the display of characters // is handled later in our pipeline, so we fake it here: bool reversed = run->reversed(false); ASSERT(run->stop() >= run->start()); size_t length = run->stop() - run->start(); for (size_t i = 0; i < length; i++) { int inputIndex = reversed ? run->stop() - i - 1 : run->start() + i; if (!isNonRenderedCodePoint(input[inputIndex])) actualOrder.push_back(inputIndex); // BidiTest.txt gives expected level data in the order of the original input. actualLevels[inputIndex] = run->level(); } run = run->next(); } if (expectedOrder.size() != actualOrder.size()) { m_ignoredCharFailures++; EXPECT_EQ(expectedOrder.size(), actualOrder.size()) << errorContext.str(); } else if (expectedOrder != actualOrder) { m_orderFailures++; printf("ORDER %s%s\n", diffString(actualOrder, expectedOrder).c_str(), errorContext.str().c_str()); } if (expectedLevels.size() != actualLevels.size()) { m_ignoredCharFailures++; EXPECT_EQ(expectedLevels.size(), actualLevels.size()) << errorContext.str(); } else { for (size_t i = 0; i < expectedLevels.size(); i++) { // level == -1 means the level should be ignored. if (expectedLevels[i] == actualLevels[i] || expectedLevels[i] == -1) continue; printf("LEVELS %s%s\n", diffString(actualLevels, expectedLevels).c_str(), errorContext.str().c_str()); m_levelFailures++; break; } } runs.deleteRuns(); }