// 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); }
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); }
// FIXME: This should be a BidiStatus constructor or create method. static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride) { WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft; RefPtr<BidiContext> context = BidiContext::create( textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM); // This copies BidiStatus and may churn the ref on BidiContext. // I doubt it matters. return BidiStatus(direction, direction, direction, context.release()); }
TextDirection determinePlaintextDirectionality(LayoutObject* root, LayoutObject* current, unsigned pos) { LayoutObject* firstLayoutObject = firstLayoutObjectForDirectionalityDetermination(root, current); InlineIterator iter(LineLayoutItem(root), LineLayoutItem(firstLayoutObject), firstLayoutObject == current ? pos : 0); InlineBidiResolver observer; observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi()))); observer.setPositionIgnoringNestedIsolates(iter); return observer.determineParagraphDirectionality(); }
TextDirection determinePlaintextDirectionality(RenderObject* root, RenderObject* current, unsigned pos) { InlineIterator iter( root, firstRenderObjectForDirectionalityDetermination(root, current), pos); InlineBidiResolver observer; observer.setStatus(BidiStatus(root->style()->direction(), isOverride(root->style()->unicodeBidi()))); observer.setPositionIgnoringNestedIsolates(iter); return observer.determineParagraphDirectionality(); }
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 GraphicsContext::drawBidiText(const Font& font, const TextRun& run, const FloatPoint& point) { if (paintingDisabled()) return; // FIXME: This ownership should be reversed. We should pass BidiRunList // to BidiResolver in createBidiRunsForLine. BidiResolver<TextRunIterator, BidiCharacterRun> bidiResolver; BidiRunList<BidiCharacterRun>& bidiRuns = bidiResolver.runs(); WTF::Unicode::Direction paragraphDirection = run.ltr() ? WTF::Unicode::LeftToRight : WTF::Unicode::RightToLeft; bidiResolver.setStatus(BidiStatus(paragraphDirection, paragraphDirection, paragraphDirection, BidiContext::create(run.ltr() ? 0 : 1, paragraphDirection, run.directionalOverride()))); bidiResolver.setPosition(TextRunIterator(&run, 0)); 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()); 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.width(subrun), 0); } bidiRuns.deleteRuns(); }
BidiStatus RootInlineBox::lineBreakBidiStatus() const { return BidiStatus(m_lineBreakBidiStatusEor, m_lineBreakBidiStatusLastStrong, m_lineBreakBidiStatusLast, m_lineBreakContext); }
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(); }
BidiStatus RootInlineBox::lineBreakBidiStatus() const { return BidiStatus(static_cast<UCharDirection>(m_lineBreakBidiStatusEor), static_cast<UCharDirection>(m_lineBreakBidiStatusLastStrong), static_cast<UCharDirection>(m_lineBreakBidiStatusLast), m_lineBreakContext); }