summaryrefslogtreecommitdiffstats
path: root/third_party/sfntly/src/subsetter/subsetter_impl.cc
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/sfntly/src/subsetter/subsetter_impl.cc')
-rw-r--r--third_party/sfntly/src/subsetter/subsetter_impl.cc289
1 files changed, 289 insertions, 0 deletions
diff --git a/third_party/sfntly/src/subsetter/subsetter_impl.cc b/third_party/sfntly/src/subsetter/subsetter_impl.cc
new file mode 100644
index 0000000..290ae9f
--- /dev/null
+++ b/third_party/sfntly/src/subsetter/subsetter_impl.cc
@@ -0,0 +1,289 @@
+/*
+ * 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 "third_party/sfntly/src/subsetter/subsetter_impl.h"
+
+#include <string.h>
+
+#include <map>
+#include <set>
+
+#include "third_party/sfntly/src/sfntly/glyph_table.h"
+#include "third_party/sfntly/src/sfntly/loca_table.h"
+#include "third_party/sfntly/src/sfntly/name_table.h"
+#include "third_party/sfntly/src/sfntly/tag.h"
+#include "third_party/sfntly/src/sfntly/data/memory_byte_array.h"
+#include "third_party/sfntly/src/sfntly/port/memory_output_stream.h"
+
+namespace sfntly {
+
+void ConstructName(UChar* name_part, UnicodeString* name, int32_t name_id) {
+ switch(name_id) {
+ case NameId::kFullFontName:
+ *name = name_part;
+ break;
+ case NameId::kFontFamilyName:
+ case NameId::kPreferredFamily:
+ case NameId::kWWSFamilyName: {
+ UnicodeString 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;
+ }
+}
+
+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;
+}
+
+SubsetterImpl::SubsetterImpl() {
+}
+
+SubsetterImpl::~SubsetterImpl() {
+}
+
+bool SubsetterImpl::LoadFont(const char* font_name,
+ const unsigned char* original_font,
+ size_t font_size) {
+ ByteArrayPtr raw_font =
+ new MemoryByteArray((byte_t*)original_font, font_size);
+ if (factory_ == NULL) {
+ factory_.attach(FontFactory::getInstance());
+ }
+
+ FontArray font_array;
+ factory_->loadFonts(raw_font, &font_array);
+ font_ = FindFont(font_name, font_array);
+ if (font_ == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids,
+ size_t glyph_count,
+ unsigned char** output_buffer) {
+ if (factory_ == NULL || font_ == NULL) {
+ return -1;
+ }
+
+ FontPtr new_font;
+ new_font.attach(Subset(glyph_ids, glyph_count));
+ if (new_font == NULL) {
+ return 0;
+ }
+
+ MemoryOutputStream output_stream;
+ factory_->serializeFont(new_font, &output_stream);
+ int length = static_cast<int>(output_stream.size());
+ if (length > 0) {
+ *output_buffer = new unsigned char[length];
+ memcpy(*output_buffer, output_stream.get(), length);
+ }
+
+ return length;
+}
+
+Font* SubsetterImpl::FindFont(const char* font_name,
+ const FontArray& font_array) {
+ if (font_array.empty() || font_array[0] == NULL) {
+ return NULL;
+ }
+
+ if (font_name && strlen(font_name)) {
+ for (FontArray::const_iterator b = font_array.begin(), e = font_array.end();
+ b != e; ++b) {
+ if (HasName(font_name, (*b).p_)) {
+ return (*b).p_;
+ }
+ }
+ }
+
+ return font_array[0].p_;
+}
+
+bool SubsetterImpl::HasName(const char* font_name, Font* font) {
+ UnicodeString font_string = UnicodeString::fromUTF8(font_name);
+ if (font_string.isEmpty())
+ return false;
+ UnicodeString regular_suffix = UnicodeString::fromUTF8(" Regular");
+ UnicodeString alt_font_string = font_string;
+ alt_font_string += regular_suffix;
+
+ typedef std::map<int32_t, UnicodeString> NameMap;
+ NameMap names;
+ NameTablePtr name_table = down_cast<NameTable*>(font->table(Tag::name));
+
+ 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: {
+ int32_t hash_code = HashCode(name_table->platformId(i),
+ name_table->encodingId(i),
+ name_table->languageId(i),
+ name_table->nameId(i));
+ UChar* name_part = name_table->name(i);
+ ConstructName(name_part, &(names[hash_code]), name_table->nameId(i));
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (!names.empty()) {
+ for (NameMap::iterator b = names.begin(), e = names.end(); b != e; ++b) {
+ if (b->second.caseCompare(font_string, 0) == 0 ||
+ b->second.caseCompare(alt_font_string, 0) == 0) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+CALLER_ATTACH Font* SubsetterImpl::Subset(const unsigned int* glyph_ids,
+ size_t glyph_count) {
+ if (glyph_ids == NULL || glyph_count == 0) {
+ return NULL;
+ }
+
+ // Find glyf and loca table.
+ GlyphTablePtr glyph_table = down_cast<GlyphTable*>(font_->table(Tag::glyf));
+ LocaTablePtr loca_table = down_cast<LocaTable*>(font_->table(Tag::loca));
+ if (glyph_table == NULL || loca_table == NULL) {
+ // The font is invalid.
+ return NULL;
+ }
+
+ // Setup font builders we need.
+ FontBuilderPtr font_builder;
+ font_builder.attach(factory_->newFontBuilder());
+
+ GlyphTableBuilderPtr glyph_table_builder;
+ glyph_table_builder.attach(down_cast<GlyphTable::Builder*>(
+ font_builder->newTableBuilder(Tag::glyf)));
+ LocaTableBuilderPtr loca_table_builder;
+ loca_table_builder.attach(down_cast<LocaTable::Builder*>(
+ font_builder->newTableBuilder(Tag::loca)));
+ if (glyph_table_builder == NULL || loca_table_builder == NULL) {
+ // Out of memory.
+ return NULL;
+ }
+
+ // Sort and uniquify glyph ids.
+ IntegerSet glyph_id_set;
+ glyph_id_set.insert(0); // Always include glyph id 0.
+ for (size_t i = 0; i < glyph_count; ++i) {
+ glyph_id_set.insert(glyph_ids[i]);
+ }
+
+ // Extract glyphs and setup loca list.
+ IntegerList loca_list;
+ loca_list.resize(loca_table->numGlyphs());
+ 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::iterator i = glyph_id_set.begin(), e = glyph_id_set.end();
+ i != e; ++i) {
+ if (*i < 0 || *i >= loca_table->numGlyphs()) {
+ // 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->glyph(offset, length));
+ if (glyph == NULL) {
+ // Error finding glyph, ignore.
+ continue;
+ }
+
+ // Add glyph to new glyf table.
+ ReadableFontDataPtr data = glyph->readFontData();
+ WritableFontDataPtr copy_data;
+ copy_data.attach(font_builder->getNewData(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;
+ }
+ last_offset += length;
+ loca_list[*i + 1] = last_offset;
+ last_glyph_id = *i;
+ }
+ for (int32_t j = last_glyph_id + 1; j <= loca_table->numGlyphs(); ++j) {
+ loca_list[j] = last_offset;
+ }
+ loca_table_builder->setLocaList(&loca_list);
+
+ // Setup remaining builders.
+ for (TableMap::iterator i = font_->tables()->begin(),
+ e = font_->tables()->end(); i != e; ++i) {
+ // We already build the builder for glyph and loca.
+ if (i->first != Tag::glyf && i->first != Tag::loca) {
+ // The newTableBuilder() call will alter internal state of font_builder
+ // AND the reference count of returned object. Therefore we need to
+ // dereference it.
+ TableBuilderPtr dereference;
+ dereference.attach(
+ font_builder->newTableBuilder(i->first, i->second->readFontData()));
+ }
+ }
+
+ return font_builder->build();
+}
+
+} // namespace sfntly