// Copyright 2018 Google LLC. // Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. #include "src/pdf/SkPDFSubsetFont.h" #if defined(SK_PDF_USE_HARFBUZZ_SUBSET) #include "include/private/SkTemplates.h" #include "include/private/SkTo.h" #include "src/utils/SkCallableTraits.h" #include "hb.h" #include "hb-subset.h" template using resource = std::unique_ptr, P>>; using HBBlob = resource; using HBFace = resource; using HBSubsetInput = resource; using HBSet = resource; static HBBlob to_blob(sk_sp data) { using blob_size_t = SkCallableTraits::argument<1>::type; if (!SkTFitsIn(data->size())) { return nullptr; } const char* blobData = static_cast(data->data()); blob_size_t blobSize = SkTo(data->size()); return HBBlob(hb_blob_create(blobData, blobSize, HB_MEMORY_MODE_READONLY, data.release(), [](void* p){ ((SkData*)p)->unref(); })); } static sk_sp to_data(HBBlob blob) { if (!blob) { return nullptr; } unsigned int length; const char* data = hb_blob_get_data(blob.get(), &length); if (!data || !length) { return nullptr; } return SkData::MakeWithProc(data, SkToSizeT(length), [](const void*, void* ctx) { hb_blob_destroy((hb_blob_t*)ctx); }, blob.release()); } template using void_t = void; template struct SkPDFHarfBuzzSubset { // This is the HarfBuzz 3.0 interface. // hb_subset_flags_t does not exist in 2.0. It isn't dependent on T, so inline the value of // HB_SUBSET_FLAGS_RETAIN_GIDS until 2.0 is no longer supported. static HBFace Make(T input, hb_face_t* face) { // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. // If it isn't known if a font is 'tricky', retain the hints. hb_subset_input_set_flags(input, 2/*HB_SUBSET_FLAGS_RETAIN_GIDS*/); return HBFace(hb_subset_or_fail(face, input)); } }; template struct SkPDFHarfBuzzSubset(), std::declval())), decltype(hb_subset_input_set_drop_hints(std::declval(), std::declval())), decltype(hb_subset(std::declval(), std::declval())) >> { // This is the HarfBuzz 2.0 (non-public) interface, used if it exists. // This code should be removed as soon as all users are migrated to the newer API. static HBFace Make(T input, hb_face_t* face) { hb_subset_input_set_retain_gids(input, true); // TODO: When possible, check if a font is 'tricky' with FT_IS_TRICKY. // If it isn't known if a font is 'tricky', retain the hints. hb_subset_input_set_drop_hints(input, false); return HBFace(hb_subset(face, input)); } }; static sk_sp subset_harfbuzz(sk_sp fontData, const SkPDFGlyphUse& glyphUsage, int ttcIndex) { if (!fontData) { return nullptr; } HBFace face(hb_face_create(to_blob(std::move(fontData)).get(), ttcIndex)); SkASSERT(face); HBSubsetInput input(hb_subset_input_create_or_fail()); SkASSERT(input); if (!face || !input) { return nullptr; } hb_set_t* glyphs = hb_subset_input_glyph_set(input.get()); glyphUsage.getSetValues([&glyphs](unsigned gid) { hb_set_add(glyphs, gid);}); HBFace subset = SkPDFHarfBuzzSubset::Make(input.get(), face.get()); if (!subset) { return nullptr; } HBBlob result(hb_face_reference_blob(subset.get())); return to_data(std::move(result)); } #endif // defined(SK_PDF_USE_HARFBUZZ_SUBSET) //////////////////////////////////////////////////////////////////////////////// #if defined(SK_PDF_USE_SFNTLY) #include "sample/chromium/font_subsetter.h" #include static sk_sp subset_sfntly(sk_sp fontData, const SkPDFGlyphUse& glyphUsage, const char* fontName, int ttcIndex) { #if defined(SK_USING_THIRD_PARTY_ICU) if (!SkLoadICU()) { return nullptr; } #endif // Generate glyph id array in format needed by sfntly. // TODO(halcanary): sfntly should take a more compact format. std::vector subset; glyphUsage.getSetValues([&subset](unsigned v) { subset.push_back(v); }); unsigned char* subsetFont{nullptr}; #if defined(SK_BUILD_FOR_GOOGLE3) // TODO(halcanary): update SK_BUILD_FOR_GOOGLE3 to newest version of Sfntly. (void)ttcIndex; int subsetFontSize = SfntlyWrapper::SubsetFont(fontName, fontData->bytes(), fontData->size(), subset.data(), subset.size(), &subsetFont); #else // defined(SK_BUILD_FOR_GOOGLE3) (void)fontName; int subsetFontSize = SfntlyWrapper::SubsetFont(ttcIndex, fontData->bytes(), fontData->size(), subset.data(), subset.size(), &subsetFont); #endif // defined(SK_BUILD_FOR_GOOGLE3) SkASSERT(subsetFontSize > 0 || subsetFont == nullptr); if (subsetFontSize < 1 || subsetFont == nullptr) { return nullptr; } return SkData::MakeWithProc(subsetFont, subsetFontSize, [](const void* p, void*) { delete[] (unsigned char*)p; }, nullptr); } #endif // defined(SK_PDF_USE_SFNTLY) //////////////////////////////////////////////////////////////////////////////// #if defined(SK_PDF_USE_SFNTLY) && defined(SK_PDF_USE_HARFBUZZ_SUBSET) sk_sp SkPDFSubsetFont(sk_sp fontData, const SkPDFGlyphUse& glyphUsage, SkPDF::Metadata::Subsetter subsetter, const char* fontName, int ttcIndex) { switch (subsetter) { case SkPDF::Metadata::kHarfbuzz_Subsetter: return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); case SkPDF::Metadata::kSfntly_Subsetter: return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); } return nullptr; } #elif defined(SK_PDF_USE_SFNTLY) sk_sp SkPDFSubsetFont(sk_sp fontData, const SkPDFGlyphUse& glyphUsage, SkPDF::Metadata::Subsetter, const char* fontName, int ttcIndex) { return subset_sfntly(std::move(fontData), glyphUsage, fontName, ttcIndex); } #elif defined(SK_PDF_USE_HARFBUZZ_SUBSET) sk_sp SkPDFSubsetFont(sk_sp fontData, const SkPDFGlyphUse& glyphUsage, SkPDF::Metadata::Subsetter, const char*, int ttcIndex) { return subset_harfbuzz(std::move(fontData), glyphUsage, ttcIndex); } #else sk_sp SkPDFSubsetFont(sk_sp, const SkPDFGlyphUse&, SkPDF::Metadata::Subsetter, const char*, int) { return nullptr; } #endif // defined(SK_PDF_USE_SFNTLY)