1 | // Copyright 2018 Google LLC. |
2 | // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. |
3 | |
4 | #include "src/pdf/SkPDFSubsetFont.h" |
5 | |
6 | #if defined(SK_USING_THIRD_PARTY_ICU) |
7 | #include "SkLoadICU.h" |
8 | #endif |
9 | |
10 | #if defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
11 | |
12 | #include "include/private/SkTemplates.h" |
13 | #include "include/private/SkTo.h" |
14 | #include "src/utils/SkCallableTraits.h" |
15 | |
16 | #include "hb.h" |
17 | #include "hb-subset.h" |
18 | |
19 | template <class T, void(*P)(T*)> using resource = |
20 | std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>>; |
21 | using HBBlob = resource<hb_blob_t, &hb_blob_destroy>; |
22 | using HBFace = resource<hb_face_t, &hb_face_destroy>; |
23 | using HBSubsetInput = resource<hb_subset_input_t, &hb_subset_input_destroy>; |
24 | using HBSet = resource<hb_set_t, &hb_set_destroy>; |
25 | |
26 | static HBBlob to_blob(sk_sp<SkData> data) { |
27 | using blob_size_t = SkCallableTraits<decltype(hb_blob_create)>::argument<1>::type; |
28 | if (!SkTFitsIn<blob_size_t>(data->size())) { |
29 | return nullptr; |
30 | } |
31 | const char* blobData = static_cast<const char*>(data->data()); |
32 | blob_size_t blobSize = SkTo<blob_size_t>(data->size()); |
33 | return HBBlob(hb_blob_create(blobData, blobSize, |
34 | HB_MEMORY_MODE_READONLY, |
35 | data.release(), [](void* p){ ((SkData*)p)->unref(); })); |
36 | } |
37 | |
38 | static sk_sp<SkData> to_data(HBBlob blob) { |
39 | if (!blob) { |
40 | return nullptr; |
41 | } |
42 | unsigned int length; |
43 | const char* data = hb_blob_get_data(blob.get(), &length); |
44 | if (!data || !length) { |
45 | return nullptr; |
46 | } |
47 | return SkData::MakeWithProc(data, SkToSizeT(length), |
48 | [](const void*, void* ctx) { hb_blob_destroy((hb_blob_t*)ctx); }, |
49 | blob.release()); |
50 | } |
51 | |
52 | static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData, |
53 | const SkPDFGlyphUse& glyphUsage, |
54 | int ttcIndex) { |
55 | #if defined(SK_USING_THIRD_PARTY_ICU) |
56 | if (!SkLoadICU()) { |
57 | return nullptr; |
58 | } |
59 | #endif |
60 | if (!fontData) { |
61 | return nullptr; |
62 | } |
63 | HBFace face(hb_face_create(to_blob(std::move(fontData)).get(), ttcIndex)); |
64 | SkASSERT(face); |
65 | |
66 | HBSubsetInput input(hb_subset_input_create_or_fail()); |
67 | SkASSERT(input); |
68 | if (!face || !input) { |
69 | return nullptr; |
70 | } |
71 | hb_set_t* glyphs = hb_subset_input_glyph_set(input.get()); |
72 | glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);}); |
73 | |
74 | hb_subset_input_set_retain_gids(input.get(), true); |
75 | // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. |
76 | // If it isn't known if a font is 'tricky', retain the hints. |
77 | hb_subset_input_set_drop_hints(input.get(), false); |
78 | HBFace subset(hb_subset(face.get(), input.get())); |
79 | HBBlob result(hb_face_reference_blob(subset.get())); |
80 | return to_data(std::move(result)); |
81 | } |
82 | |
83 | #endif // defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
84 | |
85 | //////////////////////////////////////////////////////////////////////////////// |
86 | |
87 | #if defined(SK_PDF_USE_SFNTLY) |
88 | |
89 | #include "sample/chromium/font_subsetter.h" |
90 | #include <vector> |
91 | |
92 | static sk_sp<SkData> subset_sfntly(sk_sp<SkData> fontData, |
93 | const SkPDFGlyphUse& glyphUsage, |
94 | const char* fontName, |
95 | int ttcIndex) { |
96 | #if defined(SK_USING_THIRD_PARTY_ICU) |
97 | if (!SkLoadICU()) { |
98 | return nullptr; |
99 | } |
100 | #endif |
101 | // Generate glyph id array in format needed by sfntly. |
102 | // TODO(halcanary): sfntly should take a more compact format. |
103 | std::vector<unsigned> subset; |
104 | glyphUsage.getSetValues([&subset](unsigned v) { subset.push_back(v); }); |
105 | |
106 | unsigned char* subsetFont{nullptr}; |
107 | #if defined(SK_BUILD_FOR_GOOGLE3) |
108 | // TODO(halcanary): update SK_BUILD_FOR_GOOGLE3 to newest version of Sfntly. |
109 | (void)ttcIndex; |
110 | int subsetFontSize = SfntlyWrapper::SubsetFont(fontName, |
111 | fontData->bytes(), |
112 | fontData->size(), |
113 | subset.data(), |
114 | subset.size(), |
115 | &subsetFont); |
116 | #else // defined(SK_BUILD_FOR_GOOGLE3) |
117 | (void)fontName; |
118 | int subsetFontSize = SfntlyWrapper::SubsetFont(ttcIndex, |
119 | fontData->bytes(), |
120 | fontData->size(), |
121 | subset.data(), |
122 | subset.size(), |
123 | &subsetFont); |
124 | #endif // defined(SK_BUILD_FOR_GOOGLE3) |
125 | SkASSERT(subsetFontSize > 0 || subsetFont == nullptr); |
126 | if (subsetFontSize < 1 || subsetFont == nullptr) { |
127 | return nullptr; |
128 | } |
129 | return SkData::MakeWithProc(subsetFont, subsetFontSize, |
130 | [](const void* p, void*) { delete[] (unsigned char*)p; }, |
131 | nullptr); |
132 | } |
133 | |
134 | #endif // defined(SK_PDF_USE_SFNTLY) |
135 | |
136 | //////////////////////////////////////////////////////////////////////////////// |
137 | |
138 | #if defined(SK_PDF_USE_SFNTLY) && defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
139 | |
140 | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
141 | const SkPDFGlyphUse& glyphUsage, |
142 | SkPDF::Metadata::Subsetter subsetter, |
143 | const char* fontName, |
144 | int ttcIndex) { |
145 | switch (subsetter) { |
146 | case SkPDF::Metadata::kHarfbuzz_Subsetter: |
147 | return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); |
148 | case SkPDF::Metadata::kSfntly_Subsetter: |
149 | return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); |
150 | } |
151 | return nullptr; |
152 | } |
153 | |
154 | #elif defined(SK_PDF_USE_SFNTLY) |
155 | |
156 | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
157 | const SkPDFGlyphUse& glyphUsage, |
158 | SkPDF::Metadata::Subsetter, |
159 | const char* fontName, |
160 | int ttcIndex) { |
161 | return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); |
162 | } |
163 | |
164 | #elif defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
165 | |
166 | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
167 | const SkPDFGlyphUse& glyphUsage, |
168 | SkPDF::Metadata::Subsetter, |
169 | const char*, |
170 | int ttcIndex) { |
171 | return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); |
172 | } |
173 | |
174 | #else |
175 | |
176 | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData>, const SkPDFGlyphUse&, SkPDF::Metadata::Subsetter, |
177 | const char*, int) { |
178 | return nullptr; |
179 | } |
180 | #endif // defined(SK_PDF_USE_SFNTLY) |
181 | |
182 | |