Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(125)

Unified Diff: third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp

Issue 2740083002: Add support for shaper-driven line breaking to HarfBuzzShaper (Closed)
Patch Set: Try to fix test failures Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
diff --git a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
index 7eb964e6c722b31d771abc344b1401af89953dbf..754b7e1cf6bd1e1d1016f95d3724fea1496adff0 100644
--- a/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
+++ b/third_party/WebKit/Source/platform/fonts/shaping/HarfBuzzShaper.cpp
@@ -31,6 +31,12 @@
#include "platform/fonts/shaping/HarfBuzzShaper.h"
+#include <hb.h>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+#include <algorithm>
+#include <memory>
+#include "platform/LayoutUnit.h"
#include "platform/fonts/Font.h"
#include "platform/fonts/FontDescription.h"
#include "platform/fonts/FontFallbackIterator.h"
@@ -45,11 +51,6 @@
#include "wtf/MathExtras.h"
#include "wtf/PtrUtil.h"
#include "wtf/text/Unicode.h"
-#include <algorithm>
-#include <hb.h>
-#include <memory>
-#include <unicode/uchar.h>
-#include <unicode/uscript.h>
namespace blink {
enum HolesQueueItemAction { HolesQueueNextFont, HolesQueueRange };
@@ -707,4 +708,169 @@ PassRefPtr<ShapeResult> HarfBuzzShaper::shape(const Font* font,
TextDirection direction) const {
return shape(font, direction, 0, m_textLength);
}
+
+namespace {
+
+unsigned previousBreakOpportunity(unsigned start,
+ unsigned offset,
+ unsigned textLength,
+ LazyLineBreakIterator& breakIterator,
+ LineBreakType breakType) {
+ unsigned pos = std::min(start + offset, textLength);
+ for (; pos > start; pos--) {
+ int nextBreak = 0;
+ if (breakIterator.isBreakable(pos, nextBreak, breakType))
+ return pos;
+ }
+ return start;
+}
+
+unsigned nextBreakOpportunity(unsigned offset,
+ LazyLineBreakIterator& breakIterator,
+ LineBreakType breakType) {
+ int nextBreak = 0;
+ breakIterator.isBreakable(offset, nextBreak, breakType);
+ return nextBreak;
+}
+
+unsigned previousSafeToBreakAfter(const UChar* text,
+ unsigned start,
+ unsigned offset) {
+ // TODO: This should use HarfBuzz safe to break info when available. For now
+ // it'll scan back until the previous space character or something.
+ for (; offset > start; offset--) {
+ if (text[offset - 1] == spaceCharacter)
+ break;
+ }
+ return offset;
+}
+
+unsigned nextSafeToBreakBefore(const UChar* text,
+ unsigned end,
+ unsigned offset) {
+ for (; offset < end; offset++) {
+ if (text[offset] == spaceCharacter)
+ break;
+ }
+ return offset;
+}
+
+} // namespace
+
+// Shapes a line of text by finding a valid and appropriate break opportunity
+// based on the shaping results for the entire paragraph. Re-shapes the start
+// and end of the line as needed.
+//
+// Definitions:
+// Candidate break opportunity: Ideal point to break, disregarding line
+// breaking rules. May be in the middle of a word
+// or inside a ligature.
+// Valid break opportunity: A point where a break is allowed according to
+// the relevant breaking rules.
+// Safe-to-break: A point where a break may occur without
+// affecting the rendering or metrics of the
+// text. Breaking at safe-to-break point does not
+// require reshaping.
+//
+// For example:
+// Given the string "Line breaking example", an available space of 100px and a
+// mono-space font where each glyph is 10px wide.
+//
+// Line breaking example
+// | |
+// 0 100px
+//
+// The candidate (or ideal) break opportunity would be at an offset of 10 as
+// the break would happen at exactly 100px in that case.
+// The previous valid break opportunity though is at an offset of 5.
+// If we further assume that the font kerns with space then even though it's a
+// valid break opportunity reshaping is required as the combined width of the
+// two segments "Line " and "breaking" may be different from "Line breaking".
+PassRefPtr<ShapeResult> HarfBuzzShaper::shapeLine(const Font* font,
+ const ShapeResult* result,
+ unsigned startOffset,
+ const AtomicString locale,
+ LineBreakType breakType,
+ LayoutUnit availableSpace,
+ unsigned* breakOffset) const {
+ // The start position in the original shape results.
+ LayoutUnit startPosition = result->snappedStartPositionForOffset(startOffset);
+
+ // If the start offset is not at a safe-to-break boundary the content between
+ // the start and the next safe-to-break boundary needs to be reshaped and the
+ // available space adjusted to take the reshaping into account.
+ RefPtr<ShapeResult> lineStartResult;
+ unsigned firstSafe = nextSafeToBreakBefore(m_text, m_textLength, startOffset);
+ if (firstSafe != startOffset) {
+ LayoutUnit originalWidth =
+ result->snappedEndPositionForOffset(firstSafe) - startPosition;
+ lineStartResult = shape(font, result->direction(), startOffset, firstSafe);
+ availableSpace += lineStartResult->snappedWidth() - originalWidth;
+ }
+
+ // Find a candidate break opportunity by identifying the last offset before
+ // exceeding the available space and the determine the closest valid break
+ // preceding the candidate.
+ LazyLineBreakIterator breakIterator(String(m_text, m_textLength), locale);
+ LayoutUnit endPosition = startPosition + availableSpace;
+ unsigned candidateBreak = result->offsetForPosition(endPosition, false);
+ unsigned breakOpportunity = previousBreakOpportunity(
+ startOffset, candidateBreak, m_textLength, breakIterator, breakType);
+ if (breakOpportunity <= startOffset) {
+ breakOpportunity =
+ nextBreakOpportunity(candidateBreak, breakIterator, breakType);
+ }
+
+ RefPtr<ShapeResult> lineEndResult;
+ unsigned lastSafe = breakOpportunity;
+ while (breakOpportunity > startOffset) {
+ // If the previous valid break opportunity is not at a safe-to-break
+ // boundary reshape between the safe-to-break offset and the valid break
+ // offset. If the resulting width exceeds the available space the
+ // preceding boundary is tried until the available space is sufficient.
+ unsigned previousSafe = std::max(
+ previousSafeToBreakAfter(m_text, startOffset, breakOpportunity),
+ startOffset);
+ if (previousSafe != breakOpportunity) {
+ LayoutUnit safePosition =
+ result->snappedStartPositionForOffset(previousSafe);
+ while (breakOpportunity > previousSafe && previousSafe > startOffset) {
+ lineEndResult =
+ shape(font, result->direction(), previousSafe, breakOpportunity);
+ if (safePosition + lineEndResult->snappedWidth() <= endPosition)
+ break;
+ lineEndResult = nullptr;
+ breakOpportunity =
+ previousBreakOpportunity(startOffset, breakOpportunity - 1,
+ m_textLength, breakIterator, breakType);
+ }
+ }
+
+ if (breakOpportunity > startOffset) {
+ lastSafe = previousSafe;
+ break;
+ }
+
+ // No suitable break opportunity, not exceeding the available space,
+ // found. Choose the next valid one even though it will overflow.
+ breakOpportunity =
+ nextBreakOpportunity(candidateBreak, breakIterator, breakType);
+ }
+
+ // Create shape results for the line by copying from the re-shaped result (if
+ // reshaping was needed) and the original shape results.
+ RefPtr<ShapeResult> lineResult =
+ ShapeResult::create(font, 0, result->direction());
+ unsigned maxLength = std::numeric_limits<unsigned>::max();
+ if (lineStartResult)
+ lineStartResult->copyRange(0, maxLength, lineResult.get());
+ if (lastSafe > firstSafe)
+ result->copyRange(firstSafe, lastSafe, lineResult.get());
+ if (lineEndResult)
+ lineEndResult->copyRange(lastSafe, maxLength, lineResult.get());
+
+ *breakOffset = breakOpportunity;
+ return lineResult.release();
+}
+
} // namespace blink

Powered by Google App Engine
This is Rietveld 408576698