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