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