1 /*
2 * Copyright 2016 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8 #include "include/core/SkFontMetrics.h"
9 #include "include/core/SkStream.h"
10 #include "include/core/SkTypeface.h"
11 #include "include/private/base/SkTo.h"
12 #include "modules/skshaper/include/SkShaper.h"
13 #include "src/base/SkUTF.h"
14
15 class SkShaperPrimitive : public SkShaper {
16 public:
SkShaperPrimitive()17 SkShaperPrimitive() {}
18 private:
19 void shape(const char* utf8, size_t utf8Bytes,
20 const SkFont& srcFont,
21 bool leftToRight,
22 SkScalar width,
23 RunHandler*) const override;
24
25 void shape(const char* utf8, size_t utf8Bytes,
26 FontRunIterator&,
27 BiDiRunIterator&,
28 ScriptRunIterator&,
29 LanguageRunIterator&,
30 SkScalar width,
31 RunHandler*) const override;
32
33 void shape(const char* utf8, size_t utf8Bytes,
34 FontRunIterator&,
35 BiDiRunIterator&,
36 ScriptRunIterator&,
37 LanguageRunIterator&,
38 const Feature*, size_t featureSize,
39 SkScalar width,
40 RunHandler*) const override;
41 };
42
MakePrimitive()43 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
44 return std::make_unique<SkShaperPrimitive>();
45 }
46
is_breaking_whitespace(SkUnichar c)47 static inline bool is_breaking_whitespace(SkUnichar c) {
48 switch (c) {
49 case 0x0020: // SPACE
50 //case 0x00A0: // NO-BREAK SPACE
51 case 0x1680: // OGHAM SPACE MARK
52 case 0x180E: // MONGOLIAN VOWEL SEPARATOR
53 case 0x2000: // EN QUAD
54 case 0x2001: // EM QUAD
55 case 0x2002: // EN SPACE (nut)
56 case 0x2003: // EM SPACE (mutton)
57 case 0x2004: // THREE-PER-EM SPACE (thick space)
58 case 0x2005: // FOUR-PER-EM SPACE (mid space)
59 case 0x2006: // SIX-PER-EM SPACE
60 case 0x2007: // FIGURE SPACE
61 case 0x2008: // PUNCTUATION SPACE
62 case 0x2009: // THIN SPACE
63 case 0x200A: // HAIR SPACE
64 case 0x200B: // ZERO WIDTH SPACE
65 case 0x202F: // NARROW NO-BREAK SPACE
66 case 0x205F: // MEDIUM MATHEMATICAL SPACE
67 case 0x3000: // IDEOGRAPHIC SPACE
68 //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
69 return true;
70 default:
71 return false;
72 }
73 }
74
linebreak(const char text[],const char stop[],const SkFont & font,SkScalar width,SkScalar * advance,size_t * trailing)75 static size_t linebreak(const char text[], const char stop[],
76 const SkFont& font, SkScalar width,
77 SkScalar* advance,
78 size_t* trailing)
79 {
80 SkScalar accumulatedWidth = 0;
81 int glyphIndex = 0;
82 const char* start = text;
83 const char* wordStart = text;
84 bool prevWS = true;
85 *trailing = 0;
86
87 while (text < stop) {
88 const char* prevText = text;
89 SkUnichar uni = SkUTF::NextUTF8(&text, stop);
90 accumulatedWidth += advance[glyphIndex++];
91 bool currWS = is_breaking_whitespace(uni);
92
93 if (!currWS && prevWS) {
94 wordStart = prevText;
95 }
96 prevWS = currWS;
97
98 if (width < accumulatedWidth) {
99 bool consumeWhitespace = false;
100 if (currWS) {
101 // previous fit, put this and following whitespace in trailing
102 if (prevText == start) {
103 // don't put this in trailing if it's the first thing
104 prevText = text;
105 }
106 consumeWhitespace = true;
107 } else if (wordStart != start) {
108 // backup to the last whitespace that fit
109 text = wordStart;
110 } else if (prevText > start) {
111 // backup to just before the glyph that didn't fit
112 text = prevText;
113 } else {
114 // let it overflow, put any following whitespace in trailing
115 prevText = text;
116 consumeWhitespace = true;
117 }
118 if (consumeWhitespace) {
119 const char* next = text;
120 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
121 text = next;
122 }
123 if (trailing) {
124 *trailing = text - prevText;
125 }
126 }
127 break;
128 }
129 }
130
131 return text - start;
132 }
133
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,SkScalar width,RunHandler * handler) const134 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
135 FontRunIterator& font,
136 BiDiRunIterator& bidi,
137 ScriptRunIterator&,
138 LanguageRunIterator&,
139 SkScalar width,
140 RunHandler* handler) const
141 {
142 SkFont skfont;
143 if (!font.atEnd()) {
144 font.consume();
145 skfont = font.currentFont();
146 } else {
147 skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
148 }
149 SkASSERT(skfont.getTypeface());
150 bool skbidi = 0;
151 if (!bidi.atEnd()) {
152 bidi.consume();
153 skbidi = (bidi.currentLevel() % 2) == 0;
154 }
155 return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
156 }
157
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,const Feature *,size_t,SkScalar width,RunHandler * handler) const158 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
159 FontRunIterator& font,
160 BiDiRunIterator& bidi,
161 ScriptRunIterator&,
162 LanguageRunIterator&,
163 const Feature*, size_t,
164 SkScalar width,
165 RunHandler* handler) const {
166 font.consume();
167 SkASSERT(font.currentFont().getTypeface());
168 bidi.consume();
169 return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
170 width, handler);
171 }
172
shape(const char * utf8,size_t utf8Bytes,const SkFont & font,bool leftToRight,SkScalar width,RunHandler * handler) const173 void SkShaperPrimitive::shape(const char* utf8, size_t utf8Bytes,
174 const SkFont& font,
175 bool leftToRight,
176 SkScalar width,
177 RunHandler* handler) const {
178 sk_ignore_unused_variable(leftToRight);
179
180 int glyphCount = font.countText(utf8, utf8Bytes, SkTextEncoding::kUTF8);
181 if (glyphCount < 0) {
182 return;
183 }
184
185 std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
186 font.textToGlyphs(utf8, utf8Bytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
187
188 std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
189 font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
190
191 size_t glyphOffset = 0;
192 size_t utf8Offset = 0;
193 do {
194 size_t bytesCollapsed;
195 size_t bytesConsumed = linebreak(utf8, utf8 + utf8Bytes, font, width,
196 advances.get() + glyphOffset, &bytesCollapsed);
197 size_t bytesVisible = bytesConsumed - bytesCollapsed;
198
199 size_t numGlyphs = SkUTF::CountUTF8(utf8, bytesVisible);
200 const RunHandler::RunInfo info = {
201 font,
202 0,
203 { font.measureText(utf8, bytesVisible, SkTextEncoding::kUTF8), 0 },
204 numGlyphs,
205 RunHandler::Range(utf8Offset, bytesVisible)
206 };
207 handler->beginLine();
208 if (info.glyphCount) {
209 handler->runInfo(info);
210 }
211 handler->commitRunInfo();
212 if (info.glyphCount) {
213 const auto buffer = handler->runBuffer(info);
214
215 memcpy(buffer.glyphs, glyphs.get() + glyphOffset, info.glyphCount * sizeof(SkGlyphID));
216 SkPoint position = buffer.point;
217 for (size_t i = 0; i < info.glyphCount; ++i) {
218 buffer.positions[i] = position;
219 position.fX += advances[i + glyphOffset];
220 }
221 if (buffer.clusters) {
222 const char* txtPtr = utf8;
223 for (size_t i = 0; i < info.glyphCount; ++i) {
224 // Each character maps to exactly one glyph.
225 buffer.clusters[i] = SkToU32(txtPtr - utf8 + utf8Offset);
226 SkUTF::NextUTF8(&txtPtr, utf8 + utf8Bytes);
227 }
228 }
229 handler->commitRunBuffer(info);
230 }
231 handler->commitLine();
232
233 glyphOffset += SkUTF::CountUTF8(utf8, bytesConsumed);
234 utf8Offset += bytesConsumed;
235 utf8 += bytesConsumed;
236 utf8Bytes -= bytesConsumed;
237 } while (0 < utf8Bytes);
238
239 return;
240 }
241