/src/skia/src/pdf/SkPDFSubsetFont.cpp
Line | Count | Source (jump to first uncovered line) |
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<std::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 | 0 | static HBBlob to_blob(sk_sp<SkData> data) { |
27 | 0 | using blob_size_t = SkCallableTraits<decltype(hb_blob_create)>::argument<1>::type; |
28 | 0 | if (!SkTFitsIn<blob_size_t>(data->size())) { |
29 | 0 | return nullptr; |
30 | 0 | } |
31 | 0 | const char* blobData = static_cast<const char*>(data->data()); |
32 | 0 | blob_size_t blobSize = SkTo<blob_size_t>(data->size()); |
33 | 0 | return HBBlob(hb_blob_create(blobData, blobSize, |
34 | 0 | HB_MEMORY_MODE_READONLY, |
35 | 0 | data.release(), [](void* p){ ((SkData*)p)->unref(); })); |
36 | 0 | } |
37 | | |
38 | 0 | static sk_sp<SkData> to_data(HBBlob blob) { |
39 | 0 | if (!blob) { |
40 | 0 | return nullptr; |
41 | 0 | } |
42 | 0 | unsigned int length; |
43 | 0 | const char* data = hb_blob_get_data(blob.get(), &length); |
44 | 0 | if (!data || !length) { |
45 | 0 | return nullptr; |
46 | 0 | } |
47 | 0 | return SkData::MakeWithProc(data, SkToSizeT(length), |
48 | 0 | [](const void*, void* ctx) { hb_blob_destroy((hb_blob_t*)ctx); }, |
49 | 0 | blob.release()); |
50 | 0 | } |
51 | | |
52 | | template<typename...> using void_t = void; |
53 | | template<typename T, typename = void> |
54 | | struct SkPDFHarfBuzzSubset { |
55 | | // This is the HarfBuzz 3.0 interface. |
56 | | // hb_subset_flags_t does not exist in 2.0. It isn't dependent on T, so inline the value of |
57 | | // HB_SUBSET_FLAGS_RETAIN_GIDS until 2.0 is no longer supported. |
58 | 0 | static HBFace Make(T input, hb_face_t* face) { |
59 | | // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. |
60 | | // If it isn't known if a font is 'tricky', retain the hints. |
61 | 0 | hb_subset_input_set_flags(input, 2/*HB_SUBSET_FLAGS_RETAIN_GIDS*/); |
62 | 0 | return HBFace(hb_subset_or_fail(face, input)); |
63 | 0 | } |
64 | | }; |
65 | | template<typename T> |
66 | | struct SkPDFHarfBuzzSubset<T, void_t< |
67 | | decltype(hb_subset_input_set_retain_gids(std::declval<T>(), std::declval<bool>())), |
68 | | decltype(hb_subset_input_set_drop_hints(std::declval<T>(), std::declval<bool>())), |
69 | | decltype(hb_subset(std::declval<hb_face_t*>(), std::declval<T>())) |
70 | | >> |
71 | | { |
72 | | // This is the HarfBuzz 2.0 (non-public) interface, used if it exists. |
73 | | // This code should be removed as soon as all users are migrated to the newer API. |
74 | | static HBFace Make(T input, hb_face_t* face) { |
75 | | hb_subset_input_set_retain_gids(input, true); |
76 | | // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. |
77 | | // If it isn't known if a font is 'tricky', retain the hints. |
78 | | hb_subset_input_set_drop_hints(input, false); |
79 | | return HBFace(hb_subset(face, input)); |
80 | | } |
81 | | }; |
82 | | |
83 | | static sk_sp<SkData> subset_harfbuzz(sk_sp<SkData> fontData, |
84 | | const SkPDFGlyphUse& glyphUsage, |
85 | 0 | int ttcIndex) { |
86 | | #if defined(SK_USING_THIRD_PARTY_ICU) |
87 | | if (!SkLoadICU()) { |
88 | | return nullptr; |
89 | | } |
90 | | #endif |
91 | 0 | if (!fontData) { |
92 | 0 | return nullptr; |
93 | 0 | } |
94 | 0 | HBFace face(hb_face_create(to_blob(std::move(fontData)).get(), ttcIndex)); |
95 | 0 | SkASSERT(face); |
96 | |
|
97 | 0 | HBSubsetInput input(hb_subset_input_create_or_fail()); |
98 | 0 | SkASSERT(input); |
99 | 0 | if (!face || !input) { |
100 | 0 | return nullptr; |
101 | 0 | } |
102 | 0 | hb_set_t* glyphs = hb_subset_input_glyph_set(input.get()); |
103 | 0 | glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);}); |
104 | |
|
105 | 0 | HBFace subset = SkPDFHarfBuzzSubset<hb_subset_input_t*>::Make(input.get(), face.get()); |
106 | 0 | if (!subset) { |
107 | 0 | return nullptr; |
108 | 0 | } |
109 | 0 | HBBlob result(hb_face_reference_blob(subset.get())); |
110 | 0 | return to_data(std::move(result)); |
111 | 0 | } |
112 | | |
113 | | #endif // defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
114 | | |
115 | | //////////////////////////////////////////////////////////////////////////////// |
116 | | |
117 | | #if defined(SK_PDF_USE_SFNTLY) |
118 | | |
119 | | #include "sample/chromium/font_subsetter.h" |
120 | | #include <vector> |
121 | | |
122 | | static sk_sp<SkData> subset_sfntly(sk_sp<SkData> fontData, |
123 | | const SkPDFGlyphUse& glyphUsage, |
124 | | const char* fontName, |
125 | | int ttcIndex) { |
126 | | #if defined(SK_USING_THIRD_PARTY_ICU) |
127 | | if (!SkLoadICU()) { |
128 | | return nullptr; |
129 | | } |
130 | | #endif |
131 | | // Generate glyph id array in format needed by sfntly. |
132 | | // TODO(halcanary): sfntly should take a more compact format. |
133 | | std::vector<unsigned> subset; |
134 | | glyphUsage.getSetValues([&subset](unsigned v) { subset.push_back(v); }); |
135 | | |
136 | | unsigned char* subsetFont{nullptr}; |
137 | | #if defined(SK_BUILD_FOR_GOOGLE3) |
138 | | // TODO(halcanary): update SK_BUILD_FOR_GOOGLE3 to newest version of Sfntly. |
139 | | (void)ttcIndex; |
140 | | int subsetFontSize = SfntlyWrapper::SubsetFont(fontName, |
141 | | fontData->bytes(), |
142 | | fontData->size(), |
143 | | subset.data(), |
144 | | subset.size(), |
145 | | &subsetFont); |
146 | | #else // defined(SK_BUILD_FOR_GOOGLE3) |
147 | | (void)fontName; |
148 | | int subsetFontSize = SfntlyWrapper::SubsetFont(ttcIndex, |
149 | | fontData->bytes(), |
150 | | fontData->size(), |
151 | | subset.data(), |
152 | | subset.size(), |
153 | | &subsetFont); |
154 | | #endif // defined(SK_BUILD_FOR_GOOGLE3) |
155 | | SkASSERT(subsetFontSize > 0 || subsetFont == nullptr); |
156 | | if (subsetFontSize < 1 || subsetFont == nullptr) { |
157 | | return nullptr; |
158 | | } |
159 | | return SkData::MakeWithProc(subsetFont, subsetFontSize, |
160 | | [](const void* p, void*) { delete[] (unsigned char*)p; }, |
161 | | nullptr); |
162 | | } |
163 | | |
164 | | #endif // defined(SK_PDF_USE_SFNTLY) |
165 | | |
166 | | //////////////////////////////////////////////////////////////////////////////// |
167 | | |
168 | | #if defined(SK_PDF_USE_SFNTLY) && defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
169 | | |
170 | | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
171 | | const SkPDFGlyphUse& glyphUsage, |
172 | | SkPDF::Metadata::Subsetter subsetter, |
173 | | const char* fontName, |
174 | | int ttcIndex) { |
175 | | switch (subsetter) { |
176 | | case SkPDF::Metadata::kHarfbuzz_Subsetter: |
177 | | return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); |
178 | | case SkPDF::Metadata::kSfntly_Subsetter: |
179 | | return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); |
180 | | } |
181 | | return nullptr; |
182 | | } |
183 | | |
184 | | #elif defined(SK_PDF_USE_SFNTLY) |
185 | | |
186 | | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
187 | | const SkPDFGlyphUse& glyphUsage, |
188 | | SkPDF::Metadata::Subsetter, |
189 | | const char* fontName, |
190 | | int ttcIndex) { |
191 | | return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); |
192 | | } |
193 | | |
194 | | #elif defined(SK_PDF_USE_HARFBUZZ_SUBSET) |
195 | | |
196 | | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData> fontData, |
197 | | const SkPDFGlyphUse& glyphUsage, |
198 | | SkPDF::Metadata::Subsetter, |
199 | | const char*, |
200 | 0 | int ttcIndex) { |
201 | 0 | return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); |
202 | 0 | } |
203 | | |
204 | | #else |
205 | | |
206 | | sk_sp<SkData> SkPDFSubsetFont(sk_sp<SkData>, const SkPDFGlyphUse&, SkPDF::Metadata::Subsetter, |
207 | | const char*, int) { |
208 | | return nullptr; |
209 | | } |
210 | | #endif // defined(SK_PDF_USE_SFNTLY) |