2 * Copyright 2020 Google Inc.
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
8 #include "modules/skshaper/include/SkShaper.h"
10 #ifdef SK_BUILD_FOR_MAC
11 #import <ApplicationServices/ApplicationServices.h>
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>
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"
30 class SkShaper_CoreText : public SkShaper {
32 SkShaper_CoreText() {}
34 void shape(const char* utf8, size_t utf8Bytes,
35 const SkFont& srcFont,
38 RunHandler*) const override;
40 void shape(const char* utf8, size_t utf8Bytes,
46 RunHandler*) const override;
48 void shape(const char* utf8, size_t utf8Bytes,
53 const Feature*, size_t featureSize,
55 RunHandler*) const override;
58 std::unique_ptr<SkShaper> SkShaper::MakeCoreText() {
59 return std::make_unique<SkShaper_CoreText>();
62 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
63 FontRunIterator& font,
64 BiDiRunIterator& bidi,
68 RunHandler* handler) const
73 skfont = font.currentFont();
75 skfont.setTypeface(sk_ref_sp(skfont.getTypefaceOrDefault()));
77 SkASSERT(skfont.getTypeface());
81 skbidi = (bidi.currentLevel() % 2) == 0;
83 return this->shape(utf8, utf8Bytes, skfont, skbidi, width, handler);
86 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
87 FontRunIterator& font,
88 BiDiRunIterator& bidi,
91 const Feature*, size_t,
93 RunHandler* handler) const {
95 SkASSERT(font.currentFont().getTypeface());
97 return this->shape(utf8, utf8Bytes, font.currentFont(), (bidi.currentLevel() % 2) == 0,
101 // CTFramesetter/CTFrame can do this, but require version 10.14
102 class LineBreakIter {
103 CTTypesetterRef fTypesetter;
108 LineBreakIter(CTTypesetterRef ts, SkScalar width) : fTypesetter(ts), fWidth(width) {
112 SkUniqueCFRef<CTLineRef> nextLine() {
113 CFRange stringRange {fStart, CTTypesetterSuggestLineBreak(fTypesetter, fStart, fWidth)};
114 if (stringRange.length == 0) {
117 fStart += stringRange.length;
118 return SkUniqueCFRef<CTLineRef>(CTTypesetterCreateLine(fTypesetter, stringRange));
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());
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));
135 static SkFont run_to_font(CTRunRef run, const SkFont& orig) {
136 CFDictionaryRef attr = CTRunGetAttributes(run);
137 CTFontRef ct = (CTFontRef)CFDictionaryGetValue(attr, kCTFontAttributeName);
139 SkDebugf("no ctfont in Run Attributes\n");
143 // Do I need to add a local cache, or allow the caller to manage this lookup?
145 font.setTypeface(SkMakeTypefaceFromCTFont(ct));
150 class UTF16ToUTF8IndicesMap {
152 /** Builds a UTF-16 to UTF-8 indices map; the text is not retained
153 * @return true if successful
155 bool setUTF8(const char* utf8, size_t size) {
156 SkASSERT(utf8 != nullptr);
158 if (!SkTFitsIn<int32_t>(size)) {
159 SkDEBUGF("UTF16ToUTF8IndicesMap: text too long");
163 auto utf16Size = SkUTF::UTF8ToUTF16(nullptr, 0, utf8, size);
165 SkDEBUGF("UTF16ToUTF8IndicesMap: Invalid utf8 input");
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);
182 size_t mapIndex(size_t index) const {
183 SkASSERT(index < fUtf16ToUtf8Indices.size());
184 return fUtf16ToUtf8Indices[index];
187 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};
192 std::vector<size_t> fUtf16ToUtf8Indices;
196 // kCTTrackingAttributeName not available until 10.12
197 const CFStringRef kCTTracking_AttributeName = CFSTR("CTTracking");
199 void SkShaper_CoreText::shape(const char* utf8, size_t utf8Bytes,
201 bool /* leftToRight */,
203 RunHandler* handler) const {
204 SkUniqueCFRef<CFStringRef> textString(
205 CFStringCreateWithBytes(kCFAllocatorDefault, (const uint8_t*)utf8, utf8Bytes,
206 kCFStringEncodingUTF8, false));
208 UTF16ToUTF8IndicesMap utf8IndicesMap;
209 if (!utf8IndicesMap.setUTF8(utf8, utf8Bytes)) {
213 SkUniqueCFRef<CTFontRef> ctfont = create_ctfont_from_font(font);
215 SkUniqueCFRef<CFMutableDictionaryRef> attr(
216 CFDictionaryCreateMutable(kCFAllocatorDefault, 0,
217 &kCFTypeDictionaryKeyCallBacks,
218 &kCFTypeDictionaryValueCallBacks));
219 CFDictionaryAddValue(attr.get(), kCTFontAttributeName, ctfont.get());
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);
226 SkUniqueCFRef<CFAttributedStringRef> attrString(
227 CFAttributedStringCreate(kCFAllocatorDefault, textString.get(), attr.get()));
229 SkUniqueCFRef<CTTypesetterRef> typesetter(
230 CTTypesetterCreateWithAttributedString(attrString.get()));
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;
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);
244 handler->beginLine();
246 fontStorage.reserve(runCount); // ensure the refs won't get invalidated
248 for (CFIndex j = 0; j < runCount; ++j) {
249 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
250 CFIndex runGlyphs = CTRunGetGlyphCount(run);
252 SkASSERT(sizeof(CGGlyph) == sizeof(uint16_t));
254 SkAutoSTArray<4096, CGSize> advances(runGlyphs);
255 CTRunGetAdvances(run, {0, runGlyphs}, advances.data());
257 for (CFIndex k = 0; k < runGlyphs; ++k) {
258 adv += advances[k].width;
261 CFRange cfRange = CTRunGetStringRange(run);
262 auto range = utf8IndicesMap.mapRange(cfRange.location, cfRange.length);
264 fontStorage.push_back(run_to_font(run, font));
266 fontStorage.back(), // info just stores a ref to the font
267 0, // need fBidiLevel
270 {range.first, range.second},
272 handler->runInfo(infos.back());
274 handler->commitRunInfo();
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);
282 CTRunRef run = (CTRunRef)CFArrayGetValueAtIndex(run_array, j);
283 CFIndex runGlyphs = info.glyphCount;
284 SkASSERT(CTRunGetGlyphCount(run) == (CFIndex)info.glyphCount);
286 CTRunGetGlyphs(run, {0, runGlyphs}, buffer.glyphs);
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());
296 for (CFIndex k = 0; k < runGlyphs; ++k) {
297 buffer.positions[k] = {
298 buffer.point.fX + SkScalarFromCGFloat(positions[k].x) - lineAdvance,
301 if (buffer.offsets) {
302 buffer.offsets[k] = {0, 0}; // offset relative to the origin for this glyph
304 if (buffer.clusters) {
305 buffer.clusters[k] = utf8IndicesMap.mapIndex(indices[k]);
308 handler->commitRunBuffer(info);
309 lineAdvance += info.fAdvance.fX;
311 handler->commitLine();