| 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 |  | 
|---|
| 28 | class SkShaper_CoreText : public SkShaper { | 
|---|
| 29 | public: | 
|---|
| 30 | SkShaper_CoreText() {} | 
|---|
| 31 | private: | 
|---|
| 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 |  | 
|---|
| 56 | std::unique_ptr<SkShaper> SkShaper::MakeCoreText() { | 
|---|
| 57 | return std::make_unique<SkShaper_CoreText>(); | 
|---|
| 58 | } | 
|---|
| 59 |  | 
|---|
| 60 | void 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 |  | 
|---|
| 84 | void 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 |  | 
|---|
| 99 | template <typename T> class AutoCF { | 
|---|
| 100 | T fObj; | 
|---|
| 101 | public: | 
|---|
| 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 | 
|---|
| 109 | class LineBreakIter { | 
|---|
| 110 | CTTypesetterRef fTypesetter; | 
|---|
| 111 | double          fWidth; | 
|---|
| 112 | CFIndex         fStart; | 
|---|
| 113 |  | 
|---|
| 114 | public: | 
|---|
| 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 |  | 
|---|
| 130 | static 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 |  | 
|---|
| 135 | static 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 |  | 
|---|
| 149 | static 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 |  | 
|---|
| 155 | static 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 | 
|---|
| 170 | const CFStringRef kCTTracking_AttributeName = CFSTR( "CTTracking"); | 
|---|
| 171 |  | 
|---|
| 172 | void 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 | 
|---|
| 291 | std::unique_ptr<SkShaper> SkShaper::MakeCoreText() { | 
|---|
| 292 | return nullptr; | 
|---|
| 293 | } | 
|---|
| 294 | #endif | 
|---|
| 295 |  | 
|---|