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