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