diff options
Diffstat (limited to 'gfx/sfntly/cpp/src/sample')
25 files changed, 2872 insertions, 0 deletions
diff --git a/gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc b/gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc new file mode 100644 index 0000000000..df15c182b7 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/chromium/chrome_subsetter.cc @@ -0,0 +1,131 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include <vector> +#include <string> +#include <sstream> + +#include "sfntly/port/type.h" +#include "font_subsetter.h" + +template <typename T> +class HexTo { + public: + explicit HexTo(const char* in) { + std::stringstream ss; + ss << std::hex << in; + ss >> value_; + } + operator T() const { return value_; } + + private: + T value_; +}; + +bool LoadFile(const char* input_file_path, sfntly::ByteVector* input_buffer) { + assert(input_file_path); + assert(input_buffer); + + FILE* input_file = NULL; +#if defined WIN32 + fopen_s(&input_file, input_file_path, "rb"); +#else + input_file = fopen(input_file_path, "rb"); +#endif + if (input_file == NULL) { + return false; + } + fseek(input_file, 0, SEEK_END); + size_t file_size = ftell(input_file); + fseek(input_file, 0, SEEK_SET); + input_buffer->resize(file_size); + size_t bytes_read = fread(&((*input_buffer)[0]), 1, file_size, input_file); + fclose(input_file); + return bytes_read == file_size; +} + +bool SaveFile(const char* output_file_path, const unsigned char* output_buffer, + int buffer_length) { + int byte_count = 0; + if (buffer_length > 0) { + FILE* output_file = NULL; +#if defined WIN32 + fopen_s(&output_file, output_file_path, "wb"); +#else + output_file = fopen(output_file_path, "wb"); +#endif + if (output_file) { + byte_count = fwrite(output_buffer, 1, buffer_length, output_file); + fflush(output_file); + fclose(output_file); + } + return buffer_length == byte_count; + } + return false; +} + +bool StringToGlyphId(const char* input, std::vector<unsigned int>* glyph_ids) { + assert(input); + std::string hex_csv = input; + size_t start = 0; + size_t end = hex_csv.find_first_of(","); + while (end != std::string::npos) { + glyph_ids->push_back( + HexTo<unsigned int>(hex_csv.substr(start, end - start).c_str())); + start = end + 1; + end = hex_csv.find_first_of(",", start); + } + glyph_ids->push_back(HexTo<unsigned int>(hex_csv.substr(start).c_str())); + return glyph_ids->size() > 0; +} + +int main(int argc, char** argv) { + if (argc < 5) { + fprintf(stderr, + "Usage: %s <input path> <output path> <font name> <glyph ids>\n", + argv[0]); + fprintf(stderr, "\tGlyph ids are comma separated hex values\n"); + fprintf(stderr, "\te.g. 20,1a,3b,4f\n"); + return 0; + } + + sfntly::ByteVector input_buffer; + if (!LoadFile(argv[1], &input_buffer)) { + fprintf(stderr, "ERROR: unable to load font file %s\n", argv[1]); + return 0; + } + + std::vector<unsigned int> glyph_ids; + if (!StringToGlyphId(argv[4], &glyph_ids)) { + fprintf(stderr, "ERROR: unable to parse input glyph id\n"); + return 0; + } + + unsigned char* output_buffer = NULL; + int output_length = + SfntlyWrapper::SubsetFont(argv[3], + &(input_buffer[0]), + input_buffer.size(), + &(glyph_ids[0]), + glyph_ids.size(), + &output_buffer); + + int result = SaveFile(argv[2], output_buffer, output_length) ? 1 : 0; + delete[] output_buffer; + return result; +} diff --git a/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc new file mode 100644 index 0000000000..0f9bc1341d --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.cc @@ -0,0 +1,60 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "font_subsetter.h" + +#include "subsetter_impl.h" + +int SfntlyWrapper::SubsetFont(const char* font_name, + const unsigned char* original_font, + size_t font_size, + const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer) { + if (output_buffer == NULL || + original_font == NULL || font_size == 0 || + glyph_ids == NULL || glyph_count == 0) { + return 0; + } + + sfntly::SubsetterImpl subsetter; + if (!subsetter.LoadFont(font_name, original_font, font_size)) { + return -1; // Load error or font not found. + } + + return subsetter.SubsetFont(glyph_ids, glyph_count, output_buffer); +} + +int SfntlyWrapper::SubsetFont(int font_index, + const unsigned char* original_font, + size_t font_size, + const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer) { + if (output_buffer == NULL || + original_font == NULL || font_size == 0 || + glyph_ids == NULL || glyph_count == 0) { + return 0; + } + + sfntly::SubsetterImpl subsetter; + if (!subsetter.LoadFont(font_index, original_font, font_size)) { + return -1; // Load error or font not found. + } + + return subsetter.SubsetFont(glyph_ids, glyph_count, output_buffer); +} + diff --git a/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h new file mode 100644 index 0000000000..c8e65e227d --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/chromium/font_subsetter.h @@ -0,0 +1,75 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// File is originally from Chromium third_party/sfntly/src/subsetter. +// Use as test case in sfntly so that problems can be caught in upstream early. +#ifndef SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_ +#define SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_ + +#include <stddef.h> + +class SfntlyWrapper { + public: + + // Font subsetting API + // + // Input TTF/TTC/OTF fonts, specify the glyph IDs to subset, and the subset + // font is returned in |output_buffer| (caller to delete[]). Return value is + // the length of output_buffer allocated. + // + // If subsetting fails, a negative value is returned. If none of the glyph + // IDs specified is found, the function will return 0. + // + // |font_name| Font name, required for TTC files. If specified NULL, + // the first available font is selected. + // |original_font| Original font file contents. + // |font_size| Size of |original_font| in bytes. + // |glyph_ids| Glyph IDs to subset. If the specified glyph ID is not + // found in the font file, it will be ignored silently. + // |glyph_count| Number of glyph IDs in |glyph_ids| + // |output_buffer| Generated subset font. Caller to delete[]. + static int SubsetFont(const char* font_name, + const unsigned char* original_font, + size_t font_size, + const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer); + + + // Font subsetting API + // + // Input TTF/TTC/OTF fonts, specify the glyph IDs to subset, and the subset + // font is returned in |output_buffer| (caller to delete[]). Return value is + // the length of output_buffer allocated. + // + // If subsetting fails, a negative value is returned. If none of the glyph + // IDs specified is found, the function will return 0. + // + // |font_name| Font index, ignored for non-TTC files, 0-indexed. + // |original_font| Original font file contents. + // |font_size| Size of |original_font| in bytes. + // |glyph_ids| Glyph IDs to subset. If the specified glyph ID is not + // found in the font file, it will be ignored silently. + // |glyph_count| Number of glyph IDs in |glyph_ids| + // |output_buffer| Generated subset font. Caller to delete[]. + static int SubsetFont(int font_index, + const unsigned char* original_font, + size_t font_size, + const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer); +}; + +#endif // SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_ diff --git a/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc new file mode 100644 index 0000000000..5910591b20 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.cc @@ -0,0 +1,848 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subsetter_impl.h" + +#include <string.h> + +#include <algorithm> +#include <iterator> +#include <limits> +#include <map> +#include <set> +#include <string> + +#include <unicode/ustring.h> +#include <unicode/uversion.h> + +#include "sfntly/table/bitmap/eblc_table.h" +#include "sfntly/table/bitmap/ebdt_table.h" +#include "sfntly/table/bitmap/index_sub_table.h" +#include "sfntly/table/bitmap/index_sub_table_format1.h" +#include "sfntly/table/bitmap/index_sub_table_format2.h" +#include "sfntly/table/bitmap/index_sub_table_format3.h" +#include "sfntly/table/bitmap/index_sub_table_format4.h" +#include "sfntly/table/bitmap/index_sub_table_format5.h" +#include "sfntly/table/core/name_table.h" +#include "sfntly/tag.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/port/memory_input_stream.h" +#include "sfntly/port/memory_output_stream.h" + +namespace { + +using namespace sfntly; + +/** + * std::u16string and icu::UnicodeString can't be used here. + * UChar is not always char16_t in some platforms. std::u16string is avoided. + * icu::UnicodeString C++ API is also avoided to make it more portable across + * platforms due to C++ ABI compatility issue. + */ +typedef std::basic_string<UChar> UCharString; + +// The bitmap tables must be greater than 16KB to trigger bitmap subsetter. +static const int BITMAP_SIZE_THRESHOLD = 16384; + +void ConstructName(UChar* name_part, UCharString* name, int32_t name_id) { + switch (name_id) { + case NameId::kFullFontName: + *name = name_part; + break; + case NameId::kFontFamilyName: + case NameId::kPreferredFamily: + case NameId::kWWSFamilyName: { + UCharString original = *name; + *name = name_part; + *name += original; + break; + } + case NameId::kFontSubfamilyName: + case NameId::kPreferredSubfamily: + case NameId::kWWSSubfamilyName: + *name += name_part; + break; + default: + // This name part is not used to construct font name (e.g. copyright). + // Simply ignore it. + break; + } +} + +// Convert UTF-8 string into UTF-16 string. +// +// Ill-formed input is replaced with U+FFFD. +// Otherwise, return empty string if other error occurs during the conversion. +UCharString ConvertFromUtf8(const char* src) { + int32_t srcLength = strlen(src); + int32_t destCapacity = srcLength + 1; + UChar* buffer = new UChar[destCapacity]; + UCharString dest; + if (buffer == NULL) { + return dest; + } + int32_t destLength; + UErrorCode errorCode = U_ZERO_ERROR; + u_strFromUTF8WithSub(buffer, destCapacity, &destLength, src, srcLength, + 0xfffd, // Unicode replacement character + NULL, + &errorCode); + if (U_SUCCESS(errorCode)) { + dest.append(buffer, destLength); + } + delete[] buffer; + return dest; +} + +int32_t CaseCompareUtf16(const UCharString& str1, + const UCharString& str2, uint32_t option) { + UErrorCode errorCode = U_ZERO_ERROR; + return u_strCaseCompare(str1.c_str(), str1.length(), str2.c_str(), + str2.length(), option, &errorCode); +} + +int32_t HashCode(int32_t platform_id, int32_t encoding_id, int32_t language_id, + int32_t name_id) { + int32_t result = platform_id << 24 | encoding_id << 16 | language_id << 8; + if (name_id == NameId::kFullFontName) { + result |= 0xff; + } else if (name_id == NameId::kPreferredFamily || + name_id == NameId::kPreferredSubfamily) { + result |= 0xf; + } else if (name_id == NameId::kWWSFamilyName || + name_id == NameId::kWWSSubfamilyName) { + result |= 1; + } + return result; +} + +bool HasName(const char* font_name, Font* font) { + UCharString font_string = ConvertFromUtf8(font_name); + if (font_string.empty()) + return false; + UCharString regular_suffix = ConvertFromUtf8(" Regular"); + UCharString alt_font_string = font_string; + alt_font_string += regular_suffix; + + typedef std::map<int32_t, UCharString> NameMap; + NameMap names; + NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name)); + if (name_table == NULL) { + return false; + } + + for (int32_t i = 0; i < name_table->NameCount(); ++i) { + switch (name_table->NameId(i)) { + case NameId::kFontFamilyName: + case NameId::kFontSubfamilyName: + case NameId::kFullFontName: + case NameId::kPreferredFamily: + case NameId::kPreferredSubfamily: + case NameId::kWWSFamilyName: + case NameId::kWWSSubfamilyName: { + UChar* name_part = name_table->Name(i); + if (name_part == NULL) { + continue; + } + int32_t hash_code = HashCode(name_table->PlatformId(i), + name_table->EncodingId(i), + name_table->LanguageId(i), + name_table->NameId(i)); + ConstructName(name_part, &(names[hash_code]), name_table->NameId(i)); + delete[] name_part; + break; + } + default: + break; + } + } + + if (!names.empty()) { + for (NameMap::iterator i = names.begin(), e = names.end(); i != e; ++i) { + if (CaseCompareUtf16(i->second, font_string, 0) == 0 || + CaseCompareUtf16(i->second, alt_font_string, 0) == 0) { + return true; + } + } + } + return false; +} + +Font* FindFont(const char* font_name, const FontArray& font_array) { + if (font_array.empty() || font_array[0] == NULL) { + return NULL; + } + + if (font_name && strlen(font_name)) { + for (FontArray::const_iterator i = font_array.begin(), e = font_array.end(); + i != e; ++i) { + if (HasName(font_name, i->p_)) { + return i->p_; + } + } + } + + return font_array[0].p_; +} + +bool ResolveCompositeGlyphs(GlyphTable* glyph_table, + LocaTable* loca_table, + const unsigned int* glyph_ids, + size_t glyph_count, + IntegerSet* glyph_id_processed) { + if (glyph_table == NULL || loca_table == NULL || + glyph_ids == NULL || glyph_count == 0 || glyph_id_processed == NULL) { + return false; + } + + // Sort and uniquify glyph ids. + IntegerSet glyph_id_remaining; + glyph_id_remaining.insert(0); // Always include glyph id 0. + for (size_t i = 0; i < glyph_count; ++i) { + glyph_id_remaining.insert(glyph_ids[i]); + } + + // Identify if any given glyph id maps to a composite glyph. If so, include + // the glyphs referenced by that composite glyph. + while (!glyph_id_remaining.empty()) { + IntegerSet comp_glyph_id; + for (IntegerSet::iterator i = glyph_id_remaining.begin(), + e = glyph_id_remaining.end(); i != e; ++i) { + if (*i < 0 || *i >= loca_table->num_glyphs()) { + // Invalid glyph id, ignore. + continue; + } + + int32_t length = loca_table->GlyphLength(*i); + if (length == 0) { + // Empty glyph, ignore. + continue; + } + int32_t offset = loca_table->GlyphOffset(*i); + + GlyphPtr glyph; + glyph.Attach(glyph_table->GetGlyph(offset, length)); + if (glyph == NULL) { + // Error finding glyph, ignore. + continue; + } + + if (glyph->GlyphType() == GlyphType::kComposite) { + Ptr<GlyphTable::CompositeGlyph> comp_glyph = + down_cast<GlyphTable::CompositeGlyph*>(glyph.p_); + for (int32_t j = 0; j < comp_glyph->NumGlyphs(); ++j) { + int32_t glyph_id = comp_glyph->GlyphIndex(j); + if (glyph_id_processed->find(glyph_id) == glyph_id_processed->end() && + glyph_id_remaining.find(glyph_id) == glyph_id_remaining.end()) { + comp_glyph_id.insert(comp_glyph->GlyphIndex(j)); + } + } + } + + glyph_id_processed->insert(*i); + } + + glyph_id_remaining.clear(); + glyph_id_remaining = comp_glyph_id; + } + + return true; +} + +bool SetupGlyfBuilders(Font::Builder* font_builder, + GlyphTable* glyph_table, + LocaTable* loca_table, + const IntegerSet& glyph_ids) { + if (!font_builder || !glyph_table || !loca_table) { + return false; + } + + GlyphTableBuilderPtr glyph_table_builder = + down_cast<GlyphTable::Builder*>(font_builder->NewTableBuilder(Tag::glyf)); + LocaTableBuilderPtr loca_table_builder = + down_cast<LocaTable::Builder*>(font_builder->NewTableBuilder(Tag::loca)); + if (glyph_table_builder == NULL || loca_table_builder == NULL) { + // Out of memory. + return false; + } + + // Extract glyphs and setup loca list. + IntegerList loca_list; + loca_list.resize(loca_table->num_glyphs()); + loca_list.push_back(0); + int32_t last_glyph_id = 0; + int32_t last_offset = 0; + GlyphTable::GlyphBuilderList* glyph_builders = + glyph_table_builder->GlyphBuilders(); + for (IntegerSet::const_iterator i = glyph_ids.begin(), e = glyph_ids.end(); + i != e; ++i) { + int32_t length = loca_table->GlyphLength(*i); + int32_t offset = loca_table->GlyphOffset(*i); + + GlyphPtr glyph; + glyph.Attach(glyph_table->GetGlyph(offset, length)); + + // Add glyph to new glyf table. + ReadableFontDataPtr data = glyph->ReadFontData(); + WritableFontDataPtr copy_data; + copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length())); + data->CopyTo(copy_data); + GlyphBuilderPtr glyph_builder; + glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data)); + glyph_builders->push_back(glyph_builder); + + // Configure loca list. + for (int32_t j = last_glyph_id + 1; j <= *i; ++j) { + loca_list[j] = last_offset; + } + + if (last_offset > std::numeric_limits<int32_t>::max() - length) + return false; + + last_offset += length; + loca_list[*i + 1] = last_offset; + last_glyph_id = *i; + } + for (int32_t j = last_glyph_id + 1; j <= loca_table->num_glyphs(); ++j) { + loca_list[j] = last_offset; + } + loca_table_builder->SetLocaList(&loca_list); + + return true; +} + +bool HasOverlap(int32_t range_begin, int32_t range_end, + const IntegerSet& glyph_ids) { + if (range_begin == range_end) + return glyph_ids.find(range_begin) != glyph_ids.end(); + + if (range_begin >= range_end) + return false; + + IntegerSet::const_iterator left = glyph_ids.lower_bound(range_begin); + IntegerSet::const_iterator right = glyph_ids.lower_bound(range_end); + return left != right; +} + +// Initialize builder, returns false if glyph_id subset is not covered. +// Not thread-safe, caller to ensure object life-time. +bool InitializeBitmapBuilder(EbdtTable::Builder* ebdt, EblcTable::Builder* eblc, + const IntegerSet& glyph_ids) { + BitmapLocaList loca_list; + BitmapSizeTableBuilderList* strikes = eblc->BitmapSizeBuilders(); + + // Note: Do not call eblc_builder->GenerateLocaList(&loca_list) and then + // ebdt_builder->SetLoca(loca_list). For fonts like SimSun, there are + // >28K glyphs inside, where a typical usage will be <1K glyphs. Doing + // the calls improperly will result in creation of >100K objects that + // will be destroyed immediately, inducing significant slowness. + IntegerList removed_strikes; + for (size_t i = 0; i < strikes->size(); i++) { + if (!HasOverlap((*strikes)[i]->StartGlyphIndex(), + (*strikes)[i]->EndGlyphIndex(), glyph_ids)) { + removed_strikes.push_back(i); + continue; + } + + IndexSubTableBuilderList* index_builders = + (*strikes)[i]->IndexSubTableBuilders(); + IntegerList removed_indexes; + BitmapGlyphInfoMap info_map; + for (size_t j = 0; j < index_builders->size(); ++j) { + if ((*index_builders)[j] == NULL) { + // Subtable is malformed, let's just skip it. + removed_indexes.push_back(j); + continue; + } + int32_t first_glyph_id = (*index_builders)[j]->first_glyph_index(); + int32_t last_glyph_id = (*index_builders)[j]->last_glyph_index(); + if (!HasOverlap(first_glyph_id, last_glyph_id, glyph_ids)) { + removed_indexes.push_back(j); + continue; + } + for (IntegerSet::const_iterator gid = glyph_ids.begin(), + gid_end = glyph_ids.end(); + gid != gid_end; gid++) { + if (*gid < first_glyph_id) { + continue; + } + if (*gid > last_glyph_id) { + break; + } + BitmapGlyphInfoPtr info; + info.Attach((*index_builders)[j]->GlyphInfo(*gid)); + if (info && info->length()) { // Do not include gid without bitmap + info_map[*gid] = info; + } + } + } + if (!info_map.empty()) { + loca_list.push_back(info_map); + } else { + removed_strikes.push_back(i); // Detected null entries. + } + + // Remove unused index sub tables + for (IntegerList::reverse_iterator j = removed_indexes.rbegin(), + e = removed_indexes.rend(); + j != e; j++) { + index_builders->erase(index_builders->begin() + *j); + } + } + if (removed_strikes.size() == strikes->size() || loca_list.empty()) { + return false; + } + + for (IntegerList::reverse_iterator i = removed_strikes.rbegin(), + e = removed_strikes.rend(); i != e; i++) { + strikes->erase(strikes->begin() + *i); + } + + if (strikes->empty()) { // no glyph covered, can safely drop the builders. + return false; + } + + ebdt->SetLoca(&loca_list); + ebdt->GlyphBuilders(); // Initialize the builder. + return true; +} + +void CopyBigGlyphMetrics(BigGlyphMetrics::Builder* source, + BigGlyphMetrics::Builder* target) { + target->SetHeight(static_cast<uint8_t>(source->Height())); + target->SetWidth(static_cast<uint8_t>(source->Width())); + target->SetHoriBearingX(static_cast<uint8_t>(source->HoriBearingX())); + target->SetHoriBearingY(static_cast<uint8_t>(source->HoriBearingY())); + target->SetHoriAdvance(static_cast<uint8_t>(source->HoriAdvance())); + target->SetVertBearingX(static_cast<uint8_t>(source->VertBearingX())); + target->SetVertBearingY(static_cast<uint8_t>(source->VertBearingY())); + target->SetVertAdvance(static_cast<uint8_t>(source->VertAdvance())); +} + +CALLER_ATTACH IndexSubTable::Builder* +ConstructIndexFormat4(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca, + int32_t* image_data_offset) { + IndexSubTableFormat4BuilderPtr builder4; + builder4.Attach(IndexSubTableFormat4::Builder::CreateBuilder()); + CodeOffsetPairBuilderList offset_pairs; + + size_t offset = 0; + int32_t lower_bound = b->first_glyph_index(); + int32_t upper_bound = b->last_glyph_index(); + int32_t last_gid = -1; + BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound); + BitmapGlyphInfoMap::const_iterator end = loca.end(); + if (i != end) { + last_gid = i->first; + builder4->set_first_glyph_index(last_gid); + builder4->set_image_format(b->image_format()); + builder4->set_image_data_offset(*image_data_offset); + } + for (; i != end; i++) { + int32_t gid = i->first; + if (gid > upper_bound) { + break; + } + offset_pairs.push_back( + IndexSubTableFormat4::CodeOffsetPairBuilder(gid, offset)); + offset += i->second->length(); + last_gid = gid; + } + offset_pairs.push_back( + IndexSubTableFormat4::CodeOffsetPairBuilder(-1, offset)); + builder4->set_last_glyph_index(last_gid); + *image_data_offset += offset; + builder4->SetOffsetArray(offset_pairs); + + return builder4.Detach(); +} + +CALLER_ATTACH IndexSubTable::Builder* +ConstructIndexFormat5(IndexSubTable::Builder* b, const BitmapGlyphInfoMap& loca, + int32_t* image_data_offset) { + IndexSubTableFormat5BuilderPtr new_builder; + new_builder.Attach(IndexSubTableFormat5::Builder::CreateBuilder()); + + // Copy BigMetrics + int32_t image_size = 0; + if (b->index_format() == IndexSubTable::Format::FORMAT_2) { + IndexSubTableFormat2BuilderPtr builder2 = + down_cast<IndexSubTableFormat2::Builder*>(b); + CopyBigGlyphMetrics(builder2->BigMetrics(), new_builder->BigMetrics()); + image_size = builder2->ImageSize(); + } else { + IndexSubTableFormat5BuilderPtr builder5 = + down_cast<IndexSubTableFormat5::Builder*>(b); + BigGlyphMetricsBuilderPtr metrics_builder; + CopyBigGlyphMetrics(builder5->BigMetrics(), new_builder->BigMetrics()); + image_size = builder5->ImageSize(); + } + + IntegerList* glyph_array = new_builder->GlyphArray(); + size_t offset = 0; + int32_t lower_bound = b->first_glyph_index(); + int32_t upper_bound = b->last_glyph_index(); + int32_t last_gid = -1; + BitmapGlyphInfoMap::const_iterator i = loca.lower_bound(lower_bound); + BitmapGlyphInfoMap::const_iterator end = loca.end(); + if (i != end) { + last_gid = i->first; + new_builder->set_first_glyph_index(last_gid); + new_builder->set_image_format(b->image_format()); + new_builder->set_image_data_offset(*image_data_offset); + new_builder->SetImageSize(image_size); + } + for (; i != end; i++) { + int32_t gid = i->first; + if (gid > upper_bound) { + break; + } + glyph_array->push_back(gid); + offset += i->second->length(); + last_gid = gid; + } + new_builder->set_last_glyph_index(last_gid); + *image_data_offset += offset; + return new_builder.Detach(); +} + +CALLER_ATTACH IndexSubTable::Builder* +SubsetIndexSubTable(IndexSubTable::Builder* builder, + const BitmapGlyphInfoMap& loca, + int32_t* image_data_offset) { + switch (builder->index_format()) { + case IndexSubTable::Format::FORMAT_1: + case IndexSubTable::Format::FORMAT_3: + case IndexSubTable::Format::FORMAT_4: + return ConstructIndexFormat4(builder, loca, image_data_offset); + case IndexSubTable::Format::FORMAT_2: + case IndexSubTable::Format::FORMAT_5: + return ConstructIndexFormat5(builder, loca, image_data_offset); + default: + assert(false); + break; + } + return NULL; +} + +} + +namespace sfntly { + +// Not thread-safe, caller to ensure object life-time. +void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca) { + BitmapSizeTableBuilderList* size_builders = eblc->BitmapSizeBuilders(); + if (size_builders == NULL) { + return; + } + + int32_t image_data_offset = EbdtTable::Offset::kHeaderLength; + for (size_t strike = 0; strike < size_builders->size(); ++strike) { + IndexSubTableBuilderList* index_builders = + (*size_builders)[strike]->IndexSubTableBuilders(); + for (size_t index = 0; index < index_builders->size(); ++index) { + IndexSubTable::Builder* new_builder_raw = + SubsetIndexSubTable((*index_builders)[index], new_loca[strike], + &image_data_offset); + if (NULL != new_builder_raw) { + (*index_builders)[index].Attach(new_builder_raw); + } + } + } +} + +// EBLC structure (from stuartg) +// header +// bitmapSizeTable[] +// one per strike +// holds strike metrics - sbitLineMetrics +// holds info about indexSubTableArray +// indexSubTableArray[][] +// one per strike and then one per indexSubTable for that strike +// holds info about the indexSubTable +// the indexSubTable entries pointed to can be of different formats +// indexSubTable +// one per indexSubTableArray entry +// tells how to get the glyphs +// may hold the glyph metrics if they are uniform for all the glyphs in range +// Please note that the structure can also be +// {indexSubTableArray[], indexSubTables[]}[] +// This way is also legal and in fact how Microsoft fonts are laid out. +// +// There is nothing that says that the indexSubTableArray entries and/or the +// indexSubTable items need to be unique. They may be shared between strikes. +// +// EBDT structure: +// header +// glyphs +// amorphous blob of data +// different glyphs that are only able to be figured out from the EBLC table +// may hold metrics - depends on the EBLC entry that pointed to them + +// Subsetting EBLC table (from arthurhsu) +// Most pages use only a fraction (hundreds or less) glyphs out of a given font +// (which can have >20K glyphs for CJK). It's safe to assume that the subset +// font will have sparse bitmap glyphs. So we reconstruct the EBLC table as +// format 4 or 5 here. + +enum BuildersToRemove { + kRemoveNone, + kRemoveBDAT, + kRemoveBDATAndEBDT, + kRemoveEBDT +}; + +int SetupBitmapBuilders(Font* font, Font::Builder* font_builder, + const IntegerSet& glyph_ids) { + if (!font || !font_builder) { + return false; + } + + // Check if bitmap table exists. + EbdtTablePtr ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT)); + EblcTablePtr eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); + bool use_ebdt = (ebdt_table != NULL && eblc_table != NULL); + if (!use_ebdt) { + ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::bdat)); + eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::bloc)); + if (ebdt_table == NULL || eblc_table == NULL) { + return kRemoveNone; + } + } + + // If the bitmap table's size is too small, skip subsetting. + if (ebdt_table->DataLength() + eblc_table->DataLength() < + BITMAP_SIZE_THRESHOLD) { + return use_ebdt ? kRemoveBDAT : kRemoveNone; + } + + // Get the builders. + EbdtTableBuilderPtr ebdt_table_builder = down_cast<EbdtTable::Builder*>( + font_builder->NewTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat, + ebdt_table->ReadFontData())); + EblcTableBuilderPtr eblc_table_builder = down_cast<EblcTable::Builder*>( + font_builder->NewTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc, + eblc_table->ReadFontData())); + if (ebdt_table_builder == NULL || eblc_table_builder == NULL) { + // Out of memory. + return use_ebdt ? kRemoveBDAT : kRemoveNone; + } + + if (!InitializeBitmapBuilder(ebdt_table_builder, eblc_table_builder, + glyph_ids)) { + // Bitmap tables do not cover the glyphs in our subset. + font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc); + font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat); + return use_ebdt ? kRemoveBDATAndEBDT : kRemoveEBDT; + } + + BitmapLocaList new_loca; + ebdt_table_builder->GenerateLocaList(&new_loca); + SubsetEBLC(eblc_table_builder, new_loca); + + return use_ebdt ? kRemoveBDAT : kRemoveNone; +} + +SubsetterImpl::SubsetterImpl() { +} + +SubsetterImpl::~SubsetterImpl() { +} + +bool SubsetterImpl::LoadFont(int font_index, + const unsigned char* original_font, + size_t font_size) { + MemoryInputStream mis; + mis.Attach(original_font, font_size); + if (factory_ == NULL) { + factory_.Attach(FontFactory::GetInstance()); + } + + FontArray font_array; + factory_->LoadFonts(&mis, &font_array); + if (font_index < 0 || (size_t)font_index >= font_array.size()) { + return false; + } + font_ = font_array[font_index].p_; + return font_ != NULL; +} + +bool SubsetterImpl::LoadFont(const char* font_name, + const unsigned char* original_font, + size_t font_size) { + MemoryInputStream mis; + mis.Attach(original_font, font_size); + if (factory_ == NULL) { + factory_.Attach(FontFactory::GetInstance()); + } + + FontArray font_array; + factory_->LoadFonts(&mis, &font_array); + font_ = FindFont(font_name, font_array); + return font_ != NULL; +} + +int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer) { + if (factory_ == NULL || font_ == NULL) { + return -1; + } + + // Find glyf and loca table. + GlyphTablePtr glyph_table = + down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); + LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); + if (glyph_table == NULL || loca_table == NULL) { + // We are not able to subset the font. + return 0; + } + + IntegerSet glyph_id_processed; + if (!ResolveCompositeGlyphs(glyph_table, loca_table, + glyph_ids, glyph_count, &glyph_id_processed) || + glyph_id_processed.empty()) { + return 0; + } + + FontPtr new_font; + new_font.Attach(Subset(glyph_id_processed, glyph_table, loca_table)); + if (new_font == NULL) { + return 0; + } + + MemoryOutputStream output_stream; + factory_->SerializeFont(new_font, &output_stream); + size_t length = output_stream.Size(); + if (length == 0 || + length > static_cast<size_t>(std::numeric_limits<int>::max())) { + return 0; + } + + *output_buffer = new unsigned char[length]; + memcpy(*output_buffer, output_stream.Get(), length); + return length; +} + +// Long comments regarding TTF tables and PDF (from stuartg) +// +// According to PDF spec 1.4 (section 5.8), the following tables must be +// present: +// head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm +// cmap if font is used with a simple font dict and not a CIDFont dict +// +// Other tables we need to keep for PDF rendering to support zoom in/out: +// bdat, bloc, ebdt, eblc, ebsc, gasp +// +// Special table: +// CFF - if you have this table then you shouldn't have a glyf table and this +// is the table with all the glyphs. Shall skip subsetting completely +// since sfntly is not capable of subsetting it for now. +// post - extra info here for printing on PostScript printers but maybe not +// enough to outweigh the space taken by the names +// +// Tables to break apart: +// name - could throw away all but one language and one platform strings/ might +// throw away some of the name entries +// cmap - could strip out non-needed cmap subtables +// - format 4 subtable can be subsetted as well using sfntly +// +// Graphite tables: +// silf, glat, gloc, feat - should be okay to strip out +// +// Tables that can be discarded: +// OS/2 - everything here is for layout and description of the font that is +// elsewhere (some in the PDF objects) +// BASE, GDEF, GSUB, GPOS, JSTF - all used for layout +// kern - old style layout +// DSIG - this will be invalid after subsetting +// hdmx - layout +// PCLT - metadata that's not needed +// vmtx - layout +// vhea - layout +// VDMX +// VORG - not used by TT/OT - used by CFF +// hsty - would be surprised to see one of these - used on the Newton +// AAT tables - mort, morx, feat, acnt, bsin, just, lcar, fdsc, fmtx, prop, +// Zapf, opbd, trak, fvar, gvar, avar, cvar +// - these are all layout tables and once layout happens are not +// needed anymore +// LTSH - layout + +CALLER_ATTACH +Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids, GlyphTable* glyf, + LocaTable* loca) { + // The const is initialized here to workaround VC bug of rendering all Tag::* + // as 0. These tags represents the TTF tables that we will embed in subset + // font. + const int32_t TABLES_IN_SUBSET[] = { + Tag::head, Tag::hhea, Tag::loca, Tag::maxp, Tag::cvt, + Tag::prep, Tag::glyf, Tag::hmtx, Tag::fpgm, Tag::EBDT, + Tag::EBLC, Tag::EBSC, Tag::bdat, Tag::bloc, Tag::bhed, + Tag::cmap, // Keep here for future tagged PDF development. + Tag::name, // Keep here due to legal concerns: copyright info inside. + }; + const size_t kTablesInSubSetSize = + sizeof(TABLES_IN_SUBSET) / sizeof(TABLES_IN_SUBSET[0]); + + // Setup font builders we need. + FontBuilderPtr font_builder; + font_builder.Attach(factory_->NewFontBuilder()); + IntegerSet remove_tags; + + if (SetupGlyfBuilders(font_builder, glyf, loca, glyph_ids)) { + remove_tags.insert(Tag::glyf); + remove_tags.insert(Tag::loca); + } + + // For old Apple bitmap fonts, they have only bdats and bhed is identical + // to head. As a result, we can't remove bdat tables for those fonts. + int setup_result = SetupBitmapBuilders(font_, font_builder, glyph_ids); + if (setup_result == kRemoveBDATAndEBDT || setup_result == kRemoveEBDT) { + remove_tags.insert(Tag::EBDT); + remove_tags.insert(Tag::EBLC); + remove_tags.insert(Tag::EBSC); + } + + if (setup_result == kRemoveBDAT || setup_result == kRemoveBDATAndEBDT) { + remove_tags.insert(Tag::bdat); + remove_tags.insert(Tag::bloc); + remove_tags.insert(Tag::bhed); + } + + IntegerSet allowed_tags; + for (size_t i = 0; i < kTablesInSubSetSize; ++i) + allowed_tags.insert(TABLES_IN_SUBSET[i]); + + IntegerSet result; + std::set_difference(allowed_tags.begin(), allowed_tags.end(), + remove_tags.begin(), remove_tags.end(), + std::inserter(result, result.end())); + allowed_tags = result; + + // Setup remaining builders. + for (IntegerSet::const_iterator it = allowed_tags.begin(); + it != allowed_tags.end(); ++it) { + int32_t tag = *it; + Table* table = font_->GetTable(tag); + if (table) + font_builder->NewTableBuilder(tag, table->ReadFontData()); + } + + return font_builder->Build(); +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h new file mode 100644 index 0000000000..738a8d4da8 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/chromium/subsetter_impl.h @@ -0,0 +1,77 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +// File is originally from Chromium third_party/sfntly/src/subsetter. +// Use as test case in sfntly so that problems can be caught in upstream early. + +#ifndef SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_ +#define SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_ + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/truetype/loca_table.h" +#include "sfntly/tag.h" + +namespace sfntly { + +// Smart pointer usage in sfntly: +// +// sfntly carries a smart pointer implementation like COM. Ref-countable object +// type inherits from RefCounted<>, which have AddRef and Release just like +// IUnknown (but no QueryInterface). Use a Ptr<> based smart pointer to hold +// the object so that the object ref count is handled correctly. +// +// class Foo : public RefCounted<Foo> { +// public: +// static Foo* CreateInstance() { +// Ptr<Foo> obj = new Foo(); // ref count = 1 +// return obj.detach(); +// } +// }; +// typedef Ptr<Foo> FooPtr; // common short-hand notation +// FooPtr obj; +// obj.attach(Foo::CreatedInstance()); // ref count = 1 +// { +// FooPtr obj2 = obj; // ref count = 2 +// } // ref count = 1, obj2 out of scope +// obj.release(); // ref count = 0, object destroyed + +class SubsetterImpl { + public: + SubsetterImpl(); + ~SubsetterImpl(); + + bool LoadFont(const char* font_name, + const unsigned char* original_font, + size_t font_size); + bool LoadFont(int font_index, + const unsigned char* original_font, + size_t font_size); + int SubsetFont(const unsigned int* glyph_ids, + size_t glyph_count, + unsigned char** output_buffer); + + private: + CALLER_ATTACH Font* Subset(const IntegerSet& glyph_ids, + GlyphTable* glyf, LocaTable* loca); + + FontFactoryPtr factory_; + FontPtr font_; +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_TEST_SUBSETTER_IMPL_H_ diff --git a/gfx/sfntly/cpp/src/sample/subsetter/main.cc b/gfx/sfntly/cpp/src/sample/subsetter/main.cc new file mode 100644 index 0000000000..19a3e0e23f --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subsetter/main.cc @@ -0,0 +1,44 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#if _MSC_VER > 12 + #define _CRTDBG_MAP_ALLOC + #include <stdlib.h> + #include <crtdbg.h> +#endif + +#include "sample/subsetter/subset_util.h" + +int main(int argc, char** argv) { +#ifdef _CRTDBG_MAP_ALLOC + _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF); +#endif + + if (argc < 3) { + printf("Usage: subsetter <font file> <output file>\n"); + return 0; + } + + sfntly::SubsetUtil subset_util; + subset_util.Subset(argv[1], argv[2]); + +#ifdef _CRTDBG_MAP_ALLOC + _CrtDumpMemoryLeaks(); +#endif + + return 0; +} diff --git a/gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc new file mode 100644 index 0000000000..f35eb25cd6 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.cc @@ -0,0 +1,98 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +// Remove VC++ nag on fopen. +#define _CRT_SECURE_NO_WARNINGS + +#include "sample/subsetter/subset_util.h" + +#include <stdio.h> + +#include <vector> +#include <memory> + +#include "sfntly/font.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/port/memory_output_stream.h" +#include "sfntly/port/type.h" +#include "sfntly/tag.h" +#include "sfntly/tools/subsetter/subsetter.h" + +namespace sfntly { + +SubsetUtil::SubsetUtil() { +} + +SubsetUtil::~SubsetUtil() { +} + +void SubsetUtil::Subset(const char *input_file_path, + const char *output_file_path) { + UNREFERENCED_PARAMETER(output_file_path); + ByteVector input_buffer; + FILE* input_file = fopen(input_file_path, "rb"); + if (input_file == NULL) { + fprintf(stderr, "file not found\n"); + return; + } + fseek(input_file, 0, SEEK_END); + size_t file_size = ftell(input_file); + fseek(input_file, 0, SEEK_SET); + input_buffer.resize(file_size); + size_t bytes_read = fread(&(input_buffer[0]), 1, file_size, input_file); + UNREFERENCED_PARAMETER(bytes_read); + fclose(input_file); + + FontFactoryPtr factory; + factory.Attach(FontFactory::GetInstance()); + + FontArray font_array; + factory->LoadFonts(&input_buffer, &font_array); + if (font_array.empty() || font_array[0] == NULL) + return; + + IntegerList glyphs; + for (int32_t i = 0; i < 10; i++) { + glyphs.push_back(i); + } + glyphs.push_back(11); + glyphs.push_back(10); + + Ptr<Subsetter> subsetter = new Subsetter(font_array[0], factory); + subsetter->SetGlyphs(&glyphs); + IntegerSet remove_tables; + remove_tables.insert(Tag::DSIG); + subsetter->SetRemoveTables(&remove_tables); + + FontBuilderPtr font_builder; + font_builder.Attach(subsetter->Subset()); + + FontPtr new_font; + new_font.Attach(font_builder->Build()); + + // TODO(arthurhsu): glyph renumbering/Loca table + // TODO(arthurhsu): alter CMaps + + MemoryOutputStream output_stream; + factory->SerializeFont(new_font, &output_stream); + + FILE* output_file = fopen(output_file_path, "wb"); + fwrite(output_stream.Get(), 1, output_stream.Size(), output_file); + fflush(output_file); + fclose(output_file); +} + +} // namespace sfntly diff --git a/gfx/sfntly/cpp/src/sample/subsetter/subset_util.h b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.h new file mode 100644 index 0000000000..5eb4fe4169 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subsetter/subset_util.h @@ -0,0 +1,32 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_ +#define SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_ + +namespace sfntly { + +class SubsetUtil { + public: + SubsetUtil(); + virtual ~SubsetUtil(); + + void Subset(const char* input_file_path, const char* output_file_path); +}; + +} // namespace sfntly + +#endif // SFNTLY_CPP_SRC_SAMPLE_SUBSETTER_SUBSET_UTIL_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc new file mode 100644 index 0000000000..b9c6cc787b --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.cc @@ -0,0 +1,53 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "sfntly/port/refcount.h" +#include "subtly/character_predicate.h" + +namespace subtly { +using namespace sfntly; + +// AcceptRange predicate +AcceptRange::AcceptRange(int32_t start, int32_t end) + : start_(start), + end_(end) { +} + +AcceptRange::~AcceptRange() {} + +bool AcceptRange::operator()(int32_t character) const { + return start_ <= character && character <= end_; +} + +// AcceptSet predicate +AcceptSet::AcceptSet(IntegerSet* characters) + : characters_(characters) { +} + +AcceptSet::~AcceptSet() { + delete characters_; +} + +bool AcceptSet::operator()(int32_t character) const { + return characters_->find(character) != characters_->end(); +} + +// AcceptAll predicate +bool AcceptAll::operator()(int32_t character) const { + UNREFERENCED_PARAMETER(character); + return true; +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/character_predicate.h b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.h new file mode 100644 index 0000000000..a6e3ea360e --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/character_predicate.h @@ -0,0 +1,68 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_ + +#include "sfntly/port/refcount.h" +#include "sfntly/port/type.h" + +namespace subtly { +class CharacterPredicate : virtual public sfntly::RefCount { + public: + CharacterPredicate() {} + virtual ~CharacterPredicate() {} + virtual bool operator()(int32_t character) const = 0; +}; + +// All characters except for those between [start, end] are rejected +class AcceptRange : public CharacterPredicate, + public sfntly::RefCounted<AcceptRange> { + public: + AcceptRange(int32_t start, int32_t end); + ~AcceptRange(); + virtual bool operator()(int32_t character) const; + + private: + int32_t start_; + int32_t end_; +}; + +// All characters in IntegerSet +// The set is OWNED by the predicate! Do not modify it. +// It will be freed when the predicate is destroyed. +class AcceptSet : public CharacterPredicate, + public sfntly::RefCounted<AcceptSet> { + public: + explicit AcceptSet(sfntly::IntegerSet* characters); + ~AcceptSet(); + virtual bool operator()(int32_t character) const; + + private: + sfntly::IntegerSet* characters_; +}; + +// All characters +class AcceptAll : public CharacterPredicate, + public sfntly::RefCounted<AcceptAll> { + public: + AcceptAll() {} + ~AcceptAll() {} + virtual bool operator()(int32_t character) const; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/debug_main.cc b/gfx/sfntly/cpp/src/sample/subtly/debug_main.cc new file mode 100644 index 0000000000..8324cde45c --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/debug_main.cc @@ -0,0 +1,64 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <map> +#include <utility> + +#include "sfntly/font.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/tag.h" +#include "subtly/stats.h" +#include "subtly/subsetter.h" +#include "subtly/utils.h" + +using namespace subtly; + +void PrintUsage(const char* program_name) { + fprintf(stdout, "Usage: %s <input_font_file>\n", program_name); +} + +int main(int argc, const char** argv) { + const char* program_name = argv[0]; + if (argc < 2) { + PrintUsage(program_name); + exit(1); + } + + const char* input_font_path = argv[1]; + const char* output_font_path = argv[2]; + FontPtr font; + font.Attach(subtly::LoadFont(input_font_path)); + + int32_t original_size = TotalFontSize(font); + Ptr<Subsetter> subsetter = new Subsetter(font, NULL); + Ptr<Font> new_font; + new_font.Attach(subsetter->Subset()); + if (!new_font) { + fprintf(stdout, "Cannot create subset.\n"); + return 0; + } + + subtly::SerializeFont(output_font_path, new_font); + subtly::PrintComparison(stdout, font, new_font); + int32_t new_size = TotalFontSize(new_font); + fprintf(stdout, "Went from %d to %d: %lf%% of original\n", + original_size, new_size, + static_cast<double>(new_size) / original_size * 100); + return 0; +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc new file mode 100644 index 0000000000..4717512a5c --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.cc @@ -0,0 +1,229 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subtly/font_assembler.h" + +#include <stdio.h> + +#include <set> +#include <map> + +#include "sfntly/tag.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/table/truetype/loca_table.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/core/maximum_profile_table.h" +#include "sfntly/port/type.h" +#include "sfntly/port/refcount.h" +#include "subtly/font_info.h" + +namespace subtly { +using namespace sfntly; + +FontAssembler::FontAssembler(FontInfo* font_info, + IntegerSet* table_blacklist) + : table_blacklist_(table_blacklist) { + font_info_ = font_info; + Initialize(); +} + +FontAssembler::FontAssembler(FontInfo* font_info) + : table_blacklist_(NULL) { + font_info_ = font_info; + Initialize(); +} + +void FontAssembler::Initialize() { + font_factory_.Attach(sfntly::FontFactory::GetInstance()); + font_builder_.Attach(font_factory_->NewFontBuilder()); +} + +CALLER_ATTACH Font* FontAssembler::Assemble() { + // Assemble tables we can subset. + if (!AssembleCMapTable() || !AssembleGlyphAndLocaTables()) { + return NULL; + } + // For all other tables, either include them unmodified or don't at all. + const TableMap* common_table_map = + font_info_->GetTableMap(font_info_->fonts()->begin()->first); + for (TableMap::const_iterator it = common_table_map->begin(), + e = common_table_map->end(); it != e; ++it) { + if (table_blacklist_ + && table_blacklist_->find(it->first) != table_blacklist_->end()) { + continue; + } + font_builder_->NewTableBuilder(it->first, it->second->ReadFontData()); + } + return font_builder_->Build(); +} + +bool FontAssembler::AssembleCMapTable() { + // Creating the new CMapTable and the new format 4 CMap + Ptr<CMapTable::Builder> cmap_table_builder = + down_cast<CMapTable::Builder*> + (font_builder_->NewTableBuilder(Tag::cmap)); + if (!cmap_table_builder) + return false; + Ptr<CMapTable::CMapFormat4::Builder> cmap_builder = + down_cast<CMapTable::CMapFormat4::Builder*> + (cmap_table_builder->NewCMapBuilder(CMapFormat::kFormat4, + CMapTable::WINDOWS_BMP)); + if (!cmap_builder) + return false; + // Creating the segments and the glyph id array + CharacterMap* chars_to_glyph_ids = font_info_->chars_to_glyph_ids(); + SegmentList* segment_list = new SegmentList; + IntegerList* glyph_id_array = new IntegerList; + int32_t last_chararacter = -2; + int32_t last_offset = 0; + Ptr<CMapTable::CMapFormat4::Builder::Segment> current_segment; + + // For simplicity, we will have one segment per contiguous range. + // To test the algorithm, we've replaced the original CMap with the CMap + // generated by this code without removing any character. + // Tuffy.ttf: CMap went from 3146 to 3972 bytes (1.7% to 2.17% of file) + // AnonymousPro.ttf: CMap went from 1524 to 1900 bytes (0.96% to 1.2%) + for (CharacterMap::iterator it = chars_to_glyph_ids->begin(), + e = chars_to_glyph_ids->end(); it != e; ++it) { + int32_t character = it->first; + int32_t glyph_id = it->second.glyph_id(); + if (character != last_chararacter + 1) { // new segment + if (current_segment != NULL) { + current_segment->set_end_count(last_chararacter); + segment_list->push_back(current_segment); + } + // start_code = character + // end_code = -1 (unknown for now) + // id_delta = 0 (we don't use id_delta for this representation) + // id_range_offset = last_offset (offset into the glyph_id_array) + current_segment = + new CMapTable::CMapFormat4::Builder:: + Segment(character, -1, 0, last_offset); + } + glyph_id_array->push_back(glyph_id); + last_offset += DataSize::kSHORT; + last_chararacter = character; + } + // The last segment is still open. + current_segment->set_end_count(last_chararacter); + segment_list->push_back(current_segment); + // Updating the id_range_offset for every segment. + for (int32_t i = 0, num_segs = segment_list->size(); i < num_segs; ++i) { + Ptr<CMapTable::CMapFormat4::Builder::Segment> segment = segment_list->at(i); + segment->set_id_range_offset(segment->id_range_offset() + + (num_segs - i + 1) * DataSize::kSHORT); + } + // Adding the final, required segment. + current_segment = + new CMapTable::CMapFormat4::Builder::Segment(0xffff, 0xffff, 1, 0); + segment_list->push_back(current_segment); + // Writing the segments and glyph id array to the CMap + cmap_builder->set_segments(segment_list); + cmap_builder->set_glyph_id_array(glyph_id_array); + delete segment_list; + delete glyph_id_array; + return true; +} + +bool FontAssembler::AssembleGlyphAndLocaTables() { + Ptr<LocaTable::Builder> loca_table_builder = + down_cast<LocaTable::Builder*> + (font_builder_->NewTableBuilder(Tag::loca)); + Ptr<GlyphTable::Builder> glyph_table_builder = + down_cast<GlyphTable::Builder*> + (font_builder_->NewTableBuilder(Tag::glyf)); + + GlyphIdSet* resolved_glyph_ids = font_info_->resolved_glyph_ids(); + IntegerList loca_list; + // Basic sanity check: all LOCA tables are of the same size + // This is necessary but not suficient! + int32_t previous_size = -1; + for (FontIdMap::iterator it = font_info_->fonts()->begin(); + it != font_info_->fonts()->end(); ++it) { + Ptr<LocaTable> loca_table = + down_cast<LocaTable*>(font_info_->GetTable(it->first, Tag::loca)); + int32_t current_size = loca_table->header_length(); + if (previous_size != -1 && current_size != previous_size) { + return false; + } + previous_size = current_size; + } + + // Assuming all fonts referenced by the FontInfo are the subsets of the same + // font, their loca tables should all have the same sizes. + // We'll just get the size of the first font's LOCA table for simplicty. + Ptr<LocaTable> first_loca_table = + down_cast<LocaTable*> + (font_info_->GetTable(font_info_->fonts()->begin()->first, Tag::loca)); + int32_t num_loca_glyphs = first_loca_table->num_glyphs(); + loca_list.resize(num_loca_glyphs); + loca_list.push_back(0); + int32_t last_glyph_id = 0; + int32_t last_offset = 0; + GlyphTable::GlyphBuilderList* glyph_builders = + glyph_table_builder->GlyphBuilders(); + + for (GlyphIdSet::iterator it = resolved_glyph_ids->begin(), + e = resolved_glyph_ids->end(); it != e; ++it) { + // Get the glyph for this resolved_glyph_id. + int32_t resolved_glyph_id = it->glyph_id(); + int32_t font_id = it->font_id(); + // Get the LOCA table for the current glyph id. + Ptr<LocaTable> loca_table = + down_cast<LocaTable*> + (font_info_->GetTable(font_id, Tag::loca)); + int32_t length = loca_table->GlyphLength(resolved_glyph_id); + int32_t offset = loca_table->GlyphOffset(resolved_glyph_id); + + // Get the GLYF table for the current glyph id. + Ptr<GlyphTable> glyph_table = + down_cast<GlyphTable*> + (font_info_->GetTable(font_id, Tag::glyf)); + GlyphPtr glyph; + glyph.Attach(glyph_table->GetGlyph(offset, length)); + + // The data reference by the glyph is copied into a new glyph and + // added to the glyph_builders belonging to the glyph_table_builder. + // When Build gets called, all the glyphs will be built. + Ptr<ReadableFontData> data = glyph->ReadFontData(); + Ptr<WritableFontData> copy_data; + copy_data.Attach(WritableFontData::CreateWritableFontData(data->Length())); + data->CopyTo(copy_data); + GlyphBuilderPtr glyph_builder; + glyph_builder.Attach(glyph_table_builder->GlyphBuilder(copy_data)); + glyph_builders->push_back(glyph_builder); + + // If there are missing glyphs between the last glyph_id and the + // current resolved_glyph_id, since the LOCA table needs to have the same + // size, the offset is kept the same. + loca_list.resize(std::max(loca_list.size(), + static_cast<size_t>(resolved_glyph_id + 2))); + for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i) + loca_list[i] = last_offset; + last_offset += length; + loca_list[resolved_glyph_id + 1] = last_offset; + last_glyph_id = resolved_glyph_id + 1; + } + // If there are missing glyph ids, their loca entries must all point + // to the same offset as the last valid glyph id making them all zero length. + for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i) + loca_list[i] = last_offset; + loca_table_builder->SetLocaList(&loca_list); + return true; +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/font_assembler.h b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.h new file mode 100644 index 0000000000..c53c21fd07 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/font_assembler.h @@ -0,0 +1,67 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_ + +#include <set> +#include <map> + +#include "subtly/font_info.h" + +#include "sfntly/tag.h" +#include "sfntly/font.h" +#include "sfntly/port/type.h" +#include "sfntly/port/refcount.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/truetype/loca_table.h" + +namespace subtly { +// Assembles FontInfo into font builders. +// Does not take ownership of data passed to it. +class FontAssembler : public sfntly::RefCounted<FontAssembler> { + public: + // font_info is the FontInfo which will be used for the new font + // table_blacklist is used to decide which tables to exclude from the + // final font. + FontAssembler(FontInfo* font_info, sfntly::IntegerSet* table_blacklist); + explicit FontAssembler(FontInfo* font_info); + ~FontAssembler() { } + + // Assemble a new font from the font info object. + virtual CALLER_ATTACH sfntly::Font* Assemble(); + + sfntly::IntegerSet* table_blacklist() const { return table_blacklist_; } + void set_table_blacklist(sfntly::IntegerSet* table_blacklist) { + table_blacklist_ = table_blacklist; + } + + protected: + virtual bool AssembleCMapTable(); + virtual bool AssembleGlyphAndLocaTables(); + + virtual void Initialize(); + + private: + sfntly::Ptr<FontInfo> font_info_; + sfntly::Ptr<sfntly::FontFactory> font_factory_; + sfntly::Ptr<sfntly::Font::Builder> font_builder_; + sfntly::IntegerSet* table_blacklist_; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/font_info.cc b/gfx/sfntly/cpp/src/sample/subtly/font_info.cc new file mode 100644 index 0000000000..6eb6a38070 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/font_info.cc @@ -0,0 +1,256 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subtly/font_info.h" + +#include <stdio.h> + +#include <set> +#include <map> + +#include "subtly/character_predicate.h" + +#include "sfntly/tag.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/table/truetype/loca_table.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/core/maximum_profile_table.h" +#include "sfntly/port/type.h" +#include "sfntly/port/refcount.h" + +namespace subtly { +using namespace sfntly; +/****************************************************************************** + * GlyphId class + ******************************************************************************/ +GlyphId::GlyphId(int32_t glyph_id, FontId font_id) + : glyph_id_(glyph_id), + font_id_(font_id) { +} + +bool GlyphId::operator==(const GlyphId& other) const { + return glyph_id_ == other.glyph_id(); +} + +bool GlyphId::operator<(const GlyphId& other) const { + return glyph_id_ < other.glyph_id(); +} + +/****************************************************************************** + * FontInfo class + ******************************************************************************/ +FontInfo::FontInfo() + : chars_to_glyph_ids_(new CharacterMap), + resolved_glyph_ids_(new GlyphIdSet), + fonts_(new FontIdMap) { +} + +FontInfo::FontInfo(CharacterMap* chars_to_glyph_ids, + GlyphIdSet* resolved_glyph_ids, + FontIdMap* fonts) { + chars_to_glyph_ids_ = new CharacterMap(chars_to_glyph_ids->begin(), + chars_to_glyph_ids->end()); + resolved_glyph_ids_ = new GlyphIdSet(resolved_glyph_ids->begin(), + resolved_glyph_ids->end()); + fonts_ = new FontIdMap(fonts->begin(), fonts->end()); +} + +FontInfo::~FontInfo() { + delete chars_to_glyph_ids_; + delete resolved_glyph_ids_; + delete fonts_; +} + +FontDataTable* FontInfo::GetTable(FontId font_id, int32_t tag) { + if (!fonts_) + return NULL; + FontIdMap::iterator it = fonts_->find(font_id); + if (it == fonts_->end()) + return NULL; + return it->second->GetTable(tag); +} + +const TableMap* FontInfo::GetTableMap(FontId font_id) { + if (!fonts_) + return NULL; + FontIdMap::iterator it = fonts_->find(font_id); + if (it == fonts_->end()) + return NULL; + return it->second->GetTableMap(); +} + +void FontInfo::set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids) { + *chars_to_glyph_ids_ = *chars_to_glyph_ids; +} + +void FontInfo::set_resolved_glyph_ids(GlyphIdSet* resolved_glyph_ids) { + *resolved_glyph_ids_ = *resolved_glyph_ids; +} + +void FontInfo::set_fonts(FontIdMap* fonts) { + *fonts_ = *fonts; +} + +/****************************************************************************** + * FontSourcedInfoBuilder class + ******************************************************************************/ +FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, FontId font_id) + : font_(font), + font_id_(font_id), + predicate_(NULL) { + Initialize(); +} + +FontSourcedInfoBuilder::FontSourcedInfoBuilder(Font* font, + FontId font_id, + CharacterPredicate* predicate) + : font_(font), + font_id_(font_id), + predicate_(predicate) { + Initialize(); +} + +void FontSourcedInfoBuilder::Initialize() { + Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font_->GetTable(Tag::cmap)); + // We prefer Windows BMP format 4 cmaps. + cmap_.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP)); + // But if none is found, + if (!cmap_) { + return; + } + loca_table_ = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); + glyph_table_ = down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); +} + +CALLER_ATTACH FontInfo* FontSourcedInfoBuilder::GetFontInfo() { + CharacterMap* chars_to_glyph_ids = new CharacterMap; + bool success = GetCharacterMap(chars_to_glyph_ids); + if (!success) { + delete chars_to_glyph_ids; +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Error creating character map.\n"); +#endif + return NULL; + } + GlyphIdSet* resolved_glyph_ids = new GlyphIdSet; + success = ResolveCompositeGlyphs(chars_to_glyph_ids, resolved_glyph_ids); + if (!success) { + delete chars_to_glyph_ids; + delete resolved_glyph_ids; +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Error resolving composite glyphs.\n"); +#endif + return NULL; + } + Ptr<FontInfo> font_info = new FontInfo; + font_info->set_chars_to_glyph_ids(chars_to_glyph_ids); + font_info->set_resolved_glyph_ids(resolved_glyph_ids); + FontIdMap* font_id_map = new FontIdMap; + font_id_map->insert(std::make_pair(font_id_, font_)); + font_info->set_fonts(font_id_map); + delete chars_to_glyph_ids; + delete resolved_glyph_ids; + delete font_id_map; + return font_info.Detach(); +} + +bool FontSourcedInfoBuilder::GetCharacterMap(CharacterMap* chars_to_glyph_ids) { + if (!cmap_ || !chars_to_glyph_ids) + return false; + chars_to_glyph_ids->clear(); + CMapTable::CMap::CharacterIterator* character_iterator = cmap_->Iterator(); + if (!character_iterator) + return false; + while (character_iterator->HasNext()) { + int32_t character = character_iterator->Next(); + if (!predicate_ || (*predicate_)(character)) { + chars_to_glyph_ids->insert + (std::make_pair(character, + GlyphId(cmap_->GlyphId(character), font_id_))); + } + } + delete character_iterator; + return true; +} + +bool +FontSourcedInfoBuilder::ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids, + GlyphIdSet* resolved_glyph_ids) { + if (!chars_to_glyph_ids || !resolved_glyph_ids) + return false; + resolved_glyph_ids->clear(); + resolved_glyph_ids->insert(GlyphId(0, font_id_)); + IntegerSet* unresolved_glyph_ids = new IntegerSet; + // Since composite glyph elements might themselves be composite, we would need + // to recursively resolve the elements too. To avoid the recursion we + // create two sets, |unresolved_glyph_ids| for the unresolved glyphs, + // initially containing all the ids and |resolved_glyph_ids|, initially empty. + // We'll remove glyph ids from |unresolved_glyph_ids| until it is empty and, + // if the glyph is composite, add its elements to the unresolved set. + for (CharacterMap::iterator it = chars_to_glyph_ids->begin(), + e = chars_to_glyph_ids->end(); it != e; ++it) { + unresolved_glyph_ids->insert(it->second.glyph_id()); + } + // As long as there are unresolved glyph ids. + while (!unresolved_glyph_ids->empty()) { + // Get the corresponding glyph. + int32_t glyph_id = *(unresolved_glyph_ids->begin()); + unresolved_glyph_ids->erase(unresolved_glyph_ids->begin()); + if (glyph_id < 0 || glyph_id > loca_table_->num_glyphs()) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "%d larger than %d or smaller than 0\n", glyph_id, + loca_table_->num_glyphs()); +#endif + continue; + } + int32_t length = loca_table_->GlyphLength(glyph_id); + if (length == 0) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Zero length glyph %d\n", glyph_id); +#endif + continue; + } + int32_t offset = loca_table_->GlyphOffset(glyph_id); + GlyphPtr glyph; + glyph.Attach(glyph_table_->GetGlyph(offset, length)); + if (glyph == NULL) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "GetGlyph returned NULL for %d\n", glyph_id); +#endif + continue; + } + // Mark the glyph as resolved. + resolved_glyph_ids->insert(GlyphId(glyph_id, font_id_)); + // If it is composite, add all its components to the unresolved glyph set. + if (glyph->GlyphType() == GlyphType::kComposite) { + Ptr<GlyphTable::CompositeGlyph> composite_glyph = + down_cast<GlyphTable::CompositeGlyph*>(glyph.p_); + int32_t num_glyphs = composite_glyph->NumGlyphs(); + for (int32_t i = 0; i < num_glyphs; ++i) { + int32_t glyph_id = composite_glyph->GlyphIndex(i); + if (resolved_glyph_ids->find(GlyphId(glyph_id, -1)) + == resolved_glyph_ids->end()) { + unresolved_glyph_ids->insert(glyph_id); + } + } + } + } + delete unresolved_glyph_ids; + return true; +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/font_info.h b/gfx/sfntly/cpp/src/sample/subtly/font_info.h new file mode 100644 index 0000000000..6f42d7314b --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/font_info.h @@ -0,0 +1,128 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_ + +#include <map> +#include <set> + +#include "sfntly/font.h" +#include "sfntly/port/type.h" +#include "sfntly/port/refcount.h" +#include "sfntly/table/core/cmap_table.h" +#include "sfntly/table/truetype/glyph_table.h" +#include "sfntly/table/truetype/loca_table.h" + +namespace subtly { +class CharacterPredicate; + +typedef int32_t FontId; +typedef std::map<FontId, sfntly::Ptr<sfntly::Font> > FontIdMap; + +// Glyph id pair that contains the loca table glyph id as well as the +// font id that has the glyph table this glyph belongs to. +class GlyphId { + public: + GlyphId(int32_t glyph_id, FontId font_id); + ~GlyphId() {} + + bool operator==(const GlyphId& other) const; + bool operator<(const GlyphId& other) const; + + int32_t glyph_id() const { return glyph_id_; } + void set_glyph_id(const int32_t glyph_id) { glyph_id_ = glyph_id; } + FontId font_id() const { return font_id_; } + void set_font_id(const FontId font_id) { font_id_ = font_id; } + + private: + int32_t glyph_id_; + FontId font_id_; +}; + +typedef std::map<int32_t, GlyphId> CharacterMap; +typedef std::set<GlyphId> GlyphIdSet; + +// Font information used for FontAssembler in the construction of a new font. +// Will make copies of character map, glyph id set and font id map. +class FontInfo : public sfntly::RefCounted<FontInfo> { + public: + // Empty FontInfo object. + FontInfo(); + // chars_to_glyph_ids maps characters to GlyphIds for CMap construction + // resolved_glyph_ids defines GlyphIds which should be in the final font + // fonts is a map of font ids to fonts to reference any needed table + FontInfo(CharacterMap* chars_to_glyph_ids, + GlyphIdSet* resolved_glyph_ids, + FontIdMap* fonts); + virtual ~FontInfo(); + + // Gets the table with the specified tag from the font corresponding to + // font_id or NULL if there is no such font/table. + // font_id is the id of the font that contains the table + // tag identifies the table to be obtained + virtual sfntly::FontDataTable* GetTable(FontId font_id, int32_t tag); + // Gets the table map of the font whose id is font_id + virtual const sfntly::TableMap* GetTableMap(FontId); + + CharacterMap* chars_to_glyph_ids() const { return chars_to_glyph_ids_; } + // Takes ownership of the chars_to_glyph_ids CharacterMap. + void set_chars_to_glyph_ids(CharacterMap* chars_to_glyph_ids); + GlyphIdSet* resolved_glyph_ids() const { return resolved_glyph_ids_; } + // Takes ownership of the glyph_ids GlyphIdSet. + void set_resolved_glyph_ids(GlyphIdSet* glyph_ids); + FontIdMap* fonts() const { return fonts_; } + // Takes ownership of the fonts FontIdMap. + void set_fonts(FontIdMap* fonts); + + private: + CharacterMap* chars_to_glyph_ids_; + GlyphIdSet* resolved_glyph_ids_; + FontIdMap* fonts_; +}; + +// FontSourcedInfoBuilder is used to create a FontInfo object from a Font +// optionally specifying a CharacterPredicate to filter out some of +// the font's characters. +// It does not take ownership or copy the values its constructor receives. +class FontSourcedInfoBuilder : + public sfntly::RefCounted<FontSourcedInfoBuilder> { + public: + FontSourcedInfoBuilder(sfntly::Font* font, FontId font_id); + FontSourcedInfoBuilder(sfntly::Font* font, + FontId font_id, + CharacterPredicate* predicate); + virtual ~FontSourcedInfoBuilder() { } + + virtual CALLER_ATTACH FontInfo* GetFontInfo(); + + protected: + bool GetCharacterMap(CharacterMap* chars_to_glyph_ids); + bool ResolveCompositeGlyphs(CharacterMap* chars_to_glyph_ids, + GlyphIdSet* resolved_glyph_ids); + void Initialize(); + + private: + sfntly::Ptr<sfntly::Font> font_; + FontId font_id_; + CharacterPredicate* predicate_; + + sfntly::Ptr<sfntly::CMapTable::CMap> cmap_; + sfntly::Ptr<sfntly::LocaTable> loca_table_; + sfntly::Ptr<sfntly::GlyphTable> glyph_table_; +}; +} +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/merger.cc b/gfx/sfntly/cpp/src/sample/subtly/merger.cc new file mode 100644 index 0000000000..7875c2d6b9 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/merger.cc @@ -0,0 +1,87 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subtly/merger.h" + +#include <stdio.h> + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "subtly/character_predicate.h" +#include "subtly/font_assembler.h" +#include "subtly/font_info.h" +#include "subtly/utils.h" + +namespace subtly { +using namespace sfntly; + +/****************************************************************************** + * Merger class + ******************************************************************************/ +Merger::Merger(FontArray* fonts) { + if (!fonts) { + return; + } + int32_t num_fonts = fonts->size(); + for (int32_t i = 0; i < num_fonts; ++i) { + fonts_.insert(std::make_pair(i, fonts->at(i))); + } +} + +CALLER_ATTACH Font* Merger::Merge() { + Ptr<FontInfo> merged_info; + merged_info.Attach(MergeFontInfos()); + if (!merged_info) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Could not create merged font info\n"); +#endif + return NULL; + } + Ptr<FontAssembler> font_assembler = new FontAssembler(merged_info); + return font_assembler->Assemble(); +} + +CALLER_ATTACH FontInfo* Merger::MergeFontInfos() { + Ptr<FontInfo> font_info = new FontInfo; + font_info->set_fonts(&fonts_); + for (FontIdMap::iterator it = fonts_.begin(), + e = fonts_.end(); it != e; ++it) { + Ptr<FontSourcedInfoBuilder> info_builder = + new FontSourcedInfoBuilder(it->second, it->first, NULL); + Ptr<FontInfo> current_font_info; + current_font_info.Attach(info_builder->GetFontInfo()); + if (!current_font_info) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Couldn't create font info. " + "No subset will be generated.\n"); +#endif + return NULL; + } + font_info->chars_to_glyph_ids()->insert( + current_font_info->chars_to_glyph_ids()->begin(), + current_font_info->chars_to_glyph_ids()->end()); + font_info->resolved_glyph_ids()->insert( + current_font_info->resolved_glyph_ids()->begin(), + current_font_info->resolved_glyph_ids()->end()); +#if defined (SUBTLY_DEBUG) + fprintf(stderr, "Counts: chars_to_glyph_ids: %d; resoved_glyph_ids: %d\n", + font_info->chars_to_glyph_ids()->size(), + font_info->resolved_glyph_ids()->size()); +#endif + } + return font_info.Detach(); +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/merger.h b/gfx/sfntly/cpp/src/sample/subtly/merger.h new file mode 100644 index 0000000000..43764a8b6c --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/merger.h @@ -0,0 +1,45 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_ + +#include "subtly/character_predicate.h" +#include "subtly/font_info.h" + +namespace sfntly { +class Font; +} + +namespace subtly { +// Merges the subsets in the font array into a single font. +class Merger : public sfntly::RefCounted<Merger> { + public: + explicit Merger(sfntly::FontArray* fonts); + virtual ~Merger() { } + + // Performs merging returning the subsetted font. + virtual CALLER_ATTACH sfntly::Font* Merge(); + + protected: + virtual CALLER_ATTACH FontInfo* MergeFontInfos(); + + private: + FontIdMap fonts_; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/merger_main.cc b/gfx/sfntly/cpp/src/sample/subtly/merger_main.cc new file mode 100644 index 0000000000..a977aa75f8 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/merger_main.cc @@ -0,0 +1,69 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <map> +#include <utility> + +#include "sfntly/font.h" +#include "subtly/merger.h" +#include "subtly/stats.h" +#include "subtly/utils.h" + +using namespace subtly; + +void PrintUsage(const char* program_name) { + fprintf(stdout, "Usage: %s <input_font_file1> <input_font_file2> ..." + "<input_font_filen> <output_font_file>\n", + program_name); +} + +void CheckLoading(const char* font_path, Font* font) { + if (!font || font->num_tables() == 0) { + fprintf(stderr, "Could not load font %s. Terminating.\n", font_path); + exit(1); + } +} + +int main(int argc, const char** argv) { + if (argc < 3) { + PrintUsage(argv[0]); + exit(1); + } + + FontArray fonts; + for (int32_t i = 1; i < argc - 1; ++i) { + Ptr<Font> font; + font.Attach(LoadFont(argv[i])); + CheckLoading(argv[i], font); + fonts.push_back(font); + } + + Ptr<Merger> merger = new Merger(&fonts); + FontPtr new_font; + new_font.Attach(merger->Merge()); + + fprintf(stderr, "Serializing font to %s\n", argv[argc - 1]); + SerializeFont(argv[argc - 1], new_font); + if (!new_font) { + fprintf(stdout, "Cannot create merged font.\n"); + return 1; + } + + return 0; +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/stats.cc b/gfx/sfntly/cpp/src/sample/subtly/stats.cc new file mode 100644 index 0000000000..769f691e06 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/stats.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> + +#include "sfntly/font.h" +#include "sfntly/table/table.h" +#include "sfntly/tag.h" +#include "subtly/stats.h" + +namespace subtly { +using namespace sfntly; + +int32_t TotalFontSize(Font* font) { + int32_t size = 0; + const TableMap* table_map = font->GetTableMap(); + for (TableMap::const_iterator it = table_map->begin(), + e = table_map->end(); it != e; ++it) { + size += it->second->DataLength(); + } + return size; +} + +double TableSizePercent(Font* font, int32_t tag) { + TablePtr table = font->GetTable(tag); + return static_cast<double>(table->DataLength()) / TotalFontSize(font) * 100; +} + +void PrintComparison(FILE* out, Font* font, Font* new_font) { + fprintf(out, "====== Table Comparison (original v. subset) ======\n"); + const TableMap* tables = font->GetTableMap(); + for (TableMap::const_iterator it = tables->begin(), + e = tables->end(); it != e; ++it) { + char *name = TagToString(it->first); + int32_t size = it->second->DataLength(); + fprintf(out, "-- %s: %d (%lf%%) ", name, size, + TableSizePercent(font, it->first)); + delete[] name; + + Ptr<FontDataTable> new_table = new_font->GetTable(it->first); + int32_t new_size = 0; + double size_percent = 0; + if (new_table) { + new_size = new_table->DataLength(); + size_percent = subtly::TableSizePercent(new_font, it->first); + } + + if (new_size == size) { + fprintf(out, "| same size\n"); + } else { + fprintf(out, "-> %d (%lf%%) | %lf%% of original\n", new_size, + size_percent, static_cast<double>(new_size) / size * 100); + } + } +} + +void PrintStats(FILE* out, Font* font) { + fprintf(out, "====== Table Stats ======\n"); + const TableMap* tables = font->GetTableMap(); + for (TableMap::const_iterator it = tables->begin(), + e = tables->end(); it != e; ++it) { + char *name = TagToString(it->first); + int32_t size = it->second->DataLength(); + fprintf(out, "-- %s: %d (%lf%%)\n", name, size, + TableSizePercent(font, it->first)); + delete[] name; + } +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/stats.h b/gfx/sfntly/cpp/src/sample/subtly/stats.h new file mode 100644 index 0000000000..89ef2aee19 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/stats.h @@ -0,0 +1,40 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_ + +#include <stdio.h> + +#include "sfntly/port/type.h" + +namespace sfntly { +class Font; +} + +namespace subtly { +using namespace sfntly; + +int32_t TotalFontSize(Font* font); + +double TableSizePercent(Font* font, int32_t tag); + +void PrintComparison(FILE* out, Font* font, Font* new_font); + +void PrintStats(FILE* out, Font* font); +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/subsetter.cc b/gfx/sfntly/cpp/src/sample/subtly/subsetter.cc new file mode 100644 index 0000000000..d09627ca35 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter.cc @@ -0,0 +1,67 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subtly/subsetter.h" + +#include <stdio.h> + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/tag.h" +#include "subtly/character_predicate.h" +#include "subtly/font_assembler.h" +#include "subtly/font_info.h" +#include "subtly/utils.h" + +namespace subtly { +using namespace sfntly; + +/****************************************************************************** + * Subsetter class + ******************************************************************************/ +Subsetter::Subsetter(Font* font, CharacterPredicate* predicate) + : font_(font), + predicate_(predicate) { +} + +Subsetter::Subsetter(const char* font_path, CharacterPredicate* predicate) + : predicate_(predicate) { + font_.Attach(LoadFont(font_path)); +} + +CALLER_ATTACH Font* Subsetter::Subset() { + Ptr<FontSourcedInfoBuilder> info_builder = + new FontSourcedInfoBuilder(font_, 0, predicate_); + + Ptr<FontInfo> font_info; + font_info.Attach(info_builder->GetFontInfo()); + if (!font_info) { +#if defined (SUBTLY_DEBUG) + fprintf(stderr, + "Couldn't create font info. No subset will be generated.\n"); +#endif + return NULL; + } + IntegerSet* table_blacklist = new IntegerSet; + table_blacklist->insert(Tag::DSIG); + Ptr<FontAssembler> font_assembler = new FontAssembler(font_info, + table_blacklist); + Ptr<Font> font_subset; + font_subset.Attach(font_assembler->Assemble()); + delete table_blacklist; + return font_subset.Detach(); +} +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/subsetter.h b/gfx/sfntly/cpp/src/sample/subtly/subsetter.h new file mode 100644 index 0000000000..a93747fafa --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter.h @@ -0,0 +1,41 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_ + +#include "sfntly/font.h" +// Cannot remove this header due to Ptr<T> instantiation issue +#include "subtly/character_predicate.h" + +namespace subtly { +// Subsets a given font using a character predicate. +class Subsetter : public sfntly::RefCounted<Subsetter> { + public: + Subsetter(sfntly::Font* font, CharacterPredicate* predicate); + Subsetter(const char* font_path, CharacterPredicate* predicate); + virtual ~Subsetter() { } + + // Performs subsetting returning the subsetted font. + virtual CALLER_ATTACH sfntly::Font* Subset(); + + private: + sfntly::Ptr<sfntly::Font> font_; + sfntly::Ptr<CharacterPredicate> predicate_; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_ diff --git a/gfx/sfntly/cpp/src/sample/subtly/subsetter_main.cc b/gfx/sfntly/cpp/src/sample/subtly/subsetter_main.cc new file mode 100644 index 0000000000..d438148680 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/subsetter_main.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <stdio.h> +#include <stdlib.h> + +#include <map> +#include <utility> + +#include "sfntly/font.h" +#include "subtly/character_predicate.h" +#include "subtly/stats.h" +#include "subtly/subsetter.h" +#include "subtly/utils.h" + +using namespace subtly; + +void PrintUsage(const char* program_name) { + fprintf(stdout, "Usage: %s <input_font_file> <output_font_file>" + "<start_char> <end_char>\n", program_name); +} + +int main(int argc, const char** argv) { + const char* program_name = argv[0]; + if (argc < 5) { + PrintUsage(program_name); + exit(1); + } + + const char* input_font_path = argv[1]; + const char* output_font_path = argv[2]; + FontPtr font; + font.Attach(subtly::LoadFont(input_font_path)); + if (font->num_tables() == 0) { + fprintf(stderr, "Could not load font %s.\n", input_font_path); + exit(1); + } + + const char* start_char = argv[3]; + const char* end_char = argv[4]; + if (start_char[1] != 0) { + fprintf(stderr, "Start character %c invalid.\n", start_char[0]); + exit(1); + } + if (end_char[1] != 0) { + fprintf(stderr, "Start character %c invalid.\n", end_char[0]); + exit(1); + } + int32_t original_size = TotalFontSize(font); + + + Ptr<CharacterPredicate> range_predicate = + new AcceptRange(start_char[0], end_char[0]); + Ptr<Subsetter> subsetter = new Subsetter(font, range_predicate); + Ptr<Font> new_font; + new_font.Attach(subsetter->Subset()); + if (!new_font) { + fprintf(stdout, "Cannot create subset.\n"); + return 0; + } + + subtly::SerializeFont(output_font_path, new_font); + subtly::PrintComparison(stdout, font, new_font); + int32_t new_size = TotalFontSize(new_font); + fprintf(stdout, "Went from %d to %d: %lf%% of original\n", + original_size, new_size, + static_cast<double>(new_size) / original_size * 100); + return 0; +} diff --git a/gfx/sfntly/cpp/src/sample/subtly/utils.cc b/gfx/sfntly/cpp/src/sample/subtly/utils.cc new file mode 100644 index 0000000000..15efb7a5d4 --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/utils.cc @@ -0,0 +1,91 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "subtly/utils.h" + +#include "sfntly/data/growable_memory_byte_array.h" +#include "sfntly/data/memory_byte_array.h" +#include "sfntly/font.h" +#include "sfntly/font_factory.h" +#include "sfntly/port/file_input_stream.h" +#include "sfntly/port/memory_output_stream.h" + +namespace subtly { +using namespace sfntly; + +CALLER_ATTACH Font* LoadFont(const char* font_path) { + Ptr<FontFactory> font_factory; + font_factory.Attach(FontFactory::GetInstance()); + FontArray fonts; + LoadFonts(font_path, font_factory, &fonts); + return fonts[0].Detach(); +} + +CALLER_ATTACH Font::Builder* LoadFontBuilder(const char* font_path) { + FontFactoryPtr font_factory; + font_factory.Attach(FontFactory::GetInstance()); + FontBuilderArray builders; + LoadFontBuilders(font_path, font_factory, &builders); + return builders[0].Detach(); +} + +void LoadFonts(const char* font_path, FontFactory* factory, FontArray* fonts) { + FileInputStream input_stream; + input_stream.Open(font_path); + factory->LoadFonts(&input_stream, fonts); + input_stream.Close(); +} + +void LoadFontBuilders(const char* font_path, + FontFactory* factory, + FontBuilderArray* builders) { + FileInputStream input_stream; + input_stream.Open(font_path); + factory->LoadFontsForBuilding(&input_stream, builders); + input_stream.Close(); +} + +bool SerializeFont(const char* font_path, Font* font) { + if (!font_path) + return false; + FontFactoryPtr font_factory; + font_factory.Attach(FontFactory::GetInstance()); + return SerializeFont(font_path, font_factory, font); +} + +bool SerializeFont(const char* font_path, FontFactory* factory, Font* font) { + if (!font_path || !factory || !font) + return false; + // Serializing the font to a stream. + MemoryOutputStream output_stream; + factory->SerializeFont(font, &output_stream); + // Serializing the stream to a file. + FILE* output_file = NULL; +#if defined WIN32 + fopen_s(&output_file, font_path, "wb"); +#else + output_file = fopen(font_path, "wb"); +#endif + if (output_file == static_cast<FILE*>(NULL)) + return false; + for (size_t i = 0; i < output_stream.Size(); ++i) { + fwrite(&(output_stream.Get()[i]), 1, 1, output_file); + } + fflush(output_file); + fclose(output_file); + return true; +} +}; diff --git a/gfx/sfntly/cpp/src/sample/subtly/utils.h b/gfx/sfntly/cpp/src/sample/subtly/utils.h new file mode 100644 index 0000000000..ba9f0d4d6a --- /dev/null +++ b/gfx/sfntly/cpp/src/sample/subtly/utils.h @@ -0,0 +1,38 @@ +/* + * Copyright 2011 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_ +#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_ + +#include "sfntly/font.h" +#include "sfntly/font_factory.h" + +namespace subtly { +CALLER_ATTACH sfntly::Font* LoadFont(const char* font_path); +CALLER_ATTACH sfntly::Font::Builder* LoadFontBuilder(const char* font_path); + +void LoadFonts(const char* font_path, sfntly::FontFactory* factory, + sfntly::FontArray* fonts); +void LoadFontBuilders(const char* font_path, + sfntly::FontFactory* factory, + sfntly::FontBuilderArray* builders); + +bool SerializeFont(const char* font_path, sfntly::Font* font); +bool SerializeFont(const char* font_path, sfntly::FontFactory* factory, + sfntly::Font* font); +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_ |