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