diff options
author | arthurhsu <arthurhsu@google.com> | 2011-10-18 19:32:16 +0000 |
---|---|---|
committer | arthurhsu <arthurhsu@google.com> | 2011-10-18 19:32:16 +0000 |
commit | 526b1cbc315cb6aafa4462d45289238937e54fea (patch) | |
tree | 9161d8be029be46f60c24245d60b3ca9b9f2949b /cpp | |
parent | 158cdcb9cf09418ba8b49f4be7be69e37aa8e9fa (diff) | |
download | sfntly-526b1cbc315cb6aafa4462d45289238937e54fea.tar.gz |
Update to 10-12-11 snapshot
Created new chrome_subsetter program to test font subsetter used in Chromium
Fix several porting bugs in previous CL
Diffstat (limited to 'cpp')
25 files changed, 797 insertions, 165 deletions
diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index 9a0a189..e7b87ed 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -108,3 +108,14 @@ endif(CMAKE_COMPILER_IS_GNUCXX) if(CMAKE_COMPILER_IS_GNUCXX) target_link_libraries(subtly_debug pthread) endif(CMAKE_COMPILER_IS_GNUCXX) + add_executable(chrome_subsetter + src/test/subsetter_impl.h + src/test/subsetter_impl.cc + src/test/font_subsetter.cc + src/test/font_subsetter.h + src/sample/chromium/chrome_subsetter.cc + ) + target_link_libraries(chrome_subsetter sfntly icuuc) + if(CMAKE_COMPILER_IS_GNUCXX) + target_link_libraries(chrome_subsetter pthread) + endif(CMAKE_COMPILER_IS_GNUCXX) diff --git a/cpp/src/sample/chromium/chrome_subsetter.cc b/cpp/src/sample/chromium/chrome_subsetter.cc new file mode 100644 index 0000000..6f5788e --- /dev/null +++ b/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 "test/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/cpp/src/sfntly/table/bitmap/bitmap_size_table.cc b/cpp/src/sfntly/table/bitmap/bitmap_size_table.cc index 93b8625..5e0c237 100644 --- a/cpp/src/sfntly/table/bitmap/bitmap_size_table.cc +++ b/cpp/src/sfntly/table/bitmap/bitmap_size_table.cc @@ -418,9 +418,9 @@ void BitmapSizeTable::Builder::Initialize(ReadableFontData* data) { if (data) { int32_t number_of_index_subtables = BitmapSizeTable::NumberOfIndexSubTables(data, 0); + index_sub_tables_.resize(number_of_index_subtables); for (int32_t i = 0; i < number_of_index_subtables; ++i) { - index_sub_tables_[index_sub_tables_.size()].Attach( - CreateIndexSubTableBuilder(i)); + index_sub_tables_[i].Attach(CreateIndexSubTableBuilder(i)); } } } diff --git a/cpp/src/sfntly/table/bitmap/ebdt_table.cc b/cpp/src/sfntly/table/bitmap/ebdt_table.cc index 75a5080..19e5e55 100644 --- a/cpp/src/sfntly/table/bitmap/ebdt_table.cc +++ b/cpp/src/sfntly/table/bitmap/ebdt_table.cc @@ -118,6 +118,7 @@ int32_t EbdtTable::Builder::SubSerialize(WritableFontData* new_data) { void EbdtTable::Builder::SetLoca(BitmapLocaList* loca_list) { assert(loca_list); Revert(); + glyph_loca_.resize(loca_list->size()); std::copy(loca_list->begin(), loca_list->end(), glyph_loca_.begin()); } @@ -131,7 +132,6 @@ void EbdtTable::Builder::GenerateLocaList(BitmapLocaList* output) { } } - output->resize(glyph_builders_.size()); int start_offset = Offset::kHeaderLength; for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(), builder_end = glyph_builders_.end(); @@ -211,7 +211,6 @@ void EbdtTable::Builder::Initialize(ReadableFontData* data, assert(output); output->clear(); - output->resize(loca_list->size()); if (data) { for (BitmapLocaList::iterator loca_map = loca_list->begin(), loca_end = loca_list->end(); diff --git a/cpp/src/sfntly/table/bitmap/ebdt_table.h b/cpp/src/sfntly/table/bitmap/ebdt_table.h index 723c3f9..79852c3 100644 --- a/cpp/src/sfntly/table/bitmap/ebdt_table.h +++ b/cpp/src/sfntly/table/bitmap/ebdt_table.h @@ -43,24 +43,24 @@ class EbdtTable : public SubTableContainerTable, void SetLoca(BitmapLocaList* loca_list); void GenerateLocaList(BitmapLocaList* output); -
- // Gets the List of glyph builders for the glyph table builder. These may be
- // manipulated in any way by the caller and the changes will be reflected in
- // the final glyph table produced.
- // If there is no current data for the glyph builder or the glyph builders
- // have not been previously set then this will return an empty glyph builder
- // List. If there is current data (i.e. data read from an existing font) and
- // the loca list has not been set or is null, empty, or invalid, then an
- // empty glyph builder List will be returned.
+ + // Gets the List of glyph builders for the glyph table builder. These may be + // manipulated in any way by the caller and the changes will be reflected in + // the final glyph table produced. + // If there is no current data for the glyph builder or the glyph builders + // have not been previously set then this will return an empty glyph builder + // List. If there is current data (i.e. data read from an existing font) and + // the loca list has not been set or is null, empty, or invalid, then an + // empty glyph builder List will be returned. // @return the list of glyph builders BitmapGlyphBuilderList* GlyphBuilders(); - // Replace the internal glyph builders with the one provided. The provided
- // list and all contained objects belong to this builder.
- // This call is only required if the entire set of glyphs in the glyph
- // table builder are being replaced. If the glyph builder list provided from
- // the {@link EbdtTable.Builder#glyphBuilders()} is being used and modified
- // then those changes will already be reflected in the glyph table builder.
+ // Replace the internal glyph builders with the one provided. The provided + // list and all contained objects belong to this builder. + // This call is only required if the entire set of glyphs in the glyph + // table builder are being replaced. If the glyph builder list provided from + // the {@link EbdtTable.Builder#glyphBuilders()} is being used and modified + // then those changes will already be reflected in the glyph table builder. // @param glyphBuilders the new glyph builders void SetGlyphBuilders(BitmapGlyphBuilderList* glyph_builders); @@ -102,6 +102,7 @@ class EbdtTable : public SubTableContainerTable, EbdtTable(Header* header, ReadableFontData* data); }; typedef Ptr<EbdtTable> EbdtTablePtr; +typedef Ptr<EbdtTable::Builder> EbdtTableBuilderPtr; } // namespace sfntly diff --git a/cpp/src/sfntly/table/bitmap/eblc_table.cc b/cpp/src/sfntly/table/bitmap/eblc_table.cc index bb36db0..6691b04 100644 --- a/cpp/src/sfntly/table/bitmap/eblc_table.cc +++ b/cpp/src/sfntly/table/bitmap/eblc_table.cc @@ -238,7 +238,6 @@ void EblcTable::Builder::GenerateLocaList(BitmapLocaList* output) { assert(output); BitmapSizeTableBuilderList* size_builder_list = GetSizeList(); output->clear(); - output->resize(size_builder_list->size()); #if defined (SFNTLY_DEBUG_BITMAP) int32_t size_index = 0; #endif diff --git a/cpp/src/sfntly/table/bitmap/eblc_table.h b/cpp/src/sfntly/table/bitmap/eblc_table.h index f8f052d..3a9b4c4 100644 --- a/cpp/src/sfntly/table/bitmap/eblc_table.h +++ b/cpp/src/sfntly/table/bitmap/eblc_table.h @@ -180,6 +180,7 @@ class EblcTable : public SubTableContainerTable, BitmapSizeTableList bitmap_size_table_; }; typedef Ptr<EblcTable> EblcTablePtr; +typedef Ptr<EblcTable::Builder> EblcTableBuilderPtr; } #endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_ diff --git a/cpp/src/sfntly/table/bitmap/ebsc_table.h b/cpp/src/sfntly/table/bitmap/ebsc_table.h index 43088fd..b79df38 100644 --- a/cpp/src/sfntly/table/bitmap/ebsc_table.h +++ b/cpp/src/sfntly/table/bitmap/ebsc_table.h @@ -61,9 +61,8 @@ class EbscTable : public Table, explicit BitmapScaleTable(ReadableFontData* data); }; - // TODO(stuartg): currently the builder is minimally functional - // -just builds from initial data - // - need to make fully working + // TODO(stuartg): currently the builder just builds from initial data + // - need to make fully working but few if any examples to test with class Builder : public Table::Builder, public RefCounted<Builder> { public: diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format1.cc b/cpp/src/sfntly/table/bitmap/index_sub_table_format1.cc index 809412d..634fbb0 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format1.cc +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format1.cc @@ -94,8 +94,8 @@ int32_t IndexSubTableFormat1::Builder::GlyphStartOffset(int32_t glyph_id) { return GetOffsetArray()->at(loca); } -CALLER_ATTACH -BitmapGlyphInfoIter* IndexSubTableFormat1::Builder::GetIterator() { +CALLER_ATTACH IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat1::Builder::GetIterator() { Ptr<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator> it = new IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator(this); return it.Detach(); @@ -188,6 +188,10 @@ int32_t IndexSubTableFormat1::Builder::SubSerialize( return size; } +IntegerList* IndexSubTableFormat1::Builder::OffsetArray() { + return GetOffsetArray(); +} + void IndexSubTableFormat1::Builder::SetOffsetArray( const IntegerList& offset_array) { offset_array_.clear(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format1.h b/cpp/src/sfntly/table/bitmap/index_sub_table_format1.h index c36bf18..1078831 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format1.h +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format1.h @@ -45,7 +45,7 @@ class IndexSubTableFormat1 : public IndexSubTable, virtual int32_t NumGlyphs(); virtual int32_t GlyphLength(int32_t glyph_id); virtual int32_t GlyphStartOffset(int32_t glyph_id); - CALLER_ATTACH virtual BitmapGlyphInfoIter* GetIterator(); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); virtual void SubDataSet(); @@ -53,6 +53,7 @@ class IndexSubTableFormat1 : public IndexSubTable, virtual bool SubReadyToSerialize(); virtual int32_t SubSerialize(WritableFontData* new_data); + IntegerList* OffsetArray(); void SetOffsetArray(const IntegerList& offset_array); CALLER_ATTACH BitmapGlyphInfoIter* Iterator(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format2.cc b/cpp/src/sfntly/table/bitmap/index_sub_table_format2.cc index e54481b..58881f4 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format2.cc +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format2.cc @@ -78,8 +78,8 @@ int32_t IndexSubTableFormat2::Builder::GlyphLength(int32_t glyph_id) { return ImageSize(); } -CALLER_ATTACH -BitmapGlyphInfoIter* IndexSubTableFormat2::Builder::GetIterator() { +CALLER_ATTACH IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat2::Builder::GetIterator() { Ptr<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator> it = new IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator(this); return it.Detach(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format2.h b/cpp/src/sfntly/table/bitmap/index_sub_table_format2.h index 7b13d60..52a9ae0 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format2.h +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format2.h @@ -45,7 +45,7 @@ class IndexSubTableFormat2 : public IndexSubTable, virtual int32_t NumGlyphs(); virtual int32_t GlyphStartOffset(int32_t glyph_id); virtual int32_t GlyphLength(int32_t glyph_id); - CALLER_ATTACH virtual BitmapGlyphInfoIter* GetIterator(); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); virtual void SubDataSet(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format3.cc b/cpp/src/sfntly/table/bitmap/index_sub_table_format3.cc index 75ff14c..eedc500 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format3.cc +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format3.cc @@ -95,8 +95,8 @@ int32_t IndexSubTableFormat3::Builder::GlyphLength(int32_t glyph_id) { return offset_array->at(loca + 1) - offset_array->at(loca); } -CALLER_ATTACH -BitmapGlyphInfoIter* IndexSubTableFormat3::Builder::GetIterator() { +CALLER_ATTACH IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat3::Builder::GetIterator() { Ptr<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator> it = new IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator(this); return it.Detach(); @@ -225,9 +225,9 @@ void IndexSubTableFormat3::Builder::Initialize(ReadableFontData* data) { if (data) { int32_t num_offsets = (last_glyph_index() - first_glyph_index() + 1) + 1; for (int32_t i = 0; i < num_offsets; ++i) { - offset_array_.push_back(data->ReadULongAsInt( + offset_array_.push_back(data->ReadUShort( EblcTable::Offset::kIndexSubTable3_offsetArray + - i * DataSize::kULONG)); + i * DataSize::kUSHORT)); } } } diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format3.h b/cpp/src/sfntly/table/bitmap/index_sub_table_format3.h index 2c4004c..e9f1fa2 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format3.h +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format3.h @@ -44,7 +44,7 @@ class IndexSubTableFormat3 : public IndexSubTable, virtual int32_t NumGlyphs(); virtual int32_t GlyphStartOffset(int32_t glyph_id); virtual int32_t GlyphLength(int32_t glyph_id); - CALLER_ATTACH virtual BitmapGlyphInfoIter* GetIterator(); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); virtual void SubDataSet(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format4.cc b/cpp/src/sfntly/table/bitmap/index_sub_table_format4.cc index efa5f28..898e288 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format4.cc +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format4.cc @@ -92,6 +92,10 @@ IndexSubTableFormat4::CodeOffsetPair::CodeOffsetPair(int32_t glyph_code, : glyph_code_(glyph_code), offset_(offset) { } +IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder() + : CodeOffsetPair(0, 0) { +} + IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder( int32_t glyph_code, int32_t offset) : CodeOffsetPair(glyph_code, offset) { @@ -137,8 +141,8 @@ int32_t IndexSubTableFormat4::Builder::GlyphStartOffset(int32_t glyph_id) { return GetOffsetArray()->at(pair_index).offset(); } -CALLER_ATTACH -BitmapGlyphInfoIter* IndexSubTableFormat4::Builder::GetIterator() { +CALLER_ATTACH IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat4::Builder::GetIterator() { Ptr<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator> it = new IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator(this); return it.Detach(); diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format4.h b/cpp/src/sfntly/table/bitmap/index_sub_table_format4.h index 258998d..be96628 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format4.h +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format4.h @@ -40,12 +40,12 @@ class IndexSubTableFormat4 : public IndexSubTable, class Builder; class CodeOffsetPairBuilder : public CodeOffsetPair { public: + CodeOffsetPairBuilder(); + CodeOffsetPairBuilder(int32_t glyph_code, int32_t offset); void set_glyph_code(int32_t v) { glyph_code_ = v; } void set_offset(int32_t v) { offset_ = v; } private: - CodeOffsetPairBuilder(int32_t glyph_code, int32_t offset); - friend class Builder; }; @@ -74,7 +74,7 @@ class IndexSubTableFormat4 : public IndexSubTable, virtual int32_t NumGlyphs(); virtual int32_t GlyphLength(int32_t glyph_id); virtual int32_t GlyphStartOffset(int32_t glyph_id); - CALLER_ATTACH virtual BitmapGlyphInfoIter* GetIterator(); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); virtual void SubDataSet(); @@ -130,7 +130,8 @@ class IndexSubTableFormat4 : public IndexSubTable, }; typedef Ptr<IndexSubTableFormat4> IndexSubTableFormat4Ptr; typedef Ptr<IndexSubTableFormat4::Builder> IndexSubTableFormat4BuilderPtr; - +typedef std::vector<IndexSubTableFormat4::CodeOffsetPairBuilder> + CodeOffsetPairBuilderList; } // namespace sfntly #endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_ diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format5.cc b/cpp/src/sfntly/table/bitmap/index_sub_table_format5.cc index 7ba78ec..1ddce05 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format5.cc +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format5.cc @@ -79,8 +79,7 @@ IndexSubTableFormat5::IndexSubTableFormat5(ReadableFontData* data, // static int32_t IndexSubTableFormat5::NumGlyphs(ReadableFontData* data, int32_t table_offset) { - UNREFERENCED_PARAMETER(table_offset); - int32_t num_glyphs = data->ReadULongAsInt( + int32_t num_glyphs = data->ReadULongAsInt(table_offset + EblcTable::Offset::kIndexSubTable5_numGlyphs); return num_glyphs; } @@ -115,8 +114,8 @@ int32_t IndexSubTableFormat5::Builder::GlyphStartOffset(int32_t glyph_id) { return (it - glyph_array->begin()) * ImageSize(); } -CALLER_ATTACH -BitmapGlyphInfoIter* IndexSubTableFormat5::Builder::GetIterator() { +CALLER_ATTACH IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* + IndexSubTableFormat5::Builder::GetIterator() { Ptr<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator> it = new IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator(this); return it.Detach(); @@ -282,7 +281,7 @@ void IndexSubTableFormat5::Builder::Initialize(ReadableFontData* data) { if (data) { int32_t num_glyphs = IndexSubTableFormat5::NumGlyphs(data, 0); for (int32_t i = 0; i < num_glyphs; ++i) { - glyph_array_.push_back(data->ReadULongAsInt( + glyph_array_.push_back(data->ReadUShort( EblcTable::Offset::kIndexSubTable5_glyphArray + i * DataSize::kUSHORT)); } diff --git a/cpp/src/sfntly/table/bitmap/index_sub_table_format5.h b/cpp/src/sfntly/table/bitmap/index_sub_table_format5.h index c480fd8..511f1fc 100644 --- a/cpp/src/sfntly/table/bitmap/index_sub_table_format5.h +++ b/cpp/src/sfntly/table/bitmap/index_sub_table_format5.h @@ -44,7 +44,7 @@ class IndexSubTableFormat5 : public IndexSubTable, virtual int32_t NumGlyphs(); virtual int32_t GlyphLength(int32_t glyph_id); virtual int32_t GlyphStartOffset(int32_t glyph_id); - CALLER_ATTACH virtual BitmapGlyphInfoIter* GetIterator(); + CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator(); virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data); virtual void SubDataSet(); diff --git a/cpp/src/sfntly/table/subtable.h b/cpp/src/sfntly/table/subtable.h index fbfb4b0..902d281 100644 --- a/cpp/src/sfntly/table/subtable.h +++ b/cpp/src/sfntly/table/subtable.h @@ -48,8 +48,8 @@ class SubTable : public FontDataTable { }; virtual ~SubTable(); + virtual int32_t Padding() { return padding_; } - int32_t padding() { return padding_; } // Sets the amount of padding that is part of the data being used by this // subtable. void set_padding(int32_t padding) { padding_ = padding; } diff --git a/cpp/src/sfntly/table/truetype/glyph_table.cc b/cpp/src/sfntly/table/truetype/glyph_table.cc index bec9c89..f38fac5 100644 --- a/cpp/src/sfntly/table/truetype/glyph_table.cc +++ b/cpp/src/sfntly/table/truetype/glyph_table.cc @@ -78,7 +78,6 @@ void GlyphTable::Builder::SetLoca(const IntegerList& loca) { void GlyphTable::Builder::GenerateLocaList(IntegerList* locas) { assert(locas); GlyphBuilderList* glyph_builders = GetGlyphBuilders(); - locas->resize(glyph_builders->size()); locas->push_back(0); if (glyph_builders->size() == 0) { locas->push_back(0); @@ -224,6 +223,11 @@ CALLER_ATTACH GlyphTable::Glyph* return glyph.Detach(); } +int32_t GlyphTable::Glyph::Padding() { + Initialize(); + return SubTable::Padding(); +} + int32_t GlyphTable::Glyph::GlyphType() { return glyph_type_; } @@ -332,7 +336,7 @@ int32_t GlyphTable::Glyph::Builder::SubSerialize(WritableFontData* new_data) { * GlyphTable::SimpleGlyph ******************************************************************************/ GlyphTable::SimpleGlyph::SimpleGlyph(ReadableFontData* data) - : GlyphTable::Glyph(data, GlyphType::kSimple) { + : GlyphTable::Glyph(data, GlyphType::kSimple), initialized_(false) { } GlyphTable::SimpleGlyph::~SimpleGlyph() { @@ -373,6 +377,7 @@ bool GlyphTable::SimpleGlyph::OnCurve(int32_t contour, int32_t point) { } void GlyphTable::SimpleGlyph::Initialize() { + AutoLock lock(initialization_lock_); if (initialized_) { return; } @@ -530,8 +535,9 @@ CALLER_ATTACH FontDataTable* GlyphTable::CompositeGlyph::CompositeGlyph(ReadableFontData* data) : GlyphTable::Glyph(data, GlyphType::kComposite), instruction_size_(0), - instructions_offset_(0) { - ParseData(); + instructions_offset_(0), + initialized_(false) { + Initialize(); } GlyphTable::CompositeGlyph::~CompositeGlyph() { @@ -607,7 +613,12 @@ CALLER_ATTACH ReadableFontData* GlyphTable::CompositeGlyph::Instructions() { data_->Slice(instructions_offset_, InstructionSize())); } -void GlyphTable::CompositeGlyph::ParseData() { +void GlyphTable::CompositeGlyph::Initialize() { + AutoLock lock(initialization_lock_); + if (initialized_) { + return; + } + int32_t index = 5 * DataSize::kUSHORT; int32_t flags = kFLAG_MORE_COMPONENTS; @@ -638,6 +649,8 @@ void GlyphTable::CompositeGlyph::ParseData() { } set_padding(DataLength() - non_padded_data_length); } + + initialized_ = true; } /****************************************************************************** diff --git a/cpp/src/sfntly/table/truetype/glyph_table.h b/cpp/src/sfntly/table/truetype/glyph_table.h index f8a7c33..0836971 100644 --- a/cpp/src/sfntly/table/truetype/glyph_table.h +++ b/cpp/src/sfntly/table/truetype/glyph_table.h @@ -79,6 +79,8 @@ class GlyphTable : public SubTableContainerTable, ReadableFontData* data, int32_t offset, int32_t length); + + virtual int32_t Padding(); virtual int32_t GlyphType(); virtual int32_t NumberOfContours(); virtual int32_t XMin(); @@ -93,6 +95,8 @@ class GlyphTable : public SubTableContainerTable, // Note: constructor refactored in C++ to avoid heavy lifting. // caller need to do data->Slice(offset, length) beforehand. Glyph(ReadableFontData* data, int32_t glyph_type); + virtual void Initialize() = 0; + // Note: Derived class to define initialization_lock_. private: static int32_t GlyphType(ReadableFontData* data, @@ -194,18 +198,20 @@ class GlyphTable : public SubTableContainerTable, virtual int32_t InstructionSize(); virtual CALLER_ATTACH ReadableFontData* Instructions(); + virtual void Initialize(); + int32_t NumberOfPoints(int32_t contour); int32_t XCoordinate(int32_t contour, int32_t point); int32_t YCoordinate(int32_t contour, int32_t point); bool OnCurve(int32_t contour, int32_t point); private: - void Initialize(); void ParseData(bool fill_arrays); int32_t FlagAsInt(int32_t index); int32_t ContourEndPoint(int32_t contour); bool initialized_; + Lock initialization_lock_; int32_t instruction_size_; int32_t number_of_points_; @@ -274,12 +280,15 @@ class GlyphTable : public SubTableContainerTable, virtual int32_t InstructionSize(); virtual CALLER_ATTACH ReadableFontData* Instructions(); - private: - void ParseData(); + protected: + virtual void Initialize(); + private: IntegerList contour_index_; int32_t instruction_size_; int32_t instructions_offset_; + bool initialized_; + Lock initialization_lock_; }; virtual ~GlyphTable(); diff --git a/cpp/src/sfntly/tag.h b/cpp/src/sfntly/tag.h index 87d2837..0ecbab8 100644 --- a/cpp/src/sfntly/tag.h +++ b/cpp/src/sfntly/tag.h @@ -50,7 +50,7 @@ struct Tag { static const int32_t CFF; static const int32_t VORG; - // bitmap glyph outlines + // opentype bitmap glyph outlines static const int32_t EBDT; static const int32_t EBLC; static const int32_t EBSC; @@ -88,7 +88,7 @@ struct Tag { static const int32_t Sile; static const int32_t Silf; - // Bitmap font tables + // truetype bitmap font tables static const int32_t bhed; static const int32_t bdat; static const int32_t bloc; diff --git a/cpp/src/test/bitmap_table_test.cc b/cpp/src/test/bitmap_table_test.cc index c8f239c..d5d718d 100644 --- a/cpp/src/test/bitmap_table_test.cc +++ b/cpp/src/test/bitmap_table_test.cc @@ -18,6 +18,7 @@ #include "sfntly/font.h" #include "sfntly/table/bitmap/ebdt_table.h" #include "sfntly/table/bitmap/eblc_table.h" +#include "sfntly/table/bitmap/index_sub_table_format3.h" #include "test/test_data.h" #include "test/test_font_utils.h" @@ -35,12 +36,14 @@ const int32_t STRIKE1_PPEM_Y = 10; const int32_t STRIKE1_BIT_DEPTH = 1; const int32_t STRIKE1_FLAGS = 0x01; +const int32_t STRIKE4_SUB1_INDEX_FORMAT = 3; const int32_t STRIKE4_SUB1_IMAGE_FORMAT = 1; const int32_t STRIKE4_SUB1_IMAGE_DATA_OFFSET = 0x00005893; const int32_t STRIKE4_SUB1_GLYPH_OFFSET[] = { 0x00005893, 0x00005898, 0x0000589d, 0x000058a2, 0x000058a7, 0x000058b2, 0x000058c2, 0x000058d0, 0x000058de, 0x000058e6 }; -const int32_t NUM_STRIKE4_SUB1_GLYPH_OFFSET = 10; // must be 1 less +const int32_t NUM_STRIKE4_SUB1_GLYPH_OFFSET = 10; +const int32_t STRIKE4_SUB1_GLYPH2_LENGTH = 0x58a2 - 0x589d; bool TestReadingBitmapTable() { FontFactoryPtr factory; @@ -79,15 +82,29 @@ bool TestReadingBitmapTable() { EXPECT_EQ(strike4->EndGlyphIndex(), STRIKE1_END_GLYPH_INDEX); IndexSubTablePtr sub1 = strike4->GetIndexSubTable(0); EXPECT_FALSE(sub1 == NULL); + EXPECT_EQ(sub1->index_format(), STRIKE4_SUB1_INDEX_FORMAT); + EXPECT_EQ(sub1->image_format(), STRIKE4_SUB1_IMAGE_FORMAT); EXPECT_EQ(sub1->first_glyph_index(), STRIKE1_START_GLYPH_INDEX); EXPECT_EQ(sub1->last_glyph_index(), STRIKE1_END_GLYPH_INDEX); - EXPECT_EQ(sub1->image_format(), STRIKE4_SUB1_IMAGE_FORMAT); EXPECT_EQ(sub1->image_data_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET); for (int32_t i = 0; i < NUM_STRIKE4_SUB1_GLYPH_OFFSET; ++i) { EXPECT_EQ(sub1->GlyphOffset(i), STRIKE4_SUB1_GLYPH_OFFSET[i]); } + // Strike 4 Index Sub Table 1 is a Format 3 + IndexSubTableFormat3Ptr sub3 = + down_cast<IndexSubTableFormat3*>(strike4->GetIndexSubTable(0)); + EXPECT_FALSE(sub3 == NULL); + BitmapGlyphInfoPtr info; + info.Attach(sub3->GlyphInfo(2)); + EXPECT_EQ(info->glyph_id(), 2); + EXPECT_EQ(info->block_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET); + EXPECT_EQ(info->start_offset(), + STRIKE4_SUB1_GLYPH_OFFSET[2] - STRIKE4_SUB1_GLYPH_OFFSET[0]); + EXPECT_EQ(info->format(), STRIKE4_SUB1_IMAGE_FORMAT); + EXPECT_EQ(info->length(), STRIKE4_SUB1_GLYPH2_LENGTH); + return true; } diff --git a/cpp/src/test/subsetter_impl.cc b/cpp/src/test/subsetter_impl.cc index f82a674..7934085 100644 --- a/cpp/src/test/subsetter_impl.cc +++ b/cpp/src/test/subsetter_impl.cc @@ -23,6 +23,14 @@ #include <map> #include <set> +#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/table/truetype/glyph_table.h" #include "sfntly/table/truetype/loca_table.h" @@ -34,7 +42,7 @@ namespace sfntly { void ConstructName(UChar* name_part, UnicodeString* name, int32_t name_id) { - switch(name_id) { + switch (name_id) { case NameId::kFullFontName: *name = name_part; break; @@ -73,80 +81,7 @@ int32_t HashCode(int32_t platform_id, int32_t encoding_id, int32_t language_id, return result; } -SubsetterImpl::SubsetterImpl() { -} - -SubsetterImpl::~SubsetterImpl() { -} - -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); - if (font_ == NULL) { - return false; - } - - return true; -} - -int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids, - size_t glyph_count, - unsigned char** output_buffer) { - if (factory_ == NULL || font_ == NULL) { - return -1; - } - - IntegerSet glyph_id_processed; - if (!ResolveCompositeGlyphs(glyph_ids, glyph_count, &glyph_id_processed) || - glyph_id_processed.empty()) { - return 0; - } - - FontPtr new_font; - new_font.Attach(Subset(glyph_id_processed)); - if (new_font == NULL) { - return 0; - } - - MemoryOutputStream output_stream; - factory_->SerializeFont(new_font, &output_stream); - int length = static_cast<int>(output_stream.Size()); - if (length > 0) { - *output_buffer = new unsigned char[length]; - memcpy(*output_buffer, output_stream.Get(), length); - } - - return length; -} - -Font* SubsetterImpl::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 b = font_array.begin(), e = font_array.end(); - b != e; ++b) { - if (HasName(font_name, (*b).p_)) { - return (*b).p_; - } - } - } - - return font_array[0].p_; -} - -bool SubsetterImpl::HasName(const char* font_name, Font* font) { +bool HasName(const char* font_name, Font* font) { UnicodeString font_string = UnicodeString::fromUTF8(font_name); if (font_string.isEmpty()) return false; @@ -162,7 +97,7 @@ bool SubsetterImpl::HasName(const char* font_name, Font* font) { } for (int32_t i = 0; i < name_table->NameCount(); ++i) { - switch(name_table->NameId(i)) { + switch (name_table->NameId(i)) { case NameId::kFontFamilyName: case NameId::kFontSubfamilyName: case NameId::kFullFontName: @@ -195,22 +130,37 @@ bool SubsetterImpl::HasName(const char* font_name, Font* font) { return false; } -bool SubsetterImpl::ResolveCompositeGlyphs(const unsigned int* glyph_ids, - size_t glyph_count, - IntegerSet* glyph_id_processed) { - if (glyph_ids == NULL || glyph_count == 0 || glyph_id_processed == NULL) { - return false; +Font* FindFont(const char* font_name, const FontArray& font_array) { + if (font_array.empty() || font_array[0] == NULL) { + return NULL; } - // 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) { - // The font is invalid. + if (font_name && strlen(font_name)) { + for (FontArray::const_iterator b = font_array.begin(), e = font_array.end(); + b != e; ++b) { + if (HasName(font_name, (*b).p_)) { + return (*b).p_; + } + } + } + + return font_array[0].p_; +} + +bool ResolveCompositeGlyphs(GlyphTable* glyf, + LocaTable* loca, + const unsigned int* glyph_ids, + size_t glyph_count, + IntegerSet* glyph_id_processed) { + if (glyf == NULL || loca == NULL || glyph_ids == NULL || glyph_count == 0 || + glyph_id_processed == NULL) { return false; } + // Find glyf and loca table. + GlyphTablePtr glyph_table = glyf; + LocaTablePtr loca_table = loca; + // Sort and uniquify glyph ids. IntegerSet glyph_id_remaining; glyph_id_remaining.insert(0); // Always include glyph id 0. @@ -265,23 +215,26 @@ bool SubsetterImpl::ResolveCompositeGlyphs(const unsigned int* glyph_ids, return true; } -CALLER_ATTACH Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids) { - // The tables are already checked in ResolveCompositeGlyphs(). - GlyphTablePtr glyph_table = - down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); - LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); +bool SetupGlyfBuilders(Font::Builder* builder, + GlyphTable* glyf, + LocaTable* loca, + const IntegerSet& glyph_ids) { + if (!builder || !glyf || !loca) { + return false; + } - // Setup font builders we need. - FontBuilderPtr font_builder; - font_builder.Attach(factory_->NewFontBuilder()); + // The tables are already checked in ResolveCompositeGlyphs(). + GlyphTablePtr glyph_table = glyf; + LocaTablePtr loca_table = loca; + FontBuilderPtr font_builder = builder; 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 NULL; + return false; } // Extract glyphs and setup loca list. @@ -322,12 +275,499 @@ CALLER_ATTACH Font* SubsetterImpl::Subset(const IntegerSet& glyph_ids) { } loca_table_builder->SetLocaList(&loca_list); + return true; +} + +bool HasOverlap(int32_t range1_begin, int32_t range1_end, + int32_t range2_begin, int32_t range2_end) { + return (range2_begin < range1_end && range2_begin > range1_begin) || + (range1_begin < range2_end && range1_begin > range2_begin); +} + +// Initialize builder, returns false if glyph_id subset is not covered. +bool ShallSubset(EbdtTable::Builder* ebdt, EblcTable::Builder* eblc, + const IntegerSet& glyph_ids) { + EblcTableBuilderPtr eblc_builder = eblc; + EbdtTableBuilderPtr ebdt_builder = ebdt; + + BitmapLocaList loca_list; + BitmapSizeTableBuilderList* strikes = eblc_builder->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 inproperly will result in creation of >100K objects that + // will be destroyed immediately and result in significant slowness. + IntegerList removed_strikes; + for (size_t i = 0; i < strikes->size(); i++) { + if (!HasOverlap((*strikes)[i]->StartGlyphIndex(), + (*strikes)[i]->EndGlyphIndex(), + *(glyph_ids.begin()), *(glyph_ids.rbegin()))) { + removed_strikes.push_back(i); + continue; + } + + IndexSubTableBuilderList* index_builders = + (*strikes)[i]->IndexSubTableBuilders(); + IntegerList removed_indexes; + for (size_t j = 0; j < index_builders->size(); ++j) { + BitmapGlyphInfoMap info_map; + for (IntegerSet::const_iterator gid = glyph_ids.begin(), + gid_end = glyph_ids.end(); + gid != gid_end; gid++) { + if ((*index_builders)[j]->first_glyph_index() <= *gid && + (*index_builders)[j]->last_glyph_index() >= *gid && + (*index_builders)[j]->GlyphStartOffset(*gid) != -1) { + BitmapGlyphInfoPtr info = + new BitmapGlyphInfo(*gid, + (*index_builders)[j]->image_data_offset() + + (*index_builders)[j]->GlyphStartOffset(*gid), + (*index_builders)[j]->GlyphLength(*gid), + (*index_builders)[j]->image_format()); + info_map[*gid] = info; + } + } + if (!info_map.empty()) { + loca_list.push_back(info_map); + } else { + removed_indexes.push_back(j); + } + } + + // 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; // All strikes shall be gone. + } + + // Remove unused strikes + for (IntegerList::reverse_iterator j = removed_strikes.rbegin(), + e = removed_strikes.rend(); j != e; j++) { + strikes->erase(strikes->begin() + *j); + } + + ebdt_builder->SetLoca(&loca_list); + ebdt_builder->GlyphBuilders(); // Initialize the builder. + return true; +} + +void GenerateOffsetArray(int32_t first_gid, int32_t last_gid, + const BitmapGlyphInfoMap& loca, + IntegerList* new_offsets) { + int32_t offset = 0; + int32_t length = 0; + for (int32_t i = first_gid; i <= last_gid; ++i) { + BitmapGlyphInfoMap::const_iterator it = loca.find(i); + if (it != loca.end()) { + offset = it->second->offset(); + length = it->second->length(); + new_offsets->push_back(offset); + if (i == last_gid) { + new_offsets->push_back(offset + length); + } + } else { // Glyph id is not in subset. + offset += length; + new_offsets->push_back(offset); + length = 0; + } + } +} + +/****************************************************************************** + * EXPERIMENTAL CODE STARTS + * + * The following code is used for experiment. Will obsolete once we have + * support to create format 4 and 5 index sub tables from scratch. + *****************************************************************************/ +void SubsetIndexSubTableFormat1(IndexSubTable::Builder* b, + const BitmapGlyphInfoMap& loca) { + IndexSubTableFormat1BuilderPtr builder = + down_cast<IndexSubTableFormat1::Builder*>(b); + if (builder->first_glyph_index() < loca.begin()->first) { + builder->set_first_glyph_index(loca.begin()->first); + } + if (builder->last_glyph_index() > loca.rbegin()->first) { + builder->set_last_glyph_index(loca.rbegin()->first); + } + builder->set_image_data_offset(loca.begin()->second->block_offset()); + + IntegerList new_offsets; + GenerateOffsetArray(builder->first_glyph_index(), builder->last_glyph_index(), + loca, &new_offsets); + builder->SetOffsetArray(new_offsets); +} + +void SubsetIndexSubTableFormat2(IndexSubTable::Builder* b, + const BitmapGlyphInfoMap& loca) { + UNREFERENCED_PARAMETER(b); + UNREFERENCED_PARAMETER(loca); +} + +void SubsetIndexSubTableFormat3(IndexSubTable::Builder* b, + const BitmapGlyphInfoMap& loca) { + IndexSubTableFormat3BuilderPtr builder = + down_cast<IndexSubTableFormat3::Builder*>(b); + if (builder->first_glyph_index() < loca.begin()->first) { + builder->set_first_glyph_index(loca.begin()->first); + } + if (builder->last_glyph_index() > loca.rbegin()->first) { + builder->set_last_glyph_index(loca.rbegin()->first); + } + builder->set_image_data_offset(loca.begin()->second->block_offset()); + + IntegerList new_offsets; + GenerateOffsetArray(builder->first_glyph_index(), builder->last_glyph_index(), + loca, &new_offsets); + builder->SetOffsetArray(new_offsets); +} + +void SubsetIndexSubTableFormat4(IndexSubTable::Builder* b, + const BitmapGlyphInfoMap& loca) { + IndexSubTableFormat4BuilderPtr builder = + down_cast<IndexSubTableFormat4::Builder*>(b); + CodeOffsetPairBuilderList pairs; + pairs.resize(loca.size()); + size_t index = 0; + for (BitmapGlyphInfoMap::const_iterator i = loca.begin(), e = loca.end(); + i != e; i++) { + pairs[index].set_glyph_code(i->first); + pairs[index].set_offset(i->second->offset()); + index++; + } + builder->SetOffsetArray(pairs); +} + +void SubsetIndexSubTableFormat5(IndexSubTable::Builder* b, + const BitmapGlyphInfoMap& loca) { + IndexSubTableFormat5BuilderPtr builder = + down_cast<IndexSubTableFormat5::Builder*>(b); + IntegerList* glyph_array = builder->GlyphArray(); + for (IntegerList::iterator i = glyph_array->begin(); i != glyph_array->end(); + i++) { + if (loca.find(*i) == loca.end()) { + glyph_array->erase(i); + } + } + if (!glyph_array->empty()) { + builder->set_first_glyph_index(*(glyph_array->begin())); + builder->set_last_glyph_index(*(glyph_array->rbegin())); + } else { + builder->set_first_glyph_index(0); + builder->set_last_glyph_index(0); + } +} + +void SubsetIndexSubTable(IndexSubTable::Builder* builder, + const BitmapGlyphInfoMap& loca) { + switch (builder->index_format()) { + case 1: + SubsetIndexSubTableFormat1(builder, loca); + break; + case 2: + SubsetIndexSubTableFormat2(builder, loca); + break; + case 3: + SubsetIndexSubTableFormat3(builder, loca); + break; + case 4: + SubsetIndexSubTableFormat4(builder, loca); + break; + case 5: + SubsetIndexSubTableFormat5(builder, loca); + break; + default: + assert(false); // Shall not be here. + break; + } +} + +void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca) { + EblcTableBuilderPtr eblc_builder = eblc; + BitmapSizeTableBuilderList* size_builders = eblc->BitmapSizeBuilders(); + if (size_builders == NULL) { + return; // No valid EBLC. + } + + for (size_t strike = 0; strike < size_builders->size(); ++strike) { + IndexSubTableBuilderList* index_builders = + (*size_builders)[strike]->IndexSubTableBuilders(); + bool format4_processed = false; + for (size_t index = 0; index < index_builders->size(); ++index) { + // Only one format 4 table per strike. + if ((*index_builders)[index]->index_format() == 4 && format4_processed) { + continue; + } + SubsetIndexSubTable((*index_builders)[index], new_loca[strike]); + if ((*index_builders)[index]->index_format() == 4) { + format4_processed = true; + } + } + } +} +/****************************************************************************** + * EXPERIMENTAL CODE ENDS + *****************************************************************************/ + +/****************************************************************************** + Long background comments + +EBLC structure: + 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 + +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: + 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. As a result, the EBLC table shall be + reconstructed to either format 4 or 5. +*******************************************************************************/ +bool SetupBitmapBuilders(Font* font, Font::Builder* builder, + const IntegerSet& glyph_ids) { + if (!font || !builder) { + return false; + } + + // Check if bitmap table exists. + bool use_ebdt = true; + EbdtTablePtr ebdt_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT)); + EblcTablePtr eblc_table = down_cast<EblcTable*>(font->GetTable(Tag::EBLC)); + if (ebdt_table == NULL && eblc_table == NULL) { + use_ebdt = false; + // Check BDAT variants. + 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) { + // There's no bitmap tables. + return true; + } + + // If the bitmap table's size is too small, skip subsetting. + // TODO(arthurhsu): temporarily comment out to use smaller font for testing. + /* + if (ebdt_table->DataLength() + eblc_table->DataLength() < + BITMAP_SIZE_THRESHOLD) { + return true; + } + */ + + // Get the builders. + FontBuilderPtr font_builder = builder; + 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 false; + } + + if (!ShallSubset(ebdt_table_builder, eblc_table_builder, glyph_ids)) { + // Bitmap tables do not cover the glyphs in our subset. + ebdt_table_builder.Release(); + eblc_table_builder.Release(); + font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBDT : Tag::bdat); + font_builder->RemoveTableBuilder(use_ebdt ? Tag::EBLC : Tag::bloc); + return true; + } + + BitmapLocaList new_loca; + ebdt_table_builder->GenerateLocaList(&new_loca); + SubsetEBLC(eblc_table_builder, new_loca); + + return true; +} + +SubsetterImpl::SubsetterImpl() { +} + +SubsetterImpl::~SubsetterImpl() { +} + +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); + if (font_ == NULL) { + return false; + } + + return true; +} + +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)); + if (new_font == NULL) { + return 0; + } + + MemoryOutputStream output_stream; + factory_->SerializeFont(new_font, &output_stream); + int length = static_cast<int>(output_stream.Size()); + if (length > 0) { + *output_buffer = new unsigned char[length]; + memcpy(*output_buffer, output_stream.Get(), length); + } + + return length; +} + +/******************************************************************************* + Long comments regarding TTF tables and PDF + +According to PDF spec (section 9.9), the following tables must present: + head, hhea, loca, maxp, cvt, prep, glyf, hmtx, fpgm + cmap if font is used as a TTF 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 - shall 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 if you saw 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) { + // 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 VALID_TABLE_TAG[] = { + 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. + }; + + // Setup font builders we need. + FontBuilderPtr font_builder; + font_builder.Attach(factory_->NewFontBuilder()); + IntegerSet remove_tags; + + GlyphTablePtr glyph_table = + down_cast<GlyphTable*>(font_->GetTable(Tag::glyf)); + LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca)); + + if (SetupGlyfBuilders(font_builder, glyph_table, loca_table, glyph_ids)) { + remove_tags.insert(Tag::glyf); + remove_tags.insert(Tag::loca); + } + if (SetupBitmapBuilders(font_, font_builder, glyph_ids)) { + remove_tags.insert(Tag::bdat); + remove_tags.insert(Tag::bloc); + remove_tags.insert(Tag::bhed); + remove_tags.insert(Tag::EBDT); + remove_tags.insert(Tag::EBLC); + remove_tags.insert(Tag::EBSC); + } + + IntegerSet allowed_tags; + for (size_t i = 0; i < sizeof(VALID_TABLE_TAG) / sizeof(int32_t); ++i) { + allowed_tags.insert(VALID_TABLE_TAG[i]); + } + for (IntegerSet::iterator i = remove_tags.begin(), e = remove_tags.end(); + i != e; i++) { + IntegerSet::iterator it = allowed_tags.find(*i); + if (it != allowed_tags.end()) { + allowed_tags.erase(it); + } + } + // Setup remaining builders. - for (TableMap::const_iterator i = font_->GetTableMap()->begin(), - e = font_->GetTableMap()->end(); i != e; ++i) { - // We already build the builder for glyph and loca. - if (i->first != Tag::glyf && i->first != Tag::loca) { - font_builder->NewTableBuilder(i->first, i->second->ReadFontData()); + for (IntegerSet::iterator i = allowed_tags.begin(), e = allowed_tags.end(); + i != e; ++i) { + Table* table = font_->GetTable(*i); + if (table) { + font_builder->NewTableBuilder(*i, table->ReadFontData()); } } diff --git a/cpp/src/test/subsetter_impl.h b/cpp/src/test/subsetter_impl.h index f3a8bf3..abcc420 100644 --- a/cpp/src/test/subsetter_impl.h +++ b/cpp/src/test/subsetter_impl.h @@ -21,6 +21,7 @@ #include "sfntly/font.h" #include "sfntly/font_factory.h" +#include "sfntly/tag.h" namespace sfntly { @@ -46,6 +47,13 @@ namespace sfntly { // } // ref count = 1, obj2 out of scope // obj.release(); // ref count = 0, object destroyed +namespace { + +// The bitmap tables must be greater than 256KB to trigger bitmap subsetter. +static const int BITMAP_SIZE_THRESHOLD = 262144; + +} + class SubsetterImpl { public: SubsetterImpl(); @@ -59,11 +67,6 @@ class SubsetterImpl { unsigned char** output_buffer); private: - Font* FindFont(const char* font_name, const FontArray& font_array); - bool HasName(const char* font_name, Font* font); - bool ResolveCompositeGlyphs(const unsigned int* glyph_ids, - size_t glyph_count, - IntegerSet* glyph_id_processed); CALLER_ATTACH Font* Subset(const IntegerSet& glyph_ids); FontFactoryPtr factory_; |