• 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/core/SkData.h"
9 #include "include/core/SkStream.h"
10 #include "include/core/SkTypeface.h"
11 #include "include/private/base/SkAssert.h"
12 #include "include/private/base/SkMalloc.h"
13 #include "include/private/base/SkTemplates.h"
14 #include "include/private/base/SkTo.h"
15 #include "src/pdf/SkPDFGlyphUse.h"
16 
17 #include "hb.h"  // NO_G3_REWRITE
18 #include "hb-subset.h"  // NO_G3_REWRITE
19 
20 #include <cstddef>
21 #include <memory>
22 #include <utility>
23 
24 namespace {
25 
26 using HBBlob = std::unique_ptr<hb_blob_t, SkFunctionObject<hb_blob_destroy>>;
27 using HBFace = std::unique_ptr<hb_face_t, SkFunctionObject<hb_face_destroy>>;
28 using HBSubsetInput = std::unique_ptr<hb_subset_input_t, SkFunctionObject<hb_subset_input_destroy>>;
29 using HBSet = std::unique_ptr<hb_set_t, SkFunctionObject<hb_set_destroy>>;
30 
stream_to_blob(std::unique_ptr<SkStreamAsset> asset)31 HBBlob stream_to_blob(std::unique_ptr<SkStreamAsset> asset) {
32     size_t size = asset->getLength();
33     HBBlob blob;
34     if (const void* base = asset->getMemoryBase()) {
35         blob.reset(hb_blob_create(const_cast<char*>(static_cast<const char*>(base)), SkToUInt(size),
36                                   HB_MEMORY_MODE_READONLY, asset.release(),
37                                   [](void* p) { delete (SkStreamAsset*)p; }));
38     } else {
39         void* ptr = size ? sk_malloc_throw(size) : nullptr;
40         asset->read(ptr, size);
41         blob.reset(hb_blob_create((char*)ptr, SkToUInt(size),
42                                   HB_MEMORY_MODE_READONLY, ptr, sk_free));
43     }
44     SkASSERT(blob);
45     hb_blob_make_immutable(blob.get());
46     return blob;
47 }
48 
to_data(HBBlob blob)49 sk_sp<SkData> to_data(HBBlob blob) {
50     if (!blob) {
51         return nullptr;
52     }
53     unsigned int length;
54     const char* data = hb_blob_get_data(blob.get(), &length);
55     if (!data || !length) {
56         return nullptr;
57     }
58     return SkData::MakeWithProc(data, SkToSizeT(length),
59                                 [](const void*, void* ctx) { hb_blob_destroy((hb_blob_t*)ctx); },
60                                 blob.release());
61 }
62 
make_subset(hb_subset_input_t * input,hb_face_t * face,bool retainZeroGlyph)63 HBFace make_subset(hb_subset_input_t* input, hb_face_t* face, bool retainZeroGlyph) {
64     // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY.
65     // If it isn't known if a font is 'tricky', retain the hints.
66     unsigned int flags = HB_SUBSET_FLAGS_RETAIN_GIDS;
67     if (retainZeroGlyph) {
68         flags |= HB_SUBSET_FLAGS_NOTDEF_OUTLINE;
69     }
70     hb_subset_input_set_flags(input, flags);
71     return HBFace(hb_subset_or_fail(face, input));
72 }
73 
subset_harfbuzz(const SkTypeface & typeface,const SkPDFGlyphUse & glyphUsage)74 sk_sp<SkData> subset_harfbuzz(const SkTypeface& typeface, const SkPDFGlyphUse& glyphUsage) {
75     int index = 0;
76     std::unique_ptr<SkStreamAsset> typefaceAsset = typeface.openStream(&index);
77     HBFace face;
78     HBBlob blob(stream_to_blob(std::move(typefaceAsset)));
79     // hb_face_create always succeeds. Check that the format is minimally recognized first.
80     // See https://github.com/harfbuzz/harfbuzz/issues/248
81     unsigned int num_hb_faces = hb_face_count(blob.get());
82     if (0 < num_hb_faces && (unsigned)index < num_hb_faces) {
83         face.reset(hb_face_create(blob.get(), (unsigned)index));
84         // Check the number of glyphs as a basic sanitization step.
85         if (face && hb_face_get_glyph_count(face.get()) == 0) {
86             face.reset();
87         }
88     }
89 
90     HBSubsetInput input(hb_subset_input_create_or_fail());
91     SkASSERT(input);
92     if (!face || !input) {
93         return nullptr;
94     }
95     hb_set_t* glyphs = hb_subset_input_glyph_set(input.get());
96     glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);});
97 
98     HBFace subset = make_subset(input.get(), face.get(), glyphUsage.has(0));
99     if (!subset) {
100         return nullptr;
101     }
102     HBBlob result(hb_face_reference_blob(subset.get()));
103     return to_data(std::move(result));
104 }
105 
106 }  // namespace
107 
SkPDFSubsetFont(const SkTypeface & typeface,const SkPDFGlyphUse & glyphUsage)108 sk_sp<SkData> SkPDFSubsetFont(const SkTypeface& typeface, const SkPDFGlyphUse& glyphUsage) {
109     return subset_harfbuzz(typeface, glyphUsage);
110 }
111 
112 #else
113 
SkPDFSubsetFont(const SkTypeface &,const SkPDFGlyphUse &)114 sk_sp<SkData> SkPDFSubsetFont(const SkTypeface&, const SkPDFGlyphUse&) {
115     return nullptr;
116 }
117 
118 #endif  // defined(SK_PDF_USE_HARFBUZZ_SUBSET)
119