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