Coverage Report

Created: 2021-08-22 09:07

/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)