summaryrefslogtreecommitdiff
path: root/fonts/utilities/woff2ot/woff2ot.h
diff options
context:
space:
mode:
Diffstat (limited to 'fonts/utilities/woff2ot/woff2ot.h')
-rw-r--r--fonts/utilities/woff2ot/woff2ot.h440
1 files changed, 440 insertions, 0 deletions
diff --git a/fonts/utilities/woff2ot/woff2ot.h b/fonts/utilities/woff2ot/woff2ot.h
new file mode 100644
index 0000000000..edbfe66809
--- /dev/null
+++ b/fonts/utilities/woff2ot/woff2ot.h
@@ -0,0 +1,440 @@
+#pragma once
+
+/*
+ Woff2OT: WOFF to OpenType (PostScript/TrueType) converter
+ Copyright (c) 2020 by Peter Frane Jr.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+
+ The author may be contacted via the e-mail address pfranejr@hotmail.com
+*/
+
+#include <stdexcept>
+#include <string>
+#include <cstdio>
+#include <vector>
+#include <stdint.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+
+using namespace std;
+
+#pragma comment(lib, "zdll.lib")
+
+#define COPYRIGHT_NOTICE "Woff2OT v. 1.0\nCopyright (c) 2000 P.D. Frane Jr.\n"
+
+#ifdef _MSC_VER
+#define bswap16(x) _byteswap_ushort(x)
+#define bswap32(x) _byteswap_ulong(x)
+#else
+#define bswap16(x) __builtin_bswap16(x)
+#define bswap32(x) __builtin_bswap32(x)
+#endif
+
+const uint32_t OPENTYPE_TRUETYPE = 0x00010000;
+const uint32_t OPENTYPE_TRUETYPE_MAC = 0x74727565;
+const uint32_t OPENTYPE_CFF = 0x4F54544F;
+
+typedef uint32_t offset32;
+typedef unsigned char byte_t;
+
+struct WOFF_header
+{
+ uint32_t m_signature{ 0 }; //0x774F4646 'wOFF'
+ uint32_t m_flavor{ 0 }; // The "sfnt version" of the input font.
+ uint32_t m_length{ 0 }; // Total size of the WOFF file.
+ uint16_t m_num_tables{ 0 }; // Number of entries in directory of font tables.
+ uint16_t m_reserved{ 0 }; // Reserved; set to zero.
+ uint32_t m_total_sfnt_size{ 0 }; // Total size needed for the uncompressed font data, including the sfnt header, directory, and font tables(including padding).
+ uint16_t m_major_version{ 1 }; // Major version of the WOFF file.
+ uint16_t m_minor_version{ 0 }; // Minor version of the WOFF file.
+ uint32_t m_meta_offset{ 0 }; // Offset to metadata block, from beginning of WOFF file.
+ uint32_t m_meta_length{ 0 }; // Length of compressed metadata block.
+ uint32_t m_meta_orig_length{ 0 }; // Uncompressed size of metadata block.
+ uint32_t m_priv_offset{ 0 }; // Offset to private data block, from beginning of WOFF file.
+ uint32_t m_priv_length{ 0 }; // Length of private data block.
+};
+
+struct table_directory_entry
+{
+ uint32_t tag{ 0 };
+ uint32_t offset{ 0 };
+ uint32_t comp_length{ 0 };// Length of the compressed data, excluding padding.
+ uint32_t orig_length{ 0 };// Length of the uncompressed table, excluding padding.
+ uint32_t orig_checksum{ 0 };
+
+};
+
+struct offset_table
+{
+ uint32_t sfntVersion;
+ uint16_t numTables;
+ uint16_t searchRange;
+ uint16_t entrySelector;
+ uint16_t rangeShift;
+};
+
+struct table_record
+{
+ uint32_t table_tag{ 0 };
+ uint32_t checksum{ 0 };
+ offset32 offset{ 0 };
+ uint32_t length{ 0 };
+};
+
+class Woff2OT
+{
+ FILE* m_input_file{ nullptr };
+ FILE* m_output_file{ nullptr };
+ string m_error;
+ void clear()
+ {
+ if (m_input_file)
+ {
+ fclose(m_input_file);
+ }
+ if (m_output_file)
+ {
+ fclose(m_output_file);
+ }
+
+ m_input_file = m_output_file = nullptr;
+ }
+ void load_input_file(const char* woff_file)
+ {
+ m_input_file = fopen(woff_file, "rb");
+
+ if (!m_input_file)
+ {
+ throw runtime_error("Unable to load the WOFF font file");
+ }
+ }
+ void create_output_file(const char* ot_file)
+ {
+ m_output_file = fopen(ot_file, "wb");
+
+ if (!m_output_file)
+ {
+ throw runtime_error("Unable to create the OpenType file");
+ }
+ }
+ uint32_t compute_table_sizes(table_directory_entry* tbl_directory_entry_data, uint32_t num_tables)
+ {
+ uint32_t total_size = 0;
+
+ for (uint16_t i = 0; i < num_tables; ++i)
+ {
+ uint32_t size = bswap32(tbl_directory_entry_data[i].orig_length);
+
+ total_size += (size + 3) & 0xFFFFFFFC;
+ }
+
+ return total_size;
+ }
+ void write_temp_table_record(uint16_t num_tables)
+ {
+ table_record record;
+
+ fwrite(&record, 1, num_tables * sizeof(record), m_output_file);
+ }
+ uint32_t get_max_buffer_size(table_directory_entry* tbl_directory_entry_data, uint16_t num_tables)
+ {
+ uint32_t size = 0;
+
+ for (uint16_t i = 0; i < num_tables; ++i)
+ {
+ uint32_t length = bswap32(tbl_directory_entry_data[i].orig_length);
+ uint32_t offset = bswap32(tbl_directory_entry_data[i].offset);
+ uint32_t comp_length = bswap32(tbl_directory_entry_data[i].comp_length);
+
+ tbl_directory_entry_data[i].orig_length = length;
+ tbl_directory_entry_data[i].offset = offset;
+ tbl_directory_entry_data[i].comp_length = comp_length;
+
+ size = length > size ? length : size;
+ }
+
+ return size;
+ }
+ void pad_table(byte_t* buffer, uint32_t length, uint32_t padded_length)
+ {
+ for (uint32_t i = length; i < padded_length; ++i)
+ {
+ buffer[i] = 0;
+ }
+ }
+ static int compare_offset(const void* a, const void* b)
+ {
+ table_directory_entry* i = (table_directory_entry*)a;
+ table_directory_entry* j = (table_directory_entry*)b;
+
+ return i->offset - j->offset;
+ }
+ /*
+ void dump_tables(table_directory_entry* tbl_directory_entry_data, uint32_t num_tables)
+ {
+ for (uint16_t i = 0; i < num_tables; ++i)
+ {
+ printf("%.*s %u\n", 4, (char*)&tbl_directory_entry_data[i].tag, tbl_directory_entry_data[i].offset);
+ }
+ }
+ */
+ void write_tables(table_directory_entry* tbl_directory_entry_data, uint32_t num_tables)
+ {
+ uint32_t buffer_size = get_max_buffer_size(tbl_directory_entry_data, num_tables) + 4;
+ vector<Bytef> buf1(buffer_size);
+ vector<Bytef> buf2(buffer_size);
+ byte_t* original_data = buf1.data();
+ byte_t* compressed_data = buf2.data();
+
+ qsort(tbl_directory_entry_data, num_tables, sizeof(*tbl_directory_entry_data), compare_offset);
+
+ for (uint16_t i = 0; i < num_tables; ++i)
+ {
+ table_directory_entry& entry = tbl_directory_entry_data[i];
+ uint32_t offset = entry.offset;
+ uLongf orig_length = entry.orig_length;
+ uLongf compressed_length = entry.comp_length;
+ int ret;
+
+ fseek(m_input_file, offset, SEEK_SET);
+
+ entry.offset = bswap32(ftell(m_output_file));
+
+ if (compressed_length < orig_length)
+ {
+ uLongf dest_len = buffer_size;
+ uLongf new_dest_len;
+
+ fread(compressed_data, 1, compressed_length, m_input_file);
+
+ ret = uncompress(original_data, &dest_len, compressed_data, compressed_length);
+
+ if (ret != Z_OK)
+ {
+ throw runtime_error("Error decompressing the table");
+ }
+ else if (dest_len != orig_length)
+ {
+ throw runtime_error("Decompressed length does not match the original length");
+ }
+
+ new_dest_len = (dest_len + 3) & 0xFFFFFFFC;
+
+ pad_table(original_data, dest_len, new_dest_len);
+
+ fwrite(original_data, 1, new_dest_len, m_output_file);
+
+ entry.orig_length = bswap32(dest_len);
+ }
+ else
+ {
+ uLongf new_length = (orig_length + 3) & 0xFFFFFFFC;
+
+ fread(original_data, 1, orig_length, m_input_file);
+
+ pad_table(original_data, orig_length, new_length);
+
+ fwrite(original_data, 1, new_length, m_output_file);
+
+ entry.orig_length = bswap32(orig_length);
+ }
+ }
+ }
+ static int compare_tag(const void* a, const void* b)
+ {
+ table_directory_entry* i = (table_directory_entry*)a;
+ table_directory_entry* j = (table_directory_entry*)b;
+
+ return strncmp(((char*)&i->tag), ((char*)&j->tag), 4);
+ }
+ void rewrite_table_record(long offset, table_directory_entry* tbl_directory_entry_data, uint32_t num_tables)
+ {
+ table_record record;
+
+ qsort(tbl_directory_entry_data, num_tables, sizeof(*tbl_directory_entry_data), compare_tag);
+
+ fseek(m_output_file, offset, SEEK_SET);
+
+ for (uint16_t i = 0; i < num_tables; ++i)
+ {
+ table_directory_entry& entry = tbl_directory_entry_data[i];
+
+ record.checksum = entry.orig_checksum;
+ record.length = entry.orig_length;
+ record.offset = entry.offset;
+ record.table_tag = entry.tag;
+
+ fwrite(&record, 1, sizeof(record), m_output_file);
+ }
+ }
+ uint16_t max_power_of_2(uint16_t num_tables)
+ {
+ for (int i = num_tables; i >= 1; --i)
+ {
+ if ((i & (i - 1)) == 0)
+ return i;
+ }
+ return 0;
+ }
+ void write_ot_header(uint32_t flavor, uint16_t num_tables)
+ {
+ uint16_t mp2 = max_power_of_2(num_tables);
+ offset_table header;
+
+ header.sfntVersion = flavor;
+ header.numTables = bswap16(num_tables);
+ header.searchRange = bswap16(mp2 * 16);
+ header.entrySelector = bswap16((uint16_t)log2(mp2));
+ header.rangeShift = bswap16(num_tables * 16 - (mp2 * 16));
+
+ fwrite(&header, 1, sizeof(header), m_output_file);
+ }
+ void parse_input_file()
+ {
+ WOFF_header hdr;
+
+ if (fread(&hdr, 1, sizeof(hdr), m_input_file) != sizeof(hdr))
+ {
+ throw runtime_error("Error reading the input file");
+ }
+ else
+ {
+ uint16_t num_tables = bswap16(hdr.m_num_tables);
+ long offset;
+
+ if (num_tables > 0)
+ {
+ vector<table_directory_entry> tbl_directory_entry(num_tables);
+ table_directory_entry* tbl_directory_entry_data = tbl_directory_entry.data();
+ uint32_t total_sfnt_size = bswap32(hdr.m_total_sfnt_size);
+ uint32_t size_to_read = num_tables * sizeof(*tbl_directory_entry_data);
+ uint32_t total_table_size;
+ uint32_t computed_sfnt_size;
+
+ // write the OT header
+
+ write_ot_header(hdr.m_flavor, num_tables);
+
+ offset = ftell(m_output_file);
+
+ write_temp_table_record(num_tables);
+
+ if (fread(tbl_directory_entry_data, 1, size_to_read, m_input_file) != size_to_read)
+ {
+ throw runtime_error("Error reading the table directory entries");
+ }
+
+ total_table_size = compute_table_sizes(tbl_directory_entry_data, num_tables);
+
+ computed_sfnt_size = sizeof(offset_table) + (num_tables * sizeof(table_record)) + total_table_size;
+
+ if (total_sfnt_size != computed_sfnt_size)
+ {
+ throw runtime_error("Invalid 'totalSfntSize' size");
+ }
+
+ write_tables(tbl_directory_entry_data, num_tables);
+
+ rewrite_table_record(offset, tbl_directory_entry_data, num_tables);
+ }
+ else
+ {
+ throw runtime_error("No tables found");
+ }
+ }
+ }
+
+public:
+ Woff2OT() : m_error()
+ {}
+ ~Woff2OT()
+ {}
+ string error() const
+ {
+ return m_error;
+ }
+ uint32_t get_font_type(const char* woff_file)
+ {
+ uint32_t result = 0;
+
+ try
+ {
+ WOFF_header hdr;
+
+ load_input_file(woff_file);
+
+ if (fread(&hdr, 1, sizeof(hdr), m_input_file) != sizeof(hdr))
+ {
+ throw runtime_error("Error reading the input file");
+ }
+
+ result = bswap32(hdr.m_flavor);
+ }
+ catch (const exception& ex)
+ {
+ m_error = ex.what();
+ }
+
+ clear();
+
+ return result;
+ }
+ uint32_t write_font_type(const char* woff_file)
+ {
+ uint32_t type;
+
+ type = get_font_type(woff_file);
+
+ switch (type)
+ {
+ case OPENTYPE_TRUETYPE:
+ puts("Font type is OpenType TrueType (.TTF)");
+ break;
+ case OPENTYPE_TRUETYPE_MAC:
+ puts("Font type is OpenType TrueType for Mac (.TTF)");
+ break;
+ case OPENTYPE_CFF:
+ puts("Font type is OpenType PostScript (.OTF)");
+ break;
+ default:
+ puts("Font type is unknown or font is not a WOFF font");
+ break;
+ }
+
+ return type;
+ }
+ bool convert(const char* woff_file, const char* ot_file)
+ {
+ bool result = true;
+ try
+ {
+ load_input_file(woff_file);
+ create_output_file(ot_file);
+ parse_input_file();
+ }
+ catch (const exception& ex)
+ {
+ m_error = ex.what();
+
+ result = false;
+ }
+
+ clear();
+
+ return result;
+ }
+}; \ No newline at end of file