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