From 8c433a9f5819ad995a14c8476c266487c8a82f53 Mon Sep 17 00:00:00 2001 From: "dfilimon@google.com" Date: Wed, 21 Sep 2011 03:56:38 +0000 Subject: Added sample subsetter and merger. git-svn-id: http://sfntly.googlecode.com/svn/trunk/cpp/src@91 672e30a5-4c29-85ac-ac6d-611c735e0a51 --- sample/subtly/character_predicate.cc | 52 +++++++ sample/subtly/character_predicate.h | 68 ++++++++++ sample/subtly/debug_main.cc | 64 +++++++++ sample/subtly/font_assembler.cc | 227 +++++++++++++++++++++++++++++++ sample/subtly/font_assembler.h | 67 +++++++++ sample/subtly/font_info.cc | 256 +++++++++++++++++++++++++++++++++++ sample/subtly/font_info.h | 128 ++++++++++++++++++ sample/subtly/merger.cc | 87 ++++++++++++ sample/subtly/merger.h | 45 ++++++ sample/subtly/merger_main.cc | 69 ++++++++++ sample/subtly/stats.cc | 82 +++++++++++ sample/subtly/stats.h | 40 ++++++ sample/subtly/subsetter.cc | 67 +++++++++ sample/subtly/subsetter.h | 41 ++++++ sample/subtly/subsetter_main.cc | 82 +++++++++++ sample/subtly/utils.cc | 91 +++++++++++++ sample/subtly/utils.h | 38 ++++++ 17 files changed, 1504 insertions(+) create mode 100644 sample/subtly/character_predicate.cc create mode 100644 sample/subtly/character_predicate.h create mode 100644 sample/subtly/debug_main.cc create mode 100644 sample/subtly/font_assembler.cc create mode 100644 sample/subtly/font_assembler.h create mode 100644 sample/subtly/font_info.cc create mode 100644 sample/subtly/font_info.h create mode 100644 sample/subtly/merger.cc create mode 100644 sample/subtly/merger.h create mode 100644 sample/subtly/merger_main.cc create mode 100644 sample/subtly/stats.cc create mode 100644 sample/subtly/stats.h create mode 100644 sample/subtly/subsetter.cc create mode 100644 sample/subtly/subsetter.h create mode 100644 sample/subtly/subsetter_main.cc create mode 100644 sample/subtly/utils.cc create mode 100644 sample/subtly/utils.h diff --git a/sample/subtly/character_predicate.cc b/sample/subtly/character_predicate.cc new file mode 100644 index 0000000..1509260 --- /dev/null +++ b/sample/subtly/character_predicate.cc @@ -0,0 +1,52 @@ +/* + * 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 { + return true; +} +} diff --git a/sample/subtly/character_predicate.h b/sample/subtly/character_predicate.h new file mode 100644 index 0000000..a6e3ea3 --- /dev/null +++ b/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 { + 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 { + 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 { + public: + AcceptAll() {} + ~AcceptAll() {} + virtual bool operator()(int32_t character) const; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_ diff --git a/sample/subtly/debug_main.cc b/sample/subtly/debug_main.cc new file mode 100644 index 0000000..8324cde --- /dev/null +++ b/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 +#include + +#include +#include + +#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 \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 = new Subsetter(font, NULL); + Ptr 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(new_size) / original_size * 100); + return 0; +} diff --git a/sample/subtly/font_assembler.cc b/sample/subtly/font_assembler.cc new file mode 100644 index 0000000..2f7cd11 --- /dev/null +++ b/sample/subtly/font_assembler.cc @@ -0,0 +1,227 @@ +/* + * 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 + +#include +#include + +#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 cmap_table_builder = + down_cast + (font_builder_->NewTableBuilder(Tag::cmap)); + if (!cmap_table_builder) + return false; + Ptr cmap_builder = + down_cast + (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 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 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 loca_table_builder = + down_cast + (font_builder_->NewTableBuilder(Tag::loca)); + Ptr glyph_table_builder = + down_cast + (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 loca_table = + down_cast(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 first_loca_table = + down_cast + (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 loca_table = + down_cast + (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 glyph_table = + down_cast + (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 data = glyph->ReadFontData(); + Ptr 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. + 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/sample/subtly/font_assembler.h b/sample/subtly/font_assembler.h new file mode 100644 index 0000000..c53c21f --- /dev/null +++ b/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 +#include + +#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 { + 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 font_info_; + sfntly::Ptr font_factory_; + sfntly::Ptr font_builder_; + sfntly::IntegerSet* table_blacklist_; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_ diff --git a/sample/subtly/font_info.cc b/sample/subtly/font_info.cc new file mode 100644 index 0000000..6eb6a38 --- /dev/null +++ b/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 + +#include +#include + +#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 cmap_table = down_cast(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(font_->GetTable(Tag::loca)); + glyph_table_ = down_cast(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 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 composite_glyph = + down_cast(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/sample/subtly/font_info.h b/sample/subtly/font_info.h new file mode 100644 index 0000000..6f42d73 --- /dev/null +++ b/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 +#include + +#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 > 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 CharacterMap; +typedef std::set 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 { + 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 { + 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 font_; + FontId font_id_; + CharacterPredicate* predicate_; + + sfntly::Ptr cmap_; + sfntly::Ptr loca_table_; + sfntly::Ptr glyph_table_; +}; +} +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_ diff --git a/sample/subtly/merger.cc b/sample/subtly/merger.cc new file mode 100644 index 0000000..7875c2d --- /dev/null +++ b/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 + +#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 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 font_assembler = new FontAssembler(merged_info); + return font_assembler->Assemble(); +} + +CALLER_ATTACH FontInfo* Merger::MergeFontInfos() { + Ptr font_info = new FontInfo; + font_info->set_fonts(&fonts_); + for (FontIdMap::iterator it = fonts_.begin(), + e = fonts_.end(); it != e; ++it) { + Ptr info_builder = + new FontSourcedInfoBuilder(it->second, it->first, NULL); + Ptr 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/sample/subtly/merger.h b/sample/subtly/merger.h new file mode 100644 index 0000000..43764a8 --- /dev/null +++ b/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 { + 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/sample/subtly/merger_main.cc b/sample/subtly/merger_main.cc new file mode 100644 index 0000000..a977aa7 --- /dev/null +++ b/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 +#include + +#include +#include + +#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 ..." + " \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.Attach(LoadFont(argv[i])); + CheckLoading(argv[i], font); + fonts.push_back(font); + } + + Ptr 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/sample/subtly/stats.cc b/sample/subtly/stats.cc new file mode 100644 index 0000000..769f691 --- /dev/null +++ b/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 + +#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(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 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(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/sample/subtly/stats.h b/sample/subtly/stats.h new file mode 100644 index 0000000..89ef2ae --- /dev/null +++ b/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 + +#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/sample/subtly/subsetter.cc b/sample/subtly/subsetter.cc new file mode 100644 index 0000000..d09627c --- /dev/null +++ b/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 + +#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 info_builder = + new FontSourcedInfoBuilder(font_, 0, predicate_); + + Ptr 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 font_assembler = new FontAssembler(font_info, + table_blacklist); + Ptr font_subset; + font_subset.Attach(font_assembler->Assemble()); + delete table_blacklist; + return font_subset.Detach(); +} +} diff --git a/sample/subtly/subsetter.h b/sample/subtly/subsetter.h new file mode 100644 index 0000000..a93747f --- /dev/null +++ b/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 instantiation issue +#include "subtly/character_predicate.h" + +namespace subtly { +// Subsets a given font using a character predicate. +class Subsetter : public sfntly::RefCounted { + 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 font_; + sfntly::Ptr predicate_; +}; +} + +#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_ diff --git a/sample/subtly/subsetter_main.cc b/sample/subtly/subsetter_main.cc new file mode 100644 index 0000000..d438148 --- /dev/null +++ b/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 +#include + +#include +#include + +#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 " + " \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 range_predicate = + new AcceptRange(start_char[0], end_char[0]); + Ptr subsetter = new Subsetter(font, range_predicate); + Ptr 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(new_size) / original_size * 100); + return 0; +} diff --git a/sample/subtly/utils.cc b/sample/subtly/utils.cc new file mode 100644 index 0000000..0d59155 --- /dev/null +++ b/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 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 == reinterpret_cast(NULL)) + return false; + for (int32_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/sample/subtly/utils.h b/sample/subtly/utils.h new file mode 100644 index 0000000..ba9f0d4 --- /dev/null +++ b/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_ -- cgit v1.2.3