| 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 | |