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
19template <class T, void(*P)(T*)> using resource =
20 std::unique_ptr<T, SkFunctionWrapper<skstd::remove_pointer_t<decltype(P)>, P>>;
21using HBBlob = resource<hb_blob_t, &hb_blob_destroy>;
22using HBFace = resource<hb_face_t, &hb_face_destroy>;
23using HBSubsetInput = resource<hb_subset_input_t, &hb_subset_input_destroy>;
24using HBSet = resource<hb_set_t, &hb_set_destroy>;
25
26static 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
38static 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
52static 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
92static 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
140sk_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
156sk_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
166sk_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
176sk_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