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 15template <class T, void(*P)(T*)> using resource = 16 std::unique_ptr<T, SkFunctionWrapper<std::remove_pointer_t<decltype(P)>, P>>; 17using HBBlob = resource<hb_blob_t, &hb_blob_destroy>; 18using HBFace = resource<hb_face_t, &hb_face_destroy>; 19using HBSubsetInput = resource<hb_subset_input_t, &hb_subset_input_destroy>; 20using HBSet = resource<hb_set_t, &hb_set_destroy>; 21 22static 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 34static 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 48template<typename...> using void_t = void; 49template<typename T, typename = void> 50struct 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. 54 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}; 61template<typename T> 62struct 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. 70 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 79static 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 113static 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 161sk_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 177sk_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 187sk_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 197sk_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