summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTorne (Richard Coles) <torne@google.com>2013-11-28 11:56:15 +0000
committerTorne (Richard Coles) <torne@google.com>2013-11-28 11:56:15 +0000
commit58f132533b4126b00bc71fd9aed69ecf568cdf39 (patch)
tree3409a319b231317c63b0b3477c161c76822bcb92
parent0232d0f400400568410e1f85e7aba28bcd2ac183 (diff)
parent4a8c3597eb72a7042ead9632e4c3e4fddb5ac695 (diff)
downloadsrc-58f132533b4126b00bc71fd9aed69ecf568cdf39.tar.gz
This commit was generated by merge_to_master.py. Change-Id: If4ff821474cc985d0bc16bc22c8180c8bec48014
-rw-r--r--sample/chromium/chrome_subsetter.cc131
-rw-r--r--sample/chromium/font_subsetter.cc39
-rw-r--r--sample/chromium/font_subsetter.h51
-rw-r--r--sample/chromium/subsetter_impl.cc785
-rw-r--r--sample/chromium/subsetter_impl.h74
-rw-r--r--sample/subsetter/main.cc44
-rw-r--r--sample/subsetter/subset_util.cc98
-rw-r--r--sample/subsetter/subset_util.h32
-rw-r--r--sample/subtly/character_predicate.cc53
-rw-r--r--sample/subtly/character_predicate.h68
-rw-r--r--sample/subtly/debug_main.cc64
-rw-r--r--sample/subtly/font_assembler.cc227
-rw-r--r--sample/subtly/font_assembler.h67
-rw-r--r--sample/subtly/font_info.cc256
-rw-r--r--sample/subtly/font_info.h128
-rw-r--r--sample/subtly/merger.cc87
-rw-r--r--sample/subtly/merger.h45
-rw-r--r--sample/subtly/merger_main.cc69
-rw-r--r--sample/subtly/stats.cc82
-rw-r--r--sample/subtly/stats.h40
-rw-r--r--sample/subtly/subsetter.cc67
-rw-r--r--sample/subtly/subsetter.h41
-rw-r--r--sample/subtly/subsetter_main.cc82
-rw-r--r--sample/subtly/utils.cc91
-rw-r--r--sample/subtly/utils.h38
-rw-r--r--sfntly/data/byte_array.cc199
-rw-r--r--sfntly/data/byte_array.h201
-rw-r--r--sfntly/data/font_data.cc82
-rw-r--r--sfntly/data/font_data.h135
-rw-r--r--sfntly/data/font_input_stream.cc141
-rw-r--r--sfntly/data/font_input_stream.h97
-rw-r--r--sfntly/data/font_output_stream.cc130
-rw-r--r--sfntly/data/font_output_stream.h79
-rw-r--r--sfntly/data/growable_memory_byte_array.cc82
-rw-r--r--sfntly/data/growable_memory_byte_array.h66
-rw-r--r--sfntly/data/memory_byte_array.cc93
-rw-r--r--sfntly/data/memory_byte_array.h81
-rw-r--r--sfntly/data/readable_font_data.cc336
-rw-r--r--sfntly/data/readable_font_data.h308
-rw-r--r--sfntly/data/writable_font_data.cc203
-rw-r--r--sfntly/data/writable_font_data.h211
-rw-r--r--sfntly/font.cc557
-rw-r--r--sfntly/font.h352
-rw-r--r--sfntly/font_factory.cc214
-rw-r--r--sfntly/font_factory.h140
-rw-r--r--sfntly/math/fixed1616.h41
-rw-r--r--sfntly/math/font_math.h49
-rw-r--r--sfntly/port/atomic.h71
-rw-r--r--sfntly/port/config.h28
-rw-r--r--sfntly/port/endian.h77
-rw-r--r--sfntly/port/exception_type.h125
-rw-r--r--sfntly/port/file_input_stream.cc171
-rw-r--r--sfntly/port/file_input_stream.h57
-rw-r--r--sfntly/port/input_stream.h49
-rw-r--r--sfntly/port/java_iterator.h94
-rw-r--r--sfntly/port/lock.cc72
-rw-r--r--sfntly/port/lock.h76
-rwxr-xr-xsfntly/port/memory_input_stream.cc149
-rwxr-xr-xsfntly/port/memory_input_stream.h57
-rw-r--r--sfntly/port/memory_output_stream.cc72
-rw-r--r--sfntly/port/memory_output_stream.h51
-rw-r--r--sfntly/port/output_stream.h46
-rw-r--r--sfntly/port/refcount.h277
-rw-r--r--sfntly/port/type.h102
-rw-r--r--sfntly/table/bitmap/big_glyph_metrics.cc171
-rw-r--r--sfntly/table/bitmap/big_glyph_metrics.h96
-rw-r--r--sfntly/table/bitmap/bitmap_glyph.cc101
-rw-r--r--sfntly/table/bitmap/bitmap_glyph.h119
-rw-r--r--sfntly/table/bitmap/bitmap_glyph_info.cc68
-rw-r--r--sfntly/table/bitmap/bitmap_glyph_info.h85
-rw-r--r--sfntly/table/bitmap/bitmap_size_table.cc604
-rw-r--r--sfntly/table/bitmap/bitmap_size_table.h173
-rw-r--r--sfntly/table/bitmap/composite_bitmap_glyph.cc109
-rw-r--r--sfntly/table/bitmap/composite_bitmap_glyph.h75
-rw-r--r--sfntly/table/bitmap/ebdt_table.cc236
-rw-r--r--sfntly/table/bitmap/ebdt_table.h108
-rw-r--r--sfntly/table/bitmap/eblc_table.cc313
-rw-r--r--sfntly/table/bitmap/eblc_table.h194
-rw-r--r--sfntly/table/bitmap/ebsc_table.cc107
-rw-r--r--sfntly/table/bitmap/ebsc_table.h101
-rw-r--r--sfntly/table/bitmap/glyph_metrics.cc39
-rw-r--r--sfntly/table/bitmap/glyph_metrics.h43
-rw-r--r--sfntly/table/bitmap/index_sub_table.cc278
-rw-r--r--sfntly/table/bitmap/index_sub_table.h178
-rw-r--r--sfntly/table/bitmap/index_sub_table_format1.cc302
-rw-r--r--sfntly/table/bitmap/index_sub_table_format1.h116
-rw-r--r--sfntly/table/bitmap/index_sub_table_format2.cc275
-rw-r--r--sfntly/table/bitmap/index_sub_table_format2.h106
-rw-r--r--sfntly/table/bitmap/index_sub_table_format3.cc298
-rw-r--r--sfntly/table/bitmap/index_sub_table_format3.h113
-rw-r--r--sfntly/table/bitmap/index_sub_table_format4.cc384
-rw-r--r--sfntly/table/bitmap/index_sub_table_format4.h135
-rw-r--r--sfntly/table/bitmap/index_sub_table_format5.cc347
-rw-r--r--sfntly/table/bitmap/index_sub_table_format5.h118
-rw-r--r--sfntly/table/bitmap/simple_bitmap_glyph.cc45
-rw-r--r--sfntly/table/bitmap/simple_bitmap_glyph.h44
-rw-r--r--sfntly/table/bitmap/small_glyph_metrics.cc126
-rw-r--r--sfntly/table/bitmap/small_glyph_metrics.h79
-rw-r--r--sfntly/table/byte_array_table_builder.cc70
-rw-r--r--sfntly/table/byte_array_table_builder.h53
-rw-r--r--sfntly/table/core/cmap_table.cc1287
-rw-r--r--sfntly/table/core/cmap_table.h709
-rw-r--r--sfntly/table/core/font_header_table.cc265
-rw-r--r--sfntly/table/core/font_header_table.h168
-rw-r--r--sfntly/table/core/horizontal_device_metrics_table.cc124
-rw-r--r--sfntly/table/core/horizontal_device_metrics_table.h82
-rw-r--r--sfntly/table/core/horizontal_header_table.cc213
-rw-r--r--sfntly/table/core/horizontal_header_table.h111
-rw-r--r--sfntly/table/core/horizontal_metrics_table.cc138
-rw-r--r--sfntly/table/core/horizontal_metrics_table.h87
-rw-r--r--sfntly/table/core/maximum_profile_table.cc240
-rw-r--r--sfntly/table/core/maximum_profile_table.h120
-rw-r--r--sfntly/table/core/name_table.cc723
-rw-r--r--sfntly/table/core/name_table.h743
-rw-r--r--sfntly/table/core/os2_table.cc610
-rw-r--r--sfntly/table/core/os2_table.h508
-rw-r--r--sfntly/table/font_data_table.cc193
-rw-r--r--sfntly/table/font_data_table.h123
-rw-r--r--sfntly/table/generic_table_builder.cc49
-rw-r--r--sfntly/table/generic_table_builder.h42
-rw-r--r--sfntly/table/header.cc66
-rw-r--r--sfntly/table/header.h114
-rw-r--r--sfntly/table/subtable.cc64
-rw-r--r--sfntly/table/subtable.h73
-rw-r--r--sfntly/table/subtable_container_table.h48
-rw-r--r--sfntly/table/table.cc162
-rw-r--r--sfntly/table/table.h119
-rw-r--r--sfntly/table/table_based_table_builder.cc69
-rw-r--r--sfntly/table/table_based_table_builder.h48
-rw-r--r--sfntly/table/truetype/glyph_table.cc679
-rw-r--r--sfntly/table/truetype/glyph_table.h334
-rw-r--r--sfntly/table/truetype/loca_table.cc246
-rw-r--r--sfntly/table/truetype/loca_table.h183
-rw-r--r--sfntly/tag.cc110
-rw-r--r--sfntly/tag.h123
-rw-r--r--sfntly/tools/subsetter/glyph_table_subsetter.cc90
-rw-r--r--sfntly/tools/subsetter/glyph_table_subsetter.h37
-rw-r--r--sfntly/tools/subsetter/subsetter.cc102
-rw-r--r--sfntly/tools/subsetter/subsetter.h72
-rw-r--r--sfntly/tools/subsetter/table_subsetter.h39
-rw-r--r--sfntly/tools/subsetter/table_subsetter_impl.cc38
-rw-r--r--sfntly/tools/subsetter/table_subsetter_impl.h37
-rw-r--r--test/autogenerated/cmap_basic_test.cc131
-rw-r--r--test/autogenerated/cmap_test_data.h185
-rw-r--r--test/bitmap_table_test.cc209
-rw-r--r--test/byte_array_test.cc146
-rw-r--r--test/chrome_subsetter.cc75
-rw-r--r--test/cmap_editing_test.cc101
-rw-r--r--test/cmap_iterator_test.cc156
-rw-r--r--test/cmap_test.cc213
-rw-r--r--test/endian_test.cc77
-rw-r--r--test/file_io_test.cc153
-rw-r--r--test/font_data_test.cc337
-rw-r--r--test/font_parsing_test.cc140
-rw-r--r--test/hdmx_test.cc82
-rw-r--r--test/lock_test.cc244
-rwxr-xr-xtest/memory_io_test.cc102
-rw-r--r--test/name_editing_test.cc242
-rw-r--r--test/open_type_data_test.cc70
-rw-r--r--test/otf_basic_editing_test.cc90
-rw-r--r--test/platform_thread.cc101
-rw-r--r--test/platform_thread.h75
-rwxr-xr-xtest/serialization_test.cc151
-rw-r--r--test/serialization_test.h34
-rw-r--r--test/smart_pointer_test.cc79
-rw-r--r--test/test_data.cc75
-rw-r--r--test/test_data.h44
-rw-r--r--test/test_font_utils.cc115
-rw-r--r--test/test_font_utils.h41
-rw-r--r--test/test_utils.cc89
-rw-r--r--test/test_utils.h110
-rw-r--r--test/test_utils_test.cc84
-rw-r--r--test/test_xml_utils.cc50
-rw-r--r--test/test_xml_utils.h33
-rw-r--r--test/tinyxml/tinystr.cpp111
-rw-r--r--test/tinyxml/tinystr.h305
-rw-r--r--test/tinyxml/tinyxml.cpp1886
-rw-r--r--test/tinyxml/tinyxml.h1805
-rw-r--r--test/tinyxml/tinyxmlerror.cpp52
-rw-r--r--test/tinyxml/tinyxmlparser.cpp1638
-rw-r--r--test/verify_glyf.cc59
-rw-r--r--test/verify_hhea.cc63
-rw-r--r--test/verify_hmtx.cc76
-rw-r--r--test/verify_loca.cc62
-rw-r--r--test/verify_maxp.cc72
-rw-r--r--test/verify_name.cc68
-rw-r--r--test/verify_os2.cc125
187 files changed, 33483 insertions, 0 deletions
diff --git a/sample/chromium/chrome_subsetter.cc b/sample/chromium/chrome_subsetter.cc
new file mode 100644
index 0000000..df15c18
--- /dev/null
+++ b/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/sample/chromium/font_subsetter.cc b/sample/chromium/font_subsetter.cc
new file mode 100644
index 0000000..14f5494
--- /dev/null
+++ b/sample/chromium/font_subsetter.cc
@@ -0,0 +1,39 @@
+/*
+ * 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);
+}
diff --git a/sample/chromium/font_subsetter.h b/sample/chromium/font_subsetter.h
new file mode 100644
index 0000000..07b1b5b
--- /dev/null
+++ b/sample/chromium/font_subsetter.h
@@ -0,0 +1,51 @@
+/*
+ * 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 <cstddef>
+
+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);
+};
+
+#endif // SFNTLY_CPP_SRC_TEST_FONT_SUBSETTER_H_
diff --git a/sample/chromium/subsetter_impl.cc b/sample/chromium/subsetter_impl.cc
new file mode 100644
index 0000000..528e336
--- /dev/null
+++ b/sample/chromium/subsetter_impl.cc
@@ -0,0 +1,785 @@
+/*
+ * 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 <map>
+#include <set>
+
+#include "sfntly/table/bitmap/eblc_table.h"
+#include "sfntly/table/bitmap/ebdt_table.h"
+#include "sfntly/table/bitmap/index_sub_table.h"
+#include "sfntly/table/bitmap/index_sub_table_format1.h"
+#include "sfntly/table/bitmap/index_sub_table_format2.h"
+#include "sfntly/table/bitmap/index_sub_table_format3.h"
+#include "sfntly/table/bitmap/index_sub_table_format4.h"
+#include "sfntly/table/bitmap/index_sub_table_format5.h"
+#include "sfntly/table/core/name_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+
+#if defined U_USING_ICU_NAMESPACE
+ U_NAMESPACE_USE
+#endif
+
+namespace {
+
+using namespace sfntly;
+
+// The bitmap tables must be greater than 16KB to trigger bitmap subsetter.
+static const int BITMAP_SIZE_THRESHOLD = 16384;
+
+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;
+}
+
+bool 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->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 (i->second.caseCompare(font_string, 0) == 0 ||
+ i->second.caseCompare(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;
+ }
+ 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();
+ } else if (range_end > range_begin) {
+ IntegerSet::const_iterator left = glyph_ids.lower_bound(range_begin);
+ IntegerSet::const_iterator right = glyph_ids.lower_bound(range_end);
+ return right != left;
+ }
+ return false;
+}
+
+// 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<byte_t>(source->Height()));
+ target->SetWidth(static_cast<byte_t>(source->Width()));
+ target->SetHoriBearingX(static_cast<byte_t>(source->HoriBearingX()));
+ target->SetHoriBearingY(static_cast<byte_t>(source->HoriBearingY()));
+ target->SetHoriAdvance(static_cast<byte_t>(source->HoriAdvance()));
+ target->SetVertBearingX(static_cast<byte_t>(source->VertBearingX()));
+ target->SetVertBearingY(static_cast<byte_t>(source->VertBearingY()));
+ target->SetVertAdvance(static_cast<byte_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(const char* font_name,
+ const unsigned char* original_font,
+ size_t font_size) {
+ MemoryInputStream mis;
+ mis.Attach(original_font, font_size);
+ if (factory_ == NULL) {
+ factory_.Attach(FontFactory::GetInstance());
+ }
+
+ FontArray font_array;
+ factory_->LoadFonts(&mis, &font_array);
+ font_ = FindFont(font_name, font_array);
+ if (font_ == NULL) {
+ return false;
+ }
+
+ return true;
+}
+
+int SubsetterImpl::SubsetFont(const unsigned int* glyph_ids,
+ size_t glyph_count,
+ unsigned char** output_buffer) {
+ if (factory_ == NULL || font_ == NULL) {
+ return -1;
+ }
+
+ // Find glyf and loca table.
+ GlyphTablePtr glyph_table =
+ down_cast<GlyphTable*>(font_->GetTable(Tag::glyf));
+ LocaTablePtr loca_table = down_cast<LocaTable*>(font_->GetTable(Tag::loca));
+ if (glyph_table == NULL || loca_table == NULL) {
+ // We are not able to subset the font.
+ return 0;
+ }
+
+ IntegerSet glyph_id_processed;
+ if (!ResolveCompositeGlyphs(glyph_table, loca_table,
+ glyph_ids, glyph_count, &glyph_id_processed) ||
+ glyph_id_processed.empty()) {
+ return 0;
+ }
+
+ FontPtr new_font;
+ new_font.Attach(Subset(glyph_id_processed, glyph_table, loca_table));
+ if (new_font == NULL) {
+ return 0;
+ }
+
+ MemoryOutputStream output_stream;
+ factory_->SerializeFont(new_font, &output_stream);
+ int length = static_cast<int>(output_stream.Size());
+ if (length > 0) {
+ *output_buffer = new unsigned char[length];
+ memcpy(*output_buffer, output_stream.Get(), length);
+ }
+
+ return length;
+}
+
+// Long comments regarding TTF tables and PDF (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.
+ };
+
+ // 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 < sizeof(TABLES_IN_SUBSET) / sizeof(int32_t); ++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::iterator i = allowed_tags.begin(), e = allowed_tags.end();
+ i != e; ++i) {
+ Table* table = font_->GetTable(*i);
+ if (table) {
+ font_builder->NewTableBuilder(*i, table->ReadFontData());
+ }
+ }
+
+ return font_builder->Build();
+}
+
+} // namespace sfntly
diff --git a/sample/chromium/subsetter_impl.h b/sample/chromium/subsetter_impl.h
new file mode 100644
index 0000000..ffbf408
--- /dev/null
+++ b/sample/chromium/subsetter_impl.h
@@ -0,0 +1,74 @@
+/*
+ * 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);
+ 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/sample/subsetter/main.cc b/sample/subsetter/main.cc
new file mode 100644
index 0000000..19a3e0e
--- /dev/null
+++ b/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/sample/subsetter/subset_util.cc b/sample/subsetter/subset_util.cc
new file mode 100644
index 0000000..f35eb25
--- /dev/null
+++ b/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/sample/subsetter/subset_util.h b/sample/subsetter/subset_util.h
new file mode 100644
index 0000000..5eb4fe4
--- /dev/null
+++ b/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/sample/subtly/character_predicate.cc b/sample/subtly/character_predicate.cc
new file mode 100644
index 0000000..b9c6cc7
--- /dev/null
+++ b/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/sample/subtly/character_predicate.h b/sample/subtly/character_predicate.h
new file mode 100644
index 0000000..a6e3ea3
--- /dev/null
+++ b/sample/subtly/character_predicate.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_CHARACTER_PREDICATE_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+
+namespace subtly {
+class CharacterPredicate : virtual public sfntly::RefCount {
+ public:
+ CharacterPredicate() {}
+ virtual ~CharacterPredicate() {}
+ virtual bool operator()(int32_t character) const = 0;
+};
+
+// All characters except for those between [start, end] are rejected
+class AcceptRange : public CharacterPredicate,
+ public sfntly::RefCounted<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/sample/subtly/debug_main.cc b/sample/subtly/debug_main.cc
new file mode 100644
index 0000000..8324cde
--- /dev/null
+++ b/sample/subtly/debug_main.cc
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/sample/subtly/font_assembler.cc b/sample/subtly/font_assembler.cc
new file mode 100644
index 0000000..2f7cd11
--- /dev/null
+++ b/sample/subtly/font_assembler.cc
@@ -0,0 +1,227 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/font_assembler.h"
+
+#include <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.
+ for (int32_t i = last_glyph_id + 1; i <= resolved_glyph_id; ++i)
+ loca_list[i] = last_offset;
+ last_offset += length;
+ loca_list[resolved_glyph_id + 1] = last_offset;
+ last_glyph_id = resolved_glyph_id + 1;
+ }
+ // If there are missing glyph ids, their loca entries must all point
+ // to the same offset as the last valid glyph id making them all zero length.
+ for (int32_t i = last_glyph_id + 1; i <= num_loca_glyphs; ++i)
+ loca_list[i] = last_offset;
+ loca_table_builder->SetLocaList(&loca_list);
+ return true;
+}
+}
diff --git a/sample/subtly/font_assembler.h b/sample/subtly/font_assembler.h
new file mode 100644
index 0000000..c53c21f
--- /dev/null
+++ b/sample/subtly/font_assembler.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_ASSEMBLER_H_
+
+#include <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/sample/subtly/font_info.cc b/sample/subtly/font_info.cc
new file mode 100644
index 0000000..6eb6a38
--- /dev/null
+++ b/sample/subtly/font_info.cc
@@ -0,0 +1,256 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/font_info.h"
+
+#include <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/sample/subtly/font_info.h b/sample/subtly/font_info.h
new file mode 100644
index 0000000..6f42d73
--- /dev/null
+++ b/sample/subtly/font_info.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_FONT_INFO_H_
+
+#include <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/sample/subtly/merger.cc b/sample/subtly/merger.cc
new file mode 100644
index 0000000..7875c2d
--- /dev/null
+++ b/sample/subtly/merger.cc
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/merger.h"
+
+#include <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/sample/subtly/merger.h b/sample/subtly/merger.h
new file mode 100644
index 0000000..43764a8
--- /dev/null
+++ b/sample/subtly/merger.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_MERGER_H_
+
+#include "subtly/character_predicate.h"
+#include "subtly/font_info.h"
+
+namespace sfntly {
+class Font;
+}
+
+namespace subtly {
+// Merges the subsets in the font array into a single font.
+class Merger : public sfntly::RefCounted<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/sample/subtly/merger_main.cc b/sample/subtly/merger_main.cc
new file mode 100644
index 0000000..a977aa7
--- /dev/null
+++ b/sample/subtly/merger_main.cc
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/sample/subtly/stats.cc b/sample/subtly/stats.cc
new file mode 100644
index 0000000..769f691
--- /dev/null
+++ b/sample/subtly/stats.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/sample/subtly/stats.h b/sample/subtly/stats.h
new file mode 100644
index 0000000..89ef2ae
--- /dev/null
+++ b/sample/subtly/stats.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_STATS_H_
+
+#include <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/sample/subtly/subsetter.cc b/sample/subtly/subsetter.cc
new file mode 100644
index 0000000..d09627c
--- /dev/null
+++ b/sample/subtly/subsetter.cc
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/subsetter.h"
+
+#include <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/sample/subtly/subsetter.h b/sample/subtly/subsetter.h
new file mode 100644
index 0000000..a93747f
--- /dev/null
+++ b/sample/subtly/subsetter.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_SUBSETTER_H_
+
+#include "sfntly/font.h"
+// Cannot remove this header due to Ptr<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/sample/subtly/subsetter_main.cc b/sample/subtly/subsetter_main.cc
new file mode 100644
index 0000000..d438148
--- /dev/null
+++ b/sample/subtly/subsetter_main.cc
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <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/sample/subtly/utils.cc b/sample/subtly/utils.cc
new file mode 100644
index 0000000..3c204d3
--- /dev/null
+++ b/sample/subtly/utils.cc
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "subtly/utils.h"
+
+#include "sfntly/data/growable_memory_byte_array.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+
+namespace subtly {
+using namespace sfntly;
+
+CALLER_ATTACH Font* LoadFont(const char* font_path) {
+ Ptr<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 == reinterpret_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/sample/subtly/utils.h b/sample/subtly/utils.h
new file mode 100644
index 0000000..ba9f0d4
--- /dev/null
+++ b/sample/subtly/utils.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2011 Google Inc. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+
+namespace subtly {
+CALLER_ATTACH sfntly::Font* LoadFont(const char* font_path);
+CALLER_ATTACH sfntly::Font::Builder* LoadFontBuilder(const char* font_path);
+
+void LoadFonts(const char* font_path, sfntly::FontFactory* factory,
+ sfntly::FontArray* fonts);
+void LoadFontBuilders(const char* font_path,
+ sfntly::FontFactory* factory,
+ sfntly::FontBuilderArray* builders);
+
+bool SerializeFont(const char* font_path, sfntly::Font* font);
+bool SerializeFont(const char* font_path, sfntly::FontFactory* factory,
+ sfntly::Font* font);
+}
+
+#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SAMPLE_SUBTLY_UTILS_H_
diff --git a/sfntly/data/byte_array.cc b/sfntly/data/byte_array.cc
new file mode 100644
index 0000000..915a40c
--- /dev/null
+++ b/sfntly/data/byte_array.cc
@@ -0,0 +1,199 @@
+/*
+ * 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/data/byte_array.h"
+
+#include <algorithm>
+
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+const int32_t ByteArray::COPY_BUFFER_SIZE = 8192;
+
+ByteArray::~ByteArray() {}
+
+int32_t ByteArray::Length() { return filled_length_; }
+int32_t ByteArray::Size() { return storage_length_; }
+
+int32_t ByteArray::SetFilledLength(int32_t filled_length) {
+ filled_length_ = std::min<int32_t>(filled_length, storage_length_);
+ return filled_length_;
+}
+
+int32_t ByteArray::Get(int32_t index) {
+ return InternalGet(index) & 0xff;
+}
+
+int32_t ByteArray::Get(int32_t index, ByteVector* b) {
+ assert(b);
+ return Get(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t ByteArray::Get(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ assert(b);
+ if (index < 0 || index >= filled_length_) {
+ return 0;
+ }
+ int32_t actual_length = std::min<int32_t>(length, filled_length_ - index);
+ return InternalGet(index, b, offset, actual_length);
+}
+
+void ByteArray::Put(int32_t index, byte_t b) {
+ if (index < 0 || index >= Size()) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return;
+#else
+ throw IndexOutOfBoundException(
+ "Attempt to write outside the bounds of the data");
+#endif
+ }
+ InternalPut(index, b);
+ filled_length_ = std::max<int32_t>(filled_length_, index + 1);
+}
+
+int32_t ByteArray::Put(int index, ByteVector* b) {
+ assert(b);
+ return Put(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t ByteArray::Put(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ assert(b);
+ if (index < 0 || index >= Size()) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return 0;
+#else
+ throw IndexOutOfBoundException(
+ "Attempt to write outside the bounds of the data");
+#endif
+ }
+ int32_t actual_length = std::min<int32_t>(length, Size() - index);
+ int32_t bytes_written = InternalPut(index, b, offset, actual_length);
+ filled_length_ = std::max<int32_t>(filled_length_, index + bytes_written);
+ return bytes_written;
+}
+
+int32_t ByteArray::CopyTo(ByteArray* array) {
+ return CopyTo(array, 0, Length());
+}
+
+int32_t ByteArray::CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+ return CopyTo(0, array, offset, length);
+}
+
+int32_t ByteArray::CopyTo(int32_t dst_offset, ByteArray* array,
+ int32_t src_offset, int32_t length) {
+ assert(array);
+ if (array->Size() < dst_offset + length) { // insufficient space
+ return -1;
+ }
+
+ ByteVector b(COPY_BUFFER_SIZE);
+ int32_t bytes_read = 0;
+ int32_t index = 0;
+ int32_t remaining_length = length;
+ int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+ while ((bytes_read =
+ Get(index + src_offset, &(b[0]), 0, buffer_length)) > 0) {
+ int bytes_written = array->Put(index + dst_offset, &(b[0]), 0, bytes_read);
+ UNREFERENCED_PARAMETER(bytes_written);
+ index += bytes_read;
+ remaining_length -= bytes_read;
+ buffer_length = std::min<int32_t>(b.size(), remaining_length);
+ }
+ return index;
+}
+
+int32_t ByteArray::CopyTo(OutputStream* os) {
+ return CopyTo(os, 0, Length());
+}
+
+int32_t ByteArray::CopyTo(OutputStream* os, int32_t offset, int32_t length) {
+ ByteVector b(COPY_BUFFER_SIZE);
+ int32_t bytes_read = 0;
+ int32_t index = 0;
+ int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+ while ((bytes_read = Get(index + offset, &(b[0]), 0, buffer_length)) > 0) {
+ os->Write(&b, 0, bytes_read);
+ index += bytes_read;
+ buffer_length = std::min<int32_t>(b.size(), length - index);
+ }
+ return index;
+}
+
+bool ByteArray::CopyFrom(InputStream* is, int32_t length) {
+ ByteVector b(COPY_BUFFER_SIZE);
+ int32_t bytes_read = 0;
+ int32_t index = 0;
+ int32_t buffer_length = std::min<int32_t>(COPY_BUFFER_SIZE, length);
+ while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
+ if (Put(index, &(b[0]), 0, bytes_read) != bytes_read) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return 0;
+#else
+ throw IOException("Error writing bytes.");
+#endif
+ }
+ index += bytes_read;
+ length -= bytes_read;
+ buffer_length = std::min<int32_t>(b.size(), length);
+ }
+ return true;
+}
+
+bool ByteArray::CopyFrom(InputStream* is) {
+ ByteVector b(COPY_BUFFER_SIZE);
+ int32_t bytes_read = 0;
+ int32_t index = 0;
+ int32_t buffer_length = COPY_BUFFER_SIZE;
+ while ((bytes_read = is->Read(&b, 0, buffer_length)) > 0) {
+ if (Put(index, &b[0], 0, bytes_read) != bytes_read) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return 0;
+#else
+ throw IOException("Error writing bytes.");
+#endif
+ }
+ index += bytes_read;
+ }
+ return true;
+}
+
+ByteArray::ByteArray(int32_t filled_length,
+ int32_t storage_length,
+ bool growable) {
+ Init(filled_length, storage_length, growable);
+}
+
+ByteArray::ByteArray(int32_t filled_length, int32_t storage_length) {
+ Init(filled_length, storage_length, false);
+}
+
+void ByteArray::Init(int32_t filled_length,
+ int32_t storage_length,
+ bool growable) {
+ storage_length_ = storage_length;
+ growable_ = growable;
+ SetFilledLength(filled_length);
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/byte_array.h b/sfntly/data/byte_array.h
new file mode 100644
index 0000000..70dc92f
--- /dev/null
+++ b/sfntly/data/byte_array.h
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2011 The sfntly Open Source Project
+ *
+ * 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_SFNTLY_DATA_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/input_stream.h"
+#include "sfntly/port/output_stream.h"
+
+namespace sfntly {
+
+// An abstraction to a contiguous array of bytes.
+// C++ port of this class assumes that the data are stored in a linear region
+// like std::vector.
+class ByteArray : virtual public RefCount {
+ public:
+ virtual ~ByteArray();
+
+ // Gets the current filled and readable length of the array.
+ int32_t Length();
+
+ // Gets the maximum size of the array. This is the maximum number of bytes that
+ // the array can hold and all of it may not be filled with data or even fully
+ // allocated yet.
+ int32_t Size();
+
+ // Determines whether or not this array is growable or of fixed size.
+ bool growable() { return growable_; }
+
+ int32_t SetFilledLength(int32_t filled_length);
+
+ // Gets the byte from the given index.
+ // @param index the index into the byte array
+ // @return the byte or -1 if reading beyond the bounds of the data
+ virtual int32_t Get(int32_t index);
+
+ // Gets the bytes from the given index and fill the buffer with them. As many
+ // bytes as will fit into the buffer are read unless that would go past the
+ // end of the array.
+ // @param index the index into the byte array
+ // @param b the buffer to put the bytes read into
+ // @return the number of bytes read from the buffer
+ virtual int32_t Get(int32_t index, ByteVector* b);
+
+ // Gets the bytes from the given index and fill the buffer with them starting
+ // at the offset given. As many bytes as the specified length are read unless
+ // that would go past the end of the array.
+ // @param index the index into the byte array
+ // @param b the buffer to put the bytes read into
+ // @param offset the location in the buffer to start putting the bytes
+ // @param length the number of bytes to put into the buffer
+ // @return the number of bytes read from the buffer
+ virtual int32_t Get(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+
+ // Puts the specified byte into the array at the given index unless that would
+ // be beyond the length of the array and it isn't growable.
+ virtual void Put(int32_t index, byte_t b);
+
+ // Puts the specified bytes into the array at the given index. The entire
+ // buffer is put into the array unless that would extend beyond the length and
+ // the array isn't growable.
+ virtual int32_t Put(int32_t index, ByteVector* b);
+
+ // Puts the specified bytes into the array at the given index. All of the bytes
+ // specified are put into the array unless that would extend beyond the length
+ // and the array isn't growable. The bytes to be put into the array are those
+ // in the buffer from the given offset and for the given length.
+ // @param index the index into the ByteArray
+ // @param b the bytes to put into the array
+ // @param offset the offset in the bytes to start copying from
+ // @param length the number of bytes to copy into the array
+ // @return the number of bytes actually written
+ virtual int32_t Put(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+
+ // Fully copies this ByteArray to another ByteArray to the extent that the
+ // destination array has storage for the data copied.
+ virtual int32_t CopyTo(ByteArray* array);
+
+ // Copies a segment of this ByteArray to another ByteArray.
+ // @param array the destination
+ // @param offset the offset in this ByteArray to start copying from
+ // @param length the maximum length in bytes to copy
+ // @return the number of bytes copied
+ virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length);
+
+ // Copies this ByteArray to another ByteArray.
+ // @param dstOffset the offset in the destination array to start copying to
+ // @param array the destination
+ // @param srcOffset the offset in this ByteArray to start copying from
+ // @param length the maximum length in bytes to copy
+ // @return the number of bytes copied
+ virtual int32_t CopyTo(int32_t dst_offset,
+ ByteArray* array,
+ int32_t src_offset,
+ int32_t length);
+
+ // Copies this ByteArray to an OutputStream.
+ // @param os the destination
+ // @return the number of bytes copied
+ virtual int32_t CopyTo(OutputStream* os);
+
+ // Copies this ByteArray to an OutputStream.
+ // @param os the destination
+ // @param offset
+ // @param length
+ // @return the number of bytes copied
+ virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+ // Copies from the InputStream into this ByteArray.
+ // @param is the source
+ // @param length the number of bytes to copy
+ virtual bool CopyFrom(InputStream* is, int32_t length);
+
+ // Copies everything from the InputStream into this ByteArray.
+ // @param is the source
+ virtual bool CopyFrom(InputStream* is);
+
+ protected:
+ // filledLength the length that is "filled" and readable counting from offset.
+ // storageLength the maximum storage size of the underlying data.
+ // growable is the storage growable - storageLength is the max growable size.
+ ByteArray(int32_t filled_length, int32_t storage_length, bool growable);
+ ByteArray(int32_t filled_length, int32_t storage_length);
+ void Init(int32_t filled_length, int32_t storage_length, bool growable);
+
+ // Internal subclass API
+
+ // Stores the byte at the index given.
+ // @param index the location to store at
+ // @param b the byte to store
+ virtual void InternalPut(int32_t index, byte_t b) = 0;
+
+ // Stores the array of bytes at the given index.
+ // @param index the location to store at
+ // @param b the bytes to store
+ // @param offset the offset to start from in the byte array
+ // @param length the length of the byte array to store from the offset
+ // @return the number of bytes actually stored
+ virtual int32_t InternalPut(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) = 0;
+
+ // Gets the byte at the index given.
+ // @param index the location to get from
+ // @return the byte stored at the index
+ virtual byte_t InternalGet(int32_t index) = 0;
+
+ // Gets the bytes at the index given of the given length.
+ // @param index the location to start getting from
+ // @param b the array to put the bytes into
+ // @param offset the offset in the array to put the bytes into
+ // @param length the length of bytes to read
+ // @return the number of bytes actually ready
+ virtual int32_t InternalGet(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) = 0;
+
+ // Close this instance of the ByteArray.
+ virtual void Close() = 0;
+
+ // C++ port only, raw pointer to the first element of storage.
+ virtual byte_t* Begin() = 0;
+
+ // Java toString() not ported.
+
+ static const int32_t COPY_BUFFER_SIZE;
+
+ private:
+ //bool bound_; // unused, comment out
+ int32_t filled_length_;
+ int32_t storage_length_;
+ bool growable_;
+};
+typedef Ptr<ByteArray> ByteArrayPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_BYTE_ARRAY_H_
diff --git a/sfntly/data/font_data.cc b/sfntly/data/font_data.cc
new file mode 100644
index 0000000..d2b95ea
--- /dev/null
+++ b/sfntly/data/font_data.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 <limits.h>
+#include <algorithm>
+#include <functional>
+
+#include "sfntly/data/font_data.h"
+
+namespace sfntly {
+
+int32_t FontData::Size() const {
+ return std::min<int32_t>(array_->Size() - bound_offset_, bound_length_);
+}
+
+bool FontData::Bound(int32_t offset, int32_t length) {
+ if (offset + length > Size() || offset < 0 || length < 0)
+ return false;
+
+ bound_offset_ += offset;
+ bound_length_ = length;
+ return true;
+}
+
+bool FontData::Bound(int32_t offset) {
+if (offset > Size() || offset < 0)
+ return false;
+
+ bound_offset_ += offset;
+ return true;
+}
+
+int32_t FontData::Length() const {
+ return std::min<int32_t>(array_->Length() - bound_offset_, bound_length_);
+}
+
+FontData::FontData(ByteArray* ba) {
+ Init(ba);
+}
+
+FontData::FontData(FontData* data, int32_t offset, int32_t length) {
+ Init(data->array_);
+ Bound(data->bound_offset_ + offset, length);
+}
+
+FontData::FontData(FontData* data, int32_t offset) {
+ Init(data->array_);
+ Bound(data->bound_offset_ + offset,
+ (data->bound_length_ == GROWABLE_SIZE)
+ ? GROWABLE_SIZE : data->bound_length_ - offset);
+}
+
+FontData::~FontData() {}
+
+void FontData::Init(ByteArray* ba) {
+ array_ = ba;
+ bound_offset_ = 0;
+ bound_length_ = GROWABLE_SIZE;
+}
+
+int32_t FontData::BoundOffset(int32_t offset) {
+ return offset + bound_offset_;
+}
+
+int32_t FontData::BoundLength(int32_t offset, int32_t length) {
+ return std::min<int32_t>(length, bound_length_ - offset);
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/font_data.h b/sfntly/data/font_data.h
new file mode 100644
index 0000000..d02e8b7
--- /dev/null
+++ b/sfntly/data/font_data.h
@@ -0,0 +1,135 @@
+/*
+ * 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_SFNTLY_DATA_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
+
+#include <limits.h>
+
+#include <vector>
+
+#include "sfntly/port/type.h"
+#include "sfntly/data/byte_array.h"
+#include "sfntly/port/refcount.h"
+
+namespace sfntly {
+
+struct DataSize {
+ enum {
+ kBYTE = 1,
+ kCHAR = 1,
+ kUSHORT = 2,
+ kSHORT = 2,
+ kUINT24 = 3,
+ kULONG = 4,
+ kLONG = 4,
+ kFixed = 4,
+ kFUNIT = 4,
+ kFWORD = 2,
+ kUFWORD = 2,
+ kF2DOT14 = 2,
+ kLONGDATETIME = 8,
+ kTag = 4,
+ kGlyphID = 2,
+ kOffset = 2
+ };
+};
+
+class FontData : virtual public RefCount {
+ public:
+ // Gets the maximum size of the FontData. This is the maximum number of bytes
+ // that the font data can hold and all of it may not be filled with data or
+ // even fully allocated yet.
+ // @return the maximum size of this font data
+ virtual int32_t Size() const;
+
+ // Sets limits on the size of the FontData. The FontData is then only
+ // visible within the bounds set.
+ // @param offset the start of the new bounds
+ // @param length the number of bytes in the bounded array
+ // @return true if the bounding range was successful; false otherwise
+ virtual bool Bound(int32_t offset, int32_t length);
+
+ // Sets limits on the size of the FontData. This is a offset bound only so if
+ // the FontData is writable and growable then there is no limit to that growth
+ // from the bounding operation.
+ // @param offset the start of the new bounds which must be within the current
+ // size of the FontData
+ // @return true if the bounding range was successful; false otherwise
+ virtual bool Bound(int32_t offset);
+
+ // Makes a slice of this FontData. The returned slice will share the data with
+ // the original <code>FontData</code>.
+ // @param offset the start of the slice
+ // @param length the number of bytes in the slice
+ // @return a slice of the original FontData
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length) = 0;
+
+ // Makes a bottom bound only slice of this array. The returned slice will
+ // share the data with the original <code>FontData</code>.
+ // @param offset the start of the slice
+ // @return a slice of the original FontData
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset) = 0;
+
+ // Gets the length of the data.
+ virtual int32_t Length() const;
+
+ protected:
+ // Constructor.
+ // @param ba the byte array to use for the backing data
+ explicit FontData(ByteArray* ba);
+
+ // Constructor.
+ // @param data the data to wrap
+ // @param offset the offset to start the wrap from
+ // @param length the length of the data wrapped
+ FontData(FontData* data, int32_t offset, int32_t length);
+
+ // Constructor.
+ // @param data the data to wrap
+ // @param offset the offset to start the wrap from
+ FontData(FontData* data, int32_t offset);
+ virtual ~FontData();
+
+ void Init(ByteArray* ba);
+
+ // Gets the offset in the underlying data taking into account any bounds on
+ // the data.
+ // @param offset the offset to get the bound compensated offset for
+ // @return the bound compensated offset
+ int32_t BoundOffset(int32_t offset);
+
+ // Gets the length in the underlying data taking into account any bounds on
+ // the data.
+ // @param offset the offset that the length is being used at
+ // @param length the length to get the bound compensated length for
+ // @return the bound compensated length
+ int32_t BoundLength(int32_t offset, int32_t length);
+
+ static const int32_t GROWABLE_SIZE = INT_MAX;
+
+ // TODO(arthurhsu): style guide violation: refactor this protected member
+ ByteArrayPtr array_;
+
+ private:
+ int32_t bound_offset_;
+ int32_t bound_length_;
+};
+typedef Ptr<FontData> FontDataPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_DATA_H_
diff --git a/sfntly/data/font_input_stream.cc b/sfntly/data/font_input_stream.cc
new file mode 100644
index 0000000..dcf8be3
--- /dev/null
+++ b/sfntly/data/font_input_stream.cc
@@ -0,0 +1,141 @@
+/*
+ * 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/data/font_input_stream.h"
+
+#include <algorithm>
+
+namespace sfntly {
+
+FontInputStream::FontInputStream(InputStream* is)
+ : stream_(is), position_(0), length_(0), bounded_(false) {
+}
+
+FontInputStream::FontInputStream(InputStream* is, size_t length)
+ : stream_(is), position_(0), length_(length), bounded_(true) {
+}
+
+FontInputStream::~FontInputStream() {
+ // Do not close here, underlying InputStream will close themselves.
+}
+
+int32_t FontInputStream::Available() {
+ if (stream_) {
+ return stream_->Available();
+ }
+ return 0;
+}
+
+void FontInputStream::Close() {
+ if (stream_) {
+ stream_->Close();
+ }
+}
+
+void FontInputStream::Mark(int32_t readlimit) {
+ if (stream_) {
+ stream_->Mark(readlimit);
+ }
+}
+
+bool FontInputStream::MarkSupported() {
+ if (stream_) {
+ return stream_->MarkSupported();
+ }
+ return false;
+}
+
+void FontInputStream::Reset() {
+ if (stream_) {
+ stream_->Reset();
+ }
+}
+
+int32_t FontInputStream::Read() {
+ if (!stream_ || (bounded_ && position_ >= length_)) {
+ return -1;
+ }
+ int32_t b = stream_->Read();
+ if (b >= 0) {
+ position_++;
+ }
+ return b;
+}
+
+int32_t FontInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
+ if (!stream_ || offset < 0 || length < 0 ||
+ (bounded_ && position_ >= length_)) {
+ return -1;
+ }
+ int32_t bytes_to_read =
+ bounded_ ? std::min<int32_t>(length, (int32_t)(length_ - position_)) :
+ length;
+ int32_t bytes_read = stream_->Read(b, offset, bytes_to_read);
+ position_ += bytes_read;
+ return bytes_read;
+}
+
+int32_t FontInputStream::Read(ByteVector* b) {
+ return Read(b, 0, b->size());
+}
+
+int32_t FontInputStream::ReadChar() {
+ return Read();
+}
+
+int32_t FontInputStream::ReadUShort() {
+ return 0xffff & (Read() << 8 | Read());
+}
+
+int32_t FontInputStream::ReadShort() {
+ return ((Read() << 8 | Read()) << 16) >> 16;
+}
+
+int32_t FontInputStream::ReadUInt24() {
+ return 0xffffff & (Read() << 16 | Read() << 8 | Read());
+}
+
+int64_t FontInputStream::ReadULong() {
+ return 0xffffffffL & ReadLong();
+}
+
+int32_t FontInputStream::ReadULongAsInt() {
+ int64_t ulong = ReadULong();
+ return ((int32_t)ulong) & ~0x80000000;
+}
+
+int32_t FontInputStream::ReadLong() {
+ return Read() << 24 | Read() << 16 | Read() << 8 | Read();
+}
+
+int32_t FontInputStream::ReadFixed() {
+ return ReadLong();
+}
+
+int64_t FontInputStream::ReadDateTimeAsLong() {
+ return (int64_t)ReadULong() << 32 | ReadULong();
+}
+
+int64_t FontInputStream::Skip(int64_t n) {
+ if (stream_) {
+ int64_t skipped = stream_->Skip(n);
+ position_ += skipped;
+ return skipped;
+ }
+ return 0;
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/font_input_stream.h b/sfntly/data/font_input_stream.h
new file mode 100644
index 0000000..9992b07
--- /dev/null
+++ b/sfntly/data/font_input_stream.h
@@ -0,0 +1,97 @@
+/*
+ * 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_SFNTLY_DATA_FONT_INPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+#include "sfntly/port/input_stream.h"
+
+namespace sfntly {
+
+// An input stream for reading font data.
+// The data types used are as listed:
+// BYTE 8-bit unsigned integer.
+// CHAR 8-bit signed integer.
+// USHORT 16-bit unsigned integer.
+// SHORT 16-bit signed integer.
+// UINT24 24-bit unsigned integer.
+// ULONG 32-bit unsigned integer.
+// LONG 32-bit signed integer.
+// Fixed 32-bit signed fixed-point number (16.16)
+// FUNIT Smallest measurable distance in the em space.
+// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
+// FUnits.
+// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
+// January 1, 1904. The value is represented as a signed 64-bit
+// integer.
+
+// Note: Original class inherits from Java's FilterOutputStream, which wraps
+// an InputStream within. In C++, we directly do the wrapping without
+// defining another layer of abstraction. The wrapped output stream is
+// *NOT* reference counted (because it's meaningless to ref-count an I/O
+// stream).
+class FontInputStream : public InputStream {
+ public:
+ // Constructor.
+ // @param is input stream to wrap
+ explicit FontInputStream(InputStream* is);
+
+ // Constructor for a bounded font input stream.
+ // @param is input stream to wrap
+ // @param length the maximum length of bytes to read
+ FontInputStream(InputStream* is, size_t length);
+
+ virtual ~FontInputStream();
+
+
+ virtual int32_t Available();
+ virtual void Close();
+ virtual void Mark(int32_t readlimit);
+ virtual bool MarkSupported();
+ virtual void Reset();
+
+ virtual int32_t Read();
+ virtual int32_t Read(ByteVector* buffer);
+ virtual int32_t Read(ByteVector* buffer, int32_t offset, int32_t length);
+
+ // Get the current position in the stream in bytes.
+ // @return the current position in bytes
+ virtual int64_t position() { return position_; }
+
+ virtual int32_t ReadChar();
+ virtual int32_t ReadUShort();
+ virtual int32_t ReadShort();
+ virtual int32_t ReadUInt24();
+ virtual int64_t ReadULong();
+ virtual int32_t ReadULongAsInt();
+ virtual int32_t ReadLong();
+ virtual int32_t ReadFixed();
+ virtual int64_t ReadDateTimeAsLong();
+ virtual int64_t Skip(int64_t n); // n can be negative.
+
+ private:
+ InputStream* stream_;
+ int64_t position_;
+ int64_t length_; // Bound on length of data to read.
+ bool bounded_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_INPUT_STREAM_H_
diff --git a/sfntly/data/font_output_stream.cc b/sfntly/data/font_output_stream.cc
new file mode 100644
index 0000000..3422a22
--- /dev/null
+++ b/sfntly/data/font_output_stream.cc
@@ -0,0 +1,130 @@
+/*
+ * 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/data/font_output_stream.h"
+
+#include <algorithm>
+
+namespace sfntly {
+
+FontOutputStream::FontOutputStream(OutputStream* os)
+ : stream_(os),
+ position_(0) {
+}
+
+FontOutputStream::~FontOutputStream() {
+ // Do not close, underlying stream shall clean up themselves.
+}
+
+void FontOutputStream::Write(byte_t b) {
+ if (stream_) {
+ stream_->Write(b);
+ position_++;
+ }
+}
+
+void FontOutputStream::Write(ByteVector* b) {
+ if (b) {
+ Write(b, 0, b->size());
+ position_ += b->size();
+ }
+}
+
+void FontOutputStream::Write(ByteVector* b, int32_t off, int32_t len) {
+ assert(b);
+ assert(stream_);
+ if (off < 0 || len < 0 || off + len < 0 ||
+ static_cast<size_t>(off + len) > b->size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#else
+ return;
+#endif
+ }
+
+ stream_->Write(b, off, len);
+ position_ += len;
+}
+
+void FontOutputStream::Write(byte_t* b, int32_t off, int32_t len) {
+ assert(b);
+ assert(stream_);
+ if (off < 0 || len < 0 || off + len < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#else
+ return;
+#endif
+ }
+
+ stream_->Write(b, off, len);
+ position_ += len;
+}
+
+void FontOutputStream::WriteChar(byte_t c) {
+ Write(c);
+}
+
+void FontOutputStream::WriteUShort(int32_t us) {
+ Write((byte_t)((us >> 8) & 0xff));
+ Write((byte_t)(us & 0xff));
+}
+
+void FontOutputStream::WriteShort(int32_t s) {
+ WriteUShort(s);
+}
+
+void FontOutputStream::WriteUInt24(int32_t ui) {
+ Write((byte_t)(ui >> 16) & 0xff);
+ Write((byte_t)(ui >> 8) & 0xff);
+ Write((byte_t)ui & 0xff);
+}
+
+void FontOutputStream::WriteULong(int64_t ul) {
+ Write((byte_t)((ul >> 24) & 0xff));
+ Write((byte_t)((ul >> 16) & 0xff));
+ Write((byte_t)((ul >> 8) & 0xff));
+ Write((byte_t)(ul & 0xff));
+}
+
+void FontOutputStream::WriteLong(int64_t l) {
+ WriteULong(l);
+}
+
+void FontOutputStream::WriteFixed(int32_t f) {
+ WriteULong(f);
+}
+
+void FontOutputStream::WriteDateTime(int64_t date) {
+ WriteULong((date >> 32) & 0xffffffff);
+ WriteULong(date & 0xffffffff);
+}
+
+void FontOutputStream::Flush() {
+ if (stream_) {
+ stream_->Flush();
+ }
+}
+
+void FontOutputStream::Close() {
+ if (stream_) {
+ stream_->Flush();
+ stream_->Close();
+ position_ = 0;
+ }
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/font_output_stream.h b/sfntly/data/font_output_stream.h
new file mode 100644
index 0000000..fcd48e8
--- /dev/null
+++ b/sfntly/data/font_output_stream.h
@@ -0,0 +1,79 @@
+/*
+ * 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_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+#include "sfntly/port/output_stream.h"
+
+namespace sfntly {
+
+// An output stream for writing font data.
+// The data types used are as listed:
+// BYTE 8-bit unsigned integer.
+// CHAR 8-bit signed integer.
+// USHORT 16-bit unsigned integer.
+// SHORT 16-bit signed integer.
+// UINT24 24-bit unsigned integer.
+// ULONG 32-bit unsigned integer.
+// LONG 32-bit signed integer.
+// Fixed 32-bit signed fixed-point number (16.16)
+// FUNIT Smallest measurable distance in the em space.
+// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
+// FUnits.
+// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
+// January 1, 1904. The value is represented as a signed 64-bit
+// integer.
+
+// Note: The wrapped output stream is *NOT* reference counted (because it's
+// meaningless to ref-count an I/O stream).
+class FontOutputStream : public OutputStream {
+ public:
+ explicit FontOutputStream(OutputStream* os);
+ virtual ~FontOutputStream();
+
+ virtual size_t position() { return position_; }
+
+ virtual void Write(byte_t b);
+ virtual void Write(ByteVector* b);
+ virtual void Write(ByteVector* b, int32_t off, int32_t len);
+ virtual void Write(byte_t* b, int32_t off, int32_t len);
+ virtual void WriteChar(byte_t c);
+ virtual void WriteUShort(int32_t us);
+ virtual void WriteShort(int32_t s);
+ virtual void WriteUInt24(int32_t ui);
+ virtual void WriteULong(int64_t ul);
+ virtual void WriteLong(int64_t l);
+ virtual void WriteFixed(int32_t l);
+ virtual void WriteDateTime(int64_t date);
+
+ // Note: C++ port only.
+ virtual void Flush();
+ virtual void Close();
+
+ private:
+ // Note: we do not use the variable name out as in Java because it has
+ // special meaning in VC++ and will be very confusing.
+ OutputStream* stream_;
+ size_t position_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_FONT_OUTPUT_STREAM_H_
diff --git a/sfntly/data/growable_memory_byte_array.cc b/sfntly/data/growable_memory_byte_array.cc
new file mode 100644
index 0000000..c335614
--- /dev/null
+++ b/sfntly/data/growable_memory_byte_array.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 "sfntly/data/growable_memory_byte_array.h"
+
+#include <limits.h>
+#include <string.h>
+
+#include <algorithm>
+
+namespace sfntly {
+
+GrowableMemoryByteArray::GrowableMemoryByteArray()
+ : ByteArray(0, INT_MAX, true) {
+ // Note: We did not set an initial size of array like Java because STL
+ // implementation will determine the best strategy.
+}
+
+GrowableMemoryByteArray::~GrowableMemoryByteArray() {}
+
+int32_t GrowableMemoryByteArray::CopyTo(OutputStream* os,
+ int32_t offset,
+ int32_t length) {
+ assert(os);
+ os->Write(&b_, offset, length);
+ return length;
+}
+
+void GrowableMemoryByteArray::InternalPut(int32_t index, byte_t b) {
+ if ((size_t)index >= b_.size()) {
+ b_.resize((size_t)(index + 1));
+ }
+ b_[index] = b;
+}
+
+int32_t GrowableMemoryByteArray::InternalPut(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ if ((size_t)index + length >= b_.size()) {
+ // Note: We grow one byte more than Java version. VC debuggers shows
+ // data better this way.
+ b_.resize((size_t)(index + length + 1));
+ }
+ std::copy(b + offset, b + offset + length, b_.begin() + index);
+ return length;
+}
+
+byte_t GrowableMemoryByteArray::InternalGet(int32_t index) {
+ return b_[index];
+}
+
+int32_t GrowableMemoryByteArray::InternalGet(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ memcpy(b + offset, &(b_[0]) + index, length);
+ return length;
+}
+
+void GrowableMemoryByteArray::Close() {
+ b_.clear();
+}
+
+byte_t* GrowableMemoryByteArray::Begin() {
+ return &(b_[0]);
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/growable_memory_byte_array.h b/sfntly/data/growable_memory_byte_array.h
new file mode 100644
index 0000000..8583a0d
--- /dev/null
+++ b/sfntly/data/growable_memory_byte_array.h
@@ -0,0 +1,66 @@
+/*
+ * 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_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
+
+#include "sfntly/data/byte_array.h"
+
+namespace sfntly {
+
+// Note: This is not really a port of Java version. Instead, this wraps a
+// std::vector inside and let it grow by calling resize().
+class GrowableMemoryByteArray : public ByteArray,
+ public RefCounted<GrowableMemoryByteArray> {
+ public:
+ GrowableMemoryByteArray();
+ virtual ~GrowableMemoryByteArray();
+ virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+ // Make gcc -Woverloaded-virtual happy.
+ virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
+ virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+ return ByteArray::CopyTo(array, offset, length);
+ }
+ virtual int32_t CopyTo(int32_t dst_offset,
+ ByteArray* array,
+ int32_t src_offset,
+ int32_t length) {
+ return ByteArray::CopyTo(dst_offset, array, src_offset, length);
+ }
+ virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
+
+ protected:
+ virtual void InternalPut(int32_t index, byte_t b);
+ virtual int32_t InternalPut(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+ virtual byte_t InternalGet(int32_t index);
+ virtual int32_t InternalGet(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+ virtual void Close();
+ virtual byte_t* Begin();
+
+ private:
+ ByteVector b_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_GROWABLE_MEMORY_BYTE_ARRAY_H_
diff --git a/sfntly/data/memory_byte_array.cc b/sfntly/data/memory_byte_array.cc
new file mode 100644
index 0000000..d6c9c48
--- /dev/null
+++ b/sfntly/data/memory_byte_array.cc
@@ -0,0 +1,93 @@
+/*
+ * 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/data/memory_byte_array.h"
+
+#include <string.h>
+
+namespace sfntly {
+
+MemoryByteArray::MemoryByteArray(int32_t length)
+ : ByteArray(0, length), b_(NULL), allocated_(true) {
+}
+
+MemoryByteArray::MemoryByteArray(byte_t* b, int32_t filled_length)
+ : ByteArray(filled_length, filled_length), b_(b), allocated_(false) {
+ assert(b);
+}
+
+MemoryByteArray::~MemoryByteArray() {
+ Close();
+}
+
+int32_t MemoryByteArray::CopyTo(OutputStream* os,
+ int32_t offset,
+ int32_t length) {
+ assert(os);
+ os->Write(b_, offset, length);
+ return length;
+}
+
+void MemoryByteArray::Init() {
+ if (allocated_ && b_ == NULL) {
+ b_ = new byte_t[Size()];
+ memset(b_, 0, Size());
+ }
+}
+
+void MemoryByteArray::InternalPut(int32_t index, byte_t b) {
+ Init();
+ b_[index] = b;
+}
+
+int32_t MemoryByteArray::InternalPut(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ assert(b);
+ Init();
+ memcpy(b_ + index, b + offset, length);
+ return length;
+}
+
+byte_t MemoryByteArray::InternalGet(int32_t index) {
+ Init();
+ return b_[index];
+}
+
+int32_t MemoryByteArray::InternalGet(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ assert(b);
+ Init();
+ memcpy(b + offset, b_ + index, length);
+ return length;
+}
+
+void MemoryByteArray::Close() {
+ if (allocated_ && b_) {
+ delete[] b_;
+ }
+ b_ = NULL;
+}
+
+byte_t* MemoryByteArray::Begin() {
+ Init();
+ return b_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/memory_byte_array.h b/sfntly/data/memory_byte_array.h
new file mode 100644
index 0000000..838fd1a
--- /dev/null
+++ b/sfntly/data/memory_byte_array.h
@@ -0,0 +1,81 @@
+/*
+ * 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_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
+
+#include "sfntly/data/byte_array.h"
+
+namespace sfntly {
+
+class MemoryByteArray : public ByteArray, public RefCounted<MemoryByteArray> {
+ public:
+ // Construct a new MemoryByteArray with a new array of the size given. It is
+ // assumed that none of the array is filled and readable.
+ explicit MemoryByteArray(int32_t length);
+
+ // Note: not implemented due to dangerous operations in constructor.
+ //explicit MemoryByteArray(ByteVector* b);
+
+ // Construct a new MemoryByteArray using byte array.
+ // @param b the byte array that provides the actual storage
+ // @param filled_length the index of the last byte in the array has data
+ // Note: This is different from Java version, it does not take over the
+ // ownership of b. Caller is responsible for handling the lifetime
+ // of b. C++ port also assumes filled_length is buffer_length since
+ // there is not a reliable way to identify the actual size of buffer.
+ MemoryByteArray(byte_t* b, int32_t filled_length);
+
+ virtual ~MemoryByteArray();
+ virtual int32_t CopyTo(OutputStream* os, int32_t offset, int32_t length);
+
+ // Make gcc -Woverloaded-virtual happy.
+ virtual int32_t CopyTo(ByteArray* array) { return ByteArray::CopyTo(array); }
+ virtual int32_t CopyTo(ByteArray* array, int32_t offset, int32_t length) {
+ return ByteArray::CopyTo(array, offset, length);
+ }
+ virtual int32_t CopyTo(int32_t dst_offset,
+ ByteArray* array,
+ int32_t src_offset,
+ int32_t length) {
+ return ByteArray::CopyTo(dst_offset, array, src_offset, length);
+ }
+ virtual int32_t CopyTo(OutputStream* os) { return ByteArray::CopyTo(os); }
+
+ protected:
+ virtual void InternalPut(int32_t index, byte_t b);
+ virtual int32_t InternalPut(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+ virtual byte_t InternalGet(int32_t index);
+ virtual int32_t InternalGet(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+ virtual void Close();
+ virtual byte_t* Begin();
+
+ private:
+ void Init(); // C++ port only, used to allocate memory outside constructor.
+
+ byte_t* b_;
+ bool allocated_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_MEMORY_BYTE_ARRAY_H_
diff --git a/sfntly/data/readable_font_data.cc b/sfntly/data/readable_font_data.cc
new file mode 100644
index 0000000..06d783f
--- /dev/null
+++ b/sfntly/data/readable_font_data.cc
@@ -0,0 +1,336 @@
+/*
+ * 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/data/readable_font_data.h"
+
+#include <stdio.h>
+
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+ReadableFontData::ReadableFontData(ByteArray* array)
+ : FontData(array),
+ checksum_set_(false),
+ checksum_(0) {
+}
+
+ReadableFontData::~ReadableFontData() {}
+
+// TODO(arthurhsu): re-investigate the memory model of this function. It's
+// not too useful without copying, but it's not performance
+// savvy to do copying.
+CALLER_ATTACH
+ReadableFontData* ReadableFontData::CreateReadableFontData(ByteVector* b) {
+ assert(b);
+ ByteArrayPtr ba = new MemoryByteArray(b->size());
+ ba->Put(0, b);
+ ReadableFontDataPtr wfd = new ReadableFontData(ba);
+ return wfd.Detach();
+}
+
+int64_t ReadableFontData::Checksum() {
+ AutoLock lock(checksum_lock_);
+ if (!checksum_set_) {
+ ComputeChecksum();
+ }
+ return checksum_;
+}
+
+void ReadableFontData::SetCheckSumRanges(const IntegerList& ranges) {
+ checksum_range_ = ranges;
+ checksum_set_ = false; // UNIMPLEMENTED: atomicity
+}
+
+int32_t ReadableFontData::ReadUByte(int32_t index) {
+ int32_t b = array_->Get(BoundOffset(index));
+#if !defined (SFNTLY_NO_EXCEPTION)
+ if (b < 0) {
+ throw IndexOutOfBoundException(
+ "Index attempted to be read from is out of bounds", index);
+ }
+#endif
+ return b;
+}
+
+int32_t ReadableFontData::ReadByte(int32_t index) {
+ int32_t b = array_->Get(BoundOffset(index));
+#if !defined (SFNTLY_NO_EXCEPTION)
+ if (b < 0) {
+ throw IndexOutOfBoundException(
+ "Index attempted to be read from is out of bounds", index);
+ }
+#endif
+ return (b << 24) >> 24;
+}
+
+int32_t ReadableFontData::ReadBytes(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ return array_->Get(BoundOffset(index), b, offset, BoundLength(index, length));
+}
+
+int32_t ReadableFontData::ReadChar(int32_t index) {
+ return ReadUByte(index);
+}
+
+int32_t ReadableFontData::ReadUShort(int32_t index) {
+ return 0xffff & (ReadUByte(index) << 8 | ReadUByte(index + 1));
+}
+
+int32_t ReadableFontData::ReadShort(int32_t index) {
+ return ((ReadByte(index) << 8 | ReadUByte(index + 1)) << 16) >> 16;
+}
+
+int32_t ReadableFontData::ReadUInt24(int32_t index) {
+ return 0xffffff & (ReadUByte(index) << 16 |
+ ReadUByte(index + 1) << 8 |
+ ReadUByte(index + 2));
+}
+
+int64_t ReadableFontData::ReadULong(int32_t index) {
+ return 0xffffffffL & (ReadUByte(index) << 24 |
+ ReadUByte(index + 1) << 16 |
+ ReadUByte(index + 2) << 8 |
+ ReadUByte(index + 3));
+}
+
+int32_t ReadableFontData::ReadULongAsInt(int32_t index) {
+ int64_t ulong = ReadULong(index);
+#if !defined (SFNTLY_NO_EXCEPTION)
+ if ((ulong & 0x80000000) == 0x80000000) {
+ throw ArithmeticException("Long value too large to fit into an integer.");
+ }
+#endif
+ return static_cast<int32_t>(ulong);
+}
+
+int64_t ReadableFontData::ReadULongLE(int32_t index) {
+ return 0xffffffffL & (ReadUByte(index) |
+ ReadUByte(index + 1) << 8 |
+ ReadUByte(index + 2) << 16 |
+ ReadUByte(index + 3) << 24);
+}
+
+int32_t ReadableFontData::ReadLong(int32_t index) {
+ return ReadByte(index) << 24 |
+ ReadUByte(index + 1) << 16 |
+ ReadUByte(index + 2) << 8 |
+ ReadUByte(index + 3);
+}
+
+int32_t ReadableFontData::ReadFixed(int32_t index) {
+ return ReadLong(index);
+}
+
+int64_t ReadableFontData::ReadDateTimeAsLong(int32_t index) {
+ return (int64_t)ReadULong(index) << 32 | ReadULong(index + 4);
+}
+
+int32_t ReadableFontData::ReadFWord(int32_t index) {
+ return ReadShort(index);
+}
+
+int32_t ReadableFontData::ReadFUFWord(int32_t index) {
+ return ReadUShort(index);
+}
+
+int32_t ReadableFontData::CopyTo(OutputStream* os) {
+ return array_->CopyTo(os, BoundOffset(0), Length());
+}
+
+int32_t ReadableFontData::CopyTo(WritableFontData* wfd) {
+ return array_->CopyTo(wfd->BoundOffset(0),
+ wfd->array_,
+ BoundOffset(0),
+ Length());
+}
+
+int32_t ReadableFontData::CopyTo(ByteArray* ba) {
+ return array_->CopyTo(ba, BoundOffset(0), Length());
+}
+
+int32_t ReadableFontData::SearchUShort(int32_t start_index,
+ int32_t start_offset,
+ int32_t end_index,
+ int32_t end_offset,
+ int32_t length,
+ int32_t key) {
+ int32_t location = 0;
+ int32_t bottom = 0;
+ int32_t top = length;
+ while (top != bottom) {
+ location = (top + bottom) / 2;
+ int32_t location_start = ReadUShort(start_index + location * start_offset);
+ if (key < location_start) {
+ // location is below current location
+ top = location;
+ } else {
+ // is key below the upper bound?
+ int32_t location_end = ReadUShort(end_index + location * end_offset);
+#if defined (SFNTLY_DEBUG_FONTDATA)
+ fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
+#endif
+ if (key <= location_end) {
+ return location;
+ } else {
+ // location is above the current location
+ bottom = location + 1;
+ }
+ }
+ }
+ return -1;
+}
+
+int32_t ReadableFontData::SearchUShort(int32_t start_index,
+ int32_t start_offset,
+ int32_t length,
+ int32_t key) {
+ int32_t location = 0;
+ int32_t bottom = 0;
+ int32_t top = length;
+ while (top != bottom) {
+ location = (top + bottom) / 2;
+ int32_t location_start = ReadUShort(start_index + location * start_offset);
+ if (key < location_start) {
+ // location is below current location
+ top = location;
+ } else if (key > location_start) {
+ // location is above current location
+ bottom = location + 1;
+ } else {
+ return location;
+ }
+ }
+ return -1;
+}
+
+int32_t ReadableFontData::SearchULong(int32_t start_index,
+ int32_t start_offset,
+ int32_t end_index,
+ int32_t end_offset,
+ int32_t length,
+ int32_t key) {
+ int32_t location = 0;
+ int32_t bottom = 0;
+ int32_t top = length;
+ while (top != bottom) {
+ location = (top + bottom) / 2;
+ int32_t location_start = ReadULongAsInt(start_index
+ + location * start_offset);
+ if (key < location_start) {
+ // location is below current location
+ top = location;
+ } else {
+ // is key below the upper bound?
+ int32_t location_end = ReadULongAsInt(end_index + location * end_offset);
+#if defined (SFNTLY_DEBUG_FONTDATA)
+ fprintf(stderr, "**start: %d; end: %d\n", location_start, location_end);
+#endif
+ if (key <= location_end) {
+ return location;
+ } else {
+ // location is above the current location
+ bottom = location + 1;
+ }
+ }
+ }
+ return -1;
+}
+
+CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset,
+ int32_t length) {
+ if (offset < 0 || offset + length > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException(
+ "Attempt to bind data outside of its limits");
+#endif
+ return NULL;
+ }
+ FontDataPtr slice = new ReadableFontData(this, offset, length);
+ return slice.Detach();
+}
+
+CALLER_ATTACH FontData* ReadableFontData::Slice(int32_t offset) {
+ if (offset < 0 || offset > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException(
+ "Attempt to bind data outside of its limits");
+#endif
+ return NULL;
+ }
+ FontDataPtr slice = new ReadableFontData(this, offset);
+ return slice.Detach();
+}
+
+ReadableFontData::ReadableFontData(ReadableFontData* data, int32_t offset)
+ : FontData(data, offset),
+ checksum_set_(false),
+ checksum_(0) {
+}
+
+ReadableFontData::ReadableFontData(ReadableFontData* data,
+ int32_t offset,
+ int32_t length)
+ : FontData(data, offset, length),
+ checksum_set_(false),
+ checksum_(0) {
+}
+
+void ReadableFontData::ComputeChecksum() {
+ // TODO(arthurhsu): IMPLEMENT: synchronization/atomicity
+ int64_t sum = 0;
+ if (checksum_range_.empty()) {
+ sum = ComputeCheckSum(0, Length());
+ } else {
+ for (uint32_t low_bound_index = 0; low_bound_index < checksum_range_.size();
+ low_bound_index += 2) {
+ int32_t low_bound = checksum_range_[low_bound_index];
+ int32_t high_bound = (low_bound_index == checksum_range_.size() - 1) ?
+ Length() :
+ checksum_range_[low_bound_index + 1];
+ sum += ComputeCheckSum(low_bound, high_bound);
+ }
+ }
+
+ checksum_ = sum & 0xffffffffL;
+ checksum_set_ = true;
+}
+
+int64_t ReadableFontData::ComputeCheckSum(int32_t low_bound,
+ int32_t high_bound) {
+ int64_t sum = 0;
+ // Checksum all whole 4-byte chunks.
+ for (int32_t i = low_bound; i <= high_bound - 4; i += 4) {
+ sum += ReadULong(i);
+ }
+
+ // Add last fragment if not 4-byte multiple
+ int32_t off = high_bound & -4;
+ if (off < high_bound) {
+ int32_t b3 = ReadUByte(off);
+ int32_t b2 = (off + 1 < high_bound) ? ReadUByte(off + 1) : 0;
+ int32_t b1 = (off + 2 < high_bound) ? ReadUByte(off + 2) : 0;
+ int32_t b0 = 0;
+ sum += (b3 << 24) | (b2 << 16) | (b1 << 8) | b0;
+ }
+ return sum;
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/readable_font_data.h b/sfntly/data/readable_font_data.h
new file mode 100644
index 0000000..b43c626
--- /dev/null
+++ b/sfntly/data/readable_font_data.h
@@ -0,0 +1,308 @@
+/*
+ * 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_SFNTLY_DATA_READABLE_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
+
+#include "sfntly/data/font_data.h"
+#include "sfntly/port/lock.h"
+
+namespace sfntly {
+
+class WritableFontData;
+class OutputStream;
+
+// Writable font data wrapper. Supports reading of data primitives in the
+// TrueType / OpenType spec.
+// The data types used are as listed:
+// BYTE 8-bit unsigned integer.
+// CHAR 8-bit signed integer.
+// USHORT 16-bit unsigned integer.
+// SHORT 16-bit signed integer.
+// UINT24 24-bit unsigned integer.
+// ULONG 32-bit unsigned integer.
+// LONG 32-bit signed integer.
+// Fixed 32-bit signed fixed-point number (16.16)
+// FUNIT Smallest measurable distance in the em space.
+// FWORD 16-bit signed integer (SHORT) that describes a quantity in FUnits.
+// UFWORD 16-bit unsigned integer (USHORT) that describes a quantity in
+// FUnits.
+// F2DOT14 16-bit signed fixed number with the low 14 bits of fraction (2.14)
+// LONGDATETIME Date represented in number of seconds since 12:00 midnight,
+// January 1, 1904. The value is represented as a signed 64-bit
+// integer.
+
+class ReadableFontData : public FontData,
+ public RefCounted<ReadableFontData> {
+ public:
+ explicit ReadableFontData(ByteArray* array);
+ virtual ~ReadableFontData();
+
+ static CALLER_ATTACH ReadableFontData* CreateReadableFontData(ByteVector* b);
+
+ // Gets a computed checksum for the data. This checksum uses the OpenType spec
+ // calculation. Every ULong value (32 bit unsigned) in the data is summed and
+ // the resulting value is truncated to 32 bits. If the data length in bytes is
+ // not an integral multiple of 4 then any remaining bytes are treated as the
+ // start of a 4 byte sequence whose remaining bytes are zero.
+ // @return the checksum
+ int64_t Checksum();
+
+ // Sets the bounds to use for computing the checksum. These bounds are in
+ // begin and end pairs. If an odd number is given then the final range is
+ // assumed to extend to the end of the data. The lengths of each range must be
+ // a multiple of 4.
+ // @param ranges the range bounds to use for the checksum
+ void SetCheckSumRanges(const IntegerList& ranges);
+
+ // Read the UBYTE at the given index.
+ // @param index index into the font data
+ // @return the UBYTE; -1 if outside the bounds of the font data
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadUByte(int32_t index);
+
+ // Read the BYTE at the given index.
+ // @param index index into the font data
+ // @return the BYTE
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadByte(int32_t index);
+
+ // Read the bytes at the given index into the array.
+ // @param index index into the font data
+ // @param b the destination for the bytes read
+ // @param offset offset in the byte array to place the bytes
+ // @param length the length of bytes to read
+ // @return the number of bytes actually read; -1 if the index is outside the
+ // bounds of the font data
+ virtual int32_t ReadBytes(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+
+ // Read the CHAR at the given index.
+ // @param index index into the font data
+ // @return the CHAR
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadChar(int32_t index);
+
+ // Read the USHORT at the given index.
+ // @param index index into the font data
+ // @return the USHORT
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadUShort(int32_t index);
+
+ // Read the SHORT at the given index.
+ // @param index index into the font data
+ // @return the SHORT
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadShort(int32_t index);
+
+ // Read the UINT24 at the given index.
+ // @param index index into the font data
+ // @return the UINT24
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadUInt24(int32_t index);
+
+ // Read the ULONG at the given index.
+ // @param index index into the font data
+ // @return the ULONG
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int64_t ReadULong(int32_t index);
+
+ // Read the ULONG at the given index as int32_t.
+ // @param index index into the font data
+ // @return the ULONG
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadULongAsInt(int32_t index);
+
+ // Read the ULONG at the given index, little-endian variant
+ // @param index index into the font data
+ // @return the ULONG
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int64_t ReadULongLE(int32_t index);
+
+ // Read the LONG at the given index.
+ // @param index index into the font data
+ // @return the LONG
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadLong(int32_t index);
+
+ // Read the Fixed at the given index.
+ // @param index index into the font data
+ // @return the Fixed
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadFixed(int32_t index);
+
+ // Read the LONGDATETIME at the given index.
+ // @param index index into the font data
+ // @return the LONGDATETIME
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int64_t ReadDateTimeAsLong(int32_t index);
+
+ // Read the FWORD at the given index.
+ // @param index index into the font data
+ // @return the FWORD
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadFWord(int32_t index);
+
+ // Read the UFWORD at the given index.
+ // @param index index into the font data
+ // @return the UFWORD
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t ReadFUFWord(int32_t index);
+
+ // Note: Not ported because they just throw UnsupportedOperationException()
+ // in Java.
+ /*
+ virtual int32_t ReadFUnit(int32_t index);
+ virtual int64_t ReadF2Dot14(int32_t index);
+ */
+
+ // Copy the FontData to an OutputStream.
+ // @param os the destination
+ // @return number of bytes copied
+ // @throws IOException
+ virtual int32_t CopyTo(OutputStream* os);
+
+ // Copy the FontData to a WritableFontData.
+ // @param wfd the destination
+ // @return number of bytes copied
+ // @throws IOException
+ virtual int32_t CopyTo(WritableFontData* wfd);
+
+ // Make gcc -Woverloaded-virtual happy.
+ virtual int32_t CopyTo(ByteArray* ba);
+
+ // Search for the key value in the range tables provided.
+ // The search looks through the start-end pairs looking for the key value. It
+ // is assumed that the start-end pairs are both represented by UShort values,
+ // ranges do not overlap, and are monotonically increasing.
+ // @param startIndex the position to read the first start value from
+ // @param startOffset the offset between subsequent start values
+ // @param endIndex the position to read the first end value from
+ // @param endOffset the offset between subsequent end values
+ // @param length the number of start-end pairs
+ // @param key the value to search for
+ // @return the index of the start-end pairs in which the key was found; -1
+ // otherwise
+ int32_t SearchUShort(int32_t start_index,
+ int32_t start_offset,
+ int32_t end_index,
+ int32_t end_offset,
+ int32_t length,
+ int32_t key);
+
+ // Search for the key value in the table provided.
+ // The search looks through the values looking for the key value. It is
+ // assumed that the are represented by UShort values and are monotonically
+ // increasing.
+ // @param startIndex the position to read the first start value from
+ // @param startOffset the offset between subsequent start values
+ // @param length the number of start-end pairs
+ // @param key the value to search for
+ // @return the index of the start-end pairs in which the key was found; -1
+ // otherwise
+ int32_t SearchUShort(int32_t start_index,
+ int32_t start_offset,
+ int32_t length,
+ int32_t key);
+
+ // Search for the key value in the range tables provided.
+ // The search looks through the start-end pairs looking for the key value. It
+ // is assumed that the start-end pairs are both represented by ULong values
+ // that can be represented within 31 bits, ranges do not overlap, and are
+ // monotonically increasing.
+ // @param startIndex the position to read the first start value from
+ // @param startOffset the offset between subsequent start values
+ // @param endIndex the position to read the first end value from
+ // @param endOffset the offset between subsequent end values
+ // @param length the number of start-end pairs
+ // @param key the value to search for
+ // @return the index of the start-end pairs in which the key was found; -1
+ // otherwise
+ int32_t SearchULong(int32_t start_index,
+ int32_t start_offset,
+ int32_t end_index,
+ int32_t end_offset,
+ int32_t length,
+ int32_t key);
+
+
+ // TODO(arthurhsu): IMPLEMENT
+ /*
+ virtual int32_t ReadFUnit(int32_t index);
+ virtual int64_t ReadF2Dot14(int32_t index);
+ virtual int64_t ReadLongDateTime(int32_t index);
+ */
+
+ // Makes a slice of this FontData. The returned slice will share the data with
+ // the original FontData.
+ // @param offset the start of the slice
+ // @param length the number of bytes in the slice
+ // @return a slice of the original FontData
+ // Note: C++ polymorphism requires return type to be consistent
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
+
+ // Makes a bottom bound only slice of this array. The returned slice will
+ // share the data with the original FontData.
+ // @param offset the start of the slice
+ // @return a slice of the original FontData
+ // Note: C++ polymorphism requires return type to be consistent
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset);
+
+ // Not Ported: toString()
+
+ protected:
+ // Constructor. Creates a bounded wrapper of another ReadableFontData from the
+ // given offset until the end of the original ReadableFontData.
+ // @param data data to wrap
+ // @param offset the start of this data's view of the original data
+ ReadableFontData(ReadableFontData* data, int32_t offset);
+
+ // Constructor. Creates a bounded wrapper of another ReadableFontData from the
+ // given offset until the end of the original ReadableFontData.
+ // @param data data to wrap
+ // @param offset the start of this data's view of the original data
+ // @param length the length of the other FontData to use
+ ReadableFontData(ReadableFontData* data, int32_t offset, int32_t length);
+
+ private:
+ // Compute the checksum for the font data using any ranges set for the
+ // calculation.
+ void ComputeChecksum();
+
+ // Do the actual computation of the checksum for a range using the
+ // TrueType/OpenType checksum algorithm. The range used is from the low bound
+ // to the high bound in steps of four bytes. If any of the bytes within that 4
+ // byte segment are not readable then it will considered a zero for
+ // calculation.
+ // Only called from within a synchronized method so it does not need to be
+ // synchronized itself.
+ // @param lowBound first position to start a 4 byte segment on
+ // @param highBound last possible position to start a 4 byte segment on
+ // @return the checksum for the total range
+ int64_t ComputeCheckSum(int32_t low_bound, int32_t high_bound);
+
+ Lock checksum_lock_;
+ bool checksum_set_;
+ int64_t checksum_;
+ IntegerList checksum_range_;
+};
+typedef Ptr<ReadableFontData> ReadableFontDataPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_READABLE_FONT_DATA_H_
diff --git a/sfntly/data/writable_font_data.cc b/sfntly/data/writable_font_data.cc
new file mode 100644
index 0000000..4b3b440
--- /dev/null
+++ b/sfntly/data/writable_font_data.cc
@@ -0,0 +1,203 @@
+/*
+ * 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/data/writable_font_data.h"
+
+#include <algorithm>
+
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+
+namespace sfntly {
+
+WritableFontData::WritableFontData(ByteArray* ba) : ReadableFontData(ba) {
+}
+
+WritableFontData::~WritableFontData() {}
+
+// static
+CALLER_ATTACH
+WritableFontData* WritableFontData::CreateWritableFontData(int32_t length) {
+ ByteArrayPtr ba;
+ if (length > 0) {
+ ba = new MemoryByteArray(length);
+ ba->SetFilledLength(length);
+ } else {
+ ba = new GrowableMemoryByteArray();
+ }
+ WritableFontDataPtr wfd = new WritableFontData(ba);
+ return wfd.Detach();
+}
+
+// TODO(arthurhsu): re-investigate the memory model of this function. It's
+// not too useful without copying, but it's not performance
+// savvy to do copying.
+CALLER_ATTACH
+WritableFontData* WritableFontData::CreateWritableFontData(ByteVector* b) {
+ ByteArrayPtr ba = new GrowableMemoryByteArray();
+ ba->Put(0, b);
+ WritableFontDataPtr wfd = new WritableFontData(ba);
+ return wfd.Detach();
+}
+
+int32_t WritableFontData::WriteByte(int32_t index, byte_t b) {
+ array_->Put(BoundOffset(index), b);
+ return 1;
+}
+
+int32_t WritableFontData::WriteBytes(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length) {
+ return array_->Put(BoundOffset(index),
+ b,
+ offset,
+ BoundLength(index, length));
+}
+
+int32_t WritableFontData::WriteBytes(int32_t index, ByteVector* b) {
+ assert(b);
+ return WriteBytes(index, &((*b)[0]), 0, b->size());
+}
+
+int32_t WritableFontData::WriteBytesPad(int32_t index,
+ ByteVector* b,
+ int32_t offset,
+ int32_t length,
+ byte_t pad) {
+ int32_t written =
+ array_->Put(BoundOffset(index),
+ &((*b)[0]),
+ offset,
+ BoundLength(index,
+ std::min<int32_t>(length, b->size() - offset)));
+ written += WritePadding(written + index, length - written, pad);
+ return written;
+}
+
+int32_t WritableFontData::WritePadding(int32_t index, int32_t count) {
+ return WritePadding(index, count, (byte_t)0);
+}
+
+int32_t WritableFontData::WritePadding(int32_t index, int32_t count,
+ byte_t pad) {
+ for (int32_t i = 0; i < count; ++i) {
+ array_->Put(index + i, pad);
+ }
+ return count;
+}
+
+int32_t WritableFontData::WriteChar(int32_t index, byte_t c) {
+ return WriteByte(index, c);
+}
+
+int32_t WritableFontData::WriteUShort(int32_t index, int32_t us) {
+ WriteByte(index, (byte_t)((us >> 8) & 0xff));
+ WriteByte(index + 1, (byte_t)(us & 0xff));
+ return 2;
+}
+
+int32_t WritableFontData::WriteUShortLE(int32_t index, int32_t us) {
+ WriteByte(index, (byte_t)(us & 0xff));
+ WriteByte(index + 1, (byte_t)((us >> 8) & 0xff));
+ return 2;
+}
+
+int32_t WritableFontData::WriteShort(int32_t index, int32_t s) {
+ return WriteUShort(index, s);
+}
+
+int32_t WritableFontData::WriteUInt24(int32_t index, int32_t ui) {
+ WriteByte(index, (byte_t)((ui >> 16) & 0xff));
+ WriteByte(index + 1, (byte_t)((ui >> 8) & 0xff));
+ WriteByte(index + 2, (byte_t)(ui & 0xff));
+ return 3;
+}
+
+int32_t WritableFontData::WriteULong(int32_t index, int64_t ul) {
+ WriteByte(index, (byte_t)((ul >> 24) & 0xff));
+ WriteByte(index + 1, (byte_t)((ul >> 16) & 0xff));
+ WriteByte(index + 2, (byte_t)((ul >> 8) & 0xff));
+ WriteByte(index + 3, (byte_t)(ul & 0xff));
+ return 4;
+}
+
+int32_t WritableFontData::WriteULongLE(int32_t index, int64_t ul) {
+ WriteByte(index, (byte_t)(ul & 0xff));
+ WriteByte(index + 1, (byte_t)((ul >> 8) & 0xff));
+ WriteByte(index + 2, (byte_t)((ul >> 16) & 0xff));
+ WriteByte(index + 3, (byte_t)((ul >> 24) & 0xff));
+ return 4;
+}
+
+int32_t WritableFontData::WriteLong(int32_t index, int64_t l) {
+ return WriteULong(index, l);
+}
+
+int32_t WritableFontData::WriteFixed(int32_t index, int32_t f) {
+ return WriteLong(index, f);
+}
+
+int32_t WritableFontData::WriteDateTime(int32_t index, int64_t date) {
+ WriteULong(index, (date >> 32) & 0xffffffff);
+ WriteULong(index + 4, date & 0xffffffff);
+ return 8;
+}
+
+void WritableFontData::CopyFrom(InputStream* is, int32_t length) {
+ array_->CopyFrom(is, length);
+}
+
+void WritableFontData::CopyFrom(InputStream* is) {
+ array_->CopyFrom(is);
+}
+
+CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset,
+ int32_t length) {
+ if (offset < 0 || offset + length > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException(
+ "Attempt to bind data outside of its limits");
+#endif
+ return NULL;
+ }
+ FontDataPtr slice = new WritableFontData(this, offset, length);
+ return slice.Detach();
+}
+
+CALLER_ATTACH FontData* WritableFontData::Slice(int32_t offset) {
+ if (offset > Size()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException(
+ "Attempt to bind data outside of its limits");
+#endif
+ return NULL;
+ }
+ FontDataPtr slice = new WritableFontData(this, offset);
+ return slice.Detach();
+}
+
+WritableFontData::WritableFontData(WritableFontData* data, int32_t offset)
+ : ReadableFontData(data, offset) {
+}
+
+WritableFontData::WritableFontData(WritableFontData* data,
+ int32_t offset,
+ int32_t length)
+ : ReadableFontData(data, offset, length) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/data/writable_font_data.h b/sfntly/data/writable_font_data.h
new file mode 100644
index 0000000..d2a049e
--- /dev/null
+++ b/sfntly/data/writable_font_data.h
@@ -0,0 +1,211 @@
+/*
+ * 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_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
+#define SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
+
+#include "sfntly/data/readable_font_data.h"
+
+namespace sfntly {
+
+// Writable font data wrapper. Supports writing of data primitives in the
+// TrueType / OpenType spec.
+class WritableFontData : public ReadableFontData {
+ public:
+ explicit WritableFontData(ByteArray* ba);
+ virtual ~WritableFontData();
+
+ // Constructs a writable font data object. If the length is specified as
+ // positive then a fixed size font data object will be created. If the length
+ // is zero or less then a growable font data object will be created and the
+ // size will be used as an estimate to help in allocating the original space.
+ // @param length if length > 0 create a fixed length font data; otherwise
+ // create a growable font data
+ // @return a new writable font data
+ static CALLER_ATTACH WritableFontData* CreateWritableFontData(int32_t length);
+
+ // Constructs a writable font data object. The new font data object will wrap
+ // the bytes passed in to the factory and it will take make a copy of those
+ // bytes.
+ // @param b the byte vector to wrap
+ // @return a new writable font data
+ static CALLER_ATTACH WritableFontData* CreateWritableFontData(ByteVector* b);
+
+ // Write a byte at the given index.
+ // @param index index into the font data
+ // @param b the byte to write
+ // @return the number of bytes written
+ virtual int32_t WriteByte(int32_t index, byte_t b);
+
+ // Write the bytes from the array.
+ // @param index index into the font data
+ // @param b the source for the bytes to be written
+ // @param offset offset in the byte array
+ // @param length the length of the bytes to be written
+ // @return the number of bytes actually written; -1 if the index is outside
+ // the FontData's range
+ virtual int32_t WriteBytes(int32_t index,
+ byte_t* b,
+ int32_t offset,
+ int32_t length);
+
+ // Write the bytes from the array.
+ // @param index index into the font data
+ // @param b the source for the bytes to be written
+ // @return the number of bytes actually written; -1 if the index is outside
+ // the FontData's range
+ virtual int32_t WriteBytes(int32_t index, ByteVector* b);
+
+ // Write the bytes from the array and pad if necessary.
+ // Write to the length given using the byte array provided and if there are
+ // not enough bytes in the array then pad to the requested length using the
+ // pad byte specified.
+ // @param index index into the font data
+ // @param b the source for the bytes to be written
+ // @param offset offset in the byte array
+ // @param length the length of the bytes to be written
+ // @param pad the padding byte to be used if necessary
+ // @return the number of bytes actually written
+ virtual int32_t WriteBytesPad(int32_t index,
+ ByteVector* b,
+ int32_t offset,
+ int32_t length,
+ byte_t pad);
+
+ // Writes padding to the FontData. The padding byte written is 0x00.
+ // @param index index into the font data
+ // @param count the number of pad bytes to write
+ // @return the number of pad bytes written
+ virtual int32_t WritePadding(int32_t index, int32_t count);
+
+ // Writes padding to the FontData.
+ // @param index index into the font data
+ // @param count the number of pad bytes to write
+ // @param pad the byte value to use as padding
+ // @return the number of pad bytes written
+ virtual int32_t WritePadding(int32_t index, int32_t count, byte_t pad);
+
+ // Write the CHAR at the given index.
+ // @param index index into the font data
+ // @param c the CHAR
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteChar(int32_t index, byte_t c);
+
+ // Write the USHORT at the given index.
+ // @param index index into the font data
+ // @param us the USHORT
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteUShort(int32_t index, int32_t us);
+
+ // Write the USHORT at the given index in little endian format.
+ // @param index index into the font data
+ // @param us the USHORT
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteUShortLE(int32_t index, int32_t us);
+
+ // Write the SHORT at the given index.
+ // @param index index into the font data
+ // @param s the SHORT
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteShort(int32_t index, int32_t s);
+
+ // Write the UINT24 at the given index.
+ // @param index index into the font data
+ // @param ui the UINT24
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteUInt24(int32_t index, int32_t ui);
+
+ // Write the ULONG at the given index.
+ // @param index index into the font data
+ // @param ul the ULONG
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteULong(int32_t index, int64_t ul);
+
+ // Write the ULONG at the given index in little endian format.
+ // @param index index into the font data
+ // @param ul the ULONG
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteULongLE(int32_t index, int64_t ul);
+
+ // Write the LONG at the given index.
+ // @param index index into the font data
+ // @param l the LONG
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteLong(int32_t index, int64_t l);
+
+ // Write the Fixed at the given index.
+ // @param index index into the font data
+ // @param f the Fixed
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteFixed(int32_t index, int32_t f);
+
+ // Write the LONGDATETIME at the given index.
+ // @param index index into the font data
+ // @param date the LONGDATETIME
+ // @return the number of bytes actually written
+ // @throws IndexOutOfBoundsException if index is outside the FontData's range
+ virtual int32_t WriteDateTime(int32_t index, int64_t date);
+
+ // Copy from the InputStream into this FontData.
+ // @param is the source
+ // @param length the number of bytes to copy
+ // @throws IOException
+ virtual void CopyFrom(InputStream* is, int32_t length);
+
+ // Copy everything from the InputStream into this FontData.
+ // @param is the source
+ // @throws IOException
+ virtual void CopyFrom(InputStream* is);
+
+ // Makes a slice of this FontData. The returned slice will share the data with
+ // the original FontData.
+ // @param offset the start of the slice
+ // @param length the number of bytes in the slice
+ // @return a slice of the original FontData
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset, int32_t length);
+
+ // Makes a bottom bound only slice of this array. The returned slice will
+ // share the data with the original FontData.
+ // @param offset the start of the slice
+ // @return a slice of the original FontData
+ virtual CALLER_ATTACH FontData* Slice(int32_t offset);
+
+ private:
+ // Constructor with a lower bound.
+ // @param data other WritableFontData object to share data with
+ // @param offset offset from the other WritableFontData's data
+ WritableFontData(WritableFontData* data, int32_t offset);
+
+ // Constructor with lower bound and a length bound.
+ // @param data other WritableFontData object to share data with
+ // @param offset offset from the other WritableFontData's data
+ // @param length length of other WritableFontData's data to use
+ WritableFontData(WritableFontData* data, int32_t offset, int32_t length);
+};
+typedef Ptr<WritableFontData> WritableFontDataPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_DATA_WRITABLE_FONT_DATA_H_
diff --git a/sfntly/font.cc b/sfntly/font.cc
new file mode 100644
index 0000000..347e0c1
--- /dev/null
+++ b/sfntly/font.cc
@@ -0,0 +1,557 @@
+/*
+ * 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/font.h"
+
+#include <stdio.h>
+
+#include <functional>
+#include <algorithm>
+#include <map>
+#include <string>
+#include <typeinfo>
+#include <iterator>
+
+#include "sfntly/data/font_input_stream.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/math/fixed1616.h"
+#include "sfntly/math/font_math.h"
+#include "sfntly/port/exception_type.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/table/core/horizontal_device_metrics_table.h"
+#include "sfntly/table/core/horizontal_header_table.h"
+#include "sfntly/table/core/horizontal_metrics_table.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+const int32_t SFNTVERSION_MAJOR = 1;
+const int32_t SFNTVERSION_MINOR = 0;
+
+/******************************************************************************
+ * Font class
+ ******************************************************************************/
+Font::~Font() {}
+
+bool Font::HasTable(int32_t tag) {
+ TableMap::const_iterator result = tables_.find(tag);
+ TableMap::const_iterator end = tables_.end();
+ return (result != end);
+}
+
+Table* Font::GetTable(int32_t tag) {
+ if (!HasTable(tag)) {
+ return NULL;
+ }
+ return tables_[tag];
+}
+
+const TableMap* Font::GetTableMap() {
+ return &tables_;
+}
+
+void Font::Serialize(OutputStream* os, IntegerList* table_ordering) {
+ assert(table_ordering);
+ IntegerList final_table_ordering;
+ GenerateTableOrdering(table_ordering, &final_table_ordering);
+ TableHeaderList table_records;
+ BuildTableHeadersForSerialization(&final_table_ordering, &table_records);
+
+ FontOutputStream fos(os);
+ SerializeHeader(&fos, &table_records);
+ SerializeTables(&fos, &table_records);
+}
+
+Font::Font(int32_t sfnt_version, ByteVector* digest)
+ : sfnt_version_(sfnt_version) {
+ // non-trivial assignments that makes debugging hard if placed in
+ // initialization list
+ digest_ = *digest;
+}
+
+void Font::BuildTableHeadersForSerialization(IntegerList* table_ordering,
+ TableHeaderList* table_headers) {
+ assert(table_headers);
+ assert(table_ordering);
+
+ IntegerList final_table_ordering;
+ GenerateTableOrdering(table_ordering, &final_table_ordering);
+ int32_t table_offset = Offset::kTableRecordBegin + num_tables() *
+ Offset::kTableRecordSize;
+ for (IntegerList::iterator tag = final_table_ordering.begin(),
+ tag_end = final_table_ordering.end();
+ tag != tag_end; ++tag) {
+ if (tables_.find(*tag) == tables_.end()) {
+ continue;
+ }
+ TablePtr table = tables_[*tag];
+ if (table != NULL) {
+ HeaderPtr header =
+ new Header(*tag, table->CalculatedChecksum(), table_offset,
+ table->header()->length());
+ table_headers->push_back(header);
+ table_offset += (table->DataLength() + 3) & ~3;
+ }
+ }
+}
+
+void Font::SerializeHeader(FontOutputStream* fos,
+ TableHeaderList* table_headers) {
+ fos->WriteFixed(sfnt_version_);
+ fos->WriteUShort(table_headers->size());
+ int32_t log2_of_max_power_of_2 = FontMath::Log2(table_headers->size());
+ int32_t search_range = 2 << (log2_of_max_power_of_2 - 1 + 4);
+ fos->WriteUShort(search_range);
+ fos->WriteUShort(log2_of_max_power_of_2);
+ fos->WriteUShort((table_headers->size() * 16) - search_range);
+
+ HeaderTagSortedSet sorted_headers;
+ std::copy(table_headers->begin(),
+ table_headers->end(),
+ std::inserter(sorted_headers, sorted_headers.end()));
+
+ for (HeaderTagSortedSet::iterator record = sorted_headers.begin(),
+ record_end = sorted_headers.end();
+ record != record_end; ++record) {
+ fos->WriteULong((*record)->tag());
+ fos->WriteULong((int32_t)((*record)->checksum()));
+ fos->WriteULong((*record)->offset());
+ fos->WriteULong((*record)->length());
+ }
+}
+
+void Font::SerializeTables(FontOutputStream* fos,
+ TableHeaderList* table_headers) {
+ assert(fos);
+ assert(table_headers);
+ for (TableHeaderList::iterator record = table_headers->begin(),
+ end_of_headers = table_headers->end();
+ record != end_of_headers; ++record) {
+ TablePtr target_table = GetTable((*record)->tag());
+ if (target_table == NULL) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("Table out of sync with font header.");
+#endif
+ return;
+ }
+ int32_t table_size = target_table->Serialize(fos);
+ if (table_size != (*record)->length()) {
+ assert(false);
+ }
+ int32_t filler_size = ((table_size + 3) & ~3) - table_size;
+ for (int32_t i = 0; i < filler_size; ++i) {
+ fos->Write(static_cast<byte_t>(0));
+ }
+ }
+}
+
+void Font::GenerateTableOrdering(IntegerList* default_table_ordering,
+ IntegerList* table_ordering) {
+ assert(default_table_ordering);
+ assert(table_ordering);
+ table_ordering->clear();
+ if (default_table_ordering->empty()) {
+ DefaultTableOrdering(default_table_ordering);
+ }
+
+ typedef std::map<int32_t, bool> Int2Bool;
+ typedef std::pair<int32_t, bool> Int2BoolEntry;
+ Int2Bool tables_in_font;
+ for (TableMap::iterator table = tables_.begin(), table_end = tables_.end();
+ table != table_end; ++table) {
+ tables_in_font.insert(Int2BoolEntry(table->first, false));
+ }
+ for (IntegerList::iterator tag = default_table_ordering->begin(),
+ tag_end = default_table_ordering->end();
+ tag != tag_end; ++tag) {
+ if (HasTable(*tag)) {
+ table_ordering->push_back(*tag);
+ tables_in_font[*tag] = true;
+ }
+ }
+ for (Int2Bool::iterator table = tables_in_font.begin(),
+ table_end = tables_in_font.end();
+ table != table_end; ++table) {
+ if (table->second == false)
+ table_ordering->push_back(table->first);
+ }
+}
+
+void Font::DefaultTableOrdering(IntegerList* default_table_ordering) {
+ assert(default_table_ordering);
+ default_table_ordering->clear();
+ if (HasTable(Tag::CFF)) {
+ default_table_ordering->resize(CFF_TABLE_ORDERING_SIZE);
+ std::copy(CFF_TABLE_ORDERING, CFF_TABLE_ORDERING + CFF_TABLE_ORDERING_SIZE,
+ default_table_ordering->begin());
+ return;
+ }
+ default_table_ordering->resize(TRUE_TYPE_TABLE_ORDERING_SIZE);
+ std::copy(TRUE_TYPE_TABLE_ORDERING,
+ TRUE_TYPE_TABLE_ORDERING + TRUE_TYPE_TABLE_ORDERING_SIZE,
+ default_table_ordering->begin());
+}
+
+/******************************************************************************
+ * Font::Builder class
+ ******************************************************************************/
+Font::Builder::~Builder() {}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(FontFactory* factory,
+ InputStream* is) {
+ FontBuilderPtr builder = new Builder(factory);
+ builder->LoadFont(is);
+ return builder.Detach();
+}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
+ FontFactory* factory,
+ WritableFontData* wfd,
+ int32_t offset_to_offset_table) {
+ FontBuilderPtr builder = new Builder(factory);
+ builder->LoadFont(wfd, offset_to_offset_table);
+ return builder.Detach();
+}
+
+CALLER_ATTACH Font::Builder* Font::Builder::GetOTFBuilder(
+ FontFactory* factory) {
+ FontBuilderPtr builder = new Builder(factory);
+ return builder.Detach();
+}
+
+bool Font::Builder::ReadyToBuild() {
+ // just read in data with no manipulation
+ if (table_builders_.empty() && !data_blocks_.empty()) {
+ return true;
+ }
+
+ // TODO(stuartg): font level checks - required tables etc?
+ for (TableBuilderMap::iterator table_builder = table_builders_.begin(),
+ table_builder_end = table_builders_.end();
+ table_builder != table_builder_end;
+ ++table_builder) {
+ if (!table_builder->second->ReadyToBuild())
+ return false;
+ }
+ return true;
+}
+
+CALLER_ATTACH Font* Font::Builder::Build() {
+ FontPtr font = new Font(sfnt_version_, &digest_);
+
+ if (!table_builders_.empty()) {
+ // Note: Different from Java. Directly use font->tables_ here to avoid
+ // STL container copying.
+ BuildTablesFromBuilders(font, &table_builders_, &font->tables_);
+ }
+
+ table_builders_.clear();
+ data_blocks_.clear();
+ return font.Detach();
+}
+
+void Font::Builder::SetDigest(ByteVector* digest) {
+ digest_.clear();
+ digest_ = *digest;
+}
+
+void Font::Builder::ClearTableBuilders() {
+ table_builders_.clear();
+}
+
+bool Font::Builder::HasTableBuilder(int32_t tag) {
+ return (table_builders_.find(tag) != table_builders_.end());
+}
+
+Table::Builder* Font::Builder::GetTableBuilder(int32_t tag) {
+ if (HasTableBuilder(tag))
+ return table_builders_[tag];
+ return NULL;
+}
+
+Table::Builder* Font::Builder::NewTableBuilder(int32_t tag) {
+ HeaderPtr header = new Header(tag);
+ TableBuilderPtr builder;
+ builder.Attach(Table::Builder::GetBuilder(header, NULL));
+ table_builders_.insert(TableBuilderEntry(header->tag(), builder));
+ return builder;
+}
+
+Table::Builder* Font::Builder::NewTableBuilder(int32_t tag,
+ ReadableFontData* src_data) {
+ assert(src_data);
+ WritableFontDataPtr data;
+ data.Attach(WritableFontData::CreateWritableFontData(src_data->Length()));
+ // TODO(stuarg): take over original data instead?
+ src_data->CopyTo(data);
+
+ HeaderPtr header = new Header(tag, data->Length());
+ TableBuilderPtr builder;
+ builder.Attach(Table::Builder::GetBuilder(header, data));
+ table_builders_.insert(TableBuilderEntry(tag, builder));
+ return builder;
+}
+
+void Font::Builder::RemoveTableBuilder(int32_t tag) {
+ TableBuilderMap::iterator target = table_builders_.find(tag);
+ if (target != table_builders_.end()) {
+ table_builders_.erase(target);
+ }
+}
+
+Font::Builder::Builder(FontFactory* factory)
+ : factory_(factory),
+ sfnt_version_(Fixed1616::Fixed(SFNTVERSION_MAJOR, SFNTVERSION_MINOR)) {
+}
+
+void Font::Builder::LoadFont(InputStream* is) {
+ // Note: we do not throw exception here for is. This is more of an assertion.
+ assert(is);
+ FontInputStream font_is(is);
+ HeaderOffsetSortedSet records;
+ ReadHeader(&font_is, &records);
+ LoadTableData(&records, &font_is, &data_blocks_);
+ BuildAllTableBuilders(&data_blocks_, &table_builders_);
+ font_is.Close();
+}
+
+void Font::Builder::LoadFont(WritableFontData* wfd,
+ int32_t offset_to_offset_table) {
+ // Note: we do not throw exception here for is. This is more of an assertion.
+ assert(wfd);
+ HeaderOffsetSortedSet records;
+ ReadHeader(wfd, offset_to_offset_table, &records);
+ LoadTableData(&records, wfd, &data_blocks_);
+ BuildAllTableBuilders(&data_blocks_, &table_builders_);
+}
+
+int32_t Font::Builder::SfntWrapperSize() {
+ return Offset::kSfntHeaderSize +
+ (Offset::kTableRecordSize * table_builders_.size());
+}
+
+void Font::Builder::BuildAllTableBuilders(DataBlockMap* table_data,
+ TableBuilderMap* builder_map) {
+ for (DataBlockMap::iterator record = table_data->begin(),
+ record_end = table_data->end();
+ record != record_end; ++record) {
+ TableBuilderPtr builder;
+ builder.Attach(GetTableBuilder(record->first.p_, record->second.p_));
+ builder_map->insert(TableBuilderEntry(record->first->tag(), builder));
+ }
+ InterRelateBuilders(&table_builders_);
+}
+
+CALLER_ATTACH
+Table::Builder* Font::Builder::GetTableBuilder(Header* header,
+ WritableFontData* data) {
+ return Table::Builder::GetBuilder(header, data);
+}
+
+void Font::Builder::BuildTablesFromBuilders(Font* font,
+ TableBuilderMap* builder_map,
+ TableMap* table_map) {
+ UNREFERENCED_PARAMETER(font);
+ InterRelateBuilders(builder_map);
+
+ // Now build all the tables.
+ for (TableBuilderMap::iterator builder = builder_map->begin(),
+ builder_end = builder_map->end();
+ builder != builder_end; ++builder) {
+ TablePtr table;
+ if (builder->second && builder->second->ReadyToBuild()) {
+ table.Attach(down_cast<Table*>(builder->second->Build()));
+ }
+ if (table == NULL) {
+ table_map->clear();
+#if !defined (SFNTLY_NO_EXCEPTION)
+ std::string builder_string = "Unable to build table - ";
+ char* table_name = TagToString(builder->first);
+ builder_string += table_name;
+ delete[] table_name;
+ throw RuntimeException(builder_string.c_str());
+#endif
+ return;
+ }
+ table_map->insert(TableMapEntry(table->header()->tag(), table));
+ }
+}
+
+static Table::Builder* GetBuilder(TableBuilderMap* builder_map, int32_t tag) {
+ if (builder_map) {
+ TableBuilderMap::iterator target = builder_map->find(tag);
+ if (target != builder_map->end()) {
+ return target->second.p_;
+ }
+ }
+
+ return NULL;
+}
+
+void Font::Builder::InterRelateBuilders(TableBuilderMap* builder_map) {
+ Table::Builder* raw_head_builder = GetBuilder(builder_map, Tag::head);
+ FontHeaderTableBuilderPtr header_table_builder;
+ if (raw_head_builder != NULL) {
+ header_table_builder =
+ down_cast<FontHeaderTable::Builder*>(raw_head_builder);
+ }
+
+ Table::Builder* raw_hhea_builder = GetBuilder(builder_map, Tag::hhea);
+ HorizontalHeaderTableBuilderPtr horizontal_header_builder;
+ if (raw_head_builder != NULL) {
+ horizontal_header_builder =
+ down_cast<HorizontalHeaderTable::Builder*>(raw_hhea_builder);
+ }
+
+ Table::Builder* raw_maxp_builder = GetBuilder(builder_map, Tag::maxp);
+ MaximumProfileTableBuilderPtr max_profile_builder;
+ if (raw_maxp_builder != NULL) {
+ max_profile_builder =
+ down_cast<MaximumProfileTable::Builder*>(raw_maxp_builder);
+ }
+
+ Table::Builder* raw_loca_builder = GetBuilder(builder_map, Tag::loca);
+ LocaTableBuilderPtr loca_table_builder;
+ if (raw_loca_builder != NULL) {
+ loca_table_builder = down_cast<LocaTable::Builder*>(raw_loca_builder);
+ }
+
+ Table::Builder* raw_hmtx_builder = GetBuilder(builder_map, Tag::hmtx);
+ HorizontalMetricsTableBuilderPtr horizontal_metrics_builder;
+ if (raw_hmtx_builder != NULL) {
+ horizontal_metrics_builder =
+ down_cast<HorizontalMetricsTable::Builder*>(raw_hmtx_builder);
+ }
+
+#if defined (SFNTLY_EXPERIMENTAL)
+ Table::Builder* raw_hdmx_builder = GetBuilder(builder_map, Tag::hdmx);
+ HorizontalDeviceMetricsTableBuilderPtr hdmx_table_builder;
+ if (raw_hdmx_builder != NULL) {
+ hdmx_table_builder =
+ down_cast<HorizontalDeviceMetricsTable::Builder*>(raw_hdmx_builder);
+ }
+#endif
+
+ // set the inter table data required to build certain tables
+ if (horizontal_metrics_builder != NULL) {
+ if (max_profile_builder != NULL) {
+ horizontal_metrics_builder->SetNumGlyphs(
+ max_profile_builder->NumGlyphs());
+ }
+ if (horizontal_header_builder != NULL) {
+ horizontal_metrics_builder->SetNumberOfHMetrics(
+ horizontal_header_builder->NumberOfHMetrics());
+ }
+ }
+
+ if (loca_table_builder != NULL) {
+ if (max_profile_builder != NULL) {
+ loca_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
+ }
+ if (header_table_builder != NULL) {
+ loca_table_builder->set_format_version(
+ header_table_builder->IndexToLocFormat());
+ }
+ }
+
+#if defined (SFNTLY_EXPERIMENTAL)
+ // Note: In C++, hdmx_table_builder can be NULL in a subsetter.
+ if (max_profile_builder != NULL && hdmx_table_builder != NULL) {
+ hdmx_table_builder->SetNumGlyphs(max_profile_builder->NumGlyphs());
+ }
+#endif
+}
+
+void Font::Builder::ReadHeader(FontInputStream* is,
+ HeaderOffsetSortedSet* records) {
+ assert(records);
+ sfnt_version_ = is->ReadFixed();
+ num_tables_ = is->ReadUShort();
+ search_range_ = is->ReadUShort();
+ entry_selector_ = is->ReadUShort();
+ range_shift_ = is->ReadUShort();
+
+ for (int32_t table_number = 0; table_number < num_tables_; ++table_number) {
+ // Need to use temporary vars here. C++ evaluates function parameters from
+ // right to left and thus breaks the order of input stream.
+ int32_t tag = is->ReadULongAsInt();
+ int64_t checksum = is->ReadULong();
+ int32_t offset = is->ReadULongAsInt();
+ int32_t length = is->ReadULongAsInt();
+ HeaderPtr table = new Header(tag, checksum, offset, length);
+ records->insert(table);
+ }
+}
+
+void Font::Builder::ReadHeader(ReadableFontData* fd,
+ int32_t offset,
+ HeaderOffsetSortedSet* records) {
+ assert(records);
+ sfnt_version_ = fd->ReadFixed(offset + Offset::kSfntVersion);
+ num_tables_ = fd->ReadUShort(offset + Offset::kNumTables);
+ search_range_ = fd->ReadUShort(offset + Offset::kSearchRange);
+ entry_selector_ = fd->ReadUShort(offset + Offset::kEntrySelector);
+ range_shift_ = fd->ReadUShort(offset + Offset::kRangeShift);
+
+ int32_t table_offset = offset + Offset::kTableRecordBegin;
+ for (int32_t table_number = 0;
+ table_number < num_tables_;
+ table_number++, table_offset += Offset::kTableRecordSize) {
+ int32_t tag = fd->ReadULongAsInt(table_offset + Offset::kTableTag);
+ int64_t checksum = fd->ReadULong(table_offset + Offset::kTableCheckSum);
+ int32_t offset = fd->ReadULongAsInt(table_offset + Offset::kTableOffset);
+ int32_t length = fd->ReadULongAsInt(table_offset + Offset::kTableLength);
+ HeaderPtr table = new Header(tag, checksum, offset, length);
+ records->insert(table);
+ }
+}
+
+void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
+ FontInputStream* is,
+ DataBlockMap* table_data) {
+ assert(table_data);
+ for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
+ table_end = headers->end();
+ table_header != table_end;
+ ++table_header) {
+ is->Skip((*table_header)->offset() - is->position());
+ FontInputStream table_is(is, (*table_header)->length());
+ WritableFontDataPtr data;
+ data.Attach(
+ WritableFontData::CreateWritableFontData((*table_header)->length()));
+ data->CopyFrom(&table_is, (*table_header)->length());
+ table_data->insert(DataBlockEntry(*table_header, data));
+ }
+}
+
+void Font::Builder::LoadTableData(HeaderOffsetSortedSet* headers,
+ WritableFontData* fd,
+ DataBlockMap* table_data) {
+ for (HeaderOffsetSortedSet::iterator table_header = headers->begin(),
+ table_end = headers->end();
+ table_header != table_end;
+ ++table_header) {
+ FontDataPtr sliced_data;
+ sliced_data.Attach(
+ fd->Slice((*table_header)->offset(), (*table_header)->length()));
+ WritableFontDataPtr data = down_cast<WritableFontData*>(sliced_data.p_);
+ table_data->insert(DataBlockEntry(*table_header, data));
+ }
+}
+
+} // namespace sfntly
diff --git a/sfntly/font.h b/sfntly/font.h
new file mode 100644
index 0000000..975e8cc
--- /dev/null
+++ b/sfntly/font.h
@@ -0,0 +1,352 @@
+/*
+ * 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_SFNTLY_FONT_H_
+#define SFNTLY_CPP_SRC_SFNTLY_FONT_H_
+
+#include <vector>
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/endian.h"
+#include "sfntly/data/font_input_stream.h"
+#include "sfntly/data/font_output_stream.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/table/table.h"
+
+namespace sfntly {
+
+// Note: following constants are embedded in Font class in Java. They are
+// extracted out for easier reference from other classes. Offset is the
+// one that is kept within class.
+// Platform ids. These are used in a number of places within the font whenever
+// the platform needs to be specified.
+struct PlatformId {
+ enum {
+ kUnknown = -1,
+ kUnicode = 0,
+ kMacintosh = 1,
+ kISO = 2,
+ kWindows = 3,
+ kCustom = 4
+ };
+};
+
+// Unicode encoding ids. These are used in a number of places within the font
+// whenever character encodings need to be specified.
+struct UnicodeEncodingId {
+ enum {
+ kUnknown = -1,
+ kUnicode1_0 = 0,
+ kUnicode1_1 = 1,
+ kISO10646 = 2,
+ kUnicode2_0_BMP = 3,
+ kUnicode2_0 = 4,
+ kUnicodeVariationSequences = 5
+ };
+};
+
+// Windows encoding ids. These are used in a number of places within the font
+// whenever character encodings need to be specified.
+struct WindowsEncodingId {
+ enum {
+ kUnknown = 0xffffffff,
+ kSymbol = 0,
+ kUnicodeUCS2 = 1,
+ kShiftJIS = 2,
+ kPRC = 3,
+ kBig5 = 4,
+ kWansung = 5,
+ kJohab = 6,
+ kUnicodeUCS4 = 10
+ };
+};
+
+// Macintosh encoding ids. These are used in a number of places within the
+// font whenever character encodings need to be specified.
+struct MacintoshEncodingId {
+ // Macintosh Platform Encodings
+ enum {
+ kUnknown = -1,
+ kRoman = 0,
+ kJapanese = 1,
+ kChineseTraditional = 2,
+ kKorean = 3,
+ kArabic = 4,
+ kHebrew = 5,
+ kGreek = 6,
+ kRussian = 7,
+ kRSymbol = 8,
+ kDevanagari = 9,
+ kGurmukhi = 10,
+ kGujarati = 11,
+ kOriya = 12,
+ kBengali = 13,
+ kTamil = 14,
+ kTelugu = 15,
+ kKannada = 16,
+ kMalayalam = 17,
+ kSinhalese = 18,
+ kBurmese = 19,
+ kKhmer = 20,
+ kThai = 21,
+ kLaotian = 22,
+ kGeorgian = 23,
+ kArmenian = 24,
+ kChineseSimplified = 25,
+ kTibetan = 26,
+ kMongolian = 27,
+ kGeez = 28,
+ kSlavic = 29,
+ kVietnamese = 30,
+ kSindhi = 31,
+ kUninterpreted = 32
+ };
+};
+
+class FontFactory;
+
+// An sfnt container font object. This object is immutable and thread safe. To
+// construct one use an instance of Font::Builder.
+class Font : public RefCounted<Font> {
+ public:
+ // A builder for a font object. The builder allows the for the creation of
+ // immutable Font objects. The builder is a one use non-thread safe object and
+ // once the Font object has been created it is no longer usable. To create a
+ // further Font object new builder will be required.
+ class Builder : public RefCounted<Builder> {
+ public:
+ virtual ~Builder();
+
+ static CALLER_ATTACH Builder*
+ GetOTFBuilder(FontFactory* factory, InputStream* is);
+ static CALLER_ATTACH Builder*
+ GetOTFBuilder(FontFactory* factory,
+ WritableFontData* ba,
+ int32_t offset_to_offset_table);
+ static CALLER_ATTACH Builder* GetOTFBuilder(FontFactory* factory);
+
+ // Get the font factory that created this font builder.
+ FontFactory* GetFontFactory() { return factory_; }
+
+ // Is the font ready to build?
+ bool ReadyToBuild();
+
+ // Build the Font. After this call this builder will no longer be usable.
+ CALLER_ATTACH Font* Build();
+
+ // Set a unique fingerprint for the font object.
+ void SetDigest(ByteVector* digest);
+
+ // Clear all table builders.
+ void ClearTableBuilders();
+
+ // Does this font builder have the specified table builder.
+ bool HasTableBuilder(int32_t tag);
+
+ // Get the table builder for the given tag. If there is no builder for that
+ // tag then return a null.
+ Table::Builder* GetTableBuilder(int32_t tag);
+
+ // Creates a new table builder for the table type given by the table id tag.
+ // This new table has been added to the font and will replace any existing
+ // builder for that table.
+ // @return new empty table of the type specified by tag; if tag is not known
+ // then a generic OpenTypeTable is returned
+ virtual Table::Builder* NewTableBuilder(int32_t tag);
+
+ // Creates a new table builder for the table type given by the table id tag.
+ // It makes a copy of the data provided and uses that copy for the table.
+ // This new table has been added to the font and will replace any existing
+ // builder for that table.
+ virtual Table::Builder* NewTableBuilder(int32_t tag,
+ ReadableFontData* src_data);
+
+ // Get a map of the table builders in this font builder accessed by table
+ // tag.
+ virtual TableBuilderMap* table_builders() { return &table_builders_; }
+
+ // Remove the specified table builder from the font builder.
+ // Note: different from Java: we don't return object in removeTableBuilder
+ virtual void RemoveTableBuilder(int32_t tag);
+
+ // Get the number of table builders in the font builder.
+ virtual int32_t number_of_table_builders() {
+ return (int32_t)table_builders_.size();
+ }
+
+ private:
+ explicit Builder(FontFactory* factory);
+ virtual void LoadFont(InputStream* is);
+ virtual void LoadFont(WritableFontData* wfd,
+ int32_t offset_to_offset_table);
+ int32_t SfntWrapperSize();
+ void BuildAllTableBuilders(DataBlockMap* table_data,
+ TableBuilderMap* builder_map);
+ CALLER_ATTACH Table::Builder*
+ GetTableBuilder(Header* header, WritableFontData* data);
+ void BuildTablesFromBuilders(Font* font,
+ TableBuilderMap* builder_map,
+ TableMap* tables);
+ static void InterRelateBuilders(TableBuilderMap* builder_map);
+
+ void ReadHeader(FontInputStream* is,
+ HeaderOffsetSortedSet* records);
+
+ void ReadHeader(ReadableFontData* fd,
+ int32_t offset,
+ HeaderOffsetSortedSet* records);
+
+ void LoadTableData(HeaderOffsetSortedSet* headers,
+ FontInputStream* is,
+ DataBlockMap* table_data);
+
+ void LoadTableData(HeaderOffsetSortedSet* headers,
+ WritableFontData* fd,
+ DataBlockMap* table_data);
+
+ TableBuilderMap table_builders_;
+ FontFactory* factory_; // dumb pointer, avoid circular refcounting
+ int32_t sfnt_version_;
+ int32_t num_tables_;
+ int32_t search_range_;
+ int32_t entry_selector_;
+ int32_t range_shift_;
+ DataBlockMap data_blocks_;
+ ByteVector digest_;
+ };
+
+ virtual ~Font();
+
+ // Gets the sfnt version set in the sfnt wrapper of the font.
+ int32_t sfnt_version() { return sfnt_version_; }
+
+ // Gets a copy of the fonts digest that was created when the font was read. If
+ // no digest was set at creation time then the return result will be null.
+ ByteVector* digest() { return &digest_; }
+
+ // Get the checksum for this font.
+ int64_t checksum() { return checksum_; }
+
+ // Get the number of tables in this font.
+ int32_t num_tables() { return (int32_t)tables_.size(); }
+
+ // Whether the font has a particular table.
+ bool HasTable(int32_t tag);
+
+ // UNIMPLEMENTED: public Iterator<? extends Table> iterator
+
+ // Get the table in this font with the specified id.
+ // @param tag the identifier of the table
+ // @return the table specified if it exists; null otherwise
+ // C++ port: rename table() to GetTable()
+ Table* GetTable(int32_t tag);
+
+ // Get a map of the tables in this font accessed by table tag.
+ // @return an unmodifiable view of the tables in this font
+ // Note: renamed tableMap() to GetTableMap()
+ const TableMap* GetTableMap();
+
+ // UNIMPLEMENTED: toString()
+
+ // Serialize the font to the output stream.
+ // @param os the destination for the font serialization
+ // @param tableOrdering the table ordering to apply
+ void Serialize(OutputStream* os, IntegerList* table_ordering);
+
+ private:
+ // Offsets to specific elements in the underlying data. These offsets are
+ // relative to the start of the table or the start of sub-blocks within the
+ // table.
+ struct Offset {
+ enum {
+ // Offsets within the main directory
+ kSfntVersion = 0,
+ kNumTables = 4,
+ kSearchRange = 6,
+ kEntrySelector = 8,
+ kRangeShift = 10,
+ kTableRecordBegin = 12,
+ kSfntHeaderSize = 12,
+
+ // Offsets within a specific table record
+ kTableTag = 0,
+ kTableCheckSum = 4,
+ kTableOffset = 8,
+ kTableLength = 12,
+ kTableRecordSize = 16
+ };
+ };
+
+ // Note: the two constants are moved to tag.h to avoid VC++ bug.
+// static const int32_t CFF_TABLE_ORDERING[];
+// static const int32_t TRUE_TYPE_TABLE_ORDERING[];
+
+ // Constructor.
+ // @param sfntVersion the sfnt version
+ // @param digest the computed digest for the font; null if digest was not
+ // computed
+ // Note: Current C++ port does not support SHA digest validation.
+ Font(int32_t sfnt_version, ByteVector* digest);
+
+ // Build the table headers to be used for serialization. These headers will be
+ // filled out with the data required for serialization. The headers will be
+ // sorted in the order specified and only those specified will have headers
+ // generated.
+ // @param tableOrdering the tables to generate headers for and the order to
+ // sort them
+ // @return a list of table headers ready for serialization
+ void BuildTableHeadersForSerialization(IntegerList* table_ordering,
+ TableHeaderList* table_headers);
+
+ // Searialize the headers.
+ // @param fos the destination stream for the headers
+ // @param tableHeaders the headers to serialize
+ // @throws IOException
+ void SerializeHeader(FontOutputStream* fos, TableHeaderList* table_headers);
+
+ // Serialize the tables.
+ // @param fos the destination stream for the headers
+ // @param tableHeaders the headers for the tables to serialize
+ // @throws IOException
+ void SerializeTables(FontOutputStream* fos, TableHeaderList* table_headers);
+
+ // Generate the full table ordering to used for serialization. The full
+ // ordering uses the partial ordering as a seed and then adds all remaining
+ // tables in the font in an undefined order.
+ // @param defaultTableOrdering the partial ordering to be used as a seed for
+ // the full ordering
+ // @param (out) table_ordering the full ordering for serialization
+ void GenerateTableOrdering(IntegerList* default_table_ordering,
+ IntegerList* table_ordering);
+
+ // Get the default table ordering based on the type of the font.
+ // @param (out) default_table_ordering the default table ordering
+ void DefaultTableOrdering(IntegerList* default_table_ordering);
+
+ int32_t sfnt_version_;
+ ByteVector digest_;
+ int64_t checksum_;
+ TableMap tables_;
+};
+typedef Ptr<Font> FontPtr;
+typedef std::vector<FontPtr> FontArray;
+typedef Ptr<Font::Builder> FontBuilderPtr;
+typedef std::vector<FontBuilderPtr> FontBuilderArray;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_H_
diff --git a/sfntly/font_factory.cc b/sfntly/font_factory.cc
new file mode 100644
index 0000000..c162a77
--- /dev/null
+++ b/sfntly/font_factory.cc
@@ -0,0 +1,214 @@
+/*
+ * 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/font_factory.h"
+
+#include <string.h>
+
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+FontFactory::~FontFactory() {
+}
+
+CALLER_ATTACH FontFactory* FontFactory::GetInstance() {
+ FontFactoryPtr instance = new FontFactory();
+ return instance.Detach();
+}
+
+void FontFactory::FingerprintFont(bool fingerprint) {
+ fingerprint_ = fingerprint;
+}
+
+bool FontFactory::FingerprintFont() {
+ return fingerprint_;
+}
+
+void FontFactory::LoadFonts(InputStream* is, FontArray* output) {
+ assert(output);
+ PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
+ if (IsCollection(pbis)) {
+ LoadCollection(pbis, output);
+ return;
+ }
+ FontPtr font;
+ font.Attach(LoadSingleOTF(pbis));
+ if (font) {
+ output->push_back(font);
+ }
+}
+
+void FontFactory::LoadFonts(ByteVector* b, FontArray* output) {
+ WritableFontDataPtr wfd;
+ wfd.Attach(WritableFontData::CreateWritableFontData(b));
+ if (IsCollection(wfd)) {
+ LoadCollection(wfd, output);
+ return;
+ }
+ FontPtr font;
+ font.Attach(LoadSingleOTF(wfd));
+ if (font) {
+ output->push_back(font);
+ }
+}
+
+void FontFactory::LoadFontsForBuilding(InputStream* is,
+ FontBuilderArray* output) {
+ PushbackInputStream* pbis = down_cast<PushbackInputStream*>(is);
+ if (IsCollection(pbis)) {
+ LoadCollectionForBuilding(pbis, output);
+ return;
+ }
+ FontBuilderPtr builder;
+ builder.Attach(LoadSingleOTFForBuilding(pbis));
+ if (builder) {
+ output->push_back(builder);
+ }
+}
+
+void FontFactory::LoadFontsForBuilding(ByteVector* b,
+ FontBuilderArray* output) {
+ WritableFontDataPtr wfd;
+ wfd.Attach(WritableFontData::CreateWritableFontData(b));
+ if (IsCollection(wfd)) {
+ LoadCollectionForBuilding(wfd, output);
+ return;
+ }
+ FontBuilderPtr builder;
+ builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
+ if (builder) {
+ output->push_back(builder);
+ }
+}
+
+void FontFactory::SerializeFont(Font* font, OutputStream* os) {
+ font->Serialize(os, &table_ordering_);
+}
+
+void FontFactory::SetSerializationTableOrdering(
+ const IntegerList& table_ordering) {
+ table_ordering_ = table_ordering;
+}
+
+CALLER_ATTACH Font::Builder* FontFactory::NewFontBuilder() {
+ return Font::Builder::GetOTFBuilder(this);
+}
+
+CALLER_ATTACH Font* FontFactory::LoadSingleOTF(InputStream* is) {
+ FontBuilderPtr builder;
+ builder.Attach(LoadSingleOTFForBuilding(is));
+ return builder->Build();
+}
+
+CALLER_ATTACH Font* FontFactory::LoadSingleOTF(WritableFontData* wfd) {
+ FontBuilderPtr builder;
+ builder.Attach(LoadSingleOTFForBuilding(wfd, 0));
+ return builder->Build();
+}
+
+void FontFactory::LoadCollection(InputStream* is, FontArray* output) {
+ FontBuilderArray ba;
+ LoadCollectionForBuilding(is, &ba);
+ output->reserve(ba.size());
+ for (FontBuilderArray::iterator builder = ba.begin(), builders_end = ba.end();
+ builder != builders_end; ++builder) {
+ FontPtr font;
+ font.Attach((*builder)->Build());
+ output->push_back(font);
+ }
+}
+
+void FontFactory::LoadCollection(WritableFontData* wfd, FontArray* output) {
+ FontBuilderArray builders;
+ LoadCollectionForBuilding(wfd, &builders);
+ output->reserve(builders.size());
+ for (FontBuilderArray::iterator builder = builders.begin(),
+ builders_end = builders.end();
+ builder != builders_end; ++builder) {
+ FontPtr font;
+ font.Attach((*builder)->Build());
+ output->push_back(font);
+ }
+}
+
+CALLER_ATTACH
+Font::Builder* FontFactory::LoadSingleOTFForBuilding(InputStream* is) {
+ // UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
+ Font::Builder* builder = Font::Builder::GetOTFBuilder(this, is);
+ // UNIMPLEMENTED: setDigest
+ return builder;
+}
+
+CALLER_ATTACH Font::Builder*
+ FontFactory::LoadSingleOTFForBuilding(WritableFontData* wfd,
+ int32_t offset_to_offset_table) {
+ // UNIMPLEMENTED: SHA-1 hash checking via Java DigestStream
+ Font::Builder* builder =
+ Font::Builder::GetOTFBuilder(this, wfd, offset_to_offset_table);
+ // UNIMPLEMENTED: setDigest
+ return builder;
+}
+
+void FontFactory::LoadCollectionForBuilding(InputStream* is,
+ FontBuilderArray* builders) {
+ assert(is);
+ assert(builders);
+ WritableFontDataPtr wfd;
+ wfd.Attach(WritableFontData::CreateWritableFontData(is->Available()));
+ wfd->CopyFrom(is);
+ LoadCollectionForBuilding(wfd, builders);
+}
+
+void FontFactory::LoadCollectionForBuilding(WritableFontData* wfd,
+ FontBuilderArray* builders) {
+ int32_t ttc_tag = wfd->ReadULongAsInt(Offset::kTTCTag);
+ UNREFERENCED_PARAMETER(ttc_tag);
+ int32_t version = wfd->ReadFixed(Offset::kVersion);
+ UNREFERENCED_PARAMETER(version);
+ int32_t num_fonts = wfd->ReadULongAsInt(Offset::kNumFonts);
+
+ builders->reserve(num_fonts);
+ int32_t offset_table_offset = Offset::kOffsetTable;
+ for (int32_t font_number = 0;
+ font_number < num_fonts;
+ font_number++, offset_table_offset += DataSize::kULONG) {
+ int32_t offset = wfd->ReadULongAsInt(offset_table_offset);
+ FontBuilderPtr builder;
+ builder.Attach(LoadSingleOTFForBuilding(wfd, offset));
+ builders->push_back(builder);
+ }
+}
+
+bool FontFactory::IsCollection(PushbackInputStream* pbis) {
+ ByteVector tag(4);
+ pbis->Read(&tag);
+ pbis->Unread(&tag);
+ return Tag::ttcf == GenerateTag(tag[0], tag[1], tag[2], tag[3]);
+}
+
+bool FontFactory::IsCollection(ReadableFontData* rfd) {
+ ByteVector tag(4);
+ rfd->ReadBytes(0, &(tag[0]), 0, tag.size());
+ return Tag::ttcf ==
+ GenerateTag(tag[0], tag[1], tag[2], tag[3]);
+}
+
+FontFactory::FontFactory()
+ : fingerprint_(false) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/font_factory.h b/sfntly/font_factory.h
new file mode 100644
index 0000000..63deff4
--- /dev/null
+++ b/sfntly/font_factory.h
@@ -0,0 +1,140 @@
+/*
+ * 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_SFNTLY_FONT_FACTORY_H_
+#define SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
+
+#include <vector>
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/port/type.h"
+#include "sfntly/font.h"
+
+namespace sfntly {
+
+class FontFactory : public RefCounted<FontFactory> {
+ public:
+ virtual ~FontFactory();
+
+ // Factory method for the construction of a font factory.
+ static CALLER_ATTACH FontFactory* GetInstance();
+
+ // Toggle whether fonts that are loaded are fingerprinted with a SHA-1 hash.
+ // If a font is fingerprinted then a SHA-1 hash is generated at load time and
+ // stored in the font. This is useful for uniquely identifying fonts. By
+ // default this is turned on.
+ // @param fingerprint whether fingerprinting should be turned on or off
+ // TODO(arthurhsu): IMPLEMENT: C++ port currently don't do any SHA-1
+ void FingerprintFont(bool fingerprint);
+ bool FingerprintFont();
+
+ // Load the font(s) from the input stream. The current settings on the factory
+ // are used during the loading process. One or more fonts are returned if the
+ // stream contains valid font data. Some font container formats may have more
+ // than one font and in this case multiple font objects will be returned. If
+ // the data in the stream cannot be parsed or is invalid an array of size zero
+ // will be returned.
+ void LoadFonts(InputStream* is, FontArray* output);
+
+ // ByteArray font loading
+ // Load the font(s) from the byte array. The current settings on the factory
+ // are used during the loading process. One or more fonts are returned if the
+ // stream contains valid font data. Some font container formats may have more
+ // than one font and in this case multiple font objects will be returned. If
+ // the data in the stream cannot be parsed or is invalid an array of size zero
+ // will be returned.
+ void LoadFonts(ByteVector* b, FontArray* output);
+
+ // Load the font(s) from the input stream into font builders. The current
+ // settings on the factory are used during the loading process. One or more
+ // font builders are returned if the stream contains valid font data. Some
+ // font container formats may have more than one font and in this case
+ // multiple font builder objects will be returned. If the data in the stream
+ // cannot be parsed or is invalid an array of size zero will be returned.
+ void LoadFontsForBuilding(InputStream* is, FontBuilderArray* output);
+
+ // Load the font(s) from the byte array into font builders. The current
+ // settings on the factory are used during the loading process. One or more
+ // font builders are returned if the stream contains valid font data. Some
+ // font container formats may have more than one font and in this case
+ // multiple font builder objects will be returned. If the data in the stream
+ // cannot be parsed or is invalid an array of size zero will be returned.
+ void LoadFontsForBuilding(ByteVector* b, FontBuilderArray* output);
+
+ // Font serialization
+ // Serialize the font to the output stream.
+ // NOTE: in this port we attempted not to implement I/O stream because dealing
+ // with cross-platform I/O stream itself is big enough as a project.
+ // Byte buffer it is.
+ void SerializeFont(Font* font, OutputStream* os);
+
+ // Set the table ordering to be used in serializing a font. The table ordering
+ // is an ordered list of table ids and tables will be serialized in the order
+ // given. Any tables whose id is not listed in the ordering will be placed in
+ // an unspecified order following those listed.
+ void SetSerializationTableOrdering(const IntegerList& table_ordering);
+
+ // Get an empty font builder for creating a new font from scratch.
+ CALLER_ATTACH Font::Builder* NewFontBuilder();
+
+ private:
+ // Offsets to specific elements in the underlying data. These offsets are
+ // relative to the start of the table or the start of sub-blocks within the
+ // table.
+ struct Offset {
+ enum {
+ // Offsets within the main directory.
+ kTTCTag = 0,
+ kVersion = 4,
+ kNumFonts = 8,
+ kOffsetTable = 12,
+
+ // TTC Version 2.0 extensions.
+ // Offsets from end of OffsetTable.
+ kulDsigTag = 0,
+ kulDsigLength = 4,
+ kulDsigOffset = 8
+ };
+ };
+
+ FontFactory();
+
+ CALLER_ATTACH Font* LoadSingleOTF(InputStream* is);
+ CALLER_ATTACH Font* LoadSingleOTF(WritableFontData* wfd);
+
+ void LoadCollection(InputStream* is, FontArray* output);
+ void LoadCollection(WritableFontData* wfd, FontArray* output);
+
+ CALLER_ATTACH Font::Builder* LoadSingleOTFForBuilding(InputStream* is);
+ CALLER_ATTACH Font::Builder*
+ LoadSingleOTFForBuilding(WritableFontData* wfd,
+ int32_t offset_to_offset_table);
+
+ void LoadCollectionForBuilding(InputStream* is, FontBuilderArray* builders);
+ void LoadCollectionForBuilding(WritableFontData* ba,
+ FontBuilderArray* builders);
+
+ static bool IsCollection(PushbackInputStream* pbis);
+ static bool IsCollection(ReadableFontData* wfd);
+
+ bool fingerprint_;
+ IntegerList table_ordering_;
+};
+typedef Ptr<FontFactory> FontFactoryPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_FONT_FACTORY_H_
diff --git a/sfntly/math/fixed1616.h b/sfntly/math/fixed1616.h
new file mode 100644
index 0000000..4abbe18
--- /dev/null
+++ b/sfntly/math/fixed1616.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 SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
+#define SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+class Fixed1616 {
+ public:
+ static inline int32_t Integral(int32_t fixed) {
+ return (fixed >> 16);
+ }
+
+ static inline int32_t Fractional(int32_t fixed) {
+ return (fixed & 0xffff);
+ }
+
+ static inline int32_t Fixed(int32_t integral, int32_t fractional) {
+ return ((integral & 0xffff) << 16) | (fractional & 0xffff);
+ }
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_MATH_FIXED1616_H_
diff --git a/sfntly/math/font_math.h b/sfntly/math/font_math.h
new file mode 100644
index 0000000..f1cd2d2
--- /dev/null
+++ b/sfntly/math/font_math.h
@@ -0,0 +1,49 @@
+/*
+ * 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_SFNTLY_MATH_FONT_MATH_H_
+#define SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+class FontMath {
+ public:
+ static int32_t Log2(int32_t a) {
+ int r = 0; // r will be lg(a)
+ while (a != 0) {
+ a >>= 1;
+ r++;
+ }
+ return r - 1;
+ }
+
+ // Calculates the amount of padding needed. The values provided need to be in
+ // the same units. So, if the size is given as the number of bytes then the
+ // alignment size must also be specified as byte size to align to.
+ // @param size the size of the data that may need padding
+ // @param alignmentSize the number of units to align to
+ // @return the number of units needing to be added for alignment
+ static int32_t PaddingRequired(int32_t size, int32_t alignment_size) {
+ int32_t padding = alignment_size - (size % alignment_size);
+ return padding == alignment_size ? 0 : padding;
+ }
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_MATH_FONT_MATH_H_
diff --git a/sfntly/port/atomic.h b/sfntly/port/atomic.h
new file mode 100644
index 0000000..b381a52
--- /dev/null
+++ b/sfntly/port/atomic.h
@@ -0,0 +1,71 @@
+/*
+ * 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_SFNTLY_PORT_ATOMIC_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
+
+#if defined (WIN32)
+
+#include <windows.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+#if defined (_WIN64)
+ return InterlockedIncrement64(reinterpret_cast<LONGLONG*>(address));
+#else
+ return InterlockedIncrement(reinterpret_cast<LONG*>(address));
+#endif
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+#if defined (_WIN64)
+ return InterlockedDecrement64(reinterpret_cast<LONGLONG*>(address));
+#else
+ return InterlockedDecrement(reinterpret_cast<LONG*>(address));
+#endif
+}
+
+#elif defined (__APPLE__)
+
+#include <libkern/OSAtomic.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+ return OSAtomicIncrement32Barrier(reinterpret_cast<int32_t*>(address));
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+ return OSAtomicDecrement32Barrier(reinterpret_cast<int32_t*>(address));
+}
+
+// Originally we check __GCC_HAVE_SYNC_COMPARE_AND_SWAP_4, however, there are
+// issues that clang not carring over this definition. Therefore we boldly
+// assume it's gcc or gcc-compatible here. Compilation shall still fail since
+// the intrinsics used are GCC-specific.
+
+#else
+
+#include <stddef.h>
+
+static inline size_t AtomicIncrement(size_t* address) {
+ return __sync_add_and_fetch(address, 1);
+}
+
+static inline size_t AtomicDecrement(size_t* address) {
+ return __sync_sub_and_fetch(address, 1);
+}
+
+#endif // WIN32
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_ATOMIC_H_
diff --git a/sfntly/port/config.h b/sfntly/port/config.h
new file mode 100644
index 0000000..0fcdffe
--- /dev/null
+++ b/sfntly/port/config.h
@@ -0,0 +1,28 @@
+/*
+ * 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_SFNTLY_PORT_CONFIG_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
+
+#if !defined(SFNTLY_BIG_ENDIAN) && !defined(SFNTLY_LITTLE_ENDIAN)
+ #if defined (__ppc__) || defined (__ppc64__)
+ #define SFNTLY_BIG_ENDIAN
+ #else
+ #define SFNTLY_LITTLE_ENDIAN
+ #endif
+#endif
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_CONFIG_H_
diff --git a/sfntly/port/endian.h b/sfntly/port/endian.h
new file mode 100644
index 0000000..db58f0a
--- /dev/null
+++ b/sfntly/port/endian.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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
+
+#include "sfntly/port/config.h"
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+static inline uint16_t EndianSwap16(uint16_t value) {
+ return (uint16_t)((value >> 8) | (value << 8));
+}
+
+static inline int32_t EndianSwap32(int32_t value) {
+ return (((value & 0x000000ff) << 24) |
+ ((value & 0x0000ff00) << 8) |
+ ((value & 0x00ff0000) >> 8) |
+ ((value & 0xff000000) >> 24));
+}
+
+static inline uint64_t EndianSwap64(uint64_t value) {
+ return (((value & 0x00000000000000ffLL) << 56) |
+ ((value & 0x000000000000ff00LL) << 40) |
+ ((value & 0x0000000000ff0000LL) << 24) |
+ ((value & 0x00000000ff000000LL) << 8) |
+ ((value & 0x000000ff00000000LL) >> 8) |
+ ((value & 0x0000ff0000000000LL) >> 24) |
+ ((value & 0x00ff000000000000LL) >> 40) |
+ ((value & 0xff00000000000000LL) >> 56));
+}
+
+#ifdef SFNTLY_LITTLE_ENDIAN
+ #define ToBE16(n) EndianSwap16(n)
+ #define ToBE32(n) EndianSwap32(n)
+ #define ToBE64(n) EndianSwap64(n)
+ #define ToLE16(n) (n)
+ #define ToLE32(n) (n)
+ #define ToLE64(n) (n)
+ #define FromBE16(n) EndianSwap16(n)
+ #define FromBE32(n) EndianSwap32(n)
+ #define FromBE64(n) EndianSwap64(n)
+ #define FromLE16(n) (n)
+ #define FromLE32(n) (n)
+ #define FromLE64(n) (n)
+#else // SFNTLY_BIG_ENDIAN
+ #define ToBE16(n) (n)
+ #define ToBE32(n) (n)
+ #define ToBE64(n) (n)
+ #define ToLE16(n) EndianSwap16(n)
+ #define ToLE32(n) EndianSwap32(n)
+ #define ToLE64(n) EndianSwap64(n)
+ #define FromBE16(n) (n)
+ #define FromBE32(n) (n)
+ #define FromBE64(n) (n)
+ #define FromLE16(n) EndianSwap16(n)
+ #define FromLE32(n) EndianSwap32(n)
+ #define FromLE64(n) EndianSwap64(n)
+#endif
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_ENDIAN_H_
diff --git a/sfntly/port/exception_type.h b/sfntly/port/exception_type.h
new file mode 100644
index 0000000..b96efcb
--- /dev/null
+++ b/sfntly/port/exception_type.h
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+
+// Exceptions used in sfntly
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_
+
+#if !defined (SFNTLY_NO_EXCEPTION)
+
+#include <exception>
+#include <string>
+#include <sstream>
+
+namespace sfntly {
+
+class Exception : public std::exception {
+ public:
+ Exception() : what_("Unknown exception") {}
+ explicit Exception(const char* message) throw() { SetMessage(message); }
+ virtual ~Exception() throw() {}
+ virtual const char* what() const throw() { return what_.c_str(); }
+
+ protected:
+ void SetMessage(const char* message) throw() {
+ try {
+ what_ = message;
+ } catch (...) {}
+ }
+
+ private:
+ std::string what_;
+};
+
+class IndexOutOfBoundException : public Exception {
+ public:
+ IndexOutOfBoundException() throw() : Exception("Index out of bound") {}
+ explicit IndexOutOfBoundException(const char* message) throw()
+ : Exception(message) {}
+ IndexOutOfBoundException(const char* message, int32_t index) throw() {
+ try {
+ std::ostringstream msg;
+ msg << message;
+ msg << ":";
+ msg << index;
+ SetMessage(msg.str().c_str());
+ } catch (...) {}
+ }
+ virtual ~IndexOutOfBoundException() throw() {}
+};
+
+class IOException : public Exception {
+ public:
+ IOException() throw() : Exception("I/O exception") {}
+ explicit IOException(const char* message) throw() : Exception(message) {}
+ virtual ~IOException() throw() {}
+};
+
+class ArithmeticException : public Exception {
+ public:
+ ArithmeticException() throw() : Exception("Arithmetic exception") {}
+ explicit ArithmeticException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~ArithmeticException() throw() {}
+};
+
+class UnsupportedOperationException : public Exception {
+ public:
+ UnsupportedOperationException() throw() :
+ Exception("Operation not supported") {}
+ explicit UnsupportedOperationException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~UnsupportedOperationException() throw() {}
+};
+
+class RuntimeException : public Exception {
+ public:
+ RuntimeException() throw() : Exception("Runtime exception") {}
+ explicit RuntimeException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~RuntimeException() throw() {}
+};
+
+class NoSuchElementException : public Exception {
+ public:
+ NoSuchElementException() throw() : Exception("No such element") {}
+ explicit NoSuchElementException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~NoSuchElementException() throw() {}
+};
+
+class IllegalArgumentException : public Exception {
+ public:
+ IllegalArgumentException() throw() : Exception("Illegal argument") {}
+ explicit IllegalArgumentException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~IllegalArgumentException() throw() {}
+};
+
+class IllegalStateException : public Exception {
+ public:
+ IllegalStateException() throw() : Exception("Illegal state") {}
+ explicit IllegalStateException(const char* message) throw()
+ : Exception(message) {}
+ virtual ~IllegalStateException() throw() {}
+};
+
+} // namespace sfntly
+
+#endif // #if !defined (SFNTLY_NO_EXCEPTION)
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_EXCEPTION_TYPE_H_
diff --git a/sfntly/port/file_input_stream.cc b/sfntly/port/file_input_stream.cc
new file mode 100644
index 0000000..dfe9a7b
--- /dev/null
+++ b/sfntly/port/file_input_stream.cc
@@ -0,0 +1,171 @@
+/*
+ * 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.
+ */
+
+#if defined (WIN32)
+#include <windows.h>
+#endif
+
+#include <algorithm>
+
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+FileInputStream::FileInputStream()
+ : file_(NULL),
+ position_(0),
+ length_(0) {
+}
+
+FileInputStream::~FileInputStream() {
+ Close();
+}
+
+int32_t FileInputStream::Available() {
+ return length_ - position_;
+}
+
+void FileInputStream::Close() {
+ if (file_) {
+ fclose(file_);
+ length_ = 0;
+ position_ = 0;
+ file_ = NULL;
+ }
+}
+
+void FileInputStream::Mark(int32_t readlimit) {
+ // NOP
+ UNREFERENCED_PARAMETER(readlimit);
+}
+
+bool FileInputStream::MarkSupported() {
+ return false;
+}
+
+int32_t FileInputStream::Read() {
+ if (!file_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no opened file");
+#endif
+ return 0;
+ }
+ if (feof(file_)) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("eof reached");
+#endif
+ return 0;
+ }
+ byte_t value;
+ size_t length = fread(&value, 1, 1, file_);
+ position_ += length;
+ return value;
+}
+
+int32_t FileInputStream::Read(ByteVector* b) {
+ return Read(b, 0, b->size());
+}
+
+int32_t FileInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
+ assert(b);
+ if (!file_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no opened file");
+#endif
+ return 0;
+ }
+ if (feof(file_)) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("eof reached");
+#endif
+ return 0;
+ }
+ size_t read_count = std::min<size_t>(length_ - position_, length);
+ if (b->size() < (size_t)(offset + read_count)) {
+ b->resize((size_t)(offset + read_count));
+ }
+ int32_t actual_read = fread(&((*b)[offset]), 1, read_count, file_);
+ position_ += actual_read;
+ return actual_read;
+}
+
+void FileInputStream::Reset() {
+ // NOP
+}
+
+int64_t FileInputStream::Skip(int64_t n) {
+ if (!file_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no opened file");
+#endif
+ return 0;
+ }
+ int64_t skip_count = 0;
+ if (n < 0) { // move backwards
+ skip_count = std::max<int64_t>(0 - (int64_t)position_, n);
+ position_ -= (size_t)(0 - skip_count);
+ fseek(file_, position_, SEEK_SET);
+ } else {
+ skip_count = std::min<size_t>(length_ - position_, (size_t)n);
+ position_ += (size_t)skip_count;
+ fseek(file_, (size_t)skip_count, SEEK_CUR);
+ }
+ return skip_count;
+}
+
+void FileInputStream::Unread(ByteVector* b) {
+ Unread(b, 0, b->size());
+}
+
+void FileInputStream::Unread(ByteVector* b, int32_t offset, int32_t length) {
+ assert(b);
+ assert(b->size() >= size_t(offset + length));
+ if (!file_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no opened file");
+#endif
+ return;
+ }
+ size_t unread_count = std::min<size_t>(position_, length);
+ fseek(file_, position_ - unread_count, SEEK_SET);
+ position_ -= unread_count;
+ Read(b, offset, length);
+ fseek(file_, position_ - unread_count, SEEK_SET);
+ position_ -= unread_count;
+}
+
+bool FileInputStream::Open(const char* file_path) {
+ assert(file_path);
+ if (file_) {
+ Close();
+ }
+#if defined (WIN32)
+ fopen_s(&file_, file_path, "rb");
+#else
+ file_ = fopen(file_path, "rb");
+#endif
+ if (file_ == NULL) {
+ return false;
+ }
+
+ fseek(file_, 0, SEEK_END);
+ length_ = ftell(file_);
+ fseek(file_, 0, SEEK_SET);
+ return true;
+}
+
+} // namespace sfntly
diff --git a/sfntly/port/file_input_stream.h b/sfntly/port/file_input_stream.h
new file mode 100644
index 0000000..cbca25f
--- /dev/null
+++ b/sfntly/port/file_input_stream.h
@@ -0,0 +1,57 @@
+/*
+ * 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_SFNTLY_PORT_FILE_INPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_FILE_INPUT_STREAM_H_
+
+#include <stdio.h>
+
+#include "sfntly/port/input_stream.h"
+
+namespace sfntly {
+
+class FileInputStream : public PushbackInputStream {
+ public:
+ FileInputStream();
+ virtual ~FileInputStream();
+
+ // InputStream methods
+ virtual int32_t Available();
+ virtual void Close();
+ virtual void Mark(int32_t readlimit);
+ virtual bool MarkSupported();
+ virtual int32_t Read();
+ virtual int32_t Read(ByteVector* b);
+ virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length);
+ virtual void Reset();
+ virtual int64_t Skip(int64_t n);
+
+ // PushbackInputStream methods
+ virtual void Unread(ByteVector* b);
+ virtual void Unread(ByteVector* b, int32_t offset, int32_t length);
+
+ // Own methods
+ virtual bool Open(const char* file_path);
+
+ private:
+ FILE* file_;
+ size_t position_;
+ size_t length_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_FILE_INPUT_STREAM_H_
diff --git a/sfntly/port/input_stream.h b/sfntly/port/input_stream.h
new file mode 100644
index 0000000..5d24036
--- /dev/null
+++ b/sfntly/port/input_stream.h
@@ -0,0 +1,49 @@
+/*
+ * 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_SFNTLY_PORT_INPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_INPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+// C++ equivalent to Java's OutputStream class
+class InputStream {
+ public:
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~InputStream() {}
+
+ virtual int32_t Available() = 0;
+ virtual void Close() = 0;
+ virtual void Mark(int32_t readlimit) = 0;
+ virtual bool MarkSupported() = 0;
+ virtual int32_t Read() = 0;
+ virtual int32_t Read(ByteVector* b) = 0;
+ virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length) = 0;
+ virtual void Reset() = 0;
+ virtual int64_t Skip(int64_t n) = 0;
+};
+
+class PushbackInputStream : public InputStream {
+ public:
+ virtual void Unread(ByteVector* b) = 0;
+ virtual void Unread(ByteVector* b, int32_t offset, int32_t length) = 0;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_INPUT_STREAM_H_
diff --git a/sfntly/port/java_iterator.h b/sfntly/port/java_iterator.h
new file mode 100644
index 0000000..0a99bca
--- /dev/null
+++ b/sfntly/port/java_iterator.h
@@ -0,0 +1,94 @@
+/*
+ * 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_SFNTLY_PORT_JAVA_ITERATOR_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_JAVA_ITERATOR_H_
+
+#include "sfntly/port/refcount.h"
+
+// Interface of Java iterator.
+// This is a forward read-only iterator that represents java.util.Iterator<E>
+
+namespace sfntly {
+
+template <typename ReturnType, typename ContainerBase>
+class Iterator : public virtual RefCount {
+ public:
+ virtual ~Iterator() {}
+ virtual ContainerBase* container_base() = 0;
+
+ protected:
+ Iterator() {}
+ NO_COPY_AND_ASSIGN(Iterator);
+};
+
+template <typename ReturnType, typename Container,
+ typename ContainerBase = Container>
+class PODIterator : public Iterator<ReturnType, ContainerBase>,
+ public RefCounted< PODIterator<ReturnType, Container> > {
+ public:
+ explicit PODIterator(Container* container) : container_(container) {}
+ virtual ~PODIterator() {}
+ virtual ContainerBase* container_base() {
+ return static_cast<ContainerBase*>(container_);
+ }
+
+ virtual bool HasNext() = 0;
+ virtual ReturnType Next() = 0;
+ virtual void Remove() {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ // Default to no support.
+ throw UnsupportedOperationException();
+#endif
+ }
+
+ protected:
+ Container* container() { return container_; }
+
+ private:
+ Container* container_; // Dumb pointer is used to avoid circular ref-counting
+};
+
+template <typename ReturnType, typename Container,
+ typename ContainerBase = Container>
+class RefIterator : public Iterator<ReturnType, ContainerBase>,
+ public RefCounted< RefIterator<ReturnType, Container> > {
+ public:
+ explicit RefIterator(Container* container) : container_(container) {}
+ virtual ~RefIterator() {}
+ virtual ContainerBase* container_base() {
+ return static_cast<ContainerBase*>(container_);
+ }
+
+ virtual bool HasNext() = 0;
+ CALLER_ATTACH virtual ReturnType* Next() = 0;
+ virtual void Remove() {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ // Default to no support.
+ throw UnsupportedOperationException();
+#endif
+ }
+
+ protected:
+ Container* container() { return container_; }
+
+ private:
+ Container* container_; // Dumb pointer is used to avoid circular ref-counting
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_JAVA_ITERATOR_H_
diff --git a/sfntly/port/lock.cc b/sfntly/port/lock.cc
new file mode 100644
index 0000000..6c0c309
--- /dev/null
+++ b/sfntly/port/lock.cc
@@ -0,0 +1,72 @@
+/*
+ * 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/lock.h"
+
+namespace sfntly {
+
+#if defined (WIN32)
+
+Lock::Lock() {
+ // The second parameter is the spin count, for short-held locks it avoid the
+ // contending thread from going to sleep which helps performance greatly.
+ ::InitializeCriticalSectionAndSpinCount(&os_lock_, 2000);
+}
+
+Lock::~Lock() {
+ ::DeleteCriticalSection(&os_lock_);
+}
+
+bool Lock::Try() {
+ if (::TryEnterCriticalSection(&os_lock_) != FALSE) {
+ return true;
+ }
+ return false;
+}
+
+void Lock::Acquire() {
+ ::EnterCriticalSection(&os_lock_);
+}
+
+void Lock::Unlock() {
+ ::LeaveCriticalSection(&os_lock_);
+}
+
+#else // We assume it's pthread
+
+Lock::Lock() {
+ pthread_mutex_init(&os_lock_, NULL);
+}
+
+Lock::~Lock() {
+ pthread_mutex_destroy(&os_lock_);
+}
+
+bool Lock::Try() {
+ return (pthread_mutex_trylock(&os_lock_) == 0);
+}
+
+void Lock::Acquire() {
+ pthread_mutex_lock(&os_lock_);
+}
+
+void Lock::Unlock() {
+ pthread_mutex_unlock(&os_lock_);
+}
+
+#endif
+
+} // namespace sfntly
diff --git a/sfntly/port/lock.h b/sfntly/port/lock.h
new file mode 100644
index 0000000..b2e29bf
--- /dev/null
+++ b/sfntly/port/lock.h
@@ -0,0 +1,76 @@
+/*
+ * 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_SFNTLY_PORT_LOCK_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_LOCK_H_
+
+#if defined (WIN32)
+#include <windows.h>
+#else // Assume pthread.
+#include <pthread.h>
+#include <errno.h>
+#endif
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+#if defined (WIN32)
+ typedef CRITICAL_SECTION OSLockType;
+#else // Assume pthread.
+ typedef pthread_mutex_t OSLockType;
+#endif
+
+class Lock {
+ public:
+ Lock();
+ ~Lock();
+
+ // If the lock is not held, take it and return true. If the lock is already
+ // held by something else, immediately return false.
+ bool Try();
+
+ // Take the lock, blocking until it is available if necessary.
+ void Acquire();
+
+ // Release the lock. This must only be called by the lock's holder: after
+ // a successful call to Try, or a call to Lock.
+ void Unlock();
+
+ private:
+ OSLockType os_lock_;
+ NO_COPY_AND_ASSIGN(Lock);
+};
+
+// A helper class that acquires the given Lock while the AutoLock is in scope.
+class AutoLock {
+ public:
+ explicit AutoLock(Lock& lock) : lock_(lock) {
+ lock_.Acquire();
+ }
+
+ ~AutoLock() {
+ lock_.Unlock();
+ }
+
+ private:
+ Lock& lock_;
+ NO_COPY_AND_ASSIGN(AutoLock);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_LOCK_H_
diff --git a/sfntly/port/memory_input_stream.cc b/sfntly/port/memory_input_stream.cc
new file mode 100755
index 0000000..f6f2b9b
--- /dev/null
+++ b/sfntly/port/memory_input_stream.cc
@@ -0,0 +1,149 @@
+/*
+ * 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.
+ */
+
+#if defined (WIN32)
+#include <windows.h>
+#endif
+
+#include <string.h>
+
+#include <algorithm>
+
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+MemoryInputStream::MemoryInputStream()
+ : buffer_(NULL),
+ position_(0),
+ length_(0) {
+}
+
+MemoryInputStream::~MemoryInputStream() {
+ Close();
+}
+
+int32_t MemoryInputStream::Available() {
+ return length_ - position_;
+}
+
+void MemoryInputStream::Close() {
+}
+
+void MemoryInputStream::Mark(int32_t readlimit) {
+ // NOP
+ UNREFERENCED_PARAMETER(readlimit);
+}
+
+bool MemoryInputStream::MarkSupported() {
+ return false;
+}
+
+int32_t MemoryInputStream::Read() {
+ if (!buffer_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no memory attached");
+#endif
+ return 0;
+ }
+ if (position_ >= length_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("eof reached");
+#endif
+ return 0;
+ }
+ byte_t value = buffer_[position_++];
+ return value;
+}
+
+int32_t MemoryInputStream::Read(ByteVector* b) {
+ return Read(b, 0, b->size());
+}
+
+int32_t MemoryInputStream::Read(ByteVector* b, int32_t offset, int32_t length) {
+ assert(b);
+ if (!buffer_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no memory attached");
+#endif
+ return 0;
+ }
+ if (position_ >= length_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("eof reached");
+#endif
+ return 0;
+ }
+ size_t read_count = std::min<size_t>(length_ - position_, length);
+ if (b->size() < (size_t)(offset + read_count)) {
+ b->resize((size_t)(offset + read_count));
+ }
+ memcpy(&((*b)[offset]), buffer_ + position_, read_count);
+ position_ += read_count;
+ return read_count;
+}
+
+void MemoryInputStream::Reset() {
+ // NOP
+}
+
+int64_t MemoryInputStream::Skip(int64_t n) {
+ if (!buffer_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no memory attached");
+#endif
+ return 0;
+ }
+ int64_t skip_count = 0;
+ if (n < 0) { // move backwards
+ skip_count = std::max<int64_t>(0 - (int64_t)position_, n);
+ position_ -= (size_t)(0 - skip_count);
+ } else {
+ skip_count = std::min<size_t>(length_ - position_, (size_t)n);
+ position_ += (size_t)skip_count;
+ }
+ return skip_count;
+}
+
+void MemoryInputStream::Unread(ByteVector* b) {
+ Unread(b, 0, b->size());
+}
+
+void MemoryInputStream::Unread(ByteVector* b, int32_t offset, int32_t length) {
+ assert(b);
+ assert(b->size() >= size_t(offset + length));
+ if (!buffer_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("no memory attached");
+#endif
+ return;
+ }
+ size_t unread_count = std::min<size_t>(position_, length);
+ position_ -= unread_count;
+ Read(b, offset, length);
+ position_ -= unread_count;
+}
+
+bool MemoryInputStream::Attach(const byte_t* buffer, size_t length) {
+ assert(buffer);
+ assert(length);
+ buffer_ = buffer;
+ length_ = length;
+ return true;
+}
+
+} // namespace sfntly
diff --git a/sfntly/port/memory_input_stream.h b/sfntly/port/memory_input_stream.h
new file mode 100755
index 0000000..bc861c3
--- /dev/null
+++ b/sfntly/port/memory_input_stream.h
@@ -0,0 +1,57 @@
+/*
+ * 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_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_
+
+#include <stdio.h>
+
+#include "sfntly/port/input_stream.h"
+
+namespace sfntly {
+
+class MemoryInputStream : public PushbackInputStream {
+ public:
+ MemoryInputStream();
+ virtual ~MemoryInputStream();
+
+ // InputStream methods
+ virtual int32_t Available();
+ virtual void Close();
+ virtual void Mark(int32_t readlimit);
+ virtual bool MarkSupported();
+ virtual int32_t Read();
+ virtual int32_t Read(ByteVector* b);
+ virtual int32_t Read(ByteVector* b, int32_t offset, int32_t length);
+ virtual void Reset();
+ virtual int64_t Skip(int64_t n);
+
+ // PushbackInputStream methods
+ virtual void Unread(ByteVector* b);
+ virtual void Unread(ByteVector* b, int32_t offset, int32_t length);
+
+ // Own methods
+ virtual bool Attach(const byte_t* buffer, size_t length);
+
+ private:
+ const byte_t* buffer_;
+ size_t position_;
+ size_t length_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_INPUT_STREAM_H_
diff --git a/sfntly/port/memory_output_stream.cc b/sfntly/port/memory_output_stream.cc
new file mode 100644
index 0000000..f2ff2e3
--- /dev/null
+++ b/sfntly/port/memory_output_stream.cc
@@ -0,0 +1,72 @@
+/*
+ * 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/memory_output_stream.h"
+
+namespace sfntly {
+
+MemoryOutputStream::MemoryOutputStream() {
+}
+
+MemoryOutputStream::~MemoryOutputStream() {
+}
+
+void MemoryOutputStream::Write(ByteVector* buffer) {
+ store_.insert(store_.end(), buffer->begin(), buffer->end());
+}
+
+void MemoryOutputStream::Write(ByteVector* buffer,
+ int32_t offset,
+ int32_t length) {
+ assert(buffer);
+ if (offset >= 0 && length > 0) {
+ store_.insert(store_.end(),
+ buffer->begin() + offset,
+ buffer->begin() + offset + length);
+ } else {
+#if !defined(SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ }
+}
+
+void MemoryOutputStream::Write(byte_t* buffer, int32_t offset, int32_t length) {
+ assert(buffer);
+ if (offset >= 0 && length > 0) {
+ store_.insert(store_.end(), buffer + offset, buffer + offset + length);
+ } else {
+#if !defined(SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ }
+}
+
+void MemoryOutputStream::Write(byte_t b) {
+ store_.push_back(b);
+}
+
+byte_t* MemoryOutputStream::Get() {
+ if (store_.empty()) {
+ return NULL;
+ }
+ return &(store_[0]);
+}
+
+size_t MemoryOutputStream::Size() {
+ return store_.size();
+}
+
+} // namespace sfntly
diff --git a/sfntly/port/memory_output_stream.h b/sfntly/port/memory_output_stream.h
new file mode 100644
index 0000000..d1eda7f
--- /dev/null
+++ b/sfntly/port/memory_output_stream.h
@@ -0,0 +1,51 @@
+/*
+ * 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_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_
+
+#include <cstddef>
+#include <vector>
+
+#include "sfntly/port/type.h"
+#include "sfntly/port/output_stream.h"
+
+namespace sfntly {
+
+// OutputStream backed by STL vector
+
+class MemoryOutputStream : public OutputStream {
+ public:
+ MemoryOutputStream();
+ virtual ~MemoryOutputStream();
+
+ virtual void Close() {} // no-op
+ virtual void Flush() {} // no-op
+ virtual void Write(ByteVector* buffer);
+ virtual void Write(ByteVector* buffer, int32_t offset, int32_t length);
+ virtual void Write(byte_t* buffer, int32_t offset, int32_t length);
+ virtual void Write(byte_t b);
+
+ byte_t* Get();
+ size_t Size();
+
+ private:
+ std::vector<byte_t> store_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_MEMORY_OUTPUT_STREAM_H_
diff --git a/sfntly/port/output_stream.h b/sfntly/port/output_stream.h
new file mode 100644
index 0000000..64a6024
--- /dev/null
+++ b/sfntly/port/output_stream.h
@@ -0,0 +1,46 @@
+/*
+ * 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_SFNTLY_PORT_OUTPUT_STREAM_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_OUTPUT_STREAM_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+// C++ equivalent to Java's OutputStream class
+class OutputStream {
+ public:
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~OutputStream() {}
+
+ virtual void Close() = 0;
+ virtual void Flush() = 0;
+ virtual void Write(ByteVector* buffer) = 0;
+ virtual void Write(byte_t b) = 0;
+
+ // Note: C++ port offered both versions of Write() here. The first one is
+ // better because it does check bounds. The second one is there for
+ // performance concerns.
+ virtual void Write(ByteVector* buffer, int32_t offset, int32_t length) = 0;
+
+ // Note: Caller is responsible for the boundary of buffer.
+ virtual void Write(byte_t* buffer, int32_t offset, int32_t length) = 0;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_OUTPUT_STREAM_H_
diff --git a/sfntly/port/refcount.h b/sfntly/port/refcount.h
new file mode 100644
index 0000000..eed5162
--- /dev/null
+++ b/sfntly/port/refcount.h
@@ -0,0 +1,277 @@
+/*
+ * 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.
+ */
+
+// Object reference count and smart pointer implementation.
+
+// 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
+
+// Notes on usage:
+// 1. Virtual inherit from RefCount interface in base class if smart pointers
+// are going to be defined.
+// 2. All RefCounted objects must be instantiated on the heap. Allocating the
+// object on stack will cause crash.
+// 3. Be careful when you have complex inheritance. For example,
+// class A : public RefCounted<A>;
+// class B : public A, public RefCounted<B>;
+// In this case the smart pointer is pretty dumb and don't count on it to
+// nicely destroy your objects as designed. Try refactor your code like
+// class I; // the common interface and implementations
+// class A : public I, public RefCounted<A>; // A specific implementation
+// class B : public I, public RefCounted<B>; // B specific implementation
+// 4. Smart pointers here are very bad candidates for function parameters. Use
+// dumb pointers in function parameter list.
+// 5. When down_cast is performed on a dangling pointer due to bugs in code,
+// VC++ will generate SEH which is not handled well in VC++ debugger. One
+// can use WinDBG to run it and get the faulting stack.
+// 6. Idioms for heap object as return value
+// Foo* createFoo() { FooPtr obj = new Foo(); return obj.Detach(); }
+// Foo* passthru() { FooPtr obj = createFoo(), return obj; }
+// FooPtr end_scope_pointer;
+// end_scope_pointer.Attach(passThrough);
+// If you are not passing that object back, you are the end of scope.
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
+
+#if !defined (NDEBUG)
+ #define ENABLE_OBJECT_COUNTER
+// #define REF_COUNT_DEBUGGING
+#endif
+
+#if defined (REF_COUNT_DEBUGGING)
+ #include <stdio.h>
+ #include <typeinfo>
+#endif
+
+#include "sfntly/port/atomic.h"
+#include "sfntly/port/type.h"
+
+// Special tag for functions that requires caller to attach instead of using
+// assignment operators.
+#define CALLER_ATTACH
+
+#if defined (REF_COUNT_DEBUGGING)
+ #define DEBUG_OUTPUT(a) \
+ fprintf(stderr, "%s%s:oc=%d,oid=%d,rc=%d\n", a, \
+ typeid(this).name(), object_counter_, object_id_, ref_count_)
+#else
+ #define DEBUG_OUTPUT(a)
+#endif
+
+#if defined (_MSC_VER)
+ // VC 2008/2010 incorrectly gives this warning for pure virtual functions
+ // in virtual inheritance. The only way to get around it is to disable it.
+ #pragma warning(disable:4250)
+#endif
+
+namespace sfntly {
+
+class RefCount {
+ public:
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~RefCount() {}
+
+ virtual size_t AddRef() const = 0;
+ virtual size_t Release() const = 0;
+};
+
+template <typename T>
+class NoAddRefRelease : public T {
+ public:
+ NoAddRefRelease();
+ ~NoAddRefRelease();
+
+ private:
+ virtual size_t AddRef() const = 0;
+ virtual size_t Release() const = 0;
+};
+
+template <typename TDerived>
+class RefCounted : virtual public RefCount {
+ public:
+ RefCounted() : ref_count_(0) {
+#if defined (ENABLE_OBJECT_COUNTER)
+ object_id_ = AtomicIncrement(&next_id_);
+ AtomicIncrement(&object_counter_);
+ DEBUG_OUTPUT("C ");
+#endif
+ }
+ RefCounted(const RefCounted<TDerived>&) : ref_count_(0) {}
+ virtual ~RefCounted() {
+#if defined (ENABLE_OBJECT_COUNTER)
+ AtomicDecrement(&object_counter_);
+ DEBUG_OUTPUT("D ");
+#endif
+ }
+
+ RefCounted<TDerived>& operator=(const RefCounted<TDerived>&) {
+ // Each object maintains own ref count, don't propagate.
+ return *this;
+ }
+
+ virtual size_t AddRef() const {
+ size_t new_count = AtomicIncrement(&ref_count_);
+ DEBUG_OUTPUT("A ");
+ return new_count;
+ }
+
+ virtual size_t Release() const {
+ size_t new_ref_count = AtomicDecrement(&ref_count_);
+ DEBUG_OUTPUT("R ");
+ if (new_ref_count == 0) {
+ // A C-style is used to cast away const-ness and to derived.
+ // lint does not like this but this is how it works.
+ delete (TDerived*)(this);
+ }
+ return new_ref_count;
+ }
+
+ mutable size_t ref_count_; // reference count of current object
+#if defined (ENABLE_OBJECT_COUNTER)
+ static size_t object_counter_;
+ static size_t next_id_;
+ mutable size_t object_id_;
+#endif
+};
+
+#if defined (ENABLE_OBJECT_COUNTER)
+template <typename TDerived> size_t RefCounted<TDerived>::object_counter_ = 0;
+template <typename TDerived> size_t RefCounted<TDerived>::next_id_ = 0;
+#endif
+
+// semi-smart pointer for RefCount derived objects, similar to CComPtr
+template <typename T>
+class Ptr {
+ public:
+ Ptr() : p_(NULL) {
+ }
+
+ // This constructor shall not be explicit.
+ // lint does not like this but this is how it works.
+ Ptr(T* pT) : p_(NULL) {
+ *this = pT;
+ }
+
+ Ptr(const Ptr<T>& p) : p_(NULL) {
+ *this = p;
+ }
+
+ ~Ptr() {
+ Release();
+ }
+
+ T* operator=(T* pT) {
+ if (p_ == pT) {
+ return p_;
+ }
+ if (pT) {
+ RefCount* p = static_cast<RefCount*>(pT);
+ if (p == NULL) {
+ return NULL;
+ }
+ p->AddRef(); // always AddRef() before Release()
+ }
+ Release();
+ p_ = pT;
+ return p_;
+ }
+
+ T* operator=(const Ptr<T>& p) {
+ if (p_ == p.p_) {
+ return p_;
+ }
+ return operator=(p.p_);
+ }
+
+ operator T*&() {
+ return p_;
+ }
+
+ T& operator*() const {
+ return *p_; // It can throw!
+ }
+
+ NoAddRefRelease<T>* operator->() const {
+ return (NoAddRefRelease<T>*)p_; // It can throw!
+ }
+
+ bool operator!() const {
+ return (p_ == NULL);
+ }
+
+ bool operator<(const Ptr<T>& p) const {
+ return (p_ < p.p_);
+ }
+
+ bool operator!=(T* pT) const {
+ return !operator==(pT);
+ }
+
+ bool operator==(T* pT) const {
+ return (p_ == pT);
+ }
+
+ size_t Release() const {
+ size_t ref_count = 0;
+ if (p_) {
+ RefCount* p = static_cast<RefCount*>(p_);
+ if (p) {
+ ref_count = p->Release();
+ }
+ p_ = NULL;
+ }
+ return ref_count;
+ }
+
+ void Attach(T* pT) {
+ if (p_ != pT) {
+ Release();
+ p_ = pT;
+ }
+ }
+
+ T* Detach() {
+ T* pT = p_;
+ p_ = NULL;
+ return pT;
+ }
+
+ mutable T* p_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_REFCOUNT_H_
diff --git a/sfntly/port/type.h b/sfntly/port/type.h
new file mode 100644
index 0000000..20a5ba8
--- /dev/null
+++ b/sfntly/port/type.h
@@ -0,0 +1,102 @@
+/*
+ * 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_SFNTLY_PORT_TYPE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_
+
+#include <assert.h>
+
+#if defined (_MSC_VER) && (_MSC_VER < 1600)
+ typedef unsigned char uint8_t;
+ typedef signed char int8_t;
+ typedef unsigned __int16 uint16_t;
+ typedef signed __int16 int16_t;
+ typedef unsigned __int32 uint32_t;
+ typedef signed __int32 int32_t;
+ typedef unsigned __int64 uint64_t;
+ typedef signed __int64 int64_t;
+ // Definitions to avoid ICU redefinition issue
+ #define U_HAVE_INT8_T 1
+ #define U_HAVE_UINT8_T 1
+ #define U_HAVE_INT16_T 1
+ #define U_HAVE_UINT16_T 1
+ #define U_HAVE_INT32_T 1
+ #define U_HAVE_UINT32_T 1
+ #define U_HAVE_INT64_T 1
+ #define U_HAVE_UINT64_T 1
+#else
+ #include <stdint.h>
+#endif
+
+#include <cstddef>
+#include <vector>
+#include <set>
+
+namespace sfntly {
+
+typedef uint8_t byte_t;
+typedef uint16_t word_t;
+typedef uint32_t dword_t;
+typedef uint64_t qword_t;
+
+typedef std::vector<byte_t> ByteVector;
+typedef std::vector<int32_t> IntegerList;
+typedef std::set<int32_t> IntegerSet;
+
+// A macro to disallow the copy constructor and operator= functions.
+// This should be used in the private: declarations for a class.
+#define NO_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+} // namespace sfntly
+
+// Make google3 happy since it prohibits RTTI.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+ return f;
+}
+
+template<typename To, typename From> // use like this: down_cast<T*>(foo);
+inline To down_cast(From* f) { // so we only accept pointers
+ // Ensures that To is a sub-type of From *. This test is here only
+ // for compile-time type checking, and has no overhead in an
+ // optimized build at run-time, as it will be optimized away
+ // completely.
+#if defined (_MSC_VER)
+ #pragma warning(push)
+ #pragma warning(disable:4127) // disable "conditional expression is constant"
+#endif
+ if (false) {
+ implicit_cast<From*, To>(0);
+ }
+#if defined (_MSC_VER)
+ #pragma warning(pop)
+#endif
+
+// The following code is the only place for RTTI. It is done so to allow
+// additional type checking when SFNTLY_TYPE_VERIFICATION is defined.
+#if defined (SFNTLY_TYPE_VERIFICATION)
+ assert(f == NULL || dynamic_cast<To>(f) != NULL);
+#endif
+ return static_cast<To>(f);
+}
+
+#if !defined(WIN32)
+ #define UNREFERENCED_PARAMETER(p) do { (void)p; } while (0)
+#endif
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_PORT_TYPE_H_
diff --git a/sfntly/table/bitmap/big_glyph_metrics.cc b/sfntly/table/bitmap/big_glyph_metrics.cc
new file mode 100644
index 0000000..d853212
--- /dev/null
+++ b/sfntly/table/bitmap/big_glyph_metrics.cc
@@ -0,0 +1,171 @@
+/*
+ * 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/table/bitmap/big_glyph_metrics.h"
+
+namespace sfntly {
+/******************************************************************************
+ * BigGlyphMetrics class
+ ******************************************************************************/
+BigGlyphMetrics::BigGlyphMetrics(ReadableFontData* data)
+ : GlyphMetrics(data) {
+}
+
+BigGlyphMetrics::~BigGlyphMetrics() {
+}
+
+int32_t BigGlyphMetrics::Height() {
+ return data_->ReadByte(Offset::kHeight);
+}
+
+int32_t BigGlyphMetrics::Width() {
+ return data_->ReadByte(Offset::kWidth);
+}
+
+int32_t BigGlyphMetrics::HoriBearingX() {
+ return data_->ReadByte(Offset::kHoriBearingX);
+}
+
+int32_t BigGlyphMetrics::HoriBearingY() {
+ return data_->ReadByte(Offset::kHoriBearingY);
+}
+
+int32_t BigGlyphMetrics::HoriAdvance() {
+ return data_->ReadByte(Offset::kHoriAdvance);
+}
+
+int32_t BigGlyphMetrics::VertBearingX() {
+ return data_->ReadByte(Offset::kVertBearingX);
+}
+
+int32_t BigGlyphMetrics::VertBearingY() {
+ return data_->ReadByte(Offset::kVertBearingY);
+}
+
+int32_t BigGlyphMetrics::VertAdvance() {
+ return data_->ReadByte(Offset::kVertAdvance);
+}
+
+/******************************************************************************
+ * BigGlyphMetrics::Builder class
+ ******************************************************************************/
+BigGlyphMetrics::Builder::Builder(WritableFontData* data)
+ : GlyphMetrics::Builder(data) {
+}
+
+BigGlyphMetrics::Builder::Builder(ReadableFontData* data)
+ : GlyphMetrics::Builder(data) {
+}
+
+BigGlyphMetrics::Builder::~Builder() {
+}
+
+int32_t BigGlyphMetrics::Builder::Height() {
+ return InternalReadData()->ReadByte(Offset::kHeight);
+}
+
+void BigGlyphMetrics::Builder::SetHeight(byte_t height) {
+ InternalWriteData()->WriteByte(Offset::kHeight, height);
+}
+
+int32_t BigGlyphMetrics::Builder::Width() {
+ return InternalReadData()->ReadByte(Offset::kWidth);
+}
+
+void BigGlyphMetrics::Builder::SetWidth(byte_t width) {
+ InternalWriteData()->WriteByte(Offset::kWidth, width);
+}
+
+int32_t BigGlyphMetrics::Builder::HoriBearingX() {
+ return InternalReadData()->ReadByte(Offset::kHoriBearingX);
+}
+
+void BigGlyphMetrics::Builder::SetHoriBearingX(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kHoriBearingX, bearing);
+}
+
+int32_t BigGlyphMetrics::Builder::HoriBearingY() {
+ return InternalReadData()->ReadByte(Offset::kHoriBearingY);
+}
+
+void BigGlyphMetrics::Builder::SetHoriBearingY(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kHoriBearingY, bearing);
+}
+
+int32_t BigGlyphMetrics::Builder::HoriAdvance() {
+ return InternalReadData()->ReadByte(Offset::kHoriAdvance);
+}
+
+void BigGlyphMetrics::Builder::SetHoriAdvance(byte_t advance) {
+ InternalWriteData()->WriteByte(Offset::kHoriAdvance, advance);
+}
+
+int32_t BigGlyphMetrics::Builder::VertBearingX() {
+ return InternalReadData()->ReadByte(Offset::kVertBearingX);
+}
+
+void BigGlyphMetrics::Builder::SetVertBearingX(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kVertBearingX, bearing);
+}
+
+int32_t BigGlyphMetrics::Builder::VertBearingY() {
+ return InternalReadData()->ReadByte(Offset::kVertBearingY);
+}
+
+void BigGlyphMetrics::Builder::SetVertBearingY(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kVertBearingY, bearing);
+}
+
+int32_t BigGlyphMetrics::Builder::VertAdvance() {
+ return InternalReadData()->ReadByte(Offset::kVertAdvance);
+}
+
+void BigGlyphMetrics::Builder::SetVertAdvance(byte_t advance) {
+ InternalWriteData()->WriteByte(Offset::kVertAdvance, advance);
+}
+
+CALLER_ATTACH FontDataTable*
+ BigGlyphMetrics::Builder::SubBuildTable(ReadableFontData* data) {
+ BigGlyphMetricsPtr output = new BigGlyphMetrics(data);
+ return output.Detach();
+}
+
+void BigGlyphMetrics::Builder::SubDataSet() {
+ // NOP.
+}
+
+int32_t BigGlyphMetrics::Builder::SubDataSizeToSerialize() {
+ return 0;
+}
+
+bool BigGlyphMetrics::Builder::SubReadyToSerialize() {
+ return false;
+}
+
+int32_t BigGlyphMetrics::Builder::SubSerialize(WritableFontData* new_data) {
+ return Data()->CopyTo(new_data);
+}
+
+// static
+CALLER_ATTACH
+BigGlyphMetrics::Builder* BigGlyphMetrics::Builder::CreateBuilder() {
+ WritableFontDataPtr data;
+ data.Attach(WritableFontData::CreateWritableFontData(Offset::kMetricsLength));
+ BigGlyphMetricsBuilderPtr output = new BigGlyphMetrics::Builder(data);
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/big_glyph_metrics.h b/sfntly/table/bitmap/big_glyph_metrics.h
new file mode 100644
index 0000000..a91601c
--- /dev/null
+++ b/sfntly/table/bitmap/big_glyph_metrics.h
@@ -0,0 +1,96 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_
+
+#include "sfntly/table/bitmap/glyph_metrics.h"
+
+namespace sfntly {
+
+class BigGlyphMetrics : public GlyphMetrics,
+ public RefCounted<BigGlyphMetrics> {
+ public:
+ struct Offset {
+ enum {
+ kMetricsLength = 8,
+
+ kHeight = 0,
+ kWidth = 1,
+ kHoriBearingX = 2,
+ kHoriBearingY = 3,
+ kHoriAdvance = 4,
+ kVertBearingX = 5,
+ kVertBearingY = 6,
+ kVertAdvance = 7,
+ };
+ };
+
+ class Builder : public GlyphMetrics::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ explicit Builder(WritableFontData* data);
+ explicit Builder(ReadableFontData* data);
+
+ virtual ~Builder();
+
+ int32_t Height();
+ void SetHeight(byte_t height);
+ int32_t Width();
+ void SetWidth(byte_t width);
+ int32_t HoriBearingX();
+ void SetHoriBearingX(byte_t bearing);
+ int32_t HoriBearingY();
+ void SetHoriBearingY(byte_t bearing);
+ int32_t HoriAdvance();
+ void SetHoriAdvance(byte_t advance);
+ int32_t VertBearingX();
+ void SetVertBearingX(byte_t bearing);
+ int32_t VertBearingY();
+ void SetVertBearingY(byte_t bearing);
+ int32_t VertAdvance();
+ void SetVertAdvance(byte_t advance);
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ // Static instantiation function.
+ static CALLER_ATTACH Builder* CreateBuilder();
+ };
+
+ explicit BigGlyphMetrics(ReadableFontData* data);
+ virtual ~BigGlyphMetrics();
+
+ int32_t Height();
+ int32_t Width();
+ int32_t HoriBearingX();
+ int32_t HoriBearingY();
+ int32_t HoriAdvance();
+ int32_t VertBearingX();
+ int32_t VertBearingY();
+ int32_t VertAdvance();
+};
+typedef Ptr<BigGlyphMetrics> BigGlyphMetricsPtr;
+typedef Ptr<BigGlyphMetrics::Builder> BigGlyphMetricsBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BIG_GLYPH_METRICS_H_
diff --git a/sfntly/table/bitmap/bitmap_glyph.cc b/sfntly/table/bitmap/bitmap_glyph.cc
new file mode 100644
index 0000000..334a0c0
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_glyph.cc
@@ -0,0 +1,101 @@
+/*
+ * 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/table/bitmap/bitmap_glyph.h"
+#include "sfntly/table/bitmap/simple_bitmap_glyph.h"
+#include "sfntly/table/bitmap/composite_bitmap_glyph.h"
+
+namespace sfntly {
+/******************************************************************************
+ * BitmapGlyph class
+ ******************************************************************************/
+BitmapGlyph::~BitmapGlyph() {
+}
+
+CALLER_ATTACH BitmapGlyph* BitmapGlyph::CreateGlyph(ReadableFontData* data,
+ int32_t format) {
+ BitmapGlyphPtr glyph;
+ BitmapGlyphBuilderPtr builder;
+ builder.Attach(Builder::CreateGlyphBuilder(data, format));
+ if (builder) {
+ glyph.Attach(down_cast<BitmapGlyph*>(builder->Build()));
+ }
+ return glyph;
+}
+
+BitmapGlyph::BitmapGlyph(ReadableFontData* data, int32_t format)
+ : SubTable(data), format_(format) {
+}
+
+/******************************************************************************
+ * BitmapGlyph::Builder class
+ ******************************************************************************/
+BitmapGlyph::Builder::~Builder() {
+}
+
+CALLER_ATTACH BitmapGlyph::Builder*
+BitmapGlyph::Builder::CreateGlyphBuilder(ReadableFontData* data,
+ int32_t format) {
+ BitmapGlyphBuilderPtr builder;
+ switch (format) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ builder = new SimpleBitmapGlyph::Builder(data, format);
+ break;
+ case 8:
+ case 9:
+ builder = new CompositeBitmapGlyph::Builder(data, format);
+ break;
+ }
+ return builder.Detach();
+}
+
+BitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format)
+ : SubTable::Builder(data), format_(format) {
+}
+
+BitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format)
+ : SubTable::Builder(data), format_(format) {
+}
+
+CALLER_ATTACH
+FontDataTable* BitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) {
+ UNREFERENCED_PARAMETER(data);
+ return NULL;
+}
+
+void BitmapGlyph::Builder::SubDataSet() {
+ // NOP
+}
+
+int32_t BitmapGlyph::Builder::SubDataSizeToSerialize() {
+ return InternalReadData()->Length();
+}
+
+bool BitmapGlyph::Builder::SubReadyToSerialize() {
+ return true;
+}
+
+int32_t BitmapGlyph::Builder::SubSerialize(WritableFontData* new_data) {
+ return InternalReadData()->CopyTo(new_data);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/bitmap_glyph.h b/sfntly/table/bitmap/bitmap_glyph.h
new file mode 100644
index 0000000..2dd4c3a
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_glyph.h
@@ -0,0 +1,119 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_
+
+#include <vector>
+#include <map>
+
+#include "sfntly/table/subtable.h"
+
+namespace sfntly {
+
+class BitmapGlyph : public SubTable {
+ public:
+ struct Offset {
+ enum {
+ // header
+ kVersion = 0,
+
+ kSmallGlyphMetricsLength = 5,
+ kBigGlyphMetricsLength = 8,
+ // format 1
+ kGlyphFormat1_imageData = kSmallGlyphMetricsLength,
+
+ // format 2
+ kGlyphFormat2_imageData = kSmallGlyphMetricsLength,
+
+ // format 3
+
+ // format 4
+
+ // format 5
+ kGlyphFormat5_imageData = 0,
+
+ // format 6
+ kGlyphFormat6_imageData = kBigGlyphMetricsLength,
+
+ // format 7
+ kGlyphFormat7_imageData = kBigGlyphMetricsLength,
+
+ // format 8
+ kGlyphFormat8_numComponents = kSmallGlyphMetricsLength + 1,
+ kGlyphFormat8_componentArray = kGlyphFormat8_numComponents +
+ DataSize::kUSHORT,
+
+ // format 9
+ kGlyphFormat9_numComponents = kBigGlyphMetricsLength,
+ kGlyphFormat9_componentArray = kGlyphFormat9_numComponents +
+ DataSize::kUSHORT,
+
+ // ebdtComponent
+ kEbdtComponentLength = DataSize::kUSHORT + 2 * DataSize::kCHAR,
+ kEbdtComponent_glyphCode = 0,
+ kEbdtComponent_xOffset = 2,
+ kEbdtComponent_yOffset = 3,
+ };
+ };
+
+ // TODO(stuartg): builder is not functional at all
+ // - need to add subclasses for each type of bitmap glyph
+ class Builder : public SubTable::Builder {
+ public:
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ int32_t format() { return format_; }
+
+ static CALLER_ATTACH Builder* CreateGlyphBuilder(ReadableFontData* data,
+ int32_t format);
+
+ protected:
+ Builder(WritableFontData* data, int32_t format);
+ Builder(ReadableFontData* data, int32_t format);
+
+ private:
+ int32_t format_;
+ };
+
+ virtual ~BitmapGlyph();
+
+ static CALLER_ATTACH BitmapGlyph* CreateGlyph(ReadableFontData* data,
+ int32_t format);
+ int32_t format() { return format_; }
+
+ // UNIMPLEMENTED: toString()
+
+ protected:
+ BitmapGlyph(ReadableFontData* data, int32_t format);
+
+ private:
+ int32_t format_;
+};
+typedef Ptr<BitmapGlyph> BitmapGlyphPtr;
+typedef Ptr<BitmapGlyph::Builder> BitmapGlyphBuilderPtr;
+typedef std::map<int32_t, BitmapGlyphBuilderPtr> BitmapGlyphBuilderMap;
+typedef std::vector<BitmapGlyphBuilderMap> BitmapGlyphBuilderList;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_GLYPH_H_
diff --git a/sfntly/table/bitmap/bitmap_glyph_info.cc b/sfntly/table/bitmap/bitmap_glyph_info.cc
new file mode 100644
index 0000000..ab9953b
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_glyph_info.cc
@@ -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.
+ */
+
+#include "sfntly/table/bitmap/bitmap_glyph_info.h"
+
+namespace sfntly {
+
+BitmapGlyphInfo::BitmapGlyphInfo(int32_t glyph_id,
+ int32_t block_offset,
+ int32_t start_offset,
+ int32_t length,
+ int32_t format)
+ : glyph_id_(glyph_id),
+ relative_(true),
+ block_offset_(block_offset),
+ start_offset_(start_offset),
+ length_(length),
+ format_(format) {
+}
+
+BitmapGlyphInfo::BitmapGlyphInfo(int32_t glyph_id,
+ int32_t start_offset,
+ int32_t length,
+ int32_t format)
+ : glyph_id_(glyph_id),
+ relative_(false),
+ block_offset_(0),
+ start_offset_(start_offset),
+ length_(length),
+ format_(format) {
+}
+
+bool BitmapGlyphInfo::operator==(const BitmapGlyphInfo& rhs) const {
+ return (format_ == rhs.format_ &&
+ glyph_id_ == rhs.glyph_id_ &&
+ length_ == rhs.length_ &&
+ offset() == rhs.offset());
+}
+
+bool BitmapGlyphInfo::operator==(BitmapGlyphInfo* rhs) {
+ if (rhs == NULL) {
+ return this == NULL;
+ }
+ return (format_ == rhs->format() &&
+ glyph_id_ == rhs->glyph_id() &&
+ length_ == rhs->length() &&
+ offset() == rhs->offset());
+}
+
+bool StartOffsetComparator::operator()(BitmapGlyphInfo* lhs,
+ BitmapGlyphInfo* rhs) {
+ return lhs->start_offset() > rhs->start_offset();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/bitmap_glyph_info.h b/sfntly/table/bitmap/bitmap_glyph_info.h
new file mode 100644
index 0000000..9921d0d
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_glyph_info.h
@@ -0,0 +1,85 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_
+
+#include <vector>
+#include <map>
+
+#include "sfntly/table/subtable.h"
+
+namespace sfntly {
+
+// An immutable class holding bitmap glyph information.
+class BitmapGlyphInfo : public RefCounted<BitmapGlyphInfo> {
+ public:
+ // Constructor for a relative located glyph. The glyph's position in the EBDT
+ // table is a combination of it's block offset and it's own start offset.
+ // @param glyphId the glyph id
+ // @param blockOffset the offset of the block to which the glyph belongs
+ // @param startOffset the offset of the glyph within the block
+ // @param length the byte length
+ // @param format the glyph image format
+ BitmapGlyphInfo(int32_t glyph_id,
+ int32_t block_offset,
+ int32_t start_offset,
+ int32_t length,
+ int32_t format);
+
+ // Constructor for an absolute located glyph. The glyph's position in the EBDT
+ // table is only given by it's own start offset.
+ // @param glyphId the glyph id
+ // @param startOffset the offset of the glyph within the block
+ // @param length the byte length
+ // @param format the glyph image format
+ BitmapGlyphInfo(int32_t glyph_id,
+ int32_t start_offset,
+ int32_t length,
+ int32_t format);
+
+ int32_t glyph_id() const { return glyph_id_; }
+ bool relative() const { return relative_; }
+ int32_t block_offset() const { return block_offset_; }
+ int32_t offset() const { return block_offset() + start_offset(); }
+ int32_t start_offset() const { return start_offset_; }
+ int32_t length() const { return length_; }
+ int32_t format() const { return format_; }
+
+ // UNIMPLEMENTED: hashCode()
+ bool operator==(const BitmapGlyphInfo& rhs) const;
+ bool operator==(BitmapGlyphInfo* rhs);
+
+ private:
+ int32_t glyph_id_;
+ bool relative_;
+ int32_t block_offset_;
+ int32_t start_offset_;
+ int32_t length_;
+ int32_t format_;
+};
+typedef Ptr<BitmapGlyphInfo> BitmapGlyphInfoPtr;
+typedef std::map<int32_t, BitmapGlyphInfoPtr> BitmapGlyphInfoMap;
+typedef std::vector<BitmapGlyphInfoMap> BitmapLocaList;
+
+class StartOffsetComparator {
+ public:
+ bool operator()(BitmapGlyphInfo* lhs, BitmapGlyphInfo* rhs);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_INFO_H_
diff --git a/sfntly/table/bitmap/bitmap_size_table.cc b/sfntly/table/bitmap/bitmap_size_table.cc
new file mode 100644
index 0000000..6c7d731
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_size_table.cc
@@ -0,0 +1,604 @@
+/*
+ * 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/table/bitmap/bitmap_size_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sfntly/math/font_math.h"
+#include "sfntly/table/bitmap/eblc_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"
+
+namespace sfntly {
+/******************************************************************************
+ * BitmapSizeTable class
+ ******************************************************************************/
+BitmapSizeTable::~BitmapSizeTable() {
+}
+
+int32_t BitmapSizeTable::IndexSubTableArrayOffset() {
+ return data_->ReadULongAsInt(
+ EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset);
+}
+
+int32_t BitmapSizeTable::IndexTableSize() {
+ return data_->ReadULongAsInt(
+ EblcTable::Offset::kBitmapSizeTable_indexTableSize);
+}
+
+int32_t BitmapSizeTable::NumberOfIndexSubTables() {
+ return NumberOfIndexSubTables(data_, 0);
+}
+
+int32_t BitmapSizeTable::ColorRef() {
+ return data_->ReadULongAsInt(EblcTable::Offset::kBitmapSizeTable_colorRef);
+}
+
+int32_t BitmapSizeTable::StartGlyphIndex() {
+ return data_->ReadUShort(EblcTable::Offset::kBitmapSizeTable_startGlyphIndex);
+}
+
+int32_t BitmapSizeTable::EndGlyphIndex() {
+ return data_->ReadUShort(EblcTable::Offset::kBitmapSizeTable_endGlyphIndex);
+}
+
+int32_t BitmapSizeTable::PpemX() {
+ return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_ppemX);
+}
+
+int32_t BitmapSizeTable::PpemY() {
+ return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_ppemY);
+}
+
+int32_t BitmapSizeTable::BitDepth() {
+ return data_->ReadByte(EblcTable::Offset::kBitmapSizeTable_bitDepth);
+}
+
+int32_t BitmapSizeTable::FlagsAsInt() {
+ return data_->ReadChar(EblcTable::Offset::kBitmapSizeTable_flags);
+}
+
+IndexSubTable* BitmapSizeTable::GetIndexSubTable(int32_t index) {
+ IndexSubTableList* subtable_list = GetIndexSubTableList();
+ if (index >= 0 && (size_t)index < subtable_list->size()) {
+ return (*subtable_list)[index];
+ }
+ return NULL;
+}
+
+int32_t BitmapSizeTable::GlyphOffset(int32_t glyph_id) {
+ IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->GlyphOffset(glyph_id);
+}
+
+int32_t BitmapSizeTable::GlyphLength(int32_t glyph_id) {
+ IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->GlyphLength(glyph_id);
+}
+
+CALLER_ATTACH BitmapGlyphInfo* BitmapSizeTable::GlyphInfo(int32_t glyph_id) {
+ IndexSubTable* sub_table = SearchIndexSubTables(glyph_id);
+ if (sub_table == NULL) {
+ return NULL;
+ }
+ return sub_table->GlyphInfo(glyph_id);
+}
+
+int32_t BitmapSizeTable::GlyphFormat(int32_t glyph_id) {
+ IndexSubTable* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->image_format();
+}
+
+BitmapSizeTable::BitmapSizeTable(ReadableFontData* data,
+ ReadableFontData* master_data)
+ : SubTable(data, master_data) {
+}
+
+// static
+int32_t BitmapSizeTable::NumberOfIndexSubTables(ReadableFontData* data,
+ int32_t table_offset) {
+ return data->ReadULongAsInt(table_offset +
+ EblcTable::Offset::kBitmapSizeTable_numberOfIndexSubTables);
+}
+
+IndexSubTable* BitmapSizeTable::SearchIndexSubTables(int32_t glyph_id) {
+ // would be faster to binary search but too many size tables don't have
+ // sorted subtables
+#if (SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH)
+ return BinarySearchIndexSubTables(glyph_id);
+#else
+ return LinearSearchIndexSubTables(glyph_id);
+#endif
+}
+
+IndexSubTable* BitmapSizeTable::LinearSearchIndexSubTables(int32_t glyph_id) {
+ IndexSubTableList* subtable_list = GetIndexSubTableList();
+ for (IndexSubTableList::iterator b = subtable_list->begin(),
+ e = subtable_list->end(); b != e; b++) {
+ if ((*b)->first_glyph_index() <= glyph_id &&
+ (*b)->last_glyph_index() >= glyph_id) {
+ return *b;
+ }
+ }
+ return NULL;
+}
+
+IndexSubTable* BitmapSizeTable::BinarySearchIndexSubTables(int32_t glyph_id) {
+ IndexSubTableList* subtable_list = GetIndexSubTableList();
+ int32_t index = 0;
+ int32_t bottom = 0;
+ int32_t top = subtable_list->size();
+ while (top != bottom) {
+ index = (top + bottom) / 2;
+ IndexSubTable* subtable = (*subtable_list)[index];
+ if (glyph_id < subtable->first_glyph_index()) {
+ // Location beow current location
+ top = index;
+ } else {
+ if (glyph_id <= subtable->last_glyph_index()) {
+ return subtable;
+ } else {
+ bottom = index + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+CALLER_ATTACH
+IndexSubTable* BitmapSizeTable::CreateIndexSubTable(int32_t index) {
+ return IndexSubTable::CreateIndexSubTable(master_read_data(),
+ IndexSubTableArrayOffset(),
+ index);
+}
+
+IndexSubTableList* BitmapSizeTable::GetIndexSubTableList() {
+ AutoLock lock(index_subtables_lock_);
+ if (index_subtables_.empty()) {
+ for (int32_t i = 0; i < NumberOfIndexSubTables(); ++i) {
+ IndexSubTablePtr table;
+ table.Attach(CreateIndexSubTable(i));
+ index_subtables_.push_back(table);
+ }
+ }
+ return &index_subtables_;
+}
+
+/******************************************************************************
+ * BitmapSizeTable::Builder class
+ ******************************************************************************/
+BitmapSizeTable::Builder::~Builder() {
+}
+
+CALLER_ATTACH
+FontDataTable* BitmapSizeTable::Builder::SubBuildTable(ReadableFontData* data) {
+ BitmapSizeTablePtr output = new BitmapSizeTable(data, master_read_data());
+ return output.Detach();
+}
+
+void BitmapSizeTable::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t BitmapSizeTable::Builder::SubDataSizeToSerialize() {
+ IndexSubTableBuilderList* builders = IndexSubTableBuilders();
+ if (builders->empty()) {
+ return 0;
+ }
+ int32_t size = EblcTable::Offset::kBitmapSizeTableLength;
+ bool variable = false;
+ for (IndexSubTableBuilderList::iterator b = builders->begin(),
+ e = builders->end(); b != e; b++) {
+ size += EblcTable::Offset::kIndexSubTableEntryLength;
+ int32_t sub_table_size = (*b)->SubDataSizeToSerialize();
+ int32_t padding = FontMath::PaddingRequired(abs(sub_table_size),
+ DataSize::kULONG);
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "subtable size=%d\n", sub_table_size);
+#endif
+ variable = (sub_table_size > 0) ? variable : true;
+ size += abs(sub_table_size) + padding;
+ }
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "bitmap table size=%d\n", variable ? -size : size);
+#endif
+ return variable ? -size : size;
+}
+
+bool BitmapSizeTable::Builder::SubReadyToSerialize() {
+ if (IndexSubTableBuilders()->empty()) {
+ return false;
+ }
+ return true;
+}
+
+int32_t BitmapSizeTable::Builder::SubSerialize(WritableFontData* new_data) {
+ SetNumberOfIndexSubTables(IndexSubTableBuilders()->size());
+ int32_t size = InternalReadData()->CopyTo(new_data);
+ return size;
+}
+
+CALLER_ATTACH BitmapSizeTable::Builder*
+BitmapSizeTable::Builder::CreateBuilder(WritableFontData* data,
+ ReadableFontData* master_data) {
+ BitmapSizeTableBuilderPtr output =
+ new BitmapSizeTable::Builder(data, master_data);
+ return output.Detach();
+}
+
+CALLER_ATTACH BitmapSizeTable::Builder*
+BitmapSizeTable::Builder::CreateBuilder(ReadableFontData* data,
+ ReadableFontData* master_data) {
+ BitmapSizeTableBuilderPtr output =
+ new BitmapSizeTable::Builder(data, master_data);
+ return output.Detach();
+}
+
+int32_t BitmapSizeTable::Builder::IndexSubTableArrayOffset() {
+ return InternalReadData()->ReadULongAsInt(
+ EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset);
+}
+
+void BitmapSizeTable::Builder::SetIndexSubTableArrayOffset(int32_t offset) {
+ InternalWriteData()->WriteULong(
+ EblcTable::Offset::kBitmapSizeTable_indexSubTableArrayOffset, offset);
+}
+
+int32_t BitmapSizeTable::Builder::IndexTableSize() {
+ return InternalReadData()->ReadULongAsInt(
+ EblcTable::Offset::kBitmapSizeTable_indexTableSize);
+}
+
+void BitmapSizeTable::Builder::SetIndexTableSize(int32_t size) {
+ InternalWriteData()->WriteULong(
+ EblcTable::Offset::kBitmapSizeTable_indexTableSize, size);
+}
+
+int32_t BitmapSizeTable::Builder::NumberOfIndexSubTables() {
+ return GetIndexSubTableBuilders()->size();
+}
+
+int32_t BitmapSizeTable::Builder::ColorRef() {
+ return InternalReadData()->ReadULongAsInt(
+ EblcTable::Offset::kBitmapSizeTable_colorRef);
+}
+
+int32_t BitmapSizeTable::Builder::StartGlyphIndex() {
+ return InternalReadData()->ReadUShort(
+ EblcTable::Offset::kBitmapSizeTable_startGlyphIndex);
+}
+
+int32_t BitmapSizeTable::Builder::EndGlyphIndex() {
+ return InternalReadData()->ReadUShort(
+ EblcTable::Offset::kBitmapSizeTable_endGlyphIndex);
+}
+
+int32_t BitmapSizeTable::Builder::PpemX() {
+ return InternalReadData()->ReadByte(
+ EblcTable::Offset::kBitmapSizeTable_ppemX);
+}
+
+int32_t BitmapSizeTable::Builder::PpemY() {
+ return InternalReadData()->ReadByte(
+ EblcTable::Offset::kBitmapSizeTable_ppemY);
+}
+
+int32_t BitmapSizeTable::Builder::BitDepth() {
+ return InternalReadData()->ReadByte(
+ EblcTable::Offset::kBitmapSizeTable_bitDepth);
+}
+
+int32_t BitmapSizeTable::Builder::FlagsAsInt() {
+ return InternalReadData()->ReadChar(
+ EblcTable::Offset::kBitmapSizeTable_flags);
+}
+
+IndexSubTable::Builder* BitmapSizeTable::Builder::IndexSubTableBuilder(
+ int32_t index) {
+ IndexSubTableBuilderList* sub_table_list = GetIndexSubTableBuilders();
+ return sub_table_list->at(index);
+}
+
+CALLER_ATTACH BitmapGlyphInfo* BitmapSizeTable::Builder::GlyphInfo(
+ int32_t glyph_id) {
+ IndexSubTable::Builder* sub_table = SearchIndexSubTables(glyph_id);
+ if (sub_table == NULL) {
+ return NULL;
+ }
+ return sub_table->GlyphInfo(glyph_id);
+}
+
+int32_t BitmapSizeTable::Builder::GlyphOffset(int32_t glyph_id) {
+ IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->GlyphOffset(glyph_id);
+}
+
+int32_t BitmapSizeTable::Builder::GlyphLength(int32_t glyph_id) {
+ IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->GlyphLength(glyph_id);
+}
+
+int32_t BitmapSizeTable::Builder::GlyphFormat(int32_t glyph_id) {
+ IndexSubTable::Builder* subtable = SearchIndexSubTables(glyph_id);
+ if (subtable == NULL) {
+ return -1;
+ }
+ return subtable->image_format();
+}
+
+IndexSubTableBuilderList* BitmapSizeTable::Builder::IndexSubTableBuilders() {
+ return GetIndexSubTableBuilders();
+}
+
+CALLER_ATTACH BitmapSizeTable::Builder::BitmapGlyphInfoIterator*
+BitmapSizeTable::Builder::GetIterator() {
+ Ptr<BitmapSizeTable::Builder::BitmapGlyphInfoIterator> output =
+ new BitmapSizeTable::Builder::BitmapGlyphInfoIterator(this);
+ return output.Detach();
+}
+
+void BitmapSizeTable::Builder::GenerateLocaMap(BitmapGlyphInfoMap* output) {
+ assert(output);
+ Ptr<BitmapSizeTable::Builder::BitmapGlyphInfoIterator> it;
+ it.Attach(GetIterator());
+ while (it->HasNext()) {
+ BitmapGlyphInfoPtr info;
+ info.Attach(it->Next());
+ (*output)[info->glyph_id()] = info;
+ }
+}
+
+void BitmapSizeTable::Builder::Revert() {
+ index_sub_tables_.clear();
+ set_model_changed(false);
+}
+
+BitmapSizeTable::Builder::Builder(WritableFontData* data,
+ ReadableFontData* master_data)
+ : SubTable::Builder(data, master_data) {
+}
+
+BitmapSizeTable::Builder::Builder(ReadableFontData* data,
+ ReadableFontData* master_data)
+ : SubTable::Builder(data, master_data) {
+}
+
+void BitmapSizeTable::Builder::SetNumberOfIndexSubTables(int32_t count) {
+ InternalWriteData()->WriteULong(
+ EblcTable::Offset::kBitmapSizeTable_numberOfIndexSubTables, count);
+}
+
+IndexSubTable::Builder* BitmapSizeTable::Builder::SearchIndexSubTables(
+ int32_t glyph_id) {
+ // would be faster to binary search but too many size tables don't have
+ // sorted subtables
+#if (SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH)
+ return BinarySearchIndexSubTables(glyph_id);
+#else
+ return LinearSearchIndexSubTables(glyph_id);
+#endif
+}
+
+IndexSubTable::Builder* BitmapSizeTable::Builder::LinearSearchIndexSubTables(
+ int32_t glyph_id) {
+ IndexSubTableBuilderList* subtable_list = GetIndexSubTableBuilders();
+ for (IndexSubTableBuilderList::iterator b = subtable_list->begin(),
+ e = subtable_list->end();
+ b != e; b++) {
+ if ((*b)->first_glyph_index() <= glyph_id &&
+ (*b)->last_glyph_index() >= glyph_id) {
+ return *b;
+ }
+ }
+ return NULL;
+}
+
+IndexSubTable::Builder* BitmapSizeTable::Builder::BinarySearchIndexSubTables(
+ int32_t glyph_id) {
+ IndexSubTableBuilderList* subtable_list = GetIndexSubTableBuilders();
+ int32_t index = 0;
+ int32_t bottom = 0;
+ int32_t top = subtable_list->size();
+ while (top != bottom) {
+ index = (top + bottom) / 2;
+ IndexSubTable::Builder* subtable = subtable_list->at(index);
+ if (glyph_id < subtable->first_glyph_index()) {
+ // Location beow current location
+ top = index;
+ } else {
+ if (glyph_id <= subtable->last_glyph_index()) {
+ return subtable;
+ } else {
+ bottom = index + 1;
+ }
+ }
+ }
+ return NULL;
+}
+
+IndexSubTableBuilderList* BitmapSizeTable::Builder::GetIndexSubTableBuilders() {
+ if (index_sub_tables_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &index_sub_tables_;
+}
+
+void BitmapSizeTable::Builder::Initialize(ReadableFontData* data) {
+ index_sub_tables_.clear();
+ if (data) {
+ int32_t number_of_index_subtables =
+ BitmapSizeTable::NumberOfIndexSubTables(data, 0);
+ index_sub_tables_.resize(number_of_index_subtables);
+ for (int32_t i = 0; i < number_of_index_subtables; ++i) {
+ index_sub_tables_[i].Attach(CreateIndexSubTableBuilder(i));
+ }
+ }
+}
+
+CALLER_ATTACH IndexSubTable::Builder*
+BitmapSizeTable::Builder::CreateIndexSubTableBuilder(int32_t index) {
+ return IndexSubTable::Builder::CreateBuilder(master_read_data(),
+ IndexSubTableArrayOffset(),
+ index);
+}
+
+/******************************************************************************
+ * BitmapSizeTable::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+BitmapSizeTable::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ BitmapSizeTable::Builder* container)
+ : RefIterator<BitmapGlyphInfo, BitmapSizeTable::Builder>(container) {
+ sub_table_iter_ = container->IndexSubTableBuilders()->begin();
+ sub_table_glyph_info_iter_.Attach((*sub_table_iter_)->GetIterator());
+}
+
+bool BitmapSizeTable::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (sub_table_glyph_info_iter_ && HasNext(sub_table_glyph_info_iter_)) {
+ return true;
+ }
+ while (++sub_table_iter_ != container()->IndexSubTableBuilders()->end()) {
+ sub_table_glyph_info_iter_.Attach((*sub_table_iter_)->GetIterator());
+ if (HasNext(sub_table_glyph_info_iter_)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+CALLER_ATTACH
+BitmapGlyphInfo* BitmapSizeTable::Builder::BitmapGlyphInfoIterator::Next() {
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ return Next(sub_table_glyph_info_iter_);
+}
+
+bool BitmapSizeTable::Builder::BitmapGlyphInfoIterator::HasNext(
+ BitmapGlyphInfoIter* iterator_base) {
+ if (iterator_base) {
+ switch (iterator_base->container_base()->index_format()) {
+ case 1: {
+ IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->HasNext();
+ }
+
+ case 2: {
+ IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->HasNext();
+ }
+
+ case 3: {
+ IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->HasNext();
+ }
+
+ case 4: {
+ IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->HasNext();
+ }
+
+ case 5: {
+ IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->HasNext();
+ }
+
+ default:
+ break;
+ }
+ }
+ return false;
+}
+
+CALLER_ATTACH
+BitmapGlyphInfo* BitmapSizeTable::Builder::BitmapGlyphInfoIterator::Next(
+ BitmapGlyphInfoIter* iterator_base) {
+ if (iterator_base) {
+ switch (iterator_base->container_base()->index_format()) {
+ case 1: {
+ IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->Next();
+ }
+
+ case 2: {
+ IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->Next();
+ }
+
+ case 3: {
+ IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->Next();
+ }
+
+ case 4: {
+ IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->Next();
+ }
+
+ case 5: {
+ IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator* it =
+ down_cast<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator*>(
+ iterator_base);
+ return it->Next();
+ }
+
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/bitmap_size_table.h b/sfntly/table/bitmap/bitmap_size_table.h
new file mode 100644
index 0000000..6733e20
--- /dev/null
+++ b/sfntly/table/bitmap/bitmap_size_table.h
@@ -0,0 +1,173 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_
+
+#include "sfntly/port/lock.h"
+#include "sfntly/table/bitmap/bitmap_glyph_info.h"
+#include "sfntly/table/bitmap/index_sub_table.h"
+
+namespace sfntly {
+// Binary search would be faster but many fonts have index subtables that
+// aren't sorted.
+// Note: preprocessor define is used to avoid const expression warnings in C++
+// code.
+#define SFNTLY_BITMAPSIZE_USE_BINARY_SEARCH 0
+
+class BitmapSizeTable : public SubTable,
+ public RefCounted<BitmapSizeTable> {
+ public:
+ class Builder : public SubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator :
+ public RefIterator<BitmapGlyphInfo, Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ bool HasNext(BitmapGlyphInfoIter* iterator_base);
+ CALLER_ATTACH BitmapGlyphInfo* Next(BitmapGlyphInfoIter* iterator_base);
+
+ IndexSubTableBuilderList::iterator sub_table_iter_;
+ BitmapGlyphInfoIterPtr sub_table_glyph_info_iter_;
+ };
+
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ ReadableFontData* master_data);
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ ReadableFontData* master_data);
+ // Gets the subtable array offset as set in the original table as read from
+ // the font file. This value cannot be explicitly set and will be generated
+ // during table building.
+ // @return the subtable array offset
+ int32_t IndexSubTableArrayOffset();
+
+ // Sets the subtable array offset. This is used only during the building
+ // process when the objects are being serialized.
+ // @param offset the offset to the index subtable array
+ void SetIndexSubTableArrayOffset(int32_t offset);
+
+ // Gets the subtable array size as set in the original table as read from
+ // the font file. This value cannot be explicitly set and will be generated
+ // during table building.
+ // @return the subtable array size
+ int32_t IndexTableSize();
+
+ // Sets the subtable size. This is used only during the building process
+ // when the objects are being serialized.
+ // @param size the offset to the index subtable array
+ void SetIndexTableSize(int32_t size);
+
+ int32_t NumberOfIndexSubTables();
+ int32_t ColorRef();
+ // TODO(stuartg): SBitLineMetrics hori();
+ // TODO(stuartg): SBitLineMetrics vert();
+ int32_t StartGlyphIndex();
+ int32_t EndGlyphIndex();
+ int32_t PpemX();
+ int32_t PpemY();
+ int32_t BitDepth();
+ int32_t FlagsAsInt();
+
+ IndexSubTable::Builder* IndexSubTableBuilder(int32_t index);
+ CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
+ int32_t GlyphOffset(int32_t glyph_id);
+ int32_t GlyphLength(int32_t glyph_id);
+ int32_t GlyphFormat(int32_t glyph_id);
+ IndexSubTableBuilderList* IndexSubTableBuilders();
+ // Note: renamed from iterator(), type is the derived type.
+ CALLER_ATTACH BitmapGlyphInfoIterator* GetIterator();
+ void GenerateLocaMap(BitmapGlyphInfoMap* output);
+
+ protected:
+ void Revert();
+
+ private:
+ Builder(WritableFontData* data, ReadableFontData* master_data);
+ Builder(ReadableFontData* data, ReadableFontData* master_data);
+
+ void SetNumberOfIndexSubTables(int32_t count);
+ IndexSubTable::Builder* SearchIndexSubTables(int32_t glyph_id);
+ IndexSubTable::Builder* LinearSearchIndexSubTables(int32_t glyph_id);
+ IndexSubTable::Builder* BinarySearchIndexSubTables(int32_t glyph_id);
+ IndexSubTableBuilderList* GetIndexSubTableBuilders();
+ void Initialize(ReadableFontData* data);
+ CALLER_ATTACH IndexSubTable::Builder* CreateIndexSubTableBuilder(
+ int32_t index);
+
+ IndexSubTableBuilderList index_sub_tables_;
+ };
+
+ virtual ~BitmapSizeTable();
+
+ int32_t IndexSubTableArrayOffset();
+ int32_t IndexTableSize();
+ int32_t NumberOfIndexSubTables();
+ int32_t ColorRef();
+ // TODO(stuartg): SBitLineMetrics hori();
+ // TODO(stuartg): SBitLineMetrics vert();
+ int32_t StartGlyphIndex();
+ int32_t EndGlyphIndex();
+ int32_t PpemX();
+ int32_t PpemY();
+ int32_t BitDepth();
+ int32_t FlagsAsInt();
+
+ // Note: renamed from indexSubTable()
+ IndexSubTable* GetIndexSubTable(int32_t index);
+ int32_t GlyphOffset(int32_t glyph_id);
+ int32_t GlyphLength(int32_t glyph_id);
+ CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
+ int32_t GlyphFormat(int32_t glyph_id);
+
+ protected:
+ BitmapSizeTable(ReadableFontData* data,
+ ReadableFontData* master_data);
+
+ private:
+ static int32_t NumberOfIndexSubTables(ReadableFontData* data,
+ int32_t table_offset);
+ IndexSubTable* SearchIndexSubTables(int32_t glyph_id);
+ IndexSubTable* LinearSearchIndexSubTables(int32_t glyph_id);
+ IndexSubTable* BinarySearchIndexSubTables(int32_t glyph_id);
+ CALLER_ATTACH IndexSubTable* CreateIndexSubTable(int32_t index);
+ IndexSubTableList* GetIndexSubTableList();
+
+ Lock index_subtables_lock_;
+ IndexSubTableList index_subtables_;
+};
+typedef Ptr<BitmapSizeTable> BitmapSizeTablePtr;
+typedef std::vector<BitmapSizeTablePtr> BitmapSizeTableList;
+typedef Ptr<BitmapSizeTable::Builder> BitmapSizeTableBuilderPtr;
+typedef std::vector<BitmapSizeTableBuilderPtr> BitmapSizeTableBuilderList;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_BITMAP_SIZE_TABLE_H_
diff --git a/sfntly/table/bitmap/composite_bitmap_glyph.cc b/sfntly/table/bitmap/composite_bitmap_glyph.cc
new file mode 100644
index 0000000..ae7dc5a
--- /dev/null
+++ b/sfntly/table/bitmap/composite_bitmap_glyph.cc
@@ -0,0 +1,109 @@
+/*
+ * 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/table/bitmap/composite_bitmap_glyph.h"
+
+namespace sfntly {
+/******************************************************************************
+ * CompositeBitmapGlyph class
+ ******************************************************************************/
+CompositeBitmapGlyph::CompositeBitmapGlyph(ReadableFontData* data,
+ int32_t format)
+ : BitmapGlyph(data, format) {
+ Initialize(format);
+}
+
+CompositeBitmapGlyph::~CompositeBitmapGlyph() {
+}
+
+int32_t CompositeBitmapGlyph::NumComponents() {
+ return data_->ReadUShort(num_components_offset_);
+}
+
+CompositeBitmapGlyph::Component CompositeBitmapGlyph::GetComponent(
+ int32_t component_num) const {
+ int32_t component_offset = component_array_offset_ +
+ component_num * Offset::kEbdtComponentLength;
+ return CompositeBitmapGlyph::Component(
+ data_->ReadUShort(component_offset + Offset::kEbdtComponent_glyphCode),
+ data_->ReadChar(component_offset + Offset::kEbdtComponent_xOffset),
+ data_->ReadChar(component_offset + Offset::kEbdtComponent_yOffset));
+}
+
+void CompositeBitmapGlyph::Initialize(int32_t format) {
+ if (format == 8) {
+ num_components_offset_ = Offset::kGlyphFormat8_numComponents;
+ component_array_offset_ = Offset::kGlyphFormat8_componentArray;
+ } else if (format == 9) {
+ num_components_offset_ = Offset::kGlyphFormat9_numComponents;
+ component_array_offset_ = Offset::kGlyphFormat9_componentArray;
+ } else {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalStateException("Attempt to create a Composite Bitmap Glyph "
+ "with a non-composite format.");
+#endif
+ }
+}
+
+/******************************************************************************
+ * CompositeBitmapGlyph::Component class
+ ******************************************************************************/
+CompositeBitmapGlyph::Component::Component(const Component& rhs)
+ : glyph_code_(rhs.glyph_code_),
+ x_offset_(rhs.x_offset_),
+ y_offset_(rhs.y_offset_) {
+}
+
+bool CompositeBitmapGlyph::Component::operator==(
+ const CompositeBitmapGlyph::Component& rhs) {
+ return glyph_code_ == rhs.glyph_code_;
+}
+
+CompositeBitmapGlyph::Component& CompositeBitmapGlyph::Component::operator=(
+ const CompositeBitmapGlyph::Component& rhs) {
+ glyph_code_ = rhs.glyph_code_;
+ x_offset_ = rhs.x_offset_;
+ y_offset_ = rhs.y_offset_;
+ return *this;
+}
+
+CompositeBitmapGlyph::Component::Component(int32_t glyph_code,
+ int32_t x_offset,
+ int32_t y_offset)
+ : glyph_code_(glyph_code), x_offset_(x_offset), y_offset_(y_offset) {
+}
+
+/******************************************************************************
+ * CompositeBitmapGlyph::Builder class
+ ******************************************************************************/
+CompositeBitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format)
+ : BitmapGlyph::Builder(data, format) {
+}
+
+CompositeBitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format)
+ : BitmapGlyph::Builder(data, format) {
+}
+
+CompositeBitmapGlyph::Builder::~Builder() {
+}
+
+CALLER_ATTACH FontDataTable*
+CompositeBitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) {
+ Ptr<CompositeBitmapGlyph> glyph = new CompositeBitmapGlyph(data, format());
+ return glyph.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/composite_bitmap_glyph.h b/sfntly/table/bitmap/composite_bitmap_glyph.h
new file mode 100644
index 0000000..897db7e
--- /dev/null
+++ b/sfntly/table/bitmap/composite_bitmap_glyph.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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_COMPOSITE_BITMAP_GLYPH_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_COMPOSITE_BITMAP_GLYPH_H_
+
+#include "sfntly/table/bitmap/bitmap_glyph.h"
+
+namespace sfntly {
+
+class CompositeBitmapGlyph : public BitmapGlyph,
+ public RefCounted<CompositeBitmapGlyph> {
+ public:
+ class Component {
+ public:
+ Component(const Component& rhs);
+
+ int32_t glyph_code() { return glyph_code_; }
+ int32_t x_offset() { return x_offset_; }
+ int32_t y_offset() { return y_offset_; }
+
+ // UNIMPLEMENTED: int hashCode()
+ bool operator==(const Component& rhs);
+ Component& operator=(const Component& rhs);
+
+ protected:
+ Component(int32_t glyph_code, int32_t x_offset, int32_t y_offset);
+
+ private:
+ int32_t glyph_code_;
+ int32_t x_offset_;
+ int32_t y_offset_;
+
+ friend class CompositeBitmapGlyph;
+ };
+
+ class Builder : public BitmapGlyph::Builder,
+ public RefCounted<Builder> {
+ public:
+ Builder(WritableFontData* data, int32_t format);
+ Builder(ReadableFontData* data, int32_t format);
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ };
+
+ CompositeBitmapGlyph(ReadableFontData* data, int32_t format);
+ virtual ~CompositeBitmapGlyph();
+ int32_t NumComponents();
+ // Note: returned immutable object over stack.
+ Component GetComponent(int32_t component_num) const;
+
+ private:
+ void Initialize(int32_t format);
+
+ int32_t num_components_offset_;
+ int32_t component_array_offset_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_COMPOSITE_BITMAP_GLYPH_H_
diff --git a/sfntly/table/bitmap/ebdt_table.cc b/sfntly/table/bitmap/ebdt_table.cc
new file mode 100644
index 0000000..eeb1fa0
--- /dev/null
+++ b/sfntly/table/bitmap/ebdt_table.cc
@@ -0,0 +1,236 @@
+/*
+ * 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/table/bitmap/ebdt_table.h"
+
+#include <stdlib.h>
+
+#include "sfntly/table/bitmap/composite_bitmap_glyph.h"
+#include "sfntly/table/bitmap/simple_bitmap_glyph.h"
+
+namespace sfntly {
+/******************************************************************************
+ * EbdtTable class
+ ******************************************************************************/
+EbdtTable::~EbdtTable() {
+}
+
+int32_t EbdtTable::Version() {
+ return data_->ReadFixed(Offset::kVersion);
+}
+
+CALLER_ATTACH
+BitmapGlyph* EbdtTable::Glyph(int32_t offset, int32_t length, int32_t format) {
+ ReadableFontDataPtr glyph_data;
+ glyph_data.Attach(down_cast<ReadableFontData*>(data_->Slice(offset, length)));
+ return BitmapGlyph::CreateGlyph(glyph_data, format);
+}
+
+EbdtTable::EbdtTable(Header* header, ReadableFontData* data)
+ : SubTableContainerTable(header, data) {
+}
+
+/******************************************************************************
+ * EbdtTable::Builder class
+ ******************************************************************************/
+EbdtTable::Builder::Builder(Header* header, WritableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+EbdtTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+EbdtTable::Builder::~Builder() {
+}
+
+CALLER_ATTACH FontDataTable*
+ EbdtTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new EbdtTable(header(), data);
+ return table.Detach();
+}
+
+void EbdtTable::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t EbdtTable::Builder::SubDataSizeToSerialize() {
+ if (glyph_builders_.empty()) {
+ return 0;
+ }
+ bool fixed = true;
+ int32_t size = Offset::kHeaderLength;
+ for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(),
+ builder_end = glyph_builders_.end();
+ builder_map != builder_end;
+ builder_map++) {
+ for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(),
+ glyph_entry_end = builder_map->end();
+ glyph_entry != glyph_entry_end;
+ glyph_entry++) {
+ int32_t glyph_size = glyph_entry->second->SubDataSizeToSerialize();
+ size += abs(glyph_size);
+ fixed = (glyph_size <= 0) ? false : fixed;
+ }
+ }
+ return (fixed ? 1 : -1) * size;
+}
+
+bool EbdtTable::Builder::SubReadyToSerialize() {
+ if (glyph_builders_.empty()) {
+ return false;
+ }
+ return true;
+}
+
+int32_t EbdtTable::Builder::SubSerialize(WritableFontData* new_data) {
+ int32_t size = 0;
+ size += new_data->WriteFixed(Offset::kVersion, kVersion);
+ for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(),
+ builder_end = glyph_builders_.end();
+ builder_map != builder_end;
+ builder_map++) {
+ for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(),
+ glyph_entry_end = builder_map->end();
+ glyph_entry != glyph_entry_end;
+ glyph_entry++) {
+ WritableFontDataPtr slice;
+ slice.Attach(down_cast<WritableFontData*>(new_data->Slice(size)));
+ size += glyph_entry->second->SubSerialize(slice);
+ }
+ }
+ return size;
+}
+
+void EbdtTable::Builder::SetLoca(BitmapLocaList* loca_list) {
+ assert(loca_list);
+ Revert();
+ glyph_loca_.resize(loca_list->size());
+ std::copy(loca_list->begin(), loca_list->end(), glyph_loca_.begin());
+}
+
+void EbdtTable::Builder::GenerateLocaList(BitmapLocaList* output) {
+ assert(output);
+ output->clear();
+
+ if (glyph_builders_.empty()) {
+ if (glyph_loca_.empty()) {
+ return;
+ }
+ }
+
+ int start_offset = Offset::kHeaderLength;
+ for (BitmapGlyphBuilderList::iterator builder_map = glyph_builders_.begin(),
+ builder_end = glyph_builders_.end();
+ builder_map != builder_end;
+ builder_map++) {
+ BitmapGlyphInfoMap new_loca_map;
+ int32_t glyph_offset = 0;
+ for (BitmapGlyphBuilderMap::iterator glyph_entry = builder_map->begin(),
+ glyph_end = builder_map->end();
+ glyph_entry != glyph_end;
+ glyph_entry++) {
+ BitmapGlyphBuilderPtr builder = glyph_entry->second;
+ int32_t size = builder->SubDataSizeToSerialize();
+ BitmapGlyphInfoPtr info = new BitmapGlyphInfo(glyph_entry->first,
+ start_offset + glyph_offset, size, builder->format());
+ new_loca_map[glyph_entry->first] = info;
+ glyph_offset += size;
+ }
+ start_offset += glyph_offset;
+ output->push_back(new_loca_map);
+ }
+}
+
+BitmapGlyphBuilderList* EbdtTable::Builder::GlyphBuilders() {
+ return GetGlyphBuilders();
+}
+
+void EbdtTable::Builder::SetGlyphBuilders(
+ BitmapGlyphBuilderList* glyph_builders) {
+ glyph_builders_.clear();
+ std::copy(glyph_builders->begin(), glyph_builders->end(),
+ glyph_builders_.begin());
+ set_model_changed();
+}
+
+void EbdtTable::Builder::Revert() {
+ glyph_loca_.clear();
+ glyph_builders_.clear();
+ set_model_changed(false);
+}
+
+CALLER_ATTACH
+EbdtTable::Builder* EbdtTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<EbdtTable::Builder> builder;
+ builder = new Builder(header, data);
+ return builder.Detach();
+}
+
+CALLER_ATTACH
+EbdtTable::Builder* EbdtTable::Builder::CreateBuilder(Header* header,
+ ReadableFontData* data) {
+ Ptr<EbdtTable::Builder> builder;
+ builder = new Builder(header, data);
+ return builder.Detach();
+}
+
+BitmapGlyphBuilderList* EbdtTable::Builder::GetGlyphBuilders() {
+ if (glyph_builders_.empty()) {
+ if (glyph_loca_.empty()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalStateException(
+ "Loca values not set - unable to parse glyph data.");
+#endif
+ return NULL;
+ }
+ Initialize(InternalReadData(), &glyph_loca_, &glyph_builders_);
+ set_model_changed();
+ }
+ return &glyph_builders_;
+}
+
+void EbdtTable::Builder::Initialize(ReadableFontData* data,
+ BitmapLocaList* loca_list,
+ BitmapGlyphBuilderList* output) {
+ assert(loca_list);
+ assert(output);
+
+ output->clear();
+ if (data) {
+ for (BitmapLocaList::iterator loca_map = loca_list->begin(),
+ loca_end = loca_list->end();
+ loca_map != loca_end; loca_map++) {
+ BitmapGlyphBuilderMap glyph_builder_map;
+ for (BitmapGlyphInfoMap::iterator entry = loca_map->begin(),
+ entry_end = loca_map->end();
+ entry != entry_end; entry++) {
+ BitmapGlyphInfoPtr info = entry->second;
+ ReadableFontDataPtr slice;
+ slice.Attach(down_cast<ReadableFontData*>(data->Slice(
+ info->offset(), info->length())));
+ BitmapGlyphBuilderPtr glyph_builder;
+ glyph_builder.Attach(BitmapGlyph::Builder::CreateGlyphBuilder(
+ slice, info->format()));
+ glyph_builder_map[entry->first] = glyph_builder;
+ }
+ output->push_back(glyph_builder_map);
+ }
+ }
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/ebdt_table.h b/sfntly/table/bitmap/ebdt_table.h
new file mode 100644
index 0000000..d138c14
--- /dev/null
+++ b/sfntly/table/bitmap/ebdt_table.h
@@ -0,0 +1,108 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_EBDT_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBDT_TABLE_H_
+
+#include "sfntly/table/bitmap/bitmap_glyph.h"
+#include "sfntly/table/bitmap/bitmap_glyph_info.h"
+#include "sfntly/table/subtable_container_table.h"
+
+namespace sfntly {
+
+class EbdtTable : public SubTableContainerTable,
+ public RefCounted<EbdtTable> {
+ public:
+ struct Offset {
+ enum {
+ kVersion = 0,
+ kHeaderLength = DataSize::kFixed,
+ };
+ };
+
+ class Builder : public SubTableContainerTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual void SubDataSet();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ void SetLoca(BitmapLocaList* loca_list);
+ void GenerateLocaList(BitmapLocaList* output);
+
+ // Gets the List of glyph builders for the glyph table builder. These may be
+ // manipulated in any way by the caller and the changes will be reflected in
+ // the final glyph table produced.
+ // If there is no current data for the glyph builder or the glyph builders
+ // have not been previously set then this will return an empty glyph builder
+ // List. If there is current data (i.e. data read from an existing font) and
+ // the loca list has not been set or is null, empty, or invalid, then an
+ // empty glyph builder List will be returned.
+ // @return the list of glyph builders
+ BitmapGlyphBuilderList* GlyphBuilders();
+
+ // Replace the internal glyph builders with the one provided. The provided
+ // list and all contained objects belong to this builder.
+ // This call is only required if the entire set of glyphs in the glyph
+ // table builder are being replaced. If the glyph builder list provided from
+ // the {@link EbdtTable.Builder#glyphBuilders()} is being used and modified
+ // then those changes will already be reflected in the glyph table builder.
+ // @param glyphBuilders the new glyph builders
+ void SetGlyphBuilders(BitmapGlyphBuilderList* glyph_builders);
+
+ void Revert();
+
+ // Create a new builder using the header information and data provided.
+ // @param header the header information
+ // @param data the data holding the table
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ ReadableFontData* data);
+
+ private:
+ BitmapGlyphBuilderList* GetGlyphBuilders();
+ static void Initialize(ReadableFontData* data,
+ BitmapLocaList* loca_list,
+ BitmapGlyphBuilderList* output);
+
+ static const int32_t kVersion = 0x00020000; // TODO(stuartg): const/enum
+ BitmapLocaList glyph_loca_;
+ BitmapGlyphBuilderList glyph_builders_;
+ };
+
+ virtual ~EbdtTable();
+ int32_t Version();
+ CALLER_ATTACH BitmapGlyph* Glyph(int32_t offset,
+ int32_t length,
+ int32_t format);
+ protected:
+ EbdtTable(Header* header, ReadableFontData* data);
+};
+typedef Ptr<EbdtTable> EbdtTablePtr;
+typedef Ptr<EbdtTable::Builder> EbdtTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBDT_TABLE_H_
diff --git a/sfntly/table/bitmap/eblc_table.cc b/sfntly/table/bitmap/eblc_table.cc
new file mode 100644
index 0000000..0ad2764
--- /dev/null
+++ b/sfntly/table/bitmap/eblc_table.cc
@@ -0,0 +1,313 @@
+/*
+ * 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/table/bitmap/eblc_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "sfntly/math/font_math.h"
+
+namespace sfntly {
+/******************************************************************************
+ * EblcTable class
+ ******************************************************************************/
+int32_t EblcTable::Version() {
+ return data_->ReadFixed(Offset::kVersion);
+}
+
+int32_t EblcTable::NumSizes() {
+ return data_->ReadULongAsInt(Offset::kNumSizes);
+}
+
+BitmapSizeTable* EblcTable::GetBitmapSizeTable(int32_t index) {
+ if (index < 0 || index > NumSizes()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException(
+ "Size table index is outside the range of tables.");
+#endif
+ return NULL;
+ }
+ BitmapSizeTableList* bitmap_size_table_list = GetBitmapSizeTableList();
+ if (bitmap_size_table_list) {
+ return (*bitmap_size_table_list)[index];
+ }
+ return NULL;
+}
+
+EblcTable::EblcTable(Header* header, ReadableFontData* data)
+ : SubTableContainerTable(header, data) {
+}
+
+BitmapSizeTableList* EblcTable::GetBitmapSizeTableList() {
+ AutoLock lock(bitmap_size_table_lock_);
+ if (bitmap_size_table_.empty()) {
+ CreateBitmapSizeTable(data_, NumSizes(), &bitmap_size_table_);
+ }
+ return &bitmap_size_table_;
+}
+
+// static
+void EblcTable::CreateBitmapSizeTable(ReadableFontData* data,
+ int32_t num_sizes,
+ BitmapSizeTableList* output) {
+ assert(data);
+ assert(output);
+ for (int32_t i = 0; i < num_sizes; ++i) {
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(Offset::kBitmapSizeTableArrayStart +
+ i * Offset::kBitmapSizeTableLength,
+ Offset::kBitmapSizeTableLength)));
+ BitmapSizeTableBuilderPtr size_builder;
+ size_builder.Attach(
+ BitmapSizeTable::Builder::CreateBuilder(new_data, data));
+ BitmapSizeTablePtr size;
+ size.Attach(down_cast<BitmapSizeTable*>(size_builder->Build()));
+ output->push_back(size);
+ }
+}
+
+/******************************************************************************
+ * EblcTable::Builder class
+ ******************************************************************************/
+EblcTable::Builder::Builder(Header* header, WritableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+EblcTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+EblcTable::Builder::~Builder() {
+}
+
+int32_t EblcTable::Builder::SubSerialize(WritableFontData* new_data) {
+ // header
+ int32_t size = new_data->WriteFixed(0, kVersion);
+ size += new_data->WriteULong(size, size_table_builders_.size());
+
+ // calculate the offsets
+ // offset to the start of the size table array
+ int32_t size_table_start_offset = size;
+ // walking offset in the size table array
+ int32_t size_table_offset = size_table_start_offset;
+ // offset to the start of the whole index subtable block
+ int32_t sub_table_block_start_offset = size_table_offset +
+ size_table_builders_.size() * Offset::kBitmapSizeTableLength;
+ // walking offset in the index subtable
+ // points to the start of the current subtable block
+ int32_t current_sub_table_block_start_offset = sub_table_block_start_offset;
+
+#if defined (SFNTLY_DEBUG_BITMAP)
+ int32_t size_index = 0;
+#endif
+ for (BitmapSizeTableBuilderList::iterator
+ size_builder = size_table_builders_.begin(),
+ size_builder_end = size_table_builders_.end();
+ size_builder != size_builder_end; size_builder++) {
+ (*size_builder)->SetIndexSubTableArrayOffset(
+ current_sub_table_block_start_offset);
+ IndexSubTableBuilderList* index_sub_table_builder_list =
+ (*size_builder)->IndexSubTableBuilders();
+
+ // walking offset within the current subTable array
+ int32_t index_sub_table_array_offset = current_sub_table_block_start_offset;
+ // walking offset within the subTable entries
+ int32_t index_sub_table_offset = index_sub_table_array_offset +
+ index_sub_table_builder_list->size() * Offset::kIndexSubHeaderLength;
+
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "size %d: sizeTable=%x, current subTable Block=%x, ",
+ size_index, size_table_offset,
+ current_sub_table_block_start_offset);
+ fprintf(stderr, "index subTableStart=%x\n", index_sub_table_offset);
+ size_index++;
+ int32_t sub_table_index = 0;
+#endif
+ for (IndexSubTableBuilderList::iterator
+ index_sub_table_builder = index_sub_table_builder_list->begin(),
+ index_sub_table_builder_end = index_sub_table_builder_list->end();
+ index_sub_table_builder != index_sub_table_builder_end;
+ index_sub_table_builder++) {
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "\tsubTableIndex %d: format=%x, ", sub_table_index,
+ (*index_sub_table_builder)->index_format());
+ fprintf(stderr, "indexSubTableArrayOffset=%x, indexSubTableOffset=%x\n",
+ index_sub_table_array_offset, index_sub_table_offset);
+ sub_table_index++;
+#endif
+ // array entry
+ index_sub_table_array_offset += new_data->WriteUShort(
+ index_sub_table_array_offset,
+ (*index_sub_table_builder)->first_glyph_index());
+ index_sub_table_array_offset += new_data->WriteUShort(
+ index_sub_table_array_offset,
+ (*index_sub_table_builder)->last_glyph_index());
+ index_sub_table_array_offset += new_data->WriteULong(
+ index_sub_table_array_offset,
+ index_sub_table_offset - current_sub_table_block_start_offset);
+
+ // index sub table
+ WritableFontDataPtr slice_index_sub_table;
+ slice_index_sub_table.Attach(down_cast<WritableFontData*>(
+ new_data->Slice(index_sub_table_offset)));
+ int32_t current_sub_table_size =
+ (*index_sub_table_builder)->SubSerialize(slice_index_sub_table);
+ int32_t padding = FontMath::PaddingRequired(current_sub_table_size,
+ DataSize::kULONG);
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "\t\tsubTableSize = %x, padding = %x\n",
+ current_sub_table_size, padding);
+#endif
+ index_sub_table_offset += current_sub_table_size;
+ index_sub_table_offset +=
+ new_data->WritePadding(index_sub_table_offset, padding);
+ }
+
+ // serialize size table
+ (*size_builder)->SetIndexTableSize(
+ index_sub_table_offset - current_sub_table_block_start_offset);
+ WritableFontDataPtr slice_size_table;
+ slice_size_table.Attach(down_cast<WritableFontData*>(
+ new_data->Slice(size_table_offset)));
+ size_table_offset += (*size_builder)->SubSerialize(slice_size_table);
+
+ current_sub_table_block_start_offset = index_sub_table_offset;
+ }
+ return size + current_sub_table_block_start_offset;
+}
+
+bool EblcTable::Builder::SubReadyToSerialize() {
+ if (size_table_builders_.empty()) {
+ return false;
+ }
+ for (BitmapSizeTableBuilderList::iterator b = size_table_builders_.begin(),
+ e = size_table_builders_.end();
+ b != e; b++) {
+ if (!(*b)->SubReadyToSerialize()) {
+ return false;
+ }
+ }
+ return true;
+}
+
+int32_t EblcTable::Builder::SubDataSizeToSerialize() {
+ if (size_table_builders_.empty()) {
+ return 0;
+ }
+ int32_t size = Offset::kHeaderLength;
+ bool variable = false;
+#if defined (SFNTLY_DEBUG_BITMAP)
+ size_t size_index = 0;
+#endif
+ for (BitmapSizeTableBuilderList::iterator b = size_table_builders_.begin(),
+ e = size_table_builders_.end();
+ b != e; b++) {
+ int32_t size_builder_size = (*b)->SubDataSizeToSerialize();
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "sizeIndex = %d, sizeBuilderSize=0x%x (%d)\n",
+ size_index++, size_builder_size, size_builder_size);
+#endif
+ variable = size_builder_size > 0 ? variable : true;
+ size += abs(size_builder_size);
+ }
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "eblc size=%d\n", size);
+#endif
+ return variable ? -size : size;
+}
+
+void EblcTable::Builder::SubDataSet() {
+ Revert();
+}
+
+BitmapSizeTableBuilderList* EblcTable::Builder::BitmapSizeBuilders() {
+ return GetSizeList();
+}
+
+void EblcTable::Builder::Revert() {
+ size_table_builders_.clear();
+ set_model_changed(false);
+}
+
+void EblcTable::Builder::GenerateLocaList(BitmapLocaList* output) {
+ assert(output);
+ BitmapSizeTableBuilderList* size_builder_list = GetSizeList();
+ output->clear();
+#if defined (SFNTLY_DEBUG_BITMAP)
+ int32_t size_index = 0;
+#endif
+ for (BitmapSizeTableBuilderList::iterator b = size_builder_list->begin(),
+ e = size_builder_list->end();
+ b != e; b++) {
+#if defined (SFNTLY_DEBUG_BITMAP)
+ fprintf(stderr, "size table = %d\n", size_index++);
+#endif
+ BitmapGlyphInfoMap loca_map;
+ (*b)->GenerateLocaMap(&loca_map);
+ output->push_back(loca_map);
+ }
+}
+
+CALLER_ATTACH
+FontDataTable* EblcTable::Builder::SubBuildTable(ReadableFontData* data) {
+ Ptr<EblcTable> new_table = new EblcTable(header(), data);
+ return new_table.Detach();
+}
+
+// static
+CALLER_ATTACH EblcTable::Builder*
+ EblcTable::Builder::CreateBuilder(Header* header, WritableFontData* data) {
+ Ptr<EblcTable::Builder> new_builder = new EblcTable::Builder(header, data);
+ return new_builder.Detach();
+}
+
+// static
+CALLER_ATTACH EblcTable::Builder*
+ EblcTable::Builder::CreateBuilder(Header* header, ReadableFontData* data) {
+ Ptr<EblcTable::Builder> new_builder = new EblcTable::Builder(header, data);
+ return new_builder.Detach();
+}
+
+BitmapSizeTableBuilderList* EblcTable::Builder::GetSizeList() {
+ if (size_table_builders_.empty()) {
+ Initialize(InternalReadData(), &size_table_builders_);
+ set_model_changed();
+ }
+ return &size_table_builders_;
+}
+
+void EblcTable::Builder::Initialize(ReadableFontData* data,
+ BitmapSizeTableBuilderList* output) {
+ assert(output);
+ if (data) {
+ int32_t num_sizes = data->ReadULongAsInt(Offset::kNumSizes);
+ for (int32_t i = 0; i < num_sizes; ++i) {
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(Offset::kBitmapSizeTableArrayStart +
+ i * Offset::kBitmapSizeTableLength,
+ Offset::kBitmapSizeTableLength)));
+ BitmapSizeTableBuilderPtr size_builder;
+ size_builder.Attach(BitmapSizeTable::Builder::CreateBuilder(
+ new_data, data));
+ output->push_back(size_builder);
+ }
+ }
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/eblc_table.h b/sfntly/table/bitmap/eblc_table.h
new file mode 100644
index 0000000..b04338a
--- /dev/null
+++ b/sfntly/table/bitmap/eblc_table.h
@@ -0,0 +1,194 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_
+
+#include "sfntly/port/lock.h"
+#include "sfntly/table/bitmap/big_glyph_metrics.h"
+#include "sfntly/table/bitmap/bitmap_glyph.h"
+#include "sfntly/table/bitmap/bitmap_size_table.h"
+#include "sfntly/table/subtable_container_table.h"
+
+namespace sfntly {
+
+class EblcTable : public SubTableContainerTable,
+ public RefCounted<EblcTable> {
+ public:
+ struct Offset {
+ enum {
+ // header
+ kVersion = 0,
+ kNumSizes = 4,
+ kHeaderLength = kNumSizes + DataSize::kULONG,
+
+ // bitmapSizeTable
+ kBitmapSizeTableArrayStart = kHeaderLength,
+ kBitmapSizeTableLength = 48,
+ kBitmapSizeTable_indexSubTableArrayOffset = 0,
+ kBitmapSizeTable_indexTableSize = 4,
+ kBitmapSizeTable_numberOfIndexSubTables = 8,
+ kBitmapSizeTable_colorRef = 12,
+ kBitmapSizeTable_hori = 16,
+ kBitmapSizeTable_vert = 28,
+ kBitmapSizeTable_startGlyphIndex = 40,
+ kBitmapSizeTable_endGlyphIndex = 42,
+ kBitmapSizeTable_ppemX = 44,
+ kBitmapSizeTable_ppemY = 45,
+ kBitmapSizeTable_bitDepth = 46,
+ kBitmapSizeTable_flags = 47,
+
+ // sbitLineMetrics
+ kSbitLineMetricsLength = 12,
+ kSbitLineMetrics_ascender = 0,
+ kSbitLineMetrics_descender = 1,
+ kSbitLineMetrics_widthMax = 2,
+ kSbitLineMetrics_caretSlopeNumerator = 3,
+ kSbitLineMetrics_caretSlopeDenominator = 4,
+ kSbitLineMetrics_caretOffset = 5,
+ kSbitLineMetrics_minOriginSB = 6,
+ kSbitLineMetrics_minAdvanceSB = 7,
+ kSbitLineMetrics_maxBeforeBL = 8,
+ kSbitLineMetrics_minAfterBL = 9,
+ kSbitLineMetrics_pad1 = 10,
+ kSbitLineMetrics_pad2 = 11,
+
+ // indexSubTable
+ kIndexSubTableEntryLength = 8,
+ kIndexSubTableEntry_firstGlyphIndex = 0,
+ kIndexSubTableEntry_lastGlyphIndex = 2,
+ kIndexSubTableEntry_additionalOffsetToIndexSubTable = 4,
+
+ // indexSubHeader
+ kIndexSubHeaderLength = 8,
+ kIndexSubHeader_indexFormat = 0,
+ kIndexSubHeader_imageFormat = 2,
+ kIndexSubHeader_imageDataOffset = 4,
+
+ // indexSubTable - all offset relative to the subtable start
+
+ // indexSubTable1
+ kIndexSubTable1_offsetArray = kIndexSubHeaderLength,
+ kIndexSubTable1_builderDataSize = kIndexSubHeaderLength,
+
+ // kIndexSubTable2
+ kIndexSubTable2Length = kIndexSubHeaderLength +
+ DataSize::kULONG +
+ BitmapGlyph::Offset::kBigGlyphMetricsLength,
+ kIndexSubTable2_imageSize = kIndexSubHeaderLength,
+ kIndexSubTable2_bigGlyphMetrics = kIndexSubTable2_imageSize +
+ DataSize::kULONG,
+ kIndexSubTable2_builderDataSize = kIndexSubTable2_bigGlyphMetrics +
+ BigGlyphMetrics::Offset::kMetricsLength,
+
+ // kIndexSubTable3
+ kIndexSubTable3_offsetArray = kIndexSubHeaderLength,
+ kIndexSubTable3_builderDataSize = kIndexSubTable3_offsetArray,
+
+ // kIndexSubTable4
+ kIndexSubTable4_numGlyphs = kIndexSubHeaderLength,
+ kIndexSubTable4_glyphArray = kIndexSubTable4_numGlyphs +
+ DataSize::kULONG,
+ kIndexSubTable4_codeOffsetPairLength = 2 * DataSize::kUSHORT,
+ kIndexSubTable4_codeOffsetPair_glyphCode = 0,
+ kIndexSubTable4_codeOffsetPair_offset = DataSize::kUSHORT,
+ kIndexSubTable4_builderDataSize = kIndexSubTable4_glyphArray,
+
+ // kIndexSubTable5
+ kIndexSubTable5_imageSize = kIndexSubHeaderLength,
+ kIndexSubTable5_bigGlyphMetrics = kIndexSubTable5_imageSize +
+ DataSize::kULONG,
+ kIndexSubTable5_numGlyphs = kIndexSubTable5_bigGlyphMetrics +
+ BitmapGlyph::Offset::kBigGlyphMetricsLength,
+ kIndexSubTable5_glyphArray = kIndexSubTable5_numGlyphs +
+ DataSize::kULONG,
+ kIndexSubTable5_builderDataSize = kIndexSubTable5_glyphArray,
+
+ // codeOffsetPair
+ kCodeOffsetPairLength = 2 * DataSize::kUSHORT,
+ kCodeOffsetPair_glyphCode = 0,
+ kCodeOffsetPair_offset = DataSize::kUSHORT,
+ };
+ };
+
+ class Builder : public SubTableContainerTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual void SubDataSet();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ BitmapSizeTableBuilderList* BitmapSizeBuilders();
+ void Revert();
+
+ // Generates the loca list for the EBDT table. The list is intended to be
+ // used by the EBDT to allow it to parse the glyph data and generate glyph
+ // objects. After returning from this method the list belongs to the caller.
+ // The list entries are in the same order as the size table builders are at
+ // the time of this call.
+ // @return the list of loca maps with one for each size table builder
+ void GenerateLocaList(BitmapLocaList* output);
+
+ // Create a new builder using the header information and data provided.
+ // @param header the header information
+ // @param data the data holding the table
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ ReadableFontData* data);
+
+ private:
+ BitmapSizeTableBuilderList* GetSizeList();
+ void Initialize(ReadableFontData* data, BitmapSizeTableBuilderList* output);
+
+ static const int32_t kVersion = 0x00020000;
+ BitmapSizeTableBuilderList size_table_builders_;
+ };
+
+ int32_t Version();
+ int32_t NumSizes();
+ // UNIMPLEMENTED: toString()
+
+ BitmapSizeTable* GetBitmapSizeTable(int32_t index);
+
+ static const int32_t NOTDEF = -1;
+
+ protected:
+ EblcTable(Header* header, ReadableFontData* data);
+
+ private:
+ BitmapSizeTableList* GetBitmapSizeTableList();
+
+ static void CreateBitmapSizeTable(ReadableFontData* data,
+ int32_t num_sizes,
+ BitmapSizeTableList* output);
+
+ Lock bitmap_size_table_lock_;
+ BitmapSizeTableList bitmap_size_table_;
+};
+typedef Ptr<EblcTable> EblcTablePtr;
+typedef Ptr<EblcTable::Builder> EblcTableBuilderPtr;
+}
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBLC_TABLE_H_
diff --git a/sfntly/table/bitmap/ebsc_table.cc b/sfntly/table/bitmap/ebsc_table.cc
new file mode 100644
index 0000000..458c2d4
--- /dev/null
+++ b/sfntly/table/bitmap/ebsc_table.cc
@@ -0,0 +1,107 @@
+/*
+ * 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/table/bitmap/ebsc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * EbscTable class
+ ******************************************************************************/
+EbscTable::~EbscTable() {
+}
+
+int32_t EbscTable::Version() {
+ return data_->ReadFixed(Offset::kVersion);
+}
+
+int32_t EbscTable::NumSizes() {
+ return data_->ReadULongAsInt(Offset::kNumSizes);
+}
+
+EbscTable::EbscTable(Header* header, ReadableFontData* data)
+ : Table(header, data) {
+}
+
+/******************************************************************************
+ * EbscTable::BitmapScaleTable class
+ ******************************************************************************/
+EbscTable::BitmapScaleTable::~BitmapScaleTable() {
+}
+
+EbscTable::BitmapScaleTable::BitmapScaleTable(ReadableFontData* data)
+ : SubTable(data) {
+}
+
+int32_t EbscTable::BitmapScaleTable::PpemX() {
+ return data_->ReadByte(Offset::kBitmapScaleTable_ppemX);
+}
+
+int32_t EbscTable::BitmapScaleTable::PpemY() {
+ return data_->ReadByte(Offset::kBitmapScaleTable_ppemY);
+}
+
+int32_t EbscTable::BitmapScaleTable::SubstitutePpemX() {
+ return data_->ReadByte(Offset::kBitmapScaleTable_substitutePpemX);
+}
+
+int32_t EbscTable::BitmapScaleTable::SubstitutePpemY() {
+ return data_->ReadByte(Offset::kBitmapScaleTable_substitutePpemY);
+}
+
+/******************************************************************************
+ * EbscTable::Builder class
+ ******************************************************************************/
+EbscTable::Builder::~Builder() {
+}
+
+CALLER_ATTACH EbscTable::Builder* EbscTable::Builder::CreateBuilder(
+ Header* header, WritableFontData* data) {
+ EbscTableBuilderPtr builder = new EbscTable::Builder(header, data);
+ return builder.Detach();
+}
+
+EbscTable::Builder::Builder(Header* header, WritableFontData* data)
+ : Table::Builder(header, data) {
+}
+
+EbscTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : Table::Builder(header, data) {
+}
+
+CALLER_ATTACH
+FontDataTable* EbscTable::Builder::SubBuildTable(ReadableFontData* data) {
+ EbscTablePtr output = new EbscTable(header(), data);
+ return output.Detach();
+}
+
+void EbscTable::Builder::SubDataSet() {
+ // NOP
+}
+
+int32_t EbscTable::Builder::SubDataSizeToSerialize() {
+ return 0;
+}
+
+bool EbscTable::Builder::SubReadyToSerialize() {
+ return false;
+}
+
+int32_t EbscTable::Builder::SubSerialize(WritableFontData* new_data) {
+ UNREFERENCED_PARAMETER(new_data);
+ return 0;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/ebsc_table.h b/sfntly/table/bitmap/ebsc_table.h
new file mode 100644
index 0000000..b79df38
--- /dev/null
+++ b/sfntly/table/bitmap/ebsc_table.h
@@ -0,0 +1,101 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+
+class EbscTable : public Table,
+ public RefCounted<EbscTable> {
+ public:
+ struct Offset {
+ enum {
+ // header
+ kVersion = 0,
+ kNumSizes = DataSize::kFixed,
+ kHeaderLength = kNumSizes + DataSize::kULONG,
+ kBitmapScaleTableStart = kHeaderLength,
+
+ // bitmapScaleTable
+ kBitmapScaleTable_hori = 0,
+ kBitmapScaleTable_vert = EblcTable::Offset::kSbitLineMetricsLength,
+ kBitmapScaleTable_ppemX = kBitmapScaleTable_vert +
+ EblcTable::Offset::kSbitLineMetricsLength,
+ kBitmapScaleTable_ppemY = kBitmapScaleTable_ppemX + DataSize::kBYTE,
+ kBitmapScaleTable_substitutePpemX = kBitmapScaleTable_ppemY +
+ DataSize::kBYTE,
+ kBitmapScaleTable_substitutePpemY = kBitmapScaleTable_substitutePpemX +
+ DataSize::kBYTE,
+ kBitmapScaleTableLength = kBitmapScaleTable_substitutePpemY +
+ DataSize::kBYTE,
+ };
+ };
+
+ class BitmapScaleTable : public SubTable,
+ public RefCounted<BitmapScaleTable> {
+ public:
+ virtual ~BitmapScaleTable();
+ int32_t PpemX();
+ int32_t PpemY();
+ int32_t SubstitutePpemX();
+ int32_t SubstitutePpemY();
+
+ protected:
+ // Note: caller to do data->Slice(offset, Offset::kBitmapScaleTableLength)
+ explicit BitmapScaleTable(ReadableFontData* data);
+ };
+
+ // TODO(stuartg): currently the builder just builds from initial data
+ // - need to make fully working but few if any examples to test with
+ class Builder : public Table::Builder,
+ public RefCounted<Builder> {
+ public:
+ virtual ~Builder();
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ protected:
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ };
+
+ virtual ~EbscTable();
+
+ int32_t Version();
+ int32_t NumSizes();
+ // Note: renamed from bitmapScaleTable
+ CALLER_ATTACH BitmapScaleTable* GetBitmapScaleTable(int32_t index);
+
+ private:
+ EbscTable(Header* header, ReadableFontData* data);
+ friend class Builder;
+};
+typedef Ptr<EbscTable> EbscTablePtr;
+typedef Ptr<EbscTable::Builder> EbscTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_EBSC_TABLE_H_
diff --git a/sfntly/table/bitmap/glyph_metrics.cc b/sfntly/table/bitmap/glyph_metrics.cc
new file mode 100644
index 0000000..e91eb99
--- /dev/null
+++ b/sfntly/table/bitmap/glyph_metrics.cc
@@ -0,0 +1,39 @@
+/*
+ * 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/table/bitmap/glyph_metrics.h"
+
+namespace sfntly {
+
+GlyphMetrics::~GlyphMetrics() {
+}
+
+GlyphMetrics::GlyphMetrics(ReadableFontData* data)
+ : SubTable(data) {
+}
+
+GlyphMetrics::Builder::~Builder() {
+}
+
+GlyphMetrics::Builder::Builder(WritableFontData* data)
+ : SubTable::Builder(data) {
+}
+
+GlyphMetrics::Builder::Builder(ReadableFontData* data)
+ : SubTable::Builder(data) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/glyph_metrics.h b/sfntly/table/bitmap/glyph_metrics.h
new file mode 100644
index 0000000..5f16aaa
--- /dev/null
+++ b/sfntly/table/bitmap/glyph_metrics.h
@@ -0,0 +1,43 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_GLYPH_METRICS_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_METRICS_H_
+
+#include "sfntly/table/subtable.h"
+
+namespace sfntly {
+
+class GlyphMetrics : public SubTable {
+ public:
+ virtual ~GlyphMetrics();
+
+ protected:
+ class Builder : public SubTable::Builder {
+ public:
+ virtual ~Builder();
+
+ protected:
+ explicit Builder(WritableFontData* data);
+ explicit Builder(ReadableFontData* data);
+ };
+
+ explicit GlyphMetrics(ReadableFontData* data);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_GLYPH_METRICS_H_
diff --git a/sfntly/table/bitmap/index_sub_table.cc b/sfntly/table/bitmap/index_sub_table.cc
new file mode 100644
index 0000000..5e29784
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table.cc
@@ -0,0 +1,278 @@
+/*
+ * 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/table/bitmap/index_sub_table.h"
+
+#include "sfntly/table/bitmap/eblc_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"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTable class
+ ******************************************************************************/
+CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::GlyphInfo(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return NULL;
+ }
+ if (GlyphStartOffset(glyph_id) == -1) {
+ return NULL;
+ }
+ BitmapGlyphInfoPtr output = new BitmapGlyphInfo(glyph_id,
+ image_data_offset(),
+ GlyphStartOffset(glyph_id),
+ GlyphLength(glyph_id),
+ image_format());
+ return output.Detach();
+}
+
+int32_t IndexSubTable::GlyphOffset(int32_t glyph_id) {
+ int32_t glyph_start_offset = GlyphStartOffset(glyph_id);
+ if (glyph_start_offset == -1) {
+ return -1;
+ }
+ return image_data_offset() + glyph_start_offset;
+}
+
+// static
+CALLER_ATTACH IndexSubTable*
+ IndexSubTable::CreateIndexSubTable(ReadableFontData* data,
+ int32_t offset_to_index_sub_table_array,
+ int32_t array_index) {
+ IndexSubTableBuilderPtr builder;
+ builder.Attach(IndexSubTable::Builder::CreateBuilder(
+ data, offset_to_index_sub_table_array, array_index));
+ return down_cast<IndexSubTable*>(builder->Build());
+}
+
+IndexSubTable::IndexSubTable(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : SubTable(data),
+ first_glyph_index_(first_glyph_index),
+ last_glyph_index_(last_glyph_index) {
+ index_format_ =
+ data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat);
+ image_format_ =
+ data_->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat);
+ image_data_offset_ =
+ data_->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset);
+}
+
+int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id) {
+ return CheckGlyphRange(glyph_id, first_glyph_index(), last_glyph_index());
+}
+
+// static
+int32_t IndexSubTable::CheckGlyphRange(int32_t glyph_id,
+ int32_t first_glyph_id,
+ int32_t last_glyph_id) {
+ if (glyph_id < first_glyph_id || glyph_id > last_glyph_id) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException("Glyph ID is outside of the allowed range.");
+#endif
+ return -1;
+ }
+ return glyph_id - first_glyph_id;
+}
+
+/******************************************************************************
+ * IndexSubTable::Builder class
+ ******************************************************************************/
+IndexSubTable::Builder::~Builder() {
+}
+
+void IndexSubTable::Builder::Revert() {
+ set_model_changed(false);
+ Initialize(InternalReadData());
+}
+
+CALLER_ATTACH BitmapGlyphInfo* IndexSubTable::Builder::GlyphInfo(
+ int32_t glyph_id) {
+ BitmapGlyphInfoPtr glyph_info =
+ new BitmapGlyphInfo(glyph_id,
+ image_data_offset(),
+ GlyphStartOffset(glyph_id),
+ GlyphLength(glyph_id),
+ image_format());
+ return glyph_info.Detach();
+}
+
+int32_t IndexSubTable::Builder::GlyphOffset(int32_t glyph_id) {
+ return image_data_offset() + GlyphStartOffset(glyph_id);
+}
+
+// static
+CALLER_ATTACH IndexSubTable::Builder*
+IndexSubTable::Builder::CreateBuilder(int32_t index_format) {
+ switch (index_format) {
+ case Format::FORMAT_1:
+ return IndexSubTableFormat1::Builder::CreateBuilder();
+ case Format::FORMAT_2:
+ return IndexSubTableFormat2::Builder::CreateBuilder();
+ case Format::FORMAT_3:
+ return IndexSubTableFormat3::Builder::CreateBuilder();
+ case Format::FORMAT_4:
+ return IndexSubTableFormat4::Builder::CreateBuilder();
+ case Format::FORMAT_5:
+ return IndexSubTableFormat5::Builder::CreateBuilder();
+ default:
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalArgumentException("Invalid index subtable format");
+#endif
+ return NULL;
+ }
+}
+
+// static
+CALLER_ATTACH IndexSubTable::Builder*
+IndexSubTable::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t offset_to_index_sub_table_array, int32_t array_index) {
+ int32_t index_sub_table_entry_offset =
+ offset_to_index_sub_table_array +
+ array_index * EblcTable::Offset::kIndexSubTableEntryLength;
+ int32_t first_glyph_index =
+ data->ReadUShort(index_sub_table_entry_offset +
+ EblcTable::Offset::kIndexSubTableEntry_firstGlyphIndex);
+ int32_t last_glyph_index =
+ data->ReadUShort(index_sub_table_entry_offset +
+ EblcTable::Offset::kIndexSubTableEntry_lastGlyphIndex);
+ int32_t additional_offset_to_index_subtable = data->ReadULongAsInt(
+ index_sub_table_entry_offset +
+ EblcTable::Offset::kIndexSubTableEntry_additionalOffsetToIndexSubTable);
+ int32_t index_sub_table_offset = offset_to_index_sub_table_array +
+ additional_offset_to_index_subtable;
+ int32_t index_format = data->ReadUShort(index_sub_table_offset);
+ switch (index_format) {
+ case 1:
+ return IndexSubTableFormat1::Builder::CreateBuilder(
+ data, index_sub_table_offset, first_glyph_index, last_glyph_index);
+ case 2:
+ return IndexSubTableFormat2::Builder::CreateBuilder(
+ data, index_sub_table_offset, first_glyph_index, last_glyph_index);
+ case 3:
+ return IndexSubTableFormat3::Builder::CreateBuilder(
+ data, index_sub_table_offset, first_glyph_index, last_glyph_index);
+ case 4:
+ return IndexSubTableFormat4::Builder::CreateBuilder(
+ data, index_sub_table_offset, first_glyph_index, last_glyph_index);
+ case 5:
+ return IndexSubTableFormat5::Builder::CreateBuilder(
+ data, index_sub_table_offset, first_glyph_index, last_glyph_index);
+ default:
+ // Unknown format and unable to process.
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalArgumentException("Invalid Index Subtable Format");
+#endif
+ break;
+ }
+ return NULL;
+}
+
+CALLER_ATTACH
+FontDataTable* IndexSubTable::Builder::SubBuildTable(ReadableFontData* data) {
+ UNREFERENCED_PARAMETER(data);
+ return NULL;
+}
+
+void IndexSubTable::Builder::SubDataSet() {
+ // NOP
+}
+
+int32_t IndexSubTable::Builder::SubDataSizeToSerialize() {
+ return 0;
+}
+
+bool IndexSubTable::Builder::SubReadyToSerialize() {
+ return false;
+}
+
+int32_t IndexSubTable::Builder::SubSerialize(WritableFontData* new_data) {
+ UNREFERENCED_PARAMETER(new_data);
+ return 0;
+}
+
+IndexSubTable::Builder::Builder(int32_t data_size, int32_t index_format)
+ : SubTable::Builder(data_size),
+ first_glyph_index_(0),
+ last_glyph_index_(0),
+ index_format_(index_format),
+ image_format_(0),
+ image_data_offset_(0) {
+}
+
+IndexSubTable::Builder::Builder(int32_t index_format,
+ int32_t image_format,
+ int32_t image_data_offset,
+ int32_t data_size)
+ : SubTable::Builder(data_size),
+ first_glyph_index_(0),
+ last_glyph_index_(0),
+ index_format_(index_format),
+ image_format_(image_format),
+ image_data_offset_(image_data_offset) {
+}
+
+IndexSubTable::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : SubTable::Builder(data),
+ first_glyph_index_(first_glyph_index),
+ last_glyph_index_(last_glyph_index) {
+ Initialize(data);
+}
+
+IndexSubTable::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : SubTable::Builder(data),
+ first_glyph_index_(first_glyph_index),
+ last_glyph_index_(last_glyph_index) {
+ Initialize(data);
+}
+
+int32_t IndexSubTable::Builder::CheckGlyphRange(int32_t glyph_id) {
+ return IndexSubTable::CheckGlyphRange(glyph_id,
+ first_glyph_index(),
+ last_glyph_index());
+}
+
+int32_t IndexSubTable::Builder::SerializeIndexSubHeader(
+ WritableFontData* data) {
+ int32_t size =
+ data->WriteUShort(EblcTable::Offset::kIndexSubHeader_indexFormat,
+ index_format());
+ size += data->WriteUShort(EblcTable::Offset::kIndexSubHeader_imageFormat,
+ image_format());
+ size += data->WriteULong(EblcTable::Offset::kIndexSubHeader_imageDataOffset,
+ image_data_offset());
+ return size;
+}
+
+void IndexSubTable::Builder::Initialize(ReadableFontData* data) {
+ index_format_ =
+ data->ReadUShort(EblcTable::Offset::kIndexSubHeader_indexFormat);
+ image_format_ =
+ data->ReadUShort(EblcTable::Offset::kIndexSubHeader_imageFormat);
+ image_data_offset_ =
+ data->ReadULongAsInt(EblcTable::Offset::kIndexSubHeader_imageDataOffset);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table.h b/sfntly/table/bitmap/index_sub_table.h
new file mode 100644
index 0000000..6d27129
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table.h
@@ -0,0 +1,178 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_H_
+
+#include <vector>
+
+#include "sfntly/port/java_iterator.h"
+#include "sfntly/table/subtable.h"
+#include "sfntly/table/bitmap/bitmap_glyph_info.h"
+
+namespace sfntly {
+
+class IndexSubTable : public SubTable {
+ public:
+ struct Format {
+ enum {
+ FORMAT_1 = 1,
+ FORMAT_2 = 2,
+ FORMAT_3 = 3,
+ FORMAT_4 = 4,
+ FORMAT_5 = 5,
+ };
+ };
+
+ class Builder : public SubTable::Builder {
+ public:
+ virtual ~Builder();
+
+ void Revert();
+
+ int32_t index_format() { return index_format_; }
+ int32_t first_glyph_index() { return first_glyph_index_; }
+ void set_first_glyph_index(int32_t v) { first_glyph_index_ = v; }
+ int32_t last_glyph_index() { return last_glyph_index_; }
+ void set_last_glyph_index(int32_t v) { last_glyph_index_ = v; }
+ int32_t image_format() { return image_format_; }
+ void set_image_format(int32_t v) { image_format_ = v; }
+ int32_t image_data_offset() { return image_data_offset_; }
+ void set_image_data_offset(int32_t v) { image_data_offset_ = v; }
+
+ virtual int32_t NumGlyphs() = 0;
+
+ // Gets the glyph info for the specified glyph id.
+ // @param glyphId the glyph id to look up
+ // @return the glyph info
+ CALLER_ATTACH virtual BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
+
+ // Gets the full offset of the glyph within the EBDT table.
+ // @param glyphId the glyph id
+ // @return the glyph offset
+ virtual int32_t GlyphOffset(int32_t glyph_id);
+
+ // Gets the offset of the glyph relative to the block for this index
+ // subtable.
+ // @param glyphId the glyph id
+ // @return the glyph offset
+ virtual int32_t GlyphStartOffset(int32_t glyph_id) = 0;
+
+ // Gets the length of the glyph within the EBDT table.
+ // @param glyphId the glyph id
+ // @return the glyph offset
+ virtual int32_t GlyphLength(int32_t glyph_id) = 0;
+
+ // Note: renamed from java iterator()
+ CALLER_ATTACH virtual Iterator<BitmapGlyphInfo, IndexSubTable::Builder>*
+ GetIterator() = 0;
+
+ // Static instantiation function.
+ static CALLER_ATTACH Builder* CreateBuilder(int32_t index_format);
+ static CALLER_ATTACH Builder*
+ CreateBuilder(ReadableFontData* data,
+ int32_t offset_to_index_sub_table_array,
+ int32_t array_index);
+
+ // The following methods will never be called but they need to be here to
+ // allow the BitmapSizeTable to see these methods through an abstract
+ // reference.
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ protected:
+ Builder(int32_t data_size, int32_t index_format);
+ Builder(int32_t index_format,
+ int32_t image_format,
+ int32_t image_data_offset,
+ int32_t data_size);
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ // Checks that the glyph id is within the correct range. If it returns the
+ // offset of the glyph id from the start of the range.
+ // @param glyphId
+ // @return the offset of the glyphId from the start of the glyph range
+ // @throws IndexOutOfBoundsException if the glyph id is not within the
+ // correct range
+ int32_t CheckGlyphRange(int32_t glyph_id);
+ int32_t SerializeIndexSubHeader(WritableFontData* data);
+
+ private:
+ void Initialize(ReadableFontData* data);
+
+ int32_t first_glyph_index_;
+ int32_t last_glyph_index_;
+ int32_t index_format_;
+ int32_t image_format_;
+ int32_t image_data_offset_;
+ };
+
+ int32_t index_format() { return index_format_; }
+ int32_t first_glyph_index() { return first_glyph_index_; }
+ int32_t last_glyph_index() { return last_glyph_index_; }
+ int32_t image_format() { return image_format_; }
+ int32_t image_data_offset() { return image_data_offset_; }
+
+ CALLER_ATTACH BitmapGlyphInfo* GlyphInfo(int32_t glyph_id);
+ virtual int32_t GlyphOffset(int32_t glyph_id);
+ virtual int32_t GlyphStartOffset(int32_t glyph_id) = 0;
+ virtual int32_t GlyphLength(int32_t glyph_id) = 0;
+ virtual int32_t NumGlyphs() = 0;
+
+ static CALLER_ATTACH IndexSubTable*
+ CreateIndexSubTable(ReadableFontData* data,
+ int32_t offset_to_index_sub_table_array,
+ int32_t array_index);
+
+ protected:
+ // Note: the constructor does not implement offset/length form provided in
+ // Java to avoid heavy lifting in constructors. Callers to call
+ // GetDataLength() static method of the derived class to get proper
+ // length and slice ahead.
+ IndexSubTable(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ int32_t CheckGlyphRange(int32_t glyph_id);
+ static int32_t CheckGlyphRange(int32_t glyph_id,
+ int32_t first_glyph_id,
+ int32_t last_glyph_id);
+
+ private:
+ int32_t first_glyph_index_;
+ int32_t last_glyph_index_;
+ int32_t index_format_;
+ int32_t image_format_;
+ int32_t image_data_offset_;
+};
+typedef Ptr<IndexSubTable> IndexSubTablePtr;
+typedef std::vector<IndexSubTablePtr> IndexSubTableList;
+typedef Ptr<IndexSubTable::Builder> IndexSubTableBuilderPtr;
+typedef std::vector<IndexSubTableBuilderPtr> IndexSubTableBuilderList;
+typedef Iterator<BitmapGlyphInfo, IndexSubTable::Builder> BitmapGlyphInfoIter;
+typedef Ptr<BitmapGlyphInfoIter> BitmapGlyphInfoIterPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_H_
diff --git a/sfntly/table/bitmap/index_sub_table_format1.cc b/sfntly/table/bitmap/index_sub_table_format1.cc
new file mode 100644
index 0000000..5199e18
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format1.cc
@@ -0,0 +1,302 @@
+/*
+ * 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/table/bitmap/index_sub_table_format1.h"
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTableFormat1 class
+ ******************************************************************************/
+// static
+int32_t IndexSubTableFormat1::GetDataLength(ReadableFontData* data,
+ int32_t offset,
+ int32_t first,
+ int32_t last) {
+ UNREFERENCED_PARAMETER(data);
+ UNREFERENCED_PARAMETER(offset);
+ return (last - first + 1 + 1) * DataSize::kULONG;
+}
+
+IndexSubTableFormat1::~IndexSubTableFormat1() {
+}
+
+int32_t IndexSubTableFormat1::NumGlyphs() {
+ return last_glyph_index() - first_glyph_index() + 1;
+}
+
+int32_t IndexSubTableFormat1::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return Loca(loca);
+}
+
+int32_t IndexSubTableFormat1::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return Loca(loca + 1) - Loca(loca);
+}
+
+IndexSubTableFormat1::IndexSubTableFormat1(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable(data, first_glyph_index, last_glyph_index) {
+}
+
+int32_t IndexSubTableFormat1::Loca(int32_t loca) {
+ return image_data_offset() +
+ data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable1_offsetArray +
+ loca * DataSize::kULONG);
+}
+
+/******************************************************************************
+ * IndexSubTableFormat1::Builder class
+ ******************************************************************************/
+IndexSubTableFormat1::Builder::~Builder() {
+}
+
+int32_t IndexSubTableFormat1::Builder::NumGlyphs() {
+ return GetOffsetArray()->size() - 1;
+}
+
+int32_t IndexSubTableFormat1::Builder::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return 0;
+ }
+ IntegerList* offset_array = GetOffsetArray();
+ return offset_array->at(loca + 1) - offset_array->at(loca);
+}
+
+int32_t IndexSubTableFormat1::Builder::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return GetOffsetArray()->at(loca);
+}
+
+CALLER_ATTACH IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator*
+ IndexSubTableFormat1::Builder::GetIterator() {
+ Ptr<IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator> it =
+ new IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator(this);
+ return it.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat1::Builder*
+IndexSubTableFormat1::Builder::CreateBuilder() {
+ IndexSubTableFormat1BuilderPtr output = new IndexSubTableFormat1::Builder();
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat1::Builder*
+IndexSubTableFormat1::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ if (new_data == NULL) {
+ return NULL;
+ }
+ IndexSubTableFormat1BuilderPtr output =
+ new IndexSubTableFormat1::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+
+// static
+CALLER_ATTACH IndexSubTableFormat1::Builder*
+IndexSubTableFormat1::Builder::CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ WritableFontDataPtr new_data;
+ new_data.Attach(down_cast<WritableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ IndexSubTableFormat1BuilderPtr output =
+ new IndexSubTableFormat1::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+CALLER_ATTACH FontDataTable* IndexSubTableFormat1::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ IndexSubTableFormat1Ptr output = new IndexSubTableFormat1(
+ data, first_glyph_index(), last_glyph_index());
+ return output.Detach();
+}
+
+void IndexSubTableFormat1::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t IndexSubTableFormat1::Builder::SubDataSizeToSerialize() {
+ if (offset_array_.empty()) {
+ return InternalReadData()->Length();
+ }
+ return EblcTable::Offset::kIndexSubHeaderLength +
+ offset_array_.size() * DataSize::kULONG;
+}
+
+bool IndexSubTableFormat1::Builder::SubReadyToSerialize() {
+ if (!offset_array_.empty()) {
+ return true;
+ }
+ return false;
+}
+
+int32_t IndexSubTableFormat1::Builder::SubSerialize(
+ WritableFontData* new_data) {
+ int32_t size = SerializeIndexSubHeader(new_data);
+ if (!model_changed()) {
+ if (InternalReadData() == NULL) {
+ return size;
+ }
+ ReadableFontDataPtr source;
+ WritableFontDataPtr target;
+ source.Attach(down_cast<ReadableFontData*>(InternalReadData()->Slice(
+ EblcTable::Offset::kIndexSubTable1_offsetArray)));
+ target.Attach(down_cast<WritableFontData*>(new_data->Slice(
+ EblcTable::Offset::kIndexSubTable1_offsetArray)));
+ size += source->CopyTo(target);
+ } else {
+ for (IntegerList::iterator b = GetOffsetArray()->begin(),
+ e = GetOffsetArray()->end(); b != e; b++) {
+ size += new_data->WriteLong(size, *b);
+ }
+ }
+ return size;
+}
+
+IntegerList* IndexSubTableFormat1::Builder::OffsetArray() {
+ return GetOffsetArray();
+}
+
+void IndexSubTableFormat1::Builder::SetOffsetArray(
+ const IntegerList& offset_array) {
+ offset_array_.clear();
+ offset_array_ = offset_array;
+ set_model_changed();
+}
+
+void IndexSubTableFormat1::Builder::Revert() {
+ offset_array_.clear();
+ IndexSubTable::Builder::Revert();
+}
+
+IndexSubTableFormat1::Builder::Builder()
+ : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable1_builderDataSize,
+ IndexSubTable::Format::FORMAT_1) {
+}
+
+IndexSubTableFormat1::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IndexSubTableFormat1::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IntegerList* IndexSubTableFormat1::Builder::GetOffsetArray() {
+ if (offset_array_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &offset_array_;
+}
+
+void IndexSubTableFormat1::Builder::Initialize(ReadableFontData* data) {
+ offset_array_.clear();
+ if (data) {
+ int32_t num_offsets = (last_glyph_index() - first_glyph_index() + 1) + 1;
+ for (int32_t i = 0; i < num_offsets; ++i) {
+ offset_array_.push_back(data->ReadULongAsInt(
+ EblcTable::Offset::kIndexSubTable1_offsetArray +
+ i * DataSize::kULONG));
+ }
+ }
+}
+
+// static
+int32_t IndexSubTableFormat1::Builder::DataLength(
+ ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ UNREFERENCED_PARAMETER(data);
+ UNREFERENCED_PARAMETER(index_sub_table_offset);
+ return EblcTable::Offset::kIndexSubHeaderLength +
+ (last_glyph_index - first_glyph_index + 1 + 1) * DataSize::kULONG;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ IndexSubTableFormat1::Builder* container)
+ : RefIterator<BitmapGlyphInfo, IndexSubTableFormat1::Builder,
+ IndexSubTable::Builder>(container) {
+ glyph_id_ = container->first_glyph_index();
+}
+
+bool IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (glyph_id_ <= container()->last_glyph_index()) {
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH BitmapGlyphInfo*
+IndexSubTableFormat1::Builder::BitmapGlyphInfoIterator::Next() {
+ BitmapGlyphInfoPtr output;
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ output = new BitmapGlyphInfo(glyph_id_,
+ container()->image_data_offset(),
+ container()->GlyphStartOffset(glyph_id_),
+ container()->GlyphLength(glyph_id_),
+ container()->image_format());
+ glyph_id_++;
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table_format1.h b/sfntly/table/bitmap/index_sub_table_format1.h
new file mode 100644
index 0000000..33171c1
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format1.h
@@ -0,0 +1,116 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_
+
+#include "sfntly/port/java_iterator.h"
+#include "sfntly/table/bitmap/index_sub_table.h"
+
+namespace sfntly {
+// Format 1 Index Subtable Entry.
+class IndexSubTableFormat1 : public IndexSubTable,
+ public RefCounted<IndexSubTableFormat1> {
+ public:
+ class Builder : public IndexSubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator
+ : public RefIterator<BitmapGlyphInfo, Builder, IndexSubTable::Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ int32_t glyph_id_;
+ };
+
+ virtual ~Builder();
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphLength(int32_t glyph_id);
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ IntegerList* OffsetArray();
+ void SetOffsetArray(const IntegerList& offset_array);
+ CALLER_ATTACH BitmapGlyphInfoIter* Iterator();
+
+ static CALLER_ATTACH Builder* CreateBuilder();
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ protected:
+ void Revert();
+
+ private:
+ Builder();
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ IntegerList* GetOffsetArray();
+ void Initialize(ReadableFontData* data);
+
+ static int32_t DataLength(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ IntegerList offset_array_;
+ };
+
+ virtual ~IndexSubTableFormat1();
+
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+
+ static int32_t GetDataLength(ReadableFontData* data,
+ int32_t offset,
+ int32_t first,
+ int32_t last);
+
+ private:
+ IndexSubTableFormat1(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ int32_t Loca(int32_t loca_index);
+
+ friend class Builder;
+};
+typedef Ptr<IndexSubTableFormat1> IndexSubTableFormat1Ptr;
+typedef Ptr<IndexSubTableFormat1::Builder> IndexSubTableFormat1BuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_
diff --git a/sfntly/table/bitmap/index_sub_table_format2.cc b/sfntly/table/bitmap/index_sub_table_format2.cc
new file mode 100644
index 0000000..ce73e9b
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format2.cc
@@ -0,0 +1,275 @@
+/*
+ * 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/table/bitmap/index_sub_table_format2.h"
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTableFormat2 class
+ ******************************************************************************/
+IndexSubTableFormat2::~IndexSubTableFormat2() {
+}
+
+int32_t IndexSubTableFormat2::ImageSize() {
+ return data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable2_imageSize);
+}
+
+CALLER_ATTACH BigGlyphMetrics* IndexSubTableFormat2::BigMetrics() {
+ ReadableFontDataPtr slice;
+ slice.Attach(down_cast<ReadableFontData*>(
+ data_->Slice(EblcTable::Offset::kIndexSubTable2_bigGlyphMetrics,
+ BigGlyphMetrics::Offset::kMetricsLength)));
+ BigGlyphMetricsPtr output = new BigGlyphMetrics(slice);
+ return output.Detach();
+}
+
+int32_t IndexSubTableFormat2::NumGlyphs() {
+ return last_glyph_index() - first_glyph_index() + 1;
+}
+
+int32_t IndexSubTableFormat2::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return loca * image_size_;
+}
+
+int32_t IndexSubTableFormat2::GlyphLength(int32_t glyph_id) {
+ if (CheckGlyphRange(glyph_id) == -1) {
+ return 0;
+ }
+ return image_size_;
+}
+
+IndexSubTableFormat2::IndexSubTableFormat2(ReadableFontData* data,
+ int32_t first,
+ int32_t last)
+ : IndexSubTable(data, first, last) {
+ image_size_ =
+ data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable2_imageSize);
+}
+
+/******************************************************************************
+ * IndexSubTableFormat2::Builder class
+ ******************************************************************************/
+IndexSubTableFormat2::Builder::~Builder() {
+}
+
+int32_t IndexSubTableFormat2::Builder::NumGlyphs() {
+ return last_glyph_index() - first_glyph_index() + 1;
+}
+
+int32_t IndexSubTableFormat2::Builder::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return loca * ImageSize();
+}
+
+int32_t IndexSubTableFormat2::Builder::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return 0;
+ }
+ return ImageSize();
+}
+
+CALLER_ATTACH IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator*
+ IndexSubTableFormat2::Builder::GetIterator() {
+ Ptr<IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator> it =
+ new IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator(this);
+ return it.Detach();
+}
+
+int32_t IndexSubTableFormat2::Builder::ImageSize() {
+ return InternalReadData()->ReadULongAsInt(
+ EblcTable::Offset::kIndexSubTable2_imageSize);
+}
+
+void IndexSubTableFormat2::Builder::SetImageSize(int32_t image_size) {
+ InternalWriteData()->WriteULong(EblcTable::Offset::kIndexSubTable2_imageSize,
+ image_size);
+}
+
+BigGlyphMetrics::Builder* IndexSubTableFormat2::Builder::BigMetrics() {
+ if (metrics_ == NULL) {
+ WritableFontDataPtr data;
+ data.Attach(down_cast<WritableFontData*>(InternalWriteData()->Slice(
+ EblcTable::Offset::kIndexSubTable2_bigGlyphMetrics,
+ BigGlyphMetrics::Offset::kMetricsLength)));
+ metrics_ = new BigGlyphMetrics::Builder(data);
+ }
+ return metrics_;
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat2::Builder*
+IndexSubTableFormat2::Builder::CreateBuilder() {
+ IndexSubTableFormat2BuilderPtr output = new IndexSubTableFormat2::Builder();
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat2::Builder*
+IndexSubTableFormat2::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ if (new_data == NULL) {
+ return NULL;
+ }
+ IndexSubTableFormat2BuilderPtr output =
+ new IndexSubTableFormat2::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat2::Builder*
+IndexSubTableFormat2::Builder::CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ WritableFontDataPtr new_data;
+ new_data.Attach(down_cast<WritableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ IndexSubTableFormat2BuilderPtr output =
+ new IndexSubTableFormat2::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+CALLER_ATTACH FontDataTable* IndexSubTableFormat2::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ IndexSubTableFormat2Ptr output = new IndexSubTableFormat2(
+ data, first_glyph_index(), last_glyph_index());
+ return output.Detach();
+}
+
+void IndexSubTableFormat2::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t IndexSubTableFormat2::Builder::SubDataSizeToSerialize() {
+ return EblcTable::Offset::kIndexSubTable2Length;
+}
+
+bool IndexSubTableFormat2::Builder::SubReadyToSerialize() {
+ return true;
+}
+
+int32_t IndexSubTableFormat2::Builder::SubSerialize(
+ WritableFontData* new_data) {
+ int32_t size = SerializeIndexSubHeader(new_data);
+ if (metrics_ == NULL) {
+ ReadableFontDataPtr source;
+ WritableFontDataPtr target;
+ source.Attach(down_cast<ReadableFontData*>(
+ InternalReadData()->Slice(size)));
+ target.Attach(down_cast<WritableFontData*>(new_data->Slice(size)));
+ size += source->CopyTo(target);
+ } else {
+ WritableFontDataPtr slice;
+ size += new_data->WriteLong(EblcTable::Offset::kIndexSubTable2_imageSize,
+ ImageSize());
+ slice.Attach(down_cast<WritableFontData*>(new_data->Slice(size)));
+ size += metrics_->SubSerialize(slice);
+ }
+ return size;
+}
+
+IndexSubTableFormat2::Builder::Builder()
+ : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable3_builderDataSize,
+ IndexSubTable::Format::FORMAT_2) {
+ metrics_.Attach(BigGlyphMetrics::Builder::CreateBuilder());
+}
+
+IndexSubTableFormat2::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IndexSubTableFormat2::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+// static
+int32_t IndexSubTableFormat2::Builder::DataLength(
+ ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ UNREFERENCED_PARAMETER(data);
+ UNREFERENCED_PARAMETER(index_sub_table_offset);
+ UNREFERENCED_PARAMETER(first_glyph_index);
+ UNREFERENCED_PARAMETER(last_glyph_index);
+ return EblcTable::Offset::kIndexSubTable2Length;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ IndexSubTableFormat2::Builder* container)
+ : RefIterator<BitmapGlyphInfo, IndexSubTableFormat2::Builder,
+ IndexSubTable::Builder>(container) {
+ glyph_id_ = container->first_glyph_index();
+}
+
+bool IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (glyph_id_ <= container()->last_glyph_index()) {
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH BitmapGlyphInfo*
+IndexSubTableFormat2::Builder::BitmapGlyphInfoIterator::Next() {
+ BitmapGlyphInfoPtr output;
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ output = new BitmapGlyphInfo(glyph_id_,
+ container()->image_data_offset(),
+ container()->GlyphStartOffset(glyph_id_),
+ container()->GlyphLength(glyph_id_),
+ container()->image_format());
+ glyph_id_++;
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table_format2.h b/sfntly/table/bitmap/index_sub_table_format2.h
new file mode 100644
index 0000000..784e8a3
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format2.h
@@ -0,0 +1,106 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT2_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT2_H_
+
+#include "sfntly/table/bitmap/index_sub_table.h"
+#include "sfntly/table/bitmap/big_glyph_metrics.h"
+
+namespace sfntly {
+// Format 2 Index Subtable Entry.
+class IndexSubTableFormat2 : public IndexSubTable,
+ public RefCounted<IndexSubTableFormat2> {
+ public:
+ class Builder : public IndexSubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator
+ : public RefIterator<BitmapGlyphInfo, Builder, IndexSubTable::Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ int32_t glyph_id_;
+ };
+
+ virtual ~Builder();
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+ CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ int32_t ImageSize();
+ void SetImageSize(int32_t image_size);
+ BigGlyphMetrics::Builder* BigMetrics();
+
+ static CALLER_ATTACH Builder* CreateBuilder();
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ private:
+ Builder();
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ static int32_t DataLength(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ BigGlyphMetricsBuilderPtr metrics_;
+ };
+
+ virtual ~IndexSubTableFormat2();
+
+ int32_t ImageSize();
+ CALLER_ATTACH BigGlyphMetrics* BigMetrics();
+
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+
+ private:
+ IndexSubTableFormat2(ReadableFontData* data, int32_t first, int32_t last);
+
+ int32_t image_size_;
+ friend class Builder;
+};
+typedef Ptr<IndexSubTableFormat2> IndexSubTableFormat2Ptr;
+typedef Ptr<IndexSubTableFormat2::Builder> IndexSubTableFormat2BuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT1_H_
diff --git a/sfntly/table/bitmap/index_sub_table_format3.cc b/sfntly/table/bitmap/index_sub_table_format3.cc
new file mode 100644
index 0000000..e2679b7
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format3.cc
@@ -0,0 +1,298 @@
+/*
+ * 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/table/bitmap/index_sub_table_format3.h"
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTableFormat3 class
+ ******************************************************************************/
+IndexSubTableFormat3::~IndexSubTableFormat3() {
+}
+
+int32_t IndexSubTableFormat3::NumGlyphs() {
+ return last_glyph_index() - first_glyph_index() + 1;
+}
+
+int32_t IndexSubTableFormat3::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca != -1) {
+ return Loca(loca);
+ }
+ return -1;
+}
+
+int32_t IndexSubTableFormat3::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca != -1) {
+ return Loca(glyph_id + 1) - Loca(glyph_id);
+ }
+ return 0;
+}
+
+// static
+int32_t IndexSubTableFormat3::GetDataLength(ReadableFontData* data,
+ int32_t offset,
+ int32_t first,
+ int32_t last) {
+ UNREFERENCED_PARAMETER(data);
+ UNREFERENCED_PARAMETER(offset);
+ return (last - first + 1 + 1) * DataSize::kUSHORT;
+}
+
+IndexSubTableFormat3::IndexSubTableFormat3(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable(data, first_glyph_index, last_glyph_index) {
+}
+
+int32_t IndexSubTableFormat3::Loca(int32_t loca) {
+ int32_t read_offset =
+ data_->ReadUShort(EblcTable::Offset::kIndexSubTable3_offsetArray +
+ loca * DataSize::kUSHORT);
+ return read_offset;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat3::Builder class
+ ******************************************************************************/
+IndexSubTableFormat3::Builder::~Builder() {
+}
+
+int32_t IndexSubTableFormat3::Builder::NumGlyphs() {
+ return GetOffsetArray()->size() - 1;
+}
+
+int32_t IndexSubTableFormat3::Builder::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ return GetOffsetArray()->at(loca);
+}
+
+int32_t IndexSubTableFormat3::Builder::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return 0;
+ }
+ IntegerList* offset_array = GetOffsetArray();
+ return offset_array->at(loca + 1) - offset_array->at(loca);
+}
+
+CALLER_ATTACH IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator*
+ IndexSubTableFormat3::Builder::GetIterator() {
+ Ptr<IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator> it =
+ new IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator(this);
+ return it.Detach();
+}
+
+void IndexSubTableFormat3::Builder::Revert() {
+ offset_array_.clear();
+ IndexSubTable::Builder::Revert();
+}
+
+void IndexSubTableFormat3::Builder::SetOffsetArray(
+ const IntegerList& offset_array) {
+ offset_array_.clear();
+ offset_array_ = offset_array;
+ set_model_changed();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat3::Builder*
+IndexSubTableFormat3::Builder::CreateBuilder() {
+ IndexSubTableFormat3BuilderPtr output = new IndexSubTableFormat3::Builder();
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat3::Builder*
+IndexSubTableFormat3::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ if (new_data == NULL) {
+ return NULL;
+ }
+ IndexSubTableFormat3BuilderPtr output =
+ new IndexSubTableFormat3::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat3::Builder*
+IndexSubTableFormat3::Builder::CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ WritableFontDataPtr new_data;
+ new_data.Attach(down_cast<WritableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ IndexSubTableFormat3BuilderPtr output =
+ new IndexSubTableFormat3::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+CALLER_ATTACH FontDataTable* IndexSubTableFormat3::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ IndexSubTableFormat3Ptr output = new IndexSubTableFormat3(
+ data, first_glyph_index(), last_glyph_index());
+ return output.Detach();
+}
+
+void IndexSubTableFormat3::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t IndexSubTableFormat3::Builder::SubDataSizeToSerialize() {
+ if (offset_array_.empty()) {
+ return InternalReadData()->Length();
+ }
+ return EblcTable::Offset::kIndexSubHeaderLength +
+ offset_array_.size() * DataSize::kULONG;
+}
+
+bool IndexSubTableFormat3::Builder::SubReadyToSerialize() {
+ if (!offset_array_.empty()) {
+ return true;
+ }
+ return false;
+}
+
+int32_t IndexSubTableFormat3::Builder::SubSerialize(
+ WritableFontData* new_data) {
+ int32_t size = SerializeIndexSubHeader(new_data);
+ if (!model_changed()) {
+ if (InternalReadData() == NULL) {
+ return size;
+ }
+ ReadableFontDataPtr source;
+ WritableFontDataPtr target;
+ source.Attach(down_cast<ReadableFontData*>(InternalReadData()->Slice(
+ EblcTable::Offset::kIndexSubTable3_offsetArray)));
+ target.Attach(down_cast<WritableFontData*>(new_data->Slice(
+ EblcTable::Offset::kIndexSubTable3_offsetArray)));
+ size += source->CopyTo(target);
+ } else {
+ for (IntegerList::iterator b = GetOffsetArray()->begin(),
+ e = GetOffsetArray()->end(); b != e; b++) {
+ size += new_data->WriteUShort(size, *b);
+ }
+ }
+ return size;
+}
+
+IndexSubTableFormat3::Builder::Builder()
+ : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable3_builderDataSize,
+ IndexSubTable::Format::FORMAT_3) {
+}
+
+IndexSubTableFormat3::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IndexSubTableFormat3::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IntegerList* IndexSubTableFormat3::Builder::GetOffsetArray() {
+ if (offset_array_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &offset_array_;
+}
+
+void IndexSubTableFormat3::Builder::Initialize(ReadableFontData* data) {
+ offset_array_.clear();
+ if (data) {
+ int32_t num_offsets = (last_glyph_index() - first_glyph_index() + 1) + 1;
+ for (int32_t i = 0; i < num_offsets; ++i) {
+ offset_array_.push_back(data->ReadUShort(
+ EblcTable::Offset::kIndexSubTable3_offsetArray +
+ i * DataSize::kUSHORT));
+ }
+ }
+}
+
+// static
+int32_t IndexSubTableFormat3::Builder::DataLength(
+ ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ UNREFERENCED_PARAMETER(data);
+ UNREFERENCED_PARAMETER(index_sub_table_offset);
+ return EblcTable::Offset::kIndexSubHeaderLength +
+ (last_glyph_index - first_glyph_index + 1 + 1) * DataSize::kUSHORT;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ IndexSubTableFormat3::Builder* container)
+ : RefIterator<BitmapGlyphInfo, IndexSubTableFormat3::Builder,
+ IndexSubTable::Builder>(container) {
+ glyph_id_ = container->first_glyph_index();
+}
+
+bool IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (glyph_id_ <= container()->last_glyph_index()) {
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH BitmapGlyphInfo*
+IndexSubTableFormat3::Builder::BitmapGlyphInfoIterator::Next() {
+ BitmapGlyphInfoPtr output;
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ output = new BitmapGlyphInfo(glyph_id_,
+ container()->image_data_offset(),
+ container()->GlyphStartOffset(glyph_id_),
+ container()->GlyphLength(glyph_id_),
+ container()->image_format());
+ glyph_id_++;
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table_format3.h b/sfntly/table/bitmap/index_sub_table_format3.h
new file mode 100644
index 0000000..d71f857
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format3.h
@@ -0,0 +1,113 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT3_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT3_H_
+
+#include "sfntly/table/bitmap/index_sub_table.h"
+
+namespace sfntly {
+// Format 3 Index Subtable Entry.
+class IndexSubTableFormat3 : public IndexSubTable,
+ public RefCounted<IndexSubTableFormat3> {
+ public:
+ class Builder : public IndexSubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator
+ : public RefIterator<BitmapGlyphInfo, Builder, IndexSubTable::Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ int32_t glyph_id_;
+ };
+
+ virtual ~Builder();
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+ CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ void SetOffsetArray(const IntegerList& offset_array);
+
+ static CALLER_ATTACH Builder* CreateBuilder();
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ protected:
+ void Revert();
+
+ private:
+ Builder();
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ IntegerList* GetOffsetArray();
+ void Initialize(ReadableFontData* data);
+
+ static int32_t DataLength(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ IntegerList offset_array_;
+ };
+
+ virtual ~IndexSubTableFormat3();
+
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+
+ static int32_t GetDataLength(ReadableFontData* data,
+ int32_t offset,
+ int32_t first,
+ int32_t last);
+
+ private:
+ IndexSubTableFormat3(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ int32_t Loca(int32_t loca_index);
+
+ friend class Builder;
+};
+typedef Ptr<IndexSubTableFormat3> IndexSubTableFormat3Ptr;
+typedef Ptr<IndexSubTableFormat3::Builder> IndexSubTableFormat3BuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT3_H_
diff --git a/sfntly/table/bitmap/index_sub_table_format4.cc b/sfntly/table/bitmap/index_sub_table_format4.cc
new file mode 100644
index 0000000..5baa2a5
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format4.cc
@@ -0,0 +1,384 @@
+/*
+ * 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/table/bitmap/index_sub_table_format4.h"
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTableFormat4 class
+ ******************************************************************************/
+IndexSubTableFormat4::~IndexSubTableFormat4() {
+}
+
+int32_t IndexSubTableFormat4::NumGlyphs() {
+ return IndexSubTableFormat4::NumGlyphs(data_, 0);
+}
+
+int32_t IndexSubTableFormat4::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ int32_t pair_index = FindCodeOffsetPair(glyph_id);
+ if (pair_index < 0) {
+ return -1;
+ }
+ return data_->ReadUShort(EblcTable::Offset::kIndexSubTable4_glyphArray +
+ pair_index *
+ EblcTable::Offset::kCodeOffsetPairLength +
+ EblcTable::Offset::kCodeOffsetPair_offset);
+}
+
+int32_t IndexSubTableFormat4::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+
+ int32_t pair_index = FindCodeOffsetPair(glyph_id);
+ if (pair_index < 0) {
+ return -1;
+ }
+ return data_->ReadUShort(
+ EblcTable::Offset::kIndexSubTable4_glyphArray +
+ (pair_index + 1) * EblcTable::Offset::kCodeOffsetPairLength +
+ EblcTable::Offset::kCodeOffsetPair_offset) -
+ data_->ReadUShort(
+ EblcTable::Offset::kIndexSubTable4_glyphArray +
+ (pair_index) * EblcTable::Offset::kCodeOffsetPairLength +
+ EblcTable::Offset::kCodeOffsetPair_offset);
+}
+
+IndexSubTableFormat4::IndexSubTableFormat4(ReadableFontData* data,
+ int32_t first,
+ int32_t last)
+ : IndexSubTable(data, first, last) {
+}
+
+int32_t IndexSubTableFormat4::FindCodeOffsetPair(int32_t glyph_id) {
+ return data_->SearchUShort(EblcTable::Offset::kIndexSubTable4_glyphArray,
+ EblcTable::Offset::kCodeOffsetPairLength,
+ NumGlyphs(),
+ glyph_id);
+}
+
+int32_t IndexSubTableFormat4::NumGlyphs(ReadableFontData* data,
+ int32_t table_offset) {
+ int32_t num_glyphs = data->ReadULongAsInt(table_offset +
+ EblcTable::Offset::kIndexSubTable4_numGlyphs);
+ return num_glyphs;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat4::CodeOffsetPair related class
+ ******************************************************************************/
+IndexSubTableFormat4::CodeOffsetPair::CodeOffsetPair(int32_t glyph_code,
+ int32_t offset)
+ : glyph_code_(glyph_code), offset_(offset) {
+}
+
+IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder()
+ : CodeOffsetPair(0, 0) {
+}
+
+IndexSubTableFormat4::CodeOffsetPairBuilder::CodeOffsetPairBuilder(
+ int32_t glyph_code, int32_t offset)
+ : CodeOffsetPair(glyph_code, offset) {
+}
+
+bool IndexSubTableFormat4::CodeOffsetPairGlyphCodeComparator::operator()(
+ const CodeOffsetPair& lhs, const CodeOffsetPair& rhs) {
+ return lhs.glyph_code() < rhs.glyph_code();
+}
+
+/******************************************************************************
+ * IndexSubTableFormat4::Builder class
+ ******************************************************************************/
+IndexSubTableFormat4::Builder::~Builder() {
+}
+
+int32_t IndexSubTableFormat4::Builder::NumGlyphs() {
+ return GetOffsetArray()->size() - 1;
+}
+
+int32_t IndexSubTableFormat4::Builder::GlyphLength(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return 0;
+ }
+ int32_t pair_index = FindCodeOffsetPair(glyph_id);
+ if (pair_index == -1) {
+ return 0;
+ }
+ return GetOffsetArray()->at(pair_index + 1).offset() -
+ GetOffsetArray()->at(pair_index).offset();
+}
+
+int32_t IndexSubTableFormat4::Builder::GlyphStartOffset(int32_t glyph_id) {
+ int32_t loca = CheckGlyphRange(glyph_id);
+ if (loca == -1) {
+ return -1;
+ }
+ int32_t pair_index = FindCodeOffsetPair(glyph_id);
+ if (pair_index == -1) {
+ return -1;
+ }
+ return GetOffsetArray()->at(pair_index).offset();
+}
+
+CALLER_ATTACH IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator*
+ IndexSubTableFormat4::Builder::GetIterator() {
+ Ptr<IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator> it =
+ new IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator(this);
+ return it.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat4::Builder*
+IndexSubTableFormat4::Builder::CreateBuilder() {
+ IndexSubTableFormat4BuilderPtr output = new IndexSubTableFormat4::Builder();
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat4::Builder*
+IndexSubTableFormat4::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ if (new_data == NULL) {
+ return NULL;
+ }
+ IndexSubTableFormat4BuilderPtr output =
+ new IndexSubTableFormat4::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat4::Builder*
+IndexSubTableFormat4::Builder::CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ WritableFontDataPtr new_data;
+ new_data.Attach(down_cast<WritableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ IndexSubTableFormat4BuilderPtr output =
+ new IndexSubTableFormat4::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+CALLER_ATTACH FontDataTable* IndexSubTableFormat4::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ IndexSubTableFormat4Ptr output = new IndexSubTableFormat4(
+ data, first_glyph_index(), last_glyph_index());
+ return output.Detach();
+}
+
+void IndexSubTableFormat4::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t IndexSubTableFormat4::Builder::SubDataSizeToSerialize() {
+ if (offset_pair_array_.empty()) {
+ return InternalReadData()->Length();
+ }
+ return EblcTable::Offset::kIndexSubHeaderLength + DataSize::kULONG +
+ GetOffsetArray()->size() *
+ EblcTable::Offset::kIndexSubTable4_codeOffsetPairLength;
+}
+
+bool IndexSubTableFormat4::Builder::SubReadyToSerialize() {
+ if (!offset_pair_array_.empty()) {
+ return true;
+ }
+ return false;
+}
+
+int32_t IndexSubTableFormat4::Builder::SubSerialize(
+ WritableFontData* new_data) {
+ int32_t size = SerializeIndexSubHeader(new_data);
+ if (!model_changed()) {
+ if (InternalReadData() == NULL) {
+ return size;
+ }
+ ReadableFontDataPtr source;
+ WritableFontDataPtr target;
+ source.Attach(down_cast<ReadableFontData*>(InternalReadData()->Slice(
+ EblcTable::Offset::kIndexSubTable4_glyphArray)));
+ target.Attach(down_cast<WritableFontData*>(new_data->Slice(
+ EblcTable::Offset::kIndexSubTable4_glyphArray)));
+ size += source->CopyTo(target);
+ } else {
+ size += new_data->WriteLong(size, offset_pair_array_.size() - 1);
+ for (std::vector<CodeOffsetPairBuilder>::iterator
+ b = GetOffsetArray()->begin(), e = GetOffsetArray()->end();
+ b != e; b++) {
+ size += new_data->WriteUShort(size, b->glyph_code());
+ size += new_data->WriteUShort(size, b->offset());
+ }
+ }
+ return size;
+}
+
+void IndexSubTableFormat4::Builder::Revert() {
+ offset_pair_array_.clear();
+ IndexSubTable::Builder::Revert();
+}
+
+void IndexSubTableFormat4::Builder::SetOffsetArray(
+ const std::vector<CodeOffsetPairBuilder>& pair_array) {
+ offset_pair_array_.clear();
+ offset_pair_array_ = pair_array;
+ set_model_changed();
+}
+
+IndexSubTableFormat4::Builder::Builder()
+ : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable4_builderDataSize,
+ Format::FORMAT_4) {
+}
+
+IndexSubTableFormat4::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IndexSubTableFormat4::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+std::vector<IndexSubTableFormat4::CodeOffsetPairBuilder>*
+IndexSubTableFormat4::Builder::GetOffsetArray() {
+ if (offset_pair_array_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &offset_pair_array_;
+}
+
+void IndexSubTableFormat4::Builder::Initialize(ReadableFontData* data) {
+ offset_pair_array_.clear();
+ if (data) {
+ int32_t num_pairs = IndexSubTableFormat4::NumGlyphs(data, 0) + 1;
+ int32_t offset = EblcTable::Offset::kIndexSubTable4_glyphArray;
+ for (int32_t i = 0; i < num_pairs; ++i) {
+ int32_t glyph_code = data->ReadUShort(offset +
+ EblcTable::Offset::kIndexSubTable4_codeOffsetPair_glyphCode);
+ int32_t glyph_offset = data->ReadUShort(offset +
+ EblcTable::Offset::kIndexSubTable4_codeOffsetPair_offset);
+ offset += EblcTable::Offset::kIndexSubTable4_codeOffsetPairLength;
+ CodeOffsetPairBuilder pair_builder(glyph_code, glyph_offset);
+ offset_pair_array_.push_back(pair_builder);
+ }
+ }
+}
+
+int32_t IndexSubTableFormat4::Builder::FindCodeOffsetPair(int32_t glyph_id) {
+ std::vector<CodeOffsetPairBuilder>* pair_list = GetOffsetArray();
+ int32_t location = 0;
+ int32_t bottom = 0;
+ int32_t top = pair_list->size();
+ while (top != bottom) {
+ location = (top + bottom) / 2;
+ CodeOffsetPairBuilder* pair = &(pair_list->at(location));
+ if (glyph_id < pair->glyph_code()) {
+ // location is below current location
+ top = location;
+ } else if (glyph_id > pair->glyph_code()) {
+ // location is above current location
+ bottom = location + 1;
+ } else {
+ return location;
+ }
+ }
+ return -1;
+}
+
+// static
+int32_t IndexSubTableFormat4::Builder::DataLength(
+ ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t num_glyphs = IndexSubTableFormat4::NumGlyphs(data,
+ index_sub_table_offset);
+ UNREFERENCED_PARAMETER(first_glyph_index);
+ UNREFERENCED_PARAMETER(last_glyph_index);
+ return EblcTable::Offset::kIndexSubTable4_glyphArray +
+ num_glyphs * EblcTable::Offset::kIndexSubTable4_codeOffsetPair_offset;
+}
+
+
+/******************************************************************************
+ * IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ IndexSubTableFormat4::Builder* container)
+ : RefIterator<BitmapGlyphInfo, IndexSubTableFormat4::Builder,
+ IndexSubTable::Builder>(container),
+ code_offset_pair_index_(0) {
+}
+
+bool IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (code_offset_pair_index_ <
+ (int32_t)(container()->GetOffsetArray()->size() - 1)) {
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH BitmapGlyphInfo*
+IndexSubTableFormat4::Builder::BitmapGlyphInfoIterator::Next() {
+ BitmapGlyphInfoPtr output;
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ std::vector<CodeOffsetPairBuilder>* offset_array =
+ container()->GetOffsetArray();
+ int32_t offset = offset_array->at(code_offset_pair_index_).offset();
+ int32_t next_offset = offset_array->at(code_offset_pair_index_ + 1).offset();
+ int32_t glyph_code = offset_array->at(code_offset_pair_index_).glyph_code();
+ output = new BitmapGlyphInfo(glyph_code,
+ container()->image_data_offset(),
+ offset,
+ next_offset - offset,
+ container()->image_format());
+ code_offset_pair_index_++;
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table_format4.h b/sfntly/table/bitmap/index_sub_table_format4.h
new file mode 100644
index 0000000..efd540f
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format4.h
@@ -0,0 +1,135 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_
+
+#include "sfntly/table/bitmap/index_sub_table.h"
+
+namespace sfntly {
+
+class IndexSubTableFormat4 : public IndexSubTable,
+ public RefCounted<IndexSubTableFormat4> {
+ public:
+ class CodeOffsetPair {
+ public:
+ int32_t glyph_code() const { return glyph_code_; }
+ int32_t offset() const { return offset_; }
+
+ protected:
+ CodeOffsetPair(int32_t glyph_code, int32_t offset);
+
+ // TODO(arthurhsu): C++ style guide prohibits protected members.
+ int32_t glyph_code_;
+ int32_t offset_;
+ };
+
+ class CodeOffsetPairBuilder : public CodeOffsetPair {
+ public:
+ CodeOffsetPairBuilder();
+ CodeOffsetPairBuilder(int32_t glyph_code, int32_t offset);
+ void set_glyph_code(int32_t v) { glyph_code_ = v; }
+ void set_offset(int32_t v) { offset_ = v; }
+ };
+
+ class CodeOffsetPairGlyphCodeComparator {
+ public:
+ bool operator()(const CodeOffsetPair& lhs, const CodeOffsetPair& rhs);
+ };
+
+ class Builder : public IndexSubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator
+ : public RefIterator<BitmapGlyphInfo, Builder, IndexSubTable::Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ int32_t code_offset_pair_index_;
+ };
+
+ virtual ~Builder();
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphLength(int32_t glyph_id);
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ void Revert();
+ void SetOffsetArray(const std::vector<CodeOffsetPairBuilder>& pair_array);
+
+ static CALLER_ATTACH Builder* CreateBuilder();
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ private:
+ Builder();
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ std::vector<CodeOffsetPairBuilder>* GetOffsetArray();
+ void Initialize(ReadableFontData* data);
+ int32_t FindCodeOffsetPair(int32_t glyph_id);
+
+ static int32_t DataLength(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ std::vector<CodeOffsetPairBuilder> offset_pair_array_;
+ };
+
+ virtual ~IndexSubTableFormat4();
+
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+
+ private:
+ IndexSubTableFormat4(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ int32_t FindCodeOffsetPair(int32_t glyph_id);
+ static int32_t NumGlyphs(ReadableFontData* data, int32_t table_offset);
+
+ friend class Builder;
+};
+typedef Ptr<IndexSubTableFormat4> IndexSubTableFormat4Ptr;
+typedef Ptr<IndexSubTableFormat4::Builder> IndexSubTableFormat4BuilderPtr;
+typedef std::vector<IndexSubTableFormat4::CodeOffsetPairBuilder>
+ CodeOffsetPairBuilderList;
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT4_H_
diff --git a/sfntly/table/bitmap/index_sub_table_format5.cc b/sfntly/table/bitmap/index_sub_table_format5.cc
new file mode 100644
index 0000000..0ca21fe
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format5.cc
@@ -0,0 +1,347 @@
+/*
+ * 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/table/bitmap/index_sub_table_format5.h"
+
+#include <algorithm>
+
+#include "sfntly/table/bitmap/eblc_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * IndexSubTableFormat5 class
+ ******************************************************************************/
+IndexSubTableFormat5::~IndexSubTableFormat5() {
+}
+
+int32_t IndexSubTableFormat5::NumGlyphs() {
+ return NumGlyphs(data_, 0);
+}
+
+int32_t IndexSubTableFormat5::GlyphStartOffset(int32_t glyph_id) {
+ int32_t check = CheckGlyphRange(glyph_id);
+ if (check == -1) {
+ return -1;
+ }
+ int32_t loca = ReadFontData()->SearchUShort(
+ EblcTable::Offset::kIndexSubTable5_glyphArray,
+ DataSize::kUSHORT,
+ NumGlyphs(),
+ glyph_id);
+ if (loca == -1) {
+ return loca;
+ }
+ return loca * ImageSize();
+}
+
+int32_t IndexSubTableFormat5::GlyphLength(int32_t glyph_id) {
+ int32_t check = CheckGlyphRange(glyph_id);
+ if (check == -1) {
+ return 0;
+ }
+ return image_size_;
+}
+
+int32_t IndexSubTableFormat5::ImageSize() {
+ return data_->ReadULongAsInt(EblcTable::Offset::kIndexSubTable5_imageSize);
+}
+
+CALLER_ATTACH BigGlyphMetrics* IndexSubTableFormat5::BigMetrics() {
+ ReadableFontDataPtr data;
+ data.Attach(down_cast<ReadableFontData*>(data_->Slice(
+ EblcTable::Offset::kIndexSubTable5_bigGlyphMetrics,
+ BigGlyphMetrics::Offset::kMetricsLength)));
+ BigGlyphMetricsPtr output = new BigGlyphMetrics(data);
+ return output.Detach();
+}
+
+IndexSubTableFormat5::IndexSubTableFormat5(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable(data, first_glyph_index, last_glyph_index) {
+ image_size_ = data_->ReadULongAsInt(
+ EblcTable::Offset::kIndexSubTable5_imageSize);
+}
+
+// static
+int32_t IndexSubTableFormat5::NumGlyphs(ReadableFontData* data,
+ int32_t table_offset) {
+ int32_t num_glyphs = data->ReadULongAsInt(table_offset +
+ EblcTable::Offset::kIndexSubTable5_numGlyphs);
+ return num_glyphs;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat5::Builder class
+ ******************************************************************************/
+IndexSubTableFormat5::Builder::~Builder() {
+}
+
+int32_t IndexSubTableFormat5::Builder::NumGlyphs() {
+ return GetGlyphArray()->size();
+}
+
+int32_t IndexSubTableFormat5::Builder::GlyphLength(int32_t glyph_id) {
+ UNREFERENCED_PARAMETER(glyph_id);
+ return ImageSize();
+}
+
+int32_t IndexSubTableFormat5::Builder::GlyphStartOffset(int32_t glyph_id) {
+ int32_t check = CheckGlyphRange(glyph_id);
+ if (check == -1) {
+ return -1;
+ }
+ IntegerList* glyph_array = GetGlyphArray();
+ IntegerList::iterator it = std::find(glyph_array->begin(),
+ glyph_array->end(),
+ glyph_id);
+ if (it == glyph_array->end()) {
+ return -1;
+ }
+ return (it - glyph_array->begin()) * ImageSize();
+}
+
+CALLER_ATTACH IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator*
+ IndexSubTableFormat5::Builder::GetIterator() {
+ Ptr<IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator> it =
+ new IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator(this);
+ return it.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat5::Builder*
+IndexSubTableFormat5::Builder::CreateBuilder() {
+ IndexSubTableFormat5BuilderPtr output = new IndexSubTableFormat5::Builder();
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat5::Builder*
+IndexSubTableFormat5::Builder::CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ ReadableFontDataPtr new_data;
+ new_data.Attach(down_cast<ReadableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ if (new_data == NULL) {
+ return NULL;
+ }
+ IndexSubTableFormat5BuilderPtr output =
+ new IndexSubTableFormat5::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+// static
+CALLER_ATTACH IndexSubTableFormat5::Builder*
+IndexSubTableFormat5::Builder::CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t length = Builder::DataLength(data,
+ index_sub_table_offset,
+ first_glyph_index,
+ last_glyph_index);
+ WritableFontDataPtr new_data;
+ new_data.Attach(down_cast<WritableFontData*>(
+ data->Slice(index_sub_table_offset, length)));
+ IndexSubTableFormat5BuilderPtr output =
+ new IndexSubTableFormat5::Builder(new_data,
+ first_glyph_index,
+ last_glyph_index);
+ return output.Detach();
+}
+
+CALLER_ATTACH FontDataTable* IndexSubTableFormat5::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ IndexSubTableFormat5Ptr output = new IndexSubTableFormat5(
+ data, first_glyph_index(), last_glyph_index());
+ return output.Detach();
+}
+
+void IndexSubTableFormat5::Builder::SubDataSet() {
+ Revert();
+}
+
+int32_t IndexSubTableFormat5::Builder::SubDataSizeToSerialize() {
+ if (glyph_array_.empty()) {
+ return InternalReadData()->Length();
+ }
+ return EblcTable::Offset::kIndexSubTable5_builderDataSize +
+ glyph_array_.size() * DataSize::kUSHORT;
+}
+
+bool IndexSubTableFormat5::Builder::SubReadyToSerialize() {
+ if (!glyph_array_.empty()) {
+ return true;
+ }
+ return false;
+}
+
+int32_t IndexSubTableFormat5::Builder::SubSerialize(
+ WritableFontData* new_data) {
+ int32_t size = SerializeIndexSubHeader(new_data);
+ if (!model_changed()) {
+ ReadableFontDataPtr source;
+ WritableFontDataPtr target;
+ source.Attach(down_cast<ReadableFontData*>(InternalReadData()->Slice(
+ EblcTable::Offset::kIndexSubTable5_imageSize)));
+ target.Attach(down_cast<WritableFontData*>(new_data->Slice(
+ EblcTable::Offset::kIndexSubTable5_imageSize)));
+ size += source->CopyTo(target);
+ } else {
+ size += new_data->WriteULong(EblcTable::Offset::kIndexSubTable5_imageSize,
+ ImageSize());
+ WritableFontDataPtr slice;
+ slice.Attach(down_cast<WritableFontData*>(new_data->Slice(size)));
+ size += BigMetrics()->SubSerialize(slice);
+ size += new_data->WriteULong(size, glyph_array_.size());
+ for (IntegerList::iterator b = glyph_array_.begin(), e = glyph_array_.end();
+ b != e; b++) {
+ size += new_data->WriteUShort(size, *b);
+ }
+ }
+ return size;
+}
+
+int32_t IndexSubTableFormat5::Builder::ImageSize() {
+ return InternalReadData()->ReadULongAsInt(
+ EblcTable::Offset::kIndexSubTable5_imageSize);
+}
+
+void IndexSubTableFormat5::Builder::SetImageSize(int32_t image_size) {
+ InternalWriteData()->WriteULong(
+ EblcTable::Offset::kIndexSubTable5_imageSize, image_size);
+}
+
+BigGlyphMetrics::Builder* IndexSubTableFormat5::Builder::BigMetrics() {
+ if (metrics_ == NULL) {
+ WritableFontDataPtr data;
+ data.Attach(down_cast<WritableFontData*>(InternalWriteData()->Slice(
+ EblcTable::Offset::kIndexSubTable5_bigGlyphMetrics,
+ BigGlyphMetrics::Offset::kMetricsLength)));
+ metrics_ = new BigGlyphMetrics::Builder(data);
+ set_model_changed();
+ }
+ return metrics_;
+}
+
+IntegerList* IndexSubTableFormat5::Builder::GlyphArray() {
+ return GetGlyphArray();
+}
+
+void IndexSubTableFormat5::Builder::SetGlyphArray(const IntegerList& v) {
+ glyph_array_.clear();
+ glyph_array_ = v;
+ set_model_changed();
+}
+
+void IndexSubTableFormat5::Builder::Revert() {
+ glyph_array_.clear();
+ IndexSubTable::Builder::Revert();
+}
+
+IndexSubTableFormat5::Builder::Builder()
+ : IndexSubTable::Builder(EblcTable::Offset::kIndexSubTable5_builderDataSize,
+ IndexSubTable::Format::FORMAT_5) {
+}
+
+IndexSubTableFormat5::Builder::Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IndexSubTableFormat5::Builder::Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index)
+ : IndexSubTable::Builder(data, first_glyph_index, last_glyph_index) {
+}
+
+IntegerList* IndexSubTableFormat5::Builder::GetGlyphArray() {
+ if (glyph_array_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &glyph_array_;
+}
+
+void IndexSubTableFormat5::Builder::Initialize(ReadableFontData* data) {
+ glyph_array_.clear();
+ if (data) {
+ int32_t num_glyphs = IndexSubTableFormat5::NumGlyphs(data, 0);
+ for (int32_t i = 0; i < num_glyphs; ++i) {
+ glyph_array_.push_back(data->ReadUShort(
+ EblcTable::Offset::kIndexSubTable5_glyphArray +
+ i * DataSize::kUSHORT));
+ }
+ }
+}
+
+// static
+int32_t IndexSubTableFormat5::Builder::DataLength(
+ ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index) {
+ int32_t num_glyphs = IndexSubTableFormat5::NumGlyphs(data,
+ index_sub_table_offset);
+ UNREFERENCED_PARAMETER(first_glyph_index);
+ UNREFERENCED_PARAMETER(last_glyph_index);
+ return EblcTable::Offset::kIndexSubTable5_glyphArray +
+ num_glyphs * DataSize::kUSHORT;
+}
+
+/******************************************************************************
+ * IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator class
+ ******************************************************************************/
+IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::BitmapGlyphInfoIterator(
+ IndexSubTableFormat5::Builder* container)
+ : RefIterator<BitmapGlyphInfo, IndexSubTableFormat5::Builder,
+ IndexSubTable::Builder>(container),
+ offset_index_(0) {
+}
+
+bool IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::HasNext() {
+ if (offset_index_ < (int32_t)(container()->GetGlyphArray()->size())) {
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH BitmapGlyphInfo*
+IndexSubTableFormat5::Builder::BitmapGlyphInfoIterator::Next() {
+ BitmapGlyphInfoPtr output;
+ if (!HasNext()) {
+ // Note: In C++, we do not throw exception when there's no element.
+ return NULL;
+ }
+ output = new BitmapGlyphInfo(container()->GetGlyphArray()->at(offset_index_),
+ container()->image_data_offset(),
+ offset_index_ * container()->ImageSize(),
+ container()->ImageSize(),
+ container()->image_format());
+ offset_index_++;
+ return output.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/index_sub_table_format5.h b/sfntly/table/bitmap/index_sub_table_format5.h
new file mode 100644
index 0000000..a39e88c
--- /dev/null
+++ b/sfntly/table/bitmap/index_sub_table_format5.h
@@ -0,0 +1,118 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT5_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT5_H_
+
+#include "sfntly/table/bitmap/big_glyph_metrics.h"
+#include "sfntly/table/bitmap/index_sub_table.h"
+
+namespace sfntly {
+
+class IndexSubTableFormat5 : public IndexSubTable,
+ public RefCounted<IndexSubTableFormat5> {
+ public:
+ class Builder : public IndexSubTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ class BitmapGlyphInfoIterator
+ : public RefIterator<BitmapGlyphInfo, Builder, IndexSubTable::Builder> {
+ public:
+ explicit BitmapGlyphInfoIterator(Builder* container);
+ virtual ~BitmapGlyphInfoIterator() {}
+
+ virtual bool HasNext();
+ CALLER_ATTACH virtual BitmapGlyphInfo* Next();
+
+ private:
+ int32_t offset_index_;
+ };
+ virtual ~Builder();
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphLength(int32_t glyph_id);
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ CALLER_ATTACH virtual BitmapGlyphInfoIterator* GetIterator();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ int32_t ImageSize();
+ void SetImageSize(int32_t image_size);
+ BigGlyphMetrics::Builder* BigMetrics();
+ IntegerList* GlyphArray();
+ void SetGlyphArray(const IntegerList& v);
+
+ static CALLER_ATTACH Builder* CreateBuilder();
+ static CALLER_ATTACH Builder* CreateBuilder(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ static CALLER_ATTACH Builder* CreateBuilder(WritableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ protected:
+ void Revert();
+
+ private:
+ Builder();
+ Builder(WritableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+ Builder(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ IntegerList* GetGlyphArray();
+ void Initialize(ReadableFontData* data);
+
+ static int32_t DataLength(ReadableFontData* data,
+ int32_t index_sub_table_offset,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ IntegerList glyph_array_;
+ BigGlyphMetricsBuilderPtr metrics_;
+ };
+ virtual ~IndexSubTableFormat5();
+
+ virtual int32_t NumGlyphs();
+ virtual int32_t GlyphStartOffset(int32_t glyph_id);
+ virtual int32_t GlyphLength(int32_t glyph_id);
+
+ int32_t ImageSize();
+ CALLER_ATTACH BigGlyphMetrics* BigMetrics();
+
+ private:
+ IndexSubTableFormat5(ReadableFontData* data,
+ int32_t first_glyph_index,
+ int32_t last_glyph_index);
+
+ static int32_t NumGlyphs(ReadableFontData* dta, int32_t table_offset);
+
+ int32_t image_size_;
+
+ friend class Builder;
+};
+typedef Ptr<IndexSubTableFormat5> IndexSubTableFormat5Ptr;
+typedef Ptr<IndexSubTableFormat5::Builder> IndexSubTableFormat5BuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_INDEX_SUBTABLE_FORMAT5_H_
diff --git a/sfntly/table/bitmap/simple_bitmap_glyph.cc b/sfntly/table/bitmap/simple_bitmap_glyph.cc
new file mode 100644
index 0000000..87031a1
--- /dev/null
+++ b/sfntly/table/bitmap/simple_bitmap_glyph.cc
@@ -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.
+ */
+
+#include "sfntly/table/bitmap/simple_bitmap_glyph.h"
+
+namespace sfntly {
+
+SimpleBitmapGlyph::SimpleBitmapGlyph(ReadableFontData* data, int32_t format)
+ : BitmapGlyph(data, format) {
+}
+
+SimpleBitmapGlyph::~SimpleBitmapGlyph() {
+}
+
+SimpleBitmapGlyph::Builder::Builder(ReadableFontData* data, int32_t format)
+ : BitmapGlyph::Builder(data, format) {
+}
+
+SimpleBitmapGlyph::Builder::Builder(WritableFontData* data, int32_t format)
+ : BitmapGlyph::Builder(data, format) {
+}
+
+SimpleBitmapGlyph::Builder::~Builder() {
+}
+
+CALLER_ATTACH FontDataTable*
+SimpleBitmapGlyph::Builder::SubBuildTable(ReadableFontData* data) {
+ Ptr<SimpleBitmapGlyph> glyph = new SimpleBitmapGlyph(data, format());
+ return glyph.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/simple_bitmap_glyph.h b/sfntly/table/bitmap/simple_bitmap_glyph.h
new file mode 100644
index 0000000..56ede10
--- /dev/null
+++ b/sfntly/table/bitmap/simple_bitmap_glyph.h
@@ -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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SIMPLE_BITMAP_GLYPH_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SIMPLE_BITMAP_GLYPH_H_
+
+#include "sfntly/table/bitmap/bitmap_glyph.h"
+
+namespace sfntly {
+
+class SimpleBitmapGlyph : public BitmapGlyph,
+ public RefCounted<SimpleBitmapGlyph> {
+ public:
+ class Builder : public BitmapGlyph::Builder,
+ public RefCounted<Builder> {
+ public:
+ Builder(WritableFontData* data, int32_t format);
+ Builder(ReadableFontData* data, int32_t format);
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ };
+
+ SimpleBitmapGlyph(ReadableFontData* data, int32_t format);
+ virtual ~SimpleBitmapGlyph();
+};
+typedef Ptr<SimpleBitmapGlyph> SimpleBitmapGlyphPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SIMPLE_BITMAP_GLYPH_H_
diff --git a/sfntly/table/bitmap/small_glyph_metrics.cc b/sfntly/table/bitmap/small_glyph_metrics.cc
new file mode 100644
index 0000000..0f3c1e9
--- /dev/null
+++ b/sfntly/table/bitmap/small_glyph_metrics.cc
@@ -0,0 +1,126 @@
+/*
+ * 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/table/bitmap/small_glyph_metrics.h"
+
+namespace sfntly {
+/******************************************************************************
+ * SmallGlyphMetrics class
+ ******************************************************************************/
+SmallGlyphMetrics::SmallGlyphMetrics(ReadableFontData* data)
+ : GlyphMetrics(data) {
+}
+
+SmallGlyphMetrics::~SmallGlyphMetrics() {
+}
+
+int32_t SmallGlyphMetrics::Height() {
+ return data_->ReadByte(Offset::kHeight);
+}
+
+int32_t SmallGlyphMetrics::Width() {
+ return data_->ReadByte(Offset::kWidth);
+}
+
+int32_t SmallGlyphMetrics::BearingX() {
+ return data_->ReadByte(Offset::kBearingX);
+}
+
+int32_t SmallGlyphMetrics::BearingY() {
+ return data_->ReadByte(Offset::kBearingY);
+}
+
+int32_t SmallGlyphMetrics::Advance() {
+ return data_->ReadByte(Offset::kAdvance);
+}
+
+/******************************************************************************
+ * SmallGlyphMetrics::Builder class
+ ******************************************************************************/
+SmallGlyphMetrics::Builder::Builder(WritableFontData* data)
+ : GlyphMetrics::Builder(data) {
+}
+
+SmallGlyphMetrics::Builder::Builder(ReadableFontData* data)
+ : GlyphMetrics::Builder(data) {
+}
+
+SmallGlyphMetrics::Builder::~Builder() {
+}
+
+int32_t SmallGlyphMetrics::Builder::Height() {
+ return InternalReadData()->ReadByte(Offset::kHeight);
+}
+
+void SmallGlyphMetrics::Builder::SetHeight(byte_t height) {
+ InternalWriteData()->WriteByte(Offset::kHeight, height);
+}
+
+int32_t SmallGlyphMetrics::Builder::Width() {
+ return InternalReadData()->ReadByte(Offset::kWidth);
+}
+
+void SmallGlyphMetrics::Builder::SetWidth(byte_t width) {
+ InternalWriteData()->WriteByte(Offset::kWidth, width);
+}
+
+int32_t SmallGlyphMetrics::Builder::BearingX() {
+ return InternalReadData()->ReadByte(Offset::kBearingX);
+}
+
+void SmallGlyphMetrics::Builder::SetBearingX(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kBearingX, bearing);
+}
+
+int32_t SmallGlyphMetrics::Builder::BearingY() {
+ return InternalReadData()->ReadByte(Offset::kBearingY);
+}
+
+void SmallGlyphMetrics::Builder::SetBearingY(byte_t bearing) {
+ InternalWriteData()->WriteByte(Offset::kBearingY, bearing);
+}
+
+int32_t SmallGlyphMetrics::Builder::Advance() {
+ return InternalReadData()->ReadByte(Offset::kAdvance);
+}
+
+void SmallGlyphMetrics::Builder::SetAdvance(byte_t advance) {
+ InternalWriteData()->WriteByte(Offset::kAdvance, advance);
+}
+
+CALLER_ATTACH FontDataTable*
+ SmallGlyphMetrics::Builder::SubBuildTable(ReadableFontData* data) {
+ SmallGlyphMetricsPtr output = new SmallGlyphMetrics(data);
+ return output.Detach();
+}
+
+void SmallGlyphMetrics::Builder::SubDataSet() {
+ // NOP.
+}
+
+int32_t SmallGlyphMetrics::Builder::SubDataSizeToSerialize() {
+ return 0;
+}
+
+bool SmallGlyphMetrics::Builder::SubReadyToSerialize() {
+ return false;
+}
+
+int32_t SmallGlyphMetrics::Builder::SubSerialize(WritableFontData* new_data) {
+ return Data()->CopyTo(new_data);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/bitmap/small_glyph_metrics.h b/sfntly/table/bitmap/small_glyph_metrics.h
new file mode 100644
index 0000000..ea13720
--- /dev/null
+++ b/sfntly/table/bitmap/small_glyph_metrics.h
@@ -0,0 +1,79 @@
+/*
+ * 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_SFNTLY_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/bitmap/glyph_metrics.h"
+
+namespace sfntly {
+
+class SmallGlyphMetrics : public GlyphMetrics,
+ public RefCounted<SmallGlyphMetrics> {
+ public:
+ struct Offset {
+ enum {
+ kMetricsLength = 5,
+ kHeight = 0,
+ kWidth = 1,
+ kBearingX = 2,
+ kBearingY = 3,
+ kAdvance = 4,
+ };
+ };
+
+ class Builder : public GlyphMetrics::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ explicit Builder(WritableFontData* data);
+ explicit Builder(ReadableFontData* data);
+ virtual ~Builder();
+
+ int32_t Height();
+ void SetHeight(byte_t height);
+ int32_t Width();
+ void SetWidth(byte_t width);
+ int32_t BearingX();
+ void SetBearingX(byte_t bearing);
+ int32_t BearingY();
+ void SetBearingY(byte_t bearing);
+ int32_t Advance();
+ void SetAdvance(byte_t advance);
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ };
+
+ explicit SmallGlyphMetrics(ReadableFontData* data);
+ virtual ~SmallGlyphMetrics();
+
+ int32_t Height();
+ int32_t Width();
+ int32_t BearingX();
+ int32_t BearingY();
+ int32_t Advance();
+};
+typedef Ptr<SmallGlyphMetrics> SmallGlyphMetricsPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BITMAP_SMALL_GLYPH_METRICS_H_
diff --git a/sfntly/table/byte_array_table_builder.cc b/sfntly/table/byte_array_table_builder.cc
new file mode 100644
index 0000000..631a05f
--- /dev/null
+++ b/sfntly/table/byte_array_table_builder.cc
@@ -0,0 +1,70 @@
+/*
+ * 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/table/byte_array_table_builder.h"
+
+namespace sfntly {
+
+ByteArrayTableBuilder::~ByteArrayTableBuilder() {}
+
+int32_t ByteArrayTableBuilder::ByteValue(int32_t index) {
+ ReadableFontDataPtr data = InternalReadData();
+ if (data == NULL) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("No font data for the table");
+#endif
+ return -1;
+ }
+ return data->ReadByte(index);
+}
+
+void ByteArrayTableBuilder::SetByteValue(int32_t index, byte_t b) {
+ WritableFontDataPtr data = InternalWriteData();
+ if (data == NULL) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("No font data for the table");
+#endif
+ return;
+ }
+ data->WriteByte(index, b);
+}
+
+int32_t ByteArrayTableBuilder::ByteCount() {
+ ReadableFontDataPtr data = InternalReadData();
+ if (data == NULL) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("No font data for the table");
+#endif
+ return 0;
+ }
+ return data->Length();
+}
+
+ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header,
+ WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header,
+ ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+ByteArrayTableBuilder::ByteArrayTableBuilder(Header* header)
+ : TableBasedTableBuilder(header) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/byte_array_table_builder.h b/sfntly/table/byte_array_table_builder.h
new file mode 100644
index 0000000..42d27a8
--- /dev/null
+++ b/sfntly/table/byte_array_table_builder.h
@@ -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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_
+
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// An abstract builder base for byte array based tables.
+class ByteArrayTableBuilder : public TableBasedTableBuilder {
+ public:
+ virtual ~ByteArrayTableBuilder();
+
+ // Get the byte value at the specified index. The index is relative to the
+ // start of the table.
+ // @param index index relative to the start of the table
+ // @return byte value at the given index
+ virtual int32_t ByteValue(int32_t index);
+
+ // Set the byte value at the specified index. The index is relative to the
+ // start of the table.
+ // @param index index relative to the start of the table
+ // @param b byte value to set
+ virtual void SetByteValue(int32_t index, byte_t b);
+
+ // Get the number of bytes set for this table. It may include padding bytes at
+ // the end.
+ virtual int32_t ByteCount();
+
+ protected:
+ ByteArrayTableBuilder(Header* header, WritableFontData* data);
+ ByteArrayTableBuilder(Header* header, ReadableFontData* data);
+ explicit ByteArrayTableBuilder(Header* header);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_
diff --git a/sfntly/table/core/cmap_table.cc b/sfntly/table/core/cmap_table.cc
new file mode 100644
index 0000000..1c83d2e
--- /dev/null
+++ b/sfntly/table/core/cmap_table.cc
@@ -0,0 +1,1287 @@
+/*
+ * 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.
+ */
+
+// type.h needs to be included first because of building issues on Windows
+// Type aliases we delcare are defined in other headers and make the build
+// fail otherwise.
+#include "sfntly/port/type.h"
+#include "sfntly/table/core/cmap_table.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <utility>
+
+#include "sfntly/font.h"
+#include "sfntly/math/font_math.h"
+#include "sfntly/port/endian.h"
+#include "sfntly/port/exception_type.h"
+#include "sfntly/table/core/name_table.h"
+
+namespace sfntly {
+
+const int32_t CMapTable::NOTDEF = 0;
+
+CMapTable::CMapId CMapTable::WINDOWS_BMP = {
+ PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2
+};
+CMapTable::CMapId CMapTable::WINDOWS_UCS4 = {
+ PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS4
+};
+CMapTable::CMapId CMapTable::MAC_ROMAN = {
+ PlatformId::kWindows,
+ MacintoshEncodingId::kRoman
+};
+
+/******************************************************************************
+ * CMapTable class
+ ******************************************************************************/
+CMapTable::CMapTable(Header* header, ReadableFontData* data)
+ : SubTableContainerTable(header, data) {
+}
+
+CMapTable::~CMapTable() {}
+
+CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t index) {
+ if (index < 0 || index > NumCMaps()) {
+#ifndef SFNTLY_NO_EXCEPTION
+ throw IndexOutOfBoundException("Requested CMap index is out of bounds.");
+#else
+ return NULL;
+#endif
+ }
+ int32_t platform_id = PlatformId(index);
+ int32_t encoding_id = EncodingId(index);
+ CMapId cmap_id = NewCMapId(platform_id, encoding_id);
+ int32_t offset_ = Offset(index);
+ Ptr<FontDataTable::Builder> cmap_builder =
+ (CMap::Builder::GetBuilder(data_, offset_, cmap_id));
+ if (!cmap_builder) {
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException("Cannot find builder for requested CMap.");
+#else
+ return NULL;
+#endif
+ }
+ return down_cast<CMapTable::CMap*>(cmap_builder->Build());
+}
+
+CALLER_ATTACH CMapTable::CMap* CMapTable::GetCMap(const int32_t platform_id,
+ const int32_t encoding_id) {
+ return GetCMap(NewCMapId(platform_id, encoding_id));
+}
+
+CALLER_ATTACH CMapTable::CMap*
+CMapTable::GetCMap(const CMapTable::CMapId cmap_id) {
+ CMapIdFilter id_filter(cmap_id);
+ CMapIterator cmap_iterator(this, &id_filter);
+ // There can only be one cmap with a particular CMapId
+ if (cmap_iterator.HasNext()) {
+ Ptr<CMapTable::CMap> cmap;
+ cmap.Attach(cmap_iterator.Next());
+ return cmap.Detach();
+ }
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException();
+#else
+ return NULL;
+#endif
+}
+
+int32_t CMapTable::Version() {
+ return data_->ReadUShort(Offset::kVersion);
+}
+
+int32_t CMapTable::NumCMaps() {
+ return data_->ReadUShort(Offset::kNumTables);
+}
+
+CMapTable::CMapId CMapTable::GetCMapId(int32_t index) {
+ return NewCMapId(PlatformId(index), EncodingId(index));
+}
+
+int32_t CMapTable::PlatformId(int32_t index) {
+ return data_->ReadUShort(Offset::kEncodingRecordPlatformId +
+ OffsetForEncodingRecord(index));
+}
+
+int32_t CMapTable::EncodingId(int32_t index) {
+ return data_->ReadUShort(Offset::kEncodingRecordEncodingId +
+ OffsetForEncodingRecord(index));
+}
+
+int32_t CMapTable::Offset(int32_t index) {
+ return data_->ReadULongAsInt(Offset::kEncodingRecordOffset +
+ OffsetForEncodingRecord(index));
+}
+
+int32_t CMapTable::OffsetForEncodingRecord(int32_t index) {
+ return Offset::kEncodingRecordStart + index * Offset::kEncodingRecordSize;
+}
+
+CMapTable::CMapId CMapTable::NewCMapId(int32_t platform_id,
+ int32_t encoding_id) {
+ CMapId result;
+ result.platform_id = platform_id;
+ result.encoding_id = encoding_id;
+ return result;
+}
+
+CMapTable::CMapId CMapTable::NewCMapId(const CMapId& obj) {
+ CMapId result;
+ result.platform_id = obj.platform_id;
+ result.encoding_id = obj.encoding_id;
+ return result;
+}
+
+/******************************************************************************
+ * CMapTable::CMapIterator class
+ ******************************************************************************/
+CMapTable::CMapIterator::CMapIterator(CMapTable* table,
+ const CMapFilter* filter)
+ : table_index_(0), filter_(filter), table_(table) {
+}
+
+bool CMapTable::CMapIterator::HasNext() {
+ if (!filter_) {
+ if (table_index_ < table_->NumCMaps()) {
+ return true;
+ }
+ return false;
+ }
+
+ for (; table_index_ < table_->NumCMaps(); ++table_index_) {
+ if (filter_->accept(table_->GetCMapId(table_index_))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+CALLER_ATTACH CMapTable::CMap* CMapTable::CMapIterator::Next() {
+ if (!HasNext()) {
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException();
+#else
+ return NULL;
+#endif
+ }
+ CMapPtr next_cmap;
+ next_cmap.Attach(table_->GetCMap(table_index_++));
+ if (next_cmap == NULL) {
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException("Error during the creation of the CMap");
+#else
+ return NULL;
+#endif
+ }
+ return next_cmap.Detach();
+}
+
+/******************************************************************************
+ * CMapTable::CMapId class
+ ******************************************************************************/
+
+/******************************************************************************
+ * CMapTable::CMapIdComparator class
+ ******************************************************************************/
+
+bool CMapTable::CMapIdComparator::operator()(const CMapId& lhs,
+ const CMapId& rhs) const {
+ return ((lhs.platform_id << 8 | lhs.encoding_id) >
+ (rhs.platform_id << 8 | rhs.encoding_id));
+}
+
+/******************************************************************************
+ * CMapTable::CMapIdFilter class
+ ******************************************************************************/
+CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id)
+ : wanted_id_(wanted_id),
+ comparator_(NULL) {
+}
+
+CMapTable::CMapIdFilter::CMapIdFilter(const CMapId wanted_id,
+ const CMapIdComparator* comparator)
+ : wanted_id_(wanted_id),
+ comparator_(comparator) {
+}
+
+bool CMapTable::CMapIdFilter::accept(const CMapId& cmap_id) const {
+ if (!comparator_)
+ return wanted_id_ == cmap_id;
+ return (*comparator_)(wanted_id_, cmap_id);
+}
+
+/******************************************************************************
+ * CMapTable::CMap class
+ ******************************************************************************/
+CMapTable::CMap::CMap(ReadableFontData* data, int32_t format,
+ const CMapId& cmap_id)
+ : SubTable(data), format_(format), cmap_id_(cmap_id) {
+}
+
+CMapTable::CMap::~CMap() {
+}
+
+/******************************************************************************
+ * CMapTable::CMap::Builder class
+ ******************************************************************************/
+CMapTable::CMap::Builder::~Builder() {
+}
+
+CALLER_ATTACH CMapTable::CMap::Builder*
+ CMapTable::CMap::Builder::GetBuilder(ReadableFontData* data, int32_t offset,
+ const CMapId& cmap_id) {
+ // NOT IMPLEMENTED: Java enum value validation
+ int32_t format = data->ReadUShort(offset);
+ CMapBuilderPtr builder;
+ switch (format) {
+ case CMapFormat::kFormat0:
+ builder.Attach(CMapFormat0::Builder::NewInstance(data, offset, cmap_id));
+ break;
+ case CMapFormat::kFormat2:
+#if defined (SFNTLY_DEBUG_CMAP)
+ fprintf(stderr, "Requesting Format2 builder, but it's unsupported; "
+ "returning NULL\n");
+#endif
+ break;
+ case CMapFormat::kFormat4:
+ builder.Attach(CMapFormat4::Builder::NewInstance(data, offset, cmap_id));
+ break;
+ default:
+#ifdef SFNTLY_DEBUG_CMAP
+ fprintf(stderr, "Unknown builder format requested\n");
+#endif
+ break;
+ }
+ return builder.Detach();
+}
+
+CALLER_ATTACH CMapTable::CMap::Builder*
+CMapTable::CMap::Builder::GetBuilder(int32_t format, const CMapId& cmap_id) {
+ Ptr<CMapTable::CMap::Builder> builder;
+ switch (format) {
+ case CMapFormat::kFormat0:
+ builder.Attach(CMapFormat0::Builder::NewInstance(cmap_id));
+ break;
+ case CMapFormat::kFormat2:
+#if defined (SFNTLY_DEBUG_CMAP)
+ fprintf(stderr, "Requesting Format2 builder, but it's unsupported; "
+ "returning NULL\n");
+#endif
+ break;
+ case CMapFormat::kFormat4:
+ builder.Attach(CMapFormat4::Builder::NewInstance(cmap_id));
+ break;
+ default:
+#ifdef SFNTLY_DEBUG_CMAP
+ fprintf(stderr, "Unknown builder format requested\n");
+#endif
+ break;
+ }
+ return builder.Detach();
+}
+
+CMapTable::CMap::Builder::Builder(ReadableFontData* data,
+ int32_t format,
+ const CMapId& cmap_id)
+ : SubTable::Builder(data),
+ format_(format),
+ cmap_id_(cmap_id),
+ language_(0) {
+}
+
+CMapTable::CMap::Builder::Builder(WritableFontData* data,
+ int32_t format,
+ const CMapId& cmap_id)
+ : SubTable::Builder(data),
+ format_(format),
+ cmap_id_(cmap_id),
+ language_(0) {
+}
+
+int32_t CMapTable::CMap::Builder::SubSerialize(WritableFontData* new_data) {
+ return InternalReadData()->CopyTo(new_data);
+}
+
+bool CMapTable::CMap::Builder::SubReadyToSerialize() {
+ return true;
+}
+
+int32_t CMapTable::CMap::Builder::SubDataSizeToSerialize() {
+ ReadableFontDataPtr read_data = InternalReadData();
+ if (!read_data)
+ return 0;
+ return read_data->Length();
+}
+
+void CMapTable::CMap::Builder::SubDataSet() {
+ // NOP
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat0
+ ******************************************************************************/
+CMapTable::CMapFormat0::~CMapFormat0() {
+}
+
+int32_t CMapTable::CMapFormat0::Language() {
+ return 0;
+}
+
+int32_t CMapTable::CMapFormat0::GlyphId(int32_t character) {
+ if (character < 0 || character > 255) {
+ return CMapTable::NOTDEF;
+ }
+ return data_->ReadUByte(character + Offset::kFormat0GlyphIdArray);
+}
+
+CMapTable::CMapFormat0::CMapFormat0(ReadableFontData* data,
+ const CMapId& cmap_id)
+ : CMap(data, CMapFormat::kFormat0, cmap_id) {
+}
+
+CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat0::Iterator() {
+ return new CMapTable::CMapFormat0::CharacterIterator(0, 0xff);
+}
+
+
+/******************************************************************************
+ * CMapTable::CMapFormat0::CharacterIterator
+ ******************************************************************************/
+CMapTable::CMapFormat0::CharacterIterator::CharacterIterator(int32_t start,
+ int32_t end)
+ : character_(start),
+ max_character_(end) {
+}
+
+CMapTable::CMapFormat0::CharacterIterator::~CharacterIterator() {}
+
+bool CMapTable::CMapFormat0::CharacterIterator::HasNext() {
+ return character_ < max_character_;
+}
+
+int32_t CMapTable::CMapFormat0::CharacterIterator::Next() {
+ if (HasNext())
+ return character_++;
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException("No more characters to iterate.");
+#endif
+ return -1;
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat0::Builder
+ ******************************************************************************/
+// static
+CALLER_ATTACH CMapTable::CMapFormat0::Builder*
+CMapTable::CMapFormat0::Builder::NewInstance(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id) {
+ WritableFontDataPtr wdata;
+ if (data) {
+ wdata.Attach(down_cast<WritableFontData*>(
+ data->Slice(offset,
+ data->ReadUShort(offset + Offset::kFormat0Length))));
+ }
+ return new Builder(wdata, CMapFormat::kFormat0, cmap_id);
+}
+
+// static
+CALLER_ATTACH CMapTable::CMapFormat0::Builder*
+CMapTable::CMapFormat0::Builder::NewInstance(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id) {
+ ReadableFontDataPtr rdata;
+ if (data) {
+ rdata.Attach(down_cast<ReadableFontData*>(
+ data->Slice(offset,
+ data->ReadUShort(offset + Offset::kFormat0Length))));
+ }
+ return new Builder(rdata, CMapFormat::kFormat0, cmap_id);
+}
+
+// static
+CALLER_ATTACH CMapTable::CMapFormat0::Builder*
+CMapTable::CMapFormat0::Builder::NewInstance(const CMapId& cmap_id) {
+ return new Builder(cmap_id);
+}
+
+// Always call NewInstance instead of the constructor for creating a new builder
+// object! This refactoring avoids memory leaks when slicing the font data.
+CMapTable::CMapFormat0::Builder::Builder(WritableFontData* data, int32_t offset,
+ const CMapId& cmap_id)
+ : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) {
+ UNREFERENCED_PARAMETER(offset);
+}
+
+CMapTable::CMapFormat0::Builder::Builder(
+ ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id)
+ : CMapTable::CMap::Builder(data, CMapFormat::kFormat0, cmap_id) {
+ UNREFERENCED_PARAMETER(offset);
+}
+
+CMapTable::CMapFormat0::Builder::Builder(const CMapId& cmap_id)
+ : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
+ CMapFormat::kFormat0,
+ cmap_id) {
+}
+
+CMapTable::CMapFormat0::Builder::~Builder() {
+}
+
+CALLER_ATTACH FontDataTable*
+ CMapTable::CMapFormat0::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new CMapFormat0(data, cmap_id());
+ return table.Detach();
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat2
+ ******************************************************************************/
+CMapTable::CMapFormat2::~CMapFormat2() {
+}
+
+int32_t CMapTable::CMapFormat2::Language() {
+ return 0;
+}
+
+int32_t CMapTable::CMapFormat2::GlyphId(int32_t character) {
+ if (character > 0xffff) {
+ return CMapTable::NOTDEF;
+ }
+
+ uint32_t c = ToBE32(character);
+ byte_t high_byte = (c >> 8) & 0xff;
+ byte_t low_byte = c & 0xff;
+ int32_t offset = SubHeaderOffset(high_byte);
+
+ if (offset == 0) {
+ low_byte = high_byte;
+ high_byte = 0;
+ }
+
+ int32_t first_code = FirstCode(high_byte);
+ int32_t entry_count = EntryCount(high_byte);
+
+ if (low_byte < first_code || low_byte >= first_code + entry_count) {
+ return CMapTable::NOTDEF;
+ }
+
+ int32_t id_range_offset = IdRangeOffset(high_byte);
+
+ // position of idRangeOffset + value of idRangeOffset + index for low byte
+ // = firstcode
+ int32_t p_location = (offset + Offset::kFormat2SubHeader_idRangeOffset) +
+ id_range_offset +
+ (low_byte - first_code) * DataSize::kUSHORT;
+ int p = data_->ReadUShort(p_location);
+ if (p == 0) {
+ return CMapTable::NOTDEF;
+ }
+
+ if (offset == 0) {
+ return p;
+ }
+ int id_delta = IdDelta(high_byte);
+ return (p + id_delta) % 65536;
+}
+
+int32_t CMapTable::CMapFormat2::BytesConsumed(int32_t character) {
+ uint32_t c = ToBE32(character);
+ int32_t high_byte = (c >> 8) & 0xff;
+ int32_t offset = SubHeaderOffset(high_byte);
+ return (offset == 0) ? 1 : 2;
+}
+
+CMapTable::CMapFormat2::CMapFormat2(ReadableFontData* data,
+ const CMapId& cmap_id)
+ : CMap(data, CMapFormat::kFormat2, cmap_id) {
+}
+
+int32_t CMapTable::CMapFormat2::SubHeaderOffset(int32_t sub_header_index) {
+ return data_->ReadUShort(Offset::kFormat2SubHeaderKeys +
+ sub_header_index * DataSize::kUSHORT);
+}
+
+int32_t CMapTable::CMapFormat2::FirstCode(int32_t sub_header_index) {
+ int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
+ return data_->ReadUShort(sub_header_offset +
+ Offset::kFormat2SubHeaderKeys +
+ Offset::kFormat2SubHeader_firstCode);
+}
+
+int32_t CMapTable::CMapFormat2::EntryCount(int32_t sub_header_index) {
+ int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
+ return data_->ReadUShort(sub_header_offset +
+ Offset::kFormat2SubHeaderKeys +
+ Offset::kFormat2SubHeader_entryCount);
+}
+
+int32_t CMapTable::CMapFormat2::IdRangeOffset(int32_t sub_header_index) {
+ int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
+ return data_->ReadUShort(sub_header_offset +
+ Offset::kFormat2SubHeaderKeys +
+ Offset::kFormat2SubHeader_idRangeOffset);
+}
+
+int32_t CMapTable::CMapFormat2::IdDelta(int32_t sub_header_index) {
+ int32_t sub_header_offset = SubHeaderOffset(sub_header_index);
+ return data_->ReadUShort(sub_header_offset +
+ Offset::kFormat2SubHeaderKeys +
+ Offset::kFormat2SubHeader_idDelta);
+}
+
+CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat2::Iterator() {
+ // UNIMPLEMENTED
+ return NULL;
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat2::Builder
+ ******************************************************************************/
+CMapTable::CMapFormat2::Builder::Builder(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id)
+ : CMapTable::CMap::Builder(data ? down_cast<WritableFontData*>(
+ data->Slice(offset, data->ReadUShort(
+ offset + Offset::kFormat0Length)))
+ : reinterpret_cast<WritableFontData*>(NULL),
+ CMapFormat::kFormat2, cmap_id) {
+ // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix.
+}
+
+CMapTable::CMapFormat2::Builder::Builder(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id)
+ : CMapTable::CMap::Builder(data ? down_cast<ReadableFontData*>(
+ data->Slice(offset, data->ReadUShort(
+ offset + Offset::kFormat0Length)))
+ : reinterpret_cast<ReadableFontData*>(NULL),
+ CMapFormat::kFormat2, cmap_id) {
+ // TODO(arthurhsu): FIXIT: heavy lifting and leak, need fix.
+}
+
+CMapTable::CMapFormat2::Builder::~Builder() {
+}
+
+CALLER_ATTACH FontDataTable*
+ CMapTable::CMapFormat2::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new CMapFormat2(data, cmap_id());
+ return table.Detach();
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat4
+ ******************************************************************************/
+CMapTable::CMapFormat4::CMapFormat4(ReadableFontData* data,
+ const CMapId& cmap_id)
+ : CMap(data, CMapFormat::kFormat4, cmap_id),
+ seg_count_(SegCount(data)),
+ start_code_offset_(StartCodeOffset(seg_count_)),
+ id_delta_offset_(IdDeltaOffset(seg_count_)),
+ glyph_id_array_offset_(GlyphIdArrayOffset(seg_count_)) {
+}
+
+CMapTable::CMapFormat4::~CMapFormat4() {
+}
+
+int32_t CMapTable::CMapFormat4::GlyphId(int32_t character) {
+ int32_t segment = data_->SearchUShort(StartCodeOffset(seg_count_),
+ DataSize::kUSHORT,
+ Offset::kFormat4EndCount,
+ DataSize::kUSHORT,
+ seg_count_,
+ character);
+ if (segment == -1) {
+ return CMapTable::NOTDEF;
+ }
+ int32_t start_code = StartCode(segment);
+ return RetrieveGlyphId(segment, start_code, character);
+}
+
+int32_t CMapTable::CMapFormat4::RetrieveGlyphId(int32_t segment,
+ int32_t start_code,
+ int32_t character) {
+ if (character < start_code) {
+ return CMapTable::NOTDEF;
+ }
+ int32_t id_range_offset = IdRangeOffset(segment);
+ if (id_range_offset == 0) {
+ return (character + IdDelta(segment)) % 65536;
+ }
+ return data_->ReadUShort(id_range_offset +
+ IdRangeOffsetLocation(segment) +
+ 2 * (character - start_code));
+}
+
+int32_t CMapTable::CMapFormat4::seg_count() {
+ return seg_count_;
+}
+
+int32_t CMapTable::CMapFormat4::Length() {
+ return Length(data_);
+}
+
+int32_t CMapTable::CMapFormat4::StartCode(int32_t segment) {
+ if (!IsValidIndex(segment)) {
+ return -1;
+ }
+ return StartCode(data_.p_, seg_count_, segment);
+}
+
+// static
+int32_t CMapTable::CMapFormat4::Language(ReadableFontData* data) {
+ int32_t language = data->ReadUShort(Offset::kFormat4Language);
+ return language;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::Length(ReadableFontData* data) {
+ int32_t length = data->ReadUShort(Offset::kFormat4Length);
+ return length;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::SegCount(ReadableFontData* data) {
+ int32_t seg_count = data->ReadUShort(Offset::kFormat4SegCountX2) / 2;
+ return seg_count;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::StartCode(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index) {
+ int32_t start_code = data->ReadUShort(StartCodeOffset(seg_count) +
+ index * DataSize::kUSHORT);
+ return start_code;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::StartCodeOffset(int32_t seg_count) {
+ int32_t start_code_offset = Offset::kFormat4EndCount +
+ (seg_count + 1) * DataSize::kUSHORT;
+ return start_code_offset;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::EndCode(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index) {
+ UNREFERENCED_PARAMETER(seg_count);
+ int32_t end_code = data->ReadUShort(Offset::kFormat4EndCount +
+ index * DataSize::kUSHORT);
+ return end_code;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::IdDelta(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index) {
+ int32_t id_delta = data->ReadUShort(IdDeltaOffset(seg_count) +
+ index * DataSize::kUSHORT);
+ return id_delta;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::IdDeltaOffset(int32_t seg_count) {
+ int32_t id_delta_offset =
+ Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT;
+ return id_delta_offset;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::IdRangeOffset(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index) {
+ int32_t id_range_offset =
+ data->ReadUShort(IdRangeOffsetOffset(seg_count)
+ + index * DataSize::kUSHORT);
+ return id_range_offset;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::IdRangeOffsetOffset(int32_t seg_count) {
+ int32_t id_range_offset_offset =
+ Offset::kFormat4EndCount + (2 * seg_count + 1) * DataSize::kUSHORT +
+ seg_count * DataSize::kSHORT;
+ return id_range_offset_offset;
+}
+
+// static
+int32_t CMapTable::CMapFormat4::GlyphIdArrayOffset(int32_t seg_count) {
+ int32_t glyph_id_array_offset =
+ Offset::kFormat4EndCount + (3 * seg_count + 1) * DataSize::kUSHORT +
+ seg_count * DataSize::kSHORT;
+ return glyph_id_array_offset;
+}
+
+int32_t CMapTable::CMapFormat4::EndCode(int32_t segment) {
+ if (IsValidIndex(segment)) {
+ return EndCode(data_, seg_count_, segment);
+ }
+#if defined (SFNTLY_NO_EXCEPTION)
+ return -1;
+#else
+ throw IllegalArgumentException();
+#endif
+}
+
+bool CMapTable::CMapFormat4::IsValidIndex(int32_t segment) {
+ if (segment < 0 || segment >= seg_count_) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return false;
+#else
+ throw IllegalArgumentException();
+#endif
+ }
+ return true;
+}
+
+int32_t CMapTable::CMapFormat4::IdDelta(int32_t segment) {
+ if (IsValidIndex(segment))
+ return IdDelta(data_, seg_count_, segment);
+ return -1;
+}
+
+int32_t CMapTable::CMapFormat4::IdRangeOffset(int32_t segment) {
+ if (IsValidIndex(segment))
+ return data_->ReadUShort(IdRangeOffsetLocation(segment));
+ return -1;
+}
+
+int32_t CMapTable::CMapFormat4::IdRangeOffsetLocation(int32_t segment) {
+ if (IsValidIndex(segment))
+ return IdRangeOffsetOffset(seg_count_) + segment * DataSize::kUSHORT;
+ return -1;
+}
+
+int32_t CMapTable::CMapFormat4::GlyphIdArray(int32_t index) {
+ return data_->ReadUShort(glyph_id_array_offset_ + index * DataSize::kUSHORT);
+}
+
+int32_t CMapTable::CMapFormat4::Language() {
+ return Language(data_);
+}
+
+
+CMapTable::CMap::CharacterIterator* CMapTable::CMapFormat4::Iterator() {
+ return new CharacterIterator(this);
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat4::CharacterIterator class
+ ******************************************************************************/
+CMapTable::CMapFormat4::CharacterIterator::CharacterIterator(
+ CMapFormat4* parent)
+ : parent_(parent),
+ segment_index_(0),
+ first_char_in_segment_(-1),
+ last_char_in_segment_(-1),
+ next_char_(-1),
+ next_char_set_(false) {
+}
+
+bool CMapTable::CMapFormat4::CharacterIterator::HasNext() {
+ if (next_char_set_)
+ return true;
+ while (segment_index_ < parent_->seg_count_) {
+ if (first_char_in_segment_ < 0) {
+ first_char_in_segment_ = parent_->StartCode(segment_index_);
+ last_char_in_segment_ = parent_->EndCode(segment_index_);
+ next_char_ = first_char_in_segment_;
+ next_char_set_ = true;
+ return true;
+ }
+ if (next_char_ < last_char_in_segment_) {
+ next_char_++;
+ next_char_set_ = true;
+ return true;
+ }
+ segment_index_++;
+ first_char_in_segment_ = -1;
+ }
+ return false;
+}
+
+int32_t CMapTable::CMapFormat4::CharacterIterator::Next() {
+ if (!next_char_set_) {
+ if (!HasNext()) {
+#if defined (SFNTLY_NO_EXCEPTION)
+ return -1;
+#else
+ throw NoSuchElementException("No more characters to iterate.");
+#endif
+ }
+ }
+ next_char_set_ = false;
+ return next_char_;
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat4::Builder::Segment class
+ ******************************************************************************/
+CMapTable::CMapFormat4::Builder::Segment::Segment() {}
+
+CMapTable::CMapFormat4::Builder::Segment::Segment(Segment* other)
+ : start_count_(other->start_count_),
+ end_count_(other->end_count_),
+ id_delta_(other->id_delta_),
+ id_range_offset_(other->id_range_offset_) {
+}
+
+CMapTable::CMapFormat4::Builder::Segment::Segment(int32_t start_count,
+ int32_t end_count,
+ int32_t id_delta,
+ int32_t id_range_offset)
+ : start_count_(start_count),
+ end_count_(end_count),
+ id_delta_(id_delta),
+ id_range_offset_(id_range_offset) {
+}
+
+CMapTable::CMapFormat4::Builder::Segment::~Segment() {}
+
+int32_t CMapTable::CMapFormat4::Builder::Segment::start_count() {
+ return start_count_;
+}
+
+void
+CMapTable::CMapFormat4::Builder::Segment::set_start_count(int32_t start_count) {
+ start_count_ = start_count;
+}
+
+int32_t CMapTable::CMapFormat4::Builder::Segment::end_count() {
+ return end_count_;
+}
+
+void
+CMapTable::CMapFormat4::Builder::Segment::set_end_count(int32_t end_count) {
+ end_count_ = end_count;
+}
+
+int32_t CMapTable::CMapFormat4::Builder::Segment::id_delta() {
+ return id_delta_;
+}
+
+void
+CMapTable::CMapFormat4::Builder::Segment::set_id_delta(int32_t id_delta) {
+ id_delta_ = id_delta;
+}
+
+int32_t CMapTable::CMapFormat4::Builder::Segment::id_range_offset() {
+ return id_range_offset_;
+}
+
+void
+CMapTable::CMapFormat4::Builder::Segment::
+set_id_range_offset(int32_t id_range_offset) {
+ id_range_offset_ = id_range_offset;
+}
+
+// static
+CALLER_ATTACH SegmentList*
+CMapTable::CMapFormat4::Builder::Segment::DeepCopy(SegmentList* original) {
+ SegmentList* list = new SegmentList;
+ for (SegmentList::iterator it = original->begin(),
+ e = original->end(); it != e; ++it) {
+ list->push_back(*it);
+ }
+ return list;
+}
+
+/******************************************************************************
+ * CMapTable::CMapFormat4::Builder class
+ ******************************************************************************/
+CALLER_ATTACH CMapTable::CMapFormat4::Builder*
+CMapTable::CMapFormat4::Builder::NewInstance(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id) {
+ ReadableFontDataPtr rdata;
+ if (data) {
+ rdata.Attach
+ (down_cast<ReadableFontData*>
+ (data->Slice(offset,
+ data->ReadUShort(offset + Offset::kFormat4Length))));
+ }
+ return new Builder(rdata, CMapFormat::kFormat4, cmap_id);
+}
+
+CALLER_ATTACH CMapTable::CMapFormat4::Builder*
+CMapTable::CMapFormat4::Builder::NewInstance(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id) {
+ WritableFontDataPtr wdata;
+ if (data) {
+ wdata.Attach
+ (down_cast<WritableFontData*>
+ (data->Slice(offset,
+ data->ReadUShort(offset + Offset::kFormat4Length))));
+ }
+ return new Builder(wdata, CMapFormat::kFormat4, cmap_id);
+}
+
+CALLER_ATTACH CMapTable::CMapFormat4::Builder*
+CMapTable::CMapFormat4::Builder::NewInstance(const CMapId& cmap_id) {
+ return new Builder(cmap_id);
+}
+
+CMapTable::CMapFormat4::Builder::Builder(ReadableFontData* data, int32_t offset,
+ const CMapId& cmap_id)
+ : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) {
+ UNREFERENCED_PARAMETER(offset);
+}
+
+CMapTable::CMapFormat4::Builder::Builder(WritableFontData* data, int32_t offset,
+ const CMapId& cmap_id)
+ : CMap::Builder(data, CMapFormat::kFormat4, cmap_id) {
+ UNREFERENCED_PARAMETER(offset);
+}
+
+CMapTable::CMapFormat4::Builder::Builder(SegmentList* segments,
+ IntegerList* glyph_id_array,
+ const CMapId& cmap_id)
+ : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
+ CMapFormat::kFormat4, cmap_id),
+ segments_(segments->begin(), segments->end()),
+ glyph_id_array_(glyph_id_array->begin(), glyph_id_array->end()) {
+ set_model_changed();
+}
+
+CMapTable::CMapFormat4::Builder::Builder(const CMapId& cmap_id)
+ : CMap::Builder(reinterpret_cast<ReadableFontData*>(NULL),
+ CMapFormat::kFormat4, cmap_id) {
+}
+
+CMapTable::CMapFormat4::Builder::~Builder() {}
+
+void CMapTable::CMapFormat4::Builder::Initialize(ReadableFontData* data) {
+ if (data == NULL || data->Length() == 0)
+ return;
+
+ // build segments
+ int32_t seg_count = CMapFormat4::SegCount(data);
+ for (int32_t index = 0; index < seg_count; ++index) {
+ Ptr<Segment> segment = new Segment;
+ segment->set_start_count(CMapFormat4::StartCode(data, seg_count, index));
+#if defined SFNTLY_DEBUG_CMAP
+ fprintf(stderr, "Segment %d; start %d\n", index, segment->start_count());
+#endif
+ segment->set_end_count(CMapFormat4::EndCode(data, seg_count, index));
+ segment->set_id_delta(CMapFormat4::IdDelta(data, seg_count, index));
+ segment->set_id_range_offset(CMapFormat4::IdRangeOffset(data,
+ seg_count,
+ index));
+ segments_.push_back(segment);
+ }
+
+ // build glyph id array
+ int32_t glyph_id_array_offset = CMapFormat4::GlyphIdArrayOffset(seg_count);
+ int32_t glyph_id_array_length =
+ (CMapFormat4::Length(data) - glyph_id_array_offset)
+ / DataSize::kUSHORT;
+ fprintf(stderr, "id array size %d\n", glyph_id_array_length);
+ for (int32_t i = 0; i < glyph_id_array_length; i += DataSize::kUSHORT) {
+ glyph_id_array_.push_back(data->ReadUShort(glyph_id_array_offset + i));
+ }
+}
+
+SegmentList* CMapTable::CMapFormat4::Builder::segments() {
+ if (segments_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &segments_;
+}
+
+void CMapTable::CMapFormat4::Builder::set_segments(SegmentList* segments) {
+ segments_.assign(segments->begin(), segments->end());
+ set_model_changed();
+}
+
+IntegerList* CMapTable::CMapFormat4::Builder::glyph_id_array() {
+ if (glyph_id_array_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &glyph_id_array_;
+}
+
+void CMapTable::CMapFormat4::Builder::
+set_glyph_id_array(IntegerList* glyph_id_array) {
+ glyph_id_array_.assign(glyph_id_array->begin(), glyph_id_array->end());
+ set_model_changed();
+}
+
+CALLER_ATTACH FontDataTable*
+CMapTable::CMapFormat4::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new CMapFormat4(data, cmap_id());
+ return table.Detach();
+}
+
+void CMapTable::CMapFormat4::Builder::SubDataSet() {
+ segments_.clear();
+ glyph_id_array_.clear();
+ set_model_changed();
+}
+
+int32_t CMapTable::CMapFormat4::Builder::SubDataSizeToSerialize() {
+ if (!model_changed()) {
+ return CMap::Builder::SubDataSizeToSerialize();
+ }
+ int32_t size = Offset::kFormat4FixedSize + segments_.size()
+ * (3 * DataSize::kUSHORT + DataSize::kSHORT)
+ + glyph_id_array_.size() * DataSize::kSHORT;
+ return size;
+}
+
+bool CMapTable::CMapFormat4::Builder::SubReadyToSerialize() {
+ if (!model_changed()) {
+ return CMap::Builder::SubReadyToSerialize();
+ }
+ if (!segments()->empty()) {
+ return true;
+ }
+ return false;
+}
+
+int32_t
+CMapTable::CMapFormat4::Builder::SubSerialize(WritableFontData* new_data) {
+ if (!model_changed()) {
+ return CMap::Builder::SubSerialize(new_data);
+ }
+ int32_t index = 0;
+ index += new_data->WriteUShort(index, CMapFormat::kFormat4);
+ index += DataSize::kUSHORT; // length - write this at the end
+ index += new_data->WriteUShort(index, language());
+
+ int32_t seg_count = segments_.size();
+ index += new_data->WriteUShort(index, seg_count * 2);
+ int32_t log2_seg_count = FontMath::Log2(seg_count);
+ int32_t search_range = 1 << (log2_seg_count + 1);
+ index += new_data->WriteUShort(index, search_range);
+ int32_t entry_selector = log2_seg_count;
+ index += new_data->WriteUShort(index, entry_selector);
+ int32_t range_shift = 2 * seg_count - search_range;
+ index += new_data->WriteUShort(index, range_shift);
+
+ for (int32_t i = 0; i < seg_count; ++i) {
+ index += new_data->WriteUShort(index, segments_[i]->end_count());
+ }
+ index += new_data->WriteUShort(index, 0); // reserved ushort
+ for (int32_t i = 0; i < seg_count; ++i) {
+#if defined SFNTLY_DEBUG_CMAP
+ fprintf(stderr, "Segment %d; start %d\n", i, segments_[i]->start_count());
+#endif
+ index += new_data->WriteUShort(index, segments_[i]->start_count());
+ }
+ for (int32_t i = 0; i < seg_count; ++i) {
+ index += new_data->WriteShort(index, segments_[i]->id_delta());
+ }
+ for (int32_t i = 0; i < seg_count; ++i) {
+ index += new_data->WriteUShort(index, segments_[i]->id_range_offset());
+ }
+
+#if defined SFNTLY_DEBUG_CMAP
+ fprintf(stderr, "Glyph id array size %lu\n", glyph_id_array_.size());
+#endif
+ for (size_t i = 0; i < glyph_id_array_.size(); ++i) {
+ index += new_data->WriteUShort(index, glyph_id_array_[i]);
+ }
+
+ new_data->WriteUShort(Offset::kFormat4Length, index);
+ return index;
+}
+
+/******************************************************************************
+ * CMapTable::Builder class
+ ******************************************************************************/
+CMapTable::Builder::Builder(Header* header, WritableFontData* data)
+ : SubTableContainerTable::Builder(header, data), version_(0) {
+}
+
+CMapTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : SubTableContainerTable::Builder(header, data), version_(0) {
+}
+
+CMapTable::Builder::~Builder() {
+}
+
+int32_t CMapTable::Builder::SubSerialize(WritableFontData* new_data) {
+ int32_t size = new_data->WriteUShort(CMapTable::Offset::kVersion,
+ version_);
+ size += new_data->WriteUShort(CMapTable::Offset::kNumTables,
+ GetCMapBuilders()->size());
+
+ int32_t index_offset = size;
+ size += GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize;
+ for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
+ e = GetCMapBuilders()->end(); it != e; ++it) {
+ CMapBuilderPtr b = it->second;
+ // header entry
+ index_offset += new_data->WriteUShort(index_offset, b->platform_id());
+ index_offset += new_data->WriteUShort(index_offset, b->encoding_id());
+ index_offset += new_data->WriteULong(index_offset, size);
+
+ // cmap
+ FontDataPtr slice;
+ slice.Attach(new_data->Slice(size));
+ size += b->SubSerialize(down_cast<WritableFontData*>(slice.p_));
+ }
+ return size;
+}
+
+bool CMapTable::Builder::SubReadyToSerialize() {
+ if (GetCMapBuilders()->empty())
+ return false;
+
+ // check each table
+ for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
+ e = GetCMapBuilders()->end(); it != e; ++it) {
+ if (!it->second->SubReadyToSerialize())
+ return false;
+ }
+ return true;
+}
+
+int32_t CMapTable::Builder::SubDataSizeToSerialize() {
+ if (GetCMapBuilders()->empty())
+ return 0;
+
+ bool variable = false;
+ int32_t size = CMapTable::Offset::kEncodingRecordStart +
+ GetCMapBuilders()->size() * CMapTable::Offset::kEncodingRecordSize;
+
+ // calculate size of each table
+ for (CMapBuilderMap::iterator it = GetCMapBuilders()->begin(),
+ e = GetCMapBuilders()->end(); it != e; ++it) {
+ int32_t cmap_size = it->second->SubDataSizeToSerialize();
+ size += abs(cmap_size);
+ variable |= cmap_size <= 0;
+ }
+ return variable ? -size : size;
+}
+
+void CMapTable::Builder::SubDataSet() {
+ GetCMapBuilders()->clear();
+ Table::Builder::set_model_changed();
+}
+
+CALLER_ATTACH FontDataTable*
+ CMapTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new CMapTable(header(), data);
+ return table.Detach();
+}
+
+CALLER_ATTACH CMapTable::Builder*
+ CMapTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<CMapTable::Builder> builder;
+ builder = new CMapTable::Builder(header, data);
+ return builder.Detach();
+}
+
+// static
+CALLER_ATTACH CMapTable::CMap::Builder*
+ CMapTable::Builder::CMapBuilder(ReadableFontData* data, int32_t index) {
+ if (index < 0 || index > NumCMaps(data)) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException(
+ "CMap table is outside of the bounds of the known tables.");
+#endif
+ return NULL;
+ }
+
+ int32_t platform_id = data->ReadUShort(Offset::kEncodingRecordPlatformId +
+ OffsetForEncodingRecord(index));
+ int32_t encoding_id = data->ReadUShort(Offset::kEncodingRecordEncodingId +
+ OffsetForEncodingRecord(index));
+ int32_t offset = data->ReadULongAsInt(Offset::kEncodingRecordOffset +
+ OffsetForEncodingRecord(index));
+ return CMap::Builder::GetBuilder(data, offset,
+ NewCMapId(platform_id, encoding_id));
+}
+
+// static
+int32_t CMapTable::Builder::NumCMaps(ReadableFontData* data) {
+ if (data == NULL) {
+ return 0;
+ }
+ return data->ReadUShort(Offset::kNumTables);
+}
+
+int32_t CMapTable::Builder::NumCMaps() {
+ return GetCMapBuilders()->size();
+}
+
+void CMapTable::Builder::Initialize(ReadableFontData* data) {
+ int32_t num_cmaps = NumCMaps(data);
+ for (int32_t i = 0; i < num_cmaps; ++i) {
+ CMapTable::CMap::Builder* cmap_builder = CMapBuilder(data, i);
+ if (!cmap_builder)
+ continue;
+ cmap_builders_[cmap_builder->cmap_id()] = cmap_builder;
+ }
+}
+
+CMapTable::CMap::Builder* CMapTable::Builder::NewCMapBuilder(
+ const CMapId& cmap_id,
+ ReadableFontData* data) {
+ Ptr<WritableFontData> wfd;
+ wfd.Attach(WritableFontData::CreateWritableFontData(data->Size()));
+ data->CopyTo(wfd.p_);
+ CMapTable::CMapBuilderPtr builder;
+ builder.Attach(CMap::Builder::GetBuilder(wfd.p_, 0, cmap_id));
+ CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders();
+ cmap_builders->insert(std::make_pair(cmap_id, builder.p_));
+ return builder.Detach();
+}
+
+CMapTable::CMap::Builder*
+CMapTable::Builder::NewCMapBuilder(int32_t format, const CMapId& cmap_id) {
+ Ptr<CMapTable::CMap::Builder> cmap_builder;
+ cmap_builder.Attach(CMap::Builder::GetBuilder(format, cmap_id));
+ CMapBuilderMap* cmap_builders = CMapTable::Builder::GetCMapBuilders();
+ cmap_builders->insert(std::make_pair(cmap_id, cmap_builder.p_));
+ return cmap_builder.Detach();
+}
+
+CMapTable::CMap::Builder*
+CMapTable::Builder::CMapBuilder(const CMapId& cmap_id) {
+ CMapBuilderMap* cmap_builders = this->GetCMapBuilders();
+ CMapBuilderMap::iterator builder = cmap_builders->find(cmap_id);
+ if (builder != cmap_builders->end())
+ return builder->second;
+#ifndef SFNTLY_NO_EXCEPTION
+ throw NoSuchElementException("No builder found for cmap_id");
+#else
+ return NULL;
+#endif
+}
+
+CMapTable::CMapBuilderMap* CMapTable::Builder::GetCMapBuilders() {
+ if (cmap_builders_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &cmap_builders_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/cmap_table.h b/sfntly/table/core/cmap_table.h
new file mode 100644
index 0000000..b264b07
--- /dev/null
+++ b/sfntly/table/core/cmap_table.h
@@ -0,0 +1,709 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_CMAP_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_CMAP_TABLE_H_
+
+// type.h needs to be included first because of building issues on Windows
+// Type aliases we delcare are defined in other headers and make the build
+// fail otherwise.
+#include "sfntly/port/type.h"
+#include <vector>
+#include <map>
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/subtable.h"
+#include "sfntly/table/subtable_container_table.h"
+
+namespace sfntly {
+
+// CMap subtable formats
+struct CMapFormat {
+ enum {
+ kFormat0 = 0,
+ kFormat2 = 2,
+ kFormat4 = 4,
+ kFormat6 = 6,
+ kFormat8 = 8,
+ kFormat10 = 10,
+ kFormat12 = 12,
+ kFormat13 = 13,
+ kFormat14 = 14
+ };
+};
+
+// A CMap table
+class CMapTable : public SubTableContainerTable, public RefCounted<CMapTable> {
+public:
+ // CMapTable::CMapId
+ struct CMapId {
+ int32_t platform_id;
+ int32_t encoding_id;
+ bool operator==(const CMapId& obj) const {
+ return platform_id == obj.platform_id && encoding_id == obj.encoding_id;
+ }
+ };
+ static CMapId WINDOWS_BMP;
+ static CMapId WINDOWS_UCS4;
+ static CMapId MAC_ROMAN;
+
+ // CMapTable::CMapIdComparator
+ class CMapIdComparator {
+ public:
+ bool operator()(const CMapId& lhs, const CMapId& rhs) const;
+ };
+
+ // A filter on cmap
+ // CMapTable::CMapFilter
+ class CMapFilter {
+ public:
+ // Test on whether the cmap is acceptable or not
+ // @param cmap_id the id of the cmap
+ // @return true if the cmap is acceptable; false otherwise
+ virtual bool accept(const CMapId& cmap_id) const = 0;
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~CMapFilter() {}
+ };
+
+ // Filters CMaps by CMapId to implement CMapTable::get()
+ // wanted_id is the CMap we'd like to find.
+ // We compare the current CMap to it either by equality (==) or using a
+ // comparator.
+ // CMapTable::CMapIdFilter
+ class CMapIdFilter : public CMapFilter {
+ public:
+ explicit CMapIdFilter(const CMapId wanted_id);
+ CMapIdFilter(const CMapId wanted_id,
+ const CMapIdComparator* comparator);
+ ~CMapIdFilter() {}
+ virtual bool accept(const CMapId& cmap_id) const;
+ private:
+ CMapIdFilter& operator=(const CMapIdFilter& that);
+ const CMapId wanted_id_;
+ const CMapIdComparator *comparator_;
+ };
+
+ // The abstract base class for all cmaps.
+ //
+ // CMap equality is based on the equality of the (@link {@link CMapId} that
+ // defines the CMap. In the cmap table for a font there can only be one cmap
+ // with a given cmap id (pair of platform and encoding ids) no matter what the
+ // type of the cmap is.
+ //
+ // The cmap offers CharacterIterator to allow iteration over
+ // characters that are mapped by the cmap. This iteration mostly returns the
+ // characters mapped by the cmap. It will return all characters mapped by the
+ // cmap to anything but .notdef <b>but</b> it may return some that are not
+ // mapped or are mapped to .notdef. Various cmap tables provide ranges and
+ // such to describe characters for lookup but without going the full way to
+ // mapping to the glyph id it isn't always possible to tell if a character
+ // will end up with a valid glyph id. So, some of the characters returned from
+ // the Iterator may still end up pointing to the .notdef glyph. However, the
+ // number of such characters should be small in most cases with well designed
+ // cmaps.
+ class Builder;
+ class CMap : public SubTable {
+ public:
+ // CMapTable::CMap::Builder
+ class Builder : public SubTable::Builder {
+ public:
+ virtual ~Builder();
+
+ CALLER_ATTACH static Builder*
+ GetBuilder(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ CALLER_ATTACH static Builder*
+ GetBuilder(int32_t format,
+ const CMapId& cmap_id);
+
+ // Note: yes, an object is returned on stack since it's small enough.
+ virtual CMapId cmap_id() { return cmap_id_; }
+ virtual int32_t platform_id() { return cmap_id_.platform_id; }
+ virtual int32_t encoding_id() { return cmap_id_.encoding_id; }
+ virtual int32_t format() { return format_; }
+ virtual int32_t language() { return language_; }
+ virtual void set_language(int32_t language) { language_ = language; }
+
+ protected:
+ Builder(ReadableFontData* data,
+ int32_t format,
+ const CMapId& cmap_id);
+ Builder(WritableFontData* data,
+ int32_t format,
+ const CMapId& cmap_id);
+
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual void SubDataSet();
+
+ private:
+ int32_t format_;
+ CMapId cmap_id_;
+ int32_t language_;
+
+ friend class CMapTable::Builder;
+ };
+ // Abstract CMap character iterator
+ // The fully qualified name is CMapTable::CMap::CharacterIterator
+ class CharacterIterator {
+ public:
+ virtual ~CharacterIterator() {}
+ virtual bool HasNext() = 0;
+ // Returns -1 if there are no more characters to iterate through
+ // and exceptions are turned off
+ virtual int32_t Next() = 0;
+
+ protected:
+ // Use the CMap::Iterator method below instead of directly requesting
+ // a CharacterIterator.
+ CharacterIterator() {}
+ };
+
+ CMap(ReadableFontData* data, int32_t format, const CMapId& cmap_id);
+ virtual ~CMap();
+
+ virtual CMap::CharacterIterator* Iterator() = 0;
+
+ virtual int32_t format() { return format_; }
+ virtual CMapId cmap_id() { return cmap_id_; }
+ virtual int32_t platform_id() { return cmap_id_.platform_id; }
+ virtual int32_t encoding_id() { return cmap_id_.encoding_id; }
+
+ // Get the language of the cmap.
+ //
+ // Note on the language field in 'cmap' subtables: The language field must
+ // be set to zero for all cmap subtables whose platform IDs are other than
+ // Macintosh (platform ID 1). For cmap subtables whose platform IDs are
+ // Macintosh, set this field to the Macintosh language ID of the cmap
+ // subtable plus one, or to zero if the cmap subtable is not
+ // language-specific. For example, a Mac OS Turkish cmap subtable must set
+ // this field to 18, since the Macintosh language ID for Turkish is 17. A
+ // Mac OS Roman cmap subtable must set this field to 0, since Mac OS Roman
+ // is not a language-specific encoding.
+ //
+ // @return the language id
+ virtual int32_t Language() = 0;
+
+ // Gets the glyph id for the character code provided.
+ // The character code provided must be in the encoding used by the cmap
+ // table.
+ virtual int32_t GlyphId(int32_t character) = 0;
+
+ private:
+ int32_t format_;
+ CMapId cmap_id_;
+ };
+ typedef Ptr<CMap> CMapPtr;
+ typedef Ptr<CMap::Builder> CMapBuilderPtr;
+ typedef std::map<CMapId, CMapBuilderPtr, CMapIdComparator> CMapBuilderMap;
+
+ // A cmap format 0 sub table
+ class CMapFormat0 : public CMap, public RefCounted<CMapFormat0> {
+ public:
+ // The fully qualified name is CMapTable::CMapFormat0::Builder
+ class Builder : public CMap::Builder,
+ public RefCounted<Builder> {
+ public:
+ CALLER_ATTACH static Builder* NewInstance(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ CALLER_ATTACH static Builder* NewInstance(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ CALLER_ATTACH static Builder* NewInstance(const CMapId& cmap_id);
+ virtual ~Builder();
+
+ protected:
+ virtual CALLER_ATTACH FontDataTable*
+ SubBuildTable(ReadableFontData* data);
+
+ private:
+ // When creating a new CMapFormat0 Builder, use NewInstance instead of
+ // the constructors! This avoids a memory leak when slicing the FontData.
+ Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id);
+ Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id);
+ Builder(const CMapId& cmap_id);
+ };
+
+ // The fully qualified name is CMapTable::CMapFormat0::CharacterIterator
+ class CharacterIterator : public CMap::CharacterIterator {
+ public:
+ virtual ~CharacterIterator();
+ virtual bool HasNext();
+ virtual int32_t Next();
+
+ private:
+ CharacterIterator(int32_t start, int32_t end);
+ friend class CMapFormat0;
+ int32_t character_, max_character_;
+ };
+
+ virtual ~CMapFormat0();
+ virtual int32_t Language();
+ virtual int32_t GlyphId(int32_t character);
+ CMap::CharacterIterator* Iterator();
+
+ private:
+ CMapFormat0(ReadableFontData* data, const CMapId& cmap_id);
+ };
+
+ // A cmap format 2 sub table
+ // The format 2 cmap is used for multi-byte encodings such as SJIS,
+ // EUC-JP/KR/CN, Big5, etc.
+ class CMapFormat2 : public CMap, public RefCounted<CMapFormat2> {
+ public:
+ // CMapTable::CMapFormat2::Builder
+ class Builder : public CMap::Builder,
+ public RefCounted<Builder> {
+ public:
+ Builder(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ Builder(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ virtual ~Builder();
+
+ protected:
+ virtual CALLER_ATTACH FontDataTable*
+ SubBuildTable(ReadableFontData* data);
+ };
+ // CMapTable::CMapFormat2::CharacterIterator
+ class CharacterIterator : public CMap::CharacterIterator {
+ public:
+ virtual ~CharacterIterator();
+ virtual bool hasNext();
+ virtual int32_t next();
+
+ private:
+ CharacterIterator();
+ };
+
+ virtual int32_t Language();
+ virtual int32_t GlyphId(int32_t character);
+
+ // Returns how many bytes would be consumed by a lookup of this character
+ // with this cmap. This comes about because the cmap format 2 table is
+ // designed around multi-byte encodings such as SJIS, EUC-JP, Big5, etc.
+ // return the number of bytes consumed from this "character" - either 1 or 2
+ virtual int32_t BytesConsumed(int32_t character);
+
+ virtual ~CMapFormat2();
+
+ private:
+ CMapFormat2(ReadableFontData* data, const CMapId& cmap_id);
+
+ int32_t SubHeaderOffset(int32_t sub_header_index);
+ int32_t FirstCode(int32_t sub_header_index);
+ int32_t EntryCount(int32_t sub_header_index);
+ int32_t IdRangeOffset(int32_t sub_header_index);
+ int32_t IdDelta(int32_t sub_header_index);
+ CMap::CharacterIterator* Iterator();
+ };
+
+ // CMapTable::CMapFormat4
+ class CMapFormat4 : public CMap,
+ public RefCounted<CMapFormat4> {
+ public:
+ // CMapTable::CMapFormat4::Builder
+ class Builder : public CMap::Builder,
+ public RefCounted<Builder> {
+ public:
+ // CMapTable::CMapFormat4::Builder::Segment
+ class Segment : public RefCounted<Segment> {
+ public:
+ Segment();
+ explicit Segment(Segment* other);
+ Segment(int32_t start_count,
+ int32_t end_count,
+ int32_t id_delta,
+ int32_t id_range_offset);
+ ~Segment();
+
+ // @return the startCount
+ int32_t start_count();
+ // @param startCount the startCount to set
+ void set_start_count(int32_t start_count);
+ // @return the endCount
+ int32_t end_count();
+ // @param endcount the endCount to set
+ void set_end_count(int32_t end_count);
+ // @return the idDelta
+ int32_t id_delta();
+ // @param idDelta the idDelta to set
+ void set_id_delta(int32_t id_delta);
+ // @return the idRangeOffset
+ int32_t id_range_offset();
+ // @param idRangeOffset the idRangeOffset to set
+ void set_id_range_offset(int32_t id_range_offset);
+
+ static CALLER_ATTACH
+ std::vector<Ptr<Segment> >*
+ DeepCopy(std::vector<Ptr<Segment> >* original);
+
+ private:
+ int32_t start_count_;
+ int32_t end_count_;
+ int32_t id_delta_;
+ int32_t id_range_offset_;
+ };
+ typedef std::vector<Ptr<Segment> > SegmentList;
+
+ static CALLER_ATTACH Builder* NewInstance(WritableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ static CALLER_ATTACH Builder* NewInstance(ReadableFontData* data,
+ int32_t offset,
+ const CMapId& cmap_id);
+ static CALLER_ATTACH Builder* NewInstance(const CMapId& cmap_id);
+ virtual ~Builder();
+ SegmentList* segments();
+ void set_segments(SegmentList* segments);
+ IntegerList* glyph_id_array();
+ void set_glyph_id_array(IntegerList* glyph_id_array);
+
+ protected:
+ Builder(WritableFontData* data, int32_t offset, const CMapId& cmap_id);
+ Builder(ReadableFontData* data, int32_t offset, const CMapId& cmap_id);
+ Builder(SegmentList* segments, IntegerList* glyph_id_array,
+ const CMapId& cmap_id);
+ explicit Builder(const CMapId& cmap_id);
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(
+ ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ private:
+ void Initialize(ReadableFontData* data);
+
+ SegmentList segments_;
+ IntegerList glyph_id_array_;
+ };
+
+ CMap::CharacterIterator* Iterator();
+ // CMapTable::CMapFormat4::CharacterIterator
+ class CharacterIterator : public CMap::CharacterIterator {
+ public:
+ bool HasNext();
+ int32_t Next();
+ virtual ~CharacterIterator() {}
+
+ private:
+ explicit CharacterIterator(CMapFormat4 *parent);
+ friend CMap::CharacterIterator* CMapFormat4::Iterator();
+
+ CMapFormat4* parent_;
+ int32_t segment_index_;
+ int32_t first_char_in_segment_;
+ int32_t last_char_in_segment_;
+ int32_t next_char_;
+ bool next_char_set_;
+ };
+
+ virtual int32_t GlyphId(int32_t character);
+
+ // Lower level glyph code retrieval that requires processing the Format 4
+ // segments to use.
+ // @param segment the cmap segment
+ // @param startCode the start code for the segment
+ // @param character the character to be looked up
+ // @return the glyph id for the character; CMapTable.NOTDEF if not found
+ int32_t RetrieveGlyphId(int32_t segment,
+ int32_t start_count,
+ int32_t character);
+ virtual int32_t Language();
+
+ // Get the count of the number of segments in this cmap.
+ // @return the number of segments
+ int32_t seg_count();
+ int32_t Length();
+ // Get the start code for a segment.
+ // @param segment the segment in the lookup table
+ // @return the start code for a segment
+ int32_t StartCode(int32_t segment);
+ // Get the end code for a segment.
+ // @param segment the segment in the look up table
+ // @return the end code for the segment
+ int32_t EndCode(int32_t segment);
+ // Get the id delta for a segment
+ // @param segment the segment in the look up table
+ // @return the id delta for the segment
+ int32_t IdDelta(int32_t segment);
+ // Get the id range offset for a segment
+ // @param segment the segment in the look up table
+ // @return the id range offset for the segment
+ int32_t IdRangeOffset(int32_t segment);
+ // Get the location of the id range offset for a segment
+ // @param segment the segment in the look up table
+ // @return the location of the id range offset for the segment
+ int32_t IdRangeOffsetLocation(int32_t segment);
+ // Declared above to allow friending inside CharacterIterator class.
+ // CMap::CharacterIterator* Iterator();
+ virtual ~CMapFormat4();
+
+ protected:
+ CMapFormat4(ReadableFontData* data, const CMapId& cmap_id);
+
+ private:
+ static int32_t Language(ReadableFontData* data);
+ static int32_t Length(ReadableFontData* data);
+ static int32_t SegCount(ReadableFontData* data);
+ static int32_t StartCode(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index);
+ static int32_t StartCodeOffset(int32_t seg_count);
+ static int32_t EndCode(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index);
+ static int32_t IdDelta(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index);
+ static int32_t IdDeltaOffset(int32_t seg_count);
+ static int32_t IdRangeOffset(ReadableFontData* data,
+ int32_t seg_count,
+ int32_t index);
+ static int32_t IdRangeOffsetOffset(int32_t seg_count);
+ static int32_t GlyphIdArrayOffset(int32_t seg_count);
+ // Refactored void to bool to work without exceptions.
+ bool IsValidIndex(int32_t segment);
+ int32_t GlyphIdArray(int32_t index);
+
+ int32_t seg_count_;
+ int32_t start_code_offset_;
+ int32_t id_delta_offset_;
+ int32_t glyph_id_array_offset_;
+ };
+
+ // CMapTable::Builder
+ class Builder : public SubTableContainerTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope is public because C++ does not allow base class to
+ // instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual void SubDataSet();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ CMap::Builder* NewCMapBuilder(const CMapId& cmap_id,
+ ReadableFontData* data);
+ // Create a new empty CMapBuilder of the type specified in the id.
+ CMap::Builder* NewCMapBuilder(int32_t format, const CMapId& cmap_id);
+ CMap::Builder* CMapBuilder(const CMapId& cmap_id);
+
+ int32_t NumCMaps();
+ void SetVersion(int32_t version);
+
+ CMapBuilderMap* GetCMapBuilders();
+
+ protected:
+ static CALLER_ATTACH CMap::Builder* CMapBuilder(ReadableFontData* data,
+ int32_t index);
+
+ private:
+ void Initialize(ReadableFontData* data);
+ static int32_t NumCMaps(ReadableFontData* data);
+
+ int32_t version_;
+ CMapBuilderMap cmap_builders_;
+ };
+ typedef Ptr<Builder> CMapTableBuilderPtr;
+
+ class CMapIterator {
+ public:
+ // If filter is NULL, filter through all tables.
+ CMapIterator(CMapTable* table, const CMapFilter* filter);
+ bool HasNext();
+ CMap* Next();
+
+ private:
+ int32_t table_index_;
+ const CMapFilter* filter_;
+ CMapTable* table_;
+ };
+
+ // Make a CMapId from a platform_id, encoding_id pair
+ static CMapId NewCMapId(int32_t platform_id, int32_t encoding_id);
+ // Make a CMapId from another CMapId
+ static CMapId NewCMapId(const CMapId& obj);
+
+ // Get the CMap with the specified parameters if it exists.
+ // Returns NULL otherwise.
+ CALLER_ATTACH CMap* GetCMap(const int32_t index);
+ CALLER_ATTACH CMap* GetCMap(const int32_t platform_id,
+ const int32_t encoding_id);
+ CALLER_ATTACH CMap* GetCMap(const CMapId GetCMap_id);
+
+ // Get the table version.
+ virtual int32_t Version();
+
+ // Get the number of cmaps within the CMap table.
+ virtual int32_t NumCMaps();
+
+ // Get the cmap id for the cmap with the given index.
+ // Note: yes, an object is returned on stack since it's small enough.
+ // This function is renamed from cmapId to GetCMapId().
+ virtual CMapId GetCMapId(int32_t index);
+
+ virtual int32_t PlatformId(int32_t index);
+ virtual int32_t EncodingId(int32_t index);
+
+ // Get the offset in the table data for the cmap table with the given index.
+ // The offset is from the beginning of the table.
+ virtual int32_t Offset(int32_t index);
+
+ virtual ~CMapTable();
+
+ static const int32_t NOTDEF;
+
+ private:
+ // Offsets to specific elements in the underlying data. These offsets are
+ // relative to the start of the table or the start of sub-blocks within
+ // the table.
+ struct Offset {
+ enum {
+ kVersion = 0,
+ kNumTables = 2,
+ kEncodingRecordStart = 4,
+
+ // offsets relative to the encoding record
+ kEncodingRecordPlatformId = 0,
+ kEncodingRecordEncodingId = 2,
+ kEncodingRecordOffset = 4,
+ kEncodingRecordSize = 8,
+
+ kFormat = 0,
+
+ // Format 0: Byte encoding table
+ kFormat0Format = 0,
+ kFormat0Length = 2,
+ kFormat0Language = 4,
+ kFormat0GlyphIdArray = 6,
+
+ // Format 2: High-byte mapping through table
+ kFormat2Format = 0,
+ kFormat2Length = 2,
+ kFormat2Language = 4,
+ kFormat2SubHeaderKeys = 6,
+ kFormat2SubHeaders = 518,
+ // offset relative to the subHeader structure
+ kFormat2SubHeader_firstCode = 0,
+ kFormat2SubHeader_entryCount = 2,
+ kFormat2SubHeader_idDelta = 4,
+ kFormat2SubHeader_idRangeOffset = 6,
+ kFormat2SubHeader_structLength = 8,
+
+ // Format 4: Segment mapping to delta values
+ kFormat4Format = 0,
+ kFormat4Length = 2,
+ kFormat4Language = 4,
+ kFormat4SegCountX2 = 6,
+ kFormat4SearchRange = 8,
+ kFormat4EntrySelector = 10,
+ kFormat4RangeShift = 12,
+ kFormat4EndCount = 14,
+ kFormat4FixedSize = 16,
+
+ // format 6: Trimmed table mapping
+ kFormat6Format = 0,
+ kFormat6Length = 2,
+ kFormat6Language = 4,
+ kFormat6FirstCode = 6,
+ kFormat6EntryCount = 8,
+ kFormat6GlyphIdArray = 10,
+
+ // Format 8: mixed 16-bit and 32-bit coverage
+ kFormat8Format = 0,
+ kFormat8Length = 4,
+ kFormat8Language = 8,
+ kFormat8Is32 = 12,
+ kFormat8nGroups204 = 8204,
+ kFormat8Groups208 = 8208,
+ // offset relative to the group structure
+ kFormat8Group_startCharCode = 0,
+ kFormat8Group_endCharCode = 4,
+ kFormat8Group_startGlyphId = 8,
+ kFormat8Group_structLength = 12,
+
+ // Format 10: Trimmed array
+ kFormat10Format = 0,
+ kFormat10Length = 4,
+ kFormat10Language = 8,
+ kFormat10StartCharCode = 12,
+ kFormat10NumChars = 16,
+ kFormat10Glyphs0 = 20,
+
+ // Format 12: Segmented coverage
+ kFormat12Format = 0,
+ kFormat12Length = 4,
+ kFormat12Language = 8,
+ kFormat12nGroups = 12,
+ kFormat12Groups = 16,
+ kFormat12Groups_structLength = 12,
+ // offsets within the group structure
+ kFormat12_startCharCode = 0,
+ kFormat12_endCharCode = 4,
+ kFormat12_startGlyphId = 8,
+
+ // Format 13: Last Resort Font
+ kFormat13Format = 0,
+ kFormat13Length = 4,
+ kFormat13Language = 8,
+ kFormat13nGroups = 12,
+ kFormat13Groups = 16,
+ kFormat13Groups_structLength = 12,
+ // offsets within the group structure
+ kFormat13_startCharCode = 0,
+ kFormat13_endCharCode = 4,
+ kFormat13_glyphId = 8,
+
+ // Format 14: Unicode Variation Sequences
+ kFormat14Format = 0,
+ kFormat14Length = 2,
+
+ // TODO(stuartg): finish tables
+ // Default UVS Table
+
+ // Non-default UVS Table
+ kLast = -1
+ };
+ };
+
+ CMapTable(Header* header, ReadableFontData* data);
+
+ // Get the offset in the table data for the encoding record for the cmap with
+ // the given index. The offset is from the beginning of the table.
+ static int32_t OffsetForEncodingRecord(int32_t index);
+};
+typedef std::vector<CMapTable::CMapId> CMapIdList;
+typedef Ptr<CMapTable> CMapTablePtr;
+typedef std::vector<Ptr<CMapTable::CMapFormat4::Builder::Segment> > SegmentList;
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_CMAP_TABLE_H_
diff --git a/sfntly/table/core/font_header_table.cc b/sfntly/table/core/font_header_table.cc
new file mode 100644
index 0000000..60015ca
--- /dev/null
+++ b/sfntly/table/core/font_header_table.cc
@@ -0,0 +1,265 @@
+/*
+ * 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/table/core/font_header_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * FontHeaderTable class
+ ******************************************************************************/
+FontHeaderTable::~FontHeaderTable() {}
+
+int32_t FontHeaderTable::TableVersion() {
+ return data_->ReadFixed(Offset::kTableVersion);
+}
+
+int32_t FontHeaderTable::FontRevision() {
+ return data_->ReadFixed(Offset::kFontRevision);
+}
+
+int64_t FontHeaderTable::ChecksumAdjustment() {
+ return data_->ReadULong(Offset::kCheckSumAdjustment);
+}
+
+int64_t FontHeaderTable::MagicNumber() {
+ return data_->ReadULong(Offset::kMagicNumber);
+}
+
+int32_t FontHeaderTable::FlagsAsInt() {
+ return data_->ReadUShort(Offset::kFlags);
+}
+
+int32_t FontHeaderTable::UnitsPerEm() {
+ return data_->ReadUShort(Offset::kUnitsPerEm);
+}
+
+int64_t FontHeaderTable::Created() {
+ return data_->ReadDateTimeAsLong(Offset::kCreated);
+}
+
+int64_t FontHeaderTable::Modified() {
+ return data_->ReadDateTimeAsLong(Offset::kModified);
+}
+
+int32_t FontHeaderTable::XMin() {
+ return data_->ReadUShort(Offset::kXMin);
+}
+
+int32_t FontHeaderTable::YMin() {
+ return data_->ReadUShort(Offset::kYMin);
+}
+
+int32_t FontHeaderTable::XMax() {
+ return data_->ReadUShort(Offset::kXMax);
+}
+
+int32_t FontHeaderTable::YMax() {
+ return data_->ReadUShort(Offset::kYMax);
+}
+
+int32_t FontHeaderTable::MacStyleAsInt() {
+ return data_->ReadUShort(Offset::kMacStyle);
+}
+
+int32_t FontHeaderTable::LowestRecPPEM() {
+ return data_->ReadUShort(Offset::kLowestRecPPEM);
+}
+
+int32_t FontHeaderTable::FontDirectionHint() {
+ return data_->ReadShort(Offset::kFontDirectionHint);
+}
+
+int32_t FontHeaderTable::IndexToLocFormat() {
+ return data_->ReadShort(Offset::kIndexToLocFormat);
+}
+
+int32_t FontHeaderTable::GlyphDataFormat() {
+ return data_->ReadShort(Offset::kGlyphDataFormat);
+}
+
+FontHeaderTable::FontHeaderTable(Header* header, ReadableFontData* data)
+ : Table(header, data) {
+ IntegerList checksum_ranges;
+ checksum_ranges.push_back(0);
+ checksum_ranges.push_back(Offset::kCheckSumAdjustment);
+ checksum_ranges.push_back(Offset::kMagicNumber);
+ data_->SetCheckSumRanges(checksum_ranges);
+}
+
+/******************************************************************************
+ * FontHeaderTable::Builder class
+ ******************************************************************************/
+FontHeaderTable::Builder::Builder(Header* header, WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+FontHeaderTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+FontHeaderTable::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable* FontHeaderTable::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ FontDataTablePtr table = new FontHeaderTable(header(), data);
+ return table.Detach();
+}
+
+int32_t FontHeaderTable::Builder::TableVersion() {
+ return down_cast<FontHeaderTable*>(GetTable())->TableVersion();
+}
+
+void FontHeaderTable::Builder::SetTableVersion(int32_t version) {
+ InternalWriteData()->WriteFixed(Offset::kTableVersion, version);
+}
+
+int32_t FontHeaderTable::Builder::FontRevision() {
+ return down_cast<FontHeaderTable*>(GetTable())->FontRevision();
+}
+
+void FontHeaderTable::Builder::SetFontRevision(int32_t revision) {
+ InternalWriteData()->WriteFixed(Offset::kFontRevision, revision);
+}
+
+int64_t FontHeaderTable::Builder::ChecksumAdjustment() {
+ return down_cast<FontHeaderTable*>(GetTable())->ChecksumAdjustment();
+}
+
+void FontHeaderTable::Builder::SetChecksumAdjustment(int64_t adjustment) {
+ InternalWriteData()->WriteULong(Offset::kCheckSumAdjustment, adjustment);
+}
+
+int64_t FontHeaderTable::Builder::MagicNumber() {
+ return down_cast<FontHeaderTable*>(GetTable())->MagicNumber();
+}
+
+void FontHeaderTable::Builder::SetMagicNumber(int64_t magic_number) {
+ InternalWriteData()->WriteULong(Offset::kMagicNumber, magic_number);
+}
+
+int32_t FontHeaderTable::Builder::FlagsAsInt() {
+ return down_cast<FontHeaderTable*>(GetTable())->FlagsAsInt();
+}
+
+void FontHeaderTable::Builder::SetFlagsAsInt(int32_t flags) {
+ InternalWriteData()->WriteUShort(Offset::kFlags, flags);
+}
+
+int32_t FontHeaderTable::Builder::UnitsPerEm() {
+ return down_cast<FontHeaderTable*>(GetTable())->UnitsPerEm();
+}
+
+void FontHeaderTable::Builder::SetUnitsPerEm(int32_t units) {
+ InternalWriteData()->WriteUShort(Offset::kUnitsPerEm, units);
+}
+
+int64_t FontHeaderTable::Builder::Created() {
+ return down_cast<FontHeaderTable*>(GetTable())->Created();
+}
+
+void FontHeaderTable::Builder::SetCreated(int64_t date) {
+ InternalWriteData()->WriteDateTime(Offset::kCreated, date);
+}
+
+int64_t FontHeaderTable::Builder::Modified() {
+ return down_cast<FontHeaderTable*>(GetTable())->Modified();
+}
+
+void FontHeaderTable::Builder::SetModified(int64_t date) {
+ InternalWriteData()->WriteDateTime(Offset::kModified, date);
+}
+
+int32_t FontHeaderTable::Builder::XMin() {
+ return down_cast<FontHeaderTable*>(GetTable())->XMin();
+}
+
+void FontHeaderTable::Builder::SetXMin(int32_t xmin) {
+ InternalWriteData()->WriteShort(Offset::kXMin, xmin);
+}
+
+int32_t FontHeaderTable::Builder::YMin() {
+ return down_cast<FontHeaderTable*>(GetTable())->YMin();
+}
+
+void FontHeaderTable::Builder::SetYMin(int32_t ymin) {
+ InternalWriteData()->WriteShort(Offset::kYMin, ymin);
+}
+
+int32_t FontHeaderTable::Builder::XMax() {
+ return down_cast<FontHeaderTable*>(GetTable())->XMax();
+}
+
+void FontHeaderTable::Builder::SetXMax(int32_t xmax) {
+ InternalWriteData()->WriteShort(Offset::kXMax, xmax);
+}
+
+int32_t FontHeaderTable::Builder::YMax() {
+ return down_cast<FontHeaderTable*>(GetTable())->YMax();
+}
+
+void FontHeaderTable::Builder::SetYMax(int32_t ymax) {
+ InternalWriteData()->WriteShort(Offset::kYMax, ymax);
+}
+
+int32_t FontHeaderTable::Builder::MacStyleAsInt() {
+ return down_cast<FontHeaderTable*>(GetTable())->MacStyleAsInt();
+}
+
+void FontHeaderTable::Builder::SetMacStyleAsInt(int32_t style) {
+ InternalWriteData()->WriteUShort(Offset::kMacStyle, style);
+}
+
+int32_t FontHeaderTable::Builder::LowestRecPPEM() {
+ return down_cast<FontHeaderTable*>(GetTable())->LowestRecPPEM();
+}
+
+void FontHeaderTable::Builder::SetLowestRecPPEM(int32_t size) {
+ InternalWriteData()->WriteUShort(Offset::kLowestRecPPEM, size);
+}
+
+int32_t FontHeaderTable::Builder::FontDirectionHint() {
+ return down_cast<FontHeaderTable*>(GetTable())->FontDirectionHint();
+}
+
+void FontHeaderTable::Builder::SetFontDirectionHint(int32_t hint) {
+ InternalWriteData()->WriteShort(Offset::kFontDirectionHint, hint);
+}
+
+int32_t FontHeaderTable::Builder::IndexToLocFormat() {
+ return down_cast<FontHeaderTable*>(GetTable())->IndexToLocFormat();
+}
+
+void FontHeaderTable::Builder::SetIndexToLocFormat(int32_t format) {
+ InternalWriteData()->WriteShort(Offset::kIndexToLocFormat, format);
+}
+
+int32_t FontHeaderTable::Builder::GlyphDataFormat() {
+ return down_cast<FontHeaderTable*>(GetTable())->GlyphDataFormat();
+}
+
+void FontHeaderTable::Builder::SetGlyphDataFormat(int32_t format) {
+ InternalWriteData()->WriteShort(Offset::kGlyphDataFormat, format);
+}
+
+CALLER_ATTACH FontHeaderTable::Builder*
+ FontHeaderTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<FontHeaderTable::Builder> builder;
+ builder = new FontHeaderTable::Builder(header, data);
+ return builder.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/font_header_table.h b/sfntly/table/core/font_header_table.h
new file mode 100644
index 0000000..841955b
--- /dev/null
+++ b/sfntly/table/core/font_header_table.h
@@ -0,0 +1,168 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_FONT_HEADER_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_FONT_HEADER_TABLE_H_
+
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+struct IndexToLocFormat {
+ enum {
+ kShortOffset = 0,
+ kLongOffset = 1
+ };
+};
+
+struct FontDirectionHint {
+ enum {
+ kFullyMixed = 0,
+ kOnlyStrongLTR = 1,
+ kStrongLTRAndNeutral = 2,
+ kOnlyStrongRTL = -1,
+ kStrongRTLAndNeutral = -2
+ };
+};
+
+class FontHeaderTable : public Table, public RefCounted<FontHeaderTable> {
+ public:
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ virtual int32_t TableVersion();
+ virtual void SetTableVersion(int32_t version);
+ virtual int32_t FontRevision();
+ virtual void SetFontRevision(int32_t revision);
+ virtual int64_t ChecksumAdjustment();
+ virtual void SetChecksumAdjustment(int64_t adjustment);
+ virtual int64_t MagicNumber();
+ virtual void SetMagicNumber(int64_t magic_number);
+ virtual int32_t FlagsAsInt();
+ virtual void SetFlagsAsInt(int32_t flags);
+ // TODO(arthurhsu): IMPLEMENT EnumSet<Flags> Flags()
+ // TODO(arthurhsu): IMPLEMENT setFlags(EnumSet<Flags> flags)
+ virtual int32_t UnitsPerEm();
+ virtual void SetUnitsPerEm(int32_t units);
+ virtual int64_t Created();
+ virtual void SetCreated(int64_t date);
+ virtual int64_t Modified();
+ virtual void SetModified(int64_t date);
+ virtual int32_t XMin();
+ virtual void SetXMin(int32_t xmin);
+ virtual int32_t YMin();
+ virtual void SetYMin(int32_t ymin);
+ virtual int32_t XMax();
+ virtual void SetXMax(int32_t xmax);
+ virtual int32_t YMax();
+ virtual void SetYMax(int32_t ymax);
+ virtual int32_t MacStyleAsInt();
+ virtual void SetMacStyleAsInt(int32_t style);
+ // TODO(arthurhsu): IMPLEMENT EnumSet<MacStyle> macStyle()
+ // TODO(arthurhsu): IMPLEMENT setMacStyle(EnumSet<MacStyle> style)
+ virtual int32_t LowestRecPPEM();
+ virtual void SetLowestRecPPEM(int32_t size);
+ virtual int32_t FontDirectionHint();
+ virtual void SetFontDirectionHint(int32_t hint);
+ virtual int32_t IndexToLocFormat();
+ virtual void SetIndexToLocFormat(int32_t format);
+ virtual int32_t GlyphDataFormat();
+ virtual void SetGlyphDataFormat(int32_t format);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+ };
+
+ virtual ~FontHeaderTable();
+ int32_t TableVersion();
+ int32_t FontRevision();
+
+ // Get the checksum adjustment. To compute: set it to 0, sum the entire font
+ // as ULONG, then store 0xB1B0AFBA - sum.
+ int64_t ChecksumAdjustment();
+
+ // Get the magic number. Set to 0x5F0F3CF5.
+ int64_t MagicNumber();
+
+ // TODO(arthurhsu): IMPLEMENT: EnumSet<Flags>
+ int32_t FlagsAsInt();
+ // TODO(arthurhsu): IMPLEMENT: Flags() returning EnumSet<Flags>
+
+ int32_t UnitsPerEm();
+
+ // Get the created date. Number of seconds since 12:00 midnight, January 1,
+ // 1904. 64-bit integer.
+ int64_t Created();
+ // Get the modified date. Number of seconds since 12:00 midnight, January 1,
+ // 1904. 64-bit integer.
+ int64_t Modified();
+
+ // Get the x min. For all glyph bounding boxes.
+ int32_t XMin();
+ // Get the y min. For all glyph bounding boxes.
+ int32_t YMin();
+ // Get the x max. For all glyph bounding boxes.
+ int32_t XMax();
+ // Get the y max. For all glyph bounding boxes.
+ int32_t YMax();
+
+ // TODO(arthurhsu): IMPLEMENT: EnumSet<MacStyle>
+ int32_t MacStyleAsInt();
+ // TODO(arthurhsu): IMPLEMENT: macStyle() returning EnumSet<MacStyle>
+
+ int32_t LowestRecPPEM();
+ int32_t FontDirectionHint(); // Note: no AsInt() form, already int
+ int32_t IndexToLocFormat(); // Note: no AsInt() form, already int
+ int32_t GlyphDataFormat();
+
+ private:
+ struct Offset {
+ enum {
+ kTableVersion = 0,
+ kFontRevision = 4,
+ kCheckSumAdjustment = 8,
+ kMagicNumber = 12,
+ kFlags = 16,
+ kUnitsPerEm = 18,
+ kCreated = 20,
+ kModified = 28,
+ kXMin = 36,
+ kYMin = 38,
+ kXMax = 40,
+ kYMax = 42,
+ kMacStyle = 44,
+ kLowestRecPPEM = 46,
+ kFontDirectionHint = 48,
+ kIndexToLocFormat = 50,
+ kGlyphDataFormat = 52
+ };
+ };
+
+ FontHeaderTable(Header* header, ReadableFontData* data);
+};
+typedef Ptr<FontHeaderTable> FontHeaderTablePtr;
+typedef Ptr<FontHeaderTable::Builder> FontHeaderTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_FONT_HEADER_TABLE_H_
diff --git a/sfntly/table/core/horizontal_device_metrics_table.cc b/sfntly/table/core/horizontal_device_metrics_table.cc
new file mode 100644
index 0000000..50b0cf5
--- /dev/null
+++ b/sfntly/table/core/horizontal_device_metrics_table.cc
@@ -0,0 +1,124 @@
+/*
+ * 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/table/core/horizontal_device_metrics_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * HorizontalDeviceMetricsTable class
+ ******************************************************************************/
+HorizontalDeviceMetricsTable:: ~HorizontalDeviceMetricsTable() {}
+
+int32_t HorizontalDeviceMetricsTable::Version() {
+ return data_->ReadUShort(Offset::kVersion);
+}
+
+int32_t HorizontalDeviceMetricsTable::NumRecords() {
+ return data_->ReadShort(Offset::kNumRecords);
+}
+
+int32_t HorizontalDeviceMetricsTable::RecordSize() {
+ return data_->ReadLong(Offset::kSizeDeviceRecord);
+}
+
+int32_t HorizontalDeviceMetricsTable::PixelSize(int32_t record_index) {
+ if (record_index < 0 || record_index >= NumRecords()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException();
+#endif
+ return -1;
+ }
+ return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() +
+ Offset::kDeviceRecordPixelSize);
+}
+
+int32_t HorizontalDeviceMetricsTable::MaxWidth(int32_t record_index) {
+ if (record_index < 0 || record_index >= NumRecords()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException();
+#endif
+ return -1;
+ }
+ return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() +
+ Offset::kDeviceRecordMaxWidth);
+}
+
+int32_t HorizontalDeviceMetricsTable::Width(int32_t record_index,
+ int32_t glyph_num) {
+ if (record_index < 0 || record_index >= NumRecords() ||
+ glyph_num < 0 || glyph_num >= num_glyphs_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException();
+#endif
+ return -1;
+ }
+ return data_->ReadUByte(Offset::kRecords + record_index * RecordSize() +
+ Offset::kDeviceRecordWidths + glyph_num);
+}
+
+HorizontalDeviceMetricsTable::HorizontalDeviceMetricsTable(
+ Header* header,
+ ReadableFontData* data,
+ int32_t num_glyphs)
+ : Table(header, data), num_glyphs_(num_glyphs) {
+}
+
+/******************************************************************************
+ * HorizontalDeviceMetricsTable::Builder class
+ ******************************************************************************/
+HorizontalDeviceMetricsTable::Builder::Builder(Header* header,
+ WritableFontData* data)
+ : TableBasedTableBuilder(header, data), num_glyphs_(-1) {
+}
+
+HorizontalDeviceMetricsTable::Builder::Builder(Header* header,
+ ReadableFontData* data)
+ : TableBasedTableBuilder(header, data), num_glyphs_(-1) {
+}
+
+HorizontalDeviceMetricsTable::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable*
+HorizontalDeviceMetricsTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new HorizontalDeviceMetricsTable(header(), data,
+ num_glyphs_);
+ return table.Detach();
+}
+
+void HorizontalDeviceMetricsTable::Builder::SetNumGlyphs(int32_t num_glyphs) {
+ if (num_glyphs < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalArgumentException("Number of glyphs can't be negative.");
+#endif
+ return;
+ }
+ num_glyphs_ = num_glyphs;
+ HorizontalDeviceMetricsTable* table =
+ down_cast<HorizontalDeviceMetricsTable*>(GetTable());
+ if (table) {
+ table->num_glyphs_ = num_glyphs;
+ }
+}
+
+CALLER_ATTACH HorizontalDeviceMetricsTable::Builder*
+HorizontalDeviceMetricsTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<HorizontalDeviceMetricsTable::Builder> builder;
+ builder = new HorizontalDeviceMetricsTable::Builder(header, data);
+ return builder.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/horizontal_device_metrics_table.h b/sfntly/table/core/horizontal_device_metrics_table.h
new file mode 100644
index 0000000..4a27ba0
--- /dev/null
+++ b/sfntly/table/core/horizontal_device_metrics_table.h
@@ -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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_
+
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// A Horizontal Device Metrics table - 'hdmx'
+class HorizontalDeviceMetricsTable
+ : public Table,
+ public RefCounted<HorizontalDeviceMetricsTable> {
+ public:
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ void SetNumGlyphs(int32_t num_glyphs);
+
+ private:
+ int32_t num_glyphs_;
+ };
+
+ virtual ~HorizontalDeviceMetricsTable();
+
+ int32_t Version();
+ int32_t NumRecords();
+ int32_t RecordSize();
+ int32_t PixelSize(int32_t record_index);
+ int32_t MaxWidth(int32_t record_index);
+ int32_t Width(int32_t record_index, int32_t glyph_num);
+
+ private:
+ struct Offset {
+ enum {
+ kVersion = 0,
+ kNumRecords = 2,
+ kSizeDeviceRecord = 4,
+ kRecords = 8,
+
+ // Offsets within a device record
+ kDeviceRecordPixelSize = 0,
+ kDeviceRecordMaxWidth = 1,
+ kDeviceRecordWidths = 2,
+ };
+ };
+ HorizontalDeviceMetricsTable(Header* header,
+ ReadableFontData* data,
+ int32_t num_glyphs);
+
+ int32_t num_glyphs_;
+};
+typedef Ptr<HorizontalDeviceMetricsTable> HorizontalDeviceMetricsTablePtr;
+typedef Ptr<HorizontalDeviceMetricsTable::Builder>
+ HorizontalDeviceMetricsTableBuilderPtr;
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_DEVICE_METRICS_TABLE_H_
diff --git a/sfntly/table/core/horizontal_header_table.cc b/sfntly/table/core/horizontal_header_table.cc
new file mode 100644
index 0000000..43c2058
--- /dev/null
+++ b/sfntly/table/core/horizontal_header_table.cc
@@ -0,0 +1,213 @@
+/*
+ * 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/table/core/horizontal_header_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * HorizontalHeaderTable class
+ ******************************************************************************/
+HorizontalHeaderTable:: ~HorizontalHeaderTable() {}
+
+int32_t HorizontalHeaderTable::TableVersion() {
+ return data_->ReadFixed(Offset::kVersion);
+}
+
+int32_t HorizontalHeaderTable::Ascender() {
+ return data_->ReadShort(Offset::kAscender);
+}
+
+int32_t HorizontalHeaderTable::Descender() {
+ return data_->ReadShort(Offset::kDescender);
+}
+
+int32_t HorizontalHeaderTable::LineGap() {
+ return data_->ReadShort(Offset::kLineGap);
+}
+
+int32_t HorizontalHeaderTable::AdvanceWidthMax() {
+ return data_->ReadUShort(Offset::kAdvanceWidthMax);
+}
+
+int32_t HorizontalHeaderTable::MinLeftSideBearing() {
+ return data_->ReadShort(Offset::kMinLeftSideBearing);
+}
+
+int32_t HorizontalHeaderTable::MinRightSideBearing() {
+ return data_->ReadShort(Offset::kMinRightSideBearing);
+}
+
+int32_t HorizontalHeaderTable::XMaxExtent() {
+ return data_->ReadShort(Offset::kXMaxExtent);
+}
+
+int32_t HorizontalHeaderTable::CaretSlopeRise() {
+ return data_->ReadShort(Offset::kCaretSlopeRise);
+}
+
+int32_t HorizontalHeaderTable::CaretSlopeRun() {
+ return data_->ReadShort(Offset::kCaretSlopeRun);
+}
+
+int32_t HorizontalHeaderTable::CaretOffset() {
+ return data_->ReadShort(Offset::kCaretOffset);
+}
+
+int32_t HorizontalHeaderTable::MetricDataFormat() {
+ return data_->ReadShort(Offset::kMetricDataFormat);
+}
+
+int32_t HorizontalHeaderTable::NumberOfHMetrics() {
+ return data_->ReadUShort(Offset::kNumberOfHMetrics);
+}
+
+HorizontalHeaderTable:: HorizontalHeaderTable(Header* header,
+ ReadableFontData* data)
+ : Table(header, data) {
+}
+
+/******************************************************************************
+ * HorizontalHeaderTable::Builder class
+ ******************************************************************************/
+HorizontalHeaderTable::Builder::Builder(Header* header, WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+HorizontalHeaderTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+HorizontalHeaderTable::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable*
+ HorizontalHeaderTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new HorizontalHeaderTable(header(), data);
+ return table.Detach();
+}
+
+CALLER_ATTACH HorizontalHeaderTable::Builder*
+ HorizontalHeaderTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<HorizontalHeaderTable::Builder> builder;
+ builder = new HorizontalHeaderTable::Builder(header, data);
+ return builder.Detach();
+}
+
+int32_t HorizontalHeaderTable::Builder::TableVersion() {
+ return InternalReadData()->ReadFixed(Offset::kVersion);
+}
+
+void HorizontalHeaderTable::Builder::SetTableVersion(int32_t version) {
+ InternalWriteData()->WriteFixed(Offset::kVersion, version);
+}
+
+int32_t HorizontalHeaderTable::Builder::Ascender() {
+ return InternalReadData()->ReadShort(Offset::kAscender);
+}
+
+void HorizontalHeaderTable::Builder::SetAscender(int32_t ascender) {
+ InternalWriteData()->WriteShort(Offset::kVersion, ascender);
+}
+
+int32_t HorizontalHeaderTable::Builder::Descender() {
+ return InternalReadData()->ReadShort(Offset::kDescender);
+}
+
+void HorizontalHeaderTable::Builder::SetDescender(int32_t descender) {
+ InternalWriteData()->WriteShort(Offset::kDescender, descender);
+}
+
+int32_t HorizontalHeaderTable::Builder::LineGap() {
+ return InternalReadData()->ReadShort(Offset::kLineGap);
+}
+
+void HorizontalHeaderTable::Builder::SetLineGap(int32_t line_gap) {
+ InternalWriteData()->WriteShort(Offset::kLineGap, line_gap);
+}
+
+int32_t HorizontalHeaderTable::Builder::AdvanceWidthMax() {
+ return InternalReadData()->ReadUShort(Offset::kAdvanceWidthMax);
+}
+
+void HorizontalHeaderTable::Builder::SetAdvanceWidthMax(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kAdvanceWidthMax, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::MinLeftSideBearing() {
+ return InternalReadData()->ReadShort(Offset::kMinLeftSideBearing);
+}
+
+void HorizontalHeaderTable::Builder::SetMinLeftSideBearing(int32_t value) {
+ InternalWriteData()->WriteShort(Offset::kMinLeftSideBearing, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::MinRightSideBearing() {
+ return InternalReadData()->ReadShort(Offset::kMinRightSideBearing);
+}
+
+void HorizontalHeaderTable::Builder::SetMinRightSideBearing(int32_t value) {
+ InternalWriteData()->WriteShort(Offset::kMinRightSideBearing, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::XMaxExtent() {
+ return InternalReadData()->ReadShort(Offset::kXMaxExtent);
+}
+
+void HorizontalHeaderTable::Builder::SetXMaxExtent(int32_t value) {
+ InternalWriteData()->WriteShort(Offset::kXMaxExtent, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::CaretSlopeRise() {
+ return InternalReadData()->ReadUShort(Offset::kCaretSlopeRise);
+}
+
+void HorizontalHeaderTable::Builder::SetCaretSlopeRise(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kCaretSlopeRise, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::CaretSlopeRun() {
+ return InternalReadData()->ReadUShort(Offset::kCaretSlopeRun);
+}
+
+void HorizontalHeaderTable::Builder::SetCaretSlopeRun(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kCaretSlopeRun, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::CaretOffset() {
+ return InternalReadData()->ReadUShort(Offset::kCaretOffset);
+}
+
+void HorizontalHeaderTable::Builder::SetCaretOffset(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kCaretOffset, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::MetricDataFormat() {
+ return InternalReadData()->ReadUShort(Offset::kMetricDataFormat);
+}
+
+void HorizontalHeaderTable::Builder::SetMetricDataFormat(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kMetricDataFormat, value);
+}
+
+int32_t HorizontalHeaderTable::Builder::NumberOfHMetrics() {
+ return InternalReadData()->ReadUShort(Offset::kNumberOfHMetrics);
+}
+
+void HorizontalHeaderTable::Builder::SetNumberOfHMetrics(int32_t value) {
+ InternalWriteData()->WriteUShort(Offset::kNumberOfHMetrics, value);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/horizontal_header_table.h b/sfntly/table/core/horizontal_header_table.h
new file mode 100644
index 0000000..71f30b4
--- /dev/null
+++ b/sfntly/table/core/horizontal_header_table.h
@@ -0,0 +1,111 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_HORIZONTAL_HEADER_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_HEADER_TABLE_H_
+
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// A Horizontal Header table - 'hhea'.
+class HorizontalHeaderTable : public Table,
+ public RefCounted<HorizontalHeaderTable> {
+ public:
+ // Builder for a Horizontal Header table - 'hhea'.
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ int32_t TableVersion();
+ void SetTableVersion(int32_t version);
+ int32_t Ascender();
+ void SetAscender(int32_t ascender);
+ int32_t Descender();
+ void SetDescender(int32_t descender);
+ int32_t LineGap();
+ void SetLineGap(int32_t line_gap);
+ int32_t AdvanceWidthMax();
+ void SetAdvanceWidthMax(int32_t value);
+ int32_t MinLeftSideBearing();
+ void SetMinLeftSideBearing(int32_t value);
+ int32_t MinRightSideBearing();
+ void SetMinRightSideBearing(int32_t value);
+ int32_t XMaxExtent();
+ void SetXMaxExtent(int32_t value);
+ int32_t CaretSlopeRise();
+ void SetCaretSlopeRise(int32_t value);
+ int32_t CaretSlopeRun();
+ void SetCaretSlopeRun(int32_t value);
+ int32_t CaretOffset();
+ void SetCaretOffset(int32_t value);
+ int32_t MetricDataFormat();
+ void SetMetricDataFormat(int32_t value);
+ int32_t NumberOfHMetrics();
+ void SetNumberOfHMetrics(int32_t value);
+ };
+
+ virtual ~HorizontalHeaderTable();
+ int32_t TableVersion();
+ int32_t Ascender();
+ int32_t Descender();
+ int32_t LineGap();
+ int32_t AdvanceWidthMax();
+ int32_t MinLeftSideBearing();
+ int32_t MinRightSideBearing();
+ int32_t XMaxExtent();
+ int32_t CaretSlopeRise();
+ int32_t CaretSlopeRun();
+ int32_t CaretOffset();
+ int32_t MetricDataFormat();
+ int32_t NumberOfHMetrics();
+
+ private:
+ struct Offset {
+ enum {
+ kVersion = 0,
+ kAscender = 4,
+ kDescender = 6,
+ kLineGap = 8,
+ kAdvanceWidthMax = 10,
+ kMinLeftSideBearing = 12,
+ kMinRightSideBearing = 14,
+ kXMaxExtent = 16,
+ kCaretSlopeRise = 18,
+ kCaretSlopeRun = 20,
+ kCaretOffset = 22,
+ kMetricDataFormat = 32,
+ kNumberOfHMetrics = 34,
+ };
+ };
+
+ HorizontalHeaderTable(Header* header, ReadableFontData* data);
+};
+typedef Ptr<HorizontalHeaderTable> HorizontalHeaderTablePtr;
+typedef Ptr<HorizontalHeaderTable::Builder> HorizontalHeaderTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_HEADER_TABLE_H_
diff --git a/sfntly/table/core/horizontal_metrics_table.cc b/sfntly/table/core/horizontal_metrics_table.cc
new file mode 100644
index 0000000..156387d
--- /dev/null
+++ b/sfntly/table/core/horizontal_metrics_table.cc
@@ -0,0 +1,138 @@
+/*
+ * 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/table/core/horizontal_metrics_table.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+/******************************************************************************
+ * HorizontalMetricsTable class
+ ******************************************************************************/
+HorizontalMetricsTable::~HorizontalMetricsTable() {}
+
+int32_t HorizontalMetricsTable::NumberOfHMetrics() {
+ return num_hmetrics_;
+}
+
+int32_t HorizontalMetricsTable::NumberOfLSBs() {
+ return num_glyphs_ - num_hmetrics_;
+}
+
+int32_t HorizontalMetricsTable::HMetricAdvanceWidth(int32_t entry) {
+ if (entry > num_hmetrics_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ return 0;
+ }
+ int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) +
+ Offset::kHMetricsAdvanceWidth;
+ return data_->ReadUShort(offset);
+}
+
+int32_t HorizontalMetricsTable::HMetricLSB(int32_t entry) {
+ if (entry > num_hmetrics_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ return 0;
+ }
+ int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) +
+ Offset::kHMetricsLeftSideBearing;
+ return data_->ReadShort(offset);
+}
+
+int32_t HorizontalMetricsTable::LsbTableEntry(int32_t entry) {
+ if (entry > num_hmetrics_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ return 0;
+ }
+ int32_t offset = Offset::kHMetricsStart + (entry * Offset::kHMetricsSize) +
+ Offset::kLeftSideBearingSize;
+ return data_->ReadShort(offset);
+}
+
+int32_t HorizontalMetricsTable::AdvanceWidth(int32_t glyph_id) {
+ if (glyph_id < num_hmetrics_) {
+ return HMetricAdvanceWidth(glyph_id);
+ }
+ return HMetricAdvanceWidth(glyph_id - num_hmetrics_);
+}
+
+int32_t HorizontalMetricsTable::LeftSideBearing(int32_t glyph_id) {
+ if (glyph_id < num_hmetrics_) {
+ return HMetricLSB(glyph_id);
+ }
+ return LsbTableEntry(glyph_id - num_hmetrics_);
+}
+
+HorizontalMetricsTable::HorizontalMetricsTable(Header* header,
+ ReadableFontData* data,
+ int32_t num_hmetrics,
+ int32_t num_glyphs)
+ : Table(header, data),
+ num_hmetrics_(num_hmetrics),
+ num_glyphs_(num_glyphs) {
+}
+
+/******************************************************************************
+ * HorizontalMetricsTable::Builder class
+ ******************************************************************************/
+HorizontalMetricsTable::Builder::Builder(Header* header, WritableFontData* data)
+ : TableBasedTableBuilder(header, data), num_hmetrics_(-1), num_glyphs_(-1) {
+}
+
+HorizontalMetricsTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : TableBasedTableBuilder(header, data), num_hmetrics_(-1), num_glyphs_(-1) {
+}
+
+HorizontalMetricsTable::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable*
+ HorizontalMetricsTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table =
+ new HorizontalMetricsTable(header(), data, num_hmetrics_, num_glyphs_);
+ return table.Detach();
+}
+
+CALLER_ATTACH HorizontalMetricsTable::Builder*
+ HorizontalMetricsTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<HorizontalMetricsTable::Builder> builder;
+ builder = new HorizontalMetricsTable::Builder(header, data);
+ return builder.Detach();
+}
+
+void HorizontalMetricsTable::Builder::SetNumberOfHMetrics(
+ int32_t num_hmetrics) {
+ assert(num_hmetrics >= 0);
+ num_hmetrics_ = num_hmetrics;
+ HorizontalMetricsTable* table =
+ down_cast<HorizontalMetricsTable*>(this->GetTable());
+ table->num_hmetrics_ = num_hmetrics;
+}
+
+void HorizontalMetricsTable::Builder::SetNumGlyphs(int32_t num_glyphs) {
+ assert(num_glyphs >= 0);
+ num_glyphs_ = num_glyphs;
+ HorizontalMetricsTable* table =
+ down_cast<HorizontalMetricsTable*>(this->GetTable());
+ table->num_glyphs_ = num_glyphs;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/horizontal_metrics_table.h b/sfntly/table/core/horizontal_metrics_table.h
new file mode 100644
index 0000000..44b51f2
--- /dev/null
+++ b/sfntly/table/core/horizontal_metrics_table.h
@@ -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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_METRICS_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_METRICS_TABLE_H_
+
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// A Horizontal Metrics table - 'hmtx'.
+class HorizontalMetricsTable : public Table,
+ public RefCounted<HorizontalMetricsTable> {
+ public:
+ // Builder for a Horizontal Metrics Table - 'hmtx'.
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ void SetNumberOfHMetrics(int32_t num_hmetrics);
+ void SetNumGlyphs(int32_t num_glyphs);
+
+ private:
+ int32_t num_hmetrics_;
+ int32_t num_glyphs_;
+ };
+
+ virtual ~HorizontalMetricsTable();
+ int32_t NumberOfHMetrics();
+ int32_t NumberOfLSBs();
+ int32_t HMetricAdvanceWidth(int32_t entry);
+ int32_t HMetricLSB(int32_t entry);
+ int32_t LsbTableEntry(int32_t entry);
+ int32_t AdvanceWidth(int32_t glyph_id);
+ int32_t LeftSideBearing(int32_t glyph_id);
+
+ private:
+ struct Offset {
+ enum {
+ // hMetrics
+ kHMetricsStart = 0,
+ kHMetricsSize = 4,
+
+ // Offset within an hMetric
+ kHMetricsAdvanceWidth = 0,
+ kHMetricsLeftSideBearing = 2,
+
+ kLeftSideBearingSize = 2
+ };
+ };
+
+ HorizontalMetricsTable(Header* header,
+ ReadableFontData* data,
+ int32_t num_hmetrics,
+ int32_t num_glyphs);
+
+ int32_t num_hmetrics_;
+ int32_t num_glyphs_;
+};
+typedef Ptr<HorizontalMetricsTable> HorizontalMetricsTablePtr;
+typedef Ptr<HorizontalMetricsTable::Builder> HorizontalMetricsTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_HORIZONTAL_METRICS_TABLE_H_
diff --git a/sfntly/table/core/maximum_profile_table.cc b/sfntly/table/core/maximum_profile_table.cc
new file mode 100644
index 0000000..a8ac3bc
--- /dev/null
+++ b/sfntly/table/core/maximum_profile_table.cc
@@ -0,0 +1,240 @@
+/*
+ * 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/table/core/maximum_profile_table.h"
+
+namespace sfntly {
+/******************************************************************************
+ * MaximumProfileTable class
+ ******************************************************************************/
+MaximumProfileTable::~MaximumProfileTable() {}
+
+int32_t MaximumProfileTable::TableVersion() {
+ return data_->ReadFixed(Offset::kVersion);
+}
+
+int32_t MaximumProfileTable::NumGlyphs() {
+ return data_->ReadUShort(Offset::kNumGlyphs);
+}
+
+int32_t MaximumProfileTable::MaxPoints() {
+ return data_->ReadUShort(Offset::kMaxPoints);
+}
+
+int32_t MaximumProfileTable::MaxContours() {
+ return data_->ReadUShort(Offset::kMaxContours);
+}
+
+int32_t MaximumProfileTable::MaxCompositePoints() {
+ return data_->ReadUShort(Offset::kMaxCompositePoints);
+}
+
+int32_t MaximumProfileTable::MaxCompositeContours() {
+ return data_->ReadUShort(Offset::kMaxCompositeContours);
+}
+
+int32_t MaximumProfileTable::MaxZones() {
+ return data_->ReadUShort(Offset::kMaxZones);
+}
+
+int32_t MaximumProfileTable::MaxTwilightPoints() {
+ return data_->ReadUShort(Offset::kMaxTwilightPoints);
+}
+
+int32_t MaximumProfileTable::MaxStorage() {
+ return data_->ReadUShort(Offset::kMaxStorage);
+}
+
+int32_t MaximumProfileTable::MaxFunctionDefs() {
+ return data_->ReadUShort(Offset::kMaxFunctionDefs);
+}
+
+int32_t MaximumProfileTable::MaxStackElements() {
+ return data_->ReadUShort(Offset::kMaxStackElements);
+}
+
+int32_t MaximumProfileTable::MaxSizeOfInstructions() {
+ return data_->ReadUShort(Offset::kMaxSizeOfInstructions);
+}
+
+int32_t MaximumProfileTable::MaxComponentElements() {
+ return data_->ReadUShort(Offset::kMaxComponentElements);
+}
+
+int32_t MaximumProfileTable::MaxComponentDepth() {
+ return data_->ReadUShort(Offset::kMaxComponentDepth);
+}
+
+MaximumProfileTable::MaximumProfileTable(Header* header,
+ ReadableFontData* data)
+ : Table(header, data) {
+}
+
+/******************************************************************************
+ * MaximumProfileTable::Builder class
+ ******************************************************************************/
+MaximumProfileTable::Builder::Builder(Header* header, WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+MaximumProfileTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+MaximumProfileTable::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable*
+ MaximumProfileTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new MaximumProfileTable(header(), data);
+ return table.Detach();
+}
+
+CALLER_ATTACH MaximumProfileTable::Builder*
+ MaximumProfileTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<MaximumProfileTable::Builder> builder;
+ builder = new MaximumProfileTable::Builder(header, data);
+ return builder.Detach();
+}
+
+int32_t MaximumProfileTable::Builder::TableVersion() {
+ return InternalReadData()->ReadUShort(Offset::kVersion);
+}
+
+void MaximumProfileTable::Builder::SetTableVersion(int32_t version) {
+ InternalWriteData()->WriteUShort(Offset::kVersion, version);
+}
+
+int32_t MaximumProfileTable::Builder::NumGlyphs() {
+ return InternalReadData()->ReadUShort(Offset::kNumGlyphs);
+}
+
+void MaximumProfileTable::Builder::SetNumGlyphs(int32_t num_glyphs) {
+ InternalWriteData()->WriteUShort(Offset::kNumGlyphs, num_glyphs);
+}
+
+int32_t MaximumProfileTable::Builder::MaxPoints() {
+ return InternalReadData()->ReadUShort(Offset::kMaxPoints);
+}
+
+void MaximumProfileTable::Builder::SetMaxPoints(int32_t max_points) {
+ InternalWriteData()->WriteUShort(Offset::kMaxPoints, max_points);
+}
+
+int32_t MaximumProfileTable::Builder::MaxContours() {
+ return InternalReadData()->ReadUShort(Offset::kMaxContours);
+}
+
+void MaximumProfileTable::Builder::SetMaxContours(int32_t max_contours) {
+ InternalWriteData()->WriteUShort(Offset::kMaxContours, max_contours);
+}
+
+int32_t MaximumProfileTable::Builder::MaxCompositePoints() {
+ return InternalReadData()->ReadUShort(Offset::kMaxCompositePoints);
+}
+
+void MaximumProfileTable::Builder::SetMaxCompositePoints(
+ int32_t max_composite_points) {
+ InternalWriteData()->WriteUShort(Offset::kMaxCompositePoints,
+ max_composite_points);
+}
+
+int32_t MaximumProfileTable::Builder::MaxCompositeContours() {
+ return InternalReadData()->ReadUShort(Offset::kMaxCompositeContours);
+}
+
+void MaximumProfileTable::Builder::SetMaxCompositeContours(
+ int32_t max_composite_contours) {
+ InternalWriteData()->WriteUShort(Offset::kMaxCompositeContours,
+ max_composite_contours);
+}
+
+int32_t MaximumProfileTable::Builder::MaxZones() {
+ return InternalReadData()->ReadUShort(Offset::kMaxZones);
+}
+
+void MaximumProfileTable::Builder::SetMaxZones(int32_t max_zones) {
+ InternalWriteData()->WriteUShort(Offset::kMaxZones, max_zones);
+}
+
+int32_t MaximumProfileTable::Builder::MaxTwilightPoints() {
+ return InternalReadData()->ReadUShort(Offset::kMaxTwilightPoints);
+}
+
+void MaximumProfileTable::Builder::SetMaxTwilightPoints(
+ int32_t max_twilight_points) {
+ InternalWriteData()->WriteUShort(Offset::kMaxTwilightPoints,
+ max_twilight_points);
+}
+
+int32_t MaximumProfileTable::Builder::MaxStorage() {
+ return InternalReadData()->ReadUShort(Offset::kMaxStorage);
+}
+
+void MaximumProfileTable::Builder::SetMaxStorage(int32_t max_storage) {
+ InternalWriteData()->WriteUShort(Offset::kMaxStorage, max_storage);
+}
+
+int32_t MaximumProfileTable::Builder::MaxFunctionDefs() {
+ return InternalReadData()->ReadUShort(Offset::kMaxFunctionDefs);
+}
+
+void MaximumProfileTable::Builder::SetMaxFunctionDefs(
+ int32_t max_function_defs) {
+ InternalWriteData()->WriteUShort(Offset::kMaxFunctionDefs, max_function_defs);
+}
+
+int32_t MaximumProfileTable::Builder::MaxStackElements() {
+ return InternalReadData()->ReadUShort(Offset::kMaxStackElements);
+}
+
+void MaximumProfileTable::Builder::SetMaxStackElements(
+ int32_t max_stack_elements) {
+ InternalWriteData()->WriteUShort(Offset::kMaxStackElements,
+ max_stack_elements);
+}
+
+int32_t MaximumProfileTable::Builder::MaxSizeOfInstructions() {
+ return InternalReadData()->ReadUShort(Offset::kMaxSizeOfInstructions);
+}
+
+void MaximumProfileTable::Builder::SetMaxSizeOfInstructions(
+ int32_t max_size_of_instructions) {
+ InternalWriteData()->WriteUShort(Offset::kMaxSizeOfInstructions,
+ max_size_of_instructions);
+}
+
+int32_t MaximumProfileTable::Builder::MaxComponentElements() {
+ return InternalReadData()->ReadUShort(Offset::kMaxComponentElements);
+}
+
+void MaximumProfileTable::Builder::SetMaxComponentElements(
+ int32_t max_component_elements) {
+ InternalWriteData()->WriteUShort(Offset::kMaxComponentElements,
+ max_component_elements);
+}
+
+int32_t MaximumProfileTable::Builder::MaxComponentDepth() {
+ return InternalReadData()->ReadUShort(Offset::kMaxComponentDepth);
+}
+
+void MaximumProfileTable::Builder::SetMaxComponentDepth(
+ int32_t max_component_depth) {
+ InternalWriteData()->WriteUShort(Offset::kMaxComponentDepth,
+ max_component_depth);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/maximum_profile_table.h b/sfntly/table/core/maximum_profile_table.h
new file mode 100644
index 0000000..e7c5abb
--- /dev/null
+++ b/sfntly/table/core/maximum_profile_table.h
@@ -0,0 +1,120 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_MAXIMUM_PROFILE_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_MAXIMUM_PROFILE_TABLE_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// A Maximum Profile table - 'maxp'.
+class MaximumProfileTable : public Table,
+ public RefCounted<MaximumProfileTable> {
+ public:
+ // Builder for a Maximum Profile table - 'maxp'.
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ int32_t TableVersion();
+ void SetTableVersion(int32_t version);
+ int32_t NumGlyphs();
+ void SetNumGlyphs(int32_t num_glyphs);
+ int32_t MaxPoints();
+ void SetMaxPoints(int32_t max_points);
+ int32_t MaxContours();
+ void SetMaxContours(int32_t max_contours);
+ int32_t MaxCompositePoints();
+ void SetMaxCompositePoints(int32_t max_composite_points);
+ int32_t MaxCompositeContours();
+ void SetMaxCompositeContours(int32_t max_composite_contours);
+ int32_t MaxZones();
+ void SetMaxZones(int32_t max_zones);
+ int32_t MaxTwilightPoints();
+ void SetMaxTwilightPoints(int32_t max_twilight_points);
+ int32_t MaxStorage();
+ void SetMaxStorage(int32_t max_storage);
+ int32_t MaxFunctionDefs();
+ void SetMaxFunctionDefs(int32_t max_function_defs);
+ int32_t MaxStackElements();
+ void SetMaxStackElements(int32_t max_stack_elements);
+ int32_t MaxSizeOfInstructions();
+ void SetMaxSizeOfInstructions(int32_t max_size_of_instructions);
+ int32_t MaxComponentElements();
+ void SetMaxComponentElements(int32_t max_component_elements);
+ int32_t MaxComponentDepth();
+ void SetMaxComponentDepth(int32_t max_component_depth);
+ };
+
+ virtual ~MaximumProfileTable();
+ int32_t TableVersion();
+ int32_t NumGlyphs();
+ int32_t MaxPoints();
+ int32_t MaxContours();
+ int32_t MaxCompositePoints();
+ int32_t MaxCompositeContours();
+ int32_t MaxZones();
+ int32_t MaxTwilightPoints();
+ int32_t MaxStorage();
+ int32_t MaxFunctionDefs();
+ int32_t MaxStackElements();
+ int32_t MaxSizeOfInstructions();
+ int32_t MaxComponentElements();
+ int32_t MaxComponentDepth();
+
+ private:
+ struct Offset {
+ enum {
+ // version 0.5 and 1.0
+ kVersion = 0,
+ kNumGlyphs = 4,
+
+ // version 1.0
+ kMaxPoints = 6,
+ kMaxContours = 8,
+ kMaxCompositePoints = 10,
+ kMaxCompositeContours = 12,
+ kMaxZones = 14,
+ kMaxTwilightPoints = 16,
+ kMaxStorage = 18,
+ kMaxFunctionDefs = 20,
+ kMaxInstructionDefs = 22,
+ kMaxStackElements = 24,
+ kMaxSizeOfInstructions = 26,
+ kMaxComponentElements = 28,
+ kMaxComponentDepth = 30,
+ };
+ };
+
+ MaximumProfileTable(Header* header, ReadableFontData* data);
+};
+typedef Ptr<MaximumProfileTable> MaximumProfileTablePtr;
+typedef Ptr<MaximumProfileTable::Builder> MaximumProfileTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_MAXIMUM_PROFILE_TABLE_H_
diff --git a/sfntly/table/core/name_table.cc b/sfntly/table/core/name_table.cc
new file mode 100644
index 0000000..8d2f64f
--- /dev/null
+++ b/sfntly/table/core/name_table.cc
@@ -0,0 +1,723 @@
+/*
+ * 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/table/core/name_table.h"
+
+#include <stdio.h>
+#include <string.h>
+
+#include <unicode/unistr.h>
+
+#include "sfntly/font.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+/******************************************************************************
+ * NameTable::NameEntryId class
+ ******************************************************************************/
+NameTable::NameEntryId::NameEntryId()
+ : platform_id_(0),
+ encoding_id_(0),
+ language_id_(0),
+ name_id_(0) {
+}
+
+NameTable::NameEntryId::NameEntryId(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id)
+ : platform_id_(platform_id),
+ encoding_id_(encoding_id),
+ language_id_(language_id),
+ name_id_(name_id) {
+}
+
+NameTable::NameEntryId::NameEntryId(const NameTable::NameEntryId& rhs) {
+ *this = rhs;
+}
+
+const NameTable::NameEntryId&
+ NameTable::NameEntryId::operator=(const NameTable::NameEntryId& rhs) const {
+ platform_id_ = rhs.platform_id_;
+ encoding_id_ = rhs.encoding_id_;
+ language_id_ = rhs.language_id_;
+ name_id_ = rhs.name_id_;
+ return *this;
+}
+
+bool NameTable::NameEntryId::operator==(const NameEntryId& rhs) const {
+ return platform_id_ == rhs.platform_id_ &&
+ encoding_id_ == rhs.encoding_id_ &&
+ language_id_ == rhs.language_id_ &&
+ name_id_ == rhs.name_id_;
+}
+
+bool NameTable::NameEntryId::operator<(const NameEntryId& rhs) const {
+ if (platform_id_ != rhs.platform_id_) return platform_id_ < rhs.platform_id_;
+ if (encoding_id_ != rhs.encoding_id_) return encoding_id_ < rhs.encoding_id_;
+ if (language_id_ != rhs.language_id_) return language_id_ < rhs.language_id_;
+ return name_id_ < rhs.name_id_;
+}
+
+/******************************************************************************
+ * NameTable::NameEntry class
+ ******************************************************************************/
+NameTable::NameEntry::NameEntry() {
+ Init(0, 0, 0, 0, NULL);
+}
+
+NameTable::NameEntry::NameEntry(const NameEntryId& name_entry_id,
+ const ByteVector& name_bytes) {
+ Init(name_entry_id.platform_id(),
+ name_entry_id.encoding_id(),
+ name_entry_id.language_id(),
+ name_entry_id.name_id(),
+ &name_bytes);
+}
+
+NameTable::NameEntry::NameEntry(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id,
+ const ByteVector& name_bytes) {
+ Init(platform_id, encoding_id, language_id, name_id, &name_bytes);
+}
+
+NameTable::NameEntry::~NameEntry() {}
+
+ByteVector* NameTable::NameEntry::NameAsBytes() {
+ return &name_bytes_;
+}
+
+int32_t NameTable::NameEntry::NameBytesLength() {
+ return name_bytes_.size();
+}
+
+UChar* NameTable::NameEntry::Name() {
+ return NameTable::ConvertFromNameBytes(&name_bytes_,
+ platform_id(),
+ encoding_id());
+}
+
+bool NameTable::NameEntry::operator==(const NameEntry& rhs) const {
+ return (name_entry_id_ == rhs.name_entry_id_ &&
+ name_bytes_ == rhs.name_bytes_);
+}
+
+void NameTable::NameEntry::Init(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id,
+ const ByteVector* name_bytes) {
+ name_entry_id_ = NameEntryId(platform_id, encoding_id, language_id, name_id);
+ if (name_bytes) {
+ name_bytes_ = *name_bytes;
+ } else {
+ name_bytes_.clear();
+ }
+}
+
+/******************************************************************************
+ * NameTable::NameEntryBuilder class
+ ******************************************************************************/
+NameTable::NameEntryBuilder::NameEntryBuilder() {
+ Init(0, 0, 0, 0, NULL);
+}
+
+NameTable::NameEntryBuilder::NameEntryBuilder(const NameEntryId& name_entry_id,
+ const ByteVector& name_bytes) {
+ Init(name_entry_id.platform_id(),
+ name_entry_id.encoding_id(),
+ name_entry_id.language_id(),
+ name_entry_id.name_id(),
+ &name_bytes);
+}
+
+NameTable::NameEntryBuilder::NameEntryBuilder(
+ const NameEntryId& name_entry_id) {
+ Init(name_entry_id.platform_id(),
+ name_entry_id.encoding_id(),
+ name_entry_id.language_id(),
+ name_entry_id.name_id(),
+ NULL);
+}
+
+NameTable::NameEntryBuilder::NameEntryBuilder(NameEntry* b) {
+ Init(b->platform_id(),
+ b->encoding_id(),
+ b->language_id(),
+ b->name_id(),
+ b->NameAsBytes());
+}
+
+NameTable::NameEntryBuilder::~NameEntryBuilder() {}
+
+void NameTable::NameEntryBuilder::SetName(const UChar* name) {
+ if (name == NULL) {
+ name_entry_->name_bytes_.clear();
+ return;
+ }
+ NameTable::ConvertToNameBytes(name,
+ name_entry_->platform_id(),
+ name_entry_->encoding_id(),
+ &name_entry_->name_bytes_);
+}
+
+void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes) {
+ name_entry_->name_bytes_.clear();
+ std::copy(name_bytes.begin(),
+ name_bytes.end(),
+ name_entry_->name_bytes_.begin());
+}
+
+void NameTable::NameEntryBuilder::SetName(const ByteVector& name_bytes,
+ int32_t offset,
+ int32_t length) {
+ name_entry_->name_bytes_.clear();
+ std::copy(name_bytes.begin() + offset,
+ name_bytes.begin() + offset + length,
+ name_entry_->name_bytes_.begin());
+}
+
+void NameTable::NameEntryBuilder::Init(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id,
+ const ByteVector* name_bytes) {
+ name_entry_ = new NameEntry();
+ name_entry_->Init(platform_id, encoding_id, language_id, name_id, name_bytes);
+}
+
+/******************************************************************************
+ * NameTable::NameEntryFilterInPlace class (C++ port only)
+ ******************************************************************************/
+NameTable::NameEntryFilterInPlace::NameEntryFilterInPlace(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id)
+ : platform_id_(platform_id),
+ encoding_id_(encoding_id),
+ language_id_(language_id),
+ name_id_(name_id) {
+}
+
+bool NameTable::NameEntryFilterInPlace::Accept(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ return (platform_id_ == platform_id &&
+ encoding_id_ == encoding_id &&
+ language_id_ == language_id &&
+ name_id_ == name_id);
+}
+
+/******************************************************************************
+ * NameTable::NameEntryIterator class
+ ******************************************************************************/
+NameTable::NameEntryIterator::NameEntryIterator(NameTable* table)
+ : RefIterator<NameEntry, NameTable>(table),
+ name_index_(0),
+ filter_(NULL) {
+}
+
+NameTable::NameEntryIterator::NameEntryIterator(NameTable* table,
+ NameEntryFilter* filter)
+ : RefIterator<NameEntry, NameTable>(table),
+ name_index_(0),
+ filter_(filter) {
+}
+
+bool NameTable::NameEntryIterator::HasNext() {
+ if (!filter_) {
+ if (name_index_ < container()->NameCount()) {
+ return true;
+ }
+ return false;
+ }
+ for (; name_index_ < container()->NameCount(); ++name_index_) {
+ if (filter_->Accept(container()->PlatformId(name_index_),
+ container()->EncodingId(name_index_),
+ container()->LanguageId(name_index_),
+ container()->NameId(name_index_))) {
+ return true;
+ }
+ }
+ return false;
+}
+
+CALLER_ATTACH NameTable::NameEntry* NameTable::NameEntryIterator::Next() {
+ if (!HasNext())
+ return NULL;
+ return container()->GetNameEntry(name_index_++);
+}
+
+/******************************************************************************
+ * NameTable::Builder class
+ ******************************************************************************/
+NameTable::Builder::Builder(Header* header, WritableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+NameTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+CALLER_ATTACH NameTable::Builder*
+ NameTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<NameTable::Builder> builder;
+ builder = new NameTable::Builder(header, data);
+ return builder.Detach();
+}
+
+void NameTable::Builder::RevertNames() {
+ name_entry_map_.clear();
+ set_model_changed(false);
+}
+
+int32_t NameTable::Builder::BuilderCount() {
+ GetNameBuilders(); // Ensure name_entry_map_ is built.
+ return (int32_t)name_entry_map_.size();
+}
+
+bool NameTable::Builder::Has(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ NameEntryId probe(platform_id, encoding_id, language_id, name_id);
+ GetNameBuilders(); // Ensure name_entry_map_ is built.
+ return (name_entry_map_.find(probe) != name_entry_map_.end());
+}
+
+CALLER_ATTACH NameTable::NameEntryBuilder*
+ NameTable::Builder::NameBuilder(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ NameEntryId probe(platform_id, encoding_id, language_id, name_id);
+ NameEntryBuilderMap builders;
+ GetNameBuilders(); // Ensure name_entry_map_ is built.
+ if (name_entry_map_.find(probe) != name_entry_map_.end()) {
+ return name_entry_map_[probe];
+ }
+ NameEntryBuilderPtr builder = new NameEntryBuilder(probe);
+ name_entry_map_[probe] = builder;
+ return builder.Detach();
+}
+
+bool NameTable::Builder::Remove(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ NameEntryId probe(platform_id, encoding_id, language_id, name_id);
+ GetNameBuilders(); // Ensure name_entry_map_ is built.
+ NameEntryBuilderMap::iterator position = name_entry_map_.find(probe);
+ if (position != name_entry_map_.end()) {
+ name_entry_map_.erase(position);
+ return true;
+ }
+ return false;
+}
+
+CALLER_ATTACH FontDataTable*
+ NameTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new NameTable(header(), data);
+ return table.Detach();
+}
+
+void NameTable::Builder::SubDataSet() {
+ name_entry_map_.clear();
+ set_model_changed(false);
+}
+
+int32_t NameTable::Builder::SubDataSizeToSerialize() {
+ if (name_entry_map_.empty()) {
+ return 0;
+ }
+
+ int32_t size = NameTable::Offset::kNameRecordStart +
+ name_entry_map_.size() * NameTable::Offset::kNameRecordSize;
+ for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(),
+ end = name_entry_map_.end();
+ b != end; ++b) {
+ NameEntryBuilderPtr p = b->second;
+ NameEntry* entry = p->name_entry();
+ size += entry->NameBytesLength();
+ }
+ return size;
+}
+
+bool NameTable::Builder::SubReadyToSerialize() {
+ return !name_entry_map_.empty();
+}
+
+int32_t NameTable::Builder::SubSerialize(WritableFontData* new_data) {
+ int32_t string_table_start_offset =
+ NameTable::Offset::kNameRecordStart +
+ name_entry_map_.size() * NameTable::Offset::kNameRecordSize;
+
+ // Header
+ new_data->WriteUShort(NameTable::Offset::kFormat, 0);
+ new_data->WriteUShort(NameTable::Offset::kCount, name_entry_map_.size());
+ new_data->WriteUShort(NameTable::Offset::kStringOffset,
+ string_table_start_offset);
+ int32_t name_record_offset = NameTable::Offset::kNameRecordStart;
+ int32_t string_offset = 0;
+ // Note: we offered operator< in NameEntryId, which will be used by std::less,
+ // and therefore our map will act like TreeMap in Java to provide
+ // sorted key set.
+ for (NameEntryBuilderMap::iterator b = name_entry_map_.begin(),
+ end = name_entry_map_.end();
+ b != end; ++b) {
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordPlatformId,
+ b->first.platform_id());
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordEncodingId,
+ b->first.encoding_id());
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordLanguageId,
+ b->first.language_id());
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordNameId,
+ b->first.name_id());
+ NameEntry* builder_entry = b->second->name_entry();
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordStringLength,
+ builder_entry->NameBytesLength());
+ new_data->WriteUShort(
+ name_record_offset + NameTable::Offset::kNameRecordStringOffset,
+ string_offset);
+ name_record_offset += NameTable::Offset::kNameRecordSize;
+ string_offset += new_data->WriteBytes(
+ string_offset + string_table_start_offset,
+ builder_entry->NameAsBytes());
+ }
+
+ return string_offset + string_table_start_offset;
+}
+
+void NameTable::Builder::Initialize(ReadableFontData* data) {
+ if (data) {
+ NameTablePtr table = new NameTable(header(), data);
+ Ptr<NameEntryIterator> name_iter;
+ name_iter.Attach(table->Iterator());
+ while (name_iter->HasNext()) {
+ NameEntryPtr name_entry;
+ name_entry.Attach(name_iter->Next());
+ NameEntryBuilderPtr name_entry_builder = new NameEntryBuilder(name_entry);
+ NameEntry* builder_entry = name_entry_builder->name_entry();
+ NameEntryId probe = builder_entry->name_entry_id();
+ name_entry_map_[probe] = name_entry_builder;
+ }
+ }
+}
+
+NameTable::NameEntryBuilderMap* NameTable::Builder::GetNameBuilders() {
+ if (name_entry_map_.empty()) {
+ Initialize(InternalReadData());
+ }
+ set_model_changed();
+ return &name_entry_map_;
+}
+
+/******************************************************************************
+ * NameTable class
+ ******************************************************************************/
+NameTable::~NameTable() {}
+
+int32_t NameTable::Format() {
+ return data_->ReadUShort(Offset::kFormat);
+}
+
+int32_t NameTable::NameCount() {
+ return data_->ReadUShort(Offset::kCount);
+}
+
+int32_t NameTable::PlatformId(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordPlatformId +
+ OffsetForNameRecord(index));
+}
+
+int32_t NameTable::EncodingId(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordEncodingId +
+ OffsetForNameRecord(index));
+}
+
+int32_t NameTable::LanguageId(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordLanguageId +
+ OffsetForNameRecord(index));
+}
+
+int32_t NameTable::NameId(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordNameId +
+ OffsetForNameRecord(index));
+}
+
+void NameTable::NameAsBytes(int32_t index, ByteVector* b) {
+ assert(b);
+ int32_t length = NameLength(index);
+ b->clear();
+ b->resize(length);
+ if (length > 0) {
+ data_->ReadBytes(NameOffset(index), &((*b)[0]), 0, length);
+ }
+}
+
+void NameTable::NameAsBytes(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id,
+ ByteVector* b) {
+ assert(b);
+ NameEntryPtr entry;
+ entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id));
+ if (entry) {
+ ByteVector* name = entry->NameAsBytes();
+ std::copy(name->begin(), name->end(), b->begin());
+ }
+}
+
+UChar* NameTable::Name(int32_t index) {
+ ByteVector b;
+ NameAsBytes(index, &b);
+ return ConvertFromNameBytes(&b, PlatformId(index), EncodingId(index));
+}
+
+UChar* NameTable::Name(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ NameEntryPtr entry;
+ entry.Attach(GetNameEntry(platform_id, encoding_id, language_id, name_id));
+ if (entry) {
+ return entry->Name();
+ }
+ return NULL;
+}
+
+CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t index) {
+ ByteVector b;
+ NameAsBytes(index, &b);
+ NameEntryPtr instance = new NameEntry(PlatformId(index),
+ EncodingId(index),
+ LanguageId(index),
+ NameId(index), b);
+ return instance.Detach();
+}
+
+CALLER_ATTACH NameTable::NameEntry* NameTable::GetNameEntry(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) {
+ NameTable::NameEntryFilterInPlace
+ filter(platform_id, encoding_id, language_id, name_id);
+ Ptr<NameTable::NameEntryIterator> name_entry_iter;
+ name_entry_iter.Attach(Iterator(&filter));
+ NameEntryPtr result;
+ if (name_entry_iter->HasNext()) {
+ result = name_entry_iter->Next();
+ }
+ return result;
+}
+
+CALLER_ATTACH NameTable::NameEntryIterator* NameTable::Iterator() {
+ Ptr<NameEntryIterator> output = new NameTable::NameEntryIterator(this);
+ return output.Detach();
+}
+
+CALLER_ATTACH
+NameTable::NameEntryIterator* NameTable::Iterator(NameEntryFilter* filter) {
+ Ptr<NameEntryIterator> output =
+ new NameTable::NameEntryIterator(this, filter);
+ return output.Detach();
+}
+
+NameTable::NameTable(Header* header, ReadableFontData* data)
+ : SubTableContainerTable(header, data) {}
+
+int32_t NameTable::StringOffset() {
+ return data_->ReadUShort(Offset::kStringOffset);
+}
+
+int32_t NameTable::OffsetForNameRecord(int32_t index) {
+ return Offset::kNameRecordStart + index * Offset::kNameRecordSize;
+}
+
+int32_t NameTable::NameLength(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordStringLength +
+ OffsetForNameRecord(index));
+}
+
+int32_t NameTable::NameOffset(int32_t index) {
+ return data_->ReadUShort(Offset::kNameRecordStringOffset +
+ OffsetForNameRecord(index)) + StringOffset();
+}
+
+const char* NameTable::GetEncodingName(int32_t platform_id,
+ int32_t encoding_id) {
+ switch (platform_id) {
+ case PlatformId::kUnicode:
+ return "UTF-16BE";
+ case PlatformId::kMacintosh:
+ switch (encoding_id) {
+ case MacintoshEncodingId::kRoman:
+ return "MacRoman";
+ case MacintoshEncodingId::kJapanese:
+ return "Shift-JIS";
+ case MacintoshEncodingId::kChineseTraditional:
+ return "Big5";
+ case MacintoshEncodingId::kKorean:
+ return "EUC-KR";
+ case MacintoshEncodingId::kArabic:
+ return "MacArabic";
+ case MacintoshEncodingId::kHebrew:
+ return "MacHebrew";
+ case MacintoshEncodingId::kGreek:
+ return "MacGreek";
+ case MacintoshEncodingId::kRussian:
+ return "MacCyrillic";
+ case MacintoshEncodingId::kRSymbol:
+ return "MacSymbol";
+ case MacintoshEncodingId::kThai:
+ return "MacThai";
+ case MacintoshEncodingId::kChineseSimplified:
+ return "EUC-CN";
+ default: // Note: unknown/unconfirmed cases are not ported.
+ break;
+ }
+ break;
+ case PlatformId::kISO:
+ break;
+ case PlatformId::kWindows:
+ switch (encoding_id) {
+ case WindowsEncodingId::kSymbol:
+ case WindowsEncodingId::kUnicodeUCS2:
+ return "UTF-16BE";
+ case WindowsEncodingId::kShiftJIS:
+ return "windows-933";
+ case WindowsEncodingId::kPRC:
+ return "windows-936";
+ case WindowsEncodingId::kBig5:
+ return "windows-950";
+ case WindowsEncodingId::kWansung:
+ return "windows-949";
+ case WindowsEncodingId::kJohab:
+ return "ms1361";
+ case WindowsEncodingId::kUnicodeUCS4:
+ return "UCS-4";
+ }
+ break;
+ case PlatformId::kCustom:
+ break;
+ default:
+ break;
+ }
+ return NULL;
+}
+
+UConverter* NameTable::GetCharset(int32_t platform_id, int32_t encoding_id) {
+ UErrorCode error_code = U_ZERO_ERROR;
+ UConverter* conv = ucnv_open(GetEncodingName(platform_id, encoding_id),
+ &error_code);
+ if (U_SUCCESS(error_code)) {
+ return conv;
+ }
+
+ if (conv) {
+ ucnv_close(conv);
+ }
+ return NULL;
+}
+
+void NameTable::ConvertToNameBytes(const UChar* name,
+ int32_t platform_id,
+ int32_t encoding_id,
+ ByteVector* b) {
+ assert(b);
+ assert(name);
+ b->clear();
+ UConverter* cs = GetCharset(platform_id, encoding_id);
+ if (cs == NULL) {
+ return;
+ }
+
+ // Preflight to get buffer size.
+ UErrorCode error_code = U_ZERO_ERROR;
+ int32_t length = ucnv_fromUChars(cs, NULL, 0, name, -1, &error_code);
+ b->resize(length + 4); // The longest termination "\0" is 4 bytes.
+ memset(&((*b)[0]), 0, length + 4);
+ error_code = U_ZERO_ERROR;
+ ucnv_fromUChars(cs,
+ reinterpret_cast<char*>(&((*b)[0])),
+ length + 4,
+ name,
+ -1,
+ &error_code);
+ if (!U_SUCCESS(error_code)) {
+ b->clear();
+ }
+ ucnv_close(cs);
+}
+
+UChar* NameTable::ConvertFromNameBytes(ByteVector* name_bytes,
+ int32_t platform_id,
+ int32_t encoding_id) {
+ if (name_bytes == NULL || name_bytes->size() == 0) {
+ return NULL;
+ }
+ UConverter* cs = GetCharset(platform_id, encoding_id);
+ UErrorCode error_code = U_ZERO_ERROR;
+ if (cs == NULL) {
+ char buffer[11] = {0};
+#if defined (WIN32)
+ _itoa_s(platform_id, buffer, 16);
+#else
+ snprintf(buffer, sizeof(buffer), "%x", platform_id);
+#endif
+ UChar* result = new UChar[12];
+ memset(result, 0, sizeof(UChar) * 12);
+ cs = ucnv_open("utf-8", &error_code);
+ if (U_SUCCESS(error_code)) {
+ ucnv_toUChars(cs, result, 12, buffer, 11, &error_code);
+ ucnv_close(cs);
+ if (U_SUCCESS(error_code)) {
+ return result;
+ }
+ }
+ delete[] result;
+ return NULL;
+ }
+
+ // No preflight needed here, we will be bigger.
+ UChar* output_buffer = new UChar[name_bytes->size() + 1];
+ memset(output_buffer, 0, sizeof(UChar) * (name_bytes->size() + 1));
+ int32_t length = ucnv_toUChars(cs,
+ output_buffer,
+ name_bytes->size(),
+ reinterpret_cast<char*>(&((*name_bytes)[0])),
+ name_bytes->size(),
+ &error_code);
+ ucnv_close(cs);
+ if (length > 0) {
+ return output_buffer;
+ }
+
+ delete[] output_buffer;
+ return NULL;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/name_table.h b/sfntly/table/core/name_table.h
new file mode 100644
index 0000000..4eaafbb
--- /dev/null
+++ b/sfntly/table/core/name_table.h
@@ -0,0 +1,743 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_NAME_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_NAME_TABLE_H_
+
+// Must include this before ICU to avoid stdint redefinition issue.
+#include "sfntly/port/type.h"
+
+#include <unicode/ucnv.h>
+#include <unicode/ustring.h>
+
+#include <map>
+#include <utility>
+
+#include "sfntly/port/java_iterator.h"
+#include "sfntly/table/subtable_container_table.h"
+
+namespace sfntly {
+
+// The following code implements the name table defined in TTF/OTF spec, which
+// can be found at http://www.microsoft.com/typography/otspec/name.htm.
+
+// Name IDs defined in TTF/OTF spec.
+struct NameId {
+ enum {
+ kUnknown = -1,
+ kCopyrightNotice = 0,
+ kFontFamilyName = 1,
+ kFontSubfamilyName = 2,
+ kUniqueFontIdentifier = 3,
+ kFullFontName = 4,
+ kVersionString = 5,
+ kPostscriptName = 6,
+ kTrademark = 7,
+ kManufacturerName = 8,
+ kDesigner = 9,
+ kDescription = 10,
+ kVendorURL = 11,
+ kDesignerURL = 12,
+ kLicenseDescription = 13,
+ kLicenseInfoURL = 14,
+ kReserved15 = 15,
+ kPreferredFamily = 16,
+ kPreferredSubfamily = 17,
+ kCompatibleFullName = 18,
+ kSampleText = 19,
+ kPostscriptCID = 20,
+ kWWSFamilyName = 21,
+ kWWSSubfamilyName = 22
+ };
+};
+
+// Unicode language IDs used in Name Records.
+struct UnicodeLanguageId {
+ enum {
+ kUnknown = -1,
+ kAll = 0
+ };
+};
+
+// Macintosh Language IDs (platform ID = 1)
+struct MacintoshLanguageId {
+ enum {
+ kUnknown = -1,
+ kEnglish = 0,
+ kFrench = 1,
+ kGerman = 2,
+ kItalian = 3,
+ kDutch = 4,
+ kSwedish = 5,
+ kSpanish = 6,
+ kDanish = 7,
+ kPortuguese = 8,
+ kNorwegian = 9,
+ kHebrew = 10,
+ kJapanese = 11,
+ kArabic = 12,
+ kFinnish = 13,
+ kGreek = 14,
+ kIcelandic = 15,
+ kMaltese = 16,
+ kTurkish = 17,
+ kCroatian = 18,
+ kChinese_Traditional = 19,
+ kUrdu = 20,
+ kHindi = 21,
+ kThai = 22,
+ kKorean = 23,
+ kLithuanian = 24,
+ kPolish = 25,
+ kHungarian = 26,
+ kEstonian = 27,
+ kLatvian = 28,
+ kSami = 29,
+ kFaroese = 30,
+ kFarsiPersian = 31,
+ kRussian = 32,
+ kChinese_Simplified = 33,
+ kFlemish = 34,
+ kIrishGaelic = 35,
+ kAlbanian = 36,
+ kRomanian = 37,
+ kCzech = 38,
+ kSlovak = 39,
+ kSlovenian = 40,
+ kYiddish = 41,
+ kSerbian = 42,
+ kMacedonian = 43,
+ kBulgarian = 44,
+ kUkrainian = 45,
+ kByelorussian = 46,
+ kUzbek = 47,
+ kKazakh = 48,
+ kAzerbaijani_Cyrillic = 49,
+ kAzerbaijani_Arabic = 50,
+ kArmenian = 51,
+ kGeorgian = 52,
+ kMoldavian = 53,
+ kKirghiz = 54,
+ kTajiki = 55,
+ kTurkmen = 56,
+ kMongolian_Mongolian = 57,
+ kMongolian_Cyrillic = 58,
+ kPashto = 59,
+ kKurdish = 60,
+ kKashmiri = 61,
+ kSindhi = 62,
+ kTibetan = 63,
+ kNepali = 64,
+ kSanskrit = 65,
+ kMarathi = 66,
+ kBengali = 67,
+ kAssamese = 68,
+ kGujarati = 69,
+ kPunjabi = 70,
+ kOriya = 71,
+ kMalayalam = 72,
+ kKannada = 73,
+ kTamil = 74,
+ kTelugu = 75,
+ kSinhalese = 76,
+ kBurmese = 77,
+ kKhmer = 78,
+ kLao = 79,
+ kVietnamese = 80,
+ kIndonesian = 81,
+ kTagalong = 82,
+ kMalay_Roman = 83,
+ kMalay_Arabic = 84,
+ kAmharic = 85,
+ kTigrinya = 86,
+ kGalla = 87,
+ kSomali = 88,
+ kSwahili = 89,
+ kKinyarwandaRuanda = 90,
+ kRundi = 91,
+ kNyanjaChewa = 92,
+ kMalagasy = 93,
+ kEsperanto = 94,
+ kWelsh = 128,
+ kBasque = 129,
+ kCatalan = 130,
+ kLatin = 131,
+ kQuenchua = 132,
+ kGuarani = 133,
+ kAymara = 134,
+ kTatar = 135,
+ kUighur = 136,
+ kDzongkha = 137,
+ kJavanese_Roman = 138,
+ kSundanese_Roman = 139,
+ kGalician = 140,
+ kAfrikaans = 141,
+ kBreton = 142,
+ kInuktitut = 143,
+ kScottishGaelic = 144,
+ kManxGaelic = 145,
+ kIrishGaelic_WithDotAbove = 146,
+ kTongan = 147,
+ kGreek_Polytonic = 148,
+ kGreenlandic = 149,
+ kAzerbaijani_Roman = 150
+ };
+};
+
+// Windows Language IDs (platformID = 3)
+struct WindowsLanguageId {
+ enum {
+ kUnknown = -1,
+ kAfrikaans_SouthAfrica = 0x0436,
+ kAlbanian_Albania = 0x041C,
+ kAlsatian_France = 0x0484,
+ kAmharic_Ethiopia = 0x045E,
+ kArabic_Algeria = 0x1401,
+ kArabic_Bahrain = 0x3C01,
+ kArabic_Egypt = 0x0C01,
+ kArabic_Iraq = 0x0801,
+ kArabic_Jordan = 0x2C01,
+ kArabic_Kuwait = 0x3401,
+ kArabic_Lebanon = 0x3001,
+ kArabic_Libya = 0x1001,
+ kArabic_Morocco = 0x1801,
+ kArabic_Oman = 0x2001,
+ kArabic_Qatar = 0x4001,
+ kArabic_SaudiArabia = 0x0401,
+ kArabic_Syria = 0x2801,
+ kArabic_Tunisia = 0x1C01,
+ kArabic_UAE = 0x3801,
+ kArabic_Yemen = 0x2401,
+ kArmenian_Armenia = 0x042B,
+ kAssamese_India = 0x044D,
+ kAzeri_Cyrillic_Azerbaijan = 0x082C,
+ kAzeri_Latin_Azerbaijan = 0x042C,
+ kBashkir_Russia = 0x046D,
+ kBasque_Basque = 0x042D,
+ kBelarusian_Belarus = 0x0423,
+ kBengali_Bangladesh = 0x0845,
+ kBengali_India = 0x0445,
+ kBosnian_Cyrillic_BosniaAndHerzegovina = 0x201A,
+ kBosnian_Latin_BosniaAndHerzegovina = 0x141A,
+ kBreton_France = 0x047E,
+ kBulgarian_Bulgaria = 0x0402,
+ kCatalan_Catalan = 0x0403,
+ kChinese_HongKongSAR = 0x0C04,
+ kChinese_MacaoSAR = 0x1404,
+ kChinese_PeoplesRepublicOfChina = 0x0804,
+ kChinese_Singapore = 0x1004,
+ kChinese_Taiwan = 0x0404,
+ kCorsican_France = 0x0483,
+ kCroatian_Croatia = 0x041A,
+ kCroatian_Latin_BosniaAndHerzegovina = 0x101A,
+ kCzech_CzechRepublic = 0x0405,
+ kDanish_Denmark = 0x0406,
+ kDari_Afghanistan = 0x048C,
+ kDivehi_Maldives = 0x0465,
+ kDutch_Belgium = 0x0813,
+ kDutch_Netherlands = 0x0413,
+ kEnglish_Australia = 0x0C09,
+ kEnglish_Belize = 0x2809,
+ kEnglish_Canada = 0x1009,
+ kEnglish_Caribbean = 0x2409,
+ kEnglish_India = 0x4009,
+ kEnglish_Ireland = 0x1809,
+ kEnglish_Jamaica = 0x2009,
+ kEnglish_Malaysia = 0x4409,
+ kEnglish_NewZealand = 0x1409,
+ kEnglish_RepublicOfThePhilippines = 0x3409,
+ kEnglish_Singapore = 0x4809,
+ kEnglish_SouthAfrica = 0x1C09,
+ kEnglish_TrinidadAndTobago = 0x2C09,
+ kEnglish_UnitedKingdom = 0x0809,
+ kEnglish_UnitedStates = 0x0409,
+ kEnglish_Zimbabwe = 0x3009,
+ kEstonian_Estonia = 0x0425,
+ kFaroese_FaroeIslands = 0x0438,
+ kFilipino_Philippines = 0x0464,
+ kFinnish_Finland = 0x040B,
+ kFrench_Belgium = 0x080C,
+ kFrench_Canada = 0x0C0C,
+ kFrench_France = 0x040C,
+ kFrench_Luxembourg = 0x140c,
+ kFrench_PrincipalityOfMonoco = 0x180C,
+ kFrench_Switzerland = 0x100C,
+ kFrisian_Netherlands = 0x0462,
+ kGalician_Galician = 0x0456,
+ kGeorgian_Georgia = 0x0437,
+ kGerman_Austria = 0x0C07,
+ kGerman_Germany = 0x0407,
+ kGerman_Liechtenstein = 0x1407,
+ kGerman_Luxembourg = 0x1007,
+ kGerman_Switzerland = 0x0807,
+ kGreek_Greece = 0x0408,
+ kGreenlandic_Greenland = 0x046F,
+ kGujarati_India = 0x0447,
+ kHausa_Latin_Nigeria = 0x0468,
+ kHebrew_Israel = 0x040D,
+ kHindi_India = 0x0439,
+ kHungarian_Hungary = 0x040E,
+ kIcelandic_Iceland = 0x040F,
+ kIgbo_Nigeria = 0x0470,
+ kIndonesian_Indonesia = 0x0421,
+ kInuktitut_Canada = 0x045D,
+ kInuktitut_Latin_Canada = 0x085D,
+ kIrish_Ireland = 0x083C,
+ kisiXhosa_SouthAfrica = 0x0434,
+ kisiZulu_SouthAfrica = 0x0435,
+ kItalian_Italy = 0x0410,
+ kItalian_Switzerland = 0x0810,
+ kJapanese_Japan = 0x0411,
+ kKannada_India = 0x044B,
+ kKazakh_Kazakhstan = 0x043F,
+ kKhmer_Cambodia = 0x0453,
+ kKiche_Guatemala = 0x0486,
+ kKinyarwanda_Rwanda = 0x0487,
+ kKiswahili_Kenya = 0x0441,
+ kKonkani_India = 0x0457,
+ kKorean_Korea = 0x0412,
+ kKyrgyz_Kyrgyzstan = 0x0440,
+ kLao_LaoPDR = 0x0454,
+ kLatvian_Latvia = 0x0426,
+ kLithuanian_Lithuania = 0x0427,
+ kLowerSorbian_Germany = 0x082E,
+ kLuxembourgish_Luxembourg = 0x046E,
+ kMacedonian_FYROM_FormerYugoslavRepublicOfMacedonia = 0x042F,
+ kMalay_BruneiDarussalam = 0x083E,
+ kMalay_Malaysia = 0x043E,
+ kMalayalam_India = 0x044C,
+ kMaltese_Malta = 0x043A,
+ kMaori_NewZealand = 0x0481,
+ kMapudungun_Chile = 0x047A,
+ kMarathi_India = 0x044E,
+ kMohawk_Mohawk = 0x047C,
+ kMongolian_Cyrillic_Mongolia = 0x0450,
+ kMongolian_Traditional_PeoplesRepublicOfChina = 0x0850,
+ kNepali_Nepal = 0x0461,
+ kNorwegian_Bokmal_Norway = 0x0414,
+ kNorwegian_Nynorsk_Norway = 0x0814,
+ kOccitan_France = 0x0482,
+ kOriya_India = 0x0448,
+ kPashto_Afghanistan = 0x0463,
+ kPolish_Poland = 0x0415,
+ kPortuguese_Brazil = 0x0416,
+ kPortuguese_Portugal = 0x0816,
+ kPunjabi_India = 0x0446,
+ kQuechua_Bolivia = 0x046B,
+ kQuechua_Ecuador = 0x086B,
+ kQuechua_Peru = 0x0C6B,
+ kRomanian_Romania = 0x0418,
+ kRomansh_Switzerland = 0x0417,
+ kRussian_Russia = 0x0419,
+ kSami_Inari_Finland = 0x243B,
+ kSami_Lule_Norway = 0x103B,
+ kSami_Lule_Sweden = 0x143B,
+ kSami_Northern_Finland = 0x0C3B,
+ kSami_Northern_Norway = 0x043B,
+ kSami_Northern_Sweden = 0x083B,
+ kSami_Skolt_Finland = 0x203B,
+ kSami_Southern_Norway = 0x183B,
+ kSami_Southern_Sweden = 0x1C3B,
+ kSanskrit_India = 0x044F,
+ kSerbian_Cyrillic_BosniaAndHerzegovina = 0x1C1A,
+ kSerbian_Cyrillic_Serbia = 0x0C1A,
+ kSerbian_Latin_BosniaAndHerzegovina = 0x181A,
+ kSerbian_Latin_Serbia = 0x081A,
+ kSesothoSaLeboa_SouthAfrica = 0x046C,
+ kSetswana_SouthAfrica = 0x0432,
+ kSinhala_SriLanka = 0x045B,
+ kSlovak_Slovakia = 0x041B,
+ kSlovenian_Slovenia = 0x0424,
+ kSpanish_Argentina = 0x2C0A,
+ kSpanish_Bolivia = 0x400A,
+ kSpanish_Chile = 0x340A,
+ kSpanish_Colombia = 0x240A,
+ kSpanish_CostaRica = 0x140A,
+ kSpanish_DominicanRepublic = 0x1C0A,
+ kSpanish_Ecuador = 0x300A,
+ kSpanish_ElSalvador = 0x440A,
+ kSpanish_Guatemala = 0x100A,
+ kSpanish_Honduras = 0x480A,
+ kSpanish_Mexico = 0x080A,
+ kSpanish_Nicaragua = 0x4C0A,
+ kSpanish_Panama = 0x180A,
+ kSpanish_Paraguay = 0x3C0A,
+ kSpanish_Peru = 0x280A,
+ kSpanish_PuertoRico = 0x500A,
+ kSpanish_ModernSort_Spain = 0x0C0A,
+ kSpanish_TraditionalSort_Spain = 0x040A,
+ kSpanish_UnitedStates = 0x540A,
+ kSpanish_Uruguay = 0x380A,
+ kSpanish_Venezuela = 0x200A,
+ kSweden_Finland = 0x081D,
+ kSwedish_Sweden = 0x041D,
+ kSyriac_Syria = 0x045A,
+ kTajik_Cyrillic_Tajikistan = 0x0428,
+ kTamazight_Latin_Algeria = 0x085F,
+ kTamil_India = 0x0449,
+ kTatar_Russia = 0x0444,
+ kTelugu_India = 0x044A,
+ kThai_Thailand = 0x041E,
+ kTibetan_PRC = 0x0451,
+ kTurkish_Turkey = 0x041F,
+ kTurkmen_Turkmenistan = 0x0442,
+ kUighur_PRC = 0x0480,
+ kUkrainian_Ukraine = 0x0422,
+ kUpperSorbian_Germany = 0x042E,
+ kUrdu_IslamicRepublicOfPakistan = 0x0420,
+ kUzbek_Cyrillic_Uzbekistan = 0x0843,
+ kUzbek_Latin_Uzbekistan = 0x0443,
+ kVietnamese_Vietnam = 0x042A,
+ kWelsh_UnitedKingdom = 0x0452,
+ kWolof_Senegal = 0x0448,
+ kYakut_Russia = 0x0485,
+ kYi_PRC = 0x0478,
+ kYoruba_Nigeria = 0x046A
+ };
+};
+
+class NameTable : public SubTableContainerTable, public RefCounted<NameTable> {
+ public:
+ // Unique identifier for a given name record.
+ class NameEntryId {
+ public:
+ NameEntryId(); // C++ port only, must provide default constructor.
+ NameEntryId(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+ int32_t name_id);
+ NameEntryId(const NameEntryId&);
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~NameEntryId() {}
+
+ int32_t platform_id() const { return platform_id_; }
+ int32_t encoding_id() const { return encoding_id_; }
+ int32_t language_id() const { return language_id_; }
+ int32_t name_id() const { return name_id_; }
+
+ const NameEntryId& operator=(const NameEntryId& rhs) const;
+ bool operator==(const NameEntryId& rhs) const;
+ bool operator<(const NameEntryId& rhs) const;
+
+ // UNIMPLEMENTED: int hashCode()
+ // String toString()
+
+ private:
+ mutable int32_t platform_id_;
+ mutable int32_t encoding_id_;
+ mutable int32_t language_id_;
+ mutable int32_t name_id_;
+ };
+
+ class NameEntryBuilder;
+
+ // Class to represent a name entry in the name table.
+ class NameEntry : public RefCounted<NameEntry> {
+ public:
+ NameEntry();
+ NameEntry(const NameEntryId& name_entry_id, const ByteVector& name_bytes);
+ NameEntry(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id,
+ const ByteVector& name_bytes);
+ virtual ~NameEntry();
+
+ NameEntryId& name_entry_id() { return name_entry_id_; }
+ int32_t platform_id() const { return name_entry_id_.platform_id(); }
+ int32_t encoding_id() const { return name_entry_id_.encoding_id(); }
+ int32_t language_id() const { return name_entry_id_.language_id(); }
+ int32_t name_id() const { return name_entry_id_.name_id(); }
+
+ // Get the bytes for name. Returned pointer is the address of private
+ // member of this class, do not attempt to delete.
+ ByteVector* NameAsBytes();
+
+ // C++ port only: get the length of NameAsBytes.
+ int32_t NameBytesLength();
+
+ // Returns the name in Unicode as UChar array.
+ // Note: ICU UChar* convention requires caller to delete[] it.
+ UChar* Name();
+ bool operator==(const NameEntry& rhs) const;
+
+ // UNIMPLEMENTED: String toString()
+ // int hashCode()
+
+ private:
+ void Init(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+ int32_t name_id, const ByteVector* name_bytes);
+
+ NameEntryId name_entry_id_;
+ ByteVector name_bytes_;
+
+ friend class NameEntryBuilder;
+ };
+
+ // Builder of a name entry.
+ // C++ port: original Java hierarchy inherits from NameEntry. In C++ port, we
+ // opted not doing so to avoid ref count issues and nasty protected members.
+ class NameEntryBuilder : public RefCounted<NameEntryBuilder> {
+ public:
+ NameEntryBuilder();
+ NameEntryBuilder(const NameEntryId& name_entry_id,
+ const ByteVector& name_bytes);
+ explicit NameEntryBuilder(const NameEntryId& name_entry_id);
+ explicit NameEntryBuilder(NameEntry* entry);
+ virtual ~NameEntryBuilder();
+
+ virtual void SetName(const UChar* name);
+ virtual void SetName(const ByteVector& name_bytes);
+ virtual void SetName(const ByteVector& name_bytes,
+ int32_t offset,
+ int32_t length);
+
+ // C++ port only. CALLER_ATTACH is not added because the lifetime shall be
+ // controlled by this class, therefore the caller shall not increase the ref
+ // count.
+ NameEntry* name_entry() { return name_entry_; }
+
+ private:
+ void Init(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+ int32_t name_id, const ByteVector* name_bytes);
+
+ Ptr<NameEntry> name_entry_;
+ };
+ typedef std::map<NameEntryId, Ptr<NameEntryBuilder> > NameEntryBuilderMap;
+
+ // An interface for a filter to use with the name entry iterator. This allows
+ // name entries to be iterated and only those acceptable to the filter will be
+ // returned.
+ class NameEntryFilter {
+ public:
+ virtual bool Accept(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id) = 0;
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~NameEntryFilter() {}
+ };
+
+ // C++ port only: an in-place filter to mimic Java Iterator's filtering.
+ class NameEntryFilterInPlace : public NameEntryFilter {
+ public:
+ NameEntryFilterInPlace(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id);
+ // Make gcc -Wnon-virtual-dtor happy.
+ virtual ~NameEntryFilterInPlace() {}
+
+ virtual bool Accept(int32_t platform_id,
+ int32_t encoding_id,
+ int32_t language_id,
+ int32_t name_id);
+
+ private:
+ int32_t platform_id_;
+ int32_t encoding_id_;
+ int32_t language_id_;
+ int32_t name_id_;
+ };
+
+ class NameEntryIterator : public RefIterator<NameEntry, NameTable> {
+ public:
+ // If filter is NULL, filter through all tables.
+ explicit NameEntryIterator(NameTable* table);
+ NameEntryIterator(NameTable* table, NameEntryFilter* filter);
+ virtual ~NameEntryIterator() {}
+
+ virtual bool HasNext();
+ virtual CALLER_ATTACH NameEntry* Next();
+
+ private:
+ int32_t name_index_;
+ NameEntryFilter* filter_;
+ };
+
+ // The builder to construct name table for outputting.
+ class Builder : public SubTableContainerTable::Builder,
+ public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public because C++ does not allow base
+ // class to instantiate derived class with protected constructors.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ // Revert the name builders for the name table to the last version that came
+ // from data.
+ void RevertNames();
+
+ // Number of name entry builders contained.
+ int32_t BuilderCount();
+
+ // Note: For C++ port, clear() is not implemented. The clear() function
+ // implies completely remove name entry builders, which is easy in
+ // Java but will take a lot of efforts in C++ to release the builders
+ // nicely and correctly.
+ // TODO(arthurhsu): IMPLEMENT
+ // Clear the name builders for the name table.
+ // void clear();
+
+ // Check the existance of a name entry builder by key.
+ bool Has(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+ int32_t name_id);
+
+ // Get name entry builder by key.
+ CALLER_ATTACH NameEntryBuilder* NameBuilder(int32_t platform_id,
+ int32_t encoding_id, int32_t language_id, int32_t name_id);
+
+ // Remove name entry builder by key.
+ bool Remove(int32_t platform_id, int32_t encoding_id, int32_t language_id,
+ int32_t name_id);
+
+ // FontDataTable::Builder API implementation
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ private:
+ void Initialize(ReadableFontData* data);
+ NameEntryBuilderMap* GetNameBuilders();
+
+ // Note: callers should use the getter funtion provided above to ensure that
+ // this is lazily initialized instead of accessing directly.
+ NameEntryBuilderMap name_entry_map_;
+ };
+
+ /****************************************************************************
+ * public methods of NameTable class
+ ****************************************************************************/
+ virtual ~NameTable();
+
+ // Get the format used in the name table.
+ virtual int32_t Format();
+
+ // Get the number of names in the name table.
+ virtual int32_t NameCount();
+
+ // Get the platform id for the given name record.
+ virtual int32_t PlatformId(int32_t index);
+
+ // Get the encoding id for the given name record.
+ // see MacintoshEncodingId, WindowsEncodingId, UnicodeEncodingId
+ virtual int32_t EncodingId(int32_t index);
+
+ // Get the language id for the given name record.
+ virtual int32_t LanguageId(int32_t index);
+
+ // Get the name id for given name record.
+ virtual int32_t NameId(int32_t index);
+
+ // Get the name as bytes for the specified name. If there is no entry for the
+ // requested name, then empty vector is returned.
+ virtual void NameAsBytes(int32_t index, ByteVector* b);
+ virtual void NameAsBytes(int32_t platform_id, int32_t encoding_id,
+ int32_t language_id, int32_t name_id,
+ ByteVector* b);
+
+ // Get the name as a UChar* for the given name record. If there is no
+ // encoding conversion available for the name record then a best attempt
+ // UChar* will be returned.
+ // Note: ICU UChar* convention requires caller to delete[] it.
+ virtual UChar* Name(int32_t index);
+
+ // Get the name as a UChar* for the specified name. If there is no entry for
+ // the requested name then NULL is returned. If there is no encoding
+ // conversion available for the name then a best attempt UChar* will be
+ // returned.
+ // Note: ICU UChar* convention requires caller to delete[] it.
+ virtual UChar* Name(int32_t platform_id, int32_t encoding_id,
+ int32_t language_id, int32_t name_id);
+
+ // Note: These functions are renamed in C++ port. Their original Java name is
+ // nameEntry().
+ virtual CALLER_ATTACH NameEntry* GetNameEntry(int32_t index);
+ virtual CALLER_ATTACH NameEntry* GetNameEntry(int32_t platform_id,
+ int32_t encoding_id, int32_t language_id, int32_t name_id);
+
+ // Note: Not implemented in C++ port due to complexity and low usage.
+ // virtual void names(std::set<NameEntryPtr>*);
+
+ // Get the iterator to iterate through all name entries.
+ virtual CALLER_ATTACH NameEntryIterator* Iterator();
+ virtual CALLER_ATTACH NameEntryIterator* Iterator(NameEntryFilter* filter);
+
+ private:
+ struct Offset {
+ enum {
+ kFormat = 0,
+ kCount = 2,
+ kStringOffset = 4,
+ kNameRecordStart = 6,
+
+ // Format 1 - offset from the end of the name records
+ kLangTagCount = 0,
+ kLangTagRecord = 2,
+
+ kNameRecordSize = 12,
+ // Name Records
+ kNameRecordPlatformId = 0,
+ kNameRecordEncodingId = 2,
+ kNameRecordLanguageId = 4,
+ kNameRecordNameId = 6,
+ kNameRecordStringLength = 8,
+ kNameRecordStringOffset = 10
+ };
+ };
+
+ // The table shall be constructed using Builder, no direct instantiation.
+ NameTable(Header* header, ReadableFontData* data);
+
+ // Get the offset to the string data in the name table.
+ int32_t StringOffset();
+
+ // Get the offset for the given name record.
+ int32_t OffsetForNameRecord(int32_t index);
+
+ // Get the length of the string data for the given name record.
+ int32_t NameLength(int32_t index);
+
+ // Get the offset of the string data for the given name record.
+ int32_t NameOffset(int32_t index);
+
+ // Note: string literals are returned. Caller shall not attempt to manipulate
+ // the returned pointer.
+ static const char* GetEncodingName(int32_t platform_id, int32_t encoding_id);
+
+ // Note: ICU UConverter* convention requires caller to ucnv_close() it.
+ static UConverter* GetCharset(int32_t platform_id, int32_t encoding_id);
+
+ // Note: Output will be stored in ByteVector* b. Original data in b will be
+ // erased and replaced with converted name bytes.
+ static void ConvertToNameBytes(const UChar* name, int32_t platform_id,
+ int32_t encoding_id, ByteVector* b);
+
+ // Note: ICU UChar* convention requires caller to delete[] it.
+ static UChar* ConvertFromNameBytes(ByteVector* name_bytes,
+ int32_t platform_id, int32_t encoding_id);
+}; // class NameTable
+typedef Ptr<NameTable> NameTablePtr;
+typedef Ptr<NameTable::NameEntry> NameEntryPtr;
+typedef Ptr<NameTable::Builder> NameTableBuilderPtr;
+typedef Ptr<NameTable::NameEntryBuilder> NameEntryBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_NAME_TABLE_H_
diff --git a/sfntly/table/core/os2_table.cc b/sfntly/table/core/os2_table.cc
new file mode 100644
index 0000000..1fef309
--- /dev/null
+++ b/sfntly/table/core/os2_table.cc
@@ -0,0 +1,610 @@
+/*
+ * 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/table/core/os2_table.h"
+
+#include <algorithm>
+
+namespace sfntly {
+/******************************************************************************
+ * Constants
+ ******************************************************************************/
+const int64_t CodePageRange::kLatin1_1252 = (int64_t)1 << 0;
+const int64_t CodePageRange::kLatin2_1250 = (int64_t)1 << (int64_t)1;
+const int64_t CodePageRange::kCyrillic_1251 = (int64_t)1 << 2;
+const int64_t CodePageRange::kGreek_1253 = (int64_t)1 << 3;
+const int64_t CodePageRange::kTurkish_1254 = (int64_t)1 << 4;
+const int64_t CodePageRange::kHebrew_1255 = (int64_t)1 << 5;
+const int64_t CodePageRange::kArabic_1256 = (int64_t)1 << 6;
+const int64_t CodePageRange::kWindowsBaltic_1257 = (int64_t)1 << 7;
+const int64_t CodePageRange::kVietnamese_1258 = (int64_t)1 << 8;
+const int64_t CodePageRange::kAlternateANSI9 = (int64_t)1 << 9;
+const int64_t CodePageRange::kAlternateANSI10 = (int64_t)1 << 10;
+const int64_t CodePageRange::kAlternateANSI11 = (int64_t)1 << 11;
+const int64_t CodePageRange::kAlternateANSI12 = (int64_t)1 << 12;
+const int64_t CodePageRange::kAlternateANSI13 = (int64_t)1 << 13;
+const int64_t CodePageRange::kAlternateANSI14 = (int64_t)1 << 14;
+const int64_t CodePageRange::kAlternateANSI15 = (int64_t)1 << 15;
+const int64_t CodePageRange::kThai_874 = (int64_t)1 << 16;
+const int64_t CodePageRange::kJapanJIS_932 = (int64_t)1 << 17;
+const int64_t CodePageRange::kChineseSimplified_936 = (int64_t)1 << 18;
+const int64_t CodePageRange::kKoreanWansung_949 = (int64_t)1 << 19;
+const int64_t CodePageRange::kChineseTraditional_950 = (int64_t)1 << 20;
+const int64_t CodePageRange::kKoreanJohab_1361 = (int64_t)1 << 21;
+const int64_t CodePageRange::kAlternateANSI22 = (int64_t)1 << 22;
+const int64_t CodePageRange::kAlternateANSI23 = (int64_t)1 << 23;
+const int64_t CodePageRange::kAlternateANSI24 = (int64_t)1 << 24;
+const int64_t CodePageRange::kAlternateANSI25 = (int64_t)1 << 25;
+const int64_t CodePageRange::kAlternateANSI26 = (int64_t)1 << 26;
+const int64_t CodePageRange::kAlternateANSI27 = (int64_t)1 << 27;
+const int64_t CodePageRange::kAlternateANSI28 = (int64_t)1 << 28;
+const int64_t CodePageRange::kMacintoshCharacterSet = (int64_t)1 << 29;
+const int64_t CodePageRange::kOEMCharacterSet = (int64_t)1 << 30;
+const int64_t CodePageRange::kSymbolCharacterSet = (int64_t)1 << 31;
+const int64_t CodePageRange::kReservedForOEM32 = (int64_t)1 << 32;
+const int64_t CodePageRange::kReservedForOEM33 = (int64_t)1 << 33;
+const int64_t CodePageRange::kReservedForOEM34 = (int64_t)1 << 34;
+const int64_t CodePageRange::kReservedForOEM35 = (int64_t)1 << 35;
+const int64_t CodePageRange::kReservedForOEM36 = (int64_t)1 << 36;
+const int64_t CodePageRange::kReservedForOEM37 = (int64_t)1 << 37;
+const int64_t CodePageRange::kReservedForOEM38 = (int64_t)1 << 38;
+const int64_t CodePageRange::kReservedForOEM39 = (int64_t)1 << 39;
+const int64_t CodePageRange::kReservedForOEM40 = (int64_t)1 << 40;
+const int64_t CodePageRange::kReservedForOEM41 = (int64_t)1 << 41;
+const int64_t CodePageRange::kReservedForOEM42 = (int64_t)1 << 42;
+const int64_t CodePageRange::kReservedForOEM43 = (int64_t)1 << 43;
+const int64_t CodePageRange::kReservedForOEM44 = (int64_t)1 << 44;
+const int64_t CodePageRange::kReservedForOEM45 = (int64_t)1 << 45;
+const int64_t CodePageRange::kReservedForOEM46 = (int64_t)1 << 46;
+const int64_t CodePageRange::kReservedForOEM47 = (int64_t)1 << 47;
+const int64_t CodePageRange::kIBMGreek_869 = (int64_t)1 << 48;
+const int64_t CodePageRange::kMSDOSRussion_866 = (int64_t)1 << 49;
+const int64_t CodePageRange::kMSDOSNordic_865 = (int64_t)1 << 50;
+const int64_t CodePageRange::kArabic_864 = (int64_t)1 << 51;
+const int64_t CodePageRange::kMSDOSCanadianFrench_863 = (int64_t)1 << 52;
+const int64_t CodePageRange::kHebrew_862 = (int64_t)1 << 53;
+const int64_t CodePageRange::kMSDOSIcelandic_861 = (int64_t)1 << 54;
+const int64_t CodePageRange::kMSDOSPortugese_860 = (int64_t)1 << 55;
+const int64_t CodePageRange::kIBMTurkish_857 = (int64_t)1 << 56;
+const int64_t CodePageRange::kIBMCyrillic_855 = (int64_t)1 << 57;
+const int64_t CodePageRange::kLatin2_852 = (int64_t)1 << 58;
+const int64_t CodePageRange::kMSDOSBaltic_775 = (int64_t)1 << 59;
+const int64_t CodePageRange::kGreek_737 = (int64_t)1 << 60;
+const int64_t CodePageRange::kArabic_708 = (int64_t)1 << 61;
+const int64_t CodePageRange::kLatin1_850 = (int64_t)1 << 62;
+const int64_t CodePageRange::kUS_437 = (int64_t)1 << 63;
+
+/******************************************************************************
+ * struct UnicodeRange
+ ******************************************************************************/
+int32_t UnicodeRange::range(int32_t bit) {
+ if (bit < 0 || bit > kLast) {
+ return -1;
+ }
+ return bit;
+}
+
+/******************************************************************************
+ * class OS2Table
+ ******************************************************************************/
+OS2Table::~OS2Table() {}
+
+int32_t OS2Table::TableVersion() {
+ return data_->ReadUShort(Offset::kVersion);
+}
+
+int32_t OS2Table::XAvgCharWidth() {
+ return data_->ReadShort(Offset::kXAvgCharWidth);
+}
+
+int32_t OS2Table::UsWeightClass() {
+ return data_->ReadUShort(Offset::kUsWeightClass);
+}
+
+int32_t OS2Table::UsWidthClass() {
+ return data_->ReadUShort(Offset::kUsWidthClass);
+}
+
+int32_t OS2Table::FsType() {
+ return data_->ReadUShort(Offset::kFsType);
+}
+
+int32_t OS2Table::YSubscriptXSize() {
+ return data_->ReadShort(Offset::kYSubscriptXSize);
+}
+
+int32_t OS2Table::YSubscriptYSize() {
+ return data_->ReadShort(Offset::kYSubscriptYSize);
+}
+
+int32_t OS2Table::YSubscriptXOffset() {
+ return data_->ReadShort(Offset::kYSubscriptXOffset);
+}
+
+int32_t OS2Table::YSubscriptYOffset() {
+ return data_->ReadShort(Offset::kYSubscriptYOffset);
+}
+
+int32_t OS2Table::YSuperscriptXSize() {
+ return data_->ReadShort(Offset::kYSuperscriptXSize);
+}
+
+int32_t OS2Table::YSuperscriptYSize() {
+ return data_->ReadShort(Offset::kYSuperscriptYSize);
+}
+
+int32_t OS2Table::YSuperscriptXOffset() {
+ return data_->ReadShort(Offset::kYSuperscriptXOffset);
+}
+
+int32_t OS2Table::YSuperscriptYOffset() {
+ return data_->ReadShort(Offset::kYSuperscriptYOffset);
+}
+
+int32_t OS2Table::YStrikeoutSize() {
+ return data_->ReadShort(Offset::kYStrikeoutSize);
+}
+
+int32_t OS2Table::YStrikeoutPosition() {
+ return data_->ReadShort(Offset::kYStrikeoutPosition);
+}
+
+int32_t OS2Table::SFamilyClass() {
+ return data_->ReadShort(Offset::kSFamilyClass);
+}
+
+void OS2Table::Panose(ByteVector* value) {
+ assert(value);
+ value->clear();
+ value->resize(10);
+ data_->ReadBytes(Offset::kPanose, &((*value)[0]), 0, 10);
+}
+
+int64_t OS2Table::UlUnicodeRange1() {
+ return data_->ReadULong(Offset::kUlUnicodeRange1);
+}
+
+int64_t OS2Table::UlUnicodeRange2() {
+ return data_->ReadULong(Offset::kUlUnicodeRange2);
+}
+
+int64_t OS2Table::UlUnicodeRange3() {
+ return data_->ReadULong(Offset::kUlUnicodeRange3);
+}
+
+int64_t OS2Table::UlUnicodeRange4() {
+ return data_->ReadULong(Offset::kUlUnicodeRange4);
+}
+
+void OS2Table::AchVendId(ByteVector* b) {
+ assert(b);
+ b->clear();
+ b->resize(4);
+ data_->ReadBytes(Offset::kAchVendId, &((*b)[0]), 0, 4);
+}
+
+int32_t OS2Table::FsSelection() {
+ return data_->ReadUShort(Offset::kFsSelection);
+}
+
+int32_t OS2Table::UsFirstCharIndex() {
+ return data_->ReadUShort(Offset::kUsFirstCharIndex);
+}
+
+int32_t OS2Table::UsLastCharIndex() {
+ return data_->ReadUShort(Offset::kUsLastCharIndex);
+}
+
+int32_t OS2Table::STypoAscender() {
+ return data_->ReadShort(Offset::kSTypoAscender);
+}
+
+int32_t OS2Table::STypoDescender() {
+ return data_->ReadShort(Offset::kSTypoDescender);
+}
+
+int32_t OS2Table::STypoLineGap() {
+ return data_->ReadShort(Offset::kSTypoLineGap);
+}
+
+int32_t OS2Table::UsWinAscent() {
+ return data_->ReadUShort(Offset::kUsWinAscent);
+}
+
+int32_t OS2Table::UsWinDescent() {
+ return data_->ReadUShort(Offset::kUsWinDescent);
+}
+
+int64_t OS2Table::UlCodePageRange1() {
+ return data_->ReadULong(Offset::kUlCodePageRange1);
+}
+
+int64_t OS2Table::UlCodePageRange2() {
+ return data_->ReadULong(Offset::kUlCodePageRange2);
+}
+
+int32_t OS2Table::SxHeight() {
+ return data_->ReadShort(Offset::kSxHeight);
+}
+
+int32_t OS2Table::SCapHeight() {
+ return data_->ReadShort(Offset::kSCapHeight);
+}
+
+int32_t OS2Table::UsDefaultChar() {
+ return data_->ReadUShort(Offset::kUsDefaultChar);
+}
+
+int32_t OS2Table::UsBreakChar() {
+ return data_->ReadUShort(Offset::kUsBreakChar);
+}
+
+int32_t OS2Table::UsMaxContext() {
+ return data_->ReadUShort(Offset::kUsMaxContext);
+}
+
+OS2Table::OS2Table(Header* header, ReadableFontData* data)
+ : Table(header, data) {
+}
+
+/******************************************************************************
+ * class OS2Table::Builder
+ ******************************************************************************/
+OS2Table::Builder::Builder(Header* header, WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+OS2Table::Builder::Builder(Header* header, ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+OS2Table::Builder::~Builder() {}
+
+CALLER_ATTACH FontDataTable* OS2Table::Builder::SubBuildTable(
+ ReadableFontData* data) {
+ FontDataTablePtr table = new OS2Table(header(), data);
+ return table.Detach();
+}
+
+CALLER_ATTACH OS2Table::Builder*
+ OS2Table::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<OS2Table::Builder> builder;
+ builder = new OS2Table::Builder(header, data);
+ return builder.Detach();
+}
+
+int32_t OS2Table::Builder::TableVersion() {
+ return InternalReadData()->ReadUShort(Offset::kVersion);
+}
+
+void OS2Table::Builder::SetTableVersion(int32_t version) {
+ InternalWriteData()->WriteUShort(Offset::kVersion, version);
+}
+
+int32_t OS2Table::Builder::XAvgCharWidth() {
+ return InternalReadData()->ReadShort(Offset::kXAvgCharWidth);
+}
+
+void OS2Table::Builder::SetXAvgCharWidth(int32_t width) {
+ InternalWriteData()->WriteShort(Offset::kXAvgCharWidth, width);
+}
+
+int32_t OS2Table::Builder::UsWeightClass() {
+ return InternalReadData()->ReadUShort(Offset::kUsWeightClass);
+}
+
+void OS2Table::Builder::SetUsWeightClass(int32_t weight) {
+ InternalWriteData()->WriteUShort(Offset::kUsWeightClass, weight);
+}
+
+int32_t OS2Table::Builder::UsWidthClass() {
+ return InternalReadData()->ReadUShort(Offset::kUsWidthClass);
+}
+
+void OS2Table::Builder::SetUsWidthClass(int32_t width) {
+ InternalWriteData()->WriteUShort(Offset::kUsWidthClass, width);
+}
+
+int32_t OS2Table::Builder::FsType() {
+ return InternalReadData()->ReadUShort(Offset::kFsType);
+}
+
+void OS2Table::Builder::SetFsType(int32_t fs_type) {
+ InternalWriteData()->WriteUShort(Offset::kFsType, fs_type);
+}
+
+int32_t OS2Table::Builder::YSubscriptXSize() {
+ return InternalReadData()->ReadShort(Offset::kYSubscriptXSize);
+}
+
+void OS2Table::Builder::SetYSubscriptXSize(int32_t size) {
+ InternalWriteData()->WriteShort(Offset::kYSubscriptXSize, size);
+}
+
+int32_t OS2Table::Builder::YSubscriptYSize() {
+ return InternalReadData()->ReadShort(Offset::kYSubscriptYSize);
+}
+
+void OS2Table::Builder::SetYSubscriptYSize(int32_t size) {
+ InternalWriteData()->WriteShort(Offset::kYSubscriptYSize, size);
+}
+
+int32_t OS2Table::Builder::YSubscriptXOffset() {
+ return InternalReadData()->ReadShort(Offset::kYSubscriptXOffset);
+}
+
+void OS2Table::Builder::SetYSubscriptXOffset(int32_t offset) {
+ InternalWriteData()->WriteShort(Offset::kYSubscriptXOffset, offset);
+}
+
+int32_t OS2Table::Builder::YSubscriptYOffset() {
+ return InternalReadData()->ReadShort(Offset::kYSubscriptYOffset);
+}
+
+void OS2Table::Builder::SetYSubscriptYOffset(int32_t offset) {
+ InternalWriteData()->WriteShort(Offset::kYSubscriptYOffset, offset);
+}
+
+int32_t OS2Table::Builder::YSuperscriptXSize() {
+ return InternalReadData()->ReadShort(Offset::kYSuperscriptXSize);
+}
+
+void OS2Table::Builder::SetYSuperscriptXSize(int32_t size) {
+ InternalWriteData()->WriteShort(Offset::kYSuperscriptXSize, size);
+}
+
+int32_t OS2Table::Builder::YSuperscriptYSize() {
+ return InternalReadData()->ReadShort(Offset::kYSuperscriptYSize);
+}
+
+void OS2Table::Builder::SetYSuperscriptYSize(int32_t size) {
+ InternalWriteData()->WriteShort(Offset::kYSuperscriptYSize, size);
+}
+
+int32_t OS2Table::Builder::YSuperscriptXOffset() {
+ return InternalReadData()->ReadShort(Offset::kYSuperscriptXOffset);
+}
+
+void OS2Table::Builder::SetYSuperscriptXOffset(int32_t offset) {
+ InternalWriteData()->WriteShort(Offset::kYSuperscriptXOffset, offset);
+}
+
+int32_t OS2Table::Builder::YSuperscriptYOffset() {
+ return InternalReadData()->ReadShort(Offset::kYSuperscriptYOffset);
+}
+
+void OS2Table::Builder::SetYSuperscriptYOffset(int32_t offset) {
+ InternalWriteData()->WriteShort(Offset::kYSuperscriptYOffset, offset);
+}
+
+int32_t OS2Table::Builder::YStrikeoutSize() {
+ return InternalReadData()->ReadShort(Offset::kYStrikeoutSize);
+}
+
+void OS2Table::Builder::SetYStrikeoutSize(int32_t size) {
+ InternalWriteData()->WriteShort(Offset::kYStrikeoutSize, size);
+}
+
+int32_t OS2Table::Builder::YStrikeoutPosition() {
+ return InternalReadData()->ReadShort(Offset::kYStrikeoutPosition);
+}
+
+void OS2Table::Builder::SetYStrikeoutPosition(int32_t position) {
+ InternalWriteData()->WriteShort(Offset::kYStrikeoutPosition, position);
+}
+
+int32_t OS2Table::Builder::SFamilyClass() {
+ return InternalReadData()->ReadShort(Offset::kSFamilyClass);
+}
+
+void OS2Table::Builder::SetSFamilyClass(int32_t family) {
+ InternalWriteData()->WriteShort(Offset::kSFamilyClass, family);
+}
+
+void OS2Table::Builder::Panose(ByteVector* value) {
+ assert(value);
+ value->clear();
+ value->resize(Offset::kPanoseLength);
+ InternalReadData()->ReadBytes(Offset::kPanose,
+ &((*value)[0]),
+ 0,
+ Offset::kPanoseLength);
+}
+
+void OS2Table::Builder::SetPanose(ByteVector* panose) {
+ assert(panose);
+ if (panose->size() != Offset::kPanoseLength) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalArgumentException("Panose bytes must be exactly 10 in length");
+#endif
+ return;
+ }
+ InternalWriteData()->WriteBytes(Offset::kPanose, panose);
+}
+
+int64_t OS2Table::Builder::UlUnicodeRange1() {
+ return InternalReadData()->ReadULong(Offset::kUlUnicodeRange1);
+}
+
+void OS2Table::Builder::SetUlUnicodeRange1(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlUnicodeRange1, range);
+}
+
+int64_t OS2Table::Builder::UlUnicodeRange2() {
+ return InternalReadData()->ReadULong(Offset::kUlUnicodeRange2);
+}
+
+void OS2Table::Builder::SetUlUnicodeRange2(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlUnicodeRange2, range);
+}
+
+int64_t OS2Table::Builder::UlUnicodeRange3() {
+ return InternalReadData()->ReadULong(Offset::kUlUnicodeRange3);
+}
+
+void OS2Table::Builder::SetUlUnicodeRange3(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlUnicodeRange3, range);
+}
+
+int64_t OS2Table::Builder::UlUnicodeRange4() {
+ return InternalReadData()->ReadULong(Offset::kUlUnicodeRange4);
+}
+
+void OS2Table::Builder::SetUlUnicodeRange4(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlUnicodeRange4, range);
+}
+
+void OS2Table::Builder::AchVendId(ByteVector* b) {
+ assert(b);
+ b->clear();
+ b->resize(4);
+ InternalReadData()->ReadBytes(Offset::kAchVendId, &((*b)[0]), 0, 4);
+}
+
+void OS2Table::Builder::SetAchVendId(ByteVector* b) {
+ assert(b);
+ assert(b->size());
+ InternalWriteData()->WriteBytesPad(Offset::kAchVendId,
+ b,
+ 0,
+ std::min<size_t>(
+ (size_t)Offset::kAchVendIdLength,
+ b->size()),
+ static_cast<byte_t>(' '));
+}
+
+int32_t OS2Table::Builder::FsSelection() {
+ return InternalReadData()->ReadUShort(Offset::kFsSelection);
+}
+
+void OS2Table::Builder::SetFsSelection(int32_t fs_selection) {
+ InternalWriteData()->WriteUShort(Offset::kFsSelection, fs_selection);
+}
+
+int32_t OS2Table::Builder::UsFirstCharIndex() {
+ return InternalReadData()->ReadUShort(Offset::kUsFirstCharIndex);
+}
+
+void OS2Table::Builder::SetUsFirstCharIndex(int32_t first_index) {
+ InternalWriteData()->WriteUShort(Offset::kUsFirstCharIndex, first_index);
+}
+
+int32_t OS2Table::Builder::UsLastCharIndex() {
+ return InternalReadData()->ReadUShort(Offset::kUsLastCharIndex);
+}
+
+void OS2Table::Builder::SetUsLastCharIndex(int32_t last_index) {
+ InternalWriteData()->WriteUShort(Offset::kUsLastCharIndex, last_index);
+}
+
+int32_t OS2Table::Builder::STypoAscender() {
+ return InternalReadData()->ReadShort(Offset::kSTypoAscender);
+}
+
+void OS2Table::Builder::SetSTypoAscender(int32_t ascender) {
+ InternalWriteData()->WriteShort(Offset::kSTypoAscender, ascender);
+}
+
+int32_t OS2Table::Builder::STypoDescender() {
+ return InternalReadData()->ReadShort(Offset::kSTypoDescender);
+}
+
+void OS2Table::Builder::SetSTypoDescender(int32_t descender) {
+ InternalWriteData()->WriteShort(Offset::kSTypoDescender, descender);
+}
+
+int32_t OS2Table::Builder::STypoLineGap() {
+ return InternalReadData()->ReadShort(Offset::kSTypoLineGap);
+}
+
+void OS2Table::Builder::SetSTypoLineGap(int32_t line_gap) {
+ InternalWriteData()->WriteShort(Offset::kSTypoLineGap, line_gap);
+}
+
+int32_t OS2Table::Builder::UsWinAscent() {
+ return InternalReadData()->ReadUShort(Offset::kUsWinAscent);
+}
+
+void OS2Table::Builder::SetUsWinAscent(int32_t ascent) {
+ InternalWriteData()->WriteUShort(Offset::kUsWinAscent, ascent);
+}
+
+int32_t OS2Table::Builder::UsWinDescent() {
+ return InternalReadData()->ReadUShort(Offset::kUsWinDescent);
+}
+
+void OS2Table::Builder::SetUsWinDescent(int32_t descent) {
+ InternalWriteData()->WriteUShort(Offset::kUsWinDescent, descent);
+}
+
+int64_t OS2Table::Builder::UlCodePageRange1() {
+ return InternalReadData()->ReadULong(Offset::kUlCodePageRange1);
+}
+
+void OS2Table::Builder::SetUlCodePageRange1(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlCodePageRange1, range);
+}
+
+int64_t OS2Table::Builder::UlCodePageRange2() {
+ return InternalReadData()->ReadULong(Offset::kUlCodePageRange2);
+}
+
+void OS2Table::Builder::SetUlCodePageRange2(int64_t range) {
+ InternalWriteData()->WriteULong(Offset::kUlCodePageRange2, range);
+}
+
+int32_t OS2Table::Builder::SxHeight() {
+ return InternalReadData()->ReadShort(Offset::kSxHeight);
+}
+
+void OS2Table::Builder::SetSxHeight(int32_t height) {
+ InternalWriteData()->WriteShort(Offset::kSxHeight, height);
+}
+
+int32_t OS2Table::Builder::SCapHeight() {
+ return InternalReadData()->ReadShort(Offset::kSCapHeight);
+}
+
+void OS2Table::Builder::SetSCapHeight(int32_t height) {
+ InternalWriteData()->WriteShort(Offset::kSCapHeight, height);
+}
+
+int32_t OS2Table::Builder::UsDefaultChar() {
+ return InternalReadData()->ReadUShort(Offset::kUsDefaultChar);
+}
+
+void OS2Table::Builder::SetUsDefaultChar(int32_t default_char) {
+ InternalWriteData()->WriteUShort(Offset::kUsDefaultChar, default_char);
+}
+
+int32_t OS2Table::Builder::UsBreakChar() {
+ return InternalReadData()->ReadUShort(Offset::kUsBreakChar);
+}
+
+void OS2Table::Builder::SetUsBreakChar(int32_t break_char) {
+ InternalWriteData()->WriteUShort(Offset::kUsBreakChar, break_char);
+}
+
+int32_t OS2Table::Builder::UsMaxContext() {
+ return InternalReadData()->ReadUShort(Offset::kUsMaxContext);
+}
+
+void OS2Table::Builder::SetUsMaxContext(int32_t max_context) {
+ InternalWriteData()->WriteUShort(Offset::kUsMaxContext, max_context);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/core/os2_table.h b/sfntly/table/core/os2_table.h
new file mode 100644
index 0000000..00d26d2
--- /dev/null
+++ b/sfntly/table/core/os2_table.h
@@ -0,0 +1,508 @@
+/*
+ * 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_SFNTLY_TABLE_CORE_OS2_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_OS2_TABLE_H_
+
+#include "sfntly/port/refcount.h"
+#include "sfntly/table/table.h"
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+struct WeightClass {
+ enum {
+ kThin = 100,
+ kExtraLight = 200,
+ kUltraLight = 200,
+ kLight = 300,
+ kNormal = 400,
+ kRegular = 400,
+ kMedium = 500,
+ kSemiBold = 600,
+ kDemiBold = 600,
+ kBold = 700,
+ kExtraBold = 800,
+ kUltraBold = 800,
+ kBlack = 900,
+ kHeavy = 900
+ };
+};
+
+struct WidthClass {
+ enum {
+ kUltraCondensed = 1,
+ kExtraCondensed = 2,
+ kCondensed = 3,
+ kSemiCondensed = 4,
+ kMedium = 5,
+ kNormal = 5,
+ kSemiExpanded = 6,
+ kExpanded = 7,
+ kExtraExpanded = 8,
+ kUltraExpanded = 9
+ };
+};
+
+// Flags to indicate the embedding licensing rights for a font.
+struct EmbeddingFlags {
+ enum {
+ kReserved0 = 1 << 0,
+ kRestrictedLicenseEmbedding = 1 << 1,
+ kPreviewAndPrintEmbedding = 1 << 2,
+ kEditableEmbedding = 1 << 3,
+ kReserved4 = 1 << 4,
+ kReserved5 = 1 << 5,
+ kReserved6 = 1 << 6,
+ kReserved7 = 1 << 7,
+ kNoSubsetting = 1 << 8,
+ kBitmapEmbeddingOnly = 1 << 9,
+ kReserved10 = 1 << 10,
+ kReserved11 = 1 << 11,
+ kReserved12 = 1 << 12,
+ kReserved13 = 1 << 13,
+ kReserved14 = 1 << 14,
+ kReserved15 = 1 << 15
+ };
+};
+
+struct UnicodeRange {
+ enum {
+ // Do NOT reorder. This enum relies on the ordering of the data matching the
+ // ordinal numbers of the properties.
+ kBasicLatin,
+ kLatin1Supplement,
+ kLatinExtendedA,
+ kLatinExtendedB,
+ kIPAExtensions,
+ kSpacingModifierLetters,
+ kCombiningDiacriticalMarks,
+ kGreekAndCoptic,
+ kCoptic,
+ kCyrillic,
+ kArmenian,
+ kHebrew,
+ kVai,
+ kArabic,
+ kNKo,
+ kDevanagari,
+ kBengali,
+ kGurmukhi,
+ kGujarati,
+ kOriya,
+ kTamil,
+ kTelugu,
+ kKannada,
+ kMalayalam,
+ kThai,
+ kLao,
+ kGeorgian,
+ kBalinese,
+ kHangulJamo,
+ kLatinExtendedAdditional,
+ kGreekExtended,
+ kGeneralPunctuation,
+ kSuperscriptsAndSubscripts,
+ kCurrencySymbols,
+ kNumberForms,
+ kArrows,
+ kMathematicalOperators,
+ kMiscTechnical,
+ kControlPictures,
+ kOCR,
+ kEnclosedAlphanumerics,
+ kBoxDrawing,
+ kBlockElements,
+ kGeometricShapes,
+ kMiscSymbols,
+ kDingbats,
+ kCJKSymbolsAndPunctuation,
+ kHiragana,
+ kKatakana,
+ kBopomofo,
+ kHangulCompatibilityJamo,
+ kPhagspa,
+ kEnclosedCJKLettersAndMonths,
+ kCJKCompatibility,
+ kHangulSyllables,
+ kNonPlane0,
+ kPhoenician,
+ kCJKUnifiedIdeographs,
+ kPrivateUseAreaPlane0,
+ kCJKStrokes,
+ kAlphabeticPresentationForms,
+ kArabicPresentationFormsA,
+ kCombiningHalfMarks,
+ kVerticalForms,
+ kSmallFormVariants,
+ kArabicPresentationFormsB,
+ kHalfwidthAndFullwidthForms,
+ kSpecials,
+ kTibetan,
+ kSyriac,
+ kThaana,
+ kSinhala,
+ kMyanmar,
+ kEthiopic,
+ kCherokee,
+ kUnifiedCanadianAboriginalSyllabics,
+ kOgham,
+ kRunic,
+ kKhmer,
+ kMongolian,
+ kBraillePatterns,
+ kYiSyllables,
+ kTagalog,
+ kOldItalic,
+ kGothic,
+ kDeseret,
+ kMusicalSymbols,
+ kMathematicalAlphanumericSymbols,
+ kPrivateUsePlane15And16,
+ kVariationSelectors,
+ kTags,
+ kLimbu,
+ kTaiLe,
+ kNewTaiLue,
+ kBuginese,
+ kGlagolitic,
+ kTifnagh,
+ kYijingHexagramSymbols,
+ kSylotiNagari,
+ kLinearB,
+ kAncientGreekNumbers,
+ kUgaritic,
+ kOldPersian,
+ kShavian,
+ kOsmanya,
+ kCypriotSyllabary,
+ kKharoshthi,
+ kTaiXuanJingSymbols,
+ kCuneiform,
+ kCountingRodNumerals,
+ kSudanese,
+ kLepcha,
+ kOlChiki,
+ kSaurashtra,
+ kKayahLi,
+ kRejang,
+ kCharm,
+ kAncientSymbols,
+ kPhaistosDisc,
+ kCarian,
+ kDominoTiles,
+ kReserved123,
+ kReserved124,
+ kReserved125,
+ kReserved126,
+ kReserved127,
+ kLast = kReserved127
+ };
+
+ int32_t range(int32_t bit);
+ // UNIMPLEMENTED: EnumSet<UnicodeRange> asSet(long range1, long range2,
+ // long range3, long range4)
+ // long[] asArray(EnumSet<UnicodeRange> rangeSet)
+};
+
+struct FsSelection {
+ enum {
+ kITALIC = 1 << 0,
+ kUNDERSCORE = 1 << 1,
+ kNEGATIVE = 1 << 2,
+ kOUTLINED = 1 << 3,
+ kSTRIKEOUT = 1 << 4,
+ kBOLD = 1 << 5,
+ kREGULAR = 1 << 6,
+ kUSE_TYPO_METRICS = 1 << 7,
+ kWWS = 1 << 8,
+ kOBLIQUE = 1 << 9
+ };
+ // UNIMPLEMENTED: EnumSet<FsSelection> asSet(long range1, long range2,
+ // long range3, long range4)
+ // long[] asArray(EnumSet<FsSelection> rangeSet)
+};
+
+// C++ port only: C++ does not support 64-bit enums until C++0x. For better
+// portability, we need to use static const int64_t instead.
+struct CodePageRange {
+ static const int64_t kLatin1_1252;
+ static const int64_t kLatin2_1250;
+ static const int64_t kCyrillic_1251;
+ static const int64_t kGreek_1253;
+ static const int64_t kTurkish_1254;
+ static const int64_t kHebrew_1255;
+ static const int64_t kArabic_1256;
+ static const int64_t kWindowsBaltic_1257;
+ static const int64_t kVietnamese_1258;
+ static const int64_t kAlternateANSI9;
+ static const int64_t kAlternateANSI10;
+ static const int64_t kAlternateANSI11;
+ static const int64_t kAlternateANSI12;
+ static const int64_t kAlternateANSI13;
+ static const int64_t kAlternateANSI14;
+ static const int64_t kAlternateANSI15;
+ static const int64_t kThai_874;
+ static const int64_t kJapanJIS_932;
+ static const int64_t kChineseSimplified_936;
+ static const int64_t kKoreanWansung_949;
+ static const int64_t kChineseTraditional_950;
+ static const int64_t kKoreanJohab_1361;
+ static const int64_t kAlternateANSI22;
+ static const int64_t kAlternateANSI23;
+ static const int64_t kAlternateANSI24;
+ static const int64_t kAlternateANSI25;
+ static const int64_t kAlternateANSI26;
+ static const int64_t kAlternateANSI27;
+ static const int64_t kAlternateANSI28;
+ static const int64_t kMacintoshCharacterSet;
+ static const int64_t kOEMCharacterSet;
+ static const int64_t kSymbolCharacterSet;
+ static const int64_t kReservedForOEM32;
+ static const int64_t kReservedForOEM33;
+ static const int64_t kReservedForOEM34;
+ static const int64_t kReservedForOEM35;
+ static const int64_t kReservedForOEM36;
+ static const int64_t kReservedForOEM37;
+ static const int64_t kReservedForOEM38;
+ static const int64_t kReservedForOEM39;
+ static const int64_t kReservedForOEM40;
+ static const int64_t kReservedForOEM41;
+ static const int64_t kReservedForOEM42;
+ static const int64_t kReservedForOEM43;
+ static const int64_t kReservedForOEM44;
+ static const int64_t kReservedForOEM45;
+ static const int64_t kReservedForOEM46;
+ static const int64_t kReservedForOEM47;
+ static const int64_t kIBMGreek_869;
+ static const int64_t kMSDOSRussion_866;
+ static const int64_t kMSDOSNordic_865;
+ static const int64_t kArabic_864;
+ static const int64_t kMSDOSCanadianFrench_863;
+ static const int64_t kHebrew_862;
+ static const int64_t kMSDOSIcelandic_861;
+ static const int64_t kMSDOSPortugese_860;
+ static const int64_t kIBMTurkish_857;
+ static const int64_t kIBMCyrillic_855;
+ static const int64_t kLatin2_852;
+ static const int64_t kMSDOSBaltic_775;
+ static const int64_t kGreek_737;
+ static const int64_t kArabic_708;
+ static const int64_t kLatin1_850;
+ static const int64_t kUS_437;
+
+ // UNIMPLEMENTED: EnumSet<CodePageRange> asSet(long range1, long range2,
+ // long range3, long range4)
+ // long[] asArray(EnumSet<CodePageRange> rangeSet)
+};
+
+// An OS/2 table - 'OS/2'.
+class OS2Table : public Table, public RefCounted<OS2Table> {
+ public:
+ // A builder for the OS/2 table = 'OS/2'.
+ class Builder : public TableBasedTableBuilder, public RefCounted<Builder> {
+ public:
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ int32_t TableVersion();
+ void SetTableVersion(int32_t version);
+ int32_t XAvgCharWidth();
+ void SetXAvgCharWidth(int32_t width);
+ int32_t UsWeightClass();
+ void SetUsWeightClass(int32_t weight);
+ int32_t UsWidthClass();
+ void SetUsWidthClass(int32_t width);
+ // UNIMPLEMENTED: EnumSet<EmbeddingFlags> fsType()
+ // void setFsType(EnumSeT<EmbeddingFlags> flagSet)
+ int32_t FsType();
+ void SetFsType(int32_t fs_type);
+ int32_t YSubscriptXSize();
+ void SetYSubscriptXSize(int32_t size);
+ int32_t YSubscriptYSize();
+ void SetYSubscriptYSize(int32_t size);
+ int32_t YSubscriptXOffset();
+ void SetYSubscriptXOffset(int32_t offset);
+ int32_t YSubscriptYOffset();
+ void SetYSubscriptYOffset(int32_t offset);
+ int32_t YSuperscriptXSize();
+ void SetYSuperscriptXSize(int32_t size);
+ int32_t YSuperscriptYSize();
+ void SetYSuperscriptYSize(int32_t size);
+ int32_t YSuperscriptXOffset();
+ void SetYSuperscriptXOffset(int32_t offset);
+ int32_t YSuperscriptYOffset();
+ void SetYSuperscriptYOffset(int32_t offset);
+ int32_t YStrikeoutSize();
+ void SetYStrikeoutSize(int32_t size);
+ int32_t YStrikeoutPosition();
+ void SetYStrikeoutPosition(int32_t position);
+ int32_t SFamilyClass();
+ void SetSFamilyClass(int32_t family);
+ void Panose(ByteVector* value);
+ void SetPanose(ByteVector* panose);
+ int64_t UlUnicodeRange1();
+ void SetUlUnicodeRange1(int64_t range);
+ int64_t UlUnicodeRange2();
+ void SetUlUnicodeRange2(int64_t range);
+ int64_t UlUnicodeRange3();
+ void SetUlUnicodeRange3(int64_t range);
+ int64_t UlUnicodeRange4();
+ void SetUlUnicodeRange4(int64_t range);
+ // UNIMPLEMENTED: EnumSet<UnicodeRange> UlUnicodeRange()
+ // setUlUnicodeRange(EnumSet<UnicodeRange> rangeSet)
+ void AchVendId(ByteVector* b);
+ // This field is 4 bytes in length and only the first 4 bytes of the byte
+ // array will be written. If the byte array is less than 4 bytes it will be
+ // padded out with space characters (0x20).
+ // @param b ach Vendor Id
+ void SetAchVendId(ByteVector* b);
+ // UNIMPLEMENTED: public EnumSet<FsSelection> fsSelection()
+ int32_t FsSelection();
+ void SetFsSelection(int32_t fs_selection);
+ int32_t UsFirstCharIndex();
+ void SetUsFirstCharIndex(int32_t first_index);
+ int32_t UsLastCharIndex();
+ void SetUsLastCharIndex(int32_t last_index);
+ int32_t STypoAscender();
+ void SetSTypoAscender(int32_t ascender);
+ int32_t STypoDescender();
+ void SetSTypoDescender(int32_t descender);
+ int32_t STypoLineGap();
+ void SetSTypoLineGap(int32_t line_gap);
+ int32_t UsWinAscent();
+ void SetUsWinAscent(int32_t ascent);
+ int32_t UsWinDescent();
+ void SetUsWinDescent(int32_t descent);
+ int64_t UlCodePageRange1();
+ void SetUlCodePageRange1(int64_t range);
+ int64_t UlCodePageRange2();
+ void SetUlCodePageRange2(int64_t range);
+ // UNIMPLEMENTED: EnumSet<CodePageRange> ulCodePageRange()
+ // void setUlCodePageRange(EnumSet<CodePageRange> rangeSet)
+ int32_t SxHeight();
+ void SetSxHeight(int32_t height);
+ int32_t SCapHeight();
+ void SetSCapHeight(int32_t height);
+ int32_t UsDefaultChar();
+ void SetUsDefaultChar(int32_t default_char);
+ int32_t UsBreakChar();
+ void SetUsBreakChar(int32_t break_char);
+ int32_t UsMaxContext();
+ void SetUsMaxContext(int32_t max_context);
+ };
+
+ ~OS2Table();
+
+ int32_t TableVersion();
+ int32_t XAvgCharWidth();
+ int32_t UsWeightClass();
+ int32_t UsWidthClass();
+ // UNIMPLEMENTED: public EnumSet<EmbeddingFlags> fsType()
+ int32_t FsType();
+ int32_t YSubscriptXSize();
+ int32_t YSubscriptYSize();
+ int32_t YSubscriptXOffset();
+ int32_t YSubscriptYOffset();
+ int32_t YSuperscriptXSize();
+ int32_t YSuperscriptYSize();
+ int32_t YSuperscriptXOffset();
+ int32_t YSuperscriptYOffset();
+ int32_t YStrikeoutSize();
+ int32_t YStrikeoutPosition();
+ int32_t SFamilyClass();
+ void Panose(ByteVector* value);
+ int64_t UlUnicodeRange1();
+ int64_t UlUnicodeRange2();
+ int64_t UlUnicodeRange3();
+ int64_t UlUnicodeRange4();
+ // UNIMPLEMENTED: public EnumSet<UnicodeRange> UlUnicodeRange()
+ void AchVendId(ByteVector* b);
+ // UNIMPLEMENTED: public EnumSet<FsSelection> fsSelection()
+ int32_t FsSelection();
+ int32_t UsFirstCharIndex();
+ int32_t UsLastCharIndex();
+ int32_t STypoAscender();
+ int32_t STypoDescender();
+ int32_t STypoLineGap();
+ int32_t UsWinAscent();
+ int32_t UsWinDescent();
+ int64_t UlCodePageRange1();
+ int64_t UlCodePageRange2();
+ // UNIMPLEMENTED: public EnumSet<CodePageRange> ulCodePageRange()
+ int32_t SxHeight();
+ int32_t SCapHeight();
+ int32_t UsDefaultChar();
+ int32_t UsBreakChar();
+ int32_t UsMaxContext();
+
+ private:
+ struct Offset {
+ enum {
+ kVersion = 0,
+ kXAvgCharWidth = 2,
+ kUsWeightClass = 4,
+ kUsWidthClass = 6,
+ kFsType = 8,
+ kYSubscriptXSize = 10,
+ kYSubscriptYSize = 12,
+ kYSubscriptXOffset = 14,
+ kYSubscriptYOffset = 16,
+ kYSuperscriptXSize = 18,
+ kYSuperscriptYSize = 20,
+ kYSuperscriptXOffset = 22,
+ kYSuperscriptYOffset = 24,
+ kYStrikeoutSize = 26,
+ kYStrikeoutPosition = 28,
+ kSFamilyClass = 30,
+ kPanose = 32,
+ kPanoseLength = 10, // Length of panose bytes.
+ kUlUnicodeRange1 = 42,
+ kUlUnicodeRange2 = 46,
+ kUlUnicodeRange3 = 50,
+ kUlUnicodeRange4 = 54,
+ kAchVendId = 58,
+ kAchVendIdLength = 4, // Length of ach vend id bytes.
+ kFsSelection = 62,
+ kUsFirstCharIndex = 64,
+ kUsLastCharIndex = 66,
+ kSTypoAscender = 68,
+ kSTypoDescender = 70,
+ kSTypoLineGap = 72,
+ kUsWinAscent = 74,
+ kUsWinDescent = 76,
+ kUlCodePageRange1 = 78,
+ kUlCodePageRange2 = 82,
+ kSxHeight = 86,
+ kSCapHeight = 88,
+ kUsDefaultChar = 90,
+ kUsBreakChar = 92,
+ kUsMaxContext = 94
+ };
+ };
+
+ OS2Table(Header* header, ReadableFontData* data);
+};
+typedef Ptr<OS2Table> OS2TablePtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_CORE_OS2_TABLE_H_
diff --git a/sfntly/table/font_data_table.cc b/sfntly/table/font_data_table.cc
new file mode 100644
index 0000000..0e27f7a
--- /dev/null
+++ b/sfntly/table/font_data_table.cc
@@ -0,0 +1,193 @@
+/*
+ * 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/table/font_data_table.h"
+
+#include "sfntly/data/font_output_stream.h"
+
+namespace sfntly {
+
+/******************************************************************************
+ * FontDataTable class
+ ******************************************************************************/
+
+FontDataTable::FontDataTable(ReadableFontData* data) {
+ data_ = data;
+}
+
+FontDataTable::~FontDataTable() {}
+
+ReadableFontData* FontDataTable::ReadFontData() {
+ return data_;
+}
+
+int32_t FontDataTable::DataLength() {
+ return data_->Length();
+}
+
+int32_t FontDataTable::Serialize(OutputStream* os) {
+ return data_->CopyTo(os);
+}
+
+int32_t FontDataTable::Serialize(WritableFontData* data) {
+ return data_->CopyTo(data);
+}
+
+/******************************************************************************
+ * FontDataTable::Builder class
+ ******************************************************************************/
+CALLER_ATTACH WritableFontData* FontDataTable::Builder::Data() {
+ WritableFontDataPtr new_data;
+ if (model_changed_) {
+ if (!SubReadyToSerialize()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("Table not ready to build.");
+#endif
+ return NULL;
+ }
+ int32_t size = SubDataSizeToSerialize();
+ new_data.Attach(WritableFontData::CreateWritableFontData(size));
+ SubSerialize(new_data);
+ } else {
+ ReadableFontDataPtr data = InternalReadData();
+ new_data.Attach(WritableFontData::CreateWritableFontData(
+ data != NULL ? data->Length() : 0));
+ if (data != NULL) {
+ data->CopyTo(new_data);
+ }
+ }
+ return new_data.Detach();
+}
+
+void FontDataTable::Builder::SetData(ReadableFontData* data) {
+ InternalSetData(data, true);
+}
+
+
+CALLER_ATTACH FontDataTable* FontDataTable::Builder::Build() {
+ FontDataTablePtr table; // NULL default table
+ ReadableFontDataPtr data = InternalReadData();
+ if (model_changed_) {
+ // Let subclass serialize from model.
+ if (!SubReadyToSerialize()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IOException("Table not ready to build.");
+#endif
+ return NULL;
+ }
+ int32_t size = SubDataSizeToSerialize();
+ WritableFontDataPtr new_data;
+ new_data.Attach(WritableFontData::CreateWritableFontData(size));
+ SubSerialize(new_data);
+ data = new_data;
+ }
+
+ if (data != NULL) {
+ table = SubBuildTable(data);
+ NotifyPostTableBuild(table);
+ }
+
+ r_data_.Release();
+ w_data_.Release();
+ return table;
+}
+
+bool FontDataTable::Builder::ReadyToBuild() {
+ return true;
+}
+
+ReadableFontData* FontDataTable::Builder::InternalReadData() {
+ return (r_data_ != NULL) ? r_data_.p_ :
+ static_cast<ReadableFontData*>(w_data_.p_);
+}
+
+WritableFontData* FontDataTable::Builder::InternalWriteData() {
+ if (w_data_ == NULL) {
+ WritableFontDataPtr new_data;
+ new_data.Attach(WritableFontData::CreateWritableFontData(
+ r_data_ == NULL ? 0 : r_data_->Length()));
+#if !defined (SFNTLY_NO_EXCEPTION)
+ try {
+#endif
+ if (r_data_) {
+ r_data_->CopyTo(new_data);
+ }
+#if !defined (SFNTLY_NO_EXCEPTION)
+ } catch (IOException& e) {
+ // TODO(stuartg): fix when IOExceptions are cleaned up
+ }
+#endif
+ InternalSetData(new_data, false);
+ }
+ return w_data_.p_;
+}
+
+FontDataTable::Builder::Builder()
+ : model_changed_(false),
+ contained_model_changed_(false),
+ data_changed_(false) {
+}
+
+FontDataTable::Builder::Builder(int32_t data_size)
+ : model_changed_(false),
+ contained_model_changed_(false),
+ data_changed_(false) {
+ w_data_.Attach(WritableFontData::CreateWritableFontData(data_size));
+}
+
+FontDataTable::Builder::Builder(WritableFontData* data)
+ : model_changed_(false),
+ contained_model_changed_(false),
+ data_changed_(false) {
+ w_data_ = data;
+}
+
+FontDataTable::Builder::Builder(ReadableFontData* data)
+ : model_changed_(false),
+ contained_model_changed_(false),
+ data_changed_(false) {
+ r_data_ = data;
+}
+
+FontDataTable::Builder::~Builder() {
+}
+
+void FontDataTable::Builder::NotifyPostTableBuild(FontDataTable* table) {
+ // Default: NOP.
+ UNREFERENCED_PARAMETER(table);
+}
+
+void FontDataTable::Builder::InternalSetData(WritableFontData* data,
+ bool data_changed) {
+ w_data_ = data;
+ r_data_ = NULL;
+ if (data_changed) {
+ data_changed_ = true;
+ SubDataSet();
+ }
+}
+
+void FontDataTable::Builder::InternalSetData(ReadableFontData* data,
+ bool data_changed) {
+ w_data_ = NULL;
+ r_data_ = data;
+ if (data_changed) {
+ data_changed_ = true;
+ SubDataSet();
+ }
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/font_data_table.h b/sfntly/table/font_data_table.h
new file mode 100644
index 0000000..5e437e2
--- /dev/null
+++ b/sfntly/table/font_data_table.h
@@ -0,0 +1,123 @@
+/*
+ * 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_SFNTLY_TABLE_FONT_DATA_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_FONT_DATA_TABLE_H_
+
+#include "sfntly/data/readable_font_data.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/port/refcount.h"
+
+namespace sfntly {
+
+// An abstract base for any table that contains a FontData. This is the root of
+// the table class hierarchy.
+class FontDataTable : virtual public RefCount {
+ public:
+ // Note: original version is abstract Builder<T extends FontDataTable>
+ // C++ template is not designed that way so plain class is chosen.
+ class Builder : virtual public RefCount {
+ public:
+ // Get a snapshot copy of the internal data of the builder.
+ // This causes any internal data structures to be serialized to a new data
+ // object. This data object belongs to the caller and must be properly
+ // disposed of. No changes are made to the builder and any changes to the
+ // data directly do not affect the internal state. To do that a subsequent
+ // call must be made to {@link #SetData(WritableFontData)}.
+ // @return a copy of the internal data of the builder
+ CALLER_ATTACH WritableFontData* Data();
+ virtual void SetData(ReadableFontData* data);
+
+ // Note: changed from protected to avoid accessibility error in C++
+ virtual CALLER_ATTACH FontDataTable* Build();
+ virtual bool ReadyToBuild();
+
+ ReadableFontData* InternalReadData();
+ WritableFontData* InternalWriteData();
+
+ bool data_changed() { return data_changed_; }
+ bool model_changed() {
+ return current_model_changed() || contained_model_changed();
+ }
+ bool current_model_changed() { return model_changed_; }
+ bool contained_model_changed() { return contained_model_changed_; }
+
+ bool set_model_changed() { return set_model_changed(true); }
+ bool set_model_changed(bool changed) {
+ bool old = model_changed_;
+ model_changed_ = changed;
+ return old;
+ }
+
+ protected:
+ explicit Builder();
+
+ // Construct a FontDataTable.Builder with a WritableFontData backing store
+ // of size given. A positive size will create a fixed size backing store and
+ // a 0 or less size is an estimate for a growable backing store with the
+ // estimate being the absolute of the size.
+ // @param dataSize if positive then a fixed size; if 0 or less then an
+ // estimate for a growable size
+ Builder(int32_t data_size);
+ Builder(WritableFontData* data);
+ Builder(ReadableFontData* data);
+ virtual ~Builder();
+
+ // subclass API
+ virtual void NotifyPostTableBuild(FontDataTable* table);
+ virtual int32_t SubSerialize(WritableFontData* new_data) = 0;
+ virtual bool SubReadyToSerialize() = 0;
+ virtual int32_t SubDataSizeToSerialize() = 0;
+ virtual void SubDataSet() = 0;
+ virtual CALLER_ATTACH FontDataTable*
+ SubBuildTable(ReadableFontData* data) = 0;
+
+ private:
+ void InternalSetData(WritableFontData* data, bool data_changed);
+ void InternalSetData(ReadableFontData* data, bool data_changed);
+
+ WritableFontDataPtr w_data_;
+ ReadableFontDataPtr r_data_;
+ bool model_changed_;
+ bool contained_model_changed_; // may expand to list of submodel states
+ bool data_changed_;
+ };
+
+ explicit FontDataTable(ReadableFontData* data);
+ virtual ~FontDataTable();
+
+ // Get the readable font data for this table.
+ ReadableFontData* ReadFontData();
+
+ // Get the length of the data for this table in bytes. This is the full
+ // allocated length of the data underlying the table and may or may not
+ // include any padding.
+ virtual int32_t DataLength();
+
+ virtual int32_t Serialize(OutputStream* os);
+
+ protected:
+ virtual int32_t Serialize(WritableFontData* data);
+
+ // TODO(arthurhsu): style guide violation: protected member, need refactoring
+ ReadableFontDataPtr data_;
+};
+typedef Ptr<FontDataTable> FontDataTablePtr;
+typedef Ptr<FontDataTable::Builder> FontDataTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_FONT_DATA_TABLE_H_
diff --git a/sfntly/table/generic_table_builder.cc b/sfntly/table/generic_table_builder.cc
new file mode 100644
index 0000000..78e6797
--- /dev/null
+++ b/sfntly/table/generic_table_builder.cc
@@ -0,0 +1,49 @@
+/*
+ * 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/table/generic_table_builder.h"
+
+namespace sfntly {
+
+GenericTableBuilder::~GenericTableBuilder() {}
+
+CALLER_ATTACH
+FontDataTable* GenericTableBuilder::SubBuildTable(ReadableFontData* data) {
+ // Note: In C++ port, we use GenericTable, the ref-counted version of Table
+ UNREFERENCED_PARAMETER(data);
+ Ptr<GenericTable> table = new GenericTable(header(), InternalReadData());
+ return table.Detach();
+}
+
+// static
+CALLER_ATTACH GenericTableBuilder*
+ GenericTableBuilder::CreateBuilder(Header* header, WritableFontData* data) {
+ Ptr<GenericTableBuilder> builder =
+ new GenericTableBuilder(header, data);
+ return builder.Detach();
+}
+
+GenericTableBuilder::GenericTableBuilder(Header* header,
+ WritableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+GenericTableBuilder::GenericTableBuilder(Header* header,
+ ReadableFontData* data)
+ : TableBasedTableBuilder(header, data) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/generic_table_builder.h b/sfntly/table/generic_table_builder.h
new file mode 100644
index 0000000..a100ea0
--- /dev/null
+++ b/sfntly/table/generic_table_builder.h
@@ -0,0 +1,42 @@
+/*
+ * 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_SFNTLY_TABLE_GENERIC_TABLE_BUILDER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_GENERIC_TABLE_BUILDER_H_
+
+#include "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+// A table builder to do the minimal table building for an unknown table type.
+class GenericTableBuilder : public TableBasedTableBuilder,
+ public RefCounted<GenericTableBuilder> {
+ public:
+ virtual ~GenericTableBuilder();
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+
+ static CALLER_ATTACH GenericTableBuilder*
+ CreateBuilder(Header* header, WritableFontData* data);
+
+ private:
+ GenericTableBuilder(Header* header, WritableFontData* data);
+ GenericTableBuilder(Header* header, ReadableFontData* data);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_BYTE_ARRAY_TABLE_BUILDER_H_
diff --git a/sfntly/table/header.cc b/sfntly/table/header.cc
new file mode 100644
index 0000000..672ace5
--- /dev/null
+++ b/sfntly/table/header.cc
@@ -0,0 +1,66 @@
+/*
+ * 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/table/header.h"
+
+namespace sfntly {
+
+/******************************************************************************
+ * Header class
+ ******************************************************************************/
+Header::Header(int32_t tag)
+ : tag_(tag),
+ offset_(0),
+ offset_valid_(false),
+ length_(0),
+ length_valid_(false),
+ checksum_(0),
+ checksum_valid_(false) {
+}
+
+Header::Header(int32_t tag, int32_t length)
+ : tag_(tag),
+ offset_(0),
+ offset_valid_(false),
+ length_(length),
+ length_valid_(true),
+ checksum_(0),
+ checksum_valid_(false) {
+}
+
+Header::Header(int32_t tag, int64_t checksum, int32_t offset, int32_t length)
+ : tag_(tag),
+ offset_(offset),
+ offset_valid_(true),
+ length_(length),
+ length_valid_(true),
+ checksum_(checksum),
+ checksum_valid_(true) {
+}
+
+Header::~Header() {}
+
+bool HeaderComparatorByOffset::operator() (const HeaderPtr lhs,
+ const HeaderPtr rhs) {
+ return lhs->offset_ > rhs->offset_;
+}
+
+bool HeaderComparatorByTag::operator() (const HeaderPtr lhs,
+ const HeaderPtr rhs) {
+ return lhs->tag_ > rhs->tag_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/header.h b/sfntly/table/header.h
new file mode 100644
index 0000000..280e556
--- /dev/null
+++ b/sfntly/table/header.h
@@ -0,0 +1,114 @@
+/*
+ * 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_SFNTLY_TABLE_HEADER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_HEADER_H_
+
+#include "sfntly/port/refcount.h"
+
+namespace sfntly {
+
+class Header : public RefCounted<Header> {
+ public:
+ // Make a partial header with only the basic info for an empty new table.
+ explicit Header(int32_t tag);
+
+ // Make a partial header with only the basic info for a new table.
+ Header(int32_t tag, int32_t length);
+
+ // Make a full header as read from an existing font.
+ Header(int32_t tag, int64_t checksum, int32_t offset, int32_t length);
+ virtual ~Header();
+
+ // Get the table tag.
+ int32_t tag() { return tag_; }
+
+ // Get the table offset. The offset is from the start of the font file. This
+ // offset value is what was read from the font file during construction of the
+ // font. It may not be meaningful if the font was maninpulated through the
+ // builders.
+ int32_t offset() { return offset_; }
+
+ // Is the offset in the header valid. The offset will not be valid if the
+ // table was constructed during building and has no physical location in a
+ // font file.
+ bool offset_valid() { return offset_valid_; }
+
+ // Get the length of the table as recorded in the table record header. During
+ // building the header length will reflect the length that was initially read
+ // from the font file. This may not be consistent with the current state of
+ // the data.
+ int32_t length() { return length_; }
+
+ // Is the length in the header valid. The length will not be valid if the
+ // table was constructed during building and has no physical location in a
+ // font file until the table is built from the builder.
+ bool length_valid() { return length_valid_; }
+
+ // Get the checksum for the table as recorded in the table record header.
+ int64_t checksum() { return checksum_; }
+
+ // Is the checksum valid. The checksum will not be valid if the table was
+ // constructed during building and has no physical location in a font file.
+ // Note that this does *NOT* check the validity of the checksum against
+ // the calculated checksum for the table data.
+ bool checksum_valid() { return checksum_valid_; }
+
+ // UNIMPLEMENTED: boolean equals(Object obj)
+ // int hashCode()
+ // string toString()
+
+ private:
+ int32_t tag_;
+ int32_t offset_;
+ bool offset_valid_;
+ int32_t length_;
+ bool length_valid_;
+ int64_t checksum_;
+ bool checksum_valid_;
+
+ friend class HeaderComparatorByOffset;
+ friend class HeaderComparatorByTag;
+};
+typedef Ptr<Header> HeaderPtr;
+
+class HeaderComparator {
+ public:
+ virtual ~HeaderComparator() {}
+ virtual bool operator()(const HeaderPtr h1,
+ const HeaderPtr h2) = 0;
+};
+
+class HeaderComparatorByOffset : public HeaderComparator {
+ public:
+ virtual ~HeaderComparatorByOffset() {}
+ virtual bool operator()(const HeaderPtr h1,
+ const HeaderPtr h2);
+};
+
+class HeaderComparatorByTag : public HeaderComparator {
+ public:
+ virtual ~HeaderComparatorByTag() {}
+ virtual bool operator()(const HeaderPtr h1,
+ const HeaderPtr h2);
+};
+
+typedef std::set<HeaderPtr, HeaderComparatorByOffset> HeaderOffsetSortedSet;
+typedef std::set<HeaderPtr, HeaderComparatorByTag> HeaderTagSortedSet;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_HEADER_H_
diff --git a/sfntly/table/subtable.cc b/sfntly/table/subtable.cc
new file mode 100644
index 0000000..e5b906f
--- /dev/null
+++ b/sfntly/table/subtable.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 "sfntly/table/subtable.h"
+
+namespace sfntly {
+/******************************************************************************
+ * SubTable class
+ ******************************************************************************/
+SubTable::~SubTable() {}
+
+SubTable::SubTable(ReadableFontData* data, ReadableFontData* master_data)
+ : FontDataTable(data), padding_(0) {
+ master_data_ = master_data;
+}
+
+SubTable::SubTable(ReadableFontData* data)
+ : FontDataTable(data), padding_(0) {
+}
+
+/******************************************************************************
+ * SubTable::Builder class
+ ******************************************************************************/
+SubTable::Builder::~Builder() {
+}
+
+SubTable::Builder::Builder(int32_t data_size)
+ : FontDataTable::Builder(data_size) {
+}
+
+SubTable::Builder::Builder(WritableFontData* data,
+ ReadableFontData* master_data)
+ : FontDataTable::Builder(data) {
+ master_data_ = master_data;
+}
+
+SubTable::Builder::Builder(ReadableFontData* data,
+ ReadableFontData* master_data)
+ : FontDataTable::Builder(data) {
+ master_data_ = master_data;
+}
+
+SubTable::Builder::Builder(WritableFontData* data)
+ : FontDataTable::Builder(data) {
+}
+
+SubTable::Builder::Builder(ReadableFontData* data)
+ : FontDataTable::Builder(data) {
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/subtable.h b/sfntly/table/subtable.h
new file mode 100644
index 0000000..fa6f4c6
--- /dev/null
+++ b/sfntly/table/subtable.h
@@ -0,0 +1,73 @@
+/*
+ * 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_SFNTLY_TABLE_SUBTABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_SUBTABLE_H_
+
+#include "sfntly/table/font_data_table.h"
+
+namespace sfntly {
+
+// An abstract base class for subtables. Subtables are smaller tables nested
+// within other tables and don't have an entry in the main font index. Examples
+// of these are the CMap subtables within CMap table (cmap) or a glyph within
+// the glyph table (glyf).
+class SubTable : public FontDataTable {
+ public:
+ class Builder : public FontDataTable::Builder {
+ public:
+ virtual ~Builder();
+
+ protected:
+ // @param data the data for the subtable being built
+ // @param master_data the data for the full table
+ Builder(int32_t data_size);
+ Builder(WritableFontData* data, ReadableFontData* master_data);
+ Builder(ReadableFontData* data, ReadableFontData* master_data);
+ explicit Builder(WritableFontData* data);
+ explicit Builder(ReadableFontData* data);
+
+ ReadableFontData* master_read_data() { return master_data_; }
+
+ private:
+ ReadableFontDataPtr master_data_;
+ };
+
+ virtual ~SubTable();
+ virtual int32_t Padding() { return padding_; }
+
+ // Sets the amount of padding that is part of the data being used by this
+ // subtable.
+ void set_padding(int32_t padding) { padding_ = padding; }
+
+ protected:
+ SubTable(ReadableFontData* data, ReadableFontData* master_data);
+
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit SubTable(ReadableFontData* data);
+
+ ReadableFontData* master_read_data() { return master_data_; }
+
+ private:
+ // The data for the whole table in which this subtable is contained.
+ ReadableFontDataPtr master_data_;
+ int32_t padding_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_SUBTABLE_H_
diff --git a/sfntly/table/subtable_container_table.h b/sfntly/table/subtable_container_table.h
new file mode 100644
index 0000000..0f099de
--- /dev/null
+++ b/sfntly/table/subtable_container_table.h
@@ -0,0 +1,48 @@
+/*
+ * 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_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_
+
+#include "sfntly/table/table.h"
+
+namespace sfntly {
+
+class SubTableContainerTable : public Table {
+ public:
+ class Builder : public Table::Builder {
+ public:
+ Builder(Header* header, WritableFontData* data)
+ : Table::Builder(header, data) {
+ }
+
+ Builder(Header* header, ReadableFontData* data)
+ : Table::Builder(header, data) {
+ }
+
+ virtual ~Builder() {}
+ };
+
+ SubTableContainerTable(Header* header, ReadableFontData* data)
+ : Table(header, data) {
+ }
+
+ virtual ~SubTableContainerTable() {}
+};
+
+} // namespace sfntly
+
+#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_SFNTLY_TABLE_SUBTABLE_CONTAINER_TABLE_H_
diff --git a/sfntly/table/table.cc b/sfntly/table/table.cc
new file mode 100644
index 0000000..cf574b8
--- /dev/null
+++ b/sfntly/table/table.cc
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+
+// type.h needs to be included first because of building issues on Windows
+// Type aliases we delcare are defined in other headers and make the build
+// fail otherwise.
+#include "sfntly/port/type.h"
+#include "sfntly/table/table.h"
+
+#include "sfntly/font.h"
+#include "sfntly/tag.h"
+#include "sfntly/table/bitmap/ebdt_table.h"
+#include "sfntly/table/bitmap/eblc_table.h"
+#include "sfntly/table/bitmap/ebsc_table.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/table/core/horizontal_device_metrics_table.h"
+#include "sfntly/table/core/horizontal_header_table.h"
+#include "sfntly/table/core/horizontal_metrics_table.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "sfntly/table/core/name_table.h"
+#include "sfntly/table/core/os2_table.h"
+#include "sfntly/table/generic_table_builder.h"
+#include "sfntly/table/table_based_table_builder.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+
+namespace sfntly {
+
+/******************************************************************************
+ * Table class
+ ******************************************************************************/
+Table::~Table() {}
+
+int64_t Table::CalculatedChecksum() {
+ return data_->Checksum();
+}
+
+void Table::SetFont(Font* font) {
+ font_ = font;
+}
+
+Table::Table(Header* header, ReadableFontData* data)
+ : FontDataTable(data) {
+ header_ = header;
+}
+
+/******************************************************************************
+ * Table::Builder class
+ ******************************************************************************/
+Table::Builder::~Builder() {
+ header_.Release();
+}
+
+void Table::Builder::NotifyPostTableBuild(FontDataTable* table) {
+ if (model_changed() || data_changed()) {
+ Table* derived_table = down_cast<Table*>(table);
+ derived_table->header_ = new Header(header()->tag(),
+ derived_table->DataLength());
+ }
+}
+
+CALLER_ATTACH
+Table::Builder* Table::Builder::GetBuilder(Header* header,
+ WritableFontData* table_data) {
+ int32_t tag = header->tag();
+ Table::Builder* builder_raw = NULL;
+
+ // Note: Tables are commented out when they are not used/ported.
+ // TODO(arthurhsu): IMPLEMENT: finish tables that are not ported.
+ if (tag == Tag::head) {
+ builder_raw = static_cast<Table::Builder*>(
+ FontHeaderTable::Builder::CreateBuilder(header, table_data));
+#if defined (SFNTLY_EXPERIMENTAL)
+ } else if (tag == Tag::cmap) {
+ builder_raw = static_cast<Table::Builder*>(
+ CMapTable::Builder::CreateBuilder(header, table_data));
+#endif // SFNTLY_EXPERIMENTAL
+ } else if (tag == Tag::hhea) {
+ builder_raw = static_cast<Table::Builder*>(
+ HorizontalHeaderTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::hmtx) {
+ builder_raw = static_cast<Table::Builder*>(
+ HorizontalMetricsTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::maxp) {
+ builder_raw = static_cast<Table::Builder*>(
+ MaximumProfileTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::name) {
+ builder_raw = static_cast<Table::Builder*>(
+ NameTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::OS_2) {
+ builder_raw = static_cast<Table::Builder*>(
+ OS2Table::Builder::CreateBuilder(header, table_data));
+ }/* else if (tag == Tag::PostScript) {
+ builder_raw = static_cast<Table::Builder*>(
+ PostScriptTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::cvt) {
+ builder_raw = static_cast<Table::Builder*>(
+ ControlValueTable::Builder::CreateBuilder(header, table_data));
+ }*/ else if (tag == Tag::glyf) {
+ builder_raw = static_cast<Table::Builder*>(
+ GlyphTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::loca) {
+ builder_raw = static_cast<Table::Builder*>(
+ LocaTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::EBDT || tag == Tag::bdat) {
+ builder_raw = static_cast<Table::Builder*>(
+ EbdtTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::EBLC || tag == Tag::bloc) {
+ builder_raw = static_cast<Table::Builder*>(
+ EblcTable::Builder::CreateBuilder(header, table_data));
+ } else if (tag == Tag::EBSC) {
+ builder_raw = static_cast<Table::Builder*>(
+ EbscTable::Builder::CreateBuilder(header, table_data));
+ } /* else if (tag == Tag::prep) {
+ builder_raw = static_cast<Table::Builder*>(
+ ControlProgramTable::Builder::CreateBuilder(header, table_data));
+ }*/ else if (tag == Tag::bhed) {
+ builder_raw = static_cast<Table::Builder*>(
+ FontHeaderTable::Builder::CreateBuilder(header, table_data));
+#if defined (SFNTLY_EXPERIMENTAL)
+ } else if (tag == Tag::hdmx) {
+ builder_raw = static_cast<Table::Builder*>(
+ HorizontalDeviceMetricsTable::Builder::CreateBuilder(header,
+ table_data));
+#endif // SFNTLY_EXPERIMENTAL
+ } else {
+ builder_raw = static_cast<Table::Builder*>(
+ GenericTableBuilder::CreateBuilder(header, table_data));
+ }
+
+ return builder_raw;
+}
+
+Table::Builder::Builder(Header* header, WritableFontData* data)
+ : FontDataTable::Builder(data) {
+ header_ = header;
+}
+
+Table::Builder::Builder(Header* header, ReadableFontData* data)
+ : FontDataTable::Builder(data) {
+ header_ = header;
+}
+
+Table::Builder::Builder(Header* header) {
+ header_ = header;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/table.h b/sfntly/table/table.h
new file mode 100644
index 0000000..6ebc22d
--- /dev/null
+++ b/sfntly/table/table.h
@@ -0,0 +1,119 @@
+/*
+ * 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_SFNTLY_TABLE_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_H_
+
+#include <set>
+#include <map>
+#include <vector>
+#include <utility>
+
+#include "sfntly/port/type.h"
+#include "sfntly/table/font_data_table.h"
+#include "sfntly/table/header.h"
+
+namespace sfntly {
+class Font;
+
+// A concrete implementation of a root level table in the font. This is the base
+// class used for all specific table implementations and is used as the generic
+// table for all tables which have no specific implementations.
+class Table : public FontDataTable {
+ public:
+ // Note: original version is Builder<T extends Table>
+ // C++ template is not designed that way so plain old inheritance is
+ // chosen.
+ class Builder : public FontDataTable::Builder {
+ public:
+ virtual ~Builder();
+ virtual Header* header() { return header_; }
+ virtual void NotifyPostTableBuild(FontDataTable* table);
+
+ // Get a builder for the table type specified by the data in the header.
+ // @param header the header for the table
+ // @param tableData the data to be used to build the table from
+ // @return builder for the table specified
+ static CALLER_ATTACH Builder* GetBuilder(Header* header,
+ WritableFontData* table_data);
+
+ // UNIMPLEMENTED: toString()
+
+ protected:
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ Builder(Header* header);
+
+ private:
+ Ptr<Header> header_;
+ };
+
+ // Note: GenericTableBuilder moved to table_based_table_builder.h to avoid
+ // circular inclusion.
+
+ virtual ~Table();
+
+ // Get the calculated checksum for the data in the table.
+ virtual int64_t CalculatedChecksum();
+
+ // Get the header for the table.
+ virtual Header* header() { return header_; }
+
+ // Get the tag for the table from the record header.
+ virtual int32_t header_tag() { return header_->tag(); }
+
+ // Get the offset for the table from the record header.
+ virtual int32_t header_offset() { return header_->offset(); }
+
+ // Get the length of the table from the record header.
+ virtual int32_t header_length() { return header_->length(); }
+
+ // Get the checksum for the table from the record header.
+ virtual int64_t header_checksum() { return header_->checksum(); }
+
+ // UNIMPLEMENTED: toString()
+
+ virtual void SetFont(Font* font);
+
+ protected:
+ Table(Header* header, ReadableFontData* data);
+
+ private:
+ Ptr<Header> header_;
+ Ptr<Font> font_;
+};
+
+// C++ port only
+class GenericTable : public Table, public RefCounted<GenericTable> {
+ public:
+ GenericTable(Header* header, ReadableFontData* data) : Table(header, data) {}
+ virtual ~GenericTable() {}
+};
+
+typedef Ptr<Table> TablePtr;
+typedef std::vector<HeaderPtr> TableHeaderList;
+typedef Ptr<Table::Builder> TableBuilderPtr;
+typedef std::map<int32_t, TablePtr> TableMap;
+typedef std::pair<int32_t, TablePtr> TableMapEntry;
+
+typedef std::map<HeaderPtr, WritableFontDataPtr> DataBlockMap;
+typedef std::pair<HeaderPtr, WritableFontDataPtr> DataBlockEntry;
+typedef std::map<int32_t, TableBuilderPtr> TableBuilderMap;
+typedef std::pair<int32_t, TableBuilderPtr> TableBuilderEntry;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_H_
diff --git a/sfntly/table/table_based_table_builder.cc b/sfntly/table/table_based_table_builder.cc
new file mode 100644
index 0000000..b505704
--- /dev/null
+++ b/sfntly/table/table_based_table_builder.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 "sfntly/table/table_based_table_builder.h"
+
+namespace sfntly {
+
+/******************************************************************************
+ * TableBasedTableBuilder class
+ ******************************************************************************/
+TableBasedTableBuilder::~TableBasedTableBuilder() {}
+
+int32_t TableBasedTableBuilder::SubSerialize(WritableFontData* data) {
+ UNREFERENCED_PARAMETER(data);
+ return 0;
+}
+
+bool TableBasedTableBuilder::SubReadyToSerialize() {
+ return false;
+}
+
+int32_t TableBasedTableBuilder::SubDataSizeToSerialize() {
+ return 0;
+}
+
+void TableBasedTableBuilder::SubDataSet() {
+ table_ = NULL;
+}
+
+CALLER_ATTACH FontDataTable* TableBasedTableBuilder::Build() {
+ FontDataTablePtr table = static_cast<FontDataTable*>(GetTable());
+ return table.Detach();
+}
+
+TableBasedTableBuilder::TableBasedTableBuilder(Header* header,
+ WritableFontData* data)
+ : Table::Builder(header, data) {
+}
+
+TableBasedTableBuilder::TableBasedTableBuilder(Header* header,
+ ReadableFontData* data)
+ : Table::Builder(header, data) {
+}
+
+TableBasedTableBuilder::TableBasedTableBuilder(Header* header)
+ : Table::Builder(header) {
+}
+
+Table* TableBasedTableBuilder::GetTable() {
+ if (table_ == NULL) {
+ table_.Attach(down_cast<Table*>(SubBuildTable(InternalReadData())));
+ }
+ return table_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/table_based_table_builder.h b/sfntly/table/table_based_table_builder.h
new file mode 100644
index 0000000..d88eefd
--- /dev/null
+++ b/sfntly/table/table_based_table_builder.h
@@ -0,0 +1,48 @@
+/*
+ * 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_SFNTLY_TABLE_TABLE_BASED_TABLE_BUILDER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_BASED_TABLE_BUILDER_H_
+
+#include "sfntly/table/table.h"
+
+namespace sfntly {
+
+class TableBasedTableBuilder : public Table::Builder {
+ public:
+ virtual ~TableBasedTableBuilder();
+
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual void SubDataSet();
+ virtual CALLER_ATTACH FontDataTable* Build();
+
+ protected:
+ TableBasedTableBuilder(Header* header, WritableFontData* data);
+ TableBasedTableBuilder(Header* header, ReadableFontData* data);
+ explicit TableBasedTableBuilder(Header* header);
+
+ // C++ port: renamed table() to GetTable()
+ virtual Table* GetTable();
+
+ // TODO(arthurhsu): style guide violation: protected member, need refactor
+ TablePtr table_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TABLE_BASED_TABLE_BUILDER_H_
diff --git a/sfntly/table/truetype/glyph_table.cc b/sfntly/table/truetype/glyph_table.cc
new file mode 100644
index 0000000..f38fac5
--- /dev/null
+++ b/sfntly/table/truetype/glyph_table.cc
@@ -0,0 +1,679 @@
+/*
+ * 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/table/truetype/glyph_table.h"
+
+#include <stdlib.h>
+
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+/******************************************************************************
+ * Constants
+ ******************************************************************************/
+const int32_t GlyphTable::SimpleGlyph::kFLAG_ONCURVE = 1;
+const int32_t GlyphTable::SimpleGlyph::kFLAG_XSHORT = 1 << 1;
+const int32_t GlyphTable::SimpleGlyph::kFLAG_YSHORT = 1 << 2;
+const int32_t GlyphTable::SimpleGlyph::kFLAG_REPEAT = 1 << 3;
+const int32_t GlyphTable::SimpleGlyph::kFLAG_XREPEATSIGN = 1 << 4;
+const int32_t GlyphTable::SimpleGlyph::kFLAG_YREPEATSIGN = 1 << 5;
+
+const int32_t GlyphTable::CompositeGlyph::kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_ARGS_ARE_XY_VALUES = 1 << 1;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_ROUND_XY_TO_GRID = 1 << 2;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_A_SCALE = 1 << 3;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_RESERVED = 1 << 4;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_MORE_COMPONENTS = 1 << 5;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_USE_MY_METRICS = 1 << 9;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_OVERLAP_COMPOUND = 1 << 10;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_SCALED_COMPONENT_OFFSET = 1 << 11;
+const int32_t GlyphTable::CompositeGlyph::kFLAG_UNSCALED_COMPONENT_OFFSET = 1 << 12;
+
+/******************************************************************************
+ * GlyphTable class
+ ******************************************************************************/
+GlyphTable::~GlyphTable() {
+}
+
+GlyphTable::Glyph* GlyphTable::GetGlyph(int32_t offset, int32_t length) {
+ return GlyphTable::Glyph::GetGlyph(this, this->data_, offset, length);
+}
+
+GlyphTable::GlyphTable(Header* header, ReadableFontData* data)
+ : SubTableContainerTable(header, data) {
+}
+
+/******************************************************************************
+ * GlyphTable::Builder class
+ ******************************************************************************/
+GlyphTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : SubTableContainerTable::Builder(header, data) {
+}
+
+GlyphTable::Builder::~Builder() {
+}
+
+void GlyphTable::Builder::SetLoca(const IntegerList& loca) {
+ loca_ = loca;
+ set_model_changed(false);
+ glyph_builders_.clear();
+}
+
+void GlyphTable::Builder::GenerateLocaList(IntegerList* locas) {
+ assert(locas);
+ GlyphBuilderList* glyph_builders = GetGlyphBuilders();
+ locas->push_back(0);
+ if (glyph_builders->size() == 0) {
+ locas->push_back(0);
+ } else {
+ int32_t total = 0;
+ for (GlyphBuilderList::iterator b = glyph_builders->begin(),
+ b_end = glyph_builders->end();
+ b != b_end; ++b) {
+ int32_t size = (*b)->SubDataSizeToSerialize();
+ locas->push_back(total + size);
+ total += size;
+ }
+ }
+}
+
+CALLER_ATTACH GlyphTable::Builder*
+ GlyphTable::Builder::CreateBuilder(Header* header, WritableFontData* data) {
+ Ptr<GlyphTable::Builder> builder;
+ builder = new GlyphTable::Builder(header, data);
+ return builder.Detach();
+}
+
+GlyphTable::GlyphBuilderList* GlyphTable::Builder::GlyphBuilders() {
+ return GetGlyphBuilders();
+}
+
+void GlyphTable::Builder::SetGlyphBuilders(GlyphBuilderList* glyph_builders) {
+ glyph_builders_ = *glyph_builders;
+ set_model_changed();
+}
+
+CALLER_ATTACH GlyphTable::Glyph::Builder*
+ GlyphTable::Builder::GlyphBuilder(ReadableFontData* data) {
+ return Glyph::Builder::GetBuilder(this, data);
+}
+
+CALLER_ATTACH FontDataTable*
+ GlyphTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table = new GlyphTable(header(), data);
+ return table.Detach();
+}
+
+void GlyphTable::Builder::SubDataSet() {
+ glyph_builders_.clear();
+ set_model_changed(false);
+}
+
+int32_t GlyphTable::Builder::SubDataSizeToSerialize() {
+ if (glyph_builders_.empty())
+ return 0;
+
+ bool variable = false;
+ int32_t size = 0;
+
+ // Calculate size of each table.
+ for (GlyphBuilderList::iterator b = glyph_builders_.begin(),
+ end = glyph_builders_.end(); b != end; ++b) {
+ int32_t glyph_size = (*b)->SubDataSizeToSerialize();
+ size += abs(glyph_size);
+ variable |= glyph_size <= 0;
+ }
+ return variable ? -size : size;
+}
+
+bool GlyphTable::Builder::SubReadyToSerialize() {
+ return !glyph_builders_.empty();
+}
+
+int32_t GlyphTable::Builder::SubSerialize(WritableFontData* new_data) {
+ int32_t size = 0;
+ for (GlyphBuilderList::iterator b = glyph_builders_.begin(),
+ end = glyph_builders_.end(); b != end; ++b) {
+ FontDataPtr data;
+ data.Attach(new_data->Slice(size));
+ size += (*b)->SubSerialize(down_cast<WritableFontData*>(data.p_));
+ }
+ return size;
+}
+
+void GlyphTable::Builder::Initialize(ReadableFontData* data,
+ const IntegerList& loca) {
+ if (data != NULL) {
+ if (loca_.empty()) {
+ return;
+ }
+ int32_t loca_value;
+ int32_t last_loca_value = loca[0];
+ for (size_t i = 1; i < loca.size(); ++i) {
+ loca_value = loca[i];
+ GlyphBuilderPtr builder;
+ builder.Attach(
+ Glyph::Builder::GetBuilder(this,
+ data,
+ last_loca_value /*offset*/,
+ loca_value - last_loca_value /*length*/));
+ glyph_builders_.push_back(builder);
+ last_loca_value = loca_value;
+ }
+ }
+}
+
+GlyphTable::GlyphBuilderList* GlyphTable::Builder::GetGlyphBuilders() {
+ if (glyph_builders_.empty()) {
+ if (InternalReadData() && !loca_.empty()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalStateException(
+ "Loca values not set - unable to parse glyph data.");
+#endif
+ return NULL;
+ }
+ Initialize(InternalReadData(), loca_);
+ set_model_changed();
+ }
+ return &glyph_builders_;
+}
+
+void GlyphTable::Builder::Revert() {
+ glyph_builders_.clear();
+ set_model_changed(false);
+}
+
+/******************************************************************************
+ * GlyphTable::Glyph class
+ ******************************************************************************/
+GlyphTable::Glyph::~Glyph() {}
+
+CALLER_ATTACH GlyphTable::Glyph*
+ GlyphTable::Glyph::GetGlyph(GlyphTable* table,
+ ReadableFontData* data,
+ int32_t offset,
+ int32_t length) {
+ UNREFERENCED_PARAMETER(table);
+ int32_t type = GlyphType(data, offset, length);
+ GlyphPtr glyph;
+
+ ReadableFontDataPtr sliced_data;
+ sliced_data.Attach(down_cast<ReadableFontData*>(data->Slice(offset, length)));
+ if (type == GlyphType::kSimple) {
+ glyph = new SimpleGlyph(sliced_data);
+ } else {
+ glyph = new CompositeGlyph(sliced_data);
+ }
+ return glyph.Detach();
+}
+
+int32_t GlyphTable::Glyph::Padding() {
+ Initialize();
+ return SubTable::Padding();
+}
+
+int32_t GlyphTable::Glyph::GlyphType() {
+ return glyph_type_;
+}
+
+int32_t GlyphTable::Glyph::NumberOfContours() {
+ return number_of_contours_;
+}
+
+int32_t GlyphTable::Glyph::XMin() {
+ return data_->ReadShort(Offset::kXMin);
+}
+
+int32_t GlyphTable::Glyph::XMax() {
+ return data_->ReadShort(Offset::kXMax);
+}
+
+int32_t GlyphTable::Glyph::YMin() {
+ return data_->ReadShort(Offset::kYMin);
+}
+
+int32_t GlyphTable::Glyph::YMax() {
+ return data_->ReadShort(Offset::kYMax);
+}
+
+GlyphTable::Glyph::Glyph(ReadableFontData* data, int32_t glyph_type)
+ : SubTable(data),
+ glyph_type_(glyph_type) {
+ if (data_->Length() == 0) {
+ number_of_contours_ = 0;
+ } else {
+ // -1 if composite
+ number_of_contours_ = data_->ReadShort(Offset::kNumberOfContours);
+ }
+}
+
+int32_t GlyphTable::Glyph::GlyphType(ReadableFontData* data,
+ int32_t offset,
+ int32_t length) {
+ if (length == 0) {
+ return GlyphType::kSimple;
+ }
+ int32_t number_of_contours = data->ReadShort(offset);
+ if (number_of_contours >= 0) {
+ return GlyphType::kSimple;
+ }
+ return GlyphType::kComposite;
+}
+
+/******************************************************************************
+ * GlyphTable::Glyph::Builder class
+ ******************************************************************************/
+GlyphTable::Glyph::Builder::~Builder() {
+}
+
+GlyphTable::Glyph::Builder::Builder(WritableFontData* data)
+ : SubTable::Builder(data) {
+}
+
+GlyphTable::Glyph::Builder::Builder(ReadableFontData* data)
+ : SubTable::Builder(data) {
+}
+
+CALLER_ATTACH GlyphTable::Glyph::Builder*
+ GlyphTable::Glyph::Builder::GetBuilder(
+ GlyphTable::Builder* table_builder,
+ ReadableFontData* data) {
+ return GetBuilder(table_builder, data, 0, data->Length());
+}
+
+CALLER_ATTACH GlyphTable::Glyph::Builder*
+ GlyphTable::Glyph::Builder::GetBuilder(
+ GlyphTable::Builder* table_builder,
+ ReadableFontData* data,
+ int32_t offset,
+ int32_t length) {
+ UNREFERENCED_PARAMETER(table_builder);
+ int32_t type = Glyph::GlyphType(data, offset, length);
+ GlyphBuilderPtr builder;
+ ReadableFontDataPtr sliced_data;
+ sliced_data.Attach(down_cast<ReadableFontData*>(data->Slice(offset, length)));
+ if (type == GlyphType::kSimple) {
+ builder = new SimpleGlyph::SimpleGlyphBuilder(sliced_data);
+ } else {
+ builder = new CompositeGlyph::CompositeGlyphBuilder(sliced_data);
+ }
+ return builder.Detach();
+}
+
+void GlyphTable::Glyph::Builder::SubDataSet() {
+ // NOP
+}
+
+int32_t GlyphTable::Glyph::Builder::SubDataSizeToSerialize() {
+ return InternalReadData()->Length();
+}
+
+bool GlyphTable::Glyph::Builder::SubReadyToSerialize() {
+ return true;
+}
+
+int32_t GlyphTable::Glyph::Builder::SubSerialize(WritableFontData* new_data) {
+ return InternalReadData()->CopyTo(new_data);
+}
+
+/******************************************************************************
+ * GlyphTable::SimpleGlyph
+ ******************************************************************************/
+GlyphTable::SimpleGlyph::SimpleGlyph(ReadableFontData* data)
+ : GlyphTable::Glyph(data, GlyphType::kSimple), initialized_(false) {
+}
+
+GlyphTable::SimpleGlyph::~SimpleGlyph() {
+}
+
+int32_t GlyphTable::SimpleGlyph::InstructionSize() {
+ Initialize();
+ return instruction_size_;
+}
+
+CALLER_ATTACH ReadableFontData* GlyphTable::SimpleGlyph::Instructions() {
+ Initialize();
+ return down_cast<ReadableFontData*>(
+ data_->Slice(instructions_offset_, InstructionSize()));
+}
+
+int32_t GlyphTable::SimpleGlyph::NumberOfPoints(int32_t contour) {
+ Initialize();
+ if (contour >= NumberOfContours()) {
+ return 0;
+ }
+ return contour_index_[contour + 1] - contour_index_[contour];
+}
+
+int32_t GlyphTable::SimpleGlyph::XCoordinate(int32_t contour, int32_t point) {
+ Initialize();
+ return x_coordinates_[contour_index_[contour] + point];
+}
+
+int32_t GlyphTable::SimpleGlyph::YCoordinate(int32_t contour, int32_t point) {
+ Initialize();
+ return y_coordinates_[contour_index_[contour] + point];
+}
+
+bool GlyphTable::SimpleGlyph::OnCurve(int32_t contour, int32_t point) {
+ Initialize();
+ return on_curve_[contour_index_[contour] + point];
+}
+
+void GlyphTable::SimpleGlyph::Initialize() {
+ AutoLock lock(initialization_lock_);
+ if (initialized_) {
+ return;
+ }
+
+ if (ReadFontData()->Length() == 0) {
+ instruction_size_ = 0;
+ number_of_points_ = 0;
+ instructions_offset_ = 0;
+ flags_offset_ = 0;
+ x_coordinates_offset_ = 0;
+ y_coordinates_offset_ = 0;
+ return;
+ }
+
+ instruction_size_ = data_->ReadUShort(Offset::kSimpleEndPtsOfCountours +
+ NumberOfContours() * DataSize::kUSHORT);
+ instructions_offset_ = Offset::kSimpleEndPtsOfCountours +
+ (NumberOfContours() + 1) * DataSize::kUSHORT;
+ flags_offset_ = instructions_offset_ + instruction_size_ * DataSize::kBYTE;
+ number_of_points_ = ContourEndPoint(NumberOfContours() - 1) + 1;
+ x_coordinates_.resize(number_of_points_);
+ y_coordinates_.resize(number_of_points_);
+ on_curve_.resize(number_of_points_);
+ ParseData(false);
+ x_coordinates_offset_ = flags_offset_ + flag_byte_count_ * DataSize::kBYTE;
+ y_coordinates_offset_ = x_coordinates_offset_ + x_byte_count_ *
+ DataSize::kBYTE;
+ contour_index_.resize(NumberOfContours() + 1);
+ contour_index_[0] = 0;
+ for (uint32_t contour = 0; contour < contour_index_.size() - 1; ++contour) {
+ contour_index_[contour + 1] = ContourEndPoint(contour) + 1;
+ }
+ ParseData(true);
+ int32_t non_padded_data_length =
+ 5 * DataSize::kSHORT +
+ (NumberOfContours() * DataSize::kUSHORT) +
+ DataSize::kUSHORT +
+ (instruction_size_ * DataSize::kBYTE) +
+ (flag_byte_count_ * DataSize::kBYTE) +
+ (x_byte_count_ * DataSize::kBYTE) +
+ (y_byte_count_ * DataSize::kBYTE);
+ set_padding(DataLength() - non_padded_data_length);
+ initialized_ = true;
+}
+
+void GlyphTable::SimpleGlyph::ParseData(bool fill_arrays) {
+ int32_t flag = 0;
+ int32_t flag_repeat = 0;
+ int32_t flag_index = 0;
+ int32_t x_byte_index = 0;
+ int32_t y_byte_index = 0;
+
+ for (int32_t point_index = 0; point_index < number_of_points_;
+ ++point_index) {
+ // get the flag for the current point
+ if (flag_repeat == 0) {
+ flag = FlagAsInt(flag_index++);
+ if ((flag & kFLAG_REPEAT) == kFLAG_REPEAT) {
+ flag_repeat = FlagAsInt(flag_index++);
+ }
+ } else {
+ flag_repeat--;
+ }
+
+ // on the curve?
+ if (fill_arrays) {
+ on_curve_[point_index] = ((flag & kFLAG_ONCURVE) == kFLAG_ONCURVE);
+ }
+ // get the x coordinate
+ if ((flag & kFLAG_XSHORT) == kFLAG_XSHORT) {
+ // single byte x coord value
+ if (fill_arrays) {
+ x_coordinates_[point_index] =
+ data_->ReadUByte(x_coordinates_offset_ + x_byte_index);
+ x_coordinates_[point_index] *=
+ ((flag & kFLAG_XREPEATSIGN) == kFLAG_XREPEATSIGN) ? 1 : -1;
+ }
+ x_byte_index++;
+ } else {
+ // double byte coord value
+ if (!((flag & kFLAG_XREPEATSIGN) == kFLAG_XREPEATSIGN)) {
+ if (fill_arrays) {
+ x_coordinates_[point_index] =
+ data_->ReadShort(x_coordinates_offset_ + x_byte_index);
+ }
+ x_byte_index += 2;
+ }
+ }
+ if (fill_arrays && point_index > 0) {
+ x_coordinates_[point_index] += x_coordinates_[point_index - 1];
+ }
+
+ // get the y coordinate
+ if ((flag & kFLAG_YSHORT) == kFLAG_YSHORT) {
+ if (fill_arrays) {
+ y_coordinates_[point_index] =
+ data_->ReadUByte(y_coordinates_offset_ + y_byte_index);
+ y_coordinates_[point_index] *=
+ ((flag & kFLAG_YREPEATSIGN) == kFLAG_YREPEATSIGN) ? 1 : -1;
+ }
+ y_byte_index++;
+ } else {
+ if (!((flag & kFLAG_YREPEATSIGN) == kFLAG_YREPEATSIGN)) {
+ if (fill_arrays) {
+ y_coordinates_[point_index] =
+ data_->ReadShort(y_coordinates_offset_ + y_byte_index);
+ }
+ y_byte_index += 2;
+ }
+ }
+ if (fill_arrays && point_index > 0) {
+ y_coordinates_[point_index] += y_coordinates_[point_index - 1];
+ }
+ }
+ flag_byte_count_ = flag_index;
+ x_byte_count_ = x_byte_index;
+ y_byte_count_ = y_byte_index;
+}
+
+int32_t GlyphTable::SimpleGlyph::FlagAsInt(int32_t index) {
+ return data_->ReadUByte(flags_offset_ + index * DataSize::kBYTE);
+}
+
+int32_t GlyphTable::SimpleGlyph::ContourEndPoint(int32_t contour) {
+ return data_->ReadUShort(contour * DataSize::kUSHORT +
+ Offset::kSimpleEndPtsOfCountours);
+}
+
+/******************************************************************************
+ * GlyphTable::SimpleGlyph::Builder
+ ******************************************************************************/
+GlyphTable::SimpleGlyph::SimpleGlyphBuilder::~SimpleGlyphBuilder() {
+}
+
+GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SimpleGlyphBuilder(
+ WritableFontData* data)
+ : Glyph::Builder(data) {
+}
+
+GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SimpleGlyphBuilder(
+ ReadableFontData* data)
+ : Glyph::Builder(data) {
+}
+
+CALLER_ATTACH FontDataTable*
+ GlyphTable::SimpleGlyph::SimpleGlyphBuilder::SubBuildTable(
+ ReadableFontData* data) {
+ FontDataTablePtr table = new SimpleGlyph(data);
+ return table.Detach();
+}
+
+/******************************************************************************
+ * GlyphTable::CompositeGlyph
+ ******************************************************************************/
+GlyphTable::CompositeGlyph::CompositeGlyph(ReadableFontData* data)
+ : GlyphTable::Glyph(data, GlyphType::kComposite),
+ instruction_size_(0),
+ instructions_offset_(0),
+ initialized_(false) {
+ Initialize();
+}
+
+GlyphTable::CompositeGlyph::~CompositeGlyph() {
+}
+
+int32_t GlyphTable::CompositeGlyph::Flags(int32_t contour) {
+ return data_->ReadUShort(contour_index_[contour]);
+}
+
+int32_t GlyphTable::CompositeGlyph::NumGlyphs() {
+ return contour_index_.size();
+}
+
+int32_t GlyphTable::CompositeGlyph::GlyphIndex(int32_t contour) {
+ return data_->ReadUShort(DataSize::kUSHORT + contour_index_[contour]);
+}
+
+int32_t GlyphTable::CompositeGlyph::Argument1(int32_t contour) {
+ int32_t index = 2 * DataSize::kUSHORT + contour_index_[contour];
+ int32_t contour_flags = Flags(contour);
+ if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) ==
+ kFLAG_ARG_1_AND_2_ARE_WORDS) {
+ return data_->ReadUShort(index);
+ }
+ return data_->ReadByte(index);
+}
+
+int32_t GlyphTable::CompositeGlyph::Argument2(int32_t contour) {
+ int32_t index = 2 * DataSize::kUSHORT + contour_index_[contour];
+ int32_t contour_flags = Flags(contour);
+ if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) ==
+ kFLAG_ARG_1_AND_2_ARE_WORDS) {
+ return data_->ReadUShort(index + DataSize::kUSHORT);
+ }
+ return data_->ReadByte(index + DataSize::kUSHORT);
+}
+
+int32_t GlyphTable::CompositeGlyph::TransformationSize(int32_t contour) {
+ int32_t contour_flags = Flags(contour);
+ if ((contour_flags & kFLAG_WE_HAVE_A_SCALE) == kFLAG_WE_HAVE_A_SCALE) {
+ return DataSize::kF2DOT14;
+ } else if ((contour_flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) ==
+ kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+ return 2 * DataSize::kF2DOT14;
+ } else if ((contour_flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) ==
+ kFLAG_WE_HAVE_A_TWO_BY_TWO) {
+ return 4 * DataSize::kF2DOT14;
+ }
+ return 0;
+}
+
+void GlyphTable::CompositeGlyph::Transformation(int32_t contour,
+ ByteVector* transformation) {
+ int32_t contour_flags = Flags(contour);
+ int32_t index = contour_index_[contour] + 2 * DataSize::kUSHORT;
+ if ((contour_flags & kFLAG_ARG_1_AND_2_ARE_WORDS) ==
+ kFLAG_ARG_1_AND_2_ARE_WORDS) {
+ index += 2 * DataSize::kSHORT;
+ } else {
+ index += 2 * DataSize::kBYTE;
+ }
+ int32_t tsize = TransformationSize(contour);
+ transformation->resize(tsize);
+ data_->ReadBytes(index, &((*transformation)[0]), 0, tsize);
+}
+
+int32_t GlyphTable::CompositeGlyph::InstructionSize() {
+ return instruction_size_;
+}
+
+CALLER_ATTACH ReadableFontData* GlyphTable::CompositeGlyph::Instructions() {
+ return down_cast<ReadableFontData*>(
+ data_->Slice(instructions_offset_, InstructionSize()));
+}
+
+void GlyphTable::CompositeGlyph::Initialize() {
+ AutoLock lock(initialization_lock_);
+ if (initialized_) {
+ return;
+ }
+
+ int32_t index = 5 * DataSize::kUSHORT;
+ int32_t flags = kFLAG_MORE_COMPONENTS;
+
+ while ((flags & kFLAG_MORE_COMPONENTS) == kFLAG_MORE_COMPONENTS) {
+ contour_index_.push_back(index);
+ flags = data_->ReadUShort(index);
+ index += 2 * DataSize::kUSHORT; // flags and glyphIndex
+ if ((flags & kFLAG_ARG_1_AND_2_ARE_WORDS) == kFLAG_ARG_1_AND_2_ARE_WORDS) {
+ index += 2 * DataSize::kSHORT;
+ } else {
+ index += 2 * DataSize::kBYTE;
+ }
+ if ((flags & kFLAG_WE_HAVE_A_SCALE) == kFLAG_WE_HAVE_A_SCALE) {
+ index += DataSize::kF2DOT14;
+ } else if ((flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) ==
+ kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
+ index += 2 * DataSize::kF2DOT14;
+ } else if ((flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) ==
+ kFLAG_WE_HAVE_A_TWO_BY_TWO) {
+ index += 4 * DataSize::kF2DOT14;
+ }
+ int32_t non_padded_data_length = index;
+ if ((flags & kFLAG_WE_HAVE_INSTRUCTIONS) == kFLAG_WE_HAVE_INSTRUCTIONS) {
+ instruction_size_ = data_->ReadUShort(index);
+ index += DataSize::kUSHORT;
+ instructions_offset_ = index;
+ non_padded_data_length = index + (instruction_size_ * DataSize::kBYTE);
+ }
+ set_padding(DataLength() - non_padded_data_length);
+ }
+
+ initialized_ = true;
+}
+
+/******************************************************************************
+ * GlyphTable::CompositeGlyph::Builder
+ ******************************************************************************/
+GlyphTable::CompositeGlyph::CompositeGlyphBuilder::~CompositeGlyphBuilder() {
+}
+
+GlyphTable::CompositeGlyph::CompositeGlyphBuilder::CompositeGlyphBuilder(
+ WritableFontData* data)
+ : Glyph::Builder(data) {
+}
+
+GlyphTable::CompositeGlyph::CompositeGlyphBuilder::CompositeGlyphBuilder(
+ ReadableFontData* data)
+ : Glyph::Builder(data) {
+}
+
+CALLER_ATTACH FontDataTable*
+ GlyphTable::CompositeGlyph::CompositeGlyphBuilder::SubBuildTable(
+ ReadableFontData* data) {
+ FontDataTablePtr table = new CompositeGlyph(data);
+ return table.Detach();
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/truetype/glyph_table.h b/sfntly/table/truetype/glyph_table.h
new file mode 100644
index 0000000..4ffddda
--- /dev/null
+++ b/sfntly/table/truetype/glyph_table.h
@@ -0,0 +1,334 @@
+/*
+ * 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_SFNTLY_TABLE_TRUETYPE_GLYPH_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_GLYPH_TABLE_H_
+
+#include <vector>
+
+#include "sfntly/table/table.h"
+#include "sfntly/table/subtable.h"
+#include "sfntly/table/subtable_container_table.h"
+
+namespace sfntly {
+
+struct GlyphType {
+ enum {
+ kSimple = 0,
+ kComposite = 1
+ };
+};
+
+class GlyphTable : public SubTableContainerTable,
+ public RefCounted<GlyphTable> {
+ public:
+ class Builder;
+ class Glyph : public SubTable {
+ public:
+ // Note: Contour is an empty class for the version ported
+ class Contour {
+ protected:
+ Contour() {}
+ virtual ~Contour() {}
+ };
+
+ class Builder : public SubTable::Builder {
+ public:
+ virtual ~Builder();
+
+ protected:
+ // Incoming table_builder is GlyphTable::Builder*.
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit Builder(WritableFontData* data);
+ explicit Builder(ReadableFontData* data);
+
+ static CALLER_ATTACH Builder*
+ GetBuilder(GlyphTable::Builder* table_builder,
+ ReadableFontData* data);
+ static CALLER_ATTACH Builder*
+ GetBuilder(GlyphTable::Builder* table_builder,
+ ReadableFontData* data,
+ int32_t offset,
+ int32_t length);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ private:
+ friend class GlyphTable::Builder;
+ };
+
+ virtual ~Glyph();
+ static CALLER_ATTACH Glyph* GetGlyph(GlyphTable* table,
+ ReadableFontData* data,
+ int32_t offset,
+ int32_t length);
+
+ virtual int32_t Padding();
+ virtual int32_t GlyphType();
+ virtual int32_t NumberOfContours();
+ virtual int32_t XMin();
+ virtual int32_t XMax();
+ virtual int32_t YMin();
+ virtual int32_t YMax();
+
+ virtual int32_t InstructionSize() = 0;
+ virtual ReadableFontData* Instructions() = 0;
+
+ protected:
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ Glyph(ReadableFontData* data, int32_t glyph_type);
+ virtual void Initialize() = 0;
+ // Note: Derived class to define initialization_lock_.
+
+ private:
+ static int32_t GlyphType(ReadableFontData* data,
+ int32_t offset,
+ int32_t length);
+
+ int32_t glyph_type_;
+ int32_t number_of_contours_;
+ }; // class GlyphTable::Glyph
+ typedef Ptr<GlyphTable::Glyph::Builder> GlyphBuilderPtr;
+ typedef std::vector<GlyphBuilderPtr> GlyphBuilderList;
+
+ class Builder : public SubTableContainerTable::Builder,
+ public RefCounted<GlyphTable::Builder> {
+ public:
+ // Note: Constructor scope altered to public for base class to instantiate.
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ virtual void SetLoca(const IntegerList& loca);
+ virtual void GenerateLocaList(IntegerList* locas);
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ // Gets the List of glyph builders for the glyph table builder. These may be
+ // manipulated in any way by the caller and the changes will be reflected in
+ // the final glyph table produced.
+ // If there is no current data for the glyph builder or the glyph builders
+ // have not been previously set then this will return an empty glyph builder
+ // List. If there is current data (i.e. data read from an existing font) and
+ // the <code>loca</code> list has not been set or is null, empty, or
+ // invalid, then an empty glyph builder List will be returned.
+ GlyphBuilderList* GlyphBuilders();
+
+ // Replace the internal glyph builders with the one provided. The provided
+ // list and all contained objects belong to this builder.
+ // This call is only required if the entire set of glyphs in the glyph
+ // table builder are being replaced. If the glyph builder list provided from
+ // the GlyphTable.Builder::GlyphBuilders() is being used and modified
+ // then those changes will already be reflected in the glyph table builder.
+ void SetGlyphBuilders(GlyphBuilderList* glyph_builders);
+
+ // Glyph builder factories
+ CALLER_ATTACH Glyph::Builder* GlyphBuilder(ReadableFontData* data);
+
+ protected: // internal API for building
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ private:
+ void Initialize(ReadableFontData* data, const IntegerList& loca);
+ GlyphBuilderList* GetGlyphBuilders();
+ void Revert();
+
+ GlyphBuilderList glyph_builders_;
+ IntegerList loca_;
+ };
+
+ class SimpleGlyph : public Glyph, public RefCounted<SimpleGlyph> {
+ public:
+ static const int32_t kFLAG_ONCURVE;
+ static const int32_t kFLAG_XSHORT;
+ static const int32_t kFLAG_YSHORT;
+ static const int32_t kFLAG_REPEAT;
+ static const int32_t kFLAG_XREPEATSIGN;
+ static const int32_t kFLAG_YREPEATSIGN;
+
+ class SimpleContour : public Glyph::Contour {
+ protected:
+ SimpleContour() {}
+ virtual ~SimpleContour() {}
+ };
+
+ class SimpleGlyphBuilder : public Glyph::Builder,
+ public RefCounted<SimpleGlyphBuilder> {
+ public:
+ virtual ~SimpleGlyphBuilder();
+
+ protected:
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit SimpleGlyphBuilder(WritableFontData* data);
+ explicit SimpleGlyphBuilder(ReadableFontData* data);
+ virtual CALLER_ATTACH FontDataTable*
+ SubBuildTable(ReadableFontData* data);
+
+ private:
+ friend class Glyph::Builder;
+ };
+
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit SimpleGlyph(ReadableFontData* data);
+ virtual ~SimpleGlyph();
+
+ virtual int32_t InstructionSize();
+ virtual CALLER_ATTACH ReadableFontData* Instructions();
+ virtual void Initialize();
+
+ int32_t NumberOfPoints(int32_t contour);
+ int32_t XCoordinate(int32_t contour, int32_t point);
+ int32_t YCoordinate(int32_t contour, int32_t point);
+ bool OnCurve(int32_t contour, int32_t point);
+
+ private:
+ void ParseData(bool fill_arrays);
+ int32_t FlagAsInt(int32_t index);
+ int32_t ContourEndPoint(int32_t contour);
+
+ bool initialized_;
+ Lock initialization_lock_;
+ int32_t instruction_size_;
+ int32_t number_of_points_;
+
+ // start offsets of the arrays
+ int32_t instructions_offset_;
+ int32_t flags_offset_;
+ int32_t x_coordinates_offset_;
+ int32_t y_coordinates_offset_;
+
+ int32_t flag_byte_count_;
+ int32_t x_byte_count_;
+ int32_t y_byte_count_;
+
+ IntegerList x_coordinates_;
+ IntegerList y_coordinates_;
+ std::vector<bool> on_curve_;
+ IntegerList contour_index_;
+ };
+
+ class CompositeGlyph : public Glyph, public RefCounted<CompositeGlyph> {
+ public:
+ static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS;
+ static const int32_t kFLAG_ARGS_ARE_XY_VALUES;
+ static const int32_t kFLAG_ROUND_XY_TO_GRID;
+ static const int32_t kFLAG_WE_HAVE_A_SCALE;
+ static const int32_t kFLAG_RESERVED;
+ static const int32_t kFLAG_MORE_COMPONENTS;
+ static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE;
+ static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO;
+ static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS;
+ static const int32_t kFLAG_USE_MY_METRICS;
+ static const int32_t kFLAG_OVERLAP_COMPOUND;
+ static const int32_t kFLAG_SCALED_COMPONENT_OFFSET;
+ static const int32_t kFLAG_UNSCALED_COMPONENT_OFFSET;
+
+ class CompositeGlyphBuilder : public Glyph::Builder,
+ public RefCounted<CompositeGlyphBuilder> {
+ public:
+ virtual ~CompositeGlyphBuilder();
+
+ protected:
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit CompositeGlyphBuilder(WritableFontData* data);
+ explicit CompositeGlyphBuilder(ReadableFontData* data);
+
+ virtual CALLER_ATTACH FontDataTable*
+ SubBuildTable(ReadableFontData* data);
+
+ private:
+ friend class Glyph::Builder;
+ };
+
+ // Note: constructor refactored in C++ to avoid heavy lifting.
+ // caller need to do data->Slice(offset, length) beforehand.
+ explicit CompositeGlyph(ReadableFontData* data);
+ virtual ~CompositeGlyph();
+
+ int32_t Flags(int32_t contour);
+ int32_t NumGlyphs();
+ int32_t GlyphIndex(int32_t contour);
+ int32_t Argument1(int32_t contour);
+ int32_t Argument2(int32_t contour);
+ int32_t TransformationSize(int32_t contour);
+ void Transformation(int32_t contour, ByteVector* transformation);
+ virtual int32_t InstructionSize();
+ virtual CALLER_ATTACH ReadableFontData* Instructions();
+
+ protected:
+ virtual void Initialize();
+
+ private:
+ IntegerList contour_index_;
+ int32_t instruction_size_;
+ int32_t instructions_offset_;
+ bool initialized_;
+ Lock initialization_lock_;
+ };
+
+ virtual ~GlyphTable();
+
+ // C++ port: rename glyph() to GetGlyph().
+ Glyph* GetGlyph(int32_t offset, int32_t length);
+
+ private:
+ struct Offset {
+ enum {
+ // header
+ kNumberOfContours = 0,
+ kXMin = 2,
+ kYMin = 4,
+ kXMax = 6,
+ kYMax = 8,
+
+ // Simple Glyph Description
+ kSimpleEndPtsOfCountours = 10,
+ // offset from the end of the contours array
+ kSimpleInstructionLength = 0,
+ kSimpleInstructions = 2,
+ // flags
+ // xCoordinates
+ // yCoordinates
+
+ // Composite Glyph Description
+ kCompositeFlags = 0,
+ kCompositeGyphIndexWithoutFlag = 0,
+ kCompositeGlyphIndexWithFlag = 2,
+ };
+ };
+
+ GlyphTable(Header* header, ReadableFontData* data);
+};
+typedef Ptr<GlyphTable> GlyphTablePtr;
+typedef Ptr<GlyphTable::Builder> GlyphTableBuilderPtr;
+typedef std::vector<GlyphTableBuilderPtr> GlyphTableBuilderList;
+typedef Ptr<GlyphTable::Glyph> GlyphPtr;
+typedef Ptr<GlyphTable::Glyph::Builder> GlyphBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_GLYPH_TABLE_H_
diff --git a/sfntly/table/truetype/loca_table.cc b/sfntly/table/truetype/loca_table.cc
new file mode 100644
index 0000000..c692155
--- /dev/null
+++ b/sfntly/table/truetype/loca_table.cc
@@ -0,0 +1,246 @@
+/*
+ * 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/table/truetype/loca_table.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+/******************************************************************************
+ * LocaTable class
+ ******************************************************************************/
+LocaTable::~LocaTable() {}
+
+int32_t LocaTable::GlyphOffset(int32_t glyph_id) {
+ if (glyph_id < 0 || glyph_id >= num_glyphs_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException("Glyph ID is out of bounds.");
+#endif
+ return 0;
+ }
+ return Loca(glyph_id);
+}
+
+int32_t LocaTable::GlyphLength(int32_t glyph_id) {
+ if (glyph_id < 0 || glyph_id >= num_glyphs_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException("Glyph ID is out of bounds.");
+#endif
+ return 0;
+ }
+ return Loca(glyph_id + 1) - Loca(glyph_id);
+}
+
+int32_t LocaTable::NumLocas() {
+ return num_glyphs_ + 1;
+}
+
+int32_t LocaTable::Loca(int32_t index) {
+ if (index > num_glyphs_) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundException();
+#endif
+ return 0;
+ }
+ if (format_version_ == IndexToLocFormat::kShortOffset) {
+ return 2 * data_->ReadUShort(index * DataSize::kUSHORT);
+ }
+ return data_->ReadULongAsInt(index * DataSize::kULONG);
+}
+
+LocaTable::LocaTable(Header* header,
+ ReadableFontData* data,
+ int32_t format_version,
+ int32_t num_glyphs)
+ : Table(header, data),
+ format_version_(format_version),
+ num_glyphs_(num_glyphs) {
+}
+
+/******************************************************************************
+ * LocaTable::Iterator class
+ ******************************************************************************/
+LocaTable::LocaIterator::LocaIterator(LocaTable* table)
+ : PODIterator<int32_t, LocaTable>(table), index_(-1) {
+}
+
+bool LocaTable::LocaIterator::HasNext() {
+ return index_ <= container()->num_glyphs_;
+}
+
+int32_t LocaTable::LocaIterator::Next() {
+ return container()->Loca(index_++);
+}
+
+/******************************************************************************
+ * LocaTable::Builder class
+ ******************************************************************************/
+LocaTable::Builder::Builder(Header* header, WritableFontData* data)
+ : Table::Builder(header, data),
+ format_version_(IndexToLocFormat::kLongOffset),
+ num_glyphs_(-1) {
+}
+
+LocaTable::Builder::Builder(Header* header, ReadableFontData* data)
+ : Table::Builder(header, data),
+ format_version_(IndexToLocFormat::kLongOffset),
+ num_glyphs_(-1) {
+}
+
+LocaTable::Builder::~Builder() {}
+
+CALLER_ATTACH
+LocaTable::Builder* LocaTable::Builder::CreateBuilder(Header* header,
+ WritableFontData* data) {
+ Ptr<LocaTable::Builder> builder;
+ builder = new LocaTable::Builder(header, data);
+ return builder.Detach();
+}
+
+IntegerList* LocaTable::Builder::LocaList() {
+ return GetLocaList();
+}
+
+void LocaTable::Builder::SetLocaList(IntegerList* list) {
+ loca_.clear();
+ if (list) {
+ loca_ = *list;
+ set_model_changed();
+ }
+}
+
+int32_t LocaTable::Builder::GlyphOffset(int32_t glyph_id) {
+ if (CheckGlyphRange(glyph_id) == -1) {
+ return 0;
+ }
+ return GetLocaList()->at(glyph_id);
+}
+
+int32_t LocaTable::Builder::GlyphLength(int32_t glyph_id) {
+ if (CheckGlyphRange(glyph_id) == -1) {
+ return 0;
+ }
+ return GetLocaList()->at(glyph_id + 1) - GetLocaList()->at(glyph_id);
+}
+
+void LocaTable::Builder::SetNumGlyphs(int32_t num_glyphs) {
+ num_glyphs_ = num_glyphs;
+}
+
+int32_t LocaTable::Builder::NumGlyphs() {
+ return LastGlyphIndex() - 1;
+}
+
+void LocaTable::Builder::Revert() {
+ loca_.clear();
+ set_model_changed(false);
+}
+
+int32_t LocaTable::Builder::NumLocas() {
+ return GetLocaList()->size();
+}
+
+int32_t LocaTable::Builder::Loca(int32_t index) {
+ return GetLocaList()->at(index);
+}
+
+CALLER_ATTACH
+FontDataTable* LocaTable::Builder::SubBuildTable(ReadableFontData* data) {
+ FontDataTablePtr table =
+ new LocaTable(header(), data, format_version_, num_glyphs_);
+ return table.Detach();
+}
+
+void LocaTable::Builder::SubDataSet() {
+ Initialize(InternalReadData());
+}
+
+int32_t LocaTable::Builder::SubDataSizeToSerialize() {
+ if (loca_.empty()) {
+ return 0;
+ }
+ if (format_version_ == IndexToLocFormat::kLongOffset) {
+ return loca_.size() * DataSize::kULONG;
+ }
+ return loca_.size() * DataSize::kUSHORT;
+}
+
+bool LocaTable::Builder::SubReadyToSerialize() {
+ return !loca_.empty();
+}
+
+int32_t LocaTable::Builder::SubSerialize(WritableFontData* new_data) {
+ int32_t size = 0;
+ for (IntegerList::iterator l = loca_.begin(), end = loca_.end();
+ l != end; ++l) {
+ if (format_version_ == IndexToLocFormat::kLongOffset) {
+ size += new_data->WriteULong(size, *l);
+ } else {
+ size += new_data->WriteUShort(size, *l / 2);
+ }
+ }
+ num_glyphs_ = loca_.size() - 1;
+ return size;
+}
+
+void LocaTable::Builder::Initialize(ReadableFontData* data) {
+ ClearLoca(false);
+ if (data) {
+ if (NumGlyphs() < 0) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IllegalStateException("numglyphs not set on LocaTable Builder.");
+#endif
+ return;
+ }
+ LocaTablePtr table =
+ new LocaTable(header(), data, format_version_, num_glyphs_);
+ Ptr<LocaTable::LocaIterator> loca_iter =
+ new LocaTable::LocaIterator(table);
+ while (loca_iter->HasNext()) {
+ loca_.push_back(loca_iter->Next());
+ }
+ }
+}
+
+int32_t LocaTable::Builder::CheckGlyphRange(int32_t glyph_id) {
+ if (glyph_id < 0 || glyph_id > LastGlyphIndex()) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw IndexOutOfBoundsException("Glyph ID is outside of the allowed range");
+#endif
+ return -1;
+ }
+ return glyph_id;
+}
+
+int32_t LocaTable::Builder::LastGlyphIndex() {
+ return !loca_.empty() ? loca_.size() - 2 : num_glyphs_ - 1;
+}
+
+IntegerList* LocaTable::Builder::GetLocaList() {
+ if (loca_.empty()) {
+ Initialize(InternalReadData());
+ set_model_changed();
+ }
+ return &loca_;
+}
+
+void LocaTable::Builder::ClearLoca(bool nullify) {
+ // Note: in C++ port, nullify is not used at all.
+ UNREFERENCED_PARAMETER(nullify);
+ loca_.clear();
+ set_model_changed(false);
+}
+
+} // namespace sfntly
diff --git a/sfntly/table/truetype/loca_table.h b/sfntly/table/truetype/loca_table.h
new file mode 100644
index 0000000..67c5749
--- /dev/null
+++ b/sfntly/table/truetype/loca_table.h
@@ -0,0 +1,183 @@
+/*
+ * 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_SFNTLY_TABLE_TRUETYPE_LOCA_TABLE_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_LOCA_TABLE_H_
+
+#include "sfntly/port/java_iterator.h"
+#include "sfntly/table/table.h"
+#include "sfntly/table/core/font_header_table.h"
+
+namespace sfntly {
+
+// A Loca table - 'loca'.
+class LocaTable : public Table, public RefCounted<LocaTable> {
+ public:
+ class LocaIterator : public PODIterator<int32_t, LocaTable> {
+ public:
+ explicit LocaIterator(LocaTable* table);
+ virtual ~LocaIterator() {}
+
+ virtual bool HasNext();
+ virtual int32_t Next();
+
+ private:
+ int32_t index_;
+ };
+
+ class Builder : public Table::Builder, public RefCounted<Builder> {
+ public:
+ // Constructor scope altered to public for base class to instantiate.
+ Builder(Header* header, WritableFontData* data);
+ Builder(Header* header, ReadableFontData* data);
+ virtual ~Builder();
+
+ static CALLER_ATTACH Builder* CreateBuilder(Header* header,
+ WritableFontData* data);
+
+ // Get the format version that will be used when the loca table is
+ // generated.
+ // @return the loca table format version
+ int32_t format_version() { return format_version_; }
+ void set_format_version(int32_t value) { format_version_ = value; }
+
+ // Gets the List of locas for loca table builder. These may be manipulated
+ // in any way by the caller and the changes will be reflected in the final
+ // loca table produced as long as no subsequent call is made to the
+ // SetLocaList(List) method.
+ // If there is no current data for the loca table builder or the loca list
+ // have not been previously set then this will return an empty List.
+ IntegerList* LocaList();
+
+ // Set the list of locas to be used for building this table. If any existing
+ // list was already retrieved with the LocaList() method then the
+ // connection of that previous list to this builder will be broken.
+ void SetLocaList(IntegerList* list);
+
+ // Return the offset for the given glyph id. Valid glyph ids are from 0 to
+ // one less than the number of glyphs. The zero entry is the special entry
+ // for the notdef glyph. The final entry beyond the last glyph id is used to
+ // calculate the size of the last glyph.
+ // @param glyphId the glyph id to get the offset for; must be less than or
+ // equal to one more than the number of glyph ids
+ // @return the offset in the glyph table to the specified glyph id
+ int32_t GlyphOffset(int32_t glyph_id);
+
+ // Get the length of the data in the glyph table for the specified glyph id.
+ int32_t GlyphLength(int32_t glyph_id);
+
+ // Set the number of glyphs.
+ // This method sets the number of glyphs that the builder will attempt to
+ // parse location data for from the raw binary data. This method only needs
+ // to be called (and <b>must</b> be) when the raw data for this builder has
+ // been changed. It does not by itself reset the data or clear any set loca
+ // list.
+ void SetNumGlyphs(int32_t num_glyphs);
+
+ // Get the number of glyphs that this builder has support for.
+ int NumGlyphs();
+
+ // Revert the loca table builder to the state contained in the last raw data
+ // set on the builder. That raw data may be that read from a font file when
+ // the font builder was created, that set by a user of the loca table
+ // builder, or null data if this builder was created as a new empty builder.
+ void Revert();
+
+ // Get the number of locations or locas. This will be one more than the
+ // number of glyphs for this table since the last loca position is used to
+ // indicate the size of the final glyph.
+ int32_t NumLocas();
+
+ // Get the value from the loca table for the index specified. These are the
+ // raw values from the table that are used to compute the offset and size of
+ // a glyph in the glyph table. Valid index values run from 0 to the number
+ // of glyphs in the font.
+ int32_t Loca(int32_t index);
+
+ virtual CALLER_ATTACH FontDataTable* SubBuildTable(ReadableFontData* data);
+ virtual void SubDataSet();
+ virtual int32_t SubDataSizeToSerialize();
+ virtual bool SubReadyToSerialize();
+ virtual int32_t SubSerialize(WritableFontData* new_data);
+
+ private:
+ // Initialize the internal state from the data. Done lazily since in many
+ // cases the builder will be just creating a table object with no parsing
+ // required.
+ // @param data the data to initialize from
+ void Initialize(ReadableFontData* data);
+
+ // Checks that the glyph id is within the correct range.
+ // @return glyph_id if correct, -1 otherwise.
+ int32_t CheckGlyphRange(int32_t glyph_id);
+
+ int32_t LastGlyphIndex();
+
+ // Internal method to get the loca list if already generated and if not to
+ // initialize the state of the builder.
+ // @return the loca list
+ IntegerList* GetLocaList();
+
+ void ClearLoca(bool nullify);
+
+ int32_t format_version_; // Note: IndexToLocFormat
+ int32_t num_glyphs_;
+ IntegerList loca_;
+ };
+
+ virtual ~LocaTable();
+
+ int32_t format_version() { return format_version_; }
+ int32_t num_glyphs() { return num_glyphs_; }
+
+ // Return the offset for the given glyph id. Valid glyph ids are from 0 to the
+ // one less than the number of glyphs. The zero entry is the special entry for
+ // the notdef glyph. The final entry beyond the last glyph id is used to
+ // calculate the size of the last glyph.
+ // @param glyphId the glyph id to get the offset for; must be less than or
+ // equal to one more than the number of glyph ids
+ // @return the offset in the glyph table to the specified glyph id
+ int32_t GlyphOffset(int32_t glyph_id);
+
+ // Get the length of the data in the glyph table for the specified glyph id.
+ int32_t GlyphLength(int32_t glyph_id);
+
+ // Get the number of locations or locas. This will be one more than the number
+ // of glyphs for this table since the last loca position is used to indicate
+ // the size of the final glyph.
+ int32_t NumLocas();
+
+ // Get the value from the loca table for the index specified. Valid index
+ // values run from 0 to the number of glyphs in the font.
+ int32_t Loca(int32_t index);
+
+ private:
+ LocaTable(Header* header,
+ ReadableFontData* data,
+ int32_t format_version,
+ int32_t num_glyphs);
+
+ int32_t format_version_; // Note: Java's version, renamed to format_version_
+ int32_t num_glyphs_;
+
+ friend class LocaIterator;
+};
+typedef Ptr<LocaTable> LocaTablePtr;
+typedef Ptr<LocaTable::Builder> LocaTableBuilderPtr;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TABLE_TRUETYPE_LOCA_TABLE_H_
diff --git a/sfntly/tag.cc b/sfntly/tag.cc
new file mode 100644
index 0000000..c9d8c29
--- /dev/null
+++ b/sfntly/tag.cc
@@ -0,0 +1,110 @@
+/*
+ * 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/tag.h"
+#include "sfntly/port/endian.h"
+
+// Use a macro instead of GenerateTag() because gcc 4.4.3 creates static
+// initializers in that case.
+#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d);
+
+namespace sfntly {
+
+const int32_t Tag::ttcf = TAG('t', 't', 'c', 'f');
+const int32_t Tag::cmap = TAG('c', 'm', 'a', 'p');
+const int32_t Tag::head = TAG('h', 'e', 'a', 'd');
+const int32_t Tag::hhea = TAG('h', 'h', 'e', 'a');
+const int32_t Tag::hmtx = TAG('h', 'm', 't', 'x');
+const int32_t Tag::maxp = TAG('m', 'a', 'x', 'p');
+const int32_t Tag::name = TAG('n', 'a', 'm', 'e');
+const int32_t Tag::OS_2 = TAG('O', 'S', '/', '2');
+const int32_t Tag::post = TAG('p', 'o', 's', 't');
+const int32_t Tag::cvt = TAG('c', 'v', 't', ' ');
+const int32_t Tag::fpgm = TAG('f', 'p', 'g', 'm');
+const int32_t Tag::glyf = TAG('g', 'l', 'y', 'f');
+const int32_t Tag::loca = TAG('l', 'o', 'c', 'a');
+const int32_t Tag::prep = TAG('p', 'r', 'e', 'p');
+const int32_t Tag::CFF = TAG('C', 'F', 'F', ' ');
+const int32_t Tag::VORG = TAG('V', 'O', 'R', 'G');
+const int32_t Tag::EBDT = TAG('E', 'B', 'D', 'T');
+const int32_t Tag::EBLC = TAG('E', 'B', 'L', 'C');
+const int32_t Tag::EBSC = TAG('E', 'B', 'S', 'C');
+const int32_t Tag::BASE = TAG('B', 'A', 'S', 'E');
+const int32_t Tag::GDEF = TAG('G', 'D', 'E', 'F');
+const int32_t Tag::GPOS = TAG('G', 'P', 'O', 'S');
+const int32_t Tag::GSUB = TAG('G', 'S', 'U', 'B');
+const int32_t Tag::JSTF = TAG('J', 'S', 'T', 'F');
+const int32_t Tag::DSIG = TAG('D', 'S', 'I', 'G');
+const int32_t Tag::gasp = TAG('g', 'a', 's', 'p');
+const int32_t Tag::hdmx = TAG('h', 'd', 'm', 'x');
+const int32_t Tag::kern = TAG('k', 'e', 'r', 'n');
+const int32_t Tag::LTSH = TAG('L', 'T', 'S', 'H');
+const int32_t Tag::PCLT = TAG('P', 'C', 'L', 'T');
+const int32_t Tag::VDMX = TAG('V', 'D', 'M', 'X');
+const int32_t Tag::vhea = TAG('v', 'h', 'e', 'a');
+const int32_t Tag::vmtx = TAG('v', 'm', 't', 'x');
+const int32_t Tag::bsln = TAG('b', 's', 'l', 'n');
+const int32_t Tag::feat = TAG('f', 'e', 'a', 't');
+const int32_t Tag::lcar = TAG('l', 'c', 'a', 'r');
+const int32_t Tag::morx = TAG('m', 'o', 'r', 'x');
+const int32_t Tag::opbd = TAG('o', 'p', 'b', 'd');
+const int32_t Tag::prop = TAG('p', 'r', 'o', 'p');
+const int32_t Tag::Feat = TAG('F', 'e', 'a', 't');
+const int32_t Tag::Glat = TAG('G', 'l', 'a', 't');
+const int32_t Tag::Gloc = TAG('G', 'l', 'o', 'c');
+const int32_t Tag::Sile = TAG('S', 'i', 'l', 'e');
+const int32_t Tag::Silf = TAG('S', 'i', 'l', 'f');
+const int32_t Tag::bhed = TAG('b', 'h', 'e', 'd');
+const int32_t Tag::bdat = TAG('b', 'd', 'a', 't');
+const int32_t Tag::bloc = TAG('b', 'l', 'o', 'c');
+
+const int32_t CFF_TABLE_ORDERING[] = {
+ Tag::head,
+ Tag::hhea,
+ Tag::maxp,
+ Tag::OS_2,
+ Tag::name,
+ Tag::cmap,
+ Tag::post,
+ Tag::CFF };
+const size_t CFF_TABLE_ORDERING_SIZE =
+ sizeof(CFF_TABLE_ORDERING) / sizeof(int32_t);
+
+const int32_t TRUE_TYPE_TABLE_ORDERING[] = {
+ Tag::head,
+ Tag::hhea,
+ Tag::maxp,
+ Tag::OS_2,
+ Tag::hmtx,
+ Tag::LTSH,
+ Tag::VDMX,
+ Tag::hdmx,
+ Tag::cmap,
+ Tag::fpgm,
+ Tag::prep,
+ Tag::cvt,
+ Tag::loca,
+ Tag::glyf,
+ Tag::kern,
+ Tag::name,
+ Tag::post,
+ Tag::gasp,
+ Tag::PCLT,
+ Tag::DSIG };
+const size_t TRUE_TYPE_TABLE_ORDERING_SIZE =
+ sizeof(TRUE_TYPE_TABLE_ORDERING) / sizeof(int32_t);
+
+} // namespace sfntly
diff --git a/sfntly/tag.h b/sfntly/tag.h
new file mode 100644
index 0000000..0ecbab8
--- /dev/null
+++ b/sfntly/tag.h
@@ -0,0 +1,123 @@
+/*
+ * 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_SFNTLY_TAG_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TAG_H_
+
+#include <cstddef>
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+// Font identification tags used for tables, features, etc.
+// Tag names are consistent with the OpenType and sfnt specs.
+struct Tag {
+ static const int32_t ttcf;
+
+ // Table Type Tags
+ // required tables
+ static const int32_t cmap;
+ static const int32_t head;
+ static const int32_t hhea;
+ static const int32_t hmtx;
+ static const int32_t maxp;
+ static const int32_t name;
+ static const int32_t OS_2;
+ static const int32_t post;
+
+ // TrueType outline tables
+ static const int32_t cvt;
+ static const int32_t fpgm;
+ static const int32_t glyf;
+ static const int32_t loca;
+ static const int32_t prep;
+
+ // PostScript outline tables
+ static const int32_t CFF;
+ static const int32_t VORG;
+
+ // opentype bitmap glyph outlines
+ static const int32_t EBDT;
+ static const int32_t EBLC;
+ static const int32_t EBSC;
+
+ // advanced typographic features
+ static const int32_t BASE;
+ static const int32_t GDEF;
+ static const int32_t GPOS;
+ static const int32_t GSUB;
+ static const int32_t JSTF;
+
+ // other
+ static const int32_t DSIG;
+ static const int32_t gasp;
+ static const int32_t hdmx;
+ static const int32_t kern;
+ static const int32_t LTSH;
+ static const int32_t PCLT;
+ static const int32_t VDMX;
+ static const int32_t vhea;
+ static const int32_t vmtx;
+
+ // AAT tables
+ static const int32_t bsln;
+ static const int32_t feat;
+ static const int32_t lcar;
+ static const int32_t morx;
+ static const int32_t opbd;
+ static const int32_t prop;
+
+ // Graphite tables
+ static const int32_t Feat;
+ static const int32_t Glat;
+ static const int32_t Gloc;
+ static const int32_t Sile;
+ static const int32_t Silf;
+
+ // truetype bitmap font tables
+ static const int32_t bhed;
+ static const int32_t bdat;
+ static const int32_t bloc;
+};
+
+// Create integer tag value for human readable tag name.
+inline int32_t GenerateTag(int32_t a, int32_t b, int32_t c, int32_t d) {
+ return (a << 24) | (b << 16) | (c << 8) | d;
+}
+
+// Translate tag to human readable string.
+// The Caller must delete[] the returned value.
+inline char* TagToString(int32_t tag) {
+ char *name = new char[5];
+ name[0] = static_cast<char>((tag & 0xff000000) >> 24);
+ name[1] = static_cast<char>((tag & 0x00ff0000) >> 16);
+ name[2] = static_cast<char>((tag & 0x0000ff00) >> 8);
+ name[3] = static_cast<char>(tag & 0x000000ff);
+ name[4] = 0;
+ return name;
+}
+
+// Note: For Java, these two orderings are in Font class. Moved here to avoid
+// VC++ bug of not populating correct values.
+extern const int32_t CFF_TABLE_ORDERING[];
+extern const size_t CFF_TABLE_ORDERING_SIZE;
+extern const int32_t TRUE_TYPE_TABLE_ORDERING[];
+extern const size_t TRUE_TYPE_TABLE_ORDERING_SIZE;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TAG_H_
diff --git a/sfntly/tools/subsetter/glyph_table_subsetter.cc b/sfntly/tools/subsetter/glyph_table_subsetter.cc
new file mode 100644
index 0000000..b3d6b07
--- /dev/null
+++ b/sfntly/tools/subsetter/glyph_table_subsetter.cc
@@ -0,0 +1,90 @@
+/*
+ * 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/tools/subsetter/glyph_table_subsetter.h"
+
+#include "sfntly/table/truetype/glyph_table.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/tools/subsetter/subsetter.h"
+#include "sfntly/port/exception_type.h"
+
+namespace sfntly {
+
+const int32_t kGlyphTableSubsetterTags[2] = {Tag::glyf, Tag::loca};
+
+GlyphTableSubsetter::GlyphTableSubsetter()
+ : TableSubsetterImpl(kGlyphTableSubsetterTags, 2) {
+}
+
+GlyphTableSubsetter::~GlyphTableSubsetter() {}
+
+bool GlyphTableSubsetter::Subset(Subsetter* subsetter,
+ Font* font,
+ Font::Builder* font_builder) {
+ assert(font);
+ assert(subsetter);
+ assert(font_builder);
+
+ IntegerList* permutation_table = subsetter->GlyphPermutationTable();
+ if (!permutation_table || permutation_table->empty())
+ return false;
+
+ 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) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw RuntimeException("Font to subset is not valid.");
+#endif
+ 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) {
+#if !defined (SFNTLY_NO_EXCEPTION)
+ throw RuntimeException("Builder for subset is not valid.");
+#endif
+ return false;
+ }
+ GlyphTable::GlyphBuilderList* glyph_builders =
+ glyph_table_builder->GlyphBuilders();
+ for (IntegerList::iterator old_glyph_id = permutation_table->begin(),
+ old_glyph_id_end = permutation_table->end();
+ old_glyph_id != old_glyph_id_end; ++old_glyph_id) {
+ int old_offset = loca_table->GlyphOffset(*old_glyph_id);
+ int old_length = loca_table->GlyphLength(*old_glyph_id);
+ GlyphPtr glyph;
+ glyph.Attach(glyph_table->GetGlyph(old_offset, old_length));
+ 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);
+ }
+ IntegerList loca_list;
+ glyph_table_builder->GenerateLocaList(&loca_list);
+ loca_table_builder->SetLocaList(&loca_list);
+ return true;
+}
+
+} // namespace sfntly
diff --git a/sfntly/tools/subsetter/glyph_table_subsetter.h b/sfntly/tools/subsetter/glyph_table_subsetter.h
new file mode 100644
index 0000000..88c7044
--- /dev/null
+++ b/sfntly/tools/subsetter/glyph_table_subsetter.h
@@ -0,0 +1,37 @@
+/*
+ * 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_SFNTLY_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_
+
+#include "sfntly/tools/subsetter/table_subsetter_impl.h"
+
+namespace sfntly {
+
+class GlyphTableSubsetter : public TableSubsetterImpl,
+ public RefCounted<GlyphTableSubsetter> {
+ public:
+ GlyphTableSubsetter();
+ virtual ~GlyphTableSubsetter();
+
+ virtual bool Subset(Subsetter* subsetter,
+ Font* font,
+ Font::Builder* font_builder);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_GLYPH_TABLE_SUBSETTER_H_
diff --git a/sfntly/tools/subsetter/subsetter.cc b/sfntly/tools/subsetter/subsetter.cc
new file mode 100644
index 0000000..7d98779
--- /dev/null
+++ b/sfntly/tools/subsetter/subsetter.cc
@@ -0,0 +1,102 @@
+/*
+ * 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/tools/subsetter/subsetter.h"
+
+#include <algorithm>
+#include <iterator>
+
+#include "sfntly/tools/subsetter/glyph_table_subsetter.h"
+
+namespace sfntly {
+
+Subsetter::Subsetter(Font* font, FontFactory* font_factory) {
+ font_ = font;
+ font_factory_ = font_factory;
+ TableSubsetterPtr subsetter = new GlyphTableSubsetter();
+ // TODO(arthurhsu): IMPLEMENT: CMap table subsetter
+ table_subsetters_.push_back(subsetter);
+}
+
+Subsetter::~Subsetter() {
+ font_factory_.Release();
+ font_.Release();
+ table_subsetters_.clear();
+}
+
+void Subsetter::SetGlyphs(IntegerList* glyphs) {
+ new_to_old_glyphs_ = *glyphs;
+}
+
+void Subsetter::SetCMaps(CMapIdList* cmap_ids, int32_t number) {
+ UNREFERENCED_PARAMETER(cmap_ids);
+ UNREFERENCED_PARAMETER(number);
+ // TODO(arthurhsu): IMPLEMENT
+}
+
+void Subsetter::SetRemoveTables(IntegerSet* remove_tables) {
+ remove_tables_ = *remove_tables;
+}
+
+CALLER_ATTACH Font::Builder* Subsetter::Subset() {
+ FontBuilderPtr font_builder;
+ font_builder.Attach(font_factory_->NewFontBuilder());
+
+ IntegerSet table_tags;
+ for (TableMap::const_iterator i = font_->GetTableMap()->begin(),
+ e = font_->GetTableMap()->end(); i != e; ++i) {
+ table_tags.insert(i->first);
+ }
+ if (!remove_tables_.empty()) {
+ IntegerSet result;
+ std::set_difference(table_tags.begin(), table_tags.end(),
+ remove_tables_.begin(), remove_tables_.end(),
+ std::inserter(result, result.end()));
+ table_tags = result;
+ }
+ for (TableSubsetterList::iterator
+ table_subsetter = table_subsetters_.begin(),
+ table_subsetter_end = table_subsetters_.end();
+ table_subsetter != table_subsetter_end; ++table_subsetter) {
+ bool handled = (*table_subsetter)->Subset(this, font_, font_builder);
+ if (handled) {
+ IntegerSet* handled_tags = (*table_subsetter)->TagsHandled();
+ IntegerSet result;
+ std::set_difference(table_tags.begin(), table_tags.end(),
+ handled_tags->begin(), handled_tags->end(),
+ std::inserter(result, result.end()));
+ table_tags = result;
+ }
+ }
+ for (IntegerSet::iterator tag = table_tags.begin(),
+ tag_end = table_tags.end(); tag != tag_end; ++tag) {
+ Table* table = font_->GetTable(*tag);
+ if (table) {
+ font_builder->NewTableBuilder(*tag, table->ReadFontData());
+ }
+ }
+ return font_builder.Detach();
+}
+
+IntegerList* Subsetter::GlyphPermutationTable() {
+ return &new_to_old_glyphs_;
+}
+
+CMapIdList* Subsetter::CMapId() {
+ return &cmap_ids_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/tools/subsetter/subsetter.h b/sfntly/tools/subsetter/subsetter.h
new file mode 100644
index 0000000..85940a7
--- /dev/null
+++ b/sfntly/tools/subsetter/subsetter.h
@@ -0,0 +1,72 @@
+/*
+ * 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_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_
+
+#include <vector>
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/tools/subsetter/table_subsetter.h"
+
+namespace sfntly {
+
+class Subsetter : public RefCounted<Subsetter> {
+ public:
+ Subsetter(Font* font, FontFactory* font_factory);
+ virtual ~Subsetter();
+
+ virtual void SetGlyphs(IntegerList* glyphs);
+
+ // Set the cmaps to be used in the subsetted font. The cmaps are listed in
+ // order of priority and the number parameter gives a count of how many of the
+ // list should be put into the subsetted font. If there are no matches in the
+ // font for any of the provided cmap ids which would lead to a font with no
+ // cmap then an error will be thrown during subsetting.
+ // The two most common cases would be: <list>
+ // * a list of one or more cmap ids with a count setting of 1
+ // This will use the list of cmap ids as an ordered priority and look for
+ // an available cmap in the font that matches the requests. Only the first
+ // such match will be placed in the subsetted font.
+ // * a list of one or more cmap ids with a count setting equal to the list
+ // length
+ // This will use the list of cmap ids and try to place each one specified
+ // into the subsetted font.
+ // @param cmapIds the cmap ids to use for the subsetted font
+ // @param number the maximum number of cmaps to place in the subsetted font
+ virtual void SetCMaps(CMapIdList* cmap_ids, int32_t number);
+
+ virtual void SetRemoveTables(IntegerSet* remove_tables);
+ virtual CALLER_ATTACH Font::Builder* Subset();
+ virtual IntegerList* GlyphPermutationTable();
+ virtual CMapIdList* CMapId();
+
+ private:
+ FontPtr font_;
+ FontFactoryPtr font_factory_;
+ TableSubsetterList table_subsetters_;
+
+ // Settings from user
+ IntegerSet remove_tables_;
+ IntegerList new_to_old_glyphs_;
+ CMapIdList cmap_ids_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_SUBSETTER_H_
diff --git a/sfntly/tools/subsetter/table_subsetter.h b/sfntly/tools/subsetter/table_subsetter.h
new file mode 100644
index 0000000..1336615
--- /dev/null
+++ b/sfntly/tools/subsetter/table_subsetter.h
@@ -0,0 +1,39 @@
+/*
+ * 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_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_
+
+#include <vector>
+
+#include "sfntly/font.h"
+
+namespace sfntly {
+
+class Subsetter;
+class TableSubsetter : virtual public RefCount {
+ public:
+ virtual IntegerSet* TagsHandled() = 0;
+ virtual bool TagHandled(int32_t tag) = 0;
+ virtual bool Subset(Subsetter* subsetter, Font* font,
+ Font::Builder* font_builder) = 0;
+};
+typedef Ptr<TableSubsetter> TableSubsetterPtr;
+typedef std::vector<TableSubsetterPtr> TableSubsetterList;
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_H_
diff --git a/sfntly/tools/subsetter/table_subsetter_impl.cc b/sfntly/tools/subsetter/table_subsetter_impl.cc
new file mode 100644
index 0000000..f239c78
--- /dev/null
+++ b/sfntly/tools/subsetter/table_subsetter_impl.cc
@@ -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.
+ */
+
+#include "sfntly/tools/subsetter/table_subsetter_impl.h"
+
+namespace sfntly {
+
+TableSubsetterImpl::TableSubsetterImpl(const int32_t* tags,
+ size_t tags_length) {
+ for (size_t i = 0; i < tags_length; ++i) {
+ tags_.insert(tags[i]);
+ }
+}
+
+TableSubsetterImpl::~TableSubsetterImpl() {}
+
+bool TableSubsetterImpl::TagHandled(int32_t tag) {
+ return tags_.find(tag) != tags_.end();
+}
+
+IntegerSet* TableSubsetterImpl::TagsHandled() {
+ return &tags_;
+}
+
+} // namespace sfntly
diff --git a/sfntly/tools/subsetter/table_subsetter_impl.h b/sfntly/tools/subsetter/table_subsetter_impl.h
new file mode 100644
index 0000000..de0d9a9
--- /dev/null
+++ b/sfntly/tools/subsetter/table_subsetter_impl.h
@@ -0,0 +1,37 @@
+/*
+ * 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_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_
+#define SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_
+
+#include "sfntly/tools/subsetter/table_subsetter.h"
+
+namespace sfntly {
+
+class TableSubsetterImpl : public TableSubsetter {
+ public:
+ TableSubsetterImpl(const int32_t* tags, size_t tags_length);
+ virtual ~TableSubsetterImpl();
+ virtual bool TagHandled(int32_t tag);
+ virtual IntegerSet* TagsHandled();
+
+ protected:
+ IntegerSet tags_;
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_SFNTLY_TOOLS_SUBSETTER_TABLE_SUBSETTER_IMPL_H_
diff --git a/test/autogenerated/cmap_basic_test.cc b/test/autogenerated/cmap_basic_test.cc
new file mode 100644
index 0000000..aae35e1
--- /dev/null
+++ b/test/autogenerated/cmap_basic_test.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.
+ */
+
+// type.h needs to be included first because of building issues on Windows
+// Type aliases we delcare are defined in other headers and make the build
+// fail otherwise.
+#include "sfntly/port/type.h"
+#include <assert.h>
+#include <stdio.h>
+#include <unicode/ucnv.h>
+
+#include <iostream>
+#include <string>
+
+#include "gtest/gtest.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/tag.h"
+#include "test/autogenerated/cmap_test_data.h"
+#include "test/test_font_utils.h"
+#include "test/test_utils.h"
+#include "test/test_xml_utils.h"
+
+namespace sfntly {
+
+#if GTEST_HAS_PARAM_TEST
+
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+class CMapBasicTests : public :: testing::TestWithParam<const char*> {
+ public:
+ CMapBasicTests() {}
+ virtual void SetUp();
+ virtual void TearDown() {}
+
+ Ptr<CMapTable> cmap_table_;
+ TiXmlDocument document_;
+};
+
+void CMapBasicTests::SetUp() {
+ // Loading the font
+ Ptr<FontFactory> font_factory;
+ font_factory.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ std::string font_name = "../../";
+#if defined (WIN32)
+ font_name += "../";
+#endif
+ font_name += std::string(GetParam());
+ LoadFont(font_name.c_str(), font_factory, &font_array);
+ ASSERT_FALSE(font_array.empty());
+ Ptr<Font> font = font_array.at(0);
+ ASSERT_NE(font, reinterpret_cast<Font*>(NULL));
+ cmap_table_ = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ if (!cmap_table_)
+ fprintf(stderr, "No CMap: %s\n", font_name.c_str());
+ ASSERT_NE(cmap_table_, reinterpret_cast<CMapTable*>(NULL));
+
+ // Loading the XML file
+ document_ = TiXmlDocument((font_name + ".xml").c_str());
+ ASSERT_TRUE(document_.LoadFile());
+}
+
+TEST_P(CMapBasicTests, BasicTest) {
+ TiXmlNodeVector* cmap_table = GetNodesWithName(&document_, "cmap_table");
+ // A font can only have one CMap table
+ ASSERT_EQ(cmap_table->size(), (size_t)1);
+ TiXmlNodeVector* cmaps = GetNodesWithName(cmap_table->at(0), "cmap");
+ const TiXmlAttribute* num_cmaps_attr = GetAttribute(cmap_table->at(0),
+ "num_cmaps");
+ ASSERT_NE(num_cmaps_attr, reinterpret_cast<TiXmlAttribute*>(NULL));
+ // But there may be more than one CMap in this table
+ ASSERT_LE(cmaps->size(), (size_t)num_cmaps_attr->IntValue());
+ for (TiXmlNodeVector::iterator it = cmaps->begin();
+ it != cmaps->end(); ++it) {
+ int32_t platform_id = GetAttribute(*it, "platform_id")->IntValue();
+ int32_t encoding_id = GetAttribute(*it, "encoding_id")->IntValue();
+ Ptr<CMapTable::CMap> cmap;
+ cmap.Attach(cmap_table_->GetCMap(platform_id, encoding_id));
+ if (!cmap) {
+ fprintf(stderr, "Cannot test unsupported CMapFormat%d\n",
+ GetAttribute(*it, "format")->IntValue());
+ continue;
+ }
+ ASSERT_EQ(cmap->platform_id(), platform_id);
+ ASSERT_EQ(cmap->encoding_id(), encoding_id);
+ TiXmlNodeVector* maps = GetNodesWithName(*it, "map");
+ for (TiXmlNodeVector::iterator jt = maps->begin();
+ jt != maps->end(); ++jt) {
+ int32_t character;
+#if defined (WIN32)
+ sscanf_s(GetAttribute(*jt, "char")->Value(), "%x", &character);
+#else
+ sscanf(GetAttribute(*jt, "char")->Value(), "%x", &character);
+#endif
+ int32_t glyph_id = GetAttribute(*jt, "gid")->IntValue();
+ ASSERT_EQ(cmap->GlyphId(character), glyph_id);
+ }
+ delete maps;
+ }
+ delete cmaps;
+ delete cmap_table;
+}
+
+INSTANTIATE_TEST_CASE_P(CMapBasicTests,
+ CMapBasicTests,
+ ::testing::ValuesIn(cmap_test_data::kAllTests));
+
+#else
+
+TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}
+
+#endif // GTEST_HAS_PARAM
+}
diff --git a/test/autogenerated/cmap_test_data.h b/test/autogenerated/cmap_test_data.h
new file mode 100644
index 0000000..4a9f267
--- /dev/null
+++ b/test/autogenerated/cmap_test_data.h
@@ -0,0 +1,185 @@
+/*
+ * !!! DO NOT EDIT !!!
+ * THIS FILE IS GENERATED BY A SCRIPT.
+ * FOR MORE DETAILS SEE 'README-test_data.txt'.
+ */
+
+/*
+ * 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_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_
+#define TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+namespace cmap_test_data {
+const char* kAllTests[] = {
+ "data/fonts/cousine/Cousine-BoldItalic.ttf",
+ "data/fonts/cousine/Cousine-Bold.ttf",
+ "data/fonts/cousine/Cousine-Regular.ttf",
+ "data/fonts/cousine/Cousine-Italic.ttf",
+ "data/fonts/cedarvillecursive/Cedarville-Cursive.ttf",
+ "data/fonts/dancingscript/DancingScript-Bold.ttf",
+ "data/fonts/dancingscript/DancingScript-Regular.ttf",
+ "data/fonts/damion/Damion-Regular.ttf",
+ "data/fonts/annieuseyourtelescope/AnnieUseYourTelescope.ttf",
+ "data/fonts/deliusswashcaps/DeliusSwashCaps-Regular.ttf",
+ "data/fonts/delius/Delius-Regular.ttf",
+ "data/fonts/coveredbyyourgrace/CoveredByYourGrace.ttf",
+ "data/fonts/ebgaramond/EBGaramond-Regular.ttf",
+ "data/fonts/cabinsketch/CabinSketch-Bold.ttf",
+ "data/fonts/allan/Allan-Bold.ttf",
+ "data/fonts/coda/Coda-Caption-Heavy.ttf",
+ "data/fonts/coda/Coda-Heavy.ttf",
+ "data/fonts/forum/Forum-Regular.ttf",
+ "data/fonts/alike/Alike-Regular.ttf",
+ "data/fonts/corben/Corben-Bold.ttf",
+ "data/fonts/caudex/Caudex-Regular.ttf",
+ "data/fonts/caudex/Caudex-BoldItalic.ttf",
+ "data/fonts/caudex/Caudex-Italic.ttf",
+ "data/fonts/caudex/Caudex-Bold.ttf",
+ "data/fonts/cabin/Cabin-MediumItalic.ttf",
+ "data/fonts/cabin/Cabin-SemiBold.ttf",
+ "data/fonts/cabin/Cabin-SemiBoldItalic.ttf",
+ "data/fonts/cabin/Cabin-BoldItalic.ttf",
+ "data/fonts/cabin/Cabin-Medium.ttf",
+ "data/fonts/cabin/Cabin-Italic.ttf",
+ "data/fonts/cabin/Cabin-Bold.ttf",
+ "data/fonts/cabin/Cabin-Regular.ttf",
+ "data/fonts/dawningofanewday/DawningofaNewDay.ttf",
+ "data/fonts/dangrek/Dangrek.ttf",
+ "data/fonts/blackopsone/BlackOpsOne.ttf",
+ "data/fonts/francoisone/FrancoisOne.ttf",
+ "data/fonts/bowlbyone/BowlbyOne.ttf",
+ "data/fonts/bowlbyone/BowlbyOneSC.ttf",
+ "data/fonts/calligraffiti/Calligraffiti.ttf",
+ "data/fonts/bangers/Bangers.ttf",
+ "data/fonts/astloch/Astloch-Regular.ttf",
+ "data/fonts/astloch/Astloch-Bold.ttf",
+ "data/fonts/angkor/Angkor.ttf",
+ "data/fonts/abrilfatface/AbrilFatface-Regular.ttf",
+ "data/fonts/cantarell/Cantarell-BoldOblique.ttf",
+ "data/fonts/cantarell/Cantarell-Oblique.ttf",
+ "data/fonts/cantarell/Cantarell-Bold.ttf",
+ "data/fonts/cantarell/Cantarell-Regular.ttf",
+ "data/fonts/arvo/Arvo-Bold.ttf",
+ "data/fonts/arvo/Arvo-Italic.ttf",
+ "data/fonts/arvo/Arvo-BoldItalic.ttf",
+ "data/fonts/arvo/Arvo-Regular.ttf",
+ "data/fonts/chewy/Chewy.ttf",
+ "data/fonts/bigshotone/BigshotOne.ttf",
+ "data/fonts/chenla/Chenla.ttf",
+ "data/fonts/bayon/Bayon.ttf",
+ "data/fonts/coustard/Coustard-Black.ttf",
+ "data/fonts/coustard/Coustard-Regular.ttf",
+ "data/fonts/amaticsc/AmaticSC-Bold.ttf",
+ "data/fonts/amaticsc/AmaticSC-Regular.ttf",
+ "data/fonts/comfortaa/Comfortaa-Light.ttf",
+ "data/fonts/comfortaa/Comfortaa-Bold.ttf",
+ "data/fonts/comfortaa/Comfortaa-Regular.ttf",
+ "data/fonts/expletussans/ExpletusSans-Bold.ttf",
+ "data/fonts/expletussans/ExpletusSans-MediumItalic.ttf",
+ "data/fonts/expletussans/ExpletusSans-Medium.ttf",
+ "data/fonts/expletussans/ExpletusSans-SemiBold.ttf",
+ "data/fonts/expletussans/ExpletusSans-Regular.ttf",
+ "data/fonts/expletussans/ExpletusSans-Italic.ttf",
+ "data/fonts/expletussans/ExpletusSans-SemiBoldItalic.ttf",
+ "data/fonts/expletussans/ExpletusSans-BoldItalic.ttf",
+ "data/fonts/aubrey/Aubrey-Regular.ttf",
+ "data/fonts/antic/Antic-Regular.ttf",
+ "data/fonts/copse/Copse-Regular.ttf",
+ "data/fonts/daysone/DaysOne-Regular.ttf",
+ "data/fonts/actor/Actor-Regular.ttf",
+ "data/fonts/bentham/Bentham-Regular.ttf",
+ "data/fonts/federo/Federo-Regular.ttf",
+ "data/fonts/arimo/Arimo-Italic.ttf",
+ "data/fonts/arimo/Arimo-BoldItalic.ttf",
+ "data/fonts/arimo/Arimo-Bold.ttf",
+ "data/fonts/arimo/Arimo-Regular.ttf",
+ "data/fonts/felltypes/IMFeDPsc28P.ttf",
+ "data/fonts/felltypes/IMFeGPrm28P.ttf",
+ "data/fonts/felltypes/IMFeENsc28P.ttf",
+ "data/fonts/felltypes/IMFePIit28P.ttf",
+ "data/fonts/felltypes/IMFeDPrm28P.ttf",
+ "data/fonts/felltypes/IMFePIrm28P.ttf",
+ "data/fonts/felltypes/IMFeFCrm28P.ttf",
+ "data/fonts/felltypes/IMFeGPit28P.ttf",
+ "data/fonts/felltypes/IMFeENit28P.ttf",
+ "data/fonts/felltypes/IMFeDPit28P.ttf",
+ "data/fonts/felltypes/IMFeENrm28P.ttf",
+ "data/fonts/felltypes/IMFeGPsc28P.ttf",
+ "data/fonts/felltypes/IMFePIsc28P.ttf",
+ "data/fonts/felltypes/IMFeFCsc28P.ttf",
+ "data/fonts/felltypes/IMFeFCit28P.ttf",
+ "data/fonts/bokor/Bokor.ttf",
+ "data/fonts/didactgothic/DidactGothic.ttf",
+ "data/fonts/allerta/Allerta-Medium.ttf",
+ "data/fonts/allerta/Allerta-Stencil.ttf",
+ "data/fonts/buda/Buda-Light.ttf",
+ "data/fonts/brawler/Brawler-Regular.ttf",
+ "data/fonts/carterone/CarterOne.ttf",
+ "data/fonts/candal/Candal.ttf",
+ "data/fonts/dorsa/Dorsa-Regular.ttf",
+ "data/fonts/crimson/CrimsonText-BoldItalic.ttf",
+ "data/fonts/crimson/CrimsonText-Bold.ttf",
+ "data/fonts/crimson/CrimsonText-Italic.ttf",
+ "data/fonts/crimson/CrimsonText-Roman.ttf",
+ "data/fonts/crimson/CrimsonText-Semibold.ttf",
+ "data/fonts/crimson/CrimsonText-SemiboldItalic.ttf",
+ "data/fonts/amaranth/Amaranth-Italic.ttf",
+ "data/fonts/amaranth/Amaranth-Bold.ttf",
+ "data/fonts/amaranth/Amaranth-Regular.ttf",
+ "data/fonts/amaranth/Amaranth-BoldItalic.ttf",
+ "data/fonts/crushed/Crushed.ttf",
+ "data/fonts/adamina/Adamina-Regular.ttf",
+ "data/fonts/aldrich/Aldrich-Regular.ttf",
+ "data/fonts/fanwoodtext/FanwoodText-Italic.ttf",
+ "data/fonts/fanwoodtext/FanwoodText-Regular.ttf",
+ "data/fonts/anonymouspro/AnonymousPro-Regular.ttf",
+ "data/fonts/anonymouspro/AnonymousPro-Bold.ttf",
+ "data/fonts/anonymouspro/AnonymousPro-Italic.ttf",
+ "data/fonts/anonymouspro/AnonymousPro-BoldItalic.ttf",
+ "data/fonts/bevan/Bevan.ttf",
+ "data/fonts/artifika/Artifika-Regular.ttf",
+ "data/fonts/anton/Anton.ttf",
+ "data/fonts/battambang/Battambang-Regular.ttf",
+ "data/fonts/battambang/Battambang-Bold.ttf",
+ "data/fonts/carme/Carme-Regular.ttf",
+ "data/fonts/cherrycreamsoda/CherryCreamSoda.ttf",
+ "data/fonts/deliusunicase/DeliusUnicase-Regular.ttf",
+ "data/fonts/comingsoon/ComingSoon.ttf",
+ "data/fonts/freehand/Freehand.ttf",
+ "data/fonts/alice/Alice-Regular.ttf",
+ "data/fonts/contrail/Contrail-Regular.ttf",
+ "data/fonts/cardo/Cardo-Bold.ttf",
+ "data/fonts/cardo/Cardo-Italic.ttf",
+ "data/fonts/cardo/Cardo-Regular.ttf",
+ "data/fonts/asset/Asset.ttf",
+ "data/fonts/changaone/ChangaOne-Regular.ttf",
+ "data/fonts/aclonica/Aclonica.ttf",
+ "data/fonts/craftygirls/CraftyGirls.ttf",
+ "data/fonts/architectsdaughter/ArchitectsDaughter.ttf",
+ "data/fonts/content/Content-Bold.ttf",
+ "data/fonts/content/Content-Regular.ttf",
+ "data/fonts/abel/Abel-Regular.ttf",
+ "data/fonts/cuprum/Cuprum.ttf",
+ "data/fonts/andika/Andika-R.ttf"
+};
+} // namespace cmap_test_data
+} // namespace sfntly
+
+#endif // TYPOGRAPHY_FONT_SFNTLY_SRC_TEST_AUTOGENERATED_CMAP_TEST_DATA_H_
diff --git a/test/bitmap_table_test.cc b/test/bitmap_table_test.cc
new file mode 100644
index 0000000..df3ffc0
--- /dev/null
+++ b/test/bitmap_table_test.cc
@@ -0,0 +1,209 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "sfntly/table/bitmap/ebdt_table.h"
+#include "sfntly/table/bitmap/eblc_table.h"
+#include "sfntly/table/bitmap/index_sub_table_format3.h"
+#include "sfntly/table/bitmap/index_sub_table_format4.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+const int32_t NUM_STRIKES = 4;
+const int32_t STRIKE1_ARRAY_OFFSET = 0xc8;
+const int32_t STRIKE1_INDEX_TABLE_SIZE = 0x4f4;
+const int32_t STRIKE1_NUM_INDEX_TABLES = 1;
+const int32_t STRIKE1_COLOR_REF = 0;
+const int32_t STRIKE1_START_GLYPH_INDEX = 0;
+const int32_t STRIKE1_END_GLYPH_INDEX = 623;
+const int32_t STRIKE1_PPEM_X = 10;
+const int32_t STRIKE1_PPEM_Y = 10;
+const int32_t STRIKE1_BIT_DEPTH = 1;
+const int32_t STRIKE1_FLAGS = 0x01;
+
+const int32_t STRIKE4_SUB1_INDEX_FORMAT = 3;
+const int32_t STRIKE4_SUB1_IMAGE_FORMAT = 1;
+const int32_t STRIKE4_SUB1_IMAGE_DATA_OFFSET = 0x00005893;
+const int32_t STRIKE4_SUB1_GLYPH_OFFSET[] = {
+ 0x00005893, 0x00005898, 0x0000589d, 0x000058a2, 0x000058a7,
+ 0x000058b2, 0x000058c2, 0x000058d0, 0x000058de, 0x000058e6 };
+const int32_t NUM_STRIKE4_SUB1_GLYPH_OFFSET = 10;
+const int32_t STRIKE4_SUB1_GLYPH2_LENGTH = 0x58a2 - 0x589d;
+
+bool CommonReadingTest(Font* raw_font) {
+ FontPtr font = raw_font;
+
+ EblcTablePtr bitmap_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC));
+ EbdtTablePtr bitmap_table = down_cast<EbdtTable*>(font->GetTable(Tag::EBDT));
+
+ EXPECT_FALSE(bitmap_loca == NULL);
+ EXPECT_FALSE(bitmap_table == NULL);
+
+ EXPECT_EQ(bitmap_loca->NumSizes(), NUM_STRIKES);
+
+ // Strike 1
+ BitmapSizeTablePtr strike1 = bitmap_loca->GetBitmapSizeTable(0);
+ EXPECT_FALSE(strike1 == NULL);
+ EXPECT_EQ(strike1->IndexSubTableArrayOffset(), STRIKE1_ARRAY_OFFSET);
+ EXPECT_EQ(strike1->NumberOfIndexSubTables(), STRIKE1_NUM_INDEX_TABLES);
+ EXPECT_EQ(strike1->ColorRef(), STRIKE1_COLOR_REF);
+ EXPECT_EQ(strike1->StartGlyphIndex(), STRIKE1_START_GLYPH_INDEX);
+ EXPECT_EQ(strike1->EndGlyphIndex(), STRIKE1_END_GLYPH_INDEX);
+ EXPECT_EQ(strike1->PpemX(), STRIKE1_PPEM_X);
+ EXPECT_EQ(strike1->PpemY(), STRIKE1_PPEM_Y);
+ EXPECT_EQ(strike1->BitDepth(), STRIKE1_BIT_DEPTH);
+ EXPECT_EQ(strike1->FlagsAsInt(), STRIKE1_FLAGS);
+
+ // Strike 4
+ // In this test font, all strikes and all subtables have same glyphs.
+ BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3);
+ EXPECT_FALSE(strike4 == NULL);
+ EXPECT_EQ(strike4->StartGlyphIndex(), STRIKE1_START_GLYPH_INDEX);
+ EXPECT_EQ(strike4->EndGlyphIndex(), STRIKE1_END_GLYPH_INDEX);
+ IndexSubTablePtr sub1 = strike4->GetIndexSubTable(0);
+ EXPECT_FALSE(sub1 == NULL);
+ EXPECT_EQ(sub1->image_format(), STRIKE4_SUB1_IMAGE_FORMAT);
+ EXPECT_EQ(sub1->first_glyph_index(), STRIKE1_START_GLYPH_INDEX);
+ EXPECT_EQ(sub1->last_glyph_index(), STRIKE1_END_GLYPH_INDEX);
+ EXPECT_EQ(sub1->image_data_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET);
+
+ for (int32_t i = 0; i < NUM_STRIKE4_SUB1_GLYPH_OFFSET; ++i) {
+ EXPECT_EQ(sub1->GlyphOffset(i), STRIKE4_SUB1_GLYPH_OFFSET[i]);
+ }
+ return true;
+}
+
+bool TestReadingBitmapTable() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ LoadFont(SAMPLE_BITMAP_FONT, factory, &font_array);
+ FontPtr font = font_array[0];
+ EXPECT_TRUE(CommonReadingTest(font));
+
+ EblcTablePtr bitmap_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC));
+ BitmapSizeTablePtr strike1 = bitmap_loca->GetBitmapSizeTable(0);
+ BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3);
+ IndexSubTablePtr sub1 = strike4->GetIndexSubTable(0);
+
+ EXPECT_EQ(strike1->IndexTableSize(), STRIKE1_INDEX_TABLE_SIZE);
+ EXPECT_EQ(sub1->index_format(), STRIKE4_SUB1_INDEX_FORMAT);
+
+ // Strike 4 Index Sub Table 1 is a Format 3
+ IndexSubTableFormat3Ptr sub3 =
+ down_cast<IndexSubTableFormat3*>(strike4->GetIndexSubTable(0));
+ EXPECT_FALSE(sub3 == NULL);
+ BitmapGlyphInfoPtr info;
+ info.Attach(sub3->GlyphInfo(2));
+ EXPECT_EQ(info->glyph_id(), 2);
+ EXPECT_EQ(info->block_offset(), STRIKE4_SUB1_IMAGE_DATA_OFFSET);
+ EXPECT_EQ(info->start_offset(),
+ STRIKE4_SUB1_GLYPH_OFFSET[2] - STRIKE4_SUB1_GLYPH_OFFSET[0]);
+ EXPECT_EQ(info->format(), STRIKE4_SUB1_IMAGE_FORMAT);
+ EXPECT_EQ(info->length(), STRIKE4_SUB1_GLYPH2_LENGTH);
+
+ return true;
+}
+
+// Function in subset_impl.cc
+extern
+void SubsetEBLC(EblcTable::Builder* eblc, const BitmapLocaList& new_loca);
+
+bool TestIndexFormatConversion() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray builder_array;
+ BuilderForFontFile(SAMPLE_BITMAP_FONT, factory, &builder_array);
+
+ FontBuilderPtr font_builder;
+ font_builder = builder_array[0];
+ EblcTableBuilderPtr eblc_builder =
+ down_cast<EblcTable::Builder*>(font_builder->GetTableBuilder(Tag::EBLC));
+ BitmapLocaList new_loca;
+ eblc_builder->GenerateLocaList(&new_loca);
+ SubsetEBLC(eblc_builder, new_loca); // Format 3 -> 4
+
+ FontPtr new_font;
+ new_font.Attach(font_builder->Build());
+
+ // Serialize and reload the serialized font.
+ MemoryOutputStream os;
+ factory->SerializeFont(new_font, &os);
+
+#if defined (SFNTLY_DEBUG_BITMAP)
+ SerializeToFile(&os, "anon-mod.ttf");
+#endif
+
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ FontArray font_array;
+ factory->LoadFonts(&is, &font_array);
+ new_font = font_array[0];
+
+ EXPECT_TRUE(CommonReadingTest(new_font));
+
+ // Strike 4 Index Sub Table 1 is a Format 4
+ EblcTablePtr bitmap_loca =
+ down_cast<EblcTable*>(new_font->GetTable(Tag::EBLC));
+ BitmapSizeTablePtr strike4 = bitmap_loca->GetBitmapSizeTable(3);
+ IndexSubTableFormat4Ptr sub4 =
+ down_cast<IndexSubTableFormat4*>(strike4->GetIndexSubTable(0));
+ EXPECT_FALSE(sub4 == NULL);
+
+ // And this subtable shall have exactly the same offset as original table
+ // since no subsetting happens.
+ FontArray original_font_array;
+ LoadFont(SAMPLE_BITMAP_FONT, factory, &original_font_array);
+ FontPtr font = original_font_array[0];
+ EXPECT_FALSE(font == NULL);
+ EblcTablePtr original_loca = down_cast<EblcTable*>(font->GetTable(Tag::EBLC));
+ EXPECT_FALSE(original_loca == NULL);
+ BitmapSizeTablePtr original_strike4 = bitmap_loca->GetBitmapSizeTable(3);
+ EXPECT_FALSE(original_strike4 == NULL);
+ IndexSubTableFormat3Ptr sub3 =
+ down_cast<IndexSubTableFormat3*>(strike4->GetIndexSubTable(0));
+ EXPECT_FALSE(sub3 == NULL);
+ EXPECT_EQ(strike4->StartGlyphIndex(), original_strike4->StartGlyphIndex());
+ EXPECT_EQ(strike4->EndGlyphIndex(), original_strike4->EndGlyphIndex());
+ for (int32_t i = strike4->StartGlyphIndex();
+ i <= strike4->EndGlyphIndex(); ++i) {
+ BitmapGlyphInfoPtr info, original_info;
+ info.Attach(sub4->GlyphInfo(i));
+ original_info.Attach(sub3->GlyphInfo(i));
+ EXPECT_EQ(info->format(), original_info->format());
+ EXPECT_EQ(info->glyph_id(), original_info->glyph_id());
+ EXPECT_EQ(info->length(), original_info->length());
+ EXPECT_EQ(info->offset(), original_info->offset());
+ }
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(BitmapTable, Reading) {
+ ASSERT_TRUE(sfntly::TestReadingBitmapTable());
+}
+
+TEST(BitmapTable, IndexFormatConversion) {
+ ASSERT_TRUE(sfntly::TestIndexFormatConversion());
+}
diff --git a/test/byte_array_test.cc b/test/byte_array_test.cc
new file mode 100644
index 0000000..40a6489
--- /dev/null
+++ b/test/byte_array_test.cc
@@ -0,0 +1,146 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+
+namespace sfntly {
+namespace byte_array_test {
+
+const int32_t BYTE_ARRAY_SIZES[] =
+ {1, 7, 127, 128, 129, 255, 256, 257, 666, 1023, 10000, 0xffff, 0x10000};
+
+void FillTestByteArray(ByteArray* ba, int32_t size) {
+ for (int32_t i = 0; i < size; ++i) {
+ ba->Put(i, (byte_t)(i % 256));
+ }
+}
+
+void ReadByteArrayWithBuffer(ByteArray* ba, ByteVector* buffer, ByteVector* b) {
+ b->resize(ba->Length());
+ int32_t index = 0;
+ while (index < ba->Length()) {
+ int32_t bytes_read = ba->Get(index, buffer);
+ std::copy(buffer->begin(), buffer->begin() + bytes_read,
+ b->begin() + index);
+ index += bytes_read;
+ }
+}
+
+void ReadByteArrayWithSlidingWindow(ByteArray* ba, int window_size,
+ ByteVector* b) {
+ b->resize(ba->Length());
+ int32_t index = 0;
+ int32_t actual_window_size = window_size;
+ while (index < ba->Length()) {
+ actual_window_size =
+ std::min<int32_t>(actual_window_size, b->size() - index);
+ int32_t bytes_read = ba->Get(index, &((*b)[0]), index, actual_window_size);
+ index += bytes_read;
+ }
+}
+
+bool ReadComparison(ByteArray* ba1, ByteArray* ba2) {
+ // single byte reads
+ for (int i = 0; i < ba1->Length(); ++i) {
+ EXPECT_EQ(ba1->Get(i), ba2->Get(i));
+ }
+
+ ByteVector b1, b2;
+ // buffer reads
+ int increments = std::max<int32_t>(ba1->Length() / 11, 1);
+ for (int buffer_size = 1; buffer_size < ba1->Length();
+ buffer_size += increments) {
+ ByteVector buffer(buffer_size);
+ ReadByteArrayWithBuffer(ba1, &buffer, &b1);
+ ReadByteArrayWithBuffer(ba2, &buffer, &b2);
+ EXPECT_GT(b1.size(), static_cast<size_t>(0));
+ EXPECT_EQ(b1.size(), b2.size());
+ EXPECT_TRUE(std::equal(b1.begin(), b1.end(), b2.begin()));
+ }
+
+ // sliding window reads
+ b1.clear();
+ b2.clear();
+ for (int window_size = 1; window_size < ba1->Length();
+ window_size += increments) {
+ ReadByteArrayWithSlidingWindow(ba1, window_size, &b1);
+ ReadByteArrayWithSlidingWindow(ba2, window_size, &b2);
+ EXPECT_GT(b1.size(), static_cast<size_t>(0));
+ EXPECT_EQ(b1.size(), b2.size());
+ EXPECT_TRUE(std::equal(b1.begin(), b1.end(), b2.begin()));
+ }
+
+ return true;
+}
+
+bool CopyTest(ByteArray* ba) {
+ ByteArrayPtr fixed_copy = new MemoryByteArray(ba->Length());
+ ba->CopyTo(fixed_copy);
+ EXPECT_EQ(ba->Length(), fixed_copy->Length());
+ EXPECT_TRUE(ReadComparison(ba, fixed_copy));
+
+ ByteArrayPtr growable_copy = new GrowableMemoryByteArray();
+ ba->CopyTo(growable_copy);
+ EXPECT_EQ(ba->Length(), growable_copy->Length());
+ EXPECT_TRUE(ReadComparison(ba, growable_copy));
+
+ return true;
+}
+
+bool ByteArrayTester(ByteArray* ba) {
+ return CopyTest(ba);
+}
+
+} // namespace byte_array_test
+
+bool TestMemoryByteArray() {
+ fprintf(stderr, "fixed mem: size ");
+ for (size_t i = 0;
+ i < sizeof(byte_array_test::BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) {
+ int32_t size = byte_array_test::BYTE_ARRAY_SIZES[i];
+ fprintf(stderr, "%d ", size);
+ ByteArrayPtr ba = new MemoryByteArray(size);
+ byte_array_test::FillTestByteArray(ba, size);
+ EXPECT_TRUE(byte_array_test::ByteArrayTester(ba));
+ }
+ fprintf(stderr, "\n");
+ return true;
+}
+
+bool TestGrowableMemoryByteArray() {
+ fprintf(stderr, "growable mem: size ");
+ for (size_t i = 0;
+ i < sizeof(byte_array_test::BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) {
+ int32_t size = byte_array_test::BYTE_ARRAY_SIZES[i];
+ fprintf(stderr, "%d ", size);
+ ByteArrayPtr ba = new GrowableMemoryByteArray();
+ byte_array_test::FillTestByteArray(ba, size);
+ EXPECT_TRUE(byte_array_test::ByteArrayTester(ba));
+ }
+ fprintf(stderr, "\n");
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(ByteArray, All) {
+ ASSERT_TRUE(sfntly::TestMemoryByteArray());
+ ASSERT_TRUE(sfntly::TestGrowableMemoryByteArray());
+}
diff --git a/test/chrome_subsetter.cc b/test/chrome_subsetter.cc
new file mode 100644
index 0000000..9563ab1
--- /dev/null
+++ b/test/chrome_subsetter.cc
@@ -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.
+ */
+
+#include "gtest/gtest.h"
+#include "sample/chromium/font_subsetter.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace {
+ // Use an additional variable to easily change name for testing.
+ const char* kInputFileName = sfntly::SAMPLE_TTF_FILE;
+ const char* kFontName = "Tuffy";
+ const char* kOutputFileName = "tuffy-s.ttf";
+ // The subset we want: Hello, world!
+ // The array is unsorted to verify that the subsetter gets the glyph id
+ // correctly.
+ const unsigned int kGlyphIds[] = { 43, 72, 79, 82, 15, 3, 90, 85, 71, 4 };
+ const unsigned int kGlyphIdsCount = sizeof(kGlyphIds) / sizeof(unsigned int);
+}
+
+// This function is deliberately located at global namespace.
+bool TestChromeSubsetter() {
+ sfntly::ByteVector input_buffer;
+ sfntly::LoadFile(kInputFileName, &input_buffer);
+ EXPECT_GT(input_buffer.size(), (size_t)0);
+
+ unsigned char* output_buffer = NULL;
+ int output_length =
+ SfntlyWrapper::SubsetFont(kFontName,
+ &(input_buffer[0]),
+ input_buffer.size(),
+ kGlyphIds,
+ kGlyphIdsCount,
+ &output_buffer);
+
+ EXPECT_GT(output_length, 0);
+
+ if (output_length > 0) {
+ FILE* output_file = NULL;
+#if defined WIN32
+ fopen_s(&output_file, kOutputFileName, "wb");
+#else
+ output_file = fopen(kOutputFileName, "wb");
+#endif
+ EXPECT_TRUE((output_file != NULL));
+ if (output_file) {
+ int byte_count = fwrite(output_buffer, 1, output_length, output_file);
+ EXPECT_EQ(byte_count, output_length);
+ fflush(output_file);
+ fclose(output_file);
+ }
+
+ delete[] output_buffer;
+ return true;
+ }
+
+ return false;
+}
+
+TEST(ChromeSubsetter, All) {
+ EXPECT_TRUE(TestChromeSubsetter());
+}
diff --git a/test/cmap_editing_test.cc b/test/cmap_editing_test.cc
new file mode 100644
index 0000000..6df2720
--- /dev/null
+++ b/test/cmap_editing_test.cc
@@ -0,0 +1,101 @@
+/*
+ * 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 <map>
+#include <algorithm>
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/endian.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/port/refcount.h"
+#include "gtest/gtest.h"
+
+namespace sfntly {
+TEST(CMapEditingTest, RemoveAllButOneCMap) {
+ FontBuilderArray builders;
+ FontFactoryPtr font_factory;
+ font_factory.Attach(FontFactory::GetInstance());
+ BuilderForFontFile(SAMPLE_TTF_FILE, font_factory, &builders);
+ ASSERT_FALSE(builders.empty());
+ FontBuilderPtr font_builder = builders[0];
+ Ptr<CMapTable::Builder> cmap_table_builder =
+ (CMapTable::Builder*)font_builder->GetTableBuilder(Tag::cmap);
+ ASSERT_NE(cmap_table_builder, reinterpret_cast<CMapTable::Builder*>(NULL));
+ CMapTable::CMapBuilderMap*
+ cmap_builders = cmap_table_builder->GetCMapBuilders();
+ ASSERT_FALSE(cmap_builders->empty());
+
+ for (CMapTable::CMapBuilderMap::iterator
+ it = cmap_builders->begin(); it != cmap_builders->end();) {
+ if (it->second->cmap_id() == CMapTable::WINDOWS_BMP) {
+ ++it;
+ } else {
+ cmap_builders->erase(it++);
+ }
+ }
+ ASSERT_EQ(cmap_builders->size(), (uint32_t)1);
+ Font* font = font_builder->Build();
+ CMapTablePtr cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ ASSERT_EQ(1, cmap_table->NumCMaps());
+ CMapTable::CMapPtr cmap;
+ cmap.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP));
+ ASSERT_EQ(CMapTable::WINDOWS_BMP, cmap->cmap_id());
+ delete font;
+}
+
+TEST(CMapEditingTest, CopyAllCMapsToNewFont) {
+ FontArray fonts;
+ FontFactoryPtr font_factory;
+ font_factory.Attach(FontFactory::GetInstance());
+ LoadFont(SAMPLE_TTF_FILE, font_factory, &fonts);
+
+ ASSERT_FALSE(fonts.empty());
+ ASSERT_FALSE(fonts[0] == NULL);
+ FontPtr font = fonts[0];
+ CMapTablePtr cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ FontBuilderPtr font_builder;
+ font_builder.Attach(font_factory->NewFontBuilder());
+ Ptr<CMapTable::Builder> cmap_table_builder =
+ (CMapTable::Builder*)font_builder->NewTableBuilder(Tag::cmap);
+
+ CMapTable::CMapIterator cmap_iter(cmap_table, NULL);
+ while (cmap_iter.HasNext()) {
+ CMapTable::CMapPtr cmap;
+ cmap.Attach(cmap_iter.Next());
+ if (!cmap)
+ continue;
+ cmap_table_builder->NewCMapBuilder(cmap->cmap_id(), cmap->ReadFontData());
+ }
+
+ FontPtr new_font;
+ new_font.Attach(font_builder->Build());
+ CMapTablePtr new_cmap_table =
+ down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ ASSERT_EQ(cmap_table->NumCMaps(), new_cmap_table->NumCMaps());
+ CMapTable::CMapPtr cmap;
+ cmap.Attach(cmap_table->GetCMap(CMapTable::WINDOWS_BMP));
+ ASSERT_NE(cmap, reinterpret_cast<CMapTable::CMap*>(NULL));
+ ASSERT_EQ(CMapTable::WINDOWS_BMP, cmap->cmap_id());
+}
+}
diff --git a/test/cmap_iterator_test.cc b/test/cmap_iterator_test.cc
new file mode 100644
index 0000000..2e8e6fe
--- /dev/null
+++ b/test/cmap_iterator_test.cc
@@ -0,0 +1,156 @@
+/*
+ * 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 <string.h>
+
+#include <vector>
+#include <string>
+#include <algorithm>
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/port/type.h"
+#include "sfntly/port/refcount.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+#include "gtest/gtest.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace sfntly {
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+typedef std::vector<bool> BitSet;
+
+class CMapIteratorTestCase {
+ public:
+ CMapIteratorTestCase(int32_t platform_id, int32_t encoding_id,
+ const char* file_name)
+ : platform_id_(platform_id),
+ encoding_id_(encoding_id),
+ file_name_(file_name) {
+ }
+ ~CMapIteratorTestCase() {}
+ int32_t platform_id() const { return platform_id_; }
+ int32_t encoding_id() const { return encoding_id_; }
+ const char* file_name() const { return file_name_; }
+
+ private:
+ int32_t platform_id_;
+ int32_t encoding_id_;
+ const char* file_name_;
+};
+
+class CMapIteratorTests
+ : public ::testing::TestWithParam<CMapIteratorTestCase> {
+ public:
+ virtual void SetUp();
+ virtual void TearDown() {}
+
+ BitSet* GenerateCMapEntries(int32_t start, int32_t count);
+ int32_t CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator*
+ character_iterator,
+ BitSet* bit_set);
+
+ Ptr<CMapTable::CMap> cmap_;
+};
+
+void CMapIteratorTests::SetUp() {
+ FontArray fonts;
+ Ptr<FontFactory> font_factory;
+ const char* file_name = GetParam().file_name();
+ LoadFont(file_name, font_factory, &fonts);
+ Ptr<Font> font;
+ font.Attach(fonts[0].Detach());
+ Ptr<CMapTable> cmap_table = down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ ASSERT_FALSE(cmap_table == NULL);
+ cmap_.Attach(cmap_table->GetCMap(GetParam().platform_id(),
+ GetParam().encoding_id()));
+ ASSERT_FALSE(cmap_ == NULL);
+}
+
+BitSet* CMapIteratorTests::GenerateCMapEntries(int32_t start, int32_t count) {
+ BitSet* entries = new BitSet(count);
+ for (int32_t c = start; c < start + count; ++c) {
+ int32_t g = cmap_->GlyphId(c);
+ if (g != CMapTable::NOTDEF)
+ (*entries)[c] = true;
+ }
+ return entries;
+}
+
+int32_t
+CMapIteratorTests::
+CompareCMapIterAndBitSet(CMapTable::CMap::CharacterIterator* character_iterator,
+ BitSet* bit_set) {
+ int32_t iterator_not_bitset_count = 0;
+ BitSet::iterator end = bit_set->end(),
+ beginning = bit_set->begin(),
+ init_beginning = beginning,
+ current = std::find(beginning, end, true);
+ for (int32_t next_bit = current - beginning;
+ character_iterator->HasNext() && current != end;
+ next_bit = current - init_beginning) {
+ int32_t c = character_iterator->Next();
+ EXPECT_TRUE(c <= next_bit || current == end);
+ if (!(c <= next_bit || current == end))
+ return -1;
+ if (c == next_bit) {
+ beginning = current + 1;
+ current = std::find(beginning, end, true);
+ } else {
+ iterator_not_bitset_count++;
+ }
+ }
+ EXPECT_EQ(end, current);
+#if defined (SFNTLY_DEBUG_CMAP)
+ fprintf(stderr, "%s %d: Differences between iterator and bitset: %d\n",
+ cmap_->format(), GetParam().file_name(), iterator_not_bitset_count);
+#endif
+ return iterator_not_bitset_count;
+}
+
+TEST_P(CMapIteratorTests, IteratorTest) {
+ BitSet* bit_set = GenerateCMapEntries(0, 0x10ffff);
+ CMapTable::CMap::CharacterIterator* character_iterator = NULL;
+ character_iterator = cmap_->Iterator();
+ EXPECT_NE(character_iterator,
+ reinterpret_cast<CMapTable::CMap::CharacterIterator*>(NULL));
+ CompareCMapIterAndBitSet(character_iterator, bit_set);
+ delete character_iterator;
+ delete bit_set;
+}
+
+CMapIteratorTestCase kCMapIteratorTestsTestCases[] = {
+ CMapIteratorTestCase(CMapTable::WINDOWS_BMP.platform_id,
+ CMapTable::WINDOWS_BMP.encoding_id,
+ SAMPLE_TTF_FILE)
+};
+
+INSTANTIATE_TEST_CASE_P(CMapIteratorTests,
+ CMapIteratorTests,
+ ::testing::ValuesIn(kCMapIteratorTestsTestCases));
+}
+
+#else
+
+TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}
+
+#endif // GTEST_HAS_PARAM
diff --git a/test/cmap_test.cc b/test/cmap_test.cc
new file mode 100644
index 0000000..02bf8a2
--- /dev/null
+++ b/test/cmap_test.cc
@@ -0,0 +1,213 @@
+/*
+ * 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/type.h"
+#include <assert.h>
+#include <unicode/ucnv.h>
+
+#include <string>
+#include <iostream>
+
+#include "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/cmap_table.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/tag.h"
+
+#include "test/test_utils.h"
+#include "test/test_font_utils.h"
+#include "test/test_data.h"
+
+#if GTEST_HAS_PARAM_TEST
+
+namespace sfntly {
+using ::testing::TestWithParam;
+using ::testing::Values;
+
+class CMapTestCase {
+ public:
+ CMapTestCase(const char* font_name,
+ int32_t first_platform_id,
+ int32_t first_encoding_id,
+ const char* first_charset_name,
+ int32_t second_platform_id,
+ int32_t second_encoding_id,
+ const char* second_charset_name,
+ int32_t low_char,
+ int32_t high_char)
+ : font_name_(font_name),
+ first_platform_id_(first_platform_id),
+ first_encoding_id_(first_encoding_id),
+ first_charset_name_(first_charset_name),
+ second_platform_id_(second_platform_id),
+ second_encoding_id_(second_encoding_id),
+ second_charset_name_(second_charset_name),
+ low_char_(low_char),
+ high_char_(high_char) {
+ }
+
+ const char* font_name() const { return font_name_; }
+ int32_t first_platform_id() const { return first_platform_id_; }
+ int32_t first_encoding_id() const { return first_encoding_id_; }
+ const char* first_charset_name() const { return first_charset_name_; }
+ int32_t second_platform_id() const { return second_platform_id_; }
+ int32_t second_encoding_id() const { return second_encoding_id_; }
+ const char* second_charset_name() const { return second_charset_name_; }
+ int32_t low_char() const { return low_char_; }
+ int32_t high_char() const { return high_char_; }
+
+ private:
+ const char* font_name_;
+ int32_t first_platform_id_;
+ int32_t first_encoding_id_;
+ const char* first_charset_name_;
+ int32_t second_platform_id_;
+ int32_t second_encoding_id_;
+ const char* second_charset_name_;
+ int32_t low_char_;
+ int32_t high_char_;
+};
+
+class CMapTests : public :: testing::TestWithParam<CMapTestCase> {
+ public:
+ CMapTests() : encoder1_(NULL), encoder2_(NULL), successful_setup_(false) {
+ }
+ virtual void SetUp() {}
+ virtual void TearDown();
+
+ void CommonSetUp(FontArray* font_array);
+
+ void CompareCMaps();
+
+ Ptr<CMapTable::CMap> cmap1_;
+ Ptr<CMapTable::CMap> cmap2_;
+ UConverter* encoder1_;
+ UConverter* encoder2_;
+ bool successful_setup_;
+};
+
+::std::ostream& operator<<(::std::ostream& os, const CMapTestCase *test_case) {
+ return os << "("
+ << test_case->font_name() << ", "
+ << test_case->first_platform_id() << ", "
+ << test_case->first_encoding_id() << ", "
+ << test_case->first_charset_name() << ", "
+ << test_case->second_platform_id() << ", "
+ << test_case->second_encoding_id() << ", "
+ << test_case->second_charset_name() << ", "
+ << test_case->low_char() << ", "
+ << test_case->high_char() << ")";
+}
+
+void CMapTests::CommonSetUp(FontArray* font_array) {
+ ASSERT_NE(font_array, reinterpret_cast<FontArray*>(NULL));
+ ASSERT_FALSE(font_array->empty());
+ Ptr<Font> font;
+ font = font_array->at(0);
+ ASSERT_NE(font, reinterpret_cast<Font*>(NULL));
+ Ptr<CMapTable> cmap_table =
+ down_cast<CMapTable*>(font->GetTable(Tag::cmap));
+ cmap1_.Attach(cmap_table->GetCMap(GetParam().first_platform_id(),
+ GetParam().first_encoding_id()));
+ ASSERT_NE((cmap1_), reinterpret_cast<CMapTable::CMap*>(NULL));
+ cmap2_.Attach(cmap_table->GetCMap(GetParam().second_platform_id(),
+ GetParam().second_encoding_id()));
+ ASSERT_NE((cmap2_), reinterpret_cast<CMapTable::CMap*>(NULL));
+ encoder1_ = TestUtils::GetEncoder(GetParam().first_charset_name());
+ encoder2_ = TestUtils::GetEncoder(GetParam().second_charset_name());
+ successful_setup_ = true;
+}
+
+void CMapTests::TearDown() {
+ if (encoder1_)
+ ucnv_close(encoder1_);
+ if (encoder2_)
+ ucnv_close(encoder2_);
+}
+
+void CMapTests::CompareCMaps() {
+ ASSERT_TRUE(successful_setup_);
+ for (int32_t uchar = GetParam().low_char();
+ uchar <= GetParam().high_char(); ++uchar) {
+ int32_t c1 = uchar;
+ if (encoder1_ != NULL)
+ c1 = TestUtils::EncodeOneChar(encoder1_, (int16_t)uchar);
+ int32_t c2 = uchar;
+ if (encoder2_ != NULL)
+ c2 = TestUtils::EncodeOneChar(encoder2_, (int16_t)uchar);
+ int32_t glyph_id1 = cmap1_->GlyphId(c1);
+ int32_t glyph_id2 = cmap2_->GlyphId(c2);
+#ifdef SFNTLY_DEBUG_CMAP
+ if (glyph_id1 != glyph_id2)
+ fprintf(stderr, "%x: g1=%x, %x: g2=%x\n", c1, glyph_id1, c2, glyph_id2);
+#endif
+ ASSERT_EQ(glyph_id1, glyph_id2);
+ }
+#ifdef SFNTLY_SFNTLY_DEBUG_CMAPCMAP
+ fprintf(stderr, "\n");
+#endif
+}
+
+TEST_P(CMapTests, GlyphsBetweenCMapsFingerprint) {
+ Ptr<FontFactory> font_factory;
+ font_factory.Attach(FontFactory::GetInstance());
+ font_factory->FingerprintFont(true);
+ FontArray font_array;
+ LoadFont(GetParam().font_name(), font_factory, &font_array);
+ CommonSetUp(&font_array);
+ CompareCMaps();
+}
+
+TEST_P(CMapTests, GlyphsBetweenCMapsNoFingerprint) {
+ Ptr<FontFactory> font_factory;
+ font_factory.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ LoadFont(GetParam().font_name(), font_factory, &font_array);
+ CommonSetUp(&font_array);
+ CompareCMaps();
+}
+
+TEST_P(CMapTests, GlyphsBetweenCMapsUsingByteVector) {
+ FontArray font_array;
+ LoadFontUsingByteVector(GetParam().font_name(), true, &font_array);
+ CommonSetUp(&font_array);
+ CompareCMaps();
+}
+
+CMapTestCase kCMapTestsTestCases[] = {
+ CMapTestCase(SAMPLE_TTF_FILE,
+ PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ NULL,
+ PlatformId::kUnicode,
+ UnicodeEncodingId::kUnicode2_0_BMP,
+ NULL,
+ (int32_t)0x20,
+ (int32_t)0x7f),
+};
+
+INSTANTIATE_TEST_CASE_P(CMapTests,
+ CMapTests,
+ ::testing::ValuesIn(kCMapTestsTestCases));
+}
+
+#else
+
+TEST(DummyTest, ValueParameterizedTestsAreNotSupportedOnThisPlatform) {}
+
+#endif // GTEST_HAS_PARAM
diff --git a/test/endian_test.cc b/test/endian_test.cc
new file mode 100644
index 0000000..0d9da09
--- /dev/null
+++ b/test/endian_test.cc
@@ -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.
+ */
+
+#include "gtest/gtest.h"
+#include "sfntly/tag.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/math/fixed1616.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "sfntly/data/font_output_stream.h"
+
+namespace sfntly {
+
+bool TestEndian() {
+ byte_t test_data[] = {
+ 0x68, 0x65, 0x61, 0x64, // 0: head
+ 0xca, 0xca, 0xca, 0xca, // 4: ubyte, byte, char
+ 0x00, 0x18, 0x80, 0x18, // 8: ushort, short
+ 0x00, 0x00, 0x18, 0x00, // 12: uint24
+ 0x00, 0x00, 0x00, 0x18, // 16: ulong
+ 0xff, 0xff, 0xff, 0x00, // 20: long
+ 0x00, 0x01, 0x00, 0x00 // 24: fixed
+ };
+
+ ByteArrayPtr ba1 = new GrowableMemoryByteArray();
+ for (size_t i = 0; i < sizeof(test_data); ++i) {
+ ba1->Put(i, test_data[i]);
+ }
+ ReadableFontDataPtr rfd = new ReadableFontData(ba1);
+ EXPECT_EQ(rfd->ReadULongAsInt(0), Tag::head);
+ EXPECT_EQ(rfd->ReadUByte(4), 202);
+ EXPECT_EQ(rfd->ReadByte(5), -54);
+ EXPECT_EQ(rfd->ReadChar(6), 202);
+ EXPECT_EQ(rfd->ReadUShort(8), 24);
+ EXPECT_EQ(rfd->ReadShort(10), -32744);
+ EXPECT_EQ(rfd->ReadUInt24(12), 24);
+ EXPECT_EQ(rfd->ReadULong(16), 24);
+ EXPECT_EQ(rfd->ReadLong(20), -256);
+ EXPECT_EQ(rfd->ReadFixed(24), Fixed1616::Fixed(1, 0));
+
+ MemoryOutputStream os;
+ FontOutputStream fos(&os);
+ fos.WriteULong(Tag::head);
+ fos.Write(202);
+ fos.Write(202);
+ fos.Write(202);
+ fos.Write(202);
+ fos.WriteUShort(24);
+ fos.WriteShort(-32744);
+ fos.WriteUInt24(24);
+ fos.WriteChar(0);
+ fos.WriteULong(24);
+ fos.WriteLong(-256);
+ fos.WriteFixed(Fixed1616::Fixed(1, 0));
+ EXPECT_EQ(memcmp(os.Get(), test_data, sizeof(test_data)), 0);
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(Endian, All) {
+ ASSERT_TRUE(sfntly::TestEndian());
+}
diff --git a/test/file_io_test.cc b/test/file_io_test.cc
new file mode 100644
index 0000000..3cec4d6
--- /dev/null
+++ b/test/file_io_test.cc
@@ -0,0 +1,153 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/data/font_input_stream.h"
+#include "test/test_data.h"
+
+namespace sfntly {
+
+bool TestFileInputStream() {
+ FILE* file_handle = NULL;
+#if defined (WIN32)
+ fopen_s(&file_handle, SAMPLE_TTF_FILE, "rb");
+#else
+ file_handle = fopen(SAMPLE_TTF_FILE, "rb");
+#endif
+ if (file_handle == NULL) {
+ return false;
+ }
+ fseek(file_handle, 0, SEEK_END);
+ size_t length = ftell(file_handle);
+ fseek(file_handle, 0, SEEK_SET);
+ ByteVector b1;
+ b1.resize(length);
+ size_t bytes_read = fread(&(b1[0]), 1, length, file_handle);
+ EXPECT_EQ(bytes_read, length);
+ fclose(file_handle);
+
+ // Full file reading test
+ FileInputStream is;
+ is.Open(SAMPLE_TTF_FILE);
+ EXPECT_EQ(length, (size_t)is.Available());
+ ByteVector b2;
+ is.Read(&b2, 0, length);
+ is.Close();
+ EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0);
+ b2.clear();
+
+ // Partial reading test
+ is.Open(SAMPLE_TTF_FILE);
+ is.Skip(89);
+ is.Read(&b2, 0, 100);
+ EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+ b2.clear();
+
+ // Skip test
+ is.Skip(-89);
+ is.Read(&b2, 0, 100);
+ EXPECT_EQ(memcmp(&(b1[100]), &(b2[0]), 100), 0);
+ b2.clear();
+ is.Skip(100);
+ is.Read(&b2, 0, 100);
+ EXPECT_EQ(memcmp(&(b1[300]), &(b2[0]), 100), 0);
+ is.Skip(-400);
+ b2.clear();
+
+ // Offset test
+ is.Read(&b2, 0, 100);
+ is.Read(&b2, 100, 100);
+ EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), 200), 0);
+
+ // Unread test
+ ByteVector b3;
+ b3.resize(200);
+ is.Unread(&b3);
+ EXPECT_EQ(memcmp(&(b3[0]), &(b2[0]), 200), 0);
+
+ return true;
+}
+
+bool TestFontInputStreamBasic() {
+ FILE* file_handle = NULL;
+#if defined (WIN32)
+ fopen_s(&file_handle, SAMPLE_TTF_FILE, "rb");
+#else
+ file_handle = fopen(SAMPLE_TTF_FILE, "rb");
+#endif
+ if (file_handle == NULL) {
+ return false;
+ }
+ fseek(file_handle, 0, SEEK_END);
+ size_t length = ftell(file_handle);
+ fseek(file_handle, 0, SEEK_SET);
+ ByteVector b1;
+ b1.resize(length);
+ size_t bytes_read = fread(&(b1[0]), 1, length, file_handle);
+ EXPECT_EQ(bytes_read, length);
+ fclose(file_handle);
+
+ FileInputStream is;
+ is.Open(SAMPLE_TTF_FILE);
+ FontInputStream font_is1(&is);
+ EXPECT_EQ((size_t)font_is1.Available(), length);
+
+ ByteVector b2;
+ font_is1.Read(&b2, 0, length);
+ font_is1.Close();
+ EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), length), 0);
+ b2.clear();
+
+ is.Open(SAMPLE_TTF_FILE);
+ is.Skip(89);
+ FontInputStream font_is2(&is, 200);
+ font_is2.Read(&b2, 0, 100);
+ EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+ font_is2.Read(&b2, 100, 100);
+ EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 200), 0);
+ b2.clear();
+ font_is2.Skip(-200);
+ font_is2.Read(&b2, 0, 100);
+ EXPECT_EQ(memcmp(&(b1[89]), &(b2[0]), 100), 0);
+
+ return true;
+}
+
+bool TestFontInputStreamTableLoading() {
+ FileInputStream is;
+ is.Open(SAMPLE_TTF_FILE);
+ FontInputStream font_is(&is);
+
+ font_is.Skip(TTF_OFFSET[SAMPLE_TTF_FEAT]);
+ FontInputStream gdef_is(&font_is, TTF_LENGTH[SAMPLE_TTF_FEAT]);
+ ByteVector feat_data;
+ gdef_is.Read(&feat_data, 0, TTF_LENGTH[SAMPLE_TTF_FEAT]);
+ EXPECT_EQ(memcmp(&(feat_data[0]), TTF_FEAT_DATA,
+ TTF_LENGTH[SAMPLE_TTF_FEAT]), 0);
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(FileIO, All) {
+ ASSERT_TRUE(sfntly::TestFileInputStream());
+ ASSERT_TRUE(sfntly::TestFontInputStreamBasic());
+ ASSERT_TRUE(sfntly::TestFontInputStreamTableLoading());
+}
diff --git a/test/font_data_test.cc b/test/font_data_test.cc
new file mode 100644
index 0000000..4b0db64
--- /dev/null
+++ b/test/font_data_test.cc
@@ -0,0 +1,337 @@
+/*
+ * 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 <vector>
+#include <algorithm>
+
+#include "gtest/gtest.h"
+#include "sfntly/port/type.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/data/memory_byte_array.h"
+
+namespace sfntly {
+
+const int32_t BYTE_ARRAY_SIZES[] =
+ {1, 7, 127, 128, 129, 255, 256, 257, 666, 1023, 0x10000};
+
+// array data for searching
+const int32_t LOWER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 4, 7, 13, 127};
+const int32_t UPPER_BYTE_ARRAY_FOR_SEARCHING[] = {2, 5, 12, 16, 256};
+const int32_t kLowerByteArrayForSearchingLength = 5;
+const int32_t kUpperByteArrayForSearchingLength = 5;
+
+// search test result pairs - number to search for; index found at
+const int32_t SEARCH_TEST_PAIRS[][2] = {
+ {0, -1}, {1, -1}, {2, 0}, {3, -1}, {4, 1}, {5, 1}, {6, -1}, {12, 2},
+ {13, 3}, {17, -1}, {126, -1}, {127, 4}, {256, 4}, {257, -1}, {0x1000, -1}
+};
+const int32_t kSearchTestPairsLength = 15;
+
+// offset and start index data for searching data
+// array data size, lower_start_index, lower_offset, upper_start_index,
+// upper_offset
+const int32_t SEARCH_TEST_OFFSETS[][5] = {
+ // lower[], upper[]
+ { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength)
+ * DataSize::kUSHORT,
+ 0,
+ DataSize::kUSHORT,
+ kLowerByteArrayForSearchingLength * DataSize::kUSHORT,
+ DataSize::kUSHORT },
+
+ // {lower, upper} []
+ { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength)
+ * DataSize::kUSHORT,
+ 0,
+ 2 * DataSize::kUSHORT,
+ DataSize::kUSHORT,
+ 2 * DataSize::kUSHORT },
+
+ // upper[], lower[]
+ { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength)
+ * DataSize::kUSHORT,
+ kLowerByteArrayForSearchingLength * DataSize::kUSHORT,
+ DataSize::kUSHORT,
+ 0,
+ DataSize::kUSHORT },
+
+ // {upper, lower} []
+ { (kLowerByteArrayForSearchingLength + kUpperByteArrayForSearchingLength)
+ * DataSize::kUSHORT,
+ DataSize::kUSHORT,
+ 2 * DataSize::kUSHORT,
+ 0,
+ 2 * DataSize::kUSHORT }
+};
+const int32_t kSearchTestOffsetLength = 4;
+
+ReadableFontData*
+FillTestFontDataWithShortsForSearching(WritableFontData* wfd,
+ const int32_t* lower_data,
+ int32_t lower_start_index,
+ int32_t lower_offset,
+ const int32_t* upper_data,
+ int32_t upper_start_index,
+ int32_t upper_offset) {
+ // lower data
+ int offset = lower_start_index;
+ for (int32_t i = 0; i < kLowerByteArrayForSearchingLength; ++i) {
+ wfd->WriteUShort(offset, lower_data[i]);
+ offset += lower_offset;
+ }
+
+ // upper data
+ offset = upper_start_index;
+ for (int32_t i = 0; i < kUpperByteArrayForSearchingLength; ++i) {
+ wfd->WriteUShort(offset, upper_data[i]);
+ offset += upper_offset;
+ }
+
+ return wfd;
+}
+
+bool TestReadableFontDataSearching() {
+ for (int32_t i = 0; i < kSearchTestOffsetLength; ++i) {
+ const int32_t* array_setup_offset = SEARCH_TEST_OFFSETS[i];
+ WritableFontDataPtr wfd;
+ wfd.Attach(WritableFontData::CreateWritableFontData(array_setup_offset[0]));
+ FillTestFontDataWithShortsForSearching(wfd,
+ LOWER_BYTE_ARRAY_FOR_SEARCHING,
+ array_setup_offset[1],
+ array_setup_offset[2],
+ UPPER_BYTE_ARRAY_FOR_SEARCHING,
+ array_setup_offset[3],
+ array_setup_offset[4]);
+ for (int32_t j = 0; j < kSearchTestPairsLength; ++j) {
+ const int32_t* test_case = SEARCH_TEST_PAIRS[j];
+ int32_t found = wfd->SearchUShort(array_setup_offset[1],
+ array_setup_offset[2],
+ array_setup_offset[3],
+ array_setup_offset[4],
+ kLowerByteArrayForSearchingLength,
+ test_case[0]);
+#if defined (SFNTLY_DEBUG_FONTDATA)
+ fprintf(stderr, "Searching for %d; Got %d; Expected %d; "
+ "[test %d][offset %d]\n",
+ test_case[0], found, test_case[1], j, i);
+#endif
+ EXPECT_EQ(test_case[1], found);
+ }
+ }
+ return true;
+}
+
+void FillTestByteArray(ByteArray* ba, int32_t size) {
+ for (int32_t i = 0; i < size; ++i) {
+ ba->Put(i, (byte_t)(i % 256));
+ }
+}
+
+void ReadFontDataWithSingleByte(ReadableFontData* rfd, ByteVector* buffer) {
+ buffer->resize(rfd->Length());
+ for (int32_t index = 0; index < rfd->Length(); ++index) {
+ (*buffer)[index] = (byte_t)(rfd->ReadByte(index));
+ }
+}
+
+void ReadFontDataWithBuffer(ReadableFontData* rfd,
+ int32_t buffer_size,
+ ByteVector* b) {
+ ByteVector buffer(buffer_size);
+ b->resize(rfd->Length());
+
+ int32_t index = 0;
+ while (index < rfd->Length()) {
+ int32_t bytes_read = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size());
+ EXPECT_GE(bytes_read, 0);
+ std::copy(buffer.begin(), buffer.begin() + bytes_read, b->begin() + index);
+ index += bytes_read;
+ }
+}
+
+void ReadFontDataWithSlidingWindow(ReadableFontData* rfd, int32_t window_size,
+ ByteVector* b) {
+ b->resize(rfd->Length());
+ int32_t index = 0;
+ while (index < rfd->Length()) {
+ int32_t actual_window_size =
+ std::min<int32_t>(window_size, b->size() - index);
+ int32_t bytes_read =
+ rfd->ReadBytes(index, &((*b)[0]), index, actual_window_size);
+ EXPECT_GE(bytes_read, 0);
+ index += bytes_read;
+ }
+}
+
+void WriteFontDataWithSingleByte(ReadableFontData* rfd, WritableFontData* wfd) {
+ for (int32_t index = 0; index < rfd->Length(); ++index) {
+ byte_t b = (byte_t)(rfd->ReadByte(index));
+ wfd->WriteByte(index, b);
+ }
+}
+
+void WriteFontDataWithBuffer(ReadableFontData* rfd,
+ WritableFontData* wfd,
+ int32_t buffer_size) {
+ ByteVector buffer(buffer_size);
+ int32_t index = 0;
+ while (index < rfd->Length()) {
+ int32_t bytesRead = rfd->ReadBytes(index, &(buffer[0]), 0, buffer.size());
+ wfd->WriteBytes(index, &(buffer[0]), 0, buffer.size());
+ index += bytesRead;
+ }
+}
+
+void WriteFontDataWithSlidingWindow(ReadableFontData* rfd,
+ WritableFontData* wfd,
+ int32_t window_size) {
+ ByteVector b(rfd->Length());
+ int32_t index = 0;
+ while (index < rfd->Length()) {
+ int32_t sliding_size = std::min<int32_t>(window_size, b.size() - index);
+ int32_t bytes_read = rfd->ReadBytes(index, &(b[0]), index, sliding_size);
+ wfd->WriteBytes(index, &(b[0]), index, sliding_size);
+ index += bytes_read;
+ }
+}
+
+bool ReadComparison(int32_t offset,
+ int32_t length,
+ ReadableFontData* rfd1,
+ ReadableFontData* rfd2) {
+ EXPECT_TRUE(length == rfd2->Length());
+ ByteVector b1, b2;
+ b1.resize(length);
+ b2.resize(length);
+
+ // single byte reads
+ ReadFontDataWithSingleByte(rfd1, &b1);
+ ReadFontDataWithSingleByte(rfd2, &b2);
+ EXPECT_EQ(memcmp(&(b1[offset]), &(b2[0]), length), 0);
+
+ // buffer reads
+ int32_t increments = std::max<int32_t>(length / 11, 1);
+ for (int32_t buffer_size = 1; buffer_size <= length;
+ buffer_size += increments) {
+ b1.clear();
+ b2.clear();
+ b1.resize(length);
+ b2.resize(length);
+ ReadFontDataWithBuffer(rfd1, buffer_size, &b1);
+ ReadFontDataWithBuffer(rfd2, buffer_size, &b2);
+ int result = memcmp(&(b1[offset]), &(b2[0]), length);
+ EXPECT_EQ(result, 0);
+ }
+
+ // sliding window reads
+ for (int32_t window_size = 1; window_size <= length;
+ window_size += increments) {
+ b1.clear();
+ b2.clear();
+ b1.resize(length);
+ b2.resize(length);
+ ReadFontDataWithSlidingWindow(rfd1, window_size, &b1);
+ ReadFontDataWithSlidingWindow(rfd2, window_size, &b2);
+ int result = memcmp(&(b1[offset]), &(b2[0]), length);
+ EXPECT_EQ(result, 0);
+ }
+ return true;
+}
+
+void SlicingReadTest(ReadableFontData* rfd) {
+ fprintf(stderr, "read - trim = ");
+ for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1;
+ trim += (rfd->Length() / 21) + 1) {
+ fprintf(stderr, "%d ", trim);
+ int32_t length = rfd->Length() - 2 * trim;
+ ReadableFontDataPtr slice;
+ slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length)));
+ EXPECT_TRUE(ReadComparison(trim, length, rfd, slice));
+ }
+ fprintf(stderr, "\n");
+}
+
+void SlicingWriteTest(ReadableFontData* rfd, WritableFontData* wfd) {
+ fprintf(stderr, "write - trim = ");
+ for (int32_t trim = 0; trim < (rfd->Length() / 2) + 1;
+ trim += (rfd->Length() / 21) + 1) {
+ fprintf(stderr, "%d ", trim);
+ int32_t length = rfd->Length() - 2 * trim;
+ WritableFontDataPtr w_slice;
+ ReadableFontDataPtr r_slice;
+
+ // single byte writes
+ w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length)));
+ r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length)));
+ WriteFontDataWithSingleByte(r_slice, w_slice);
+ EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice));
+
+ // buffer writes
+ int32_t increments = std::max<int32_t>(length / 11, 1);
+ for (int32_t buffer_size = 1; buffer_size < length;
+ buffer_size += increments) {
+ w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length)));
+ r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length)));
+ WriteFontDataWithBuffer(r_slice, w_slice, buffer_size);
+ EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice));
+ }
+
+ // sliding window writes
+ for (int window_size = 1; window_size < length; window_size += increments) {
+ w_slice.Attach(down_cast<WritableFontData*>(wfd->Slice(trim, length)));
+ r_slice.Attach(down_cast<ReadableFontData*>(rfd->Slice(trim, length)));
+ WriteFontDataWithSlidingWindow(r_slice, w_slice, window_size);
+ EXPECT_TRUE(ReadComparison(trim, length, rfd, w_slice));
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+bool TestReadableFontData() {
+ for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) {
+ int32_t size = BYTE_ARRAY_SIZES[i];
+ ByteArrayPtr ba = new MemoryByteArray(size);
+ FillTestByteArray(ba, size);
+ ReadableFontDataPtr rfd = new ReadableFontData(ba);
+ SlicingReadTest(rfd);
+ }
+ return true;
+}
+
+bool TestWritableFontData() {
+ for (size_t i = 0; i < sizeof(BYTE_ARRAY_SIZES) / sizeof(int32_t); ++i) {
+ int32_t size = BYTE_ARRAY_SIZES[i];
+ ByteArrayPtr ba = new MemoryByteArray(size);
+ FillTestByteArray(ba, size);
+ WritableFontDataPtr wfd = new WritableFontData(ba);
+ SlicingReadTest(wfd);
+ ByteArrayPtr temp = new MemoryByteArray(size);
+ WritableFontDataPtr wfd_copy = new WritableFontData(temp);
+ SlicingWriteTest(wfd, wfd_copy);
+ }
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(FontData, ReadableFontDataSearching) {
+ ASSERT_TRUE(sfntly::TestReadableFontDataSearching());
+}
+
+TEST(FontData, All) {
+ ASSERT_TRUE(sfntly::TestReadableFontData());
+ ASSERT_TRUE(sfntly::TestWritableFontData());
+}
diff --git a/test/font_parsing_test.cc b/test/font_parsing_test.cc
new file mode 100644
index 0000000..6fd5c3b
--- /dev/null
+++ b/test/font_parsing_test.cc
@@ -0,0 +1,140 @@
+/*
+ * 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 "gtest/gtest.h"
+
+#include "sfntly/data/font_input_stream.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/table/table.h"
+#include "sfntly/table/generic_table_builder.h"
+#include "sfntly/table/table_based_table_builder.h"
+#include "sfntly/tag.h"
+#include "sfntly/port/file_input_stream.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+bool TestFontParsing() {
+ ByteVector input_buffer;
+ LoadFile(SAMPLE_TTF_FILE, &input_buffer);
+
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ // File based
+ FontBuilderArray font_builder_array;
+ BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+ // Memory based
+ FontBuilderArray font_builder_array2;
+ factory->LoadFontsForBuilding(&input_buffer, &font_builder_array2);
+ FontBuilderPtr font_builder2 = font_builder_array2[0];
+
+ for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) {
+ EXPECT_TRUE(font_builder->HasTableBuilder(TTF_KNOWN_TAGS[i]));
+ EXPECT_TRUE(font_builder2->HasTableBuilder(TTF_KNOWN_TAGS[i]));
+ }
+
+ // Generic table
+ Ptr<GenericTableBuilder> gdef_builder =
+ down_cast<GenericTableBuilder*>(font_builder->GetTableBuilder(Tag::feat));
+ HeaderPtr gdef_header = gdef_builder->header();
+ EXPECT_EQ(gdef_header->length(), TTF_LENGTH[SAMPLE_TTF_FEAT]);
+ EXPECT_EQ(gdef_header->offset(), TTF_OFFSET[SAMPLE_TTF_FEAT]);
+ EXPECT_EQ(gdef_header->checksum(), TTF_CHECKSUM[SAMPLE_TTF_FEAT]);
+ EXPECT_TRUE(gdef_header->checksum_valid());
+
+ WritableFontDataPtr wfd;
+ wfd.Attach(gdef_builder->Data());
+ ByteVector b;
+ b.resize(TTF_LENGTH[SAMPLE_TTF_FEAT]);
+ wfd->ReadBytes(0, &(b[0]), 0, TTF_LENGTH[SAMPLE_TTF_FEAT]);
+ EXPECT_EQ(memcmp(&(b[0]), TTF_FEAT_DATA, TTF_LENGTH[SAMPLE_TTF_FEAT]), 0);
+
+ // Header table
+ FontHeaderTableBuilderPtr header_builder =
+ down_cast<FontHeaderTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::head));
+ HeaderPtr header_header = header_builder->header();
+ EXPECT_EQ(header_header->length(), TTF_LENGTH[SAMPLE_TTF_HEAD]);
+ EXPECT_EQ(header_header->offset(), TTF_OFFSET[SAMPLE_TTF_HEAD]);
+ EXPECT_EQ(header_header->checksum(), TTF_CHECKSUM[SAMPLE_TTF_HEAD]);
+ EXPECT_TRUE(header_header->checksum_valid());
+
+ // Data conformance
+ for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) {
+ ByteVector b1, b2;
+ b1.resize(TTF_LENGTH[i]);
+ b2.resize(TTF_LENGTH[i]);
+ TableBuilderPtr builder1 =
+ font_builder->GetTableBuilder(TTF_KNOWN_TAGS[i]);
+ TableBuilderPtr builder2 =
+ font_builder2->GetTableBuilder(TTF_KNOWN_TAGS[i]);
+ WritableFontDataPtr wfd1;
+ wfd1.Attach(builder1->Data());
+ WritableFontDataPtr wfd2;
+ wfd2.Attach(builder2->Data());
+ wfd1->ReadBytes(0, &(b1[0]), 0, TTF_LENGTH[i]);
+ wfd2->ReadBytes(0, &(b2[0]), 0, TTF_LENGTH[i]);
+ EXPECT_EQ(memcmp(&(b1[0]), &(b2[0]), TTF_LENGTH[i]), 0);
+ }
+
+ return true;
+}
+
+bool TestTTFReadWrite() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+ FontPtr font;
+ font.Attach(font_builder->Build());
+ MemoryOutputStream output_stream;
+ factory->SerializeFont(font, &output_stream);
+ EXPECT_GE(output_stream.Size(), SAMPLE_TTF_SIZE);
+
+ return true;
+}
+
+bool TestTTFMemoryBasedReadWrite() {
+ ByteVector input_buffer;
+ LoadFile(SAMPLE_TTF_FILE, &input_buffer);
+
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ factory->LoadFontsForBuilding(&input_buffer, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+ FontPtr font;
+ font.Attach(font_builder->Build());
+ MemoryOutputStream output_stream;
+ factory->SerializeFont(font, &output_stream);
+ EXPECT_GE(output_stream.Size(), input_buffer.size());
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(FontParsing, All) {
+ ASSERT_TRUE(sfntly::TestFontParsing());
+ ASSERT_TRUE(sfntly::TestTTFReadWrite());
+ ASSERT_TRUE(sfntly::TestTTFMemoryBasedReadWrite());
+}
diff --git a/test/hdmx_test.cc b/test/hdmx_test.cc
new file mode 100644
index 0000000..cdc4ed0
--- /dev/null
+++ b/test/hdmx_test.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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/core/horizontal_device_metrics_table.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+const int32_t HDMX_VERSION = 0;
+const int32_t HDMX_NUM_RECORDS = 4;
+const int32_t HDMX_RECORD_SIZE = 628;
+const int32_t HDMX_PIXEL_SIZE[] = {10, 11, 12, 13};
+const int32_t HDMX_MAX_WIDTH[] = {5, 6, 7, 7};
+
+bool TestReadingHdmxTable() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ LoadFont(SAMPLE_BITMAP_FONT, factory, &font_array);
+ FontPtr font = font_array[0];
+
+ HorizontalDeviceMetricsTablePtr hdmx_table =
+ down_cast<HorizontalDeviceMetricsTable*>(font->GetTable(Tag::hdmx));
+
+ EXPECT_FALSE(hdmx_table == NULL);
+
+ EXPECT_EQ(hdmx_table->Version(), HDMX_VERSION);
+ EXPECT_EQ(hdmx_table->NumRecords(), HDMX_NUM_RECORDS);
+ EXPECT_EQ(hdmx_table->RecordSize(), HDMX_RECORD_SIZE);
+
+ for (int32_t i = 0; i < HDMX_NUM_RECORDS; ++i) {
+ EXPECT_EQ(hdmx_table->PixelSize(i), HDMX_PIXEL_SIZE[i]);
+ EXPECT_EQ(hdmx_table->MaxWidth(i), HDMX_MAX_WIDTH[i]);
+ }
+
+ EXPECT_EQ(hdmx_table->Width(0, 0), HDMX_MAX_WIDTH[0]);
+ EXPECT_EQ(hdmx_table->Width(0, 19), HDMX_MAX_WIDTH[0]);
+ EXPECT_EQ(hdmx_table->Width(0, 623), HDMX_MAX_WIDTH[0]);
+ EXPECT_EQ(hdmx_table->Width(1, 0), HDMX_MAX_WIDTH[1]);
+ EXPECT_EQ(hdmx_table->Width(1, 19), HDMX_MAX_WIDTH[1]);
+ EXPECT_EQ(hdmx_table->Width(1, 623), HDMX_MAX_WIDTH[1]);
+ EXPECT_EQ(hdmx_table->Width(2, 0), HDMX_MAX_WIDTH[2]);
+ EXPECT_EQ(hdmx_table->Width(2, 19), HDMX_MAX_WIDTH[2]);
+ EXPECT_EQ(hdmx_table->Width(2, 623), HDMX_MAX_WIDTH[2]);
+ EXPECT_EQ(hdmx_table->Width(3, 0), HDMX_MAX_WIDTH[3]);
+ EXPECT_EQ(hdmx_table->Width(3, 19), HDMX_MAX_WIDTH[3]);
+ EXPECT_EQ(hdmx_table->Width(3, 623), HDMX_MAX_WIDTH[3]);
+
+#if defined(SFNTLY_NO_EXCEPTION)
+ EXPECT_EQ(hdmx_table->PixelSize(4), -1);
+ EXPECT_EQ(hdmx_table->PixelSize(-1), -1);
+ EXPECT_EQ(hdmx_table->MaxWidth(4), -1);
+ EXPECT_EQ(hdmx_table->MaxWidth(-1), -1);
+ EXPECT_EQ(hdmx_table->Width(0, 624), -1);
+ EXPECT_EQ(hdmx_table->Width(1, -1), -1);
+ EXPECT_EQ(hdmx_table->Width(-1, 0), -1);
+ EXPECT_EQ(hdmx_table->Width(-1, -1), -1);
+#endif
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(HdmxTable, All) {
+ ASSERT_TRUE(sfntly::TestReadingHdmxTable());
+}
diff --git a/test/lock_test.cc b/test/lock_test.cc
new file mode 100644
index 0000000..b29a4bf
--- /dev/null
+++ b/test/lock_test.cc
@@ -0,0 +1,244 @@
+/*
+ * 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 <stdlib.h>
+
+#include "gtest/gtest.h"
+#include "sfntly/port/lock.h"
+#include "test/platform_thread.h"
+
+namespace sfntly {
+
+// Basic test to make sure that Acquire()/Unlock()/Try() don't crash
+
+class BasicLockTestThread : public PlatformThread::Delegate {
+ public:
+ BasicLockTestThread(Lock* lock) : lock_(lock), acquired_(0) {}
+
+ virtual void ThreadMain() {
+ for (int i = 0; i < 10; i++) {
+ lock_->Acquire();
+ acquired_++;
+ lock_->Unlock();
+ }
+ for (int i = 0; i < 10; i++) {
+ lock_->Acquire();
+ acquired_++;
+ PlatformThread::Sleep(rand() % 20);
+ lock_->Unlock();
+ }
+ for (int i = 0; i < 10; i++) {
+ if (lock_->Try()) {
+ acquired_++;
+ PlatformThread::Sleep(rand() % 20);
+ lock_->Unlock();
+ }
+ }
+ }
+
+ int acquired() const { return acquired_; }
+
+ private:
+ Lock* lock_;
+ int acquired_;
+
+ NO_COPY_AND_ASSIGN(BasicLockTestThread);
+};
+
+bool BasicLockTest() {
+ Lock lock;
+ BasicLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
+
+ int acquired = 0;
+ for (int i = 0; i < 5; i++) {
+ lock.Acquire();
+ acquired++;
+ lock.Unlock();
+ }
+ for (int i = 0; i < 10; i++) {
+ lock.Acquire();
+ acquired++;
+ PlatformThread::Sleep(rand() % 20);
+ lock.Unlock();
+ }
+ for (int i = 0; i < 10; i++) {
+ if (lock.Try()) {
+ acquired++;
+ PlatformThread::Sleep(rand() % 20);
+ lock.Unlock();
+ }
+ }
+ for (int i = 0; i < 5; i++) {
+ lock.Acquire();
+ acquired++;
+ PlatformThread::Sleep(rand() % 20);
+ lock.Unlock();
+ }
+
+ PlatformThread::Join(handle);
+
+ EXPECT_GE(acquired, 20);
+ EXPECT_GE(thread.acquired(), 20);
+
+ return true;
+}
+
+// Test that Try() works as expected -------------------------------------------
+
+class TryLockTestThread : public PlatformThread::Delegate {
+ public:
+ TryLockTestThread(Lock* lock) : lock_(lock), got_lock_(false) {}
+
+ virtual void ThreadMain() {
+ got_lock_ = lock_->Try();
+ if (got_lock_)
+ lock_->Unlock();
+ }
+
+ bool got_lock() const { return got_lock_; }
+
+ private:
+ Lock* lock_;
+ bool got_lock_;
+
+ NO_COPY_AND_ASSIGN(TryLockTestThread);
+};
+
+bool TryLockTest() {
+ Lock lock;
+
+ EXPECT_TRUE(lock.Try());
+ // We now have the lock....
+
+ // This thread will not be able to get the lock.
+ {
+ TryLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
+
+ PlatformThread::Join(handle);
+
+ EXPECT_FALSE(thread.got_lock());
+ }
+
+ lock.Unlock();
+
+ // This thread will....
+ {
+ TryLockTestThread thread(&lock);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
+
+ PlatformThread::Join(handle);
+
+ EXPECT_TRUE(thread.got_lock());
+ // But it released it....
+ EXPECT_TRUE(lock.Try());
+ }
+
+ lock.Unlock();
+ return true;
+}
+
+// Tests that locks actually exclude -------------------------------------------
+
+class MutexLockTestThread : public PlatformThread::Delegate {
+ public:
+ MutexLockTestThread(Lock* lock, int* value) : lock_(lock), value_(value) {}
+
+ // Static helper which can also be called from the main thread.
+ static void DoStuff(Lock* lock, int* value) {
+ for (int i = 0; i < 40; i++) {
+ lock->Acquire();
+ int v = *value;
+ PlatformThread::Sleep(rand() % 10);
+ *value = v + 1;
+ lock->Unlock();
+ }
+ }
+
+ virtual void ThreadMain() {
+ DoStuff(lock_, value_);
+ }
+
+ private:
+ Lock* lock_;
+ int* value_;
+
+ NO_COPY_AND_ASSIGN(MutexLockTestThread);
+};
+
+bool MutexTwoThreads() {
+ Lock lock;
+ int value = 0;
+
+ MutexLockTestThread thread(&lock, &value);
+ PlatformThreadHandle handle = kNullThreadHandle;
+
+ EXPECT_TRUE(PlatformThread::Create(&thread, &handle));
+
+ MutexLockTestThread::DoStuff(&lock, &value);
+
+ PlatformThread::Join(handle);
+
+ EXPECT_EQ(2 * 40, value);
+ return true;
+}
+
+bool MutexFourThreads() {
+ Lock lock;
+ int value = 0;
+
+ MutexLockTestThread thread1(&lock, &value);
+ MutexLockTestThread thread2(&lock, &value);
+ MutexLockTestThread thread3(&lock, &value);
+ PlatformThreadHandle handle1 = kNullThreadHandle;
+ PlatformThreadHandle handle2 = kNullThreadHandle;
+ PlatformThreadHandle handle3 = kNullThreadHandle;
+
+ EXPECT_TRUE(PlatformThread::Create(&thread1, &handle1));
+ EXPECT_TRUE(PlatformThread::Create(&thread2, &handle2));
+ EXPECT_TRUE(PlatformThread::Create(&thread3, &handle3));
+
+ MutexLockTestThread::DoStuff(&lock, &value);
+
+ PlatformThread::Join(handle1);
+ PlatformThread::Join(handle2);
+ PlatformThread::Join(handle3);
+
+ EXPECT_EQ(4 * 40, value);
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(LockTest, Basic) {
+ ASSERT_TRUE(sfntly::BasicLockTest());
+}
+
+TEST(LockTest, TryLock) {
+ ASSERT_TRUE(sfntly::TryLockTest());
+}
+
+TEST(LockTest, Mutex) {
+ ASSERT_TRUE(sfntly::MutexTwoThreads());
+ ASSERT_TRUE(sfntly::MutexFourThreads());
+}
diff --git a/test/memory_io_test.cc b/test/memory_io_test.cc
new file mode 100755
index 0000000..b34e1e7
--- /dev/null
+++ b/test/memory_io_test.cc
@@ -0,0 +1,102 @@
+/*
+ * 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 <algorithm>
+
+#include "gtest/gtest.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "sfntly/port/type.h"
+
+namespace {
+ const char* kTestData =
+"01234567890123456789012345678901234567890123456789" // 50
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx" // 100
+"yz"; // 102
+ const size_t kTestBufferLen = 102;
+}
+
+namespace sfntly {
+
+bool TestMemoryInputStream() {
+ ByteVector test_buffer;
+ test_buffer.resize(kTestBufferLen);
+ std::copy(kTestData, kTestData + kTestBufferLen, test_buffer.begin());
+
+ MemoryInputStream is;
+ is.Attach(&(test_buffer[0]), kTestBufferLen);
+ EXPECT_EQ(is.Available(), (int32_t)kTestBufferLen);
+
+ // Read one byte
+ EXPECT_EQ(is.Read(), '0'); // position 1
+ EXPECT_EQ(is.Read(), '1'); // position 2
+ EXPECT_EQ(is.Read(), '2'); // position 3
+
+ // Read byte vector
+ ByteVector b;
+ b.resize(7);
+ EXPECT_EQ(is.Read(&b), 7); // position 10
+ EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 3, 7), 0);
+
+ b.resize(17);
+ EXPECT_EQ(is.Read(&b, 7, 10), 10); // position 20
+ EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 3, 17), 0);
+
+ // Test skip
+ b.clear();
+ b.resize(10);
+ EXPECT_EQ(is.Skip(30), 30); // position 50
+ EXPECT_EQ(is.Read(&b), 10); // position 60
+ EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 50, 10), 0);
+ b.clear();
+ b.resize(10);
+ EXPECT_EQ(is.Skip(-20), -20); // position 40
+ EXPECT_EQ(is.Read(&b), 10); // position 50
+ EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + 40, 10), 0);
+
+ EXPECT_EQ(is.Available(), (int32_t)kTestBufferLen - 50);
+ EXPECT_EQ(is.Skip(-60), -50); // Out of bound, position 0
+ EXPECT_EQ(is.Skip(kTestBufferLen + 10), (int32_t)kTestBufferLen);
+
+ b.clear();
+ b.resize(10);
+ is.Unread(&b);
+ EXPECT_EQ(memcmp(&(b[0]), &(test_buffer[0]) + kTestBufferLen - 10, 10), 0);
+
+ return true;
+}
+
+bool TestMemoryOutputStream() {
+ ByteVector test_buffer;
+ test_buffer.resize(kTestBufferLen);
+ std::copy(kTestData, kTestData + kTestBufferLen, test_buffer.begin());
+
+ MemoryOutputStream os;
+ os.Write(&(test_buffer[0]), (int32_t)50, (int32_t)(kTestBufferLen - 50));
+ EXPECT_EQ(os.Size(), kTestBufferLen - 50);
+ EXPECT_EQ(memcmp(os.Get(), &(test_buffer[0]) + 50, kTestBufferLen - 50), 0);
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(MemoryIO, All) {
+ ASSERT_TRUE(sfntly::TestMemoryInputStream());
+ ASSERT_TRUE(sfntly::TestMemoryOutputStream());
+}
diff --git a/test/name_editing_test.cc b/test/name_editing_test.cc
new file mode 100644
index 0000000..260d9d4
--- /dev/null
+++ b/test/name_editing_test.cc
@@ -0,0 +1,242 @@
+/*
+ * 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.
+ */
+
+// Must include this before ICU to avoid stdint redefinition issue.
+#include "sfntly/port/type.h"
+
+#include <unicode/ustring.h>
+#include <unicode/unistr.h>
+
+#include "gtest/gtest.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "sfntly/table/core/name_table.h"
+#include "sfntly/tag.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+static ByteVector input_buffer;
+
+void LoadTestFile(FontFactory* factory, FontBuilderArray* font_builders) {
+ assert(factory);
+ assert(font_builders);
+ if (input_buffer.empty()) {
+ LoadFile(SAMPLE_TTF_FILE, &input_buffer);
+ }
+ factory->LoadFontsForBuilding(&input_buffer, font_builders);
+}
+
+bool TestChangeOneName() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ LoadTestFile(factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::name));
+
+ // Change the font name.
+ NameEntryBuilderPtr neb =
+ name_builder->NameBuilder(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ U_STRING_DECL(new_name, "Timothy", 7);
+ neb->SetName(new_name);
+
+ // Build the font.
+ FontPtr font;
+ font.Attach(font_builder->Build());
+
+ // Serialize and reload the serialized font.
+ MemoryOutputStream os;
+ factory->SerializeFont(font, &os);
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ FontArray font_array;
+ factory->LoadFonts(&is, &font_array);
+ FontPtr new_font = font_array[0];
+
+ // Check the font name.
+ NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name));
+ UChar* name = name_table->Name(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ EXPECT_TRUE(name != NULL);
+ EXPECT_EQ(u_strcmp(name, new_name), 0);
+ delete[] name;
+ return true;
+}
+
+bool TestModifyNameTableAndRevert() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ LoadTestFile(factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::name));
+
+ // Change the font name.
+ NameEntryBuilderPtr neb =
+ name_builder->NameBuilder(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ NameTable::NameEntry* neb_entry = neb->name_entry();
+ UChar* original_name = neb_entry->Name();
+ EXPECT_TRUE(original_name != NULL);
+
+ U_STRING_DECL(new_name, "Timothy", 7);
+ neb->SetName(new_name);
+ name_builder->RevertNames();
+
+ // Build the font.
+ FontPtr font;
+ font.Attach(font_builder->Build());
+
+ // Serialize and reload the serialized font.
+ MemoryOutputStream os;
+ factory->SerializeFont(font, &os);
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ FontArray font_array;
+ factory->LoadFonts(&is, &font_array);
+ FontPtr new_font = font_array[0];
+
+ // Check the font name.
+ NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name));
+ UChar* name = name_table->Name(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+
+ EXPECT_EQ(u_strcmp(name, original_name), 0);
+ delete[] name;
+ delete[] original_name;
+
+ return true;
+}
+
+bool TestRemoveOneName() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ LoadTestFile(factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::name));
+
+ EXPECT_TRUE(name_builder->Has(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName));
+ EXPECT_TRUE(name_builder->Remove(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName));
+
+ // Build the font.
+ FontPtr font;
+ font.Attach(font_builder->Build());
+
+ // Serialize and reload the serialized font.
+ MemoryOutputStream os;
+ factory->SerializeFont(font, &os);
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ FontArray font_array;
+ factory->LoadFonts(&is, &font_array);
+ FontPtr new_font = font_array[0];
+
+ // Check the font name.
+ NameTablePtr name_table = down_cast<NameTable*>(font->GetTable(Tag::name));
+ UChar* name = name_table->Name(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ EXPECT_TRUE(name == NULL);
+
+ return true;
+}
+
+// Note: Function is not implemented but the test case is built. Uncomment
+// when NameTable::clear() is implemented.
+/*
+bool TestClearAllNamesAndSetOne() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ LoadTestFile(factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ NameTableBuilderPtr name_builder = down_cast<NameTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::name));
+
+ EXPECT_GT(name_builder->builderCount(), 0);
+ name_builder->clear();
+ EXPECT_EQ(name_builder->builderCount(), 0);
+
+ // Change the font name.
+ NameEntryBuilderPtr neb =
+ name_builder->NameBuilder(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ U_STRING_DECL(new_name, "Fred", 4);
+ neb->SetName(new_name);
+
+ // Build the font.
+ FontPtr font = font_builder->Build();
+
+ // Serialize and reload the serialized font.
+ MemoryOutputStream os;
+ factory->SerializeFont(font, &os);
+ FontArray font_array;
+ ByteArrayPtr new_ba = new MemoryByteArray(os.Get(), os.Size());
+ factory->LoadFonts(new_ba, &font_array);
+ FontPtr new_font = font_array[0];
+
+ // Check the font name.
+ NameTablePtr name_table = down_cast<NameTable*>(font->table(Tag::name));
+ UChar* name = name_table->Name(PlatformId::kWindows,
+ WindowsEncodingId::kUnicodeUCS2,
+ WindowsLanguageId::kEnglish_UnitedStates,
+ NameId::kFontFamilyName);
+ EXPECT_EQ(name_table->NameCount(), 1);
+ EXPECT_EQ(u_strcmp(name, new_name), 0);
+
+ delete[] name;
+ return true;
+}
+*/
+
+} // namespace sfntly
+
+TEST(NameEditing, All) {
+ EXPECT_TRUE(sfntly::TestChangeOneName());
+ EXPECT_TRUE(sfntly::TestModifyNameTableAndRevert());
+ EXPECT_TRUE(sfntly::TestRemoveOneName());
+}
diff --git a/test/open_type_data_test.cc b/test/open_type_data_test.cc
new file mode 100644
index 0000000..0917aab
--- /dev/null
+++ b/test/open_type_data_test.cc
@@ -0,0 +1,70 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/data/writable_font_data.h"
+#include "sfntly/data/memory_byte_array.h"
+
+namespace sfntly {
+
+const byte_t TEST_OTF_DATA[] =
+ {0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01};
+
+bool TestOTFRead() {
+ ByteVector bytes;
+ for (size_t i = 0; i < sizeof(TEST_OTF_DATA) / sizeof(byte_t); ++i) {
+ bytes.push_back(TEST_OTF_DATA[i]);
+ }
+ ByteArrayPtr array = new MemoryByteArray(&(bytes[0]), bytes.size());
+ ReadableFontDataPtr data = new ReadableFontData(array);
+
+ EXPECT_EQ(-1, data->ReadByte(0));
+ EXPECT_EQ(0xff, data->ReadUByte(0));
+ EXPECT_EQ(0x01, data->ReadByte(1));
+ EXPECT_EQ(65281, data->ReadUShort(0));
+ EXPECT_EQ(-255, data->ReadShort(0));
+ EXPECT_EQ(16711937, data->ReadUInt24(0));
+ EXPECT_EQ(4278255873LL, data->ReadULong(0));
+ EXPECT_EQ(-16711423, data->ReadLong(0));
+ return true;
+}
+
+bool TestOTFCopy() {
+ ByteVector source_bytes(1024);
+ for (size_t i = 0; i < source_bytes.size(); ++i) {
+ source_bytes[i] = (byte_t)(i & 0xff);
+ }
+ ByteArrayPtr source_array = new MemoryByteArray(&(source_bytes[0]), 1024);
+ ReadableFontDataPtr source = new ReadableFontData(source_array);
+
+ ByteVector destination_bytes(1024);
+ ByteArrayPtr destination_array =
+ new MemoryByteArray(&(destination_bytes[0]), 1024);
+ WritableFontDataPtr destination = new WritableFontData(destination_array);
+
+ int32_t length = source->CopyTo(destination);
+ EXPECT_EQ(1024, length);
+ EXPECT_TRUE(std::equal(source_bytes.begin(), source_bytes.end(),
+ destination_bytes.begin()));
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(OpenTypeData, All) {
+ ASSERT_TRUE(sfntly::TestOTFRead());
+ ASSERT_TRUE(sfntly::TestOTFCopy());
+}
diff --git a/test/otf_basic_editing_test.cc b/test/otf_basic_editing_test.cc
new file mode 100644
index 0000000..7388fac
--- /dev/null
+++ b/test/otf_basic_editing_test.cc
@@ -0,0 +1,90 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/table/core/font_header_table.h"
+#include "sfntly/tag.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/port/endian.h"
+#include "sfntly/port/file_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+bool TestOTFBasicEditing() {
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ BuilderForFontFile(SAMPLE_TTF_FILE, factory, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ // ensure the builder is not bogus
+ EXPECT_TRUE(font_builder != NULL);
+ TableBuilderMap* builder_map = font_builder->table_builders();
+ EXPECT_TRUE(builder_map != NULL);
+ IntegerSet builder_tags;
+ for (TableBuilderMap::iterator i = builder_map->begin(),
+ e = builder_map->end(); i != e; ++i) {
+ EXPECT_TRUE(i->second != NULL);
+ if (i->second == NULL) {
+ char tag[5] = {0};
+ int32_t value = ToBE32(i->first);
+ memcpy(tag, &value, 4);
+ fprintf(stderr, "tag %s does not have valid builder\n", tag);
+ } else {
+ builder_tags.insert(i->first);
+ }
+ }
+
+ FontHeaderTableBuilderPtr header_builder =
+ down_cast<FontHeaderTable::Builder*>(
+ font_builder->GetTableBuilder(Tag::head));
+ int64_t mod_date = header_builder->Modified();
+ header_builder->SetModified(mod_date + 1);
+ FontPtr font;
+ font.Attach(font_builder->Build());
+
+ // ensure every table had a builder
+ const TableMap* table_map = font->GetTableMap();
+ for (TableMap::const_iterator i = table_map->begin(), e = table_map->end();
+ i != e; ++i) {
+ TablePtr table = (*i).second;
+ HeaderPtr header = table->header();
+ EXPECT_TRUE(builder_tags.find(header->tag()) != builder_tags.end());
+ builder_tags.erase(header->tag());
+ }
+ EXPECT_TRUE(builder_tags.empty());
+
+ FontHeaderTablePtr header =
+ down_cast<FontHeaderTable*>(font->GetTable(Tag::head));
+ int64_t after_mod_date = header->Modified();
+ EXPECT_EQ(mod_date + 1, after_mod_date);
+
+ // Checksum correctness of builder.
+ TablePtr post = font->GetTable(Tag::post);
+ EXPECT_EQ(post->CalculatedChecksum(), TTF_CHECKSUM[SAMPLE_TTF_POST]);
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(OTFBasicEditing, All) {
+ ASSERT_TRUE(sfntly::TestOTFBasicEditing());
+}
diff --git a/test/platform_thread.cc b/test/platform_thread.cc
new file mode 100644
index 0000000..6a0b84b
--- /dev/null
+++ b/test/platform_thread.cc
@@ -0,0 +1,101 @@
+/*
+ * 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 "test/platform_thread.h"
+
+namespace sfntly {
+
+#if defined (WIN32)
+
+DWORD __stdcall ThreadFunc(void* params) {
+ PlatformThread::Delegate* delegate =
+ static_cast<PlatformThread::Delegate*>(params);
+ delegate->ThreadMain();
+ return 0;
+}
+
+// static
+bool PlatformThread::Create(Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ assert(thread_handle);
+ *thread_handle = CreateThread(NULL, 0, ThreadFunc, delegate, 0, NULL);
+ if (!(*thread_handle)) {
+ return false;
+ }
+
+ return true;
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ assert(thread_handle);
+ DWORD result = WaitForSingleObject(thread_handle, INFINITE);
+ assert(result == WAIT_OBJECT_0);
+ CloseHandle(thread_handle);
+}
+
+// static
+void PlatformThread::Sleep(int32_t duration_ms) {
+ ::Sleep(duration_ms);
+}
+
+#else
+
+void* ThreadFunc(void* params) {
+ PlatformThread::Delegate* delegate =
+ static_cast<PlatformThread::Delegate*>(params);
+ delegate->ThreadMain();
+ return NULL;
+}
+
+// static
+bool PlatformThread::Create(Delegate* delegate,
+ PlatformThreadHandle* thread_handle) {
+ assert(thread_handle);
+
+ bool success = false;
+ pthread_attr_t attributes;
+ pthread_attr_init(&attributes);
+ success = !pthread_create(thread_handle, &attributes, ThreadFunc, delegate);
+ pthread_attr_destroy(&attributes);
+
+ return success;
+}
+
+// static
+void PlatformThread::Join(PlatformThreadHandle thread_handle) {
+ assert(thread_handle);
+ pthread_join(thread_handle, NULL);
+}
+
+// static
+void PlatformThread::Sleep(int32_t duration_ms) {
+ struct timespec sleep_time, remaining;
+
+ // Contains the portion of duration_ms >= 1 sec.
+ sleep_time.tv_sec = duration_ms / 1000;
+ duration_ms -= sleep_time.tv_sec * 1000;
+
+ // Contains the portion of duration_ms < 1 sec.
+ sleep_time.tv_nsec = duration_ms * 1000 * 1000; // nanoseconds.
+
+ while (nanosleep(&sleep_time, &remaining) == -1 && errno == EINTR)
+ sleep_time = remaining;
+}
+
+#endif // WIN32
+
+} // namespace sfntly
diff --git a/test/platform_thread.h b/test/platform_thread.h
new file mode 100644
index 0000000..f236f4c
--- /dev/null
+++ b/test/platform_thread.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.
+ */
+
+// Simple platform thread implementation used to test our cross-platform locks.
+// This is a trimmed down version of Chromium base/threading/platform_thread.h.
+
+#ifndef SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_
+#define SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_
+
+#if defined (WIN32)
+#include <windows.h>
+#else // Assume pthread
+#include <errno.h>
+#include <pthread.h>
+#include <time.h>
+#endif // if defined (WIN32)
+
+#include "sfntly/port/type.h"
+
+namespace sfntly {
+
+#if defined (WIN32)
+typedef HANDLE PlatformThreadHandle;
+const PlatformThreadHandle kNullThreadHandle = NULL;
+#else // Assume pthread
+typedef pthread_t PlatformThreadHandle;
+const PlatformThreadHandle kNullThreadHandle = 0;
+#endif
+
+class PlatformThread {
+ public:
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+ virtual void ThreadMain() = 0;
+ };
+
+ // Sleeps for the specified duration (units are milliseconds).
+ static void Sleep(int32_t duration_ms);
+
+ // Creates a new thread using default stack size. Upon success,
+ // |*thread_handle| will be assigned a handle to the newly created thread,
+ // and |delegate|'s ThreadMain method will be executed on the newly created
+ // thread.
+ // NOTE: When you are done with the thread handle, you must call Join to
+ // release system resources associated with the thread. You must ensure that
+ // the Delegate object outlives the thread.
+ static bool Create(Delegate* delegate, PlatformThreadHandle* thread_handle);
+
+ // Joins with a thread created via the Create function. This function blocks
+ // the caller until the designated thread exits. This will invalidate
+ // |thread_handle|.
+ static void Join(PlatformThreadHandle thread_handle);
+
+private:
+ PlatformThread() {}
+ NO_COPY_AND_ASSIGN(PlatformThread);
+};
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_TEST_PLATFORM_THREAD_H_
diff --git a/test/serialization_test.cc b/test/serialization_test.cc
new file mode 100755
index 0000000..0f2f489
--- /dev/null
+++ b/test/serialization_test.cc
@@ -0,0 +1,151 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/memory_input_stream.h"
+#include "sfntly/port/memory_output_stream.h"
+#include "test/test_data.h"
+#include "test/test_font_utils.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+bool TestSerialization() {
+ FontFactoryPtr factory1, factory2, factory3;
+ factory1.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ LoadFont(SAMPLE_TTF_FILE, factory1, &font_array);
+ FontPtr original = font_array[0];
+
+ factory2.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ BuilderForFontFile(SAMPLE_TTF_FILE, factory2, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ FontPtr intermediate;
+ intermediate.Attach(font_builder->Build());
+ MemoryOutputStream os;
+ factory2->SerializeFont(intermediate, &os);
+
+ factory3.Attach(FontFactory::GetInstance());
+ FontArray new_font_array;
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ factory3->LoadFonts(&is, &new_font_array);
+ FontPtr serialized = new_font_array[0];
+
+ // Check number of tables
+ EXPECT_EQ(original->num_tables(), serialized->num_tables());
+
+ // Check if same set of tables
+ const TableMap* original_tables = original->GetTableMap();
+ const TableMap* serialized_tables = serialized->GetTableMap();
+ EXPECT_EQ(original_tables->size(), serialized_tables->size());
+ TableMap::const_iterator not_found = serialized_tables->end();
+ for (TableMap::const_iterator b = original_tables->begin(),
+ e = original_tables->end(); b != e; ++b) {
+ EXPECT_TRUE((serialized_tables->find(b->first) != not_found));
+ }
+
+ // TODO(arthurhsu): check cmap equivalence
+ // Check checksum equivalence
+ for (size_t i = 0; i < SAMPLE_TTF_KNOWN_TAGS; ++i) {
+ TablePtr original_table = original->GetTable(TTF_KNOWN_TAGS[i]);
+ TablePtr serialized_table = serialized->GetTable(TTF_KNOWN_TAGS[i]);
+ EXPECT_EQ(original_table->CalculatedChecksum(),
+ serialized_table->CalculatedChecksum());
+ EXPECT_EQ(original_table->DataLength(), serialized_table->DataLength());
+
+ if (TTF_KNOWN_TAGS[i] == Tag::hhea) {
+ EXPECT_TRUE(VerifyHHEA(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::glyf) {
+ EXPECT_TRUE(VerifyGLYF(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::hmtx) {
+ EXPECT_TRUE(VerifyHMTX(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::loca) {
+ EXPECT_TRUE(VerifyLOCA(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::maxp) {
+ EXPECT_TRUE(VerifyMAXP(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::name) {
+ EXPECT_TRUE(VerifyNAME(original_table, serialized_table));
+ } else if (TTF_KNOWN_TAGS[i] == Tag::OS_2) {
+ EXPECT_TRUE(VerifyOS_2(original_table, serialized_table));
+ }
+ }
+
+ return true;
+}
+
+bool TestSerializationBitmap() {
+ FontFactoryPtr factory1, factory2, factory3;
+ factory1.Attach(FontFactory::GetInstance());
+ FontArray font_array;
+ LoadFont(SAMPLE_BITMAP_FONT, factory1, &font_array);
+ FontPtr original = font_array[0];
+
+ factory2.Attach(FontFactory::GetInstance());
+ FontBuilderArray font_builder_array;
+ BuilderForFontFile(SAMPLE_BITMAP_FONT, factory2, &font_builder_array);
+ FontBuilderPtr font_builder = font_builder_array[0];
+
+ FontPtr intermediate;
+ intermediate.Attach(font_builder->Build());
+ MemoryOutputStream os;
+ factory2->SerializeFont(intermediate, &os);
+
+ factory3.Attach(FontFactory::GetInstance());
+ FontArray new_font_array;
+ MemoryInputStream is;
+ is.Attach(os.Get(), os.Size());
+ factory3->LoadFonts(&is, &new_font_array);
+ FontPtr serialized = new_font_array[0];
+
+ // Check number of tables
+ EXPECT_EQ(original->num_tables(), serialized->num_tables());
+
+ // Check if same set of tables
+ const TableMap* original_tables = original->GetTableMap();
+ const TableMap* serialized_tables = serialized->GetTableMap();
+ EXPECT_EQ(original_tables->size(), serialized_tables->size());
+ TableMap::const_iterator not_found = serialized_tables->end();
+ for (TableMap::const_iterator b = original_tables->begin(),
+ e = original_tables->end(); b != e; ++b) {
+ EXPECT_TRUE((serialized_tables->find(b->first) != not_found));
+ }
+
+ // Check checksum equivalence
+ for (size_t i = 0; i < SAMPLE_BITMAP_KNOWN_TAGS; ++i) {
+ TablePtr original_table = original->GetTable(BITMAP_KNOWN_TAGS[i]);
+ TablePtr serialized_table = serialized->GetTable(BITMAP_KNOWN_TAGS[i]);
+ EXPECT_EQ(original_table->CalculatedChecksum(),
+ serialized_table->CalculatedChecksum());
+ EXPECT_EQ(original_table->DataLength(), serialized_table->DataLength());
+ }
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(Serialization, Simple) {
+ ASSERT_TRUE(sfntly::TestSerialization());
+}
+
+TEST(Serialization, Bitmap) {
+ ASSERT_TRUE(sfntly::TestSerializationBitmap());
+}
diff --git a/test/serialization_test.h b/test/serialization_test.h
new file mode 100644
index 0000000..8996793
--- /dev/null
+++ b/test/serialization_test.h
@@ -0,0 +1,34 @@
+/*
+ * 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_TEST_SERIALIZATION_TEST_H_
+#define SFNTLY_CPP_SRC_TEST_SERIALIZATION_TEST_H_
+
+#include "sfntly/table/table.h"
+
+namespace sfntly {
+
+bool VerifyHHEA(Table* original, Table* target);
+bool VerifyGLYF(Table* original, Table* target);
+bool VerifyHMTX(Table* original, Table* target);
+bool VerifyLOCA(Table* original, Table* target);
+bool VerifyMAXP(Table* original, Table* target);
+bool VerifyNAME(Table* original, Table* target);
+bool VerifyOS_2(Table* original, Table* target);
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_TEST_SERIALIZATION_TEST_H_
diff --git a/test/smart_pointer_test.cc b/test/smart_pointer_test.cc
new file mode 100644
index 0000000..9e81baa
--- /dev/null
+++ b/test/smart_pointer_test.cc
@@ -0,0 +1,79 @@
+/*
+ * 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 "gtest/gtest.h"
+#define ENABLE_OBJECT_COUNTER
+#include "sfntly/port/refcount.h"
+
+using sfntly::RefCounted;
+using sfntly::Ptr;
+
+class Foo : public RefCounted<Foo> {
+public: // put in something to make sure it's not empty
+ int foo_;
+ int foo() { return foo_; }
+};
+
+bool TestSmartPointer() {
+ // scope out allocation
+ {
+ Ptr<Foo> p1;
+ p1 = new Foo();
+ EXPECT_EQ(size_t(1), p1->ref_count_);
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+
+ Ptr<Foo> p2;
+ p2 = p1;
+ EXPECT_EQ(size_t(2), p1->ref_count_);
+ EXPECT_EQ(size_t(2), p2->ref_count_);
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+
+ Ptr<Foo> p3;
+ p3 = p1;
+ EXPECT_EQ(size_t(3), p1->ref_count_);
+ EXPECT_EQ(size_t(3), p2->ref_count_);
+ EXPECT_EQ(size_t(3), p3->ref_count_);
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+
+ p2 = new Foo();
+ EXPECT_EQ(size_t(2), p1->ref_count_);
+ EXPECT_EQ(size_t(1), p2->ref_count_);
+ EXPECT_EQ(size_t(2), p3->ref_count_);
+ EXPECT_EQ(size_t(2), RefCounted<Foo>::object_counter_);
+
+ p3.Release();
+ EXPECT_EQ(size_t(1), p1->ref_count_);
+ EXPECT_EQ(NULL, p3.p_);
+ EXPECT_EQ(size_t(2), RefCounted<Foo>::object_counter_);
+
+ p2 = NULL;
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+
+ p1 = p1;
+ EXPECT_EQ(size_t(1), p1->ref_count_);
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+
+ p1 = &(*p1);
+ EXPECT_EQ(size_t(1), p1->ref_count_);
+ EXPECT_EQ(size_t(1), RefCounted<Foo>::object_counter_);
+ }
+ EXPECT_EQ(size_t(0), RefCounted<Foo>::object_counter_);
+ return true;
+}
+
+TEST(SmartPointer, All) {
+ ASSERT_TRUE(TestSmartPointer());
+}
diff --git a/test/test_data.cc b/test/test_data.cc
new file mode 100644
index 0000000..05bc759
--- /dev/null
+++ b/test/test_data.cc
@@ -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.
+ */
+
+#include "sfntly/tag.cc"
+#include "test/test_data.h"
+
+namespace sfntly {
+
+// If the TTF file used in test changed, the verify*.cc in test need to be
+// changed also.
+// TODO(arthurhsu): Refactor this into a test class and have all const inside.
+// This way we can test multiple fonts using same set of
+// code.
+
+const char* SAMPLE_TTF_FILE = "Tuffy.ttf";
+const char* SAMPLE_BITMAP_FONT = "AnonymousPro-Regular.ttf";
+
+const size_t SAMPLE_TTF_SIZE = 183936;
+const size_t SAMPLE_TTF_TABLES = 17;
+const size_t SAMPLE_TTF_KNOWN_TAGS = 16;
+const size_t SAMPLE_BITMAP_KNOWN_TAGS = 20;
+const size_t SAMPLE_TTF_FEAT = 3;
+const size_t SAMPLE_TTF_HEAD = 6;
+const size_t SAMPLE_TTF_POST = 14;
+
+const int32_t TTF_KNOWN_TAGS[] = {
+ Tag::OS_2, Tag::cmap, Tag::cvt, Tag::feat, Tag::gasp,
+ Tag::glyf, Tag::head, Tag::hhea, Tag::hmtx, Tag::kern,
+ Tag::loca, Tag::maxp, Tag::morx, Tag::name, Tag::post,
+ Tag::prop };
+
+const int32_t BITMAP_KNOWN_TAGS[] = {
+ Tag::EBDT, Tag::EBLC, Tag::EBSC, Tag::LTSH, Tag::OS_2,
+ Tag::VDMX, Tag::cmap, Tag::cvt, Tag::fpgm, Tag::gasp,
+ Tag::glyf, Tag::hdmx, Tag::head, Tag::hhea, Tag::hmtx,
+ Tag::loca, Tag::maxp, Tag::name, Tag::post, Tag::prep };
+
+const int64_t TTF_CHECKSUM[] = {
+ 0xD463FC48, 0x252028D1, 0x0065078A, 0xC01407B5, 0xFFFF0003,
+ 0x9544342B, 0xFC8F16AD, 0x0EC30C7A, 0xA029CD5D, 0x32513087,
+ 0x05C323B0, 0x06320195, 0x3B67E701, 0xE7DB08F3, 0xD46E5E89,
+ 0xE6EB4A27 };
+
+const int64_t TTF_OFFSET[] = {
+ 0x00000198, 0x00001964, 0x000025B0, 0x0002CA74, 0x0002C854,
+ 0x00003D34, 0x0000011C, 0x00000154, 0x000001F0, 0x000245D8,
+ 0x000025B8, 0x00000178, 0x0002CAB4, 0x00024860, 0x00028854,
+ 0x0002C85C };
+
+const int32_t TTF_LENGTH[] = {
+ 86, 3146, 8, 64, 8,
+ 133284, 54, 36, 6002, 648,
+ 6012, 32, 944, 16371, 16383,
+ 536 };
+
+const unsigned char TTF_FEAT_DATA[] = {
+ 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+ 0, 0, 0, 0x30, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0x34,
+ 0, 0, 1, 1, 0, 0xB, 0, 2, 0, 0, 0, 0x38, 0xC0, 0, 1, 2,
+ 0, 0, 1, 3, 0, 2, 1, 4, 0, 0, 1, 5, 0, 2, 1, 6 };
+
+} // namespace sfntly
diff --git a/test/test_data.h b/test/test_data.h
new file mode 100644
index 0000000..d5e576f
--- /dev/null
+++ b/test/test_data.h
@@ -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.
+ */
+
+#ifndef SFNTLY_CPP_SRC_TEST_TEST_DATA_H_
+#define SFNTLY_CPP_SRC_TEST_TEST_DATA_H_
+
+#include "sfntly/tag.h"
+
+namespace sfntly {
+
+extern const char* SAMPLE_TTF_FILE;
+extern const char* SAMPLE_BITMAP_FONT;
+
+extern const size_t SAMPLE_TTF_SIZE;
+extern const size_t SAMPLE_TTF_TABLES;
+extern const size_t SAMPLE_TTF_KNOWN_TAGS;
+extern const size_t SAMPLE_BITMAP_KNOWN_TAGS;
+extern const size_t SAMPLE_TTF_FEAT;
+extern const size_t SAMPLE_TTF_HEAD;
+extern const size_t SAMPLE_TTF_POST;
+
+extern const int32_t TTF_KNOWN_TAGS[];
+extern const int32_t BITMAP_KNOWN_TAGS[];
+extern const int64_t TTF_CHECKSUM[];
+extern const int64_t TTF_OFFSET[];
+extern const int32_t TTF_LENGTH[];
+extern const unsigned char TTF_FEAT_DATA[];
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_TEST_TEST_DATA_H_
diff --git a/test/test_font_utils.cc b/test/test_font_utils.cc
new file mode 100644
index 0000000..d59b52b
--- /dev/null
+++ b/test/test_font_utils.cc
@@ -0,0 +1,115 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+#include "sfntly/port/file_input_stream.h"
+#include "test/test_font_utils.h"
+
+namespace sfntly {
+
+void BuilderForFontFile(const char* font_path, FontFactory* factory,
+ FontBuilderArray* builders) {
+ assert(factory);
+ FileInputStream is;
+ is.Open(font_path);
+ factory->LoadFontsForBuilding(&is, builders);
+ EXPECT_GT(builders->size(), static_cast<size_t>(0));
+}
+
+void SerializeFont(const char* font_path, FontFactory* factory, Font* font) {
+ assert(font_path);
+ assert(factory);
+ assert(font);
+ MemoryOutputStream output_stream;
+ factory->SerializeFont(font, &output_stream);
+ SerializeToFile(&output_stream, font_path);
+}
+
+void LoadFont(const char* font_path, FontFactory* factory, FontArray* fonts) {
+ FileInputStream is;
+ is.Open(font_path);
+ factory->LoadFonts(&is, fonts);
+ is.Close();
+}
+
+void LoadFontUsingByteVector(const char* font_path,
+ bool fingerprint,
+ FontArray* fonts) {
+ ByteVector bv;
+ LoadFile(font_path, &bv);
+ FontFactoryPtr factory;
+ factory.Attach(FontFactory::GetInstance());
+ factory->FingerprintFont(fingerprint);
+ factory->LoadFonts(&bv, fonts);
+}
+
+void LoadFile(const char* input_file_path, 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
+ EXPECT_NE(input_file, reinterpret_cast<FILE*>(NULL));
+ 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);
+ EXPECT_EQ(bytes_read, file_size);
+ fclose(input_file);
+}
+
+void SerializeToFile(MemoryOutputStream* output_stream, const char* file_path) {
+ assert(file_path);
+ assert(output_stream);
+
+ FILE* output_file = NULL;
+#if defined WIN32
+ fopen_s(&output_file, file_path, "wb");
+#else
+ output_file = fopen(file_path, "wb");
+#endif
+ EXPECT_NE(output_file, reinterpret_cast<FILE*>(NULL));
+ fwrite(output_stream->Get(), 1, output_stream->Size(), output_file);
+ fflush(output_file);
+ fclose(output_file);
+}
+
+void HexDump(const unsigned char* byte_data, size_t length) {
+ if (byte_data == NULL || length == 0) {
+ fprintf(stderr, "<NULL>\n");
+ return;
+ }
+
+ fprintf(stderr, "data length = %ld (%lx)\n", length, length);
+ for (size_t i = 0; i < length; ++i) {
+ fprintf(stderr, "%02x ", byte_data[i]);
+ if ((i & 0xf) == 0xf) {
+ fprintf(stderr, "\n");
+ }
+ }
+ fprintf(stderr, "\n");
+}
+
+} // namespace sfntly
diff --git a/test/test_font_utils.h b/test/test_font_utils.h
new file mode 100644
index 0000000..57fde7a
--- /dev/null
+++ b/test/test_font_utils.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 SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_
+#define SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_
+
+#include "sfntly/font.h"
+#include "sfntly/font_factory.h"
+#include "sfntly/port/memory_output_stream.h"
+
+namespace sfntly {
+
+void BuilderForFontFile(const char* font_path, FontFactory* factory,
+ FontBuilderArray* builders);
+void SerializeFont(const char* font_path, FontFactory* factory, Font* font);
+void LoadFont(const char* font_path, FontFactory* factory, FontArray* fonts);
+void LoadFontUsingByteVector(const char* font_path,
+ bool fingerprint,
+ FontArray* fonts);
+
+void LoadFile(const char* input_file_path, ByteVector* input_buffer);
+void SerializeToFile(MemoryOutputStream* output_stream, const char* file_path);
+
+void HexDump(const unsigned char* byte_data, size_t length);
+
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_TEST_TEST_FONT_UTILS_H_
diff --git a/test/test_utils.cc b/test/test_utils.cc
new file mode 100644
index 0000000..4751b92
--- /dev/null
+++ b/test/test_utils.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 "test/test_utils.h"
+
+#include <stdio.h>
+#include <unicode/ucnv.h>
+#include <unicode/uchar.h>
+
+#include "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/data/memory_byte_array.h"
+#include "sfntly/data/growable_memory_byte_array.h"
+#include "sfntly/port/file_input_stream.h"
+
+namespace sfntly {
+TestUtils::TestUtils() {}
+
+// static
+// OutputStream CreateOutputStream(const char *file_path) {
+// }
+
+// static
+// void TestUtils::CreateNewFile(const char* file_path) {
+// }
+
+// static
+int32_t TestUtils::EncodeOneChar(UConverter* encoder, int16_t uchar) {
+ char* target = new char[ucnv_getMaxCharSize(encoder) * 2];
+ char* target_end;
+ UChar* source = new UChar[2];
+ UChar* source_end;
+ source[0] = (UChar)uchar;
+ source[1] = 0;
+ UErrorCode status = U_ZERO_ERROR;
+ source_end = source;
+ target_end = target;
+ ucnv_fromUnicode(encoder, &target_end, target + 4,
+ (const UChar**)&source_end, source + sizeof(UChar),
+ NULL, TRUE, &status);
+ if (!U_SUCCESS(status)) {
+ fprintf(stderr, "Error occured in conversion of %d: %s\n",
+ uchar, u_errorName(status));
+ delete[] source;
+ delete[] target;
+ return 0;
+ }
+ int32_t enc_char = 0;
+ for (int32_t position = 0; position < target_end - target; ++position) {
+ enc_char <<= 8;
+ enc_char |= (target[position] & 0xff);
+ }
+ delete[] source;
+ delete[] target;
+ return enc_char;
+}
+
+// static
+UConverter* TestUtils::GetEncoder(const char* charset_name) {
+ if (charset_name == NULL || strcmp(charset_name, "") == 0)
+ return NULL;
+ UErrorCode status = U_ZERO_ERROR;
+ UConverter* conv = ucnv_open(charset_name, &status);
+ // if (!U_SUCCESS(status))
+ // return NULL;
+ return conv; // returns NULL @ error anyway
+}
+
+// Get a file's extension
+// static
+const char* TestUtils::Extension(const char* file_path) {
+ if (!file_path)
+ return NULL;
+ return strrchr(file_path, EXTENSION_SEPARATOR);
+}
+}
diff --git a/test/test_utils.h b/test/test_utils.h
new file mode 100644
index 0000000..af3ffd6
--- /dev/null
+++ b/test/test_utils.h
@@ -0,0 +1,110 @@
+/*
+ * 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_TEST_TEST_UTILS_H_
+#define SFNTLY_CPP_SRC_TEST_TEST_UTILS_H_
+
+// Must include this before ICU to avoid stdint redefinition issue.
+#include "sfntly/port/type.h"
+
+#include <unicode/ucnv.h>
+
+#include <string>
+
+#include "sfntly/font.h"
+#include "sfntly/data/memory_byte_array.h"
+
+namespace sfntly {
+class TestUtils {
+ TestUtils();
+
+ public:
+ // Compare sections of two byte arrays for equality
+ // @param b1 byte array 1
+ // @param offset1 offset for comparison in byte array 1
+ // @param b2 byte array 2
+ // @param offset2 offset for comparison in byte array 2
+ // @param length the length of the byte arrays to compare
+ // @return true if the array segments are equal; false otherwise
+ // TODO(dfilimon): implement
+ static bool Equals(ByteArray* b1,
+ int32_t offset1,
+ ByteArray* b2,
+ int32_t offset2);
+
+ // @param offset1 offset to start comparing the first ByteArray from
+ // @param ba1 the first ByteArray
+ // @param offset2 offset to start comparing the second ByteArray from
+ // @param ba2 the second ByteArray
+ // @param length the number of bytes to compare
+ // @return true if all bytes in the ranges given are equal; false otherwise
+ // TODO(dfilimon): implement
+ static bool Equals(ByteArray* b1,
+ int32_t offset1,
+ ByteArray* b2,
+ int32_t offset2,
+ int32_t length);
+
+ // TODO(dfilimon): implement FileOutputStream in port/file_output_stream.*
+ // static OutputStream createOutputStream(const char* file_path);
+
+ // TODO(dfilimon): adapt & implement
+ // static FileChannel createFilechannelForWriting(File file);
+
+ // Creates a new file including deleting an already existing file with the
+ // same path and name and creating any needed directories.
+ // TODO(dfilimon): implement
+ static void CreateNewFile(const char* file_path);
+
+ // Converts an integer into a 4 character string using the ASCII encoding.
+ // @param i the value to convert
+ // @return the String based on the number
+ // TODO(dfilimon): implement
+ static void DumpLongAsString(int32_t i, std::string* result);
+
+ // Calculate an OpenType checksum from the array.
+ // @param b the array to calculate checksum on
+ // @param offset the starting index in the array
+ // @param length the number of bytes to check; must be a multiple of 4
+ // @return checksum
+ // TODO(dfilimon): implement
+ static int64_t CheckSum(ByteArray* b, int32_t offset, int32_t length);
+
+ // Encode a single character in UTF-16.
+ // We only support the BMP for now
+ // @param encoder the encoder to use for the encoding
+ // @param uchar the Unicode character to encode
+ // @return the encoded character
+ static int32_t EncodeOneChar(UConverter* encoder, int16_t uchar);
+
+ // Get an encoder for the charset name.
+ // If the name is null or the empty string then just return null.
+ // @param charsetName the charset to get an encoder for
+ // @return an encoder or null if no encoder available for charset name
+ static UConverter* GetEncoder(const char* charsetName);
+
+ private:
+ static const char EXTENSION_SEPARATOR = '.';
+
+ public:
+ // Get the extension of a file's name.
+ // @param file the whose name to process
+ // @return string containing the extension or an empty string if
+ // there is no extension
+ static const char* Extension(const char* file_path);
+};
+}
+#endif // SFNTLY_CPP_SRC_TEST_TEST_UTILS_H_
diff --git a/test/test_utils_test.cc b/test/test_utils_test.cc
new file mode 100644
index 0000000..9afc03c
--- /dev/null
+++ b/test/test_utils_test.cc
@@ -0,0 +1,84 @@
+/*
+ * 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.
+ */
+
+// Must include at the first line to avoid ICU / stdint conflict.
+#include "sfntly/port/type.h"
+
+#include <stdio.h>
+#include <unicode/ucnv.h>
+#include <unicode/uchar.h>
+
+#include "gtest/gtest.h"
+#include "test/test_utils.h"
+
+namespace sfntly {
+
+// Check if proper encoding is being performed
+// Conversion is done from UTF16 to UTF8, SJIS
+bool TestEncoding() {
+ UConverter* conv = TestUtils::GetEncoder("utf8");
+ EXPECT_TRUE(conv != NULL);
+ // Ūnĭcōde̽
+ UChar from[8] = {0x016A, 0x006E, 0x012D, 0x0063, 0x014D, 0x0064, 0x0065,
+ 0x033D};
+ int32_t want[12] = {0xc5, 0xaa, 0x6e, 0xc4, 0xad, 0x63, 0xc5, 0x8d, 0x64,
+ 0x65, 0xcc, 0xbd};
+ int32_t i, j = 0;
+ for (i = 0; i < 7; ++i) {
+ int32_t encoded = TestUtils::EncodeOneChar(conv, (int16_t)from[i]);
+ for (; encoded; encoded <<= 8) {
+ byte_t b = (encoded & 0xff000000) >> 24;
+ if (!b)
+ continue;
+ EXPECT_EQ(want[j], b);
+ if (want[j++] != b) {
+ ucnv_close(conv);
+ return false;
+ }
+ }
+ }
+ ucnv_close(conv);
+ return true;
+}
+
+// Check if the proper extension is obtained
+bool TestExtension() {
+ // usual file name
+ const char *result;
+ result = TestUtils::Extension("../data/ext/tuffy.ttf");
+ EXPECT_EQ(strcmp(result, ".ttf"), 0);
+
+ // more than one 'extension'
+ result = TestUtils::Extension("tuffy.ttf.fake");
+ EXPECT_EQ(strcmp(result, ".fake"), 0);
+
+ // no extension
+ result = TestUtils::Extension("tuffy");
+ EXPECT_STREQ(result, NULL);
+
+ // bogus extension
+ result = TestUtils::Extension("tuffy.");
+ EXPECT_EQ(strcmp(result, "."), 0);
+
+ return true;
+}
+
+} // namespace sfntly
+
+TEST(TestUtils, All) {
+ ASSERT_TRUE(sfntly::TestExtension());
+ ASSERT_TRUE(sfntly::TestEncoding());
+}
diff --git a/test/test_xml_utils.cc b/test/test_xml_utils.cc
new file mode 100644
index 0000000..dc65add
--- /dev/null
+++ b/test/test_xml_utils.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 <map>
+#include <string>
+#include "test/test_xml_utils.h"
+#include "test/tinyxml/tinyxml.h"
+
+namespace sfntly {
+void InternalGetNodesWithName(const TiXmlNode* node, const std::string& name,
+ TiXmlNodeVector* wanted_nodes) {
+ if (node->ValueStr() == name)
+ wanted_nodes->push_back(node);
+ for (const TiXmlNode* child = node->FirstChild();
+ child != NULL; child = child->NextSibling()) {
+ InternalGetNodesWithName(child, name, wanted_nodes);
+ }
+}
+
+TiXmlNodeVector* GetNodesWithName(const TiXmlNode* node,
+ const std::string& name) {
+ TiXmlNodeVector* wanted_nodes = new TiXmlNodeVector;
+ InternalGetNodesWithName(node, name, wanted_nodes);
+ return wanted_nodes;
+}
+
+const TiXmlAttribute* GetAttribute(const TiXmlNode* node,
+ const std::string& name) {
+ for (const TiXmlAttribute* attribute = node->ToElement()->FirstAttribute();
+ attribute != NULL; attribute = attribute->Next()) {
+ if (attribute->Name() == name) {
+ return attribute;
+ }
+ }
+ return NULL;
+}
+}
diff --git a/test/test_xml_utils.h b/test/test_xml_utils.h
new file mode 100644
index 0000000..7a5fe49
--- /dev/null
+++ b/test/test_xml_utils.h
@@ -0,0 +1,33 @@
+/*
+ * 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 "test/tinyxml/tinyxml.h"
+
+#ifndef SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_
+#define SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_
+
+namespace sfntly {
+typedef std::map<std::string, std::string> AttributeMap;
+typedef std::vector<const TiXmlNode*> TiXmlNodeVector;
+
+TiXmlNodeVector* GetNodesWithName(const TiXmlNode* node,
+ const std::string& name);
+const TiXmlAttribute* GetAttribute(const TiXmlNode* node,
+ const std::string& name);
+} // namespace sfntly
+
+#endif // SFNTLY_CPP_SRC_TEST_TEST_XML_UTILS_H_
diff --git a/test/tinyxml/tinystr.cpp b/test/tinyxml/tinystr.cpp
new file mode 100644
index 0000000..0665768
--- /dev/null
+++ b/test/tinyxml/tinystr.cpp
@@ -0,0 +1,111 @@
+/*
+www.sourceforge.net/projects/tinyxml
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TIXML_USE_STL
+
+#include "tinystr.h"
+
+// Error value for find primitive
+const TiXmlString::size_type TiXmlString::npos = static_cast< TiXmlString::size_type >(-1);
+
+
+// Null rep.
+TiXmlString::Rep TiXmlString::nullrep_ = { 0, 0, { '\0' } };
+
+
+void TiXmlString::reserve (size_type cap)
+{
+ if (cap > capacity())
+ {
+ TiXmlString tmp;
+ tmp.init(length(), cap);
+ memcpy(tmp.start(), data(), length());
+ swap(tmp);
+ }
+}
+
+
+TiXmlString& TiXmlString::assign(const char* str, size_type len)
+{
+ size_type cap = capacity();
+ if (len > cap || cap > 3*(len + 8))
+ {
+ TiXmlString tmp;
+ tmp.init(len);
+ memcpy(tmp.start(), str, len);
+ swap(tmp);
+ }
+ else
+ {
+ memmove(start(), str, len);
+ set_size(len);
+ }
+ return *this;
+}
+
+
+TiXmlString& TiXmlString::append(const char* str, size_type len)
+{
+ size_type newsize = length() + len;
+ if (newsize > capacity())
+ {
+ reserve (newsize + capacity());
+ }
+ memmove(finish(), str, len);
+ set_size(newsize);
+ return *this;
+}
+
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ tmp.reserve(a.length() + b.length());
+ tmp += a;
+ tmp += b;
+ return tmp;
+}
+
+TiXmlString operator + (const TiXmlString & a, const char* b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type b_len = static_cast<TiXmlString::size_type>( strlen(b) );
+ tmp.reserve(a.length() + b_len);
+ tmp += a;
+ tmp.append(b, b_len);
+ return tmp;
+}
+
+TiXmlString operator + (const char* a, const TiXmlString & b)
+{
+ TiXmlString tmp;
+ TiXmlString::size_type a_len = static_cast<TiXmlString::size_type>( strlen(a) );
+ tmp.reserve(a_len + b.length());
+ tmp.append(a, a_len);
+ tmp += b;
+ return tmp;
+}
+
+
+#endif // TIXML_USE_STL
diff --git a/test/tinyxml/tinystr.h b/test/tinyxml/tinystr.h
new file mode 100644
index 0000000..89cca33
--- /dev/null
+++ b/test/tinyxml/tinystr.h
@@ -0,0 +1,305 @@
+/*
+www.sourceforge.net/projects/tinyxml
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TIXML_USE_STL
+
+#ifndef TIXML_STRING_INCLUDED
+#define TIXML_STRING_INCLUDED
+
+#include <assert.h>
+#include <string.h>
+
+/* The support for explicit isn't that universal, and it isn't really
+ required - it is used to check that the TiXmlString class isn't incorrectly
+ used. Be nice to old compilers and macro it here:
+*/
+#if defined(_MSC_VER) && (_MSC_VER >= 1200 )
+ // Microsoft visual studio, version 6 and higher.
+ #define TIXML_EXPLICIT explicit
+#elif defined(__GNUC__) && (__GNUC__ >= 3 )
+ // GCC version 3 and higher.s
+ #define TIXML_EXPLICIT explicit
+#else
+ #define TIXML_EXPLICIT
+#endif
+
+
+/*
+ TiXmlString is an emulation of a subset of the std::string template.
+ Its purpose is to allow compiling TinyXML on compilers with no or poor STL support.
+ Only the member functions relevant to the TinyXML project have been implemented.
+ The buffer allocation is made by a simplistic power of 2 like mechanism : if we increase
+ a string and there's no more room, we allocate a buffer twice as big as we need.
+*/
+class TiXmlString
+{
+ public :
+ // The size type used
+ typedef size_t size_type;
+
+ // Error value for find primitive
+ static const size_type npos; // = -1;
+
+
+ // TiXmlString empty constructor
+ TiXmlString () : rep_(&nullrep_)
+ {
+ }
+
+ // TiXmlString copy constructor
+ TiXmlString ( const TiXmlString & copy) : rep_(0)
+ {
+ init(copy.length());
+ memcpy(start(), copy.data(), length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * copy) : rep_(0)
+ {
+ init( static_cast<size_type>( strlen(copy) ));
+ memcpy(start(), copy, length());
+ }
+
+ // TiXmlString constructor, based on a string
+ TIXML_EXPLICIT TiXmlString ( const char * str, size_type len) : rep_(0)
+ {
+ init(len);
+ memcpy(start(), str, len);
+ }
+
+ // TiXmlString destructor
+ ~TiXmlString ()
+ {
+ quit();
+ }
+
+ TiXmlString& operator = (const char * copy)
+ {
+ return assign( copy, (size_type)strlen(copy));
+ }
+
+ TiXmlString& operator = (const TiXmlString & copy)
+ {
+ return assign(copy.start(), copy.length());
+ }
+
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const char * suffix)
+ {
+ return append(suffix, static_cast<size_type>( strlen(suffix) ));
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (char single)
+ {
+ return append(&single, 1);
+ }
+
+ // += operator. Maps to append
+ TiXmlString& operator += (const TiXmlString & suffix)
+ {
+ return append(suffix.data(), suffix.length());
+ }
+
+
+ // Convert a TiXmlString into a null-terminated char *
+ const char * c_str () const { return rep_->str; }
+
+ // Convert a TiXmlString into a char * (need not be null terminated).
+ const char * data () const { return rep_->str; }
+
+ // Return the length of a TiXmlString
+ size_type length () const { return rep_->size; }
+
+ // Alias for length()
+ size_type size () const { return rep_->size; }
+
+ // Checks if a TiXmlString is empty
+ bool empty () const { return rep_->size == 0; }
+
+ // Return capacity of string
+ size_type capacity () const { return rep_->capacity; }
+
+
+ // single char extraction
+ const char& at (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // [] operator
+ char& operator [] (size_type index) const
+ {
+ assert( index < length() );
+ return rep_->str[ index ];
+ }
+
+ // find a char in a string. Return TiXmlString::npos if not found
+ size_type find (char lookup) const
+ {
+ return find(lookup, 0);
+ }
+
+ // find a char in a string from an offset. Return TiXmlString::npos if not found
+ size_type find (char tofind, size_type offset) const
+ {
+ if (offset >= length()) return npos;
+
+ for (const char* p = c_str() + offset; *p != '\0'; ++p)
+ {
+ if (*p == tofind) return static_cast< size_type >( p - c_str() );
+ }
+ return npos;
+ }
+
+ void clear ()
+ {
+ //Lee:
+ //The original was just too strange, though correct:
+ // TiXmlString().swap(*this);
+ //Instead use the quit & re-init:
+ quit();
+ init(0,0);
+ }
+
+ /* Function to reserve a big amount of data when we know we'll need it. Be aware that this
+ function DOES NOT clear the content of the TiXmlString if any exists.
+ */
+ void reserve (size_type cap);
+
+ TiXmlString& assign (const char* str, size_type len);
+
+ TiXmlString& append (const char* str, size_type len);
+
+ void swap (TiXmlString& other)
+ {
+ Rep* r = rep_;
+ rep_ = other.rep_;
+ other.rep_ = r;
+ }
+
+ private:
+
+ void init(size_type sz) { init(sz, sz); }
+ void set_size(size_type sz) { rep_->str[ rep_->size = sz ] = '\0'; }
+ char* start() const { return rep_->str; }
+ char* finish() const { return rep_->str + rep_->size; }
+
+ struct Rep
+ {
+ size_type size, capacity;
+ char str[1];
+ };
+
+ void init(size_type sz, size_type cap)
+ {
+ if (cap)
+ {
+ // Lee: the original form:
+ // rep_ = static_cast<Rep*>(operator new(sizeof(Rep) + cap));
+ // doesn't work in some cases of new being overloaded. Switching
+ // to the normal allocation, although use an 'int' for systems
+ // that are overly picky about structure alignment.
+ const size_type bytesNeeded = sizeof(Rep) + cap;
+ const size_type intsNeeded = ( bytesNeeded + sizeof(int) - 1 ) / sizeof( int );
+ rep_ = reinterpret_cast<Rep*>( new int[ intsNeeded ] );
+
+ rep_->str[ rep_->size = sz ] = '\0';
+ rep_->capacity = cap;
+ }
+ else
+ {
+ rep_ = &nullrep_;
+ }
+ }
+
+ void quit()
+ {
+ if (rep_ != &nullrep_)
+ {
+ // The rep_ is really an array of ints. (see the allocator, above).
+ // Cast it back before delete, so the compiler won't incorrectly call destructors.
+ delete [] ( reinterpret_cast<int*>( rep_ ) );
+ }
+ }
+
+ Rep * rep_;
+ static Rep nullrep_;
+
+} ;
+
+
+inline bool operator == (const TiXmlString & a, const TiXmlString & b)
+{
+ return ( a.length() == b.length() ) // optimization on some platforms
+ && ( strcmp(a.c_str(), b.c_str()) == 0 ); // actual compare
+}
+inline bool operator < (const TiXmlString & a, const TiXmlString & b)
+{
+ return strcmp(a.c_str(), b.c_str()) < 0;
+}
+
+inline bool operator != (const TiXmlString & a, const TiXmlString & b) { return !(a == b); }
+inline bool operator > (const TiXmlString & a, const TiXmlString & b) { return b < a; }
+inline bool operator <= (const TiXmlString & a, const TiXmlString & b) { return !(b < a); }
+inline bool operator >= (const TiXmlString & a, const TiXmlString & b) { return !(a < b); }
+
+inline bool operator == (const TiXmlString & a, const char* b) { return strcmp(a.c_str(), b) == 0; }
+inline bool operator == (const char* a, const TiXmlString & b) { return b == a; }
+inline bool operator != (const TiXmlString & a, const char* b) { return !(a == b); }
+inline bool operator != (const char* a, const TiXmlString & b) { return !(b == a); }
+
+TiXmlString operator + (const TiXmlString & a, const TiXmlString & b);
+TiXmlString operator + (const TiXmlString & a, const char* b);
+TiXmlString operator + (const char* a, const TiXmlString & b);
+
+
+/*
+ TiXmlOutStream is an emulation of std::ostream. It is based on TiXmlString.
+ Only the operators that we need for TinyXML have been developped.
+*/
+class TiXmlOutStream : public TiXmlString
+{
+public :
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const TiXmlString & in)
+ {
+ *this += in;
+ return *this;
+ }
+
+ // TiXmlOutStream << operator.
+ TiXmlOutStream & operator << (const char * in)
+ {
+ *this += in;
+ return *this;
+ }
+
+} ;
+
+#endif // TIXML_STRING_INCLUDED
+#endif // TIXML_USE_STL
diff --git a/test/tinyxml/tinyxml.cpp b/test/tinyxml/tinyxml.cpp
new file mode 100644
index 0000000..9c161df
--- /dev/null
+++ b/test/tinyxml/tinyxml.cpp
@@ -0,0 +1,1886 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+
+#ifdef TIXML_USE_STL
+#include <sstream>
+#include <iostream>
+#endif
+
+#include "tinyxml.h"
+
+FILE* TiXmlFOpen( const char* filename, const char* mode );
+
+bool TiXmlBase::condenseWhiteSpace = true;
+
+// Microsoft compiler security
+FILE* TiXmlFOpen( const char* filename, const char* mode )
+{
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ FILE* fp = 0;
+ errno_t err = fopen_s( &fp, filename, mode );
+ if ( !err && fp )
+ return fp;
+ return 0;
+ #else
+ return fopen( filename, mode );
+ #endif
+}
+
+void TiXmlBase::EncodeString( const TIXML_STRING& str, TIXML_STRING* outString )
+{
+ int i=0;
+
+ while( i<(int)str.length() )
+ {
+ unsigned char c = (unsigned char) str[i];
+
+ if ( c == '&'
+ && i < ( (int)str.length() - 2 )
+ && str[i+1] == '#'
+ && str[i+2] == 'x' )
+ {
+ // Hexadecimal character reference.
+ // Pass through unchanged.
+ // &#xA9; -- copyright symbol, for example.
+ //
+ // The -1 is a bug fix from Rob Laveaux. It keeps
+ // an overflow from happening if there is no ';'.
+ // There are actually 2 ways to exit this loop -
+ // while fails (error case) and break (semicolon found).
+ // However, there is no mechanism (currently) for
+ // this function to return an error.
+ while ( i<(int)str.length()-1 )
+ {
+ outString->append( str.c_str() + i, 1 );
+ ++i;
+ if ( str[i] == ';' )
+ break;
+ }
+ }
+ else if ( c == '&' )
+ {
+ outString->append( entity[0].str, entity[0].strLength );
+ ++i;
+ }
+ else if ( c == '<' )
+ {
+ outString->append( entity[1].str, entity[1].strLength );
+ ++i;
+ }
+ else if ( c == '>' )
+ {
+ outString->append( entity[2].str, entity[2].strLength );
+ ++i;
+ }
+ else if ( c == '\"' )
+ {
+ outString->append( entity[3].str, entity[3].strLength );
+ ++i;
+ }
+ else if ( c == '\'' )
+ {
+ outString->append( entity[4].str, entity[4].strLength );
+ ++i;
+ }
+ else if ( c < 32 )
+ {
+ // Easy pass at non-alpha/numeric/symbol
+ // Below 32 is symbolic.
+ char buf[ 32 ];
+
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #else
+ sprintf( buf, "&#x%02X;", (unsigned) ( c & 0xff ) );
+ #endif
+
+ //*ME: warning C4267: convert 'size_t' to 'int'
+ //*ME: Int-Cast to make compiler happy ...
+ outString->append( buf, (int)strlen( buf ) );
+ ++i;
+ }
+ else
+ {
+ //char realc = (char) c;
+ //outString->append( &realc, 1 );
+ *outString += (char) c; // somewhat more efficient function call.
+ ++i;
+ }
+ }
+}
+
+
+TiXmlNode::TiXmlNode( NodeType _type ) : TiXmlBase()
+{
+ parent = 0;
+ type = _type;
+ firstChild = 0;
+ lastChild = 0;
+ prev = 0;
+ next = 0;
+}
+
+
+TiXmlNode::~TiXmlNode()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+}
+
+
+void TiXmlNode::CopyTo( TiXmlNode* target ) const
+{
+ target->SetValue (value.c_str() );
+ target->userData = userData;
+ target->location = location;
+}
+
+
+void TiXmlNode::Clear()
+{
+ TiXmlNode* node = firstChild;
+ TiXmlNode* temp = 0;
+
+ while ( node )
+ {
+ temp = node;
+ node = node->next;
+ delete temp;
+ }
+
+ firstChild = 0;
+ lastChild = 0;
+}
+
+
+TiXmlNode* TiXmlNode::LinkEndChild( TiXmlNode* node )
+{
+ assert( node->parent == 0 || node->parent == this );
+ assert( node->GetDocument() == 0 || node->GetDocument() == this->GetDocument() );
+
+ if ( node->Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ delete node;
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ node->parent = this;
+
+ node->prev = lastChild;
+ node->next = 0;
+
+ if ( lastChild )
+ lastChild->next = node;
+ else
+ firstChild = node; // it was an empty list.
+
+ lastChild = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertEndChild( const TiXmlNode& addThis )
+{
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+
+ return LinkEndChild( node );
+}
+
+
+TiXmlNode* TiXmlNode::InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis )
+{
+ if ( !beforeThis || beforeThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->next = beforeThis;
+ node->prev = beforeThis->prev;
+ if ( beforeThis->prev )
+ {
+ beforeThis->prev->next = node;
+ }
+ else
+ {
+ assert( firstChild == beforeThis );
+ firstChild = node;
+ }
+ beforeThis->prev = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis )
+{
+ if ( !afterThis || afterThis->parent != this ) {
+ return 0;
+ }
+ if ( addThis.Type() == TiXmlNode::TINYXML_DOCUMENT )
+ {
+ if ( GetDocument() )
+ GetDocument()->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = addThis.Clone();
+ if ( !node )
+ return 0;
+ node->parent = this;
+
+ node->prev = afterThis;
+ node->next = afterThis->next;
+ if ( afterThis->next )
+ {
+ afterThis->next->prev = node;
+ }
+ else
+ {
+ assert( lastChild == afterThis );
+ lastChild = node;
+ }
+ afterThis->next = node;
+ return node;
+}
+
+
+TiXmlNode* TiXmlNode::ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis )
+{
+ if ( !replaceThis )
+ return 0;
+
+ if ( replaceThis->parent != this )
+ return 0;
+
+ if ( withThis.ToDocument() ) {
+ // A document can never be a child. Thanks to Noam.
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_DOCUMENT_TOP_ONLY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ TiXmlNode* node = withThis.Clone();
+ if ( !node )
+ return 0;
+
+ node->next = replaceThis->next;
+ node->prev = replaceThis->prev;
+
+ if ( replaceThis->next )
+ replaceThis->next->prev = node;
+ else
+ lastChild = node;
+
+ if ( replaceThis->prev )
+ replaceThis->prev->next = node;
+ else
+ firstChild = node;
+
+ delete replaceThis;
+ node->parent = this;
+ return node;
+}
+
+
+bool TiXmlNode::RemoveChild( TiXmlNode* removeThis )
+{
+ if ( !removeThis ) {
+ return false;
+ }
+
+ if ( removeThis->parent != this )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ if ( removeThis->next )
+ removeThis->next->prev = removeThis->prev;
+ else
+ lastChild = removeThis->prev;
+
+ if ( removeThis->prev )
+ removeThis->prev->next = removeThis->next;
+ else
+ firstChild = removeThis->next;
+
+ delete removeThis;
+ return true;
+}
+
+const TiXmlNode* TiXmlNode::FirstChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = firstChild; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::LastChild( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = lastChild; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild();
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling();
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::IterateChildren( const char * val, const TiXmlNode* previous ) const
+{
+ if ( !previous )
+ {
+ return FirstChild( val );
+ }
+ else
+ {
+ assert( previous->parent == this );
+ return previous->NextSibling( val );
+ }
+}
+
+
+const TiXmlNode* TiXmlNode::NextSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = next; node; node = node->next )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+const TiXmlNode* TiXmlNode::PreviousSibling( const char * _value ) const
+{
+ const TiXmlNode* node;
+ for ( node = prev; node; node = node->prev )
+ {
+ if ( strcmp( node->Value(), _value ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+void TiXmlElement::RemoveAttribute( const char * name )
+{
+ #ifdef TIXML_USE_STL
+ TIXML_STRING str( name );
+ TiXmlAttribute* node = attributeSet.Find( str );
+ #else
+ TiXmlAttribute* node = attributeSet.Find( name );
+ #endif
+ if ( node )
+ {
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+const TiXmlElement* TiXmlNode::FirstChildElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::FirstChildElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = FirstChild( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement() const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling();
+ node;
+ node = node->NextSibling() )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlElement* TiXmlNode::NextSiblingElement( const char * _value ) const
+{
+ const TiXmlNode* node;
+
+ for ( node = NextSibling( _value );
+ node;
+ node = node->NextSibling( _value ) )
+ {
+ if ( node->ToElement() )
+ return node->ToElement();
+ }
+ return 0;
+}
+
+
+const TiXmlDocument* TiXmlNode::GetDocument() const
+{
+ const TiXmlNode* node;
+
+ for( node = this; node; node = node->parent )
+ {
+ if ( node->ToDocument() )
+ return node->ToDocument();
+ }
+ return 0;
+}
+
+
+TiXmlElement::TiXmlElement (const char * _value)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlElement::TiXmlElement( const std::string& _value )
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ value = _value;
+}
+#endif
+
+
+TiXmlElement::TiXmlElement( const TiXmlElement& copy)
+ : TiXmlNode( TiXmlNode::TINYXML_ELEMENT )
+{
+ firstChild = lastChild = 0;
+ copy.CopyTo( this );
+}
+
+
+TiXmlElement& TiXmlElement::operator=( const TiXmlElement& base )
+{
+ ClearThis();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+TiXmlElement::~TiXmlElement()
+{
+ ClearThis();
+}
+
+
+void TiXmlElement::ClearThis()
+{
+ Clear();
+ while( attributeSet.First() )
+ {
+ TiXmlAttribute* node = attributeSet.First();
+ attributeSet.Remove( node );
+ delete node;
+ }
+}
+
+
+const char* TiXmlElement::Attribute( const char* name ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( node )
+ return node->Value();
+ return 0;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( attrib )
+ return &attrib->ValueStr();
+ return 0;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, int* i ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( i ) {
+ attrib->QueryIntValue( i );
+ }
+ }
+ return result;
+}
+#endif
+
+
+const char* TiXmlElement::Attribute( const char* name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const char* result = 0;
+
+ if ( attrib ) {
+ result = attrib->Value();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+
+
+#ifdef TIXML_USE_STL
+const std::string* TiXmlElement::Attribute( const std::string& name, double* d ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ const std::string* result = 0;
+
+ if ( attrib ) {
+ result = &attrib->ValueStr();
+ if ( d ) {
+ attrib->QueryDoubleValue( d );
+ }
+ }
+ return result;
+}
+#endif
+
+
+int TiXmlElement::QueryIntAttribute( const char* name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+
+
+int TiXmlElement::QueryUnsignedAttribute( const char* name, unsigned* value ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int ival = 0;
+ int result = node->QueryIntValue( &ival );
+ *value = (unsigned)ival;
+ return result;
+}
+
+
+int TiXmlElement::QueryBoolAttribute( const char* name, bool* bval ) const
+{
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ int result = TIXML_WRONG_TYPE;
+ if ( StringEqual( node->Value(), "true", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "yes", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "1", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = true;
+ result = TIXML_SUCCESS;
+ }
+ else if ( StringEqual( node->Value(), "false", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "no", true, TIXML_ENCODING_UNKNOWN )
+ || StringEqual( node->Value(), "0", true, TIXML_ENCODING_UNKNOWN ) )
+ {
+ *bval = false;
+ result = TIXML_SUCCESS;
+ }
+ return result;
+}
+
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryIntAttribute( const std::string& name, int* ival ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryIntValue( ival );
+}
+#endif
+
+
+int TiXmlElement::QueryDoubleAttribute( const char* name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+
+
+#ifdef TIXML_USE_STL
+int TiXmlElement::QueryDoubleAttribute( const std::string& name, double* dval ) const
+{
+ const TiXmlAttribute* attrib = attributeSet.Find( name );
+ if ( !attrib )
+ return TIXML_NO_ATTRIBUTE;
+ return attrib->QueryDoubleValue( dval );
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& name, int val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetIntValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetDoubleAttribute( const char * name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetDoubleAttribute( const std::string& name, double val )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( name );
+ if ( attrib ) {
+ attrib->SetDoubleValue( val );
+ }
+}
+#endif
+
+
+void TiXmlElement::SetAttribute( const char * cname, const char * cvalue )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( cname );
+ if ( attrib ) {
+ attrib->SetValue( cvalue );
+ }
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlElement::SetAttribute( const std::string& _name, const std::string& _value )
+{
+ TiXmlAttribute* attrib = attributeSet.FindOrCreate( _name );
+ if ( attrib ) {
+ attrib->SetValue( _value );
+ }
+}
+#endif
+
+
+void TiXmlElement::Print( FILE* cfile, int depth ) const
+{
+ int i;
+ assert( cfile );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+
+ fprintf( cfile, "<%s", value.c_str() );
+
+ const TiXmlAttribute* attrib;
+ for ( attrib = attributeSet.First(); attrib; attrib = attrib->Next() )
+ {
+ fprintf( cfile, " " );
+ attrib->Print( cfile, depth );
+ }
+
+ // There are 3 different formatting approaches:
+ // 1) An element without children is printed as a <foo /> node
+ // 2) An element with only a text child is printed as <foo> text </foo>
+ // 3) An element with children is printed on multiple lines.
+ TiXmlNode* node;
+ if ( !firstChild )
+ {
+ fprintf( cfile, " />" );
+ }
+ else if ( firstChild == lastChild && firstChild->ToText() )
+ {
+ fprintf( cfile, ">" );
+ firstChild->Print( cfile, depth + 1 );
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+ else
+ {
+ fprintf( cfile, ">" );
+
+ for ( node = firstChild; node; node=node->NextSibling() )
+ {
+ if ( !node->ToText() )
+ {
+ fprintf( cfile, "\n" );
+ }
+ node->Print( cfile, depth+1 );
+ }
+ fprintf( cfile, "\n" );
+ for( i=0; i<depth; ++i ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "</%s>", value.c_str() );
+ }
+}
+
+
+void TiXmlElement::CopyTo( TiXmlElement* target ) const
+{
+ // superclass:
+ TiXmlNode::CopyTo( target );
+
+ // Element class:
+ // Clone the attributes, then clone the children.
+ const TiXmlAttribute* attribute = 0;
+ for( attribute = attributeSet.First();
+ attribute;
+ attribute = attribute->Next() )
+ {
+ target->SetAttribute( attribute->Name(), attribute->Value() );
+ }
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+bool TiXmlElement::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this, attributeSet.First() ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+TiXmlNode* TiXmlElement::Clone() const
+{
+ TiXmlElement* clone = new TiXmlElement( Value() );
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+const char* TiXmlElement::GetText() const
+{
+ const TiXmlNode* child = this->FirstChild();
+ if ( child ) {
+ const TiXmlText* childText = child->ToText();
+ if ( childText ) {
+ return childText->Value();
+ }
+ }
+ return 0;
+}
+
+
+TiXmlDocument::TiXmlDocument() : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ ClearError();
+}
+
+TiXmlDocument::TiXmlDocument( const char * documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDocument::TiXmlDocument( const std::string& documentName ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ tabsize = 4;
+ useMicrosoftBOM = false;
+ value = documentName;
+ ClearError();
+}
+#endif
+
+
+TiXmlDocument::TiXmlDocument( const TiXmlDocument& copy ) : TiXmlNode( TiXmlNode::TINYXML_DOCUMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDocument& TiXmlDocument::operator=( const TiXmlDocument& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+bool TiXmlDocument::LoadFile( TiXmlEncoding encoding )
+{
+ return LoadFile( Value(), encoding );
+}
+
+
+bool TiXmlDocument::SaveFile() const
+{
+ return SaveFile( Value() );
+}
+
+bool TiXmlDocument::LoadFile( const char* _filename, TiXmlEncoding encoding )
+{
+ TIXML_STRING filename( _filename );
+ value = filename;
+
+ // reading in binary mode so that tinyxml can normalize the EOL
+ FILE* file = TiXmlFOpen( value.c_str (), "rb" );
+
+ if ( file )
+ {
+ bool result = LoadFile( file, encoding );
+ fclose( file );
+ return result;
+ }
+ else
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+}
+
+bool TiXmlDocument::LoadFile( FILE* file, TiXmlEncoding encoding )
+{
+ if ( !file )
+ {
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Delete the existing data:
+ Clear();
+ location.Clear();
+
+ // Get the file size, so we can pre-allocate the string. HUGE speed impact.
+ long length = 0;
+ fseek( file, 0, SEEK_END );
+ length = ftell( file );
+ fseek( file, 0, SEEK_SET );
+
+ // Strange case, but good to handle up front.
+ if ( length <= 0 )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Subtle bug here. TinyXml did use fgets. But from the XML spec:
+ // 2.11 End-of-Line Handling
+ // <snip>
+ // <quote>
+ // ...the XML processor MUST behave as if it normalized all line breaks in external
+ // parsed entities (including the document entity) on input, before parsing, by translating
+ // both the two-character sequence #xD #xA and any #xD that is not followed by #xA to
+ // a single #xA character.
+ // </quote>
+ //
+ // It is not clear fgets does that, and certainly isn't clear it works cross platform.
+ // Generally, you expect fgets to translate from the convention of the OS to the c/unix
+ // convention, and not work generally.
+
+ /*
+ while( fgets( buf, sizeof(buf), file ) )
+ {
+ data += buf;
+ }
+ */
+
+ char* buf = new char[ length+1 ];
+ buf[0] = 0;
+
+ if ( fread( buf, length, 1, file ) != 1 ) {
+ delete [] buf;
+ SetError( TIXML_ERROR_OPENING_FILE, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return false;
+ }
+
+ // Process the buffer in place to normalize new lines. (See comment above.)
+ // Copies from the 'p' to 'q' pointer, where p can advance faster if
+ // a newline-carriage return is hit.
+ //
+ // Wikipedia:
+ // Systems based on ASCII or a compatible character set use either LF (Line feed, '\n', 0x0A, 10 in decimal) or
+ // CR (Carriage return, '\r', 0x0D, 13 in decimal) individually, or CR followed by LF (CR+LF, 0x0D 0x0A)...
+ // * LF: Multics, Unix and Unix-like systems (GNU/Linux, AIX, Xenix, Mac OS X, FreeBSD, etc.), BeOS, Amiga, RISC OS, and others
+ // * CR+LF: DEC RT-11 and most other early non-Unix, non-IBM OSes, CP/M, MP/M, DOS, OS/2, Microsoft Windows, Symbian OS
+ // * CR: Commodore 8-bit machines, Apple II family, Mac OS up to version 9 and OS-9
+
+ const char* p = buf; // the read head
+ char* q = buf; // the write head
+ const char CR = 0x0d;
+ const char LF = 0x0a;
+
+ buf[length] = 0;
+ while( *p ) {
+ assert( p < (buf+length) );
+ assert( q <= (buf+length) );
+ assert( q <= p );
+
+ if ( *p == CR ) {
+ *q++ = LF;
+ p++;
+ if ( *p == LF ) { // check for CR+LF (and skip LF)
+ p++;
+ }
+ }
+ else {
+ *q++ = *p++;
+ }
+ }
+ assert( q <= (buf+length) );
+ *q = 0;
+
+ Parse( buf, 0, encoding );
+
+ delete [] buf;
+ return !Error();
+}
+
+
+bool TiXmlDocument::SaveFile( const char * filename ) const
+{
+ // The old c stuff lives on...
+ FILE* fp = TiXmlFOpen( filename, "w" );
+ if ( fp )
+ {
+ bool result = SaveFile( fp );
+ fclose( fp );
+ return result;
+ }
+ return false;
+}
+
+
+bool TiXmlDocument::SaveFile( FILE* fp ) const
+{
+ if ( useMicrosoftBOM )
+ {
+ const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+ const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+ const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+ fputc( TIXML_UTF_LEAD_0, fp );
+ fputc( TIXML_UTF_LEAD_1, fp );
+ fputc( TIXML_UTF_LEAD_2, fp );
+ }
+ Print( fp, 0 );
+ return (ferror(fp) == 0);
+}
+
+
+void TiXmlDocument::CopyTo( TiXmlDocument* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->error = error;
+ target->errorId = errorId;
+ target->errorDesc = errorDesc;
+ target->tabsize = tabsize;
+ target->errorLocation = errorLocation;
+ target->useMicrosoftBOM = useMicrosoftBOM;
+
+ TiXmlNode* node = 0;
+ for ( node = firstChild; node; node = node->NextSibling() )
+ {
+ target->LinkEndChild( node->Clone() );
+ }
+}
+
+
+TiXmlNode* TiXmlDocument::Clone() const
+{
+ TiXmlDocument* clone = new TiXmlDocument();
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlDocument::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ node->Print( cfile, depth );
+ fprintf( cfile, "\n" );
+ }
+}
+
+
+bool TiXmlDocument::Accept( TiXmlVisitor* visitor ) const
+{
+ if ( visitor->VisitEnter( *this ) )
+ {
+ for ( const TiXmlNode* node=FirstChild(); node; node=node->NextSibling() )
+ {
+ if ( !node->Accept( visitor ) )
+ break;
+ }
+ }
+ return visitor->VisitExit( *this );
+}
+
+
+const TiXmlAttribute* TiXmlAttribute::Next() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Next()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( next->value.empty() && next->name.empty() )
+ return 0;
+ return next;
+}
+*/
+
+const TiXmlAttribute* TiXmlAttribute::Previous() const
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+
+/*
+TiXmlAttribute* TiXmlAttribute::Previous()
+{
+ // We are using knowledge of the sentinel. The sentinel
+ // have a value or name.
+ if ( prev->value.empty() && prev->name.empty() )
+ return 0;
+ return prev;
+}
+*/
+
+void TiXmlAttribute::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ TIXML_STRING n, v;
+
+ EncodeString( name, &n );
+ EncodeString( value, &v );
+
+ if (value.find ('\"') == TIXML_STRING::npos) {
+ if ( cfile ) {
+ fprintf (cfile, "%s=\"%s\"", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "=\""; (*str) += v; (*str) += "\"";
+ }
+ }
+ else {
+ if ( cfile ) {
+ fprintf (cfile, "%s='%s'", n.c_str(), v.c_str() );
+ }
+ if ( str ) {
+ (*str) += n; (*str) += "='"; (*str) += v; (*str) += "'";
+ }
+ }
+}
+
+
+int TiXmlAttribute::QueryIntValue( int* ival ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%d", ival ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+int TiXmlAttribute::QueryDoubleValue( double* dval ) const
+{
+ if ( TIXML_SSCANF( value.c_str(), "%lf", dval ) == 1 )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+}
+
+void TiXmlAttribute::SetIntValue( int _value )
+{
+ char buf [64];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF(buf, sizeof(buf), "%d", _value);
+ #else
+ sprintf (buf, "%d", _value);
+ #endif
+ SetValue (buf);
+}
+
+void TiXmlAttribute::SetDoubleValue( double _value )
+{
+ char buf [256];
+ #if defined(TIXML_SNPRINTF)
+ TIXML_SNPRINTF( buf, sizeof(buf), "%g", _value);
+ #else
+ sprintf (buf, "%g", _value);
+ #endif
+ SetValue (buf);
+}
+
+int TiXmlAttribute::IntValue() const
+{
+ return atoi (value.c_str ());
+}
+
+double TiXmlAttribute::DoubleValue() const
+{
+ return atof (value.c_str ());
+}
+
+
+TiXmlComment::TiXmlComment( const TiXmlComment& copy ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlComment& TiXmlComment::operator=( const TiXmlComment& base )
+{
+ Clear();
+ base.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlComment::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ for ( int i=0; i<depth; i++ )
+ {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<!--%s-->", value.c_str() );
+}
+
+
+void TiXmlComment::CopyTo( TiXmlComment* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlComment::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlComment::Clone() const
+{
+ TiXmlComment* clone = new TiXmlComment();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlText::Print( FILE* cfile, int depth ) const
+{
+ assert( cfile );
+ if ( cdata )
+ {
+ int i;
+ fprintf( cfile, "\n" );
+ for ( i=0; i<depth; i++ ) {
+ fprintf( cfile, " " );
+ }
+ fprintf( cfile, "<![CDATA[%s]]>\n", value.c_str() ); // unformatted output
+ }
+ else
+ {
+ TIXML_STRING buffer;
+ EncodeString( value, &buffer );
+ fprintf( cfile, "%s", buffer.c_str() );
+ }
+}
+
+
+void TiXmlText::CopyTo( TiXmlText* target ) const
+{
+ TiXmlNode::CopyTo( target );
+ target->cdata = cdata;
+}
+
+
+bool TiXmlText::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlText::Clone() const
+{
+ TiXmlText* clone = 0;
+ clone = new TiXmlText( "" );
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlDeclaration::TiXmlDeclaration( const char * _version,
+ const char * _encoding,
+ const char * _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlDeclaration::TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ version = _version;
+ encoding = _encoding;
+ standalone = _standalone;
+}
+#endif
+
+
+TiXmlDeclaration::TiXmlDeclaration( const TiXmlDeclaration& copy )
+ : TiXmlNode( TiXmlNode::TINYXML_DECLARATION )
+{
+ copy.CopyTo( this );
+}
+
+
+TiXmlDeclaration& TiXmlDeclaration::operator=( const TiXmlDeclaration& copy )
+{
+ Clear();
+ copy.CopyTo( this );
+ return *this;
+}
+
+
+void TiXmlDeclaration::Print( FILE* cfile, int /*depth*/, TIXML_STRING* str ) const
+{
+ if ( cfile ) fprintf( cfile, "<?xml " );
+ if ( str ) (*str) += "<?xml ";
+
+ if ( !version.empty() ) {
+ if ( cfile ) fprintf (cfile, "version=\"%s\" ", version.c_str ());
+ if ( str ) { (*str) += "version=\""; (*str) += version; (*str) += "\" "; }
+ }
+ if ( !encoding.empty() ) {
+ if ( cfile ) fprintf (cfile, "encoding=\"%s\" ", encoding.c_str ());
+ if ( str ) { (*str) += "encoding=\""; (*str) += encoding; (*str) += "\" "; }
+ }
+ if ( !standalone.empty() ) {
+ if ( cfile ) fprintf (cfile, "standalone=\"%s\" ", standalone.c_str ());
+ if ( str ) { (*str) += "standalone=\""; (*str) += standalone; (*str) += "\" "; }
+ }
+ if ( cfile ) fprintf( cfile, "?>" );
+ if ( str ) (*str) += "?>";
+}
+
+
+void TiXmlDeclaration::CopyTo( TiXmlDeclaration* target ) const
+{
+ TiXmlNode::CopyTo( target );
+
+ target->version = version;
+ target->encoding = encoding;
+ target->standalone = standalone;
+}
+
+
+bool TiXmlDeclaration::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlDeclaration::Clone() const
+{
+ TiXmlDeclaration* clone = new TiXmlDeclaration();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+void TiXmlUnknown::Print( FILE* cfile, int depth ) const
+{
+ for ( int i=0; i<depth; i++ )
+ fprintf( cfile, " " );
+ fprintf( cfile, "<%s>", value.c_str() );
+}
+
+
+void TiXmlUnknown::CopyTo( TiXmlUnknown* target ) const
+{
+ TiXmlNode::CopyTo( target );
+}
+
+
+bool TiXmlUnknown::Accept( TiXmlVisitor* visitor ) const
+{
+ return visitor->Visit( *this );
+}
+
+
+TiXmlNode* TiXmlUnknown::Clone() const
+{
+ TiXmlUnknown* clone = new TiXmlUnknown();
+
+ if ( !clone )
+ return 0;
+
+ CopyTo( clone );
+ return clone;
+}
+
+
+TiXmlAttributeSet::TiXmlAttributeSet()
+{
+ sentinel.next = &sentinel;
+ sentinel.prev = &sentinel;
+}
+
+
+TiXmlAttributeSet::~TiXmlAttributeSet()
+{
+ assert( sentinel.next == &sentinel );
+ assert( sentinel.prev == &sentinel );
+}
+
+
+void TiXmlAttributeSet::Add( TiXmlAttribute* addMe )
+{
+ #ifdef TIXML_USE_STL
+ assert( !Find( TIXML_STRING( addMe->Name() ) ) ); // Shouldn't be multiply adding to the set.
+ #else
+ assert( !Find( addMe->Name() ) ); // Shouldn't be multiply adding to the set.
+ #endif
+
+ addMe->next = &sentinel;
+ addMe->prev = sentinel.prev;
+
+ sentinel.prev->next = addMe;
+ sentinel.prev = addMe;
+}
+
+void TiXmlAttributeSet::Remove( TiXmlAttribute* removeMe )
+{
+ TiXmlAttribute* node;
+
+ for( node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node == removeMe )
+ {
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ node->next = 0;
+ node->prev = 0;
+ return;
+ }
+ }
+ assert( 0 ); // we tried to remove a non-linked attribute.
+}
+
+
+#ifdef TIXML_USE_STL
+TiXmlAttribute* TiXmlAttributeSet::Find( const std::string& name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( node->name == name )
+ return node;
+ }
+ return 0;
+}
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const std::string& _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+#endif
+
+
+TiXmlAttribute* TiXmlAttributeSet::Find( const char* name ) const
+{
+ for( TiXmlAttribute* node = sentinel.next; node != &sentinel; node = node->next )
+ {
+ if ( strcmp( node->name.c_str(), name ) == 0 )
+ return node;
+ }
+ return 0;
+}
+
+
+TiXmlAttribute* TiXmlAttributeSet::FindOrCreate( const char* _name )
+{
+ TiXmlAttribute* attrib = Find( _name );
+ if ( !attrib ) {
+ attrib = new TiXmlAttribute();
+ Add( attrib );
+ attrib->SetName( _name );
+ }
+ return attrib;
+}
+
+
+#ifdef TIXML_USE_STL
+std::istream& operator>> (std::istream & in, TiXmlNode & base)
+{
+ TIXML_STRING tag;
+ tag.reserve( 8 * 1000 );
+ base.StreamIn( &in, &tag );
+
+ base.Parse( tag.c_str(), 0, TIXML_DEFAULT_ENCODING );
+ return in;
+}
+#endif
+
+
+#ifdef TIXML_USE_STL
+std::ostream& operator<< (std::ostream & out, const TiXmlNode & base)
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out << printer.Str();
+
+ return out;
+}
+
+
+std::string& operator<< (std::string& out, const TiXmlNode& base )
+{
+ TiXmlPrinter printer;
+ printer.SetStreamPrinting();
+ base.Accept( &printer );
+ out.append( printer.Str() );
+
+ return out;
+}
+#endif
+
+
+TiXmlHandle TiXmlHandle::FirstChild() const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChild( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlNode* child = node->FirstChild( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement() const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement();
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::FirstChildElement( const char * value ) const
+{
+ if ( node )
+ {
+ TiXmlElement* child = node->FirstChildElement( value );
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::Child( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlNode* child = node->FirstChild( value );
+ for ( i=0;
+ child && i<count;
+ child = child->NextSibling( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement();
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement(), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+TiXmlHandle TiXmlHandle::ChildElement( const char* value, int count ) const
+{
+ if ( node )
+ {
+ int i;
+ TiXmlElement* child = node->FirstChildElement( value );
+ for ( i=0;
+ child && i<count;
+ child = child->NextSiblingElement( value ), ++i )
+ {
+ // nothing
+ }
+ if ( child )
+ return TiXmlHandle( child );
+ }
+ return TiXmlHandle( 0 );
+}
+
+
+bool TiXmlPrinter::VisitEnter( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitExit( const TiXmlDocument& )
+{
+ return true;
+}
+
+bool TiXmlPrinter::VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += element.Value();
+
+ for( const TiXmlAttribute* attrib = firstAttribute; attrib; attrib = attrib->Next() )
+ {
+ buffer += " ";
+ attrib->Print( 0, 0, &buffer );
+ }
+
+ if ( !element.FirstChild() )
+ {
+ buffer += " />";
+ DoLineBreak();
+ }
+ else
+ {
+ buffer += ">";
+ if ( element.FirstChild()->ToText()
+ && element.LastChild() == element.FirstChild()
+ && element.FirstChild()->ToText()->CDATA() == false )
+ {
+ simpleTextPrint = true;
+ // no DoLineBreak()!
+ }
+ else
+ {
+ DoLineBreak();
+ }
+ }
+ ++depth;
+ return true;
+}
+
+
+bool TiXmlPrinter::VisitExit( const TiXmlElement& element )
+{
+ --depth;
+ if ( !element.FirstChild() )
+ {
+ // nothing.
+ }
+ else
+ {
+ if ( simpleTextPrint )
+ {
+ simpleTextPrint = false;
+ }
+ else
+ {
+ DoIndent();
+ }
+ buffer += "</";
+ buffer += element.Value();
+ buffer += ">";
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlText& text )
+{
+ if ( text.CDATA() )
+ {
+ DoIndent();
+ buffer += "<![CDATA[";
+ buffer += text.Value();
+ buffer += "]]>";
+ DoLineBreak();
+ }
+ else if ( simpleTextPrint )
+ {
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ }
+ else
+ {
+ DoIndent();
+ TIXML_STRING str;
+ TiXmlBase::EncodeString( text.ValueTStr(), &str );
+ buffer += str;
+ DoLineBreak();
+ }
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlDeclaration& declaration )
+{
+ DoIndent();
+ declaration.Print( 0, 0, &buffer );
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlComment& comment )
+{
+ DoIndent();
+ buffer += "<!--";
+ buffer += comment.Value();
+ buffer += "-->";
+ DoLineBreak();
+ return true;
+}
+
+
+bool TiXmlPrinter::Visit( const TiXmlUnknown& unknown )
+{
+ DoIndent();
+ buffer += "<";
+ buffer += unknown.Value();
+ buffer += ">";
+ DoLineBreak();
+ return true;
+}
+
diff --git a/test/tinyxml/tinyxml.h b/test/tinyxml/tinyxml.h
new file mode 100644
index 0000000..a3589e5
--- /dev/null
+++ b/test/tinyxml/tinyxml.h
@@ -0,0 +1,1805 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+
+#ifndef TINYXML_INCLUDED
+#define TINYXML_INCLUDED
+
+#ifdef _MSC_VER
+#pragma warning( push )
+#pragma warning( disable : 4530 )
+#pragma warning( disable : 4786 )
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+// Help out windows:
+#if defined( _DEBUG ) && !defined( DEBUG )
+#define DEBUG
+#endif
+
+#ifdef TIXML_USE_STL
+ #include <string>
+ #include <iostream>
+ #include <sstream>
+ #define TIXML_STRING std::string
+#else
+ #include "tinystr.h"
+ #define TIXML_STRING TiXmlString
+#endif
+
+// Deprecated library function hell. Compilers want to use the
+// new safe versions. This probably doesn't fully address the problem,
+// but it gets closer. There are too many compilers for me to fully
+// test. If you get compilation troubles, undefine TIXML_SAFE
+#define TIXML_SAFE
+
+#ifdef TIXML_SAFE
+ #if defined(_MSC_VER) && (_MSC_VER >= 1400 )
+ // Microsoft visual studio, version 2005 and higher.
+ #define TIXML_SNPRINTF _snprintf_s
+ #define TIXML_SSCANF sscanf_s
+ #elif defined(_MSC_VER) && (_MSC_VER >= 1200 )
+ // Microsoft visual studio, version 6 and higher.
+ //#pragma message( "Using _sn* functions." )
+ #define TIXML_SNPRINTF _snprintf
+ #define TIXML_SSCANF sscanf
+ #elif defined(__GNUC__) && (__GNUC__ >= 3 )
+ // GCC version 3 and higher.s
+ //#warning( "Using sn* functions." )
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_SSCANF sscanf
+ #else
+ #define TIXML_SNPRINTF snprintf
+ #define TIXML_SSCANF sscanf
+ #endif
+#endif
+
+class TiXmlDocument;
+class TiXmlElement;
+class TiXmlComment;
+class TiXmlUnknown;
+class TiXmlAttribute;
+class TiXmlText;
+class TiXmlDeclaration;
+class TiXmlParsingData;
+
+const int TIXML_MAJOR_VERSION = 2;
+const int TIXML_MINOR_VERSION = 6;
+const int TIXML_PATCH_VERSION = 2;
+
+/* Internal structure for tracking location of items
+ in the XML file.
+*/
+struct TiXmlCursor
+{
+ TiXmlCursor() { Clear(); }
+ void Clear() { row = col = -1; }
+
+ int row; // 0 based.
+ int col; // 0 based.
+};
+
+
+/**
+ Implements the interface to the "Visitor pattern" (see the Accept() method.)
+ If you call the Accept() method, it requires being passed a TiXmlVisitor
+ class to handle callbacks. For nodes that contain other nodes (Document, Element)
+ you will get called with a VisitEnter/VisitExit pair. Nodes that are always leaves
+ are simply called with Visit().
+
+ If you return 'true' from a Visit method, recursive parsing will continue. If you return
+ false, <b>no children of this node or its sibilings</b> will be Visited.
+
+ All flavors of Visit methods have a default implementation that returns 'true' (continue
+ visiting). You need to only override methods that are interesting to you.
+
+ Generally Accept() is called on the TiXmlDocument, although all nodes suppert Visiting.
+
+ You should never change the document from a callback.
+
+ @sa TiXmlNode::Accept()
+*/
+class TiXmlVisitor
+{
+public:
+ virtual ~TiXmlVisitor() {}
+
+ /// Visit a document.
+ virtual bool VisitEnter( const TiXmlDocument& /*doc*/ ) { return true; }
+ /// Visit a document.
+ virtual bool VisitExit( const TiXmlDocument& /*doc*/ ) { return true; }
+
+ /// Visit an element.
+ virtual bool VisitEnter( const TiXmlElement& /*element*/, const TiXmlAttribute* /*firstAttribute*/ ) { return true; }
+ /// Visit an element.
+ virtual bool VisitExit( const TiXmlElement& /*element*/ ) { return true; }
+
+ /// Visit a declaration
+ virtual bool Visit( const TiXmlDeclaration& /*declaration*/ ) { return true; }
+ /// Visit a text node
+ virtual bool Visit( const TiXmlText& /*text*/ ) { return true; }
+ /// Visit a comment node
+ virtual bool Visit( const TiXmlComment& /*comment*/ ) { return true; }
+ /// Visit an unknown node
+ virtual bool Visit( const TiXmlUnknown& /*unknown*/ ) { return true; }
+};
+
+// Only used by Attribute::Query functions
+enum
+{
+ TIXML_SUCCESS,
+ TIXML_NO_ATTRIBUTE,
+ TIXML_WRONG_TYPE
+};
+
+
+// Used by the parsing routines.
+enum TiXmlEncoding
+{
+ TIXML_ENCODING_UNKNOWN,
+ TIXML_ENCODING_UTF8,
+ TIXML_ENCODING_LEGACY
+};
+
+const TiXmlEncoding TIXML_DEFAULT_ENCODING = TIXML_ENCODING_UNKNOWN;
+
+/** TiXmlBase is a base class for every class in TinyXml.
+ It does little except to establish that TinyXml classes
+ can be printed and provide some utility functions.
+
+ In XML, the document and elements can contain
+ other elements and other types of nodes.
+
+ @verbatim
+ A Document can contain: Element (container or leaf)
+ Comment (leaf)
+ Unknown (leaf)
+ Declaration( leaf )
+
+ An Element can contain: Element (container or leaf)
+ Text (leaf)
+ Attributes (not on tree)
+ Comment (leaf)
+ Unknown (leaf)
+
+ A Decleration contains: Attributes (not on tree)
+ @endverbatim
+*/
+class TiXmlBase
+{
+ friend class TiXmlNode;
+ friend class TiXmlElement;
+ friend class TiXmlDocument;
+
+public:
+ TiXmlBase() : userData(0) {}
+ virtual ~TiXmlBase() {}
+
+ /** All TinyXml classes can print themselves to a filestream
+ or the string class (TiXmlString in non-STL mode, std::string
+ in STL mode.) Either or both cfile and str can be null.
+
+ This is a formatted print, and will insert
+ tabs and newlines.
+
+ (For an unformatted stream, use the << operator.)
+ */
+ virtual void Print( FILE* cfile, int depth ) const = 0;
+
+ /** The world does not agree on whether white space should be kept or
+ not. In order to make everyone happy, these global, static functions
+ are provided to set whether or not TinyXml will condense all white space
+ into a single space or not. The default is to condense. Note changing this
+ value is not thread safe.
+ */
+ static void SetCondenseWhiteSpace( bool condense ) { condenseWhiteSpace = condense; }
+
+ /// Return the current white space setting.
+ static bool IsWhiteSpaceCondensed() { return condenseWhiteSpace; }
+
+ /** Return the position, in the original source file, of this node or attribute.
+ The row and column are 1-based. (That is the first row and first column is
+ 1,1). If the returns values are 0 or less, then the parser does not have
+ a row and column value.
+
+ Generally, the row and column value will be set when the TiXmlDocument::Load(),
+ TiXmlDocument::LoadFile(), or any TiXmlNode::Parse() is called. It will NOT be set
+ when the DOM was created from operator>>.
+
+ The values reflect the initial load. Once the DOM is modified programmatically
+ (by adding or changing nodes and attributes) the new values will NOT update to
+ reflect changes in the document.
+
+ There is a minor performance cost to computing the row and column. Computation
+ can be disabled if TiXmlDocument::SetTabSize() is called with 0 as the value.
+
+ @sa TiXmlDocument::SetTabSize()
+ */
+ int Row() const { return location.row + 1; }
+ int Column() const { return location.col + 1; } ///< See Row()
+
+ void SetUserData( void* user ) { userData = user; } ///< Set a pointer to arbitrary user data.
+ void* GetUserData() { return userData; } ///< Get a pointer to arbitrary user data.
+ const void* GetUserData() const { return userData; } ///< Get a pointer to arbitrary user data.
+
+ // Table that returs, for a given lead byte, the total number of bytes
+ // in the UTF-8 sequence.
+ static const int utf8ByteTable[256];
+
+ virtual const char* Parse( const char* p,
+ TiXmlParsingData* data,
+ TiXmlEncoding encoding /*= TIXML_ENCODING_UNKNOWN */ ) = 0;
+
+ /** Expands entities in a string. Note this should not contian the tag's '<', '>', etc,
+ or they will be transformed into entities!
+ */
+ static void EncodeString( const TIXML_STRING& str, TIXML_STRING* out );
+
+ enum
+ {
+ TIXML_NO_ERROR = 0,
+ TIXML_ERROR,
+ TIXML_ERROR_OPENING_FILE,
+ TIXML_ERROR_PARSING_ELEMENT,
+ TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME,
+ TIXML_ERROR_READING_ELEMENT_VALUE,
+ TIXML_ERROR_READING_ATTRIBUTES,
+ TIXML_ERROR_PARSING_EMPTY,
+ TIXML_ERROR_READING_END_TAG,
+ TIXML_ERROR_PARSING_UNKNOWN,
+ TIXML_ERROR_PARSING_COMMENT,
+ TIXML_ERROR_PARSING_DECLARATION,
+ TIXML_ERROR_DOCUMENT_EMPTY,
+ TIXML_ERROR_EMBEDDED_NULL,
+ TIXML_ERROR_PARSING_CDATA,
+ TIXML_ERROR_DOCUMENT_TOP_ONLY,
+
+ TIXML_ERROR_STRING_COUNT
+ };
+
+protected:
+
+ static const char* SkipWhiteSpace( const char*, TiXmlEncoding encoding );
+
+ inline static bool IsWhiteSpace( char c )
+ {
+ return ( isspace( (unsigned char) c ) || c == '\n' || c == '\r' );
+ }
+ inline static bool IsWhiteSpace( int c )
+ {
+ if ( c < 256 )
+ return IsWhiteSpace( (char) c );
+ return false; // Again, only truly correct for English/Latin...but usually works.
+ }
+
+ #ifdef TIXML_USE_STL
+ static bool StreamWhiteSpace( std::istream * in, TIXML_STRING * tag );
+ static bool StreamTo( std::istream * in, int character, TIXML_STRING * tag );
+ #endif
+
+ /* Reads an XML name into the string provided. Returns
+ a pointer just past the last character of the name,
+ or 0 if the function has an error.
+ */
+ static const char* ReadName( const char* p, TIXML_STRING* name, TiXmlEncoding encoding );
+
+ /* Reads text. Returns a pointer past the given end tag.
+ Wickedly complex options, but it keeps the (sensitive) code in one place.
+ */
+ static const char* ReadText( const char* in, // where to start
+ TIXML_STRING* text, // the string read
+ bool ignoreWhiteSpace, // whether to keep the white space
+ const char* endTag, // what ends this text
+ bool ignoreCase, // whether to ignore case in the end tag
+ TiXmlEncoding encoding ); // the current encoding
+
+ // If an entity has been found, transform it into a character.
+ static const char* GetEntity( const char* in, char* value, int* length, TiXmlEncoding encoding );
+
+ // Get a character, while interpreting entities.
+ // The length can be from 0 to 4 bytes.
+ inline static const char* GetChar( const char* p, char* _value, int* length, TiXmlEncoding encoding )
+ {
+ assert( p );
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ *length = utf8ByteTable[ *((const unsigned char*)p) ];
+ assert( *length >= 0 && *length < 5 );
+ }
+ else
+ {
+ *length = 1;
+ }
+
+ if ( *length == 1 )
+ {
+ if ( *p == '&' )
+ return GetEntity( p, _value, length, encoding );
+ *_value = *p;
+ return p+1;
+ }
+ else if ( *length )
+ {
+ //strncpy( _value, p, *length ); // lots of compilers don't like this function (unsafe),
+ // and the null terminator isn't needed
+ for( int i=0; p[i] && i<*length; ++i ) {
+ _value[i] = p[i];
+ }
+ return p + (*length);
+ }
+ else
+ {
+ // Not valid text.
+ return 0;
+ }
+ }
+
+ // Return true if the next characters in the stream are any of the endTag sequences.
+ // Ignore case only works for english, and should only be relied on when comparing
+ // to English words: StringEqual( p, "version", true ) is fine.
+ static bool StringEqual( const char* p,
+ const char* endTag,
+ bool ignoreCase,
+ TiXmlEncoding encoding );
+
+ static const char* errorString[ TIXML_ERROR_STRING_COUNT ];
+
+ TiXmlCursor location;
+
+ /// Field containing a generic user pointer
+ void* userData;
+
+ // None of these methods are reliable for any language except English.
+ // Good for approximation, not great for accuracy.
+ static int IsAlpha( unsigned char anyByte, TiXmlEncoding encoding );
+ static int IsAlphaNum( unsigned char anyByte, TiXmlEncoding encoding );
+ inline static int ToLower( int v, TiXmlEncoding encoding )
+ {
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ if ( v < 128 ) return tolower( v );
+ return v;
+ }
+ else
+ {
+ return tolower( v );
+ }
+ }
+ static void ConvertUTF32ToUTF8( unsigned long input, char* output, int* length );
+
+private:
+ TiXmlBase( const TiXmlBase& ); // not implemented.
+ void operator=( const TiXmlBase& base ); // not allowed.
+
+ struct Entity
+ {
+ const char* str;
+ unsigned int strLength;
+ char chr;
+ };
+ enum
+ {
+ NUM_ENTITY = 5,
+ MAX_ENTITY_LENGTH = 6
+
+ };
+ static Entity entity[ NUM_ENTITY ];
+ static bool condenseWhiteSpace;
+};
+
+
+/** The parent class for everything in the Document Object Model.
+ (Except for attributes).
+ Nodes have siblings, a parent, and children. A node can be
+ in a document, or stand on its own. The type of a TiXmlNode
+ can be queried, and it can be cast to its more defined type.
+*/
+class TiXmlNode : public TiXmlBase
+{
+ friend class TiXmlDocument;
+ friend class TiXmlElement;
+
+public:
+ #ifdef TIXML_USE_STL
+
+ /** An input stream operator, for every class. Tolerant of newlines and
+ formatting, but doesn't expect them.
+ */
+ friend std::istream& operator >> (std::istream& in, TiXmlNode& base);
+
+ /** An output stream operator, for every class. Note that this outputs
+ without any newlines or formatting, as opposed to Print(), which
+ includes tabs and new lines.
+
+ The operator<< and operator>> are not completely symmetric. Writing
+ a node to a stream is very well defined. You'll get a nice stream
+ of output, without any extra whitespace or newlines.
+
+ But reading is not as well defined. (As it always is.) If you create
+ a TiXmlElement (for example) and read that from an input stream,
+ the text needs to define an element or junk will result. This is
+ true of all input streams, but it's worth keeping in mind.
+
+ A TiXmlDocument will read nodes until it reads a root element, and
+ all the children of that root element.
+ */
+ friend std::ostream& operator<< (std::ostream& out, const TiXmlNode& base);
+
+ /// Appends the XML node or attribute to a std::string.
+ friend std::string& operator<< (std::string& out, const TiXmlNode& base );
+
+ #endif
+
+ /** The types of XML nodes supported by TinyXml. (All the
+ unsupported types are picked up by UNKNOWN.)
+ */
+ enum NodeType
+ {
+ TINYXML_DOCUMENT,
+ TINYXML_ELEMENT,
+ TINYXML_COMMENT,
+ TINYXML_UNKNOWN,
+ TINYXML_TEXT,
+ TINYXML_DECLARATION,
+ TINYXML_TYPECOUNT
+ };
+
+ virtual ~TiXmlNode();
+
+ /** The meaning of 'value' changes for the specific type of
+ TiXmlNode.
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+
+ The subclasses will wrap this function.
+ */
+ const char *Value() const { return value.c_str (); }
+
+ #ifdef TIXML_USE_STL
+ /** Return Value() as a std::string. If you only use STL,
+ this is more efficient than calling Value().
+ Only available in STL mode.
+ */
+ const std::string& ValueStr() const { return value; }
+ #endif
+
+ const TIXML_STRING& ValueTStr() const { return value; }
+
+ /** Changes the value of the node. Defined as:
+ @verbatim
+ Document: filename of the xml file
+ Element: name of the element
+ Comment: the comment text
+ Unknown: the tag contents
+ Text: the text string
+ @endverbatim
+ */
+ void SetValue(const char * _value) { value = _value;}
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+ #endif
+
+ /// Delete all the children of this node. Does not affect 'this'.
+ void Clear();
+
+ /// One step up the DOM.
+ TiXmlNode* Parent() { return parent; }
+ const TiXmlNode* Parent() const { return parent; }
+
+ const TiXmlNode* FirstChild() const { return firstChild; } ///< The first child of this node. Will be null if there are no children.
+ TiXmlNode* FirstChild() { return firstChild; }
+ const TiXmlNode* FirstChild( const char * value ) const; ///< The first child of this node with the matching 'value'. Will be null if none found.
+ /// The first child of this node with the matching 'value'. Will be null if none found.
+ TiXmlNode* FirstChild( const char * _value ) {
+ // Call through to the const version - safe since nothing is changed. Exiting syntax: cast this to a const (always safe)
+ // call the method, cast the return back to non-const.
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->FirstChild( _value ));
+ }
+ const TiXmlNode* LastChild() const { return lastChild; } /// The last child of this node. Will be null if there are no children.
+ TiXmlNode* LastChild() { return lastChild; }
+
+ const TiXmlNode* LastChild( const char * value ) const; /// The last child of this node matching 'value'. Will be null if there are no children.
+ TiXmlNode* LastChild( const char * _value ) {
+ return const_cast< TiXmlNode* > ((const_cast< const TiXmlNode* >(this))->LastChild( _value ));
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* FirstChild( const std::string& _value ) const { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* FirstChild( const std::string& _value ) { return FirstChild (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* LastChild( const std::string& _value ) const { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* LastChild( const std::string& _value ) { return LastChild (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** An alternate way to walk the children of a node.
+ One way to iterate over nodes is:
+ @verbatim
+ for( child = parent->FirstChild(); child; child = child->NextSibling() )
+ @endverbatim
+
+ IterateChildren does the same thing with the syntax:
+ @verbatim
+ child = 0;
+ while( child = parent->IterateChildren( child ) )
+ @endverbatim
+
+ IterateChildren takes the previous child as input and finds
+ the next one. If the previous child is null, it returns the
+ first. IterateChildren will return null when done.
+ */
+ const TiXmlNode* IterateChildren( const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( previous ) );
+ }
+
+ /// This flavor of IterateChildren searches for children with a particular 'value'
+ const TiXmlNode* IterateChildren( const char * value, const TiXmlNode* previous ) const;
+ TiXmlNode* IterateChildren( const char * _value, const TiXmlNode* previous ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->IterateChildren( _value, previous ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) const { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ TiXmlNode* IterateChildren( const std::string& _value, const TiXmlNode* previous ) { return IterateChildren (_value.c_str (), previous); } ///< STL std::string form.
+ #endif
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertEndChild( const TiXmlNode& addThis );
+
+
+ /** Add a new node related to this. Adds a child past the LastChild.
+
+ NOTE: the node to be added is passed by pointer, and will be
+ henceforth owned (and deleted) by tinyXml. This method is efficient
+ and avoids an extra copy, but should be used with care as it
+ uses a different memory model than the other insert functions.
+
+ @sa InsertEndChild
+ */
+ TiXmlNode* LinkEndChild( TiXmlNode* addThis );
+
+ /** Add a new node related to this. Adds a child before the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertBeforeChild( TiXmlNode* beforeThis, const TiXmlNode& addThis );
+
+ /** Add a new node related to this. Adds a child after the specified child.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* InsertAfterChild( TiXmlNode* afterThis, const TiXmlNode& addThis );
+
+ /** Replace a child of this node.
+ Returns a pointer to the new object or NULL if an error occured.
+ */
+ TiXmlNode* ReplaceChild( TiXmlNode* replaceThis, const TiXmlNode& withThis );
+
+ /// Delete a child of this node.
+ bool RemoveChild( TiXmlNode* removeThis );
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling() const { return prev; }
+ TiXmlNode* PreviousSibling() { return prev; }
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* PreviousSibling( const char * ) const;
+ TiXmlNode* PreviousSibling( const char *_prev ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->PreviousSibling( _prev ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlNode* PreviousSibling( const std::string& _value ) const { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* PreviousSibling( const std::string& _value ) { return PreviousSibling (_value.c_str ()); } ///< STL std::string form.
+ const TiXmlNode* NextSibling( const std::string& _value) const { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ TiXmlNode* NextSibling( const std::string& _value) { return NextSibling (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Navigate to a sibling node.
+ const TiXmlNode* NextSibling() const { return next; }
+ TiXmlNode* NextSibling() { return next; }
+
+ /// Navigate to a sibling node with the given 'value'.
+ const TiXmlNode* NextSibling( const char * ) const;
+ TiXmlNode* NextSibling( const char* _next ) {
+ return const_cast< TiXmlNode* >( (const_cast< const TiXmlNode* >(this))->NextSibling( _next ) );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement() const;
+ TiXmlElement* NextSiblingElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement() );
+ }
+
+ /** Convenience function to get through elements.
+ Calls NextSibling and ToElement. Will skip all non-Element
+ nodes. Returns 0 if there is not another element.
+ */
+ const TiXmlElement* NextSiblingElement( const char * ) const;
+ TiXmlElement* NextSiblingElement( const char *_next ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->NextSiblingElement( _next ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* NextSiblingElement( const std::string& _value) const { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* NextSiblingElement( const std::string& _value) { return NextSiblingElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement() const;
+ TiXmlElement* FirstChildElement() {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement() );
+ }
+
+ /// Convenience function to get through elements.
+ const TiXmlElement* FirstChildElement( const char * _value ) const;
+ TiXmlElement* FirstChildElement( const char * _value ) {
+ return const_cast< TiXmlElement* >( (const_cast< const TiXmlNode* >(this))->FirstChildElement( _value ) );
+ }
+
+ #ifdef TIXML_USE_STL
+ const TiXmlElement* FirstChildElement( const std::string& _value ) const { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ TiXmlElement* FirstChildElement( const std::string& _value ) { return FirstChildElement (_value.c_str ()); } ///< STL std::string form.
+ #endif
+
+ /** Query the type (as an enumerated value, above) of this node.
+ The possible types are: TINYXML_DOCUMENT, TINYXML_ELEMENT, TINYXML_COMMENT,
+ TINYXML_UNKNOWN, TINYXML_TEXT, and TINYXML_DECLARATION.
+ */
+ int Type() const { return type; }
+
+ /** Return a pointer to the Document this node lives in.
+ Returns null if not in a document.
+ */
+ const TiXmlDocument* GetDocument() const;
+ TiXmlDocument* GetDocument() {
+ return const_cast< TiXmlDocument* >( (const_cast< const TiXmlNode* >(this))->GetDocument() );
+ }
+
+ /// Returns true if this node has no children.
+ bool NoChildren() const { return !firstChild; }
+
+ virtual const TiXmlDocument* ToDocument() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlElement* ToElement() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlComment* ToComment() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlUnknown* ToUnknown() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlText* ToText() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual const TiXmlDeclaration* ToDeclaration() const { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ virtual TiXmlDocument* ToDocument() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlElement* ToElement() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlComment* ToComment() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlText* ToText() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return 0; } ///< Cast to a more defined type. Will return null if not of the requested type.
+
+ /** Create an exact duplicate of this node and return it. The memory must be deleted
+ by the caller.
+ */
+ virtual TiXmlNode* Clone() const = 0;
+
+ /** Accept a hierchical visit the nodes in the TinyXML DOM. Every node in the
+ XML tree will be conditionally visited and the host will be called back
+ via the TiXmlVisitor interface.
+
+ This is essentially a SAX interface for TinyXML. (Note however it doesn't re-parse
+ the XML for the callbacks, so the performance of TinyXML is unchanged by using this
+ interface versus any other.)
+
+ The interface has been based on ideas from:
+
+ - http://www.saxproject.org/
+ - http://c2.com/cgi/wiki?HierarchicalVisitorPattern
+
+ Which are both good references for "visiting".
+
+ An example of using Accept():
+ @verbatim
+ TiXmlPrinter printer;
+ tinyxmlDoc.Accept( &printer );
+ const char* xmlcstr = printer.CStr();
+ @endverbatim
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const = 0;
+
+protected:
+ TiXmlNode( NodeType _type );
+
+ // Copy to the allocated object. Shared functionality between Clone, Copy constructor,
+ // and the assignment operator.
+ void CopyTo( TiXmlNode* target ) const;
+
+ #ifdef TIXML_USE_STL
+ // The real work of the input operator.
+ virtual void StreamIn( std::istream* in, TIXML_STRING* tag ) = 0;
+ #endif
+
+ // Figure out what is at *p, and parse it. Returns null if it is not an xml node.
+ TiXmlNode* Identify( const char* start, TiXmlEncoding encoding );
+
+ TiXmlNode* parent;
+ NodeType type;
+
+ TiXmlNode* firstChild;
+ TiXmlNode* lastChild;
+
+ TIXML_STRING value;
+
+ TiXmlNode* prev;
+ TiXmlNode* next;
+
+private:
+ TiXmlNode( const TiXmlNode& ); // not implemented.
+ void operator=( const TiXmlNode& base ); // not allowed.
+};
+
+
+/** An attribute is a name-value pair. Elements have an arbitrary
+ number of attributes, each with a unique name.
+
+ @note The attributes are not TiXmlNodes, since they are not
+ part of the tinyXML document object model. There are other
+ suggested ways to look at this problem.
+*/
+class TiXmlAttribute : public TiXmlBase
+{
+ friend class TiXmlAttributeSet;
+
+public:
+ /// Construct an empty attribute.
+ TiXmlAttribute() : TiXmlBase()
+ {
+ document = 0;
+ prev = next = 0;
+ }
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlAttribute( const std::string& _name, const std::string& _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+ #endif
+
+ /// Construct an attribute with a name and value.
+ TiXmlAttribute( const char * _name, const char * _value )
+ {
+ name = _name;
+ value = _value;
+ document = 0;
+ prev = next = 0;
+ }
+
+ const char* Name() const { return name.c_str(); } ///< Return the name of this attribute.
+ const char* Value() const { return value.c_str(); } ///< Return the value of this attribute.
+ #ifdef TIXML_USE_STL
+ const std::string& ValueStr() const { return value; } ///< Return the value of this attribute.
+ #endif
+ int IntValue() const; ///< Return the value of this attribute, converted to an integer.
+ double DoubleValue() const; ///< Return the value of this attribute, converted to a double.
+
+ // Get the tinyxml string representation
+ const TIXML_STRING& NameTStr() const { return name; }
+
+ /** QueryIntValue examines the value string. It is an alternative to the
+ IntValue() method with richer error checking.
+ If the value is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE.
+
+ A specialized but useful call. Note that for success it returns 0,
+ which is the opposite of almost all other TinyXml calls.
+ */
+ int QueryIntValue( int* _value ) const;
+ /// QueryDoubleValue examines the value string. See QueryIntValue().
+ int QueryDoubleValue( double* _value ) const;
+
+ void SetName( const char* _name ) { name = _name; } ///< Set the name of this attribute.
+ void SetValue( const char* _value ) { value = _value; } ///< Set the value.
+
+ void SetIntValue( int _value ); ///< Set the value from an integer.
+ void SetDoubleValue( double _value ); ///< Set the value from a double.
+
+ #ifdef TIXML_USE_STL
+ /// STL std::string form.
+ void SetName( const std::string& _name ) { name = _name; }
+ /// STL std::string form.
+ void SetValue( const std::string& _value ) { value = _value; }
+ #endif
+
+ /// Get the next sibling attribute in the DOM. Returns null at end.
+ const TiXmlAttribute* Next() const;
+ TiXmlAttribute* Next() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Next() );
+ }
+
+ /// Get the previous sibling attribute in the DOM. Returns null at beginning.
+ const TiXmlAttribute* Previous() const;
+ TiXmlAttribute* Previous() {
+ return const_cast< TiXmlAttribute* >( (const_cast< const TiXmlAttribute* >(this))->Previous() );
+ }
+
+ bool operator==( const TiXmlAttribute& rhs ) const { return rhs.name == name; }
+ bool operator<( const TiXmlAttribute& rhs ) const { return name < rhs.name; }
+ bool operator>( const TiXmlAttribute& rhs ) const { return name > rhs.name; }
+
+ /* Attribute parsing starts: first letter of the name
+ returns: the next char after the value end quote
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ // Prints this Attribute to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+ void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+
+ // [internal use]
+ // Set the document pointer so the attribute can report errors.
+ void SetDocument( TiXmlDocument* doc ) { document = doc; }
+
+private:
+ TiXmlAttribute( const TiXmlAttribute& ); // not implemented.
+ void operator=( const TiXmlAttribute& base ); // not allowed.
+
+ TiXmlDocument* document; // A pointer back to a document, for error reporting.
+ TIXML_STRING name;
+ TIXML_STRING value;
+ TiXmlAttribute* prev;
+ TiXmlAttribute* next;
+};
+
+
+/* A class used to manage a group of attributes.
+ It is only used internally, both by the ELEMENT and the DECLARATION.
+
+ The set can be changed transparent to the Element and Declaration
+ classes that use it, but NOT transparent to the Attribute
+ which has to implement a next() and previous() method. Which makes
+ it a bit problematic and prevents the use of STL.
+
+ This version is implemented with circular lists because:
+ - I like circular lists
+ - it demonstrates some independence from the (typical) doubly linked list.
+*/
+class TiXmlAttributeSet
+{
+public:
+ TiXmlAttributeSet();
+ ~TiXmlAttributeSet();
+
+ void Add( TiXmlAttribute* attribute );
+ void Remove( TiXmlAttribute* attribute );
+
+ const TiXmlAttribute* First() const { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ TiXmlAttribute* First() { return ( sentinel.next == &sentinel ) ? 0 : sentinel.next; }
+ const TiXmlAttribute* Last() const { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+ TiXmlAttribute* Last() { return ( sentinel.prev == &sentinel ) ? 0 : sentinel.prev; }
+
+ TiXmlAttribute* Find( const char* _name ) const;
+ TiXmlAttribute* FindOrCreate( const char* _name );
+
+# ifdef TIXML_USE_STL
+ TiXmlAttribute* Find( const std::string& _name ) const;
+ TiXmlAttribute* FindOrCreate( const std::string& _name );
+# endif
+
+
+private:
+ //*ME: Because of hidden/disabled copy-construktor in TiXmlAttribute (sentinel-element),
+ //*ME: this class must be also use a hidden/disabled copy-constructor !!!
+ TiXmlAttributeSet( const TiXmlAttributeSet& ); // not allowed
+ void operator=( const TiXmlAttributeSet& ); // not allowed (as TiXmlAttribute)
+
+ TiXmlAttribute sentinel;
+};
+
+
+/** The element is a container class. It has a value, the element name,
+ and can contain other elements, text, comments, and unknowns.
+ Elements also contain an arbitrary number of attributes.
+*/
+class TiXmlElement : public TiXmlNode
+{
+public:
+ /// Construct an element.
+ TiXmlElement (const char * in_value);
+
+ #ifdef TIXML_USE_STL
+ /// std::string constructor.
+ TiXmlElement( const std::string& _value );
+ #endif
+
+ TiXmlElement( const TiXmlElement& );
+
+ TiXmlElement& operator=( const TiXmlElement& base );
+
+ virtual ~TiXmlElement();
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ */
+ const char* Attribute( const char* name ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an integer,
+ the integer value will be put in the return 'i', if 'i'
+ is non-null.
+ */
+ const char* Attribute( const char* name, int* i ) const;
+
+ /** Given an attribute name, Attribute() returns the value
+ for the attribute of that name, or null if none exists.
+ If the attribute exists and can be converted to an double,
+ the double value will be put in the return 'd', if 'd'
+ is non-null.
+ */
+ const char* Attribute( const char* name, double* d ) const;
+
+ /** QueryIntAttribute examines the attribute - it is an alternative to the
+ Attribute() method with richer error checking.
+ If the attribute is an integer, it is stored in 'value' and
+ the call returns TIXML_SUCCESS. If it is not
+ an integer, it returns TIXML_WRONG_TYPE. If the attribute
+ does not exist, then TIXML_NO_ATTRIBUTE is returned.
+ */
+ int QueryIntAttribute( const char* name, int* _value ) const;
+ /// QueryUnsignedAttribute examines the attribute - see QueryIntAttribute().
+ int QueryUnsignedAttribute( const char* name, unsigned* _value ) const;
+ /** QueryBoolAttribute examines the attribute - see QueryIntAttribute().
+ Note that '1', 'true', or 'yes' are considered true, while '0', 'false'
+ and 'no' are considered false.
+ */
+ int QueryBoolAttribute( const char* name, bool* _value ) const;
+ /// QueryDoubleAttribute examines the attribute - see QueryIntAttribute().
+ int QueryDoubleAttribute( const char* name, double* _value ) const;
+ /// QueryFloatAttribute examines the attribute - see QueryIntAttribute().
+ int QueryFloatAttribute( const char* name, float* _value ) const {
+ double d;
+ int result = QueryDoubleAttribute( name, &d );
+ if ( result == TIXML_SUCCESS ) {
+ *_value = (float)d;
+ }
+ return result;
+ }
+
+ #ifdef TIXML_USE_STL
+ /// QueryStringAttribute examines the attribute - see QueryIntAttribute().
+ int QueryStringAttribute( const char* name, std::string* _value ) const {
+ const char* cstr = Attribute( name );
+ if ( cstr ) {
+ *_value = std::string( cstr );
+ return TIXML_SUCCESS;
+ }
+ return TIXML_NO_ATTRIBUTE;
+ }
+
+ /** Template form of the attribute query which will try to read the
+ attribute into the specified type. Very easy, very powerful, but
+ be careful to make sure to call this with the correct type.
+
+ NOTE: This method doesn't work correctly for 'string' types that contain spaces.
+
+ @return TIXML_SUCCESS, TIXML_WRONG_TYPE, or TIXML_NO_ATTRIBUTE
+ */
+ template< typename T > int QueryValueAttribute( const std::string& name, T* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+
+ std::stringstream sstream( node->ValueStr() );
+ sstream >> *outValue;
+ if ( !sstream.fail() )
+ return TIXML_SUCCESS;
+ return TIXML_WRONG_TYPE;
+ }
+
+ int QueryValueAttribute( const std::string& name, std::string* outValue ) const
+ {
+ const TiXmlAttribute* node = attributeSet.Find( name );
+ if ( !node )
+ return TIXML_NO_ATTRIBUTE;
+ *outValue = node->ValueStr();
+ return TIXML_SUCCESS;
+ }
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char* name, const char * _value );
+
+ #ifdef TIXML_USE_STL
+ const std::string* Attribute( const std::string& name ) const;
+ const std::string* Attribute( const std::string& name, int* i ) const;
+ const std::string* Attribute( const std::string& name, double* d ) const;
+ int QueryIntAttribute( const std::string& name, int* _value ) const;
+ int QueryDoubleAttribute( const std::string& name, double* _value ) const;
+
+ /// STL std::string form.
+ void SetAttribute( const std::string& name, const std::string& _value );
+ ///< STL std::string form.
+ void SetAttribute( const std::string& name, int _value );
+ ///< STL std::string form.
+ void SetDoubleAttribute( const std::string& name, double value );
+ #endif
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetAttribute( const char * name, int value );
+
+ /** Sets an attribute of name to a given value. The attribute
+ will be created if it does not exist, or changed if it does.
+ */
+ void SetDoubleAttribute( const char * name, double value );
+
+ /** Deletes an attribute with the given name.
+ */
+ void RemoveAttribute( const char * name );
+ #ifdef TIXML_USE_STL
+ void RemoveAttribute( const std::string& name ) { RemoveAttribute (name.c_str ()); } ///< STL std::string form.
+ #endif
+
+ const TiXmlAttribute* FirstAttribute() const { return attributeSet.First(); } ///< Access the first attribute in this element.
+ TiXmlAttribute* FirstAttribute() { return attributeSet.First(); }
+ const TiXmlAttribute* LastAttribute() const { return attributeSet.Last(); } ///< Access the last attribute in this element.
+ TiXmlAttribute* LastAttribute() { return attributeSet.Last(); }
+
+ /** Convenience function for easy access to the text inside an element. Although easy
+ and concise, GetText() is limited compared to getting the TiXmlText child
+ and accessing it directly.
+
+ If the first child of 'this' is a TiXmlText, the GetText()
+ returns the character string of the Text node, else null is returned.
+
+ This is a convenient method for getting the text of simple contained text:
+ @verbatim
+ <foo>This is text</foo>
+ const char* str = fooElement->GetText();
+ @endverbatim
+
+ 'str' will be a pointer to "This is text".
+
+ Note that this function can be misleading. If the element foo was created from
+ this XML:
+ @verbatim
+ <foo><b>This is text</b></foo>
+ @endverbatim
+
+ then the value of str would be null. The first child node isn't a text node, it is
+ another element. From this XML:
+ @verbatim
+ <foo>This is <b>text</b></foo>
+ @endverbatim
+ GetText() will return "This is ".
+
+ WARNING: GetText() accesses a child node - don't become confused with the
+ similarly named TiXmlHandle::Text() and TiXmlNode::ToText() which are
+ safe type casts on the referenced node.
+ */
+ const char* GetText() const;
+
+ /// Creates a new Element and returns it - the returned element is a copy.
+ virtual TiXmlNode* Clone() const;
+ // Print the Element to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: next char past '<'
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlElement* ToElement() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlElement* ToElement() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+
+ void CopyTo( TiXmlElement* target ) const;
+ void ClearThis(); // like clear, but initializes 'this' object as well
+
+ // Used to be public [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+ /* [internal use]
+ Reads the "value" of the element -- another element, or text.
+ This should terminate with the current end tag.
+ */
+ const char* ReadValue( const char* in, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+private:
+ TiXmlAttributeSet attributeSet;
+};
+
+
+/** An XML comment.
+*/
+class TiXmlComment : public TiXmlNode
+{
+public:
+ /// Constructs an empty comment.
+ TiXmlComment() : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {}
+ /// Construct a comment from text.
+ TiXmlComment( const char* _value ) : TiXmlNode( TiXmlNode::TINYXML_COMMENT ) {
+ SetValue( _value );
+ }
+ TiXmlComment( const TiXmlComment& );
+ TiXmlComment& operator=( const TiXmlComment& base );
+
+ virtual ~TiXmlComment() {}
+
+ /// Returns a copy of this Comment.
+ virtual TiXmlNode* Clone() const;
+ // Write this Comment to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /* Attribtue parsing starts: at the ! of the !--
+ returns: next char past '>'
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlComment* ToComment() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlComment* ToComment() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlComment* target ) const;
+
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+// virtual void StreamOut( TIXML_OSTREAM * out ) const;
+
+private:
+
+};
+
+
+/** XML text. A text node can have 2 ways to output the next. "normal" output
+ and CDATA. It will default to the mode it was parsed from the XML file and
+ you generally want to leave it alone, but you can change the output mode with
+ SetCDATA() and query it with CDATA().
+*/
+class TiXmlText : public TiXmlNode
+{
+ friend class TiXmlElement;
+public:
+ /** Constructor for text element. By default, it is treated as
+ normal, encoded text. If you want it be output as a CDATA text
+ element, set the parameter _cdata to 'true'
+ */
+ TiXmlText (const char * initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+ {
+ SetValue( initValue );
+ cdata = false;
+ }
+ virtual ~TiXmlText() {}
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlText( const std::string& initValue ) : TiXmlNode (TiXmlNode::TINYXML_TEXT)
+ {
+ SetValue( initValue );
+ cdata = false;
+ }
+ #endif
+
+ TiXmlText( const TiXmlText& copy ) : TiXmlNode( TiXmlNode::TINYXML_TEXT ) { copy.CopyTo( this ); }
+ TiXmlText& operator=( const TiXmlText& base ) { base.CopyTo( this ); return *this; }
+
+ // Write this text object to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ /// Queries whether this represents text using a CDATA section.
+ bool CDATA() const { return cdata; }
+ /// Turns on or off a CDATA representation of text.
+ void SetCDATA( bool _cdata ) { cdata = _cdata; }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlText* ToText() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlText* ToText() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ /// [internal use] Creates a new Element and returns it.
+ virtual TiXmlNode* Clone() const;
+ void CopyTo( TiXmlText* target ) const;
+
+ bool Blank() const; // returns true if all white space and new lines
+ // [internal use]
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+ bool cdata; // true if this should be input and output as a CDATA style text element
+};
+
+
+/** In correct XML the declaration is the first entry in the file.
+ @verbatim
+ <?xml version="1.0" standalone="yes"?>
+ @endverbatim
+
+ TinyXml will happily read or write files without a declaration,
+ however. There are 3 possible attributes to the declaration:
+ version, encoding, and standalone.
+
+ Note: In this version of the code, the attributes are
+ handled as special cases, not generic attributes, simply
+ because there can only be at most 3 and they are always the same.
+*/
+class TiXmlDeclaration : public TiXmlNode
+{
+public:
+ /// Construct an empty declaration.
+ TiXmlDeclaration() : TiXmlNode( TiXmlNode::TINYXML_DECLARATION ) {}
+
+#ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDeclaration( const std::string& _version,
+ const std::string& _encoding,
+ const std::string& _standalone );
+#endif
+
+ /// Construct.
+ TiXmlDeclaration( const char* _version,
+ const char* _encoding,
+ const char* _standalone );
+
+ TiXmlDeclaration( const TiXmlDeclaration& copy );
+ TiXmlDeclaration& operator=( const TiXmlDeclaration& copy );
+
+ virtual ~TiXmlDeclaration() {}
+
+ /// Version. Will return an empty string if none was found.
+ const char *Version() const { return version.c_str (); }
+ /// Encoding. Will return an empty string if none was found.
+ const char *Encoding() const { return encoding.c_str (); }
+ /// Is this a standalone document?
+ const char *Standalone() const { return standalone.c_str (); }
+
+ /// Creates a copy of this Declaration and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this declaration to a FILE stream.
+ virtual void Print( FILE* cfile, int depth, TIXML_STRING* str ) const;
+ virtual void Print( FILE* cfile, int depth ) const {
+ Print( cfile, depth, 0 );
+ }
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlDeclaration* ToDeclaration() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDeclaration* ToDeclaration() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* visitor ) const;
+
+protected:
+ void CopyTo( TiXmlDeclaration* target ) const;
+ // used to be public
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+
+ TIXML_STRING version;
+ TIXML_STRING encoding;
+ TIXML_STRING standalone;
+};
+
+
+/** Any tag that tinyXml doesn't recognize is saved as an
+ unknown. It is a tag of text, but should not be modified.
+ It will be written back to the XML, unchanged, when the file
+ is saved.
+
+ DTD tags get thrown into TiXmlUnknowns.
+*/
+class TiXmlUnknown : public TiXmlNode
+{
+public:
+ TiXmlUnknown() : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) {}
+ virtual ~TiXmlUnknown() {}
+
+ TiXmlUnknown( const TiXmlUnknown& copy ) : TiXmlNode( TiXmlNode::TINYXML_UNKNOWN ) { copy.CopyTo( this ); }
+ TiXmlUnknown& operator=( const TiXmlUnknown& copy ) { copy.CopyTo( this ); return *this; }
+
+ /// Creates a copy of this Unknown and returns it.
+ virtual TiXmlNode* Clone() const;
+ // Print this Unknown to a FILE stream.
+ virtual void Print( FILE* cfile, int depth ) const;
+
+ virtual const char* Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding );
+
+ virtual const TiXmlUnknown* ToUnknown() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlUnknown* ToUnknown() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected:
+ void CopyTo( TiXmlUnknown* target ) const;
+
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+
+};
+
+
+/** Always the top level node. A document binds together all the
+ XML pieces. It can be saved, loaded, and printed to the screen.
+ The 'value' of a document node is the xml file name.
+*/
+class TiXmlDocument : public TiXmlNode
+{
+public:
+ /// Create an empty document, that has no name.
+ TiXmlDocument();
+ /// Create a document with a name. The name of the document is also the filename of the xml.
+ TiXmlDocument( const char * documentName );
+
+ #ifdef TIXML_USE_STL
+ /// Constructor.
+ TiXmlDocument( const std::string& documentName );
+ #endif
+
+ TiXmlDocument( const TiXmlDocument& copy );
+ TiXmlDocument& operator=( const TiXmlDocument& copy );
+
+ virtual ~TiXmlDocument() {}
+
+ /** Load a file using the current document value.
+ Returns true if successful. Will delete any existing
+ document data before loading.
+ */
+ bool LoadFile( TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the current document value. Returns true if successful.
+ bool SaveFile() const;
+ /// Load a file using the given filename. Returns true if successful.
+ bool LoadFile( const char * filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given filename. Returns true if successful.
+ bool SaveFile( const char * filename ) const;
+ /** Load a file using the given FILE*. Returns true if successful. Note that this method
+ doesn't stream - the entire object pointed at by the FILE*
+ will be interpreted as an XML file. TinyXML doesn't stream in XML from the current
+ file location. Streaming may be added in the future.
+ */
+ bool LoadFile( FILE*, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+ /// Save a file using the given FILE*. Returns true if successful.
+ bool SaveFile( FILE* ) const;
+
+ #ifdef TIXML_USE_STL
+ bool LoadFile( const std::string& filename, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING ) ///< STL std::string version.
+ {
+ return LoadFile( filename.c_str(), encoding );
+ }
+ bool SaveFile( const std::string& filename ) const ///< STL std::string version.
+ {
+ return SaveFile( filename.c_str() );
+ }
+ #endif
+
+ /** Parse the given null terminated block of xml data. Passing in an encoding to this
+ method (either TIXML_ENCODING_LEGACY or TIXML_ENCODING_UTF8 will force TinyXml
+ to use that encoding, regardless of what TinyXml might otherwise try to detect.
+ */
+ virtual const char* Parse( const char* p, TiXmlParsingData* data = 0, TiXmlEncoding encoding = TIXML_DEFAULT_ENCODING );
+
+ /** Get the root element -- the only top level element -- of the document.
+ In well formed XML, there should only be one. TinyXml is tolerant of
+ multiple elements at the document level.
+ */
+ const TiXmlElement* RootElement() const { return FirstChildElement(); }
+ TiXmlElement* RootElement() { return FirstChildElement(); }
+
+ /** If an error occurs, Error will be set to true. Also,
+ - The ErrorId() will contain the integer identifier of the error (not generally useful)
+ - The ErrorDesc() method will return the name of the error. (very useful)
+ - The ErrorRow() and ErrorCol() will return the location of the error (if known)
+ */
+ bool Error() const { return error; }
+
+ /// Contains a textual (english) description of the error if one occurs.
+ const char * ErrorDesc() const { return errorDesc.c_str (); }
+
+ /** Generally, you probably want the error string ( ErrorDesc() ). But if you
+ prefer the ErrorId, this function will fetch it.
+ */
+ int ErrorId() const { return errorId; }
+
+ /** Returns the location (if known) of the error. The first column is column 1,
+ and the first row is row 1. A value of 0 means the row and column wasn't applicable
+ (memory errors, for example, have no row/column) or the parser lost the error. (An
+ error in the error reporting, in that case.)
+
+ @sa SetTabSize, Row, Column
+ */
+ int ErrorRow() const { return errorLocation.row+1; }
+ int ErrorCol() const { return errorLocation.col+1; } ///< The column where the error occured. See ErrorRow()
+
+ /** SetTabSize() allows the error reporting functions (ErrorRow() and ErrorCol())
+ to report the correct values for row and column. It does not change the output
+ or input in any way.
+
+ By calling this method, with a tab size
+ greater than 0, the row and column of each node and attribute is stored
+ when the file is loaded. Very useful for tracking the DOM back in to
+ the source file.
+
+ The tab size is required for calculating the location of nodes. If not
+ set, the default of 4 is used. The tabsize is set per document. Setting
+ the tabsize to 0 disables row/column tracking.
+
+ Note that row and column tracking is not supported when using operator>>.
+
+ The tab size needs to be enabled before the parse or load. Correct usage:
+ @verbatim
+ TiXmlDocument doc;
+ doc.SetTabSize( 8 );
+ doc.Load( "myfile.xml" );
+ @endverbatim
+
+ @sa Row, Column
+ */
+ void SetTabSize( int _tabsize ) { tabsize = _tabsize; }
+
+ int TabSize() const { return tabsize; }
+
+ /** If you have handled the error, it can be reset with this call. The error
+ state is automatically cleared if you Parse a new XML block.
+ */
+ void ClearError() { error = false;
+ errorId = 0;
+ errorDesc = "";
+ errorLocation.row = errorLocation.col = 0;
+ //errorLocation.last = 0;
+ }
+
+ /** Write the document to standard out using formatted printing ("pretty print"). */
+ void Print() const { Print( stdout, 0 ); }
+
+ /* Write the document to a string using formatted printing ("pretty print"). This
+ will allocate a character array (new char[]) and return it as a pointer. The
+ calling code pust call delete[] on the return char* to avoid a memory leak.
+ */
+ //char* PrintToMemory() const;
+
+ /// Print this Document to a FILE stream.
+ virtual void Print( FILE* cfile, int depth = 0 ) const;
+ // [internal use]
+ void SetError( int err, const char* errorLocation, TiXmlParsingData* prevData, TiXmlEncoding encoding );
+
+ virtual const TiXmlDocument* ToDocument() const { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+ virtual TiXmlDocument* ToDocument() { return this; } ///< Cast to a more defined type. Will return null not of the requested type.
+
+ /** Walk the XML tree visiting this node and all of its children.
+ */
+ virtual bool Accept( TiXmlVisitor* content ) const;
+
+protected :
+ // [internal use]
+ virtual TiXmlNode* Clone() const;
+ #ifdef TIXML_USE_STL
+ virtual void StreamIn( std::istream * in, TIXML_STRING * tag );
+ #endif
+
+private:
+ void CopyTo( TiXmlDocument* target ) const;
+
+ bool error;
+ int errorId;
+ TIXML_STRING errorDesc;
+ int tabsize;
+ TiXmlCursor errorLocation;
+ bool useMicrosoftBOM; // the UTF-8 BOM were found when read. Note this, and try to write.
+};
+
+
+/**
+ A TiXmlHandle is a class that wraps a node pointer with null checks; this is
+ an incredibly useful thing. Note that TiXmlHandle is not part of the TinyXml
+ DOM structure. It is a separate utility class.
+
+ Take an example:
+ @verbatim
+ <Document>
+ <Element attributeA = "valueA">
+ <Child attributeB = "value1" />
+ <Child attributeB = "value2" />
+ </Element>
+ <Document>
+ @endverbatim
+
+ Assuming you want the value of "attributeB" in the 2nd "Child" element, it's very
+ easy to write a *lot* of code that looks like:
+
+ @verbatim
+ TiXmlElement* root = document.FirstChildElement( "Document" );
+ if ( root )
+ {
+ TiXmlElement* element = root->FirstChildElement( "Element" );
+ if ( element )
+ {
+ TiXmlElement* child = element->FirstChildElement( "Child" );
+ if ( child )
+ {
+ TiXmlElement* child2 = child->NextSiblingElement( "Child" );
+ if ( child2 )
+ {
+ // Finally do something useful.
+ @endverbatim
+
+ And that doesn't even cover "else" cases. TiXmlHandle addresses the verbosity
+ of such code. A TiXmlHandle checks for null pointers so it is perfectly safe
+ and correct to use:
+
+ @verbatim
+ TiXmlHandle docHandle( &document );
+ TiXmlElement* child2 = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", 1 ).ToElement();
+ if ( child2 )
+ {
+ // do something useful
+ @endverbatim
+
+ Which is MUCH more concise and useful.
+
+ It is also safe to copy handles - internally they are nothing more than node pointers.
+ @verbatim
+ TiXmlHandle handleCopy = handle;
+ @endverbatim
+
+ What they should not be used for is iteration:
+
+ @verbatim
+ int i=0;
+ while ( true )
+ {
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).Child( "Child", i ).ToElement();
+ if ( !child )
+ break;
+ // do something
+ ++i;
+ }
+ @endverbatim
+
+ It seems reasonable, but it is in fact two embedded while loops. The Child method is
+ a linear walk to find the element, so this code would iterate much more than it needs
+ to. Instead, prefer:
+
+ @verbatim
+ TiXmlElement* child = docHandle.FirstChild( "Document" ).FirstChild( "Element" ).FirstChild( "Child" ).ToElement();
+
+ for( child; child; child=child->NextSiblingElement() )
+ {
+ // do something
+ }
+ @endverbatim
+*/
+class TiXmlHandle
+{
+public:
+ /// Create a handle from any node (at any depth of the tree.) This can be a null pointer.
+ TiXmlHandle( TiXmlNode* _node ) { this->node = _node; }
+ /// Copy constructor
+ TiXmlHandle( const TiXmlHandle& ref ) { this->node = ref.node; }
+ TiXmlHandle operator=( const TiXmlHandle& ref ) { if ( &ref != this ) this->node = ref.node; return *this; }
+
+ /// Return a handle to the first child node.
+ TiXmlHandle FirstChild() const;
+ /// Return a handle to the first child node with the given name.
+ TiXmlHandle FirstChild( const char * value ) const;
+ /// Return a handle to the first child element.
+ TiXmlHandle FirstChildElement() const;
+ /// Return a handle to the first child element with the given name.
+ TiXmlHandle FirstChildElement( const char * value ) const;
+
+ /** Return a handle to the "index" child with the given name.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( const char* value, int index ) const;
+ /** Return a handle to the "index" child.
+ The first child is 0, the second 1, etc.
+ */
+ TiXmlHandle Child( int index ) const;
+ /** Return a handle to the "index" child element with the given name.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( const char* value, int index ) const;
+ /** Return a handle to the "index" child element.
+ The first child element is 0, the second 1, etc. Note that only TiXmlElements
+ are indexed: other types are not counted.
+ */
+ TiXmlHandle ChildElement( int index ) const;
+
+ #ifdef TIXML_USE_STL
+ TiXmlHandle FirstChild( const std::string& _value ) const { return FirstChild( _value.c_str() ); }
+ TiXmlHandle FirstChildElement( const std::string& _value ) const { return FirstChildElement( _value.c_str() ); }
+
+ TiXmlHandle Child( const std::string& _value, int index ) const { return Child( _value.c_str(), index ); }
+ TiXmlHandle ChildElement( const std::string& _value, int index ) const { return ChildElement( _value.c_str(), index ); }
+ #endif
+
+ /** Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* ToNode() const { return node; }
+ /** Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* ToElement() const { return ( ( node && node->ToElement() ) ? node->ToElement() : 0 ); }
+ /** Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* ToText() const { return ( ( node && node->ToText() ) ? node->ToText() : 0 ); }
+ /** Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* ToUnknown() const { return ( ( node && node->ToUnknown() ) ? node->ToUnknown() : 0 ); }
+
+ /** @deprecated use ToNode.
+ Return the handle as a TiXmlNode. This may return null.
+ */
+ TiXmlNode* Node() const { return ToNode(); }
+ /** @deprecated use ToElement.
+ Return the handle as a TiXmlElement. This may return null.
+ */
+ TiXmlElement* Element() const { return ToElement(); }
+ /** @deprecated use ToText()
+ Return the handle as a TiXmlText. This may return null.
+ */
+ TiXmlText* Text() const { return ToText(); }
+ /** @deprecated use ToUnknown()
+ Return the handle as a TiXmlUnknown. This may return null.
+ */
+ TiXmlUnknown* Unknown() const { return ToUnknown(); }
+
+private:
+ TiXmlNode* node;
+};
+
+
+/** Print to memory functionality. The TiXmlPrinter is useful when you need to:
+
+ -# Print to memory (especially in non-STL mode)
+ -# Control formatting (line endings, etc.)
+
+ When constructed, the TiXmlPrinter is in its default "pretty printing" mode.
+ Before calling Accept() you can call methods to control the printing
+ of the XML document. After TiXmlNode::Accept() is called, the printed document can
+ be accessed via the CStr(), Str(), and Size() methods.
+
+ TiXmlPrinter uses the Visitor API.
+ @verbatim
+ TiXmlPrinter printer;
+ printer.SetIndent( "\t" );
+
+ doc.Accept( &printer );
+ fprintf( stdout, "%s", printer.CStr() );
+ @endverbatim
+*/
+class TiXmlPrinter : public TiXmlVisitor
+{
+public:
+ TiXmlPrinter() : depth( 0 ), simpleTextPrint( false ),
+ buffer(), indent( " " ), lineBreak( "\n" ) {}
+
+ virtual bool VisitEnter( const TiXmlDocument& doc );
+ virtual bool VisitExit( const TiXmlDocument& doc );
+
+ virtual bool VisitEnter( const TiXmlElement& element, const TiXmlAttribute* firstAttribute );
+ virtual bool VisitExit( const TiXmlElement& element );
+
+ virtual bool Visit( const TiXmlDeclaration& declaration );
+ virtual bool Visit( const TiXmlText& text );
+ virtual bool Visit( const TiXmlComment& comment );
+ virtual bool Visit( const TiXmlUnknown& unknown );
+
+ /** Set the indent characters for printing. By default 4 spaces
+ but tab (\t) is also useful, or null/empty string for no indentation.
+ */
+ void SetIndent( const char* _indent ) { indent = _indent ? _indent : "" ; }
+ /// Query the indention string.
+ const char* Indent() { return indent.c_str(); }
+ /** Set the line breaking string. By default set to newline (\n).
+ Some operating systems prefer other characters, or can be
+ set to the null/empty string for no indenation.
+ */
+ void SetLineBreak( const char* _lineBreak ) { lineBreak = _lineBreak ? _lineBreak : ""; }
+ /// Query the current line breaking string.
+ const char* LineBreak() { return lineBreak.c_str(); }
+
+ /** Switch over to "stream printing" which is the most dense formatting without
+ linebreaks. Common when the XML is needed for network transmission.
+ */
+ void SetStreamPrinting() { indent = "";
+ lineBreak = "";
+ }
+ /// Return the result.
+ const char* CStr() { return buffer.c_str(); }
+ /// Return the length of the result string.
+ size_t Size() { return buffer.size(); }
+
+ #ifdef TIXML_USE_STL
+ /// Return the result.
+ const std::string& Str() { return buffer; }
+ #endif
+
+private:
+ void DoIndent() {
+ for( int i=0; i<depth; ++i )
+ buffer += indent;
+ }
+ void DoLineBreak() {
+ buffer += lineBreak;
+ }
+
+ int depth;
+ bool simpleTextPrint;
+ TIXML_STRING buffer;
+ TIXML_STRING indent;
+ TIXML_STRING lineBreak;
+};
+
+
+#ifdef _MSC_VER
+#pragma warning( pop )
+#endif
+
+#endif
diff --git a/test/tinyxml/tinyxmlerror.cpp b/test/tinyxml/tinyxmlerror.cpp
new file mode 100644
index 0000000..538c21d
--- /dev/null
+++ b/test/tinyxml/tinyxmlerror.cpp
@@ -0,0 +1,52 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code (2.0 and earlier )copyright (c) 2000-2006 Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include "tinyxml.h"
+
+// The goal of the seperate error file is to make the first
+// step towards localization. tinyxml (currently) only supports
+// english error messages, but the could now be translated.
+//
+// It also cleans up the code a bit.
+//
+
+const char* TiXmlBase::errorString[ TiXmlBase::TIXML_ERROR_STRING_COUNT ] =
+{
+ "No error",
+ "Error",
+ "Failed to open file",
+ "Error parsing Element.",
+ "Failed to read Element name",
+ "Error reading Element value.",
+ "Error reading Attributes.",
+ "Error: empty tag.",
+ "Error reading end tag.",
+ "Error parsing Unknown.",
+ "Error parsing Comment.",
+ "Error parsing Declaration.",
+ "Error document empty.",
+ "Error null (0) or unexpected EOF found in input stream.",
+ "Error parsing CDATA.",
+ "Error when TiXmlDocument added to document, because TiXmlDocument can only be at the root.",
+};
diff --git a/test/tinyxml/tinyxmlparser.cpp b/test/tinyxml/tinyxmlparser.cpp
new file mode 100644
index 0000000..81b7eae
--- /dev/null
+++ b/test/tinyxml/tinyxmlparser.cpp
@@ -0,0 +1,1638 @@
+/*
+www.sourceforge.net/projects/tinyxml
+Original code by Lee Thomason (www.grinninglizard.com)
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any
+damages arising from the use of this software.
+
+Permission is granted to anyone to use this software for any
+purpose, including commercial applications, and to alter it and
+redistribute it freely, subject to the following restrictions:
+
+1. The origin of this software must not be misrepresented; you must
+not claim that you wrote the original software. If you use this
+software in a product, an acknowledgment in the product documentation
+would be appreciated but is not required.
+
+2. Altered source versions must be plainly marked as such, and
+must not be misrepresented as being the original software.
+
+3. This notice may not be removed or altered from any source
+distribution.
+*/
+
+#include <ctype.h>
+#include <stddef.h>
+
+#include "tinyxml.h"
+
+//#define DEBUG_PARSER
+#if defined( DEBUG_PARSER )
+# if defined( DEBUG ) && defined( _MSC_VER )
+# include <windows.h>
+# define TIXML_LOG OutputDebugString
+# else
+# define TIXML_LOG printf
+# endif
+#endif
+
+// Note tha "PutString" hardcodes the same list. This
+// is less flexible than it appears. Changing the entries
+// or order will break putstring.
+TiXmlBase::Entity TiXmlBase::entity[ TiXmlBase::NUM_ENTITY ] =
+{
+ { "&amp;", 5, '&' },
+ { "&lt;", 4, '<' },
+ { "&gt;", 4, '>' },
+ { "&quot;", 6, '\"' },
+ { "&apos;", 6, '\'' }
+};
+
+// Bunch of unicode info at:
+// http://www.unicode.org/faq/utf_bom.html
+// Including the basic of this table, which determines the #bytes in the
+// sequence from the lead byte. 1 placed for invalid sequences --
+// although the result will be junk, pass it through as much as possible.
+// Beware of the non-characters in UTF-8:
+// ef bb bf (Microsoft "lead bytes")
+// ef bf be
+// ef bf bf
+
+const unsigned char TIXML_UTF_LEAD_0 = 0xefU;
+const unsigned char TIXML_UTF_LEAD_1 = 0xbbU;
+const unsigned char TIXML_UTF_LEAD_2 = 0xbfU;
+
+const int TiXmlBase::utf8ByteTable[256] =
+{
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x00
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x10
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x20
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x30
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x40
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x50
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x60
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x70 End of ASCII range
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x80 0x80 to 0xc1 invalid
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0x90
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xa0
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 0xb0
+ 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xc0 0xc2 to 0xdf 2 byte
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // 0xd0
+ 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 0xe0 0xe0 to 0xef 3 byte
+ 4, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 // 0xf0 0xf0 to 0xf4 4 byte, 0xf5 and higher invalid
+};
+
+
+void TiXmlBase::ConvertUTF32ToUTF8( unsigned long input, char* output, int* length )
+{
+ const unsigned long BYTE_MASK = 0xBF;
+ const unsigned long BYTE_MARK = 0x80;
+ const unsigned long FIRST_BYTE_MARK[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC };
+
+ if (input < 0x80)
+ *length = 1;
+ else if ( input < 0x800 )
+ *length = 2;
+ else if ( input < 0x10000 )
+ *length = 3;
+ else if ( input < 0x200000 )
+ *length = 4;
+ else
+ { *length = 0; return; } // This code won't covert this correctly anyway.
+
+ output += *length;
+
+ // Scary scary fall throughs.
+ switch (*length)
+ {
+ case 4:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 3:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 2:
+ --output;
+ *output = (char)((input | BYTE_MARK) & BYTE_MASK);
+ input >>= 6;
+ case 1:
+ --output;
+ *output = (char)(input | FIRST_BYTE_MARK[*length]);
+ }
+}
+
+
+/*static*/ int TiXmlBase::IsAlpha( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if ( encoding == TIXML_ENCODING_UTF8 )
+// {
+ if ( anyByte < 127 )
+ return isalpha( anyByte );
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalpha( anyByte );
+// }
+}
+
+
+/*static*/ int TiXmlBase::IsAlphaNum( unsigned char anyByte, TiXmlEncoding /*encoding*/ )
+{
+ // This will only work for low-ascii, everything else is assumed to be a valid
+ // letter. I'm not sure this is the best approach, but it is quite tricky trying
+ // to figure out alhabetical vs. not across encoding. So take a very
+ // conservative approach.
+
+// if ( encoding == TIXML_ENCODING_UTF8 )
+// {
+ if ( anyByte < 127 )
+ return isalnum( anyByte );
+ else
+ return 1; // What else to do? The unicode set is huge...get the english ones right.
+// }
+// else
+// {
+// return isalnum( anyByte );
+// }
+}
+
+
+class TiXmlParsingData
+{
+ friend class TiXmlDocument;
+ public:
+ void Stamp( const char* now, TiXmlEncoding encoding );
+
+ const TiXmlCursor& Cursor() const { return cursor; }
+
+ private:
+ // Only used by the document!
+ TiXmlParsingData( const char* start, int _tabsize, int row, int col )
+ {
+ assert( start );
+ stamp = start;
+ tabsize = _tabsize;
+ cursor.row = row;
+ cursor.col = col;
+ }
+
+ TiXmlCursor cursor;
+ const char* stamp;
+ int tabsize;
+};
+
+
+void TiXmlParsingData::Stamp( const char* now, TiXmlEncoding encoding )
+{
+ assert( now );
+
+ // Do nothing if the tabsize is 0.
+ if ( tabsize < 1 )
+ {
+ return;
+ }
+
+ // Get the current row, column.
+ int row = cursor.row;
+ int col = cursor.col;
+ const char* p = stamp;
+ assert( p );
+
+ while ( p < now )
+ {
+ // Treat p as unsigned, so we have a happy compiler.
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Code contributed by Fletcher Dunn: (modified by lee)
+ switch (*pU) {
+ case 0:
+ // We *should* never get here, but in case we do, don't
+ // advance past the terminating null character, ever
+ return;
+
+ case '\r':
+ // bump down to the next line
+ ++row;
+ col = 0;
+ // Eat the character
+ ++p;
+
+ // Check for \r\n sequence, and treat this as a single character
+ if (*p == '\n') {
+ ++p;
+ }
+ break;
+
+ case '\n':
+ // bump down to the next line
+ ++row;
+ col = 0;
+
+ // Eat the character
+ ++p;
+
+ // Check for \n\r sequence, and treat this as a single
+ // character. (Yes, this bizarre thing does occur still
+ // on some arcane platforms...)
+ if (*p == '\r') {
+ ++p;
+ }
+ break;
+
+ case '\t':
+ // Eat the character
+ ++p;
+
+ // Skip to next tab stop
+ col = (col / tabsize + 1) * tabsize;
+ break;
+
+ case TIXML_UTF_LEAD_0:
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ if ( *(p+1) && *(p+2) )
+ {
+ // In these cases, don't advance the column. These are
+ // 0-width spaces.
+ if ( *(pU+1)==TIXML_UTF_LEAD_1 && *(pU+2)==TIXML_UTF_LEAD_2 )
+ p += 3;
+ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbeU )
+ p += 3;
+ else if ( *(pU+1)==0xbfU && *(pU+2)==0xbfU )
+ p += 3;
+ else
+ { p +=3; ++col; } // A normal character.
+ }
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+
+ default:
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ // Eat the 1 to 4 byte utf8 character.
+ int step = TiXmlBase::utf8ByteTable[*((const unsigned char*)p)];
+ if ( step == 0 )
+ step = 1; // Error case from bad encoding, but handle gracefully.
+ p += step;
+
+ // Just advance one column, of course.
+ ++col;
+ }
+ else
+ {
+ ++p;
+ ++col;
+ }
+ break;
+ }
+ }
+ cursor.row = row;
+ cursor.col = col;
+ assert( cursor.row >= -1 );
+ assert( cursor.col >= -1 );
+ stamp = p;
+ assert( stamp );
+}
+
+
+const char* TiXmlBase::SkipWhiteSpace( const char* p, TiXmlEncoding encoding )
+{
+ if ( !p || !*p )
+ {
+ return 0;
+ }
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ while ( *p )
+ {
+ const unsigned char* pU = (const unsigned char*)p;
+
+ // Skip the stupid Microsoft UTF-8 Byte order marks
+ if ( *(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==TIXML_UTF_LEAD_1
+ && *(pU+2)==TIXML_UTF_LEAD_2 )
+ {
+ p += 3;
+ continue;
+ }
+ else if(*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbeU )
+ {
+ p += 3;
+ continue;
+ }
+ else if(*(pU+0)==TIXML_UTF_LEAD_0
+ && *(pU+1)==0xbfU
+ && *(pU+2)==0xbfU )
+ {
+ p += 3;
+ continue;
+ }
+
+ if ( IsWhiteSpace( *p ) ) // Still using old rules for white space.
+ ++p;
+ else
+ break;
+ }
+ }
+ else
+ {
+ while ( *p && IsWhiteSpace( *p ) )
+ ++p;
+ }
+
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+/*static*/ bool TiXmlBase::StreamWhiteSpace( std::istream * in, TIXML_STRING * tag )
+{
+ for( ;; )
+ {
+ if ( !in->good() ) return false;
+
+ int c = in->peek();
+ // At this scope, we can't get to a document. So fail silently.
+ if ( !IsWhiteSpace( c ) || c <= 0 )
+ return true;
+
+ *tag += (char) in->get();
+ }
+}
+
+/*static*/ bool TiXmlBase::StreamTo( std::istream * in, int character, TIXML_STRING * tag )
+{
+ //assert( character > 0 && character < 128 ); // else it won't work in utf-8
+ while ( in->good() )
+ {
+ int c = in->peek();
+ if ( c == character )
+ return true;
+ if ( c <= 0 ) // Silent failure: can't get document at this scope
+ return false;
+
+ in->get();
+ *tag += (char) c;
+ }
+ return false;
+}
+#endif
+
+// One of TinyXML's more performance demanding functions. Try to keep the memory overhead down. The
+// "assign" optimization removes over 10% of the execution time.
+//
+const char* TiXmlBase::ReadName( const char* p, TIXML_STRING * name, TiXmlEncoding encoding )
+{
+ // Oddly, not supported on some comilers,
+ //name->clear();
+ // So use this:
+ *name = "";
+ assert( p );
+
+ // Names start with letters or underscores.
+ // Of course, in unicode, tinyxml has no idea what a letter *is*. The
+ // algorithm is generous.
+ //
+ // After that, they can be letters, underscores, numbers,
+ // hyphens, or colons. (Colons are valid ony for namespaces,
+ // but tinyxml can't tell namespaces from names.)
+ if ( p && *p
+ && ( IsAlpha( (unsigned char) *p, encoding ) || *p == '_' ) )
+ {
+ const char* start = p;
+ while( p && *p
+ && ( IsAlphaNum( (unsigned char ) *p, encoding )
+ || *p == '_'
+ || *p == '-'
+ || *p == '.'
+ || *p == ':' ) )
+ {
+ //(*name) += *p; // expensive
+ ++p;
+ }
+ if ( p-start > 0 ) {
+ name->assign( start, p-start );
+ }
+ return p;
+ }
+ return 0;
+}
+
+const char* TiXmlBase::GetEntity( const char* p, char* value, int* length, TiXmlEncoding encoding )
+{
+ // Presume an entity, and pull it out.
+ TIXML_STRING ent;
+ int i;
+ *length = 0;
+
+ if ( *(p+1) && *(p+1) == '#' && *(p+2) )
+ {
+ unsigned long ucs = 0;
+ ptrdiff_t delta = 0;
+ unsigned mult = 1;
+
+ if ( *(p+2) == 'x' )
+ {
+ // Hexadecimal.
+ if ( !*(p+3) ) return 0;
+
+ const char* q = p+3;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) return 0;
+
+ delta = q-p;
+ --q;
+
+ while ( *q != 'x' )
+ {
+ if ( *q >= '0' && *q <= '9' )
+ ucs += mult * (*q - '0');
+ else if ( *q >= 'a' && *q <= 'f' )
+ ucs += mult * (*q - 'a' + 10);
+ else if ( *q >= 'A' && *q <= 'F' )
+ ucs += mult * (*q - 'A' + 10 );
+ else
+ return 0;
+ mult *= 16;
+ --q;
+ }
+ }
+ else
+ {
+ // Decimal.
+ if ( !*(p+2) ) return 0;
+
+ const char* q = p+2;
+ q = strchr( q, ';' );
+
+ if ( !q || !*q ) return 0;
+
+ delta = q-p;
+ --q;
+
+ while ( *q != '#' )
+ {
+ if ( *q >= '0' && *q <= '9' )
+ ucs += mult * (*q - '0');
+ else
+ return 0;
+ mult *= 10;
+ --q;
+ }
+ }
+ if ( encoding == TIXML_ENCODING_UTF8 )
+ {
+ // convert the UCS to UTF-8
+ ConvertUTF32ToUTF8( ucs, value, length );
+ }
+ else
+ {
+ *value = (char)ucs;
+ *length = 1;
+ }
+ return p + delta + 1;
+ }
+
+ // Now try to match it.
+ for( i=0; i<NUM_ENTITY; ++i )
+ {
+ if ( strncmp( entity[i].str, p, entity[i].strLength ) == 0 )
+ {
+ assert( strlen( entity[i].str ) == entity[i].strLength );
+ *value = entity[i].chr;
+ *length = 1;
+ return ( p + entity[i].strLength );
+ }
+ }
+
+ // So it wasn't an entity, its unrecognized, or something like that.
+ *value = *p; // Don't put back the last one, since we return it!
+ //*length = 1; // Leave unrecognized entities - this doesn't really work.
+ // Just writes strange XML.
+ return p+1;
+}
+
+
+bool TiXmlBase::StringEqual( const char* p,
+ const char* tag,
+ bool ignoreCase,
+ TiXmlEncoding encoding )
+{
+ assert( p );
+ assert( tag );
+ if ( !p || !*p )
+ {
+ assert( 0 );
+ return false;
+ }
+
+ const char* q = p;
+
+ if ( ignoreCase )
+ {
+ while ( *q && *tag && ToLower( *q, encoding ) == ToLower( *tag, encoding ) )
+ {
+ ++q;
+ ++tag;
+ }
+
+ if ( *tag == 0 )
+ return true;
+ }
+ else
+ {
+ while ( *q && *tag && *q == *tag )
+ {
+ ++q;
+ ++tag;
+ }
+
+ if ( *tag == 0 ) // Have we found the end of the tag, and everything equal?
+ return true;
+ }
+ return false;
+}
+
+const char* TiXmlBase::ReadText( const char* p,
+ TIXML_STRING * text,
+ bool trimWhiteSpace,
+ const char* endTag,
+ bool caseInsensitive,
+ TiXmlEncoding encoding )
+{
+ *text = "";
+ if ( !trimWhiteSpace // certain tags always keep whitespace
+ || !condenseWhiteSpace ) // if true, whitespace is always kept
+ {
+ // Keep all the white space.
+ while ( p && *p
+ && !StringEqual( p, endTag, caseInsensitive, encoding )
+ )
+ {
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar( p, cArr, &len, encoding );
+ text->append( cArr, len );
+ }
+ }
+ else
+ {
+ bool whitespace = false;
+
+ // Remove leading white space:
+ p = SkipWhiteSpace( p, encoding );
+ while ( p && *p
+ && !StringEqual( p, endTag, caseInsensitive, encoding ) )
+ {
+ if ( *p == '\r' || *p == '\n' )
+ {
+ whitespace = true;
+ ++p;
+ }
+ else if ( IsWhiteSpace( *p ) )
+ {
+ whitespace = true;
+ ++p;
+ }
+ else
+ {
+ // If we've found whitespace, add it before the
+ // new character. Any whitespace just becomes a space.
+ if ( whitespace )
+ {
+ (*text) += ' ';
+ whitespace = false;
+ }
+ int len;
+ char cArr[4] = { 0, 0, 0, 0 };
+ p = GetChar( p, cArr, &len, encoding );
+ if ( len == 1 )
+ (*text) += cArr[0]; // more efficient
+ else
+ text->append( cArr, len );
+ }
+ }
+ }
+ if ( p && *p )
+ p += strlen( endTag );
+ return ( p && *p ) ? p : 0;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlDocument::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ // The basic issue with a document is that we don't know what we're
+ // streaming. Read something presumed to be a tag (and hope), then
+ // identify it, and call the appropriate stream method on the tag.
+ //
+ // This "pre-streaming" will never read the closing ">" so the
+ // sub-tag can orient itself.
+
+ if ( !StreamTo( in, '<', tag ) )
+ {
+ SetError( TIXML_ERROR_PARSING_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ while ( in->good() )
+ {
+ int tagIndex = (int) tag->length();
+ while ( in->good() && in->peek() != '>' )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ break;
+ }
+ (*tag) += (char) c;
+ }
+
+ if ( in->good() )
+ {
+ // We now have something we presume to be a node of
+ // some sort. Identify it, and call the node to
+ // continue streaming.
+ TiXmlNode* node = Identify( tag->c_str() + tagIndex, TIXML_DEFAULT_ENCODING );
+
+ if ( node )
+ {
+ node->StreamIn( in, tag );
+ bool isElement = node->ToElement() != 0;
+ delete node;
+ node = 0;
+
+ // If this is the root element, we're done. Parsing will be
+ // done by the >> operator.
+ if ( isElement )
+ {
+ return;
+ }
+ }
+ else
+ {
+ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ }
+ }
+ // We should have returned sooner.
+ SetError( TIXML_ERROR, 0, 0, TIXML_ENCODING_UNKNOWN );
+}
+
+#endif
+
+const char* TiXmlDocument::Parse( const char* p, TiXmlParsingData* prevData, TiXmlEncoding encoding )
+{
+ ClearError();
+
+ // Parse away, at the document level. Since a document
+ // contains nothing but other tags, most of what happens
+ // here is skipping white space.
+ if ( !p || !*p )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ // Note that, for a document, this needs to come
+ // before the while space skip, so that parsing
+ // starts from the pointer we are given.
+ location.Clear();
+ if ( prevData )
+ {
+ location.row = prevData->cursor.row;
+ location.col = prevData->cursor.col;
+ }
+ else
+ {
+ location.row = 0;
+ location.col = 0;
+ }
+ TiXmlParsingData data( p, TabSize(), location.row, location.col );
+ location = data.Cursor();
+
+ if ( encoding == TIXML_ENCODING_UNKNOWN )
+ {
+ // Check for the Microsoft UTF-8 lead bytes.
+ const unsigned char* pU = (const unsigned char*)p;
+ if ( *(pU+0) && *(pU+0) == TIXML_UTF_LEAD_0
+ && *(pU+1) && *(pU+1) == TIXML_UTF_LEAD_1
+ && *(pU+2) && *(pU+2) == TIXML_UTF_LEAD_2 )
+ {
+ encoding = TIXML_ENCODING_UTF8;
+ useMicrosoftBOM = true;
+ }
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p )
+ {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return 0;
+ }
+
+ while ( p && *p )
+ {
+ TiXmlNode* node = Identify( p, encoding );
+ if ( node )
+ {
+ p = node->Parse( p, &data, encoding );
+ LinkEndChild( node );
+ }
+ else
+ {
+ break;
+ }
+
+ // Did we get encoding info?
+ if ( encoding == TIXML_ENCODING_UNKNOWN
+ && node->ToDeclaration() )
+ {
+ TiXmlDeclaration* dec = node->ToDeclaration();
+ const char* enc = dec->Encoding();
+ assert( enc );
+
+ if ( *enc == 0 )
+ encoding = TIXML_ENCODING_UTF8;
+ else if ( StringEqual( enc, "UTF-8", true, TIXML_ENCODING_UNKNOWN ) )
+ encoding = TIXML_ENCODING_UTF8;
+ else if ( StringEqual( enc, "UTF8", true, TIXML_ENCODING_UNKNOWN ) )
+ encoding = TIXML_ENCODING_UTF8; // incorrect, but be nice
+ else
+ encoding = TIXML_ENCODING_LEGACY;
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+ }
+
+ // Was this empty?
+ if ( !firstChild ) {
+ SetError( TIXML_ERROR_DOCUMENT_EMPTY, 0, 0, encoding );
+ return 0;
+ }
+
+ // All is well.
+ return p;
+}
+
+void TiXmlDocument::SetError( int err, const char* pError, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ // The first error in a chain is more accurate - don't set again!
+ if ( error )
+ return;
+
+ assert( err > 0 && err < TIXML_ERROR_STRING_COUNT );
+ error = true;
+ errorId = err;
+ errorDesc = errorString[ errorId ];
+
+ errorLocation.Clear();
+ if ( pError && data )
+ {
+ data->Stamp( pError, encoding );
+ errorLocation = data->Cursor();
+ }
+}
+
+
+TiXmlNode* TiXmlNode::Identify( const char* p, TiXmlEncoding encoding )
+{
+ TiXmlNode* returnNode = 0;
+
+ p = SkipWhiteSpace( p, encoding );
+ if( !p || !*p || *p != '<' )
+ {
+ return 0;
+ }
+
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( !p || !*p )
+ {
+ return 0;
+ }
+
+ // What is this thing?
+ // - Elements start with a letter or underscore, but xml is reserved.
+ // - Comments: <!--
+ // - Decleration: <?xml
+ // - Everthing else is unknown to tinyxml.
+ //
+
+ const char* xmlHeader = { "<?xml" };
+ const char* commentHeader = { "<!--" };
+ const char* dtdHeader = { "<!" };
+ const char* cdataHeader = { "<![CDATA[" };
+
+ if ( StringEqual( p, xmlHeader, true, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Declaration\n" );
+ #endif
+ returnNode = new TiXmlDeclaration();
+ }
+ else if ( StringEqual( p, commentHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Comment\n" );
+ #endif
+ returnNode = new TiXmlComment();
+ }
+ else if ( StringEqual( p, cdataHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing CDATA\n" );
+ #endif
+ TiXmlText* text = new TiXmlText( "" );
+ text->SetCDATA( true );
+ returnNode = text;
+ }
+ else if ( StringEqual( p, dtdHeader, false, encoding ) )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Unknown(1)\n" );
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+ else if ( IsAlpha( *(p+1), encoding )
+ || *(p+1) == '_' )
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Element\n" );
+ #endif
+ returnNode = new TiXmlElement( "" );
+ }
+ else
+ {
+ #ifdef DEBUG_PARSER
+ TIXML_LOG( "XML parsing Unknown(2)\n" );
+ #endif
+ returnNode = new TiXmlUnknown();
+ }
+
+ if ( returnNode )
+ {
+ // Set the parent, so it can report errors
+ returnNode->parent = this;
+ }
+ return returnNode;
+}
+
+#ifdef TIXML_USE_STL
+
+void TiXmlElement::StreamIn (std::istream * in, TIXML_STRING * tag)
+{
+ // We're called with some amount of pre-parsing. That is, some of "this"
+ // element is in "tag". Go ahead and stream to the closing ">"
+ while( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c ;
+
+ if ( c == '>' )
+ break;
+ }
+
+ if ( tag->length() < 3 ) return;
+
+ // Okay...if we are a "/>" tag, then we're done. We've read a complete tag.
+ // If not, identify and stream.
+
+ if ( tag->at( tag->length() - 1 ) == '>'
+ && tag->at( tag->length() - 2 ) == '/' )
+ {
+ // All good!
+ return;
+ }
+ else if ( tag->at( tag->length() - 1 ) == '>' )
+ {
+ // There is more. Could be:
+ // text
+ // cdata text (which looks like another node)
+ // closing tag
+ // another node.
+ for ( ;; )
+ {
+ StreamWhiteSpace( in, tag );
+
+ // Do we have text?
+ if ( in->good() && in->peek() != '<' )
+ {
+ // Yep, text.
+ TiXmlText text( "" );
+ text.StreamIn( in, tag );
+
+ // What follows text is a closing tag or another node.
+ // Go around again and figure it out.
+ continue;
+ }
+
+ // We now have either a closing tag...or another node.
+ // We should be at a "<", regardless.
+ if ( !in->good() ) return;
+ assert( in->peek() == '<' );
+ int tagIndex = (int) tag->length();
+
+ bool closingTag = false;
+ bool firstCharFound = false;
+
+ for( ;; )
+ {
+ if ( !in->good() )
+ return;
+
+ int c = in->peek();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ if ( c == '>' )
+ break;
+
+ *tag += (char) c;
+ in->get();
+
+ // Early out if we find the CDATA id.
+ if ( c == '[' && tag->size() >= 9 )
+ {
+ size_t len = tag->size();
+ const char* start = tag->c_str() + len - 9;
+ if ( strcmp( start, "<![CDATA[" ) == 0 ) {
+ assert( !closingTag );
+ break;
+ }
+ }
+
+ if ( !firstCharFound && c != '<' && !IsWhiteSpace( c ) )
+ {
+ firstCharFound = true;
+ if ( c == '/' )
+ closingTag = true;
+ }
+ }
+ // If it was a closing tag, then read in the closing '>' to clean up the input stream.
+ // If it was not, the streaming will be done by the tag.
+ if ( closingTag )
+ {
+ if ( !in->good() )
+ return;
+
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ assert( c == '>' );
+ *tag += (char) c;
+
+ // We are done, once we've found our closing tag.
+ return;
+ }
+ else
+ {
+ // If not a closing tag, id it, and stream.
+ const char* tagloc = tag->c_str() + tagIndex;
+ TiXmlNode* node = Identify( tagloc, TIXML_DEFAULT_ENCODING );
+ if ( !node )
+ return;
+ node->StreamIn( in, tag );
+ delete node;
+ node = 0;
+
+ // No return: go around from the beginning: text, closing tag, or node.
+ }
+ }
+ }
+}
+#endif
+
+const char* TiXmlElement::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ p = SkipWhiteSpace( p, encoding );
+ TiXmlDocument* document = GetDocument();
+
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, 0, 0, encoding );
+ return 0;
+ }
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+
+ if ( *p != '<' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, p, data, encoding );
+ return 0;
+ }
+
+ p = SkipWhiteSpace( p+1, encoding );
+
+ // Read the name.
+ const char* pErr = p;
+
+ p = ReadName( p, &value, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_FAILED_TO_READ_ELEMENT_NAME, pErr, data, encoding );
+ return 0;
+ }
+
+ TIXML_STRING endTag ("</");
+ endTag += value;
+
+ // Check for and read attributes. Also look for an empty
+ // tag or an end tag.
+ while ( p && *p )
+ {
+ pErr = p;
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+ return 0;
+ }
+ if ( *p == '/' )
+ {
+ ++p;
+ // Empty tag.
+ if ( *p != '>' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_EMPTY, p, data, encoding );
+ return 0;
+ }
+ return (p+1);
+ }
+ else if ( *p == '>' )
+ {
+ // Done with attributes (if there were any.)
+ // Read the value -- which can include other
+ // elements -- read the end tag, and return.
+ ++p;
+ p = ReadValue( p, data, encoding ); // Note this is an Element method, and will set the error if one happens.
+ if ( !p || !*p ) {
+ // We were looking for the end tag, but found nothing.
+ // Fix for [ 1663758 ] Failure to report error on bad XML
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+
+ // We should find the end tag now
+ // note that:
+ // </foo > and
+ // </foo>
+ // are both valid end tags.
+ if ( StringEqual( p, endTag.c_str(), false, encoding ) )
+ {
+ p += endTag.length();
+ p = SkipWhiteSpace( p, encoding );
+ if ( p && *p && *p == '>' ) {
+ ++p;
+ return p;
+ }
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+ else
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_END_TAG, p, data, encoding );
+ return 0;
+ }
+ }
+ else
+ {
+ // Try to read an attribute:
+ TiXmlAttribute* attrib = new TiXmlAttribute();
+ if ( !attrib )
+ {
+ return 0;
+ }
+
+ attrib->SetDocument( document );
+ pErr = p;
+ p = attrib->Parse( p, data, encoding );
+
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
+ delete attrib;
+ return 0;
+ }
+
+ // Handle the strange case of double attributes:
+ #ifdef TIXML_USE_STL
+ TiXmlAttribute* node = attributeSet.Find( attrib->NameTStr() );
+ #else
+ TiXmlAttribute* node = attributeSet.Find( attrib->Name() );
+ #endif
+ if ( node )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_ELEMENT, pErr, data, encoding );
+ delete attrib;
+ return 0;
+ }
+
+ attributeSet.Add( attrib );
+ }
+ }
+ return p;
+}
+
+
+const char* TiXmlElement::ReadValue( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+
+ // Read in text and elements in any order.
+ const char* pWithWhiteSpace = p;
+ p = SkipWhiteSpace( p, encoding );
+
+ while ( p && *p )
+ {
+ if ( *p != '<' )
+ {
+ // Take what we have, make a text element.
+ TiXmlText* textNode = new TiXmlText( "" );
+
+ if ( !textNode )
+ {
+ return 0;
+ }
+
+ if ( TiXmlBase::IsWhiteSpaceCondensed() )
+ {
+ p = textNode->Parse( p, data, encoding );
+ }
+ else
+ {
+ // Special case: we want to keep the white space
+ // so that leading spaces aren't removed.
+ p = textNode->Parse( pWithWhiteSpace, data, encoding );
+ }
+
+ if ( !textNode->Blank() )
+ LinkEndChild( textNode );
+ else
+ delete textNode;
+ }
+ else
+ {
+ // We hit a '<'
+ // Have we hit a new element or an end tag? This could also be
+ // a TiXmlText in the "CDATA" style.
+ if ( StringEqual( p, "</", false, encoding ) )
+ {
+ return p;
+ }
+ else
+ {
+ TiXmlNode* node = Identify( p, encoding );
+ if ( node )
+ {
+ p = node->Parse( p, data, encoding );
+ LinkEndChild( node );
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ }
+ pWithWhiteSpace = p;
+ p = SkipWhiteSpace( p, encoding );
+ }
+
+ if ( !p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ELEMENT_VALUE, 0, 0, encoding );
+ }
+ return p;
+}
+
+
+#ifdef TIXML_USE_STL
+void TiXmlUnknown::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c;
+
+ if ( c == '>' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlUnknown::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ if ( !p || !*p || *p != '<' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_UNKNOWN, p, data, encoding );
+ return 0;
+ }
+ ++p;
+ value = "";
+
+ while ( p && *p && *p != '>' )
+ {
+ value += *p;
+ ++p;
+ }
+
+ if ( !p )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_UNKNOWN, 0, 0, encoding );
+ }
+ if ( p && *p == '>' )
+ return p+1;
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlComment::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ (*tag) += (char) c;
+
+ if ( c == '>'
+ && tag->at( tag->length() - 2 ) == '-'
+ && tag->at( tag->length() - 3 ) == '-' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+
+const char* TiXmlComment::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ TiXmlDocument* document = GetDocument();
+ value = "";
+
+ p = SkipWhiteSpace( p, encoding );
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ const char* startTag = "<!--";
+ const char* endTag = "-->";
+
+ if ( !StringEqual( p, startTag, false, encoding ) )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_COMMENT, p, data, encoding );
+ return 0;
+ }
+ p += strlen( startTag );
+
+ // [ 1475201 ] TinyXML parses entities in comments
+ // Oops - ReadText doesn't work, because we don't want to parse the entities.
+ // p = ReadText( p, &value, false, endTag, false, encoding );
+ //
+ // from the XML spec:
+ /*
+ [Definition: Comments may appear anywhere in a document outside other markup; in addition,
+ they may appear within the document type declaration at places allowed by the grammar.
+ They are not part of the document's character data; an XML processor MAY, but need not,
+ make it possible for an application to retrieve the text of comments. For compatibility,
+ the string "--" (double-hyphen) MUST NOT occur within comments.] Parameter entity
+ references MUST NOT be recognized within comments.
+
+ An example of a comment:
+
+ <!-- declarations for <head> & <body> -->
+ */
+
+ value = "";
+ // Keep all the white space.
+ while ( p && *p && !StringEqual( p, endTag, false, encoding ) )
+ {
+ value.append( p, 1 );
+ ++p;
+ }
+ if ( p && *p )
+ p += strlen( endTag );
+
+ return p;
+}
+
+
+const char* TiXmlAttribute::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p ) return 0;
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+ // Read the name, the '=' and the value.
+ const char* pErr = p;
+ p = ReadName( p, &name, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, pErr, data, encoding );
+ return 0;
+ }
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p || *p != '=' )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+
+ ++p; // skip '='
+ p = SkipWhiteSpace( p, encoding );
+ if ( !p || !*p )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+
+ const char* end;
+ const char SINGLE_QUOTE = '\'';
+ const char DOUBLE_QUOTE = '\"';
+
+ if ( *p == SINGLE_QUOTE )
+ {
+ ++p;
+ end = "\'"; // single quote in string
+ p = ReadText( p, &value, false, end, false, encoding );
+ }
+ else if ( *p == DOUBLE_QUOTE )
+ {
+ ++p;
+ end = "\""; // double quote in string
+ p = ReadText( p, &value, false, end, false, encoding );
+ }
+ else
+ {
+ // All attribute values should be in single or double quotes.
+ // But this is such a common error that the parser will try
+ // its best, even without them.
+ value = "";
+ while ( p && *p // existence
+ && !IsWhiteSpace( *p ) // whitespace
+ && *p != '/' && *p != '>' ) // tag end
+ {
+ if ( *p == SINGLE_QUOTE || *p == DOUBLE_QUOTE ) {
+ // [ 1451649 ] Attribute values with trailing quotes not handled correctly
+ // We did not have an opening quote but seem to have a
+ // closing one. Give up and throw an error.
+ if ( document ) document->SetError( TIXML_ERROR_READING_ATTRIBUTES, p, data, encoding );
+ return 0;
+ }
+ value += *p;
+ ++p;
+ }
+ }
+ return p;
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlText::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->peek();
+ if ( !cdata && (c == '<' ) )
+ {
+ return;
+ }
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+
+ (*tag) += (char) c;
+ in->get(); // "commits" the peek made above
+
+ if ( cdata && c == '>' && tag->size() >= 3 ) {
+ size_t len = tag->size();
+ if ( (*tag)[len-2] == ']' && (*tag)[len-3] == ']' ) {
+ // terminator of cdata.
+ return;
+ }
+ }
+ }
+}
+#endif
+
+const char* TiXmlText::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding encoding )
+{
+ value = "";
+ TiXmlDocument* document = GetDocument();
+
+ if ( data )
+ {
+ data->Stamp( p, encoding );
+ location = data->Cursor();
+ }
+
+ const char* const startTag = "<![CDATA[";
+ const char* const endTag = "]]>";
+
+ if ( cdata || StringEqual( p, startTag, false, encoding ) )
+ {
+ cdata = true;
+
+ if ( !StringEqual( p, startTag, false, encoding ) )
+ {
+ if ( document )
+ document->SetError( TIXML_ERROR_PARSING_CDATA, p, data, encoding );
+ return 0;
+ }
+ p += strlen( startTag );
+
+ // Keep all the white space, ignore the encoding, etc.
+ while ( p && *p
+ && !StringEqual( p, endTag, false, encoding )
+ )
+ {
+ value += *p;
+ ++p;
+ }
+
+ TIXML_STRING dummy;
+ p = ReadText( p, &dummy, false, endTag, false, encoding );
+ return p;
+ }
+ else
+ {
+ bool ignoreWhite = true;
+
+ const char* end = "<";
+ p = ReadText( p, &value, ignoreWhite, end, false, encoding );
+ if ( p && *p )
+ return p-1; // don't truncate the '<'
+ return 0;
+ }
+}
+
+#ifdef TIXML_USE_STL
+void TiXmlDeclaration::StreamIn( std::istream * in, TIXML_STRING * tag )
+{
+ while ( in->good() )
+ {
+ int c = in->get();
+ if ( c <= 0 )
+ {
+ TiXmlDocument* document = GetDocument();
+ if ( document )
+ document->SetError( TIXML_ERROR_EMBEDDED_NULL, 0, 0, TIXML_ENCODING_UNKNOWN );
+ return;
+ }
+ (*tag) += (char) c;
+
+ if ( c == '>' )
+ {
+ // All is well.
+ return;
+ }
+ }
+}
+#endif
+
+const char* TiXmlDeclaration::Parse( const char* p, TiXmlParsingData* data, TiXmlEncoding _encoding )
+{
+ p = SkipWhiteSpace( p, _encoding );
+ // Find the beginning, find the end, and look for
+ // the stuff in-between.
+ TiXmlDocument* document = GetDocument();
+ if ( !p || !*p || !StringEqual( p, "<?xml", true, _encoding ) )
+ {
+ if ( document ) document->SetError( TIXML_ERROR_PARSING_DECLARATION, 0, 0, _encoding );
+ return 0;
+ }
+ if ( data )
+ {
+ data->Stamp( p, _encoding );
+ location = data->Cursor();
+ }
+ p += 5;
+
+ version = "";
+ encoding = "";
+ standalone = "";
+
+ while ( p && *p )
+ {
+ if ( *p == '>' )
+ {
+ ++p;
+ return p;
+ }
+
+ p = SkipWhiteSpace( p, _encoding );
+ if ( StringEqual( p, "version", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ version = attrib.Value();
+ }
+ else if ( StringEqual( p, "encoding", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ encoding = attrib.Value();
+ }
+ else if ( StringEqual( p, "standalone", true, _encoding ) )
+ {
+ TiXmlAttribute attrib;
+ p = attrib.Parse( p, data, _encoding );
+ standalone = attrib.Value();
+ }
+ else
+ {
+ // Read over whatever it is.
+ while( p && *p && *p != '>' && !IsWhiteSpace( *p ) )
+ ++p;
+ }
+ }
+ return 0;
+}
+
+bool TiXmlText::Blank() const
+{
+ for ( unsigned i=0; i<value.length(); i++ )
+ if ( !IsWhiteSpace( value[i] ) )
+ return false;
+ return true;
+}
+
diff --git a/test/verify_glyf.cc b/test/verify_glyf.cc
new file mode 100644
index 0000000..abfe1ab
--- /dev/null
+++ b/test/verify_glyf.cc
@@ -0,0 +1,59 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/truetype/glyph_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+// We spot check only glyph id 33.
+const int32_t GLYPH33_OFFSET = 0xAC8;
+const int32_t GLYPH33_LENGTH = 40;
+const int32_t GLYPH33_XMIN = 92;
+const int32_t GLYPH33_YMIN = 20;
+const int32_t GLYPH33_XMAX = 797;
+const int32_t GLYPH33_YMAX = 1235;
+
+// TODO(arthurhsu): Tuffy does not have composite glyphs. Need better testing.
+static bool VerifyGLYF(Table* table) {
+ GlyphTablePtr glyf_table = down_cast<GlyphTable*>(table);
+ if (glyf_table == NULL) {
+ return false;
+ }
+
+ GlyphPtr glyf;
+ glyf.Attach(glyf_table->GetGlyph(GLYPH33_OFFSET, GLYPH33_LENGTH));
+ if (glyf == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(glyf->XMin(), GLYPH33_XMIN);
+ EXPECT_EQ(glyf->YMin(), GLYPH33_YMIN);
+ EXPECT_EQ(glyf->XMax(), GLYPH33_XMAX);
+ EXPECT_EQ(glyf->YMax(), GLYPH33_YMAX);
+
+ return true;
+}
+
+bool VerifyGLYF(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyGLYF(original));
+ EXPECT_TRUE(VerifyGLYF(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_hhea.cc b/test/verify_hhea.cc
new file mode 100644
index 0000000..81ddb58
--- /dev/null
+++ b/test/verify_hhea.cc
@@ -0,0 +1,63 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/math/fixed1616.h"
+#include "sfntly/table/core/horizontal_header_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t HHEA_ASCENDER = 2023;
+const int32_t HHEA_DESCENDER = -648;
+const int32_t HHEA_LINE_GAP = 93;
+const int32_t HHEA_ADVANCE_WIDTH_MAX = 2753;
+const int32_t HHEA_MIN_LSB = -968;
+const int32_t HHEA_MIN_RSB = -411;
+const int32_t HHEA_X_MAX_EXTENT = 2628;
+const int32_t HHEA_METRIC_DATA_FORMAT = 0;
+const int32_t HHEA_NUM_METRICS = 1499;
+
+static bool VerifyHHEA(Table* table) {
+ HorizontalHeaderTablePtr hhea = down_cast<HorizontalHeaderTable*>(table);
+ if (hhea == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(hhea->TableVersion(), Fixed1616::Fixed(1, 0));
+ EXPECT_EQ(hhea->Ascender(), HHEA_ASCENDER);
+ EXPECT_EQ(hhea->Descender(), HHEA_DESCENDER);
+ EXPECT_EQ(hhea->AdvanceWidthMax(), HHEA_ADVANCE_WIDTH_MAX);
+ EXPECT_EQ(hhea->MinLeftSideBearing(), HHEA_MIN_LSB);
+ EXPECT_EQ(hhea->MinRightSideBearing(), HHEA_MIN_RSB);
+ EXPECT_EQ(hhea->XMaxExtent(), HHEA_X_MAX_EXTENT);
+ // TODO(arthurhsu): CaretSlopeRise() not tested
+ // TODO(arthurhsu): CaretSlopeRun() not tested
+ // TODO(arthurhsu): CaretOffset() not tested
+ EXPECT_EQ(hhea->MetricDataFormat(), HHEA_METRIC_DATA_FORMAT);
+ EXPECT_EQ(hhea->NumberOfHMetrics(), HHEA_NUM_METRICS);
+
+ return true;
+}
+
+bool VerifyHHEA(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyHHEA(original));
+ EXPECT_TRUE(VerifyHHEA(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_hmtx.cc b/test/verify_hmtx.cc
new file mode 100644
index 0000000..d37bba9
--- /dev/null
+++ b/test/verify_hmtx.cc
@@ -0,0 +1,76 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/core/horizontal_metrics_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t HMTX_ENTRIES_COUNT = 1499;
+const int32_t HMTX_LSB_COUNT = 3;
+
+struct HmtxEntry {
+ int32_t advance_width_;
+ int32_t lsb_;
+
+ HmtxEntry(int32_t advance_width, int32_t lsb)
+ : advance_width_(advance_width), lsb_(lsb) {}
+};
+
+const HmtxEntry HMTX_ENTRIES[] = {
+ HmtxEntry(748, 68), // 0
+ HmtxEntry(0, 0), // 1
+ HmtxEntry(682, 0), // 2
+ HmtxEntry(616, 0), // 3
+ HmtxEntry(421, 103), // 4
+ HmtxEntry(690, 129), // 5
+ HmtxEntry(1589, 129), // 6
+ HmtxEntry(1017, 25), // 7
+ HmtxEntry(1402, 104), // 8
+ HmtxEntry(1241, 100), // 9
+};
+const int32_t NUM_HMTX_ENTRIES = 10;
+
+static bool VerifyHMTX(Table* table) {
+ HorizontalMetricsTablePtr hmtx = down_cast<HorizontalMetricsTable*>(table);
+ if (hmtx == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(hmtx->NumberOfHMetrics(), HMTX_ENTRIES_COUNT);
+ EXPECT_EQ(hmtx->NumberOfLSBs(), HMTX_LSB_COUNT);
+
+ for (int32_t i = 0; i < NUM_HMTX_ENTRIES; ++i) {
+ EXPECT_EQ(hmtx->AdvanceWidth(i), HMTX_ENTRIES[i].advance_width_);
+ EXPECT_EQ(hmtx->LeftSideBearing(i), HMTX_ENTRIES[i].lsb_);
+ }
+
+ // No such element case.
+ EXPECT_EQ(hmtx->AdvanceWidth(HMTX_ENTRIES_COUNT),
+ HMTX_ENTRIES[0].advance_width_);
+ EXPECT_EQ(hmtx->LeftSideBearing(HMTX_ENTRIES_COUNT), HMTX_ENTRIES[0].lsb_);
+ return true;
+}
+
+bool VerifyHMTX(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyHMTX(original));
+ EXPECT_TRUE(VerifyHMTX(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_loca.cc b/test/verify_loca.cc
new file mode 100644
index 0000000..4a32928
--- /dev/null
+++ b/test/verify_loca.cc
@@ -0,0 +1,62 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/truetype/loca_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t LOCA_NUM_LOCAS = 1503;
+const int32_t LOCAS[] = {
+ 0x00000, // 0
+ 0x00058, // 1
+ 0x00058, // 2
+ 0x00058, // 3
+ 0x00058, // 4
+ 0x000B8, // 5
+ 0x00138, // 6
+ 0x001A4, // 7
+ 0x0025C, // 8
+ 0x00328, // 9
+ 0x003B8, // 10
+};
+const int32_t NUM_TEST_LOCAS = 11;
+
+static bool VerifyLOCA(Table* table) {
+ LocaTablePtr loca = down_cast<LocaTable*>(table);
+ if (loca == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(loca->NumLocas(), LOCA_NUM_LOCAS);
+ EXPECT_EQ(loca->num_glyphs(), LOCA_NUM_LOCAS - 1);
+
+ for (int32_t i = 0; i < NUM_TEST_LOCAS - 1; ++i) {
+ EXPECT_EQ(loca->GlyphOffset(i), LOCAS[i]);
+ EXPECT_EQ(loca->GlyphLength(i), LOCAS[i + 1] - LOCAS[i]);
+ }
+ return true;
+}
+
+bool VerifyLOCA(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyLOCA(original));
+ EXPECT_TRUE(VerifyLOCA(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_maxp.cc b/test/verify_maxp.cc
new file mode 100644
index 0000000..dcd776e
--- /dev/null
+++ b/test/verify_maxp.cc
@@ -0,0 +1,72 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/math/fixed1616.h"
+#include "sfntly/table/core/maximum_profile_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t MAXP_NUM_GLYPHS = 1502;
+const int32_t MAXP_MAX_POINTS = 181;
+const int32_t MAXP_MAX_CONTOURS = 9;
+const int32_t MAXP_MAX_COMPOSITE_POINTS = 172;
+const int32_t MAXP_MAX_COMPOSITE_CONTOURS = 5;
+const int32_t MAXP_MAX_ZONES = 2;
+const int32_t MAXP_MAX_TWILIGHT_POINTS = 0;
+const int32_t MAXP_MAX_STORAGE = 1;
+const int32_t MAXP_MAX_FUNCTION_DEFS = 1;
+const int32_t MAXP_MAX_INSTR_DEFS = 0;
+const int32_t MAXP_MAX_STACK_ELEMENTS = 64;
+const int32_t MAXP_MAX_INSTR_SIZE = 46;
+const int32_t MAXP_MAX_COMPONENT_ELEMENTS = 4;
+const int32_t MAXP_MAX_COMPONENT_DEPTH = 3;
+
+static bool VerifyMAXP(Table* table) {
+ MaximumProfileTablePtr maxp = down_cast<MaximumProfileTable*>(table);
+ if (maxp == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(maxp->TableVersion(), Fixed1616::Fixed(1, 0));
+ EXPECT_EQ(maxp->NumGlyphs(), MAXP_NUM_GLYPHS);
+ EXPECT_EQ(maxp->MaxPoints(), MAXP_MAX_POINTS);
+ EXPECT_EQ(maxp->MaxContours(), MAXP_MAX_CONTOURS);
+ EXPECT_EQ(maxp->MaxCompositePoints(), MAXP_MAX_COMPOSITE_POINTS);
+ EXPECT_EQ(maxp->MaxCompositeContours(), MAXP_MAX_COMPOSITE_CONTOURS);
+ EXPECT_EQ(maxp->MaxZones(), MAXP_MAX_ZONES);
+ EXPECT_EQ(maxp->MaxTwilightPoints(), MAXP_MAX_TWILIGHT_POINTS);
+ EXPECT_EQ(maxp->MaxStorage(), MAXP_MAX_STORAGE);
+ EXPECT_EQ(maxp->MaxFunctionDefs(), MAXP_MAX_FUNCTION_DEFS);
+ // TODO(arthurhsu): maxInstructionDefs observed in Microsoft TTF report.
+ // Check with stuartg and see if this is a miss.
+ EXPECT_EQ(maxp->MaxStackElements(), MAXP_MAX_STACK_ELEMENTS);
+ EXPECT_EQ(maxp->MaxSizeOfInstructions(), MAXP_MAX_INSTR_SIZE);
+ EXPECT_EQ(maxp->MaxComponentElements(), MAXP_MAX_COMPONENT_ELEMENTS);
+ EXPECT_EQ(maxp->MaxComponentDepth(), MAXP_MAX_COMPONENT_DEPTH);
+
+ return true;
+}
+
+bool VerifyMAXP(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyMAXP(original));
+ EXPECT_TRUE(VerifyMAXP(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_name.cc b/test/verify_name.cc
new file mode 100644
index 0000000..e1101c0
--- /dev/null
+++ b/test/verify_name.cc
@@ -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.
+ */
+
+#include "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/core/name_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t NAME_FORMAT = 0;
+const int32_t NAME_COUNT = 75;
+const NameTable::NameEntryId NAME_IDS[] = {
+ NameTable::NameEntryId(1, 0, 0, 0), // 0
+ NameTable::NameEntryId(1, 0, 0, 1), // 1
+ NameTable::NameEntryId(1, 0, 0, 2), // 2
+ NameTable::NameEntryId(1, 0, 0, 3), // 3
+ NameTable::NameEntryId(1, 0, 0, 4), // 4
+ NameTable::NameEntryId(1, 0, 0, 5), // 5
+ NameTable::NameEntryId(1, 0, 0, 6), // 6
+ NameTable::NameEntryId(1, 0, 0, 9), // 7
+ NameTable::NameEntryId(1, 0, 0, 11), // 8
+ NameTable::NameEntryId(1, 0, 0, 12), // 9
+};
+const int32_t NAME_IDS_TEST = 10;
+
+static bool VerifyNAME(Table* table) {
+ // TODO(arthurhsu): Better testing can be done here. Right now we just
+ // iterate through the entries and get entry ids.
+ NameTablePtr name = down_cast<NameTable*>(table);
+ if (name == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(name->Format(), NAME_FORMAT);
+ EXPECT_EQ(name->NameCount(), NAME_COUNT);
+ fprintf(stderr, "checking name entry: ");
+ for (int32_t i = 0; i < NAME_IDS_TEST; ++i) {
+ fprintf(stderr, "%d ", i);
+ EXPECT_EQ(name->PlatformId(i), NAME_IDS[i].platform_id());
+ EXPECT_EQ(name->EncodingId(i), NAME_IDS[i].encoding_id());
+ EXPECT_EQ(name->LanguageId(i), NAME_IDS[i].language_id());
+ EXPECT_EQ(name->NameId(i), NAME_IDS[i].name_id());
+ }
+ fprintf(stderr, "\n");
+ return true;
+}
+
+bool VerifyNAME(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyNAME(original));
+ EXPECT_TRUE(VerifyNAME(target));
+ return true;
+}
+
+} // namespace sfntly
diff --git a/test/verify_os2.cc b/test/verify_os2.cc
new file mode 100644
index 0000000..4897e87
--- /dev/null
+++ b/test/verify_os2.cc
@@ -0,0 +1,125 @@
+/*
+ * 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 "gtest/gtest.h"
+#include "sfntly/font.h"
+#include "sfntly/table/core/os2_table.h"
+#include "test/serialization_test.h"
+
+namespace sfntly {
+
+const int32_t OS2_VERSION = 1;
+const int32_t OS2_XAVG_CHAR_WIDTH = 863;
+const int32_t OS2_US_WEIGHT_CLASS = 500;
+const int32_t OS2_US_WIDTH_CLASS = 5;
+const int32_t OS2_FS_TYPE = 0;
+const int32_t OS2_YSUBS_XSIZE = 0;
+const int32_t OS2_YSUBS_YSIZE = 2;
+const int32_t OS2_YSUBS_XOFFSET = -16560;
+const int32_t OS2_YSUBS_YOFFSET = 0;
+const int32_t OS2_YSUPS_XSIZE = -25944;
+const int32_t OS2_YSUPS_YSIZE = -27176;
+const int32_t OS2_YSUPS_XOFFSET = -16376;
+const int32_t OS2_YSUPS_YOFFSET = 1;
+const int32_t OS2_YSTRIKEOUT_SIZE = 12312;
+const int32_t OS2_YSTRIKEOUT_POS = -16224;
+const int32_t OS2_SFAMILY_CLASS = 0;
+const byte_t OS2_PANOSE[] = { 2, 11, 6, 3, 6, 1, 0, 0, 0, 0 };
+const int64_t OS2_UL_UNICODE_RANGE1 = 0xE00002FFL;
+const int64_t OS2_UL_UNICODE_RANGE2 = 0x520020FBL;
+const int64_t OS2_UL_UNICODE_RANGE3 = 0L;
+const int64_t OS2_UL_UNICODE_RANGE4 = 0L;
+const byte_t OS2_ACH_VEND_ID[] = { 'P', 'f', 'E', 'd' };
+const int32_t OS2_FS_SELECTION = 0x0040;
+const int32_t OS2_US_FIRST_CHAR_IDX = 0x0020;
+const int32_t OS2_US_LAST_CHAR_IDX = 0xFFFF;
+const int32_t OS2_STYPO_ASCENDER = 1597;
+const int32_t OS2_STYPO_DESCENDER = -451;
+const int32_t OS2_STYPO_LINE_GAP = 0;
+const int32_t OS2_US_WIN_ASCENT = 2023;
+const int32_t OS2_US_WIN_DESCENT = 648;
+const int64_t OS2_UL_CODE_PAGE_RANGE1 = 0x2000019FL;
+const int64_t OS2_UL_CODE_PAGE_RANGE2 = 0x00000000L;
+
+static bool VerifyOS_2(Table* table) {
+ OS2TablePtr os2 = down_cast<OS2Table*>(table);
+ if (os2 == NULL) {
+ return false;
+ }
+
+ EXPECT_EQ(os2->TableVersion(), OS2_VERSION);
+ EXPECT_EQ(os2->XAvgCharWidth(), OS2_XAVG_CHAR_WIDTH);
+ EXPECT_EQ(os2->UsWeightClass(), OS2_US_WEIGHT_CLASS);
+ EXPECT_EQ(os2->UsWidthClass(), OS2_US_WIDTH_CLASS);
+ EXPECT_EQ(os2->FsType(), OS2_FS_TYPE);
+ EXPECT_EQ(os2->YSubscriptXSize(), OS2_YSUBS_XSIZE);
+ EXPECT_EQ(os2->YSubscriptYSize(), OS2_YSUBS_YSIZE);
+ EXPECT_EQ(os2->YSubscriptXOffset(), OS2_YSUBS_XOFFSET);
+ EXPECT_EQ(os2->YSubscriptYOffset(), OS2_YSUBS_YOFFSET);
+ EXPECT_EQ(os2->YSuperscriptXSize(), OS2_YSUPS_XSIZE);
+ EXPECT_EQ(os2->YSuperscriptYSize(), OS2_YSUPS_YSIZE);
+ EXPECT_EQ(os2->YSuperscriptXOffset(), OS2_YSUPS_XOFFSET);
+ EXPECT_EQ(os2->YSuperscriptYOffset(), OS2_YSUPS_YOFFSET);
+ EXPECT_EQ(os2->YStrikeoutSize(), OS2_YSTRIKEOUT_SIZE);
+ EXPECT_EQ(os2->YStrikeoutPosition(), OS2_YSTRIKEOUT_POS);
+ EXPECT_EQ(os2->SFamilyClass(), OS2_SFAMILY_CLASS);
+
+ ByteVector panose;
+ os2->Panose(&panose);
+ EXPECT_EQ(panose.size(), sizeof(OS2_PANOSE));
+ for (size_t i = 0; i < panose.size(); ++i) {
+ EXPECT_EQ(panose[i], OS2_PANOSE[i]);
+ }
+
+ EXPECT_EQ(os2->UlUnicodeRange1(), OS2_UL_UNICODE_RANGE1);
+ EXPECT_EQ(os2->UlUnicodeRange2(), OS2_UL_UNICODE_RANGE2);
+ EXPECT_EQ(os2->UlUnicodeRange3(), OS2_UL_UNICODE_RANGE3);
+ EXPECT_EQ(os2->UlUnicodeRange4(), OS2_UL_UNICODE_RANGE4);
+
+ ByteVector vend_id;
+ os2->AchVendId(&vend_id);
+ EXPECT_EQ(vend_id.size(), sizeof(OS2_ACH_VEND_ID));
+ for (size_t i = 0; i < vend_id.size(); ++i) {
+ EXPECT_EQ(vend_id[i], OS2_ACH_VEND_ID[i]);
+ }
+
+ EXPECT_EQ(os2->FsSelection(), OS2_FS_SELECTION);
+ EXPECT_EQ(os2->UsFirstCharIndex(), OS2_US_FIRST_CHAR_IDX);
+ EXPECT_EQ(os2->UsLastCharIndex(), OS2_US_LAST_CHAR_IDX);
+ EXPECT_EQ(os2->STypoAscender(), OS2_STYPO_ASCENDER);
+ EXPECT_EQ(os2->STypoDescender(), OS2_STYPO_DESCENDER);
+ EXPECT_EQ(os2->STypoLineGap(), OS2_STYPO_LINE_GAP);
+ EXPECT_EQ(os2->UsWinAscent(), OS2_US_WIN_ASCENT);
+ EXPECT_EQ(os2->UsWinDescent(), OS2_US_WIN_DESCENT);
+ EXPECT_EQ(os2->UlCodePageRange1(), OS2_UL_CODE_PAGE_RANGE1);
+ EXPECT_EQ(os2->UlCodePageRange2(), OS2_UL_CODE_PAGE_RANGE2);
+
+ // TODO(arthurhsu): SxHeight() not tested
+ // TODO(arthurhsu): SCapHeight() not tested
+ // TODO(arthurhsu): UsDefaultChar() not tested
+ // TODO(arthurhsu): UsBreakChar() not tested
+ // TODO(arthurhsu): UsMaxContext() not tested
+
+ return true;
+}
+
+bool VerifyOS_2(Table* original, Table* target) {
+ EXPECT_TRUE(VerifyOS_2(original));
+ EXPECT_TRUE(VerifyOS_2(target));
+ return true;
+}
+
+} // namespace sfntly