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 "SkFontMetrics.h"
9 #include "SkMakeUnique.h"
10 #include "SkShaper.h"
11 #include "SkStream.h"
12 #include "SkTo.h"
13 #include "SkTypeface.h"
14 #include "SkUTF.h"
15 
16 class SkShaperPrimitive : public SkShaper {
17 public:
SkShaperPrimitive()18     SkShaperPrimitive() {}
19 private:
20     SkPoint shape(RunHandler* handler,
21                   const SkFont& srcFont,
22                   const char* utf8text,
23                   size_t textBytes,
24                   bool leftToRight,
25                   SkPoint point,
26                   SkScalar width) const override;
27 };
28 
MakePrimitive()29 std::unique_ptr<SkShaper> SkShaper::MakePrimitive() {
30     return skstd::make_unique<SkShaperPrimitive>();
31 }
32 
is_breaking_whitespace(SkUnichar c)33 static inline bool is_breaking_whitespace(SkUnichar c) {
34     switch (c) {
35         case 0x0020: // SPACE
36         //case 0x00A0: // NO-BREAK SPACE
37         case 0x1680: // OGHAM SPACE MARK
38         case 0x180E: // MONGOLIAN VOWEL SEPARATOR
39         case 0x2000: // EN QUAD
40         case 0x2001: // EM QUAD
41         case 0x2002: // EN SPACE (nut)
42         case 0x2003: // EM SPACE (mutton)
43         case 0x2004: // THREE-PER-EM SPACE (thick space)
44         case 0x2005: // FOUR-PER-EM SPACE (mid space)
45         case 0x2006: // SIX-PER-EM SPACE
46         case 0x2007: // FIGURE SPACE
47         case 0x2008: // PUNCTUATION SPACE
48         case 0x2009: // THIN SPACE
49         case 0x200A: // HAIR SPACE
50         case 0x200B: // ZERO WIDTH SPACE
51         case 0x202F: // NARROW NO-BREAK SPACE
52         case 0x205F: // MEDIUM MATHEMATICAL SPACE
53         case 0x3000: // IDEOGRAPHIC SPACE
54         //case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
55             return true;
56         default:
57             return false;
58     }
59 }
60 
linebreak(const char text[],const char stop[],const SkFont & font,SkScalar width,SkScalar * advance,size_t * trailing)61 static size_t linebreak(const char text[], const char stop[],
62                         const SkFont& font, SkScalar width,
63                         SkScalar* advance,
64                         size_t* trailing)
65 {
66     SkScalar accumulatedWidth = 0;
67     int glyphIndex = 0;
68     const char* start = text;
69     const char* word_start = text;
70     bool prevWS = true;
71     *trailing = 0;
72 
73     while (text < stop) {
74         const char* prevText = text;
75         SkUnichar uni = SkUTF::NextUTF8(&text, stop);
76         accumulatedWidth += advance[glyphIndex++];
77         bool currWS = is_breaking_whitespace(uni);
78 
79         if (!currWS && prevWS) {
80             word_start = prevText;
81         }
82         prevWS = currWS;
83 
84         if (width < accumulatedWidth) {
85             if (currWS) {
86                 // eat the rest of the whitespace
87                 const char* next = text;
88                 while (next < stop && is_breaking_whitespace(SkUTF::NextUTF8(&next, stop))) {
89                     text = next;
90                 }
91                 if (trailing) {
92                     *trailing = text - prevText;
93                 }
94             } else {
95                 // backup until a whitespace (or 1 char)
96                 if (word_start == start) {
97                     if (prevText > start) {
98                         text = prevText;
99                     }
100                 } else {
101                     text = word_start;
102                 }
103             }
104             break;
105         }
106 
107         if ('\n' == uni) {
108             size_t ret = text - start;
109             size_t lineBreakSize = 1;
110             if (text < stop) {
111                 uni = SkUTF::NextUTF8(&text, stop);
112                 if ('\r' == uni) {
113                     ret = text - start;
114                     ++lineBreakSize;
115                 }
116             }
117             if (trailing) {
118                 *trailing = lineBreakSize;
119             }
120             return ret;
121         }
122 
123         if ('\r' == uni) {
124             size_t ret = text - start;
125             size_t lineBreakSize = 1;
126             if (text < stop) {
127                 uni = SkUTF::NextUTF8(&text, stop);
128                 if ('\n' == uni) {
129                     ret = text - start;
130                     ++lineBreakSize;
131                 }
132             }
133             if (trailing) {
134                 *trailing = lineBreakSize;
135             }
136             return ret;
137         }
138     }
139 
140     return text - start;
141 }
142 
shape(RunHandler * handler,const SkFont & font,const char * utf8text,size_t textBytes,bool leftToRight,SkPoint point,SkScalar width) const143 SkPoint SkShaperPrimitive::shape(RunHandler* handler,
144                                  const SkFont& font,
145                                  const char* utf8text,
146                                  size_t textBytes,
147                                  bool leftToRight,
148                                  SkPoint point,
149                                  SkScalar width) const {
150     sk_ignore_unused_variable(leftToRight);
151 
152     int glyphCount = font.countText(utf8text, textBytes, SkTextEncoding::kUTF8);
153     if (glyphCount <= 0) {
154         return point;
155     }
156 
157     std::unique_ptr<SkGlyphID[]> glyphs(new SkGlyphID[glyphCount]);
158     font.textToGlyphs(utf8text, textBytes, SkTextEncoding::kUTF8, glyphs.get(), glyphCount);
159 
160     std::unique_ptr<SkScalar[]> advances(new SkScalar[glyphCount]);
161     font.getWidthsBounds(glyphs.get(), glyphCount, advances.get(), nullptr, nullptr);
162 
163     SkFontMetrics metrics;
164     font.getMetrics(&metrics);
165 
166     size_t glyphOffset = 0;
167     size_t utf8Offset = 0;
168     while (0 < textBytes) {
169         point.fY -= metrics.fAscent;
170 
171         size_t bytesCollapsed;
172         size_t bytesConsumed = linebreak(utf8text, utf8text + textBytes, font, width,
173                                          advances.get() + glyphOffset, &bytesCollapsed);
174         size_t bytesVisible = bytesConsumed - bytesCollapsed;
175 
176         int numGlyphs = SkUTF::CountUTF8(utf8text, bytesVisible);
177         const RunHandler::RunInfo info = {
178             { font.measureText(utf8text, bytesVisible, SkTextEncoding::kUTF8), 0 },
179             metrics.fAscent,
180             metrics.fDescent,
181             metrics.fLeading,
182         };
183         const auto buffer = handler->newRunBuffer(info, font, numGlyphs,
184                                                   SkSpan<const char>(utf8text, bytesVisible));
185 
186         memcpy(buffer.glyphs, glyphs.get() + glyphOffset, numGlyphs * sizeof(SkGlyphID));
187         SkScalar position = point.fX;
188         for (int i = 0; i < numGlyphs; ++i) {
189             buffer.positions[i] = { position, point.fY };
190             position += advances[i + glyphOffset];
191         }
192         if (buffer.clusters) {
193             const char* txtPtr = utf8text;
194             for (int i = 0; i < numGlyphs; ++i) {
195                 // Each character maps to exactly one glyph.
196                 buffer.clusters[i] = SkToU32(txtPtr - utf8text + utf8Offset);
197                 SkUTF::NextUTF8(&txtPtr, utf8text + textBytes);
198             }
199         }
200         handler->commitRun();
201         handler->commitLine();
202 
203         glyphOffset += SkUTF::CountUTF8(utf8text, bytesConsumed);
204         utf8Offset += bytesConsumed;
205         utf8text += bytesConsumed;
206         textBytes -= bytesConsumed;
207         point.fY += metrics.fDescent + metrics.fLeading;
208     }
209 
210     return point;
211 }
212