• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 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 "modules/skshaper/include/SkShaper.h"
9 
10 #ifdef SK_BUILD_FOR_MAC
11 #import <ApplicationServices/ApplicationServices.h>
12 #endif
13 
14 #ifdef SK_BUILD_FOR_IOS
15 #include <CoreText/CoreText.h>
16 #include <CoreText/CTFontManager.h>
17 #include <CoreGraphics/CoreGraphics.h>
18 #include <CoreFoundation/CoreFoundation.h>
19 #endif
20 
21 #include "include/ports/SkTypeface_mac.h"
22 #include "include/private/SkTemplates.h"
23 #include "src/utils/SkUTF.h"
24 #include "src/utils/mac/SkCGBase.h"
25 #include "src/utils/mac/SkUniqueCFRef.h"
26 
27 #include <vector>
28 #include <utility>
29 
30 class SkShaper_CoreText : public SkShaper {
31 public:
SkShaper_CoreText()32     SkShaper_CoreText() {}
33 private:
34     void shape(const char* utf8, size_t utf8Bytes,
35                const SkFont& srcFont,
36                bool leftToRight,
37                SkScalar width,
38                RunHandler*) const override;
39 
40     void shape(const char* utf8, size_t utf8Bytes,
41                FontRunIterator&,
42                BiDiRunIterator&,
43                ScriptRunIterator&,
44                LanguageRunIterator&,
45                SkScalar width,
46                RunHandler*) const override;
47 
48     void shape(const char* utf8, size_t utf8Bytes,
49                FontRunIterator&,
50                BiDiRunIterator&,
51                ScriptRunIterator&,
52                LanguageRunIterator&,
53                const Feature*, size_t featureSize,
54                SkScalar width,
55                RunHandler*) const override;
56 };
57 
MakeCoreText()58 std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
59     return std::make_unique<SkShaper_CoreText>();
60 }
61 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,SkScalar width,RunHandler * handler) const62 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
63                               FontRunIterator& font,
64                               BiDiRunIterator& bidi,
65                               ScriptRunIterator&,
66                               LanguageRunIterator&,
67                               SkScalar width,
68                               RunHandler* handler) const
69 {
70     SkFont skfont;
71     if (!font.atEnd()) {
72         font.consume();
73         skfont = font.currentFont();
74     } else {
75         skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
76     }
77     SkASSERT(skfont.getTypeface());
78     bool skbidi = 0;
79     if (!bidi.atEnd()) {
80         bidi.consume();
81         skbidi = (bidi.currentLevel() % 2) == 0;
82     }
83     return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
84 }
85 
shape(const char * utf8,size_t utf8Bytes,FontRunIterator & font,BiDiRunIterator & bidi,ScriptRunIterator &,LanguageRunIterator &,const Feature *,size_t,SkScalar width,RunHandler * handler) const86 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
87                               FontRunIterator& font,
88                               BiDiRunIterator& bidi,
89                               ScriptRunIterator&,
90                               LanguageRunIterator&,
91                               const Feature*, size_t,
92                               SkScalar width,
93                               RunHandler* handler) const {
94     font.consume();
95     SkASSERT(font.currentFont().getTypeface());
96     bidi.consume();
97     return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
98                        width, handler);
99 }
100 
101 // CTFramesetter/CTFrame can do this, but require version 10.14
102 class LineBreakIter {
103     CTTypesetterRef fTypesetter;
104     double          fWidth;
105     CFIndex         fStart;
106 
107 public:
LineBreakIter(CTTypesetterRef ts,SkScalar width)108     LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
109         fStart = 0;
110     }
111 
nextLine()112     SkUniqueCFRef<CTLineRef> nextLine() {
113         CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
114         if (stringRange.length == 0) {
115             return nullptr;
116         }
117         fStart += stringRange.length;
118         return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
119     }
120 };
121 
dict_add_double(CFMutableDictionaryRef d,const void * name,double value)122 static void dict_add_double(CFMutableDictionaryRef d, const void* name, double value) {
123     SkUniqueCFRef<CFNumberRef> number(
124             CFNumberCreate(kCFAllocatorDefault, kCFNumberDoubleType, &value));
125     CFDictionaryAddValue(d, name, number.get());
126 }
127 
create_ctfont_from_font(const SkFont & font)128 static SkUniqueCFRef<CTFontRef> create_ctfont_from_font(const SkFont& font) {
129     auto typeface = font.getTypefaceOrDefault();
130     auto ctfont = SkTypeface_GetCTFontRef(typeface);
131     return SkUniqueCFRef<CTFontRef>(
132             CTFontCreateCopyWithAttributes(ctfont, font.getSize(), nullptr, nullptr));
133 }
134 
run_to_font(CTRunRef run,const SkFont & orig)135 static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
136     CFDictionaryRef attr = CTRunGetAttributes(run);
137     CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
138     if (!ct) {
139         SkDebugf("no ctfont in Run Attributes\n");
140         CFShow(attr);
141         return orig;
142     }
143     // Do I need to add a local cache, or allow the caller to manage this lookup?
144     SkFont font(orig);
145     font.setTypeface(SkMakeTypefaceFromCTFont(ct));
146     return font;
147 }
148 
149 namespace {
150 class UTF16ToUTF8IndicesMap {
151 public:
152     /** Builds a UTF-16 to UTF-8 indices map; the text is not retained
153      * @return true if successful
154      */
setUTF8(const char * utf8,size_t size)155     bool setUTF8(const char* utf8, size_t size) {
156         SkASSERT(utf8 != nullptr);
157 
158         if (!SkTFitsIn<int32_t>(size)) {
159             SkDEBUGF("UTF16ToUTF8IndicesMap: text too long");
160             return false;
161         }
162 
163         auto utf16Size = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, size);
164         if (utf16Size < 0) {
165             SkDEBUGF("UTF16ToUTF8IndicesMap: Invalid utf8 input");
166             return false;
167         }
168 
169         // utf16Size+1 to also store the size
170         fUtf16ToUtf8Indices = std::vector<size_t>(utf16Size + 1);
171         auto utf16 = fUtf16ToUtf8Indices.begin();
172         auto utf8Begin = utf8, utf8End = utf8 + size;
173         while (utf8Begin < utf8End) {
174             *utf16 = utf8Begin - utf8;
175             utf16 += SkUTF::ToUTF16(SkUTF::NextUTF8(&utf8Begin, utf8End), nullptr);
176         }
177         *utf16 = size;
178 
179         return true;
180     }
181 
mapIndex(size_t index) const182     size_t mapIndex(size_t index) const {
183         SkASSERT(index < fUtf16ToUtf8Indices.size());
184         return fUtf16ToUtf8Indices[index];
185     }
186 
mapRange(size_t start,size_t size) const187     std::pair<size_t, size_t> mapRange(size_t start, size_t size) const {
188         auto utf8Start = mapIndex(start);
189         return {utf8Start, mapIndex(start + size) - utf8Start};
190     }
191 private:
192     std::vector<size_t> fUtf16ToUtf8Indices;
193 };
194 } // namespace
195 
196 // kCTTrackingAttributeName not available until 10.12
197 const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");
198 
shape(const char * utf8,size_t utf8Bytes,const SkFont & font,bool,SkScalar width,RunHandler * handler) const199 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
200                               const SkFont& font,
201                               bool /* leftToRight */,
202                               SkScalar width,
203                               RunHandler* handler) const {
204     SkUniqueCFRef<CFStringRef> textString(
205             CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
206                                     kCFStringEncodingUTF8, false));
207 
208     UTF16ToUTF8IndicesMap utf8IndicesMap;
209     if (!utf8IndicesMap.setUTF8(utf8, utf8Bytes)) {
210         return;
211     }
212 
213     SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);
214 
215     SkUniqueCFRef<CFMutableDictionaryRef> attr(
216             CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
217                                       &kCFTypeDictionaryKeyCallBacks,
218                                       &kCFTypeDictionaryValueCallBacks));
219     CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
220     if (false) {
221         // trying to see what these affect
222         dict_add_double(attr.get(), kCTTracking_AttributeName, 1);
223         dict_add_double(attr.get(), kCTKernAttributeName, 0.0);
224     }
225 
226     SkUniqueCFRef<CFAttributedStringRef> attrString(
227             CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));
228 
229     SkUniqueCFRef<CTTypesetterRef> typesetter(
230             CTTypesetterCreateWithAttributedString(attrString.get()));
231 
232     // We have to compute RunInfos in a loop, and then reuse them in a 2nd loop,
233     // so we store them in an array (we reuse the array's storage for each line).
234     std::vector<SkFont> fontStorage;
235     std::vector<SkShaper::RunHandler::RunInfo> infos;
236 
237     LineBreakIter iter(typesetter.get(), width);
238     while (SkUniqueCFRef<CTLineRef> line = iter.nextLine()) {
239         CFArrayRef run_array = CTLineGetGlyphRuns(line.get());
240         CFIndex runCount = CFArrayGetCount(run_array);
241         if (runCount == 0) {
242             continue;
243         }
244         handler->beginLine();
245         fontStorage.clear();
246         fontStorage.reserve(runCount); // ensure the refs won't get invalidated
247         infos.clear();
248         for (CFIndex j = 0; j < runCount; ++j) {
249             CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
250             CFIndex runGlyphs = CTRunGetGlyphCount(run);
251 
252             SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));
253 
254             SkAutoSTArray<4096, CGSize> advances(runGlyphs);
255             CTRunGetAdvances(run, {0, runGlyphs}, advances.data());
256             SkScalar adv = 0;
257             for (CFIndex k = 0; k < runGlyphs; ++k) {
258                 adv += advances[k].width;
259             }
260 
261             CFRange cfRange = CTRunGetStringRange(run);
262             auto range = utf8IndicesMap.mapRange(cfRange.location, cfRange.length);
263 
264             fontStorage.push_back(run_to_font(run, font));
265             infos.push_back({
266                 fontStorage.back(), // info just stores a ref to the font
267                 0,                  // need fBidiLevel
268                 {adv, 0},
269                 (size_t)runGlyphs,
270                 {range.first, range.second},
271             });
272             handler->runInfo(infos.back());
273         }
274         handler->commitRunInfo();
275 
276         // Now loop through again and fill in the buffers
277         SkScalar lineAdvance = 0;
278         for (CFIndex j = 0; j < runCount; ++j) {
279             const auto& info = infos[j];
280             auto buffer = handler->runBuffer(info);
281 
282             CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
283             CFIndex runGlyphs = info.glyphCount;
284             SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);
285 
286             CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);
287 
288             SkAutoSTArray<4096, CGPoint> positions(runGlyphs);
289             CTRunGetPositions(run, {0, runGlyphs}, positions.data());
290             SkAutoSTArray<4096, CFIndex> indices;
291             if (buffer.clusters) {
292                 indices.reset(runGlyphs);
293                 CTRunGetStringIndices(run, {0, runGlyphs}, indices.data());
294             }
295 
296             for (CFIndex k = 0; k < runGlyphs; ++k) {
297                 buffer.positions[k] = {
298                     buffer.point.fX + SkScalarFromCGFloat(positions[k].x) - lineAdvance,
299                     buffer.point.fY,
300                 };
301                 if (buffer.offsets) {
302                     buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
303                 }
304                 if (buffer.clusters) {
305                     buffer.clusters[k] = utf8IndicesMap.mapIndex(indices[k]);
306                 }
307             }
308             handler->commitRunBuffer(info);
309             lineAdvance += info.fAdvance.fX;
310         }
311         handler->commitLine();
312     }
313 }
314