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 
to_blob(sk_sp<SkData> data)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 
to_data(HBBlob blob)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 
subset_harfbuzz(sk_sp<SkData> fontData,const SkPDFGlyphUse & glyphUsage,int ttcIndex)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 
subset_sfntly(sk_sp<SkData> fontData,const SkPDFGlyphUse & glyphUsage,const char * fontName,int ttcIndex)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 
SkPDFSubsetFont(sk_sp<SkData> fontData,const SkPDFGlyphUse & glyphUsage,SkPDF::Metadata::Subsetter subsetter,const char * fontName,int ttcIndex)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 
SkPDFSubsetFont(sk_sp<SkData> fontData,const SkPDFGlyphUse & glyphUsage,SkPDF::Metadata::Subsetter,const char * fontName,int ttcIndex)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 
SkPDFSubsetFont(sk_sp<SkData> fontData,const SkPDFGlyphUse & glyphUsage,SkPDF::Metadata::Subsetter,const char *,int ttcIndex)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 
SkPDFSubsetFont(sk_sp<SkData>,const SkPDFGlyphUse &,SkPDF::Metadata::Subsetter,const char *,int)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