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