Commit ca992267 authored by ksakamoto's avatar ksakamoto Committed by Commit bot

Add woff2 to src/third_party

This patch adds the woff2 library [1] which is used to decode WOFF 2.0
web font file format. It's going to replace the old version of WOFF 2.0
decoder included in third_party/ots.

[1] https://github.com/google/woff2 (revision 445f5419)

BUG=434596
TEST=trybots

Review URL: https://codereview.chromium.org/954163002

Cr-Commit-Position: refs/heads/master@{#322517}
parent 0c24b120
*.o
/woff2_compress
/woff2_decompress
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
source_set("woff2_dec") {
sources = [
"src/buffer.h",
"src/round.h",
"src/store_bytes.h",
"src/table_tags.cc",
"src/table_tags.h",
"src/woff2_common.h",
"src/woff2_dec.cc",
"src/woff2_dec.h",
]
configs -= [ "//build/config/compiler:chromium_code" ]
configs += [ "//build/config/compiler:no_chromium_code" ]
deps = [
"//third_party/brotli",
]
include_dirs = [
"src",
"//third_party/brotli/dec",
]
# TODO(ksakamoto): http://crbug.com/167187
if (is_win) {
cflags = [ "/wd4267" ] # Conversion from size_t to 'type'.
}
}
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
OS := $(shell uname)
IDIRS=-I./brotli/dec/ -I./brotli/enc/ -I./src
CXX = g++
LFLAGS =
GFLAGS=-no-canonical-prefixes -fno-omit-frame-pointer -m64
CXXFLAGS = -c $(IDIRS) -std=c++0x $(GFLAGS)
ifeq ($(OS), Darwin)
CXXFLAGS += -DOS_MACOSX
else
CXXFLAGS += -fno-tree-vrp
endif
SRCDIR = src
OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \
woff2_dec.o woff2_enc.o
BROTLI = brotli
ENCOBJ = $(BROTLI)/enc/*.o
DECOBJ = $(BROTLI)/dec/*.o
OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ))
EXECUTABLES=woff2_compress woff2_decompress
EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES))
all : $(OBJS) $(EXECUTABLES)
$(EXECUTABLES) : $(EXE_OBJS) deps
$(CXX) $(LFLAGS) $(OBJS) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o -o $@
deps :
make -C $(BROTLI)/dec
make -C $(BROTLI)/enc
clean :
rm -f $(OBJS) $(EXE_OBJS) $(EXECUTABLES)
make -C $(BROTLI)/dec clean
make -C $(BROTLI)/enc clean
ksakamoto@chromium.org
bashi@chromium.org
Name: woff2
URL: https://github.com/google/woff2
Version: 445f541996fe8376f3976d35692fd2b9a6eedf2d
License: Apache 2.0
License File: LICENSE
Security Critical: yes
Description:
The reference implementation of the WOFF 2.0 web font compression
format (http://www.w3.org/TR/WOFF2/).
Local Modifications:
- BUILD.gn: Added.
- woff2.gyp: Added.
- Cherry-picked https://github.com/google/woff2/commit/0c1ec8895bce7c7b2da66ebc6e9887ef2e0c1a55.
This is a README for the font compression reference code. There are several
compression related modules in this repository.
brotli/ contains reference code for the Brotli byte-level compression
algorithm. Note that it is licensed under an Apache 2 license.
src/ contains the C++ code for compressing and decompressing fonts.
# Build & Run
This document documents how to run the compression reference code. At this
writing, the code, while it is intended to produce a bytestream that can be
reconstructed into a working font, the reference decompression code is not
done, and the exact format of that bytestream is subject to change.
## Build
On a standard Unix-style environment:
```
git clone https://github.com/google/woff2.git
cd woff2
git submodule init
git submodule update
make clean all
```
## Run
```
woff2_compress myfont.ttf
woff2_decompress myfont.woff2
```
# References
http://www.w3.org/TR/WOFF2/
http://www.w3.org/Submission/MTX/
Also please refer to documents (currently Google Docs):
WOFF Ultra Condensed file format: proposals and discussion of wire format
issues (PDF is in docs/ directory)
WIFF Ultra Condensed: more discussion of results and compression techniques.
This tool was used to prepare the data in that document.
// Copyright 2013 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.
//
// The parts of ots.h & opentype-sanitiser.h that we need, taken from the
// https://code.google.com/p/ots/ project.
#ifndef WOFF2_BUFFER_H_
#define WOFF2_BUFFER_H_
#if defined(_WIN32)
#include <stdlib.h>
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
#define ntohl(x) _byteswap_ulong (x)
#define ntohs(x) _byteswap_ushort (x)
#define htonl(x) _byteswap_ulong (x)
#define htons(x) _byteswap_ushort (x)
#else
#include <arpa/inet.h>
#include <stdint.h>
#endif
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <limits>
namespace woff2 {
#if defined(_MSC_VER) || !defined(FONT_COMPRESSION_DEBUG)
#define FONT_COMPRESSION_FAILURE() false
#else
#define FONT_COMPRESSION_FAILURE() \
util::compression::font::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__)
inline bool Failure(const char *f, int l, const char *fn) {
fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn);
fflush(stderr);
return false;
}
#endif
// -----------------------------------------------------------------------------
// Buffer helper class
//
// This class perform some trival buffer operations while checking for
// out-of-bounds errors. As a family they return false if anything is amiss,
// updating the current offset otherwise.
// -----------------------------------------------------------------------------
class Buffer {
public:
Buffer(const uint8_t *buffer, size_t len)
: buffer_(buffer),
length_(len),
offset_(0) { }
bool Skip(size_t n_bytes) {
return Read(NULL, n_bytes);
}
bool Read(uint8_t *buffer, size_t n_bytes) {
if (n_bytes > 1024 * 1024 * 1024) {
return FONT_COMPRESSION_FAILURE();
}
if ((offset_ + n_bytes > length_) ||
(offset_ > length_ - n_bytes)) {
return FONT_COMPRESSION_FAILURE();
}
if (buffer) {
std::memcpy(buffer, buffer_ + offset_, n_bytes);
}
offset_ += n_bytes;
return true;
}
inline bool ReadU8(uint8_t *value) {
if (offset_ + 1 > length_) {
return FONT_COMPRESSION_FAILURE();
}
*value = buffer_[offset_];
++offset_;
return true;
}
bool ReadU16(uint16_t *value) {
if (offset_ + 2 > length_) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint16_t));
*value = ntohs(*value);
offset_ += 2;
return true;
}
bool ReadS16(int16_t *value) {
return ReadU16(reinterpret_cast<uint16_t*>(value));
}
bool ReadU24(uint32_t *value) {
if (offset_ + 3 > length_) {
return FONT_COMPRESSION_FAILURE();
}
*value = static_cast<uint32_t>(buffer_[offset_]) << 16 |
static_cast<uint32_t>(buffer_[offset_ + 1]) << 8 |
static_cast<uint32_t>(buffer_[offset_ + 2]);
offset_ += 3;
return true;
}
bool ReadU32(uint32_t *value) {
if (offset_ + 4 > length_) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
*value = ntohl(*value);
offset_ += 4;
return true;
}
bool ReadS32(int32_t *value) {
return ReadU32(reinterpret_cast<uint32_t*>(value));
}
bool ReadTag(uint32_t *value) {
if (offset_ + 4 > length_) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint32_t));
offset_ += 4;
return true;
}
bool ReadR64(uint64_t *value) {
if (offset_ + 8 > length_) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(value, buffer_ + offset_, sizeof(uint64_t));
offset_ += 8;
return true;
}
const uint8_t *buffer() const { return buffer_; }
size_t offset() const { return offset_; }
size_t length() const { return length_; }
void set_offset(size_t newoffset) { offset_ = newoffset; }
private:
const uint8_t * const buffer_;
const size_t length_;
size_t offset_;
};
} // namespace woff2
#endif // WOFF2_BUFFER_H_
// Copyright 2013 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 IO helpers
#ifndef WOFF2_FILE_H_
#define WOFF2_FILE_H_
#include <fstream>
#include <iterator>
namespace woff2 {
inline std::string GetFileContent(std::string filename) {
std::ifstream ifs(filename.c_str(), std::ios::binary);
return std::string(
std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>());
}
inline void SetFileContents(std::string filename, std::string content) {
std::ofstream ofs(filename.c_str(), std::ios::binary);
std::copy(content.begin(),
content.end(),
std::ostream_iterator<char>(ofs));
}
} // namespace woff2
#endif // WOFF2_FILE_H_
// Copyright 2013 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.
//
// Font management utilities
#include "./font.h"
#include <algorithm>
#include "./buffer.h"
#include "./port.h"
#include "./store_bytes.h"
#include "./table_tags.h"
namespace woff2 {
Font::Table* Font::FindTable(uint32_t tag) {
std::map<uint32_t, Font::Table>::iterator it = tables.find(tag);
return it == tables.end() ? 0 : &it->second;
}
const Font::Table* Font::FindTable(uint32_t tag) const {
std::map<uint32_t, Font::Table>::const_iterator it = tables.find(tag);
return it == tables.end() ? 0 : &it->second;
}
bool ReadFont(const uint8_t* data, size_t len, Font* font) {
Buffer file(data, len);
// We don't care about the search_range, entry_selector and range_shift
// fields, they will always be computed upon writing the font.
if (!file.ReadU32(&font->flavor) ||
!file.ReadU16(&font->num_tables) ||
!file.Skip(6)) {
return FONT_COMPRESSION_FAILURE();
}
std::map<uint32_t, uint32_t> intervals;
for (uint16_t i = 0; i < font->num_tables; ++i) {
Font::Table table;
if (!file.ReadU32(&table.tag) ||
!file.ReadU32(&table.checksum) ||
!file.ReadU32(&table.offset) ||
!file.ReadU32(&table.length)) {
return FONT_COMPRESSION_FAILURE();
}
if ((table.offset & 3) != 0 ||
table.length > len ||
len - table.length < table.offset) {
return FONT_COMPRESSION_FAILURE();
}
intervals[table.offset] = table.length;
table.data = data + table.offset;
if (font->tables.find(table.tag) != font->tables.end()) {
return FONT_COMPRESSION_FAILURE();
}
font->tables[table.tag] = table;
}
// Check that tables are non-overlapping.
uint32_t last_offset = 12UL + 16UL * font->num_tables;
for (const auto& i : intervals) {
if (i.first < last_offset || i.first + i.second < i.first) {
return FONT_COMPRESSION_FAILURE();
}
last_offset = i.first + i.second;
}
return true;
}
size_t FontFileSize(const Font& font) {
size_t max_offset = 12ULL + 16ULL * font.num_tables;
for (const auto& i : font.tables) {
const Font::Table& table = i.second;
size_t padding_size = (4 - (table.length & 3)) & 3;
size_t end_offset = (padding_size + table.offset) + table.length;
max_offset = std::max(max_offset, end_offset);
}
return max_offset;
}
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) {
if (dst_size < 12ULL + 16ULL * font.num_tables) {
return FONT_COMPRESSION_FAILURE();
}
size_t offset = 0;
StoreU32(font.flavor, &offset, dst);
Store16(font.num_tables, &offset, dst);
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
uint16_t range_shift = (font.num_tables << 4) - search_range;
Store16(search_range, &offset, dst);
Store16(max_pow2, &offset, dst);
Store16(range_shift, &offset, dst);
for (const auto& i : font.tables) {
const Font::Table& table = i.second;
StoreU32(table.tag, &offset, dst);
StoreU32(table.checksum, &offset, dst);
StoreU32(table.offset, &offset, dst);
StoreU32(table.length, &offset, dst);
if (table.offset + table.length < table.offset ||
dst_size < table.offset + table.length) {
return FONT_COMPRESSION_FAILURE();
}
memcpy(dst + table.offset, table.data, table.length);
size_t padding_size = (4 - (table.length & 3)) & 3;
if (table.offset + table.length + padding_size < padding_size ||
dst_size < table.offset + table.length + padding_size) {
return FONT_COMPRESSION_FAILURE();
}
memset(dst + table.offset + table.length, 0, padding_size);
}
return true;
}
int NumGlyphs(const Font& font) {
const Font::Table* head_table = font.FindTable(kHeadTableTag);
const Font::Table* loca_table = font.FindTable(kLocaTableTag);
if (head_table == NULL || loca_table == NULL || head_table->length < 52) {
return 0;
}
int index_fmt = IndexFormat(font);
int num_glyphs = (loca_table->length / (index_fmt == 0 ? 2 : 4)) - 1;
return num_glyphs;
}
int IndexFormat(const Font& font) {
const Font::Table* head_table = font.FindTable(kHeadTableTag);
if (head_table == NULL) {
return 0;
}
return head_table->data[51];
}
bool GetGlyphData(const Font& font, int glyph_index,
const uint8_t** glyph_data, size_t* glyph_size) {
if (glyph_index < 0) {
return FONT_COMPRESSION_FAILURE();
}
const Font::Table* head_table = font.FindTable(kHeadTableTag);
const Font::Table* loca_table = font.FindTable(kLocaTableTag);
const Font::Table* glyf_table = font.FindTable(kGlyfTableTag);
if (head_table == NULL || loca_table == NULL || glyf_table == NULL ||
head_table->length < 52) {
return FONT_COMPRESSION_FAILURE();
}
int index_fmt = IndexFormat(font);
Buffer loca_buf(loca_table->data, loca_table->length);
if (index_fmt == 0) {
uint16_t offset1, offset2;
if (!loca_buf.Skip(2 * glyph_index) ||
!loca_buf.ReadU16(&offset1) ||
!loca_buf.ReadU16(&offset2) ||
offset2 < offset1 ||
2 * offset2 > glyf_table->length) {
return FONT_COMPRESSION_FAILURE();
}
*glyph_data = glyf_table->data + 2 * offset1;
*glyph_size = 2 * (offset2 - offset1);
} else {
uint32_t offset1, offset2;
if (!loca_buf.Skip(4 * glyph_index) ||
!loca_buf.ReadU32(&offset1) ||
!loca_buf.ReadU32(&offset2) ||
offset2 < offset1 ||
offset2 > glyf_table->length) {
return FONT_COMPRESSION_FAILURE();
}
*glyph_data = glyf_table->data + offset1;
*glyph_size = offset2 - offset1;
}
return true;
}
bool RemoveDigitalSignature(Font* font) {
std::map<uint32_t, Font::Table>::iterator it =
font->tables.find(kDsigTableTag);
if (it != font->tables.end()) {
font->tables.erase(it);
font->num_tables = font->tables.size();
}
return true;
}
} // namespace woff2
// Copyright 2013 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.
//
// Data model for a font file in sfnt format, reading and writing functions and
// accessors for the glyph data.
#ifndef WOFF2_FONT_H_
#define WOFF2_FONT_H_
#include <stddef.h>
#include <inttypes.h>
#include <map>
#include <vector>
namespace woff2 {
// Represents an sfnt font file. Only the table directory is parsed, for the
// table data we only store a raw pointer, therefore a font object is valid only
// as long the data from which it was parsed is around.
struct Font {
uint32_t flavor;
uint16_t num_tables;
struct Table {
uint32_t tag;
uint32_t checksum;
uint32_t offset;
uint32_t length;
const uint8_t* data;
// Buffer used to mutate the data before writing out.
std::vector<uint8_t> buffer;
};
std::map<uint32_t, Table> tables;
Table* FindTable(uint32_t tag);
const Table* FindTable(uint32_t tag) const;
};
// Parses the font from the given data. Returns false on parsing failure or
// buffer overflow. The font is valid only so long the input data pointer is
// valid.
bool ReadFont(const uint8_t* data, size_t len, Font* font);
// Returns the file size of the font.
size_t FontFileSize(const Font& font);
// Writes the font into the specified dst buffer. The dst_size should be the
// same as returned by FontFileSize(). Returns false upon buffer overflow (which
// should not happen if dst_size was computed by FontFileSize()).
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size);
// Returns the number of glyphs in the font.
// NOTE: Currently this works only for TrueType-flavored fonts, will return
// zero for CFF-flavored fonts.
int NumGlyphs(const Font& font);
// Returns the index format of the font
int IndexFormat(const Font& font);
// Sets *glyph_data and *glyph_size to point to the location of the glyph data
// with the given index. Returns false if the glyph is not found.
bool GetGlyphData(const Font& font, int glyph_index,
const uint8_t** glyph_data, size_t* glyph_size);
// Removes the digital signature (DSIG) table
bool RemoveDigitalSignature(Font* font);
} // namespace woff2
#endif // WOFF2_FONT_H_
// Copyright 2013 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.
//
// Glyph manipulation
#include "./glyph.h"
#include <stdlib.h>
#include <limits>
#include "./buffer.h"
#include "./store_bytes.h"
namespace woff2 {
static const int32_t kFLAG_ONCURVE = 1;
static const int32_t kFLAG_XSHORT = 1 << 1;
static const int32_t kFLAG_YSHORT = 1 << 2;
static const int32_t kFLAG_REPEAT = 1 << 3;
static const int32_t kFLAG_XREPEATSIGN = 1 << 4;
static const int32_t kFLAG_YREPEATSIGN = 1 << 5;
static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3;
static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5;
static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) {
glyph->have_instructions = false;
glyph->composite_data = buffer->buffer() + buffer->offset();
size_t start_offset = buffer->offset();
uint16_t flags = kFLAG_MORE_COMPONENTS;
while (flags & kFLAG_MORE_COMPONENTS) {
if (!buffer->ReadU16(&flags)) {
return FONT_COMPRESSION_FAILURE();
}
glyph->have_instructions |= (flags & kFLAG_WE_HAVE_INSTRUCTIONS) != 0;
size_t arg_size = 2; // glyph index
if (flags & kFLAG_ARG_1_AND_2_ARE_WORDS) {
arg_size += 4;
} else {
arg_size += 2;
}
if (flags & kFLAG_WE_HAVE_A_SCALE) {
arg_size += 2;
} else if (flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
arg_size += 4;
} else if (flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) {
arg_size += 8;
}
if (!buffer->Skip(arg_size)) {
return FONT_COMPRESSION_FAILURE();
}
}
if (buffer->offset() - start_offset > std::numeric_limits<uint32_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
glyph->composite_data_size = buffer->offset() - start_offset;
return true;
}
bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
Buffer buffer(data, len);
int16_t num_contours;
if (!buffer.ReadS16(&num_contours)) {
return FONT_COMPRESSION_FAILURE();
}
if (num_contours == 0) {
// Empty glyph.
return true;
}
// Read the bounding box.
if (!buffer.ReadS16(&glyph->x_min) ||
!buffer.ReadS16(&glyph->y_min) ||
!buffer.ReadS16(&glyph->x_max) ||
!buffer.ReadS16(&glyph->y_max)) {
return FONT_COMPRESSION_FAILURE();
}
if (num_contours > 0) {
// Simple glyph.
glyph->contours.resize(num_contours);
// Read the number of points per contour.
uint16_t last_point_index = 0;
for (int i = 0; i < num_contours; ++i) {
uint16_t point_index;
if (!buffer.ReadU16(&point_index)) {
return FONT_COMPRESSION_FAILURE();
}
uint16_t num_points = point_index - last_point_index + (i == 0 ? 1 : 0);
glyph->contours[i].resize(num_points);
last_point_index = point_index;
}
// Read the instructions.
if (!buffer.ReadU16(&glyph->instructions_size)) {
return FONT_COMPRESSION_FAILURE();
}
glyph->instructions_data = data + buffer.offset();
if (!buffer.Skip(glyph->instructions_size)) {
return FONT_COMPRESSION_FAILURE();
}
// Read the run-length coded flags.
std::vector<std::vector<uint8_t> > flags(num_contours);
uint8_t flag = 0;
uint8_t flag_repeat = 0;
for (int i = 0; i < num_contours; ++i) {
flags[i].resize(glyph->contours[i].size());
for (int j = 0; j < glyph->contours[i].size(); ++j) {
if (flag_repeat == 0) {
if (!buffer.ReadU8(&flag)) {
return FONT_COMPRESSION_FAILURE();
}
if (flag & kFLAG_REPEAT) {
if (!buffer.ReadU8(&flag_repeat)) {
return FONT_COMPRESSION_FAILURE();
}
}
} else {
flag_repeat--;
}
flags[i][j] = flag;
glyph->contours[i][j].on_curve = flag & kFLAG_ONCURVE;
}
}
// Read the x coordinates.
int prev_x = 0;
for (int i = 0; i < num_contours; ++i) {
for (int j = 0; j < glyph->contours[i].size(); ++j) {
uint8_t flag = flags[i][j];
if (flag & kFLAG_XSHORT) {
// single byte x-delta coord value
uint8_t x_delta;
if (!buffer.ReadU8(&x_delta)) {
return FONT_COMPRESSION_FAILURE();
}
int sign = (flag & kFLAG_XREPEATSIGN) ? 1 : -1;
glyph->contours[i][j].x = prev_x + sign * x_delta;
} else {
// double byte x-delta coord value
int16_t x_delta = 0;
if (!(flag & kFLAG_XREPEATSIGN)) {
if (!buffer.ReadS16(&x_delta)) {
return FONT_COMPRESSION_FAILURE();
}
}
glyph->contours[i][j].x = prev_x + x_delta;
}
prev_x = glyph->contours[i][j].x;
}
}
// Read the y coordinates.
int prev_y = 0;
for (int i = 0; i < num_contours; ++i) {
for (int j = 0; j < glyph->contours[i].size(); ++j) {
uint8_t flag = flags[i][j];
if (flag & kFLAG_YSHORT) {
// single byte y-delta coord value
uint8_t y_delta;
if (!buffer.ReadU8(&y_delta)) {
return FONT_COMPRESSION_FAILURE();
}
int sign = (flag & kFLAG_YREPEATSIGN) ? 1 : -1;
glyph->contours[i][j].y = prev_y + sign * y_delta;
} else {
// double byte y-delta coord value
int16_t y_delta = 0;
if (!(flag & kFLAG_YREPEATSIGN)) {
if (!buffer.ReadS16(&y_delta)) {
return FONT_COMPRESSION_FAILURE();
}
}
glyph->contours[i][j].y = prev_y + y_delta;
}
prev_y = glyph->contours[i][j].y;
}
}
} else if (num_contours == -1) {
// Composite glyph.
if (!ReadCompositeGlyphData(&buffer, glyph)) {
return FONT_COMPRESSION_FAILURE();
}
// Read the instructions.
if (glyph->have_instructions) {
if (!buffer.ReadU16(&glyph->instructions_size)) {
return FONT_COMPRESSION_FAILURE();
}
glyph->instructions_data = data + buffer.offset();
if (!buffer.Skip(glyph->instructions_size)) {
return FONT_COMPRESSION_FAILURE();
}
} else {
glyph->instructions_size = 0;
}
} else {
return FONT_COMPRESSION_FAILURE();
}
return true;
}
namespace {
void StoreBbox(const Glyph& glyph, size_t* offset, uint8_t* dst) {
Store16(glyph.x_min, offset, dst);
Store16(glyph.y_min, offset, dst);
Store16(glyph.x_max, offset, dst);
Store16(glyph.y_max, offset, dst);
}
void StoreInstructions(const Glyph& glyph, size_t* offset, uint8_t* dst) {
Store16(glyph.instructions_size, offset, dst);
StoreBytes(glyph.instructions_data, glyph.instructions_size, offset, dst);
}
bool StoreEndPtsOfContours(const Glyph& glyph, size_t* offset, uint8_t* dst) {
int end_point = -1;
for (const auto& contour : glyph.contours) {
end_point += contour.size();
if (contour.size() > std::numeric_limits<uint16_t>::max() ||
end_point > std::numeric_limits<uint16_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
Store16(end_point, offset, dst);
}
return true;
}
bool StorePoints(const Glyph& glyph, size_t* offset,
uint8_t* dst, size_t dst_size) {
int last_flag = -1;
int repeat_count = 0;
int last_x = 0;
int last_y = 0;
size_t x_bytes = 0;
size_t y_bytes = 0;
// Store the flags and calculate the total size of the x and y coordinates.
for (const auto& contour : glyph.contours) {
for (const auto& point : contour) {
int flag = point.on_curve ? kFLAG_ONCURVE : 0;
int dx = point.x - last_x;
int dy = point.y - last_y;
if (dx == 0) {
flag |= kFLAG_XREPEATSIGN;
} else if (dx > -256 && dx < 256) {
flag |= kFLAG_XSHORT | (dx > 0 ? kFLAG_XREPEATSIGN : 0);
x_bytes += 1;
} else {
x_bytes += 2;
}
if (dy == 0) {
flag |= kFLAG_YREPEATSIGN;
} else if (dy > -256 && dy < 256) {
flag |= kFLAG_YSHORT | (dy > 0 ? kFLAG_YREPEATSIGN : 0);
y_bytes += 1;
} else {
y_bytes += 2;
}
if (flag == last_flag && repeat_count != 255) {
dst[*offset - 1] |= kFLAG_REPEAT;
repeat_count++;
} else {
if (repeat_count != 0) {
if (*offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[(*offset)++] = repeat_count;
}
if (*offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[(*offset)++] = flag;
repeat_count = 0;
}
last_x = point.x;
last_y = point.y;
last_flag = flag;
}
}
if (repeat_count != 0) {
if (*offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[(*offset)++] = repeat_count;
}
if (*offset + x_bytes + y_bytes > dst_size) {
return FONT_COMPRESSION_FAILURE();
}
// Store the x and y coordinates.
size_t x_offset = *offset;
size_t y_offset = *offset + x_bytes;
last_x = 0;
last_y = 0;
for (const auto& contour : glyph.contours) {
for (const auto& point : contour) {
int dx = point.x - last_x;
int dy = point.y - last_y;
if (dx == 0) {
// pass
} else if (dx > -256 && dx < 256) {
dst[x_offset++] = std::abs(dx);
} else {
Store16(dx, &x_offset, dst);
}
if (dy == 0) {
// pass
} else if (dy > -256 && dy < 256) {
dst[y_offset++] = std::abs(dy);
} else {
Store16(dy, &y_offset, dst);
}
last_x += dx;
last_y += dy;
}
}
*offset = y_offset;
return true;
}
} // namespace
bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size) {
size_t offset = 0;
if (glyph.composite_data_size > 0) {
// Composite glyph.
if (*dst_size < ((10ULL + glyph.composite_data_size) +
((glyph.have_instructions ? 2ULL : 0) +
glyph.instructions_size))) {
return FONT_COMPRESSION_FAILURE();
}
Store16(-1, &offset, dst);
StoreBbox(glyph, &offset, dst);
StoreBytes(glyph.composite_data, glyph.composite_data_size, &offset, dst);
if (glyph.have_instructions) {
StoreInstructions(glyph, &offset, dst);
}
} else if (glyph.contours.size() > 0) {
// Simple glyph.
if (glyph.contours.size() > std::numeric_limits<int16_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
if (*dst_size < ((12ULL + 2 * glyph.contours.size()) +
glyph.instructions_size)) {
return FONT_COMPRESSION_FAILURE();
}
Store16(glyph.contours.size(), &offset, dst);
StoreBbox(glyph, &offset, dst);
if (!StoreEndPtsOfContours(glyph, &offset, dst)) {
return FONT_COMPRESSION_FAILURE();
}
StoreInstructions(glyph, &offset, dst);
if (!StorePoints(glyph, &offset, dst, *dst_size)) {
return FONT_COMPRESSION_FAILURE();
}
}
*dst_size = offset;
return true;
}
} // namespace woff2
// Copyright 2013 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.
//
// Data model and I/O for glyph data within sfnt format files for the purpose of
// performing the preprocessing step of the WOFF 2.0 conversion.
#ifndef WOFF2_GLYPH_H_
#define WOFF2_GLYPH_H_
#include <stddef.h>
#include <inttypes.h>
#include <vector>
namespace woff2 {
// Represents a parsed simple or composite glyph. The composite glyph data and
// instructions are un-parsed and we keep only pointers to the raw data,
// therefore the glyph is valid only so long the data from which it was parsed
// is around.
class Glyph {
public:
Glyph() : instructions_size(0), composite_data_size(0) {}
// Bounding box.
int16_t x_min;
int16_t x_max;
int16_t y_min;
int16_t y_max;
// Instructions.
uint16_t instructions_size;
const uint8_t* instructions_data;
// Data model for simple glyphs.
struct Point {
int x;
int y;
bool on_curve;
};
std::vector<std::vector<Point> > contours;
// Data for composite glyphs.
const uint8_t* composite_data;
uint32_t composite_data_size;
bool have_instructions;
};
// Parses the glyph from the given data. Returns false on parsing failure or
// buffer overflow. The glyph is valid only so long the input data pointer is
// valid.
bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph);
// Stores the glyph into the specified dst buffer. The *dst_size is the buffer
// size on entry and is set to the actual (unpadded) stored size on exit.
// Returns false on buffer overflow.
bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size);
} // namespace woff2
#endif // WOFF2_GLYPH_H_
// Copyright 2013 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.
//
// Glyph normalization
#include "./normalize.h"
#include <inttypes.h>
#include <stddef.h>
#include "./buffer.h"
#include "./port.h"
#include "./font.h"
#include "./glyph.h"
#include "./round.h"
#include "./store_bytes.h"
#include "./table_tags.h"
namespace woff2 {
namespace {
void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) {
if (index_fmt == 0) {
Store16(value >> 1, offset, dst);
} else {
StoreU32(value, offset, dst);
}
}
void NormalizeSimpleGlyphBoundingBox(Glyph* glyph) {
if (glyph->contours.empty() || glyph->contours[0].empty()) {
return;
}
int16_t x_min = glyph->contours[0][0].x;
int16_t y_min = glyph->contours[0][0].y;
int16_t x_max = x_min;
int16_t y_max = y_min;
for (const auto& contour : glyph->contours) {
for (const auto& point : contour) {
if (point.x < x_min) x_min = point.x;
if (point.x > x_max) x_max = point.x;
if (point.y < y_min) y_min = point.y;
if (point.y > y_max) y_max = point.y;
}
}
glyph->x_min = x_min;
glyph->y_min = y_min;
glyph->x_max = x_max;
glyph->y_max = y_max;
}
} // namespace
namespace {
bool WriteNormalizedLoca(int index_fmt, int num_glyphs, Font* font) {
Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
Font::Table* loca_table = font->FindTable(kLocaTableTag);
int glyph_sz = index_fmt == 0 ? 2 : 4;
loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz);
loca_table->length = (num_glyphs + 1) * glyph_sz;
uint8_t* glyf_dst = &glyf_table->buffer[0];
uint8_t* loca_dst = &loca_table->buffer[0];
uint32_t glyf_offset = 0;
size_t loca_offset = 0;
for (int i = 0; i < num_glyphs; ++i) {
StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
Glyph glyph;
const uint8_t* glyph_data;
size_t glyph_size;
if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
(glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
return FONT_COMPRESSION_FAILURE();
}
NormalizeSimpleGlyphBoundingBox(&glyph);
size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset;
if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) {
return FONT_COMPRESSION_FAILURE();
}
glyf_dst_size = Round4(glyf_dst_size);
if (glyf_dst_size > std::numeric_limits<uint32_t>::max() ||
glyf_offset + static_cast<uint32_t>(glyf_dst_size) < glyf_offset ||
(index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) {
return FONT_COMPRESSION_FAILURE();
}
glyf_offset += glyf_dst_size;
}
if (glyf_offset == 0) {
return false;
}
StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst);
glyf_table->buffer.resize(glyf_offset);
glyf_table->data = &glyf_table->buffer[0];
glyf_table->length = glyf_offset;
loca_table->data = &loca_table->buffer[0];
return true;
}
} // namespace
namespace {
bool MakeEditableBuffer(Font* font, int tableTag) {
Font::Table* table = font->FindTable(tableTag);
if (table == NULL) {
return FONT_COMPRESSION_FAILURE();
}
int sz = Round4(table->length);
table->buffer.resize(sz);
uint8_t* buf = &table->buffer[0];
memcpy(buf, table->data, sz);
table->data = buf;
return true;
}
} // namespace
bool NormalizeGlyphs(Font* font) {
Font::Table* cff_table = font->FindTable(kCffTableTag);
Font::Table* head_table = font->FindTable(kHeadTableTag);
Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
Font::Table* loca_table = font->FindTable(kLocaTableTag);
if (head_table == NULL) {
return FONT_COMPRESSION_FAILURE();
}
// CFF, no loca, no glyf is OK for CFF. If so, don't normalize.
if (cff_table != NULL && loca_table == NULL && glyf_table == NULL) {
return true;
}
if (loca_table == NULL || glyf_table == NULL) {
return FONT_COMPRESSION_FAILURE();
}
int index_fmt = head_table->data[51];
int num_glyphs = NumGlyphs(*font);
// We need to allocate a bit more than its original length for the normalized
// glyf table, since it can happen that the glyphs in the original table are
// 2-byte aligned, while in the normalized table they are 4-byte aligned.
// That gives a maximum of 2 bytes increase per glyph. However, there is no
// theoretical guarantee that the total size of the flags plus the coordinates
// is the smallest possible in the normalized version, so we have to allow
// some general overhead.
// TODO(user) Figure out some more precise upper bound on the size of
// the overhead.
size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs;
glyf_table->buffer.resize(max_normalized_glyf_size);
// if we can't write a loca using short's (index_fmt 0)
// try again using longs (index_fmt 1)
if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
if (index_fmt != 0) {
return FONT_COMPRESSION_FAILURE();
}
// Rewrite loca with 4-byte entries & update head to match
index_fmt = 1;
if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) {
return FONT_COMPRESSION_FAILURE();
}
head_table->buffer[51] = 1;
}
return true;
}
bool NormalizeOffsets(Font* font) {
uint32_t offset = 12 + 16 * font->num_tables;
for (auto& i : font->tables) {
i.second.offset = offset;
offset += Round4(i.second.length);
}
return true;
}
namespace {
uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
uint32_t checksum = 0;
for (size_t i = 0; i < size; i += 4) {
checksum += ((buf[i] << 24) |
(buf[i + 1] << 16) |
(buf[i + 2] << 8) |
buf[i + 3]);
}
return checksum;
}
uint32_t ComputeHeaderChecksum(const Font& font) {
uint32_t checksum = font.flavor;
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
uint16_t range_shift = (font.num_tables << 4) - search_range;
checksum += (font.num_tables << 16 | search_range);
checksum += (max_pow2 << 16 | range_shift);
for (const auto& i : font.tables) {
checksum += i.second.tag;
checksum += i.second.checksum;
checksum += i.second.offset;
checksum += i.second.length;
}
return checksum;
}
} // namespace
bool FixChecksums(Font* font) {
Font::Table* head_table = font->FindTable(kHeadTableTag);
if (head_table == NULL || head_table->length < 12) {
return FONT_COMPRESSION_FAILURE();
}
uint8_t* head_buf = &head_table->buffer[0];
size_t offset = 8;
StoreU32(0, &offset, head_buf);
uint32_t file_checksum = 0;
for (auto& i : font->tables) {
Font::Table* table = &i.second;
table->checksum = ComputeChecksum(table->data, table->length);
file_checksum += table->checksum;
}
file_checksum += ComputeHeaderChecksum(*font);
offset = 8;
StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
return true;
}
bool NormalizeFont(Font* font) {
return (MakeEditableBuffer(font, kHeadTableTag) &&
RemoveDigitalSignature(font) &&
NormalizeGlyphs(font) &&
NormalizeOffsets(font) &&
FixChecksums(font));
}
} // namespace woff2
// Copyright 2013 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.
//
// Functions for normalizing fonts. Since the WOFF 2.0 decoder creates font
// files in normalized form, the WOFF 2.0 conversion is guaranteed to be
// lossless (in a bitwise sense) only for normalized font files.
#ifndef WOFF2_NORMALIZE_H_
#define WOFF2_NORMALIZE_H_
namespace woff2 {
struct Font;
// Changes the offset fields of the table headers so that the data for the
// tables will be written in order of increasing tag values, without any gaps
// other than the 4-byte padding.
bool NormalizeOffsets(Font* font);
// Changes the checksum fields of the table headers and the checksum field of
// the head table so that it matches the current data.
bool FixChecksums(Font* font);
// Parses each of the glyphs in the font and writes them again to the glyf
// table in normalized form, as defined by the StoreGlyph() function. Changes
// the loca table accordigly.
bool NormalizeGlyphs(Font* font);
// Performs all of the normalization steps above.
bool NormalizeFont(Font* font);
} // namespace woff2
#endif // WOFF2_NORMALIZE_H_
// Copyright 2013 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.
//
// Helper function for bit twiddling
#ifndef WOFF2_PORT_H_
#define WOFF2_PORT_H_
namespace woff2 {
typedef unsigned int uint32;
inline int Log2Floor(uint32 n) {
#if defined(__GNUC__)
return n == 0 ? -1 : 31 ^ __builtin_clz(n);
#else
if (n == 0)
return -1;
int log = 0;
uint32 value = n;
for (int i = 4; i >= 0; --i) {
int shift = (1 << i);
uint32 x = value >> shift;
if (x != 0) {
value = x;
log += shift;
}
}
assert(value == 1);
return log;
#endif
}
} // namespace woff2
#endif // WOFF2_PORT_H_
// Copyright 2013 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.
//
// Helper for rounding
#ifndef WOFF2_ROUND_H_
#define WOFF2_ROUND_H_
#include <limits>
namespace woff2 {
// Round a value up to the nearest multiple of 4. Don't round the value in the
// case that rounding up overflows.
template<typename T> T Round4(T value) {
if (std::numeric_limits<T>::max() - value < 3) {
return value;
}
return (value + 3) & ~3;
}
} // namespace woff2
#endif // WOFF2_ROUND_H_
// Copyright 2013 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.
//
// Helper functions for storing integer values into byte streams.
// No bounds checking is performed, that is the responsibility of the caller.
#ifndef WOFF2_STORE_BYTES_H_
#define WOFF2_STORE_BYTES_H_
#include <inttypes.h>
#include <stddef.h>
#include <string.h>
namespace woff2 {
inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) {
dst[offset] = x >> 24;
dst[offset + 1] = x >> 16;
dst[offset + 2] = x >> 8;
dst[offset + 3] = x;
return offset + 4;
}
inline size_t Store16(uint8_t* dst, size_t offset, int x) {
dst[offset] = x >> 8;
dst[offset + 1] = x;
return offset + 2;
}
inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) {
dst[(*offset)++] = val >> 24;
dst[(*offset)++] = val >> 16;
dst[(*offset)++] = val >> 8;
dst[(*offset)++] = val;
}
inline void Store16(int val, size_t* offset, uint8_t* dst) {
dst[(*offset)++] = val >> 8;
dst[(*offset)++] = val;
}
inline void StoreBytes(const uint8_t* data, size_t len,
size_t* offset, uint8_t* dst) {
memcpy(&dst[*offset], data, len);
*offset += len;
}
} // namespace woff2
#endif // WOFF2_STORE_BYTES_H_
// Copyright 2014 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.
//
// Font table tags
#include "./table_tags.h"
namespace woff2 {
// Note that the byte order is big-endian, not the same as ots.cc
#define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d)
const uint32_t kKnownTags[63] = {
TAG('c', 'm', 'a', 'p'), // 0
TAG('h', 'e', 'a', 'd'), // 1
TAG('h', 'h', 'e', 'a'), // 2
TAG('h', 'm', 't', 'x'), // 3
TAG('m', 'a', 'x', 'p'), // 4
TAG('n', 'a', 'm', 'e'), // 5
TAG('O', 'S', '/', '2'), // 6
TAG('p', 'o', 's', 't'), // 7
TAG('c', 'v', 't', ' '), // 8
TAG('f', 'p', 'g', 'm'), // 9
TAG('g', 'l', 'y', 'f'), // 10
TAG('l', 'o', 'c', 'a'), // 11
TAG('p', 'r', 'e', 'p'), // 12
TAG('C', 'F', 'F', ' '), // 13
TAG('V', 'O', 'R', 'G'), // 14
TAG('E', 'B', 'D', 'T'), // 15
TAG('E', 'B', 'L', 'C'), // 16
TAG('g', 'a', 's', 'p'), // 17
TAG('h', 'd', 'm', 'x'), // 18
TAG('k', 'e', 'r', 'n'), // 19
TAG('L', 'T', 'S', 'H'), // 20
TAG('P', 'C', 'L', 'T'), // 21
TAG('V', 'D', 'M', 'X'), // 22
TAG('v', 'h', 'e', 'a'), // 23
TAG('v', 'm', 't', 'x'), // 24
TAG('B', 'A', 'S', 'E'), // 25
TAG('G', 'D', 'E', 'F'), // 26
TAG('G', 'P', 'O', 'S'), // 27
TAG('G', 'S', 'U', 'B'), // 28
TAG('E', 'B', 'S', 'C'), // 29
TAG('J', 'S', 'T', 'F'), // 30
TAG('M', 'A', 'T', 'H'), // 31
TAG('C', 'B', 'D', 'T'), // 32
TAG('C', 'B', 'L', 'C'), // 33
TAG('C', 'O', 'L', 'R'), // 34
TAG('C', 'P', 'A', 'L'), // 35
TAG('S', 'V', 'G', ' '), // 36
TAG('s', 'b', 'i', 'x'), // 37
TAG('a', 'c', 'n', 't'), // 38
TAG('a', 'v', 'a', 'r'), // 39
TAG('b', 'd', 'a', 't'), // 40
TAG('b', 'l', 'o', 'c'), // 41
TAG('b', 's', 'l', 'n'), // 42
TAG('c', 'v', 'a', 'r'), // 43
TAG('f', 'd', 's', 'c'), // 44
TAG('f', 'e', 'a', 't'), // 45
TAG('f', 'm', 't', 'x'), // 46
TAG('f', 'v', 'a', 'r'), // 47
TAG('g', 'v', 'a', 'r'), // 48
TAG('h', 's', 't', 'y'), // 49
TAG('j', 'u', 's', 't'), // 50
TAG('l', 'c', 'a', 'r'), // 51
TAG('m', 'o', 'r', 't'), // 52
TAG('m', 'o', 'r', 'x'), // 53
TAG('o', 'p', 'b', 'd'), // 54
TAG('p', 'r', 'o', 'p'), // 55
TAG('t', 'r', 'a', 'k'), // 56
TAG('Z', 'a', 'p', 'f'), // 57
TAG('S', 'i', 'l', 'f'), // 58
TAG('G', 'l', 'a', 't'), // 59
TAG('G', 'l', 'o', 'c'), // 60
TAG('F', 'e', 'a', 't'), // 61
TAG('S', 'i', 'l', 'l'), // 62
};
} // namespace woff2
// Copyright 2014 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.
//
// Font table tags
#ifndef WOFF2_TABLE_TAGS_H_
#define WOFF2_TABLE_TAGS_H_
#include <inttypes.h>
namespace woff2 {
// Tags of popular tables.
static const uint32_t kGlyfTableTag = 0x676c7966;
static const uint32_t kHeadTableTag = 0x68656164;
static const uint32_t kLocaTableTag = 0x6c6f6361;
static const uint32_t kDsigTableTag = 0x44534947;
static const uint32_t kCffTableTag = 0x43464620;
extern const uint32_t kKnownTags[];
} // namespace woff2
#endif // WOFF2_TABLE_TAGS_H_
// Copyright 2013 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.
//
// Library for preprocessing fonts as part of the WOFF 2.0 conversion.
#include "./transform.h"
#include <complex> // for std::abs
#include "./buffer.h"
#include "./font.h"
#include "./glyph.h"
#include "./table_tags.h"
namespace woff2 {
namespace {
const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) {
if (len == 0) return;
size_t offset = out->size();
out->resize(offset + len);
memcpy(&(*out)[offset], data, len);
}
void WriteBytes(std::vector<uint8_t>* out, const std::vector<uint8_t>& in) {
for (int i = 0; i < in.size(); ++i) {
out->push_back(in[i]);
}
}
void WriteUShort(std::vector<uint8_t>* out, int value) {
out->push_back(value >> 8);
out->push_back(value & 255);
}
void WriteLong(std::vector<uint8_t>* out, int value) {
out->push_back((value >> 24) & 255);
out->push_back((value >> 16) & 255);
out->push_back((value >> 8) & 255);
out->push_back(value & 255);
}
void Write255UShort(std::vector<uint8_t>* out, int value) {
if (value < 253) {
out->push_back(value);
} else if (value < 506) {
out->push_back(255);
out->push_back(value - 253);
} else if (value < 762) {
out->push_back(254);
out->push_back(value - 506);
} else {
out->push_back(253);
out->push_back(value >> 8);
out->push_back(value & 0xff);
}
}
// Glyf table preprocessing, based on
// GlyfEncoder.java
// but only the "sbbox" and "cbbox" options are supported.
class GlyfEncoder {
public:
explicit GlyfEncoder(int num_glyphs)
: sbbox_(false), cbbox_(true), n_glyphs_(num_glyphs) {
bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2);
}
bool Encode(int glyph_id, const Glyph& glyph) {
if (glyph.composite_data_size > 0) {
WriteCompositeGlyph(glyph_id, glyph);
} else if (glyph.contours.size() > 0) {
WriteSimpleGlyph(glyph_id, glyph);
} else {
WriteUShort(&n_contour_stream_, 0);
}
return true;
}
void GetTransformedGlyfBytes(std::vector<uint8_t>* result) {
WriteLong(result, 0); // version
WriteUShort(result, n_glyphs_);
WriteUShort(result, 0); // index_format, will be set later
WriteLong(result, n_contour_stream_.size());
WriteLong(result, n_points_stream_.size());
WriteLong(result, flag_byte_stream_.size());
WriteLong(result, glyph_stream_.size());
WriteLong(result, composite_stream_.size());
WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size());
WriteLong(result, instruction_stream_.size());
WriteBytes(result, n_contour_stream_);
WriteBytes(result, n_points_stream_);
WriteBytes(result, flag_byte_stream_);
WriteBytes(result, glyph_stream_);
WriteBytes(result, composite_stream_);
WriteBytes(result, bbox_bitmap_);
WriteBytes(result, bbox_stream_);
WriteBytes(result, instruction_stream_);
}
private:
void WriteInstructions(const Glyph& glyph) {
Write255UShort(&glyph_stream_, glyph.instructions_size);
WriteBytes(&instruction_stream_,
glyph.instructions_data, glyph.instructions_size);
}
void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) {
int num_contours = glyph.contours.size();
WriteUShort(&n_contour_stream_, num_contours);
if (sbbox_) {
WriteBbox(glyph_id, glyph);
}
// TODO: check that bbox matches, write bbox if not
for (int i = 0; i < num_contours; i++) {
Write255UShort(&n_points_stream_, glyph.contours[i].size());
}
int lastX = 0;
int lastY = 0;
for (int i = 0; i < num_contours; i++) {
int num_points = glyph.contours[i].size();
for (int j = 0; j < num_points; j++) {
int x = glyph.contours[i][j].x;
int y = glyph.contours[i][j].y;
int dx = x - lastX;
int dy = y - lastY;
WriteTriplet(glyph.contours[i][j].on_curve, dx, dy);
lastX = x;
lastY = y;
}
}
if (num_contours > 0) {
WriteInstructions(glyph);
}
}
void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) {
WriteUShort(&n_contour_stream_, -1);
if (cbbox_) {
WriteBbox(glyph_id, glyph);
}
WriteBytes(&composite_stream_,
glyph.composite_data,
glyph.composite_data_size);
if (glyph.have_instructions) {
WriteInstructions(glyph);
}
}
void WriteBbox(int glyph_id, const Glyph& glyph) {
bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7);
WriteUShort(&bbox_stream_, glyph.x_min);
WriteUShort(&bbox_stream_, glyph.y_min);
WriteUShort(&bbox_stream_, glyph.x_max);
WriteUShort(&bbox_stream_, glyph.y_max);
}
void WriteTriplet(bool on_curve, int x, int y) {
int abs_x = std::abs(x);
int abs_y = std::abs(y);
int on_curve_bit = on_curve ? 0 : 128;
int x_sign_bit = (x < 0) ? 0 : 1;
int y_sign_bit = (y < 0) ? 0 : 1;
int xy_sign_bits = x_sign_bit + 2 * y_sign_bit;
if (x == 0 && abs_y < 1280) {
flag_byte_stream_.push_back(on_curve_bit +
((abs_y & 0xf00) >> 7) + y_sign_bit);
glyph_stream_.push_back(abs_y & 0xff);
} else if (y == 0 && abs_x < 1280) {
flag_byte_stream_.push_back(on_curve_bit + 10 +
((abs_x & 0xf00) >> 7) + x_sign_bit);
glyph_stream_.push_back(abs_x & 0xff);
} else if (abs_x < 65 && abs_y < 65) {
flag_byte_stream_.push_back(on_curve_bit + 20 +
((abs_x - 1) & 0x30) +
(((abs_y - 1) & 0x30) >> 2) +
xy_sign_bits);
glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf));
} else if (abs_x < 769 && abs_y < 769) {
flag_byte_stream_.push_back(on_curve_bit + 84 +
12 * (((abs_x - 1) & 0x300) >> 8) +
(((abs_y - 1) & 0x300) >> 6) + xy_sign_bits);
glyph_stream_.push_back((abs_x - 1) & 0xff);
glyph_stream_.push_back((abs_y - 1) & 0xff);
} else if (abs_x < 4096 && abs_y < 4096) {
flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits);
glyph_stream_.push_back(abs_x >> 4);
glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8));
glyph_stream_.push_back(abs_y & 0xff);
} else {
flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits);
glyph_stream_.push_back(abs_x >> 8);
glyph_stream_.push_back(abs_x & 0xff);
glyph_stream_.push_back(abs_y >> 8);
glyph_stream_.push_back(abs_y & 0xff);
}
}
std::vector<uint8_t> n_contour_stream_;
std::vector<uint8_t> n_points_stream_;
std::vector<uint8_t> flag_byte_stream_;
std::vector<uint8_t> composite_stream_;
std::vector<uint8_t> bbox_bitmap_;
std::vector<uint8_t> bbox_stream_;
std::vector<uint8_t> glyph_stream_;
std::vector<uint8_t> instruction_stream_;
bool sbbox_;
bool cbbox_;
int n_glyphs_;
};
} // namespace
bool TransformGlyfAndLocaTables(Font* font) {
// no transform for CFF
if (font->FindTable(kCffTableTag) != NULL
&& font->FindTable(kGlyfTableTag) == NULL
&& font->FindTable(kLocaTableTag) == NULL) {
return true;
}
Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080];
int num_glyphs = NumGlyphs(*font);
GlyfEncoder encoder(num_glyphs);
for (int i = 0; i < num_glyphs; ++i) {
Glyph glyph;
const uint8_t* glyph_data;
size_t glyph_size;
if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
(glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
return FONT_COMPRESSION_FAILURE();
}
encoder.Encode(i, glyph);
}
encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer);
const Font::Table* head_table = font->FindTable(kHeadTableTag);
if (head_table == NULL || head_table->length < 52) {
return FONT_COMPRESSION_FAILURE();
}
transformed_glyf->buffer[7] = head_table->data[51]; // index_format
transformed_glyf->tag = kGlyfTableTag ^ 0x80808080;
transformed_glyf->length = transformed_glyf->buffer.size();
transformed_glyf->data = transformed_glyf->buffer.data();
transformed_loca->tag = kLocaTableTag ^ 0x80808080;
transformed_loca->length = 0;
transformed_loca->data = NULL;
return true;
}
} // namespace woff2
// Copyright 2013 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.
//
// Library for preprocessing fonts as part of the WOFF 2.0 conversion.
#ifndef WOFF2_TRANSFORM_H_
#define WOFF2_TRANSFORM_H_
#include "./font.h"
namespace woff2 {
// Adds the transformed versions of the glyf and loca tables to the font. The
// transformed loca table has zero length. The tag of the transformed tables is
// derived from the original tag by flipping the MSBs of every byte.
bool TransformGlyfAndLocaTables(Font* font);
} // namespace woff2
#endif // WOFF2_TRANSFORM_H_
// Copyright 2014 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.
//
// Common definition for WOFF2 encoding/decoding
#ifndef WOFF2_WOFF2_COMMON_H_
#define WOFF2_WOFF2_COMMON_H_
#include <inttypes.h>
namespace woff2 {
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
const unsigned int kWoff2FlagsContinueStream = 1 << 4;
const unsigned int kWoff2FlagsTransform = 1 << 5;
struct Point {
int x;
int y;
bool on_curve;
};
struct Table {
uint32_t tag;
uint32_t flags;
uint32_t src_offset;
uint32_t src_length;
uint32_t transform_length;
uint32_t dst_offset;
uint32_t dst_length;
const uint8_t* dst_data;
bool operator<(const Table& other) const {
return tag < other.tag;
}
};
} // namespace woff2
#endif // WOFF2_WOFF2_COMMON_H_
// Copyright 2013 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.
//
// A commandline tool for compressing ttf format files to woff2.
#include <string>
#include "file.h"
#include "./woff2_enc.h"
int main(int argc, char **argv) {
using std::string;
if (argc != 2) {
fprintf(stderr, "One argument, the input filename, must be provided.\n");
return 1;
}
string filename(argv[1]);
string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2";
fprintf(stdout, "Processing %s => %s\n",
filename.c_str(), outfilename.c_str());
string input = woff2::GetFileContent(filename);
const uint8_t* input_data = reinterpret_cast<const uint8_t*>(input.data());
size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size());
string output(output_size, 0);
uint8_t* output_data = reinterpret_cast<uint8_t*>(&output[0]);
if (!woff2::ConvertTTFToWOFF2(input_data, input.size(),
output_data, &output_size)) {
fprintf(stderr, "Compression failed.\n");
return 1;
}
output.resize(output_size);
woff2::SetFileContents(outfilename, output);
return 0;
}
// Copyright 2014 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.
//
// Library for converting WOFF2 format font files to their TTF versions.
#include "./woff2_dec.h"
#include <stdlib.h>
#include <complex>
#include <cstring>
#include <limits>
#include <string>
#include <algorithm>
#include <vector>
#include "./buffer.h"
#include "./decode.h"
#include "./round.h"
#include "./store_bytes.h"
#include "./table_tags.h"
#include "./woff2_common.h"
namespace woff2 {
namespace {
using std::string;
using std::vector;
// simple glyph flags
const int kGlyfOnCurve = 1 << 0;
const int kGlyfXShort = 1 << 1;
const int kGlyfYShort = 1 << 2;
const int kGlyfRepeat = 1 << 3;
const int kGlyfThisXIsSame = 1 << 4;
const int kGlyfThisYIsSame = 1 << 5;
// composite glyph flags
// See CompositeGlyph.java in sfntly for full definitions
const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
const int FLAG_WE_HAVE_A_SCALE = 1 << 3;
const int FLAG_MORE_COMPONENTS = 1 << 5;
const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
const size_t kSfntHeaderSize = 12;
const size_t kSfntEntrySize = 16;
const size_t kCheckSumAdjustmentOffset = 8;
const size_t kEndPtsOfContoursOffset = 10;
const size_t kCompositeGlyphBegin = 10;
// Based on section 6.1.1 of MicroType Express draft spec
bool Read255UShort(Buffer* buf, unsigned int* value) {
static const int kWordCode = 253;
static const int kOneMoreByteCode2 = 254;
static const int kOneMoreByteCode1 = 255;
static const int kLowestUCode = 253;
uint8_t code = 0;
if (!buf->ReadU8(&code)) {
return FONT_COMPRESSION_FAILURE();
}
if (code == kWordCode) {
uint16_t result = 0;
if (!buf->ReadU16(&result)) {
return FONT_COMPRESSION_FAILURE();
}
*value = result;
return true;
} else if (code == kOneMoreByteCode1) {
uint8_t result = 0;
if (!buf->ReadU8(&result)) {
return FONT_COMPRESSION_FAILURE();
}
*value = result + kLowestUCode;
return true;
} else if (code == kOneMoreByteCode2) {
uint8_t result = 0;
if (!buf->ReadU8(&result)) {
return FONT_COMPRESSION_FAILURE();
}
*value = result + kLowestUCode * 2;
return true;
} else {
*value = code;
return true;
}
}
bool ReadBase128(Buffer* buf, uint32_t* value) {
uint32_t result = 0;
for (size_t i = 0; i < 5; ++i) {
uint8_t code = 0;
if (!buf->ReadU8(&code)) {
return FONT_COMPRESSION_FAILURE();
}
// If any of the top seven bits are set then we're about to overflow.
if (result & 0xfe000000) {
return FONT_COMPRESSION_FAILURE();
}
result = (result << 7) | (code & 0x7f);
if ((code & 0x80) == 0) {
*value = result;
return true;
}
}
// Make sure not to exceed the size bound
return FONT_COMPRESSION_FAILURE();
}
int WithSign(int flag, int baseval) {
// Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
return (flag & 1) ? baseval : -baseval;
}
bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
unsigned int n_points, std::vector<Point>* result,
size_t* in_bytes_consumed) {
int x = 0;
int y = 0;
if (n_points > in_size) {
return FONT_COMPRESSION_FAILURE();
}
unsigned int triplet_index = 0;
for (unsigned int i = 0; i < n_points; ++i) {
uint8_t flag = flags_in[i];
bool on_curve = !(flag >> 7);
flag &= 0x7f;
unsigned int n_data_bytes;
if (flag < 84) {
n_data_bytes = 1;
} else if (flag < 120) {
n_data_bytes = 2;
} else if (flag < 124) {
n_data_bytes = 3;
} else {
n_data_bytes = 4;
}
if (triplet_index + n_data_bytes > in_size ||
triplet_index + n_data_bytes < triplet_index) {
return FONT_COMPRESSION_FAILURE();
}
int dx, dy;
if (flag < 10) {
dx = 0;
dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]);
} else if (flag < 20) {
dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]);
dy = 0;
} else if (flag < 84) {
int b0 = flag - 20;
int b1 = in[triplet_index];
dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4));
dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f));
} else if (flag < 120) {
int b0 = flag - 84;
dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]);
dy = WithSign(flag >> 1,
1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]);
} else if (flag < 124) {
int b2 = in[triplet_index + 1];
dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4));
dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]);
} else {
dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]);
dy = WithSign(flag >> 1,
(in[triplet_index + 2] << 8) + in[triplet_index + 3]);
}
triplet_index += n_data_bytes;
// Possible overflow but coordinate values are not security sensitive
x += dx;
y += dy;
result->push_back(Point());
Point& back = result->back();
back.x = x;
back.y = y;
back.on_curve = on_curve;
}
*in_bytes_consumed = triplet_index;
return true;
}
// This function stores just the point data. On entry, dst points to the
// beginning of a simple glyph. Returns true on success.
bool StorePoints(const std::vector<Point>& points,
unsigned int n_contours, unsigned int instruction_length,
uint8_t* dst, size_t dst_size, size_t* glyph_size) {
// I believe that n_contours < 65536, in which case this is safe. However, a
// comment and/or an assert would be good.
unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
instruction_length;
int last_flag = -1;
int repeat_count = 0;
int last_x = 0;
int last_y = 0;
unsigned int x_bytes = 0;
unsigned int y_bytes = 0;
for (unsigned int i = 0; i < points.size(); ++i) {
const Point& point = points[i];
int flag = point.on_curve ? kGlyfOnCurve : 0;
int dx = point.x - last_x;
int dy = point.y - last_y;
if (dx == 0) {
flag |= kGlyfThisXIsSame;
} else if (dx > -256 && dx < 256) {
flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
x_bytes += 1;
} else {
x_bytes += 2;
}
if (dy == 0) {
flag |= kGlyfThisYIsSame;
} else if (dy > -256 && dy < 256) {
flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
y_bytes += 1;
} else {
y_bytes += 2;
}
if (flag == last_flag && repeat_count != 255) {
dst[flag_offset - 1] |= kGlyfRepeat;
repeat_count++;
} else {
if (repeat_count != 0) {
if (flag_offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[flag_offset++] = repeat_count;
}
if (flag_offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[flag_offset++] = flag;
repeat_count = 0;
}
last_x = point.x;
last_y = point.y;
last_flag = flag;
}
if (repeat_count != 0) {
if (flag_offset >= dst_size) {
return FONT_COMPRESSION_FAILURE();
}
dst[flag_offset++] = repeat_count;
}
unsigned int xy_bytes = x_bytes + y_bytes;
if (xy_bytes < x_bytes ||
flag_offset + xy_bytes < flag_offset ||
flag_offset + xy_bytes > dst_size) {
return FONT_COMPRESSION_FAILURE();
}
int x_offset = flag_offset;
int y_offset = flag_offset + x_bytes;
last_x = 0;
last_y = 0;
for (unsigned int i = 0; i < points.size(); ++i) {
int dx = points[i].x - last_x;
if (dx == 0) {
// pass
} else if (dx > -256 && dx < 256) {
dst[x_offset++] = std::abs(dx);
} else {
// will always fit for valid input, but overflow is harmless
x_offset = Store16(dst, x_offset, dx);
}
last_x += dx;
int dy = points[i].y - last_y;
if (dy == 0) {
// pass
} else if (dy > -256 && dy < 256) {
dst[y_offset++] = std::abs(dy);
} else {
y_offset = Store16(dst, y_offset, dy);
}
last_y += dy;
}
*glyph_size = y_offset;
return true;
}
// Compute the bounding box of the coordinates, and store into a glyf buffer.
// A precondition is that there are at least 10 bytes available.
void ComputeBbox(const std::vector<Point>& points, uint8_t* dst) {
int x_min = 0;
int y_min = 0;
int x_max = 0;
int y_max = 0;
for (unsigned int i = 0; i < points.size(); ++i) {
int x = points[i].x;
int y = points[i].y;
if (i == 0 || x < x_min) x_min = x;
if (i == 0 || x > x_max) x_max = x;
if (i == 0 || y < y_min) y_min = y;
if (i == 0 || y > y_max) y_max = y;
}
size_t offset = 2;
offset = Store16(dst, offset, x_min);
offset = Store16(dst, offset, y_min);
offset = Store16(dst, offset, x_max);
offset = Store16(dst, offset, y_max);
}
// Process entire bbox stream. This is done as a separate pass to allow for
// composite bbox computations (an optional more aggressive transform).
bool ProcessBboxStream(Buffer* bbox_stream, unsigned int n_glyphs,
const std::vector<uint32_t>& loca_values, uint8_t* glyf_buf,
size_t glyf_buf_length) {
const uint8_t* buf = bbox_stream->buffer();
if (n_glyphs >= 65536 || loca_values.size() != n_glyphs + 1) {
return FONT_COMPRESSION_FAILURE();
}
// Safe because n_glyphs is bounded
unsigned int bitmap_length = ((n_glyphs + 31) >> 5) << 2;
if (!bbox_stream->Skip(bitmap_length)) {
return FONT_COMPRESSION_FAILURE();
}
for (unsigned int i = 0; i < n_glyphs; ++i) {
if (buf[i >> 3] & (0x80 >> (i & 7))) {
uint32_t loca_offset = loca_values[i];
if (loca_values[i + 1] - loca_offset < kEndPtsOfContoursOffset) {
return FONT_COMPRESSION_FAILURE();
}
if (glyf_buf_length < 2 + 10 ||
loca_offset > glyf_buf_length - 2 - 10) {
return FONT_COMPRESSION_FAILURE();
}
if (!bbox_stream->Read(glyf_buf + loca_offset + 2, 8)) {
return FONT_COMPRESSION_FAILURE();
}
}
}
return true;
}
bool ProcessComposite(Buffer* composite_stream, uint8_t* dst,
size_t dst_size, size_t* glyph_size, bool* have_instructions) {
size_t start_offset = composite_stream->offset();
bool we_have_instructions = false;
uint16_t flags = FLAG_MORE_COMPONENTS;
while (flags & FLAG_MORE_COMPONENTS) {
if (!composite_stream->ReadU16(&flags)) {
return FONT_COMPRESSION_FAILURE();
}
we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0;
size_t arg_size = 2; // glyph index
if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) {
arg_size += 4;
} else {
arg_size += 2;
}
if (flags & FLAG_WE_HAVE_A_SCALE) {
arg_size += 2;
} else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) {
arg_size += 4;
} else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) {
arg_size += 8;
}
if (!composite_stream->Skip(arg_size)) {
return FONT_COMPRESSION_FAILURE();
}
}
size_t composite_glyph_size = composite_stream->offset() - start_offset;
if (composite_glyph_size + kCompositeGlyphBegin > dst_size) {
return FONT_COMPRESSION_FAILURE();
}
Store16(dst, 0, 0xffff); // nContours = -1 for composite glyph
std::memcpy(dst + kCompositeGlyphBegin,
composite_stream->buffer() + start_offset,
composite_glyph_size);
*glyph_size = kCompositeGlyphBegin + composite_glyph_size;
*have_instructions = we_have_instructions;
return true;
}
// Build TrueType loca table
bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
uint8_t* dst, size_t dst_size) {
const uint64_t loca_size = loca_values.size();
const uint64_t offset_size = index_format ? 4 : 2;
if ((loca_size << 2) >> 2 != loca_size) {
return FONT_COMPRESSION_FAILURE();
}
if (offset_size * loca_size > dst_size) {
return FONT_COMPRESSION_FAILURE();
}
size_t offset = 0;
for (size_t i = 0; i < loca_values.size(); ++i) {
uint32_t value = loca_values[i];
if (index_format) {
offset = StoreU32(dst, offset, value);
} else {
offset = Store16(dst, offset, value >> 1);
}
}
return true;
}
// Reconstruct entire glyf table based on transformed original
bool ReconstructGlyf(const uint8_t* data, size_t data_size,
uint8_t* dst, size_t dst_size,
uint8_t* loca_buf, size_t loca_size) {
static const int kNumSubStreams = 7;
Buffer file(data, data_size);
uint32_t version;
std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
if (!file.ReadU32(&version)) {
return FONT_COMPRESSION_FAILURE();
}
uint16_t num_glyphs;
uint16_t index_format;
if (!file.ReadU16(&num_glyphs) ||
!file.ReadU16(&index_format)) {
return FONT_COMPRESSION_FAILURE();
}
unsigned int offset = (2 + kNumSubStreams) * 4;
if (offset > data_size) {
return FONT_COMPRESSION_FAILURE();
}
// Invariant from here on: data_size >= offset
for (int i = 0; i < kNumSubStreams; ++i) {
uint32_t substream_size;
if (!file.ReadU32(&substream_size)) {
return FONT_COMPRESSION_FAILURE();
}
if (substream_size > data_size - offset) {
return FONT_COMPRESSION_FAILURE();
}
substreams[i] = std::make_pair(data + offset, substream_size);
offset += substream_size;
}
Buffer n_contour_stream(substreams[0].first, substreams[0].second);
Buffer n_points_stream(substreams[1].first, substreams[1].second);
Buffer flag_stream(substreams[2].first, substreams[2].second);
Buffer glyph_stream(substreams[3].first, substreams[3].second);
Buffer composite_stream(substreams[4].first, substreams[4].second);
Buffer bbox_stream(substreams[5].first, substreams[5].second);
Buffer instruction_stream(substreams[6].first, substreams[6].second);
std::vector<uint32_t> loca_values(num_glyphs + 1);
std::vector<unsigned int> n_points_vec;
std::vector<Point> points;
uint32_t loca_offset = 0;
for (unsigned int i = 0; i < num_glyphs; ++i) {
size_t glyph_size = 0;
uint16_t n_contours = 0;
if (!n_contour_stream.ReadU16(&n_contours)) {
return FONT_COMPRESSION_FAILURE();
}
uint8_t* glyf_dst = dst + loca_offset;
size_t glyf_dst_size = dst_size - loca_offset;
if (n_contours == 0xffff) {
// composite glyph
bool have_instructions = false;
unsigned int instruction_size = 0;
if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size,
&glyph_size, &have_instructions)) {
return FONT_COMPRESSION_FAILURE();
}
if (have_instructions) {
if (!Read255UShort(&glyph_stream, &instruction_size)) {
return FONT_COMPRESSION_FAILURE();
}
if (instruction_size + 2 > glyf_dst_size - glyph_size) {
return FONT_COMPRESSION_FAILURE();
}
Store16(glyf_dst, glyph_size, instruction_size);
if (!instruction_stream.Read(glyf_dst + glyph_size + 2,
instruction_size)) {
return FONT_COMPRESSION_FAILURE();
}
glyph_size += instruction_size + 2;
}
} else if (n_contours > 0) {
// simple glyph
n_points_vec.clear();
points.clear();
unsigned int total_n_points = 0;
unsigned int n_points_contour;
for (unsigned int j = 0; j < n_contours; ++j) {
if (!Read255UShort(&n_points_stream, &n_points_contour)) {
return FONT_COMPRESSION_FAILURE();
}
n_points_vec.push_back(n_points_contour);
if (total_n_points + n_points_contour < total_n_points) {
return FONT_COMPRESSION_FAILURE();
}
total_n_points += n_points_contour;
}
unsigned int flag_size = total_n_points;
if (flag_size > flag_stream.length() - flag_stream.offset()) {
return FONT_COMPRESSION_FAILURE();
}
const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset();
const uint8_t* triplet_buf = glyph_stream.buffer() +
glyph_stream.offset();
size_t triplet_size = glyph_stream.length() - glyph_stream.offset();
size_t triplet_bytes_consumed = 0;
if (!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points,
&points, &triplet_bytes_consumed)) {
return FONT_COMPRESSION_FAILURE();
}
const uint32_t header_and_endpts_contours_size =
kEndPtsOfContoursOffset + 2 * n_contours;
if (glyf_dst_size < header_and_endpts_contours_size) {
return FONT_COMPRESSION_FAILURE();
}
Store16(glyf_dst, 0, n_contours);
ComputeBbox(points, glyf_dst);
size_t offset = kEndPtsOfContoursOffset;
int end_point = -1;
for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) {
end_point += n_points_vec[contour_ix];
if (end_point >= 65536) {
return FONT_COMPRESSION_FAILURE();
}
offset = Store16(glyf_dst, offset, end_point);
}
if (!flag_stream.Skip(flag_size)) {
return FONT_COMPRESSION_FAILURE();
}
if (!glyph_stream.Skip(triplet_bytes_consumed)) {
return FONT_COMPRESSION_FAILURE();
}
unsigned int instruction_size;
if (!Read255UShort(&glyph_stream, &instruction_size)) {
return FONT_COMPRESSION_FAILURE();
}
if (glyf_dst_size - header_and_endpts_contours_size <
instruction_size + 2) {
return FONT_COMPRESSION_FAILURE();
}
uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size;
Store16(instruction_dst, 0, instruction_size);
if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) {
return FONT_COMPRESSION_FAILURE();
}
if (!StorePoints(points, n_contours, instruction_size,
glyf_dst, glyf_dst_size, &glyph_size)) {
return FONT_COMPRESSION_FAILURE();
}
} else {
glyph_size = 0;
}
loca_values[i] = loca_offset;
if (glyph_size + 3 < glyph_size) {
return FONT_COMPRESSION_FAILURE();
}
glyph_size = Round4(glyph_size);
if (glyph_size > dst_size - loca_offset) {
// This shouldn't happen, but this test defensively maintains the
// invariant that loca_offset <= dst_size.
return FONT_COMPRESSION_FAILURE();
}
loca_offset += glyph_size;
}
loca_values[num_glyphs] = loca_offset;
if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values,
dst, dst_size)) {
return FONT_COMPRESSION_FAILURE();
}
return StoreLoca(loca_values, index_format, loca_buf, loca_size);
}
// This is linear search, but could be changed to binary because we
// do have a guarantee that the tables are sorted by tag. But the total
// cpu time is expected to be very small in any case.
const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
size_t n_tables = tables.size();
for (size_t i = 0; i < n_tables; ++i) {
if (tables[i].tag == tag) {
return &tables[i];
}
}
return NULL;
}
bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
const uint8_t* transformed_buf, size_t transformed_size,
uint8_t* dst, size_t dst_length) {
if (tag == kGlyfTableTag) {
const Table* glyf_table = FindTable(tables, tag);
const Table* loca_table = FindTable(tables, kLocaTableTag);
if (glyf_table == NULL || loca_table == NULL) {
return FONT_COMPRESSION_FAILURE();
}
if (static_cast<uint64_t>(glyf_table->dst_offset + glyf_table->dst_length) >
dst_length) {
return FONT_COMPRESSION_FAILURE();
}
if (static_cast<uint64_t>(loca_table->dst_offset + loca_table->dst_length) >
dst_length) {
return FONT_COMPRESSION_FAILURE();
}
return ReconstructGlyf(transformed_buf, transformed_size,
dst + glyf_table->dst_offset, glyf_table->dst_length,
dst + loca_table->dst_offset, loca_table->dst_length);
} else if (tag == kLocaTableTag) {
// processing was already done by glyf table, but validate
if (!FindTable(tables, kGlyfTableTag)) {
return FONT_COMPRESSION_FAILURE();
}
} else {
// transform for the tag is not known
return FONT_COMPRESSION_FAILURE();
}
return true;
}
uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
uint32_t checksum = 0;
for (size_t i = 0; i < size; i += 4) {
// We assume the addition is mod 2^32, which is valid because unsigned
checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
(buf[i + 2] << 8) | buf[i + 3];
}
return checksum;
}
bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
const Table* head_table = FindTable(tables, kHeadTableTag);
if (head_table == NULL ||
head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
return FONT_COMPRESSION_FAILURE();
}
size_t adjustment_offset = head_table->dst_offset + kCheckSumAdjustmentOffset;
StoreU32(dst, adjustment_offset, 0);
size_t n_tables = tables.size();
uint32_t file_checksum = 0;
for (size_t i = 0; i < n_tables; ++i) {
const Table* table = &tables[i];
size_t table_length = table->dst_length;
uint8_t* table_data = dst + table->dst_offset;
uint32_t checksum = ComputeChecksum(table_data, table_length);
StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
file_checksum += checksum;
}
file_checksum += ComputeChecksum(dst,
kSfntHeaderSize + kSfntEntrySize * n_tables);
uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
StoreU32(dst, adjustment_offset, checksum_adjustment);
return true;
}
bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
const uint8_t* src_buf, size_t src_size) {
size_t uncompressed_size = dst_size;
int ok = BrotliDecompressBuffer(src_size, src_buf,
&uncompressed_size, dst_buf);
if (!ok || uncompressed_size != dst_size) {
return FONT_COMPRESSION_FAILURE();
}
return true;
}
bool ReadShortDirectory(Buffer* file, std::vector<Table>* tables,
size_t num_tables) {
for (size_t i = 0; i < num_tables; ++i) {
Table* table = &(*tables)[i];
uint8_t flag_byte;
if (!file->ReadU8(&flag_byte)) {
return FONT_COMPRESSION_FAILURE();
}
uint32_t tag;
if ((flag_byte & 0x3f) == 0x3f) {
if (!file->ReadU32(&tag)) {
return FONT_COMPRESSION_FAILURE();
}
} else {
tag = kKnownTags[flag_byte & 0x3f];
}
// Bits 6 and 7 are reserved and must be 0.
if ((flag_byte & 0xC0) != 0) {
return FONT_COMPRESSION_FAILURE();
}
uint32_t flags = 0;
if (i > 0) {
flags |= kWoff2FlagsContinueStream;
}
// Always transform the glyf and loca tables
if (tag == kGlyfTableTag || tag == kLocaTableTag) {
flags |= kWoff2FlagsTransform;
}
uint32_t dst_length;
if (!ReadBase128(file, &dst_length)) {
return FONT_COMPRESSION_FAILURE();
}
uint32_t transform_length = dst_length;
if ((flags & kWoff2FlagsTransform) != 0) {
if (!ReadBase128(file, &transform_length)) {
return FONT_COMPRESSION_FAILURE();
}
}
table->tag = tag;
table->flags = flags;
table->transform_length = transform_length;
table->dst_length = dst_length;
}
return true;
}
} // namespace
size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
Buffer file(data, length);
uint32_t total_length;
if (!file.Skip(16) ||
!file.ReadU32(&total_length)) {
return 0;
}
return total_length;
}
bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
const uint8_t* data, size_t length) {
Buffer file(data, length);
uint32_t signature;
uint32_t flavor;
if (!file.ReadU32(&signature) || signature != kWoff2Signature ||
!file.ReadU32(&flavor)) {
return FONT_COMPRESSION_FAILURE();
}
// TODO(user): Should call IsValidVersionTag() here.
uint32_t reported_length;
if (!file.ReadU32(&reported_length) || length != reported_length) {
return FONT_COMPRESSION_FAILURE();
}
uint16_t num_tables;
if (!file.ReadU16(&num_tables) || !num_tables) {
return FONT_COMPRESSION_FAILURE();
}
// We don't care about these fields of the header:
// uint16_t reserved
// uint32_t total_sfnt_size
if (!file.Skip(6)) {
return FONT_COMPRESSION_FAILURE();
}
uint32_t compressed_length;
if (!file.ReadU32(&compressed_length)) {
return FONT_COMPRESSION_FAILURE();
}
// We don't care about these fields of the header:
// uint16_t major_version, minor_version
// uint32_t meta_offset, meta_length, meta_orig_length
// uint32_t priv_offset, priv_length
if (!file.Skip(24)) {
return FONT_COMPRESSION_FAILURE();
}
std::vector<Table> tables(num_tables);
// Note: change below to ReadLongDirectory to enable long format.
if (!ReadShortDirectory(&file, &tables, num_tables)) {
return FONT_COMPRESSION_FAILURE();
}
uint64_t src_offset = file.offset();
uint64_t dst_offset = kSfntHeaderSize +
kSfntEntrySize * static_cast<uint64_t>(num_tables);
uint64_t uncompressed_sum = 0;
for (uint16_t i = 0; i < num_tables; ++i) {
Table* table = &tables[i];
table->src_offset = src_offset;
table->src_length = (i == 0 ? compressed_length : 0);
src_offset += table->src_length;
if (src_offset > std::numeric_limits<uint32_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
src_offset = Round4(src_offset); // TODO: reconsider
table->dst_offset = dst_offset;
dst_offset += table->dst_length;
if (dst_offset > std::numeric_limits<uint32_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
dst_offset = Round4(dst_offset);
uncompressed_sum += table->src_length;
if (uncompressed_sum > std::numeric_limits<uint32_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
}
// Enforce same 30M limit on uncompressed tables as OTS
if (uncompressed_sum > 30 * 1024 * 1024) {
return FONT_COMPRESSION_FAILURE();
}
if (src_offset > length || dst_offset > result_length) {
return FONT_COMPRESSION_FAILURE();
}
const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
if (sfnt_header_and_table_directory_size > result_length) {
return FONT_COMPRESSION_FAILURE();
}
// Start building the font
size_t offset = 0;
offset = StoreU32(result, offset, flavor);
offset = Store16(result, offset, num_tables);
unsigned max_pow2 = 0;
while (1u << (max_pow2 + 1) <= num_tables) {
max_pow2++;
}
const uint16_t output_search_range = (1u << max_pow2) << 4;
offset = Store16(result, offset, output_search_range);
offset = Store16(result, offset, max_pow2);
offset = Store16(result, offset, (num_tables << 4) - output_search_range);
// sort tags in the table directory in ascending alphabetical order
std::vector<Table> sorted_tables(tables);
std::sort(sorted_tables.begin(), sorted_tables.end());
for (uint16_t i = 0; i < num_tables; ++i) {
const Table* table = &sorted_tables[i];
offset = StoreU32(result, offset, table->tag);
offset = StoreU32(result, offset, 0); // checksum, to fill in later
offset = StoreU32(result, offset, table->dst_offset);
offset = StoreU32(result, offset, table->dst_length);
}
std::vector<uint8_t> uncompressed_buf;
bool continue_valid = false;
const uint8_t* transform_buf = NULL;
for (uint16_t i = 0; i < num_tables; ++i) {
const Table* table = &tables[i];
uint32_t flags = table->flags;
const uint8_t* src_buf = data + table->src_offset;
size_t transform_length = table->transform_length;
if ((flags & kWoff2FlagsContinueStream) != 0) {
if (!continue_valid) {
return FONT_COMPRESSION_FAILURE();
}
} else if ((flags & kWoff2FlagsContinueStream) == 0) {
uint64_t total_size = transform_length;
for (uint16_t j = i + 1; j < num_tables; ++j) {
if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) {
break;
}
total_size += tables[j].transform_length;
if (total_size > std::numeric_limits<uint32_t>::max()) {
return FONT_COMPRESSION_FAILURE();
}
}
uncompressed_buf.resize(total_size);
if (!Woff2Uncompress(&uncompressed_buf[0], total_size,
src_buf, compressed_length)) {
return FONT_COMPRESSION_FAILURE();
}
transform_buf = &uncompressed_buf[0];
continue_valid = true;
} else {
return FONT_COMPRESSION_FAILURE();
}
if ((flags & kWoff2FlagsTransform) == 0) {
if (transform_length != table->dst_length) {
return FONT_COMPRESSION_FAILURE();
}
if (static_cast<uint64_t>(table->dst_offset + transform_length) >
result_length) {
return FONT_COMPRESSION_FAILURE();
}
std::memcpy(result + table->dst_offset, transform_buf,
transform_length);
} else {
if (!ReconstructTransformed(tables, table->tag,
transform_buf, transform_length, result, result_length)) {
return FONT_COMPRESSION_FAILURE();
}
}
if (continue_valid) {
transform_buf += transform_length;
if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) {
return FONT_COMPRESSION_FAILURE();
}
}
}
return FixChecksums(sorted_tables, result);
}
} // namespace woff2
// Copyright 2014 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.
//
// Library for converting WOFF2 format font files to their TTF versions.
#ifndef WOFF2_WOFF2_DEC_H_
#define WOFF2_WOFF2_DEC_H_
#include <stddef.h>
#include <inttypes.h>
namespace woff2 {
// Compute the size of the final uncompressed font, or 0 on error.
size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length);
// Decompresses the font into the target buffer. The result_length should
// be the same as determined by ComputeFinalSize(). Returns true on successful
// decompression.
bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
const uint8_t *data, size_t length);
} // namespace woff2
#endif // WOFF2_WOFF2_DEC_H_
// Copyright 2013 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.
//
// A very simple commandline tool for decompressing woff2 format files to true
// type font files.
#include <string>
#include "file.h"
#include "./woff2_dec.h"
int main(int argc, char **argv) {
using std::string;
if (argc != 2) {
fprintf(stderr, "One argument, the input filename, must be provided.\n");
return 1;
}
string filename(argv[1]);
string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf";
fprintf(stdout, "Processing %s => %s\n",
filename.c_str(), outfilename.c_str());
string input = woff2::GetFileContent(filename);
size_t decompressed_size = woff2::ComputeWOFF2FinalSize(
reinterpret_cast<const uint8_t*>(input.data()), input.size());
string output(decompressed_size, 0);
const bool ok = woff2::ConvertWOFF2ToTTF(
reinterpret_cast<uint8_t*>(&output[0]), decompressed_size,
reinterpret_cast<const uint8_t*>(input.data()), input.size());
if (!ok) {
fprintf(stderr, "Decompression failed\n");
return 1;
}
woff2::SetFileContents(outfilename, output);
return 0;
}
// Copyright 2014 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.
//
// Library for converting TTF format font files to their WOFF2 versions.
#include "./woff2_enc.h"
#include <stdlib.h>
#include <complex>
#include <cstring>
#include <limits>
#include <string>
#include <vector>
#include "./buffer.h"
#include "./encode.h"
#include "./font.h"
#include "./normalize.h"
#include "./round.h"
#include "./store_bytes.h"
#include "./table_tags.h"
#include "./transform.h"
#include "./woff2_common.h"
namespace woff2 {
namespace {
using std::string;
using std::vector;
const size_t kWoff2HeaderSize = 48;
const size_t kWoff2EntrySize = 20;
size_t Base128Size(size_t n) {
size_t size = 1;
for (; n >= 128; n >>= 7) ++size;
return size;
}
void StoreBase128(size_t len, size_t* offset, uint8_t* dst) {
size_t size = Base128Size(len);
for (int i = 0; i < size; ++i) {
int b = static_cast<int>((len >> (7 * (size - i - 1))) & 0x7f);
if (i < size - 1) {
b |= 0x80;
}
dst[(*offset)++] = b;
}
}
bool Compress(const uint8_t* data, const size_t len,
uint8_t* result, uint32_t* result_len,
brotli::BrotliParams::Mode mode) {
size_t compressed_len = *result_len;
brotli::BrotliParams params;
params.mode = mode;
if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result)
== 0) {
return false;
}
*result_len = compressed_len;
return true;
}
bool Woff2Compress(const uint8_t* data, const size_t len,
uint8_t* result, uint32_t* result_len) {
return Compress(data, len, result, result_len,
brotli::BrotliParams::MODE_FONT);
}
bool TextCompress(const uint8_t* data, const size_t len,
uint8_t* result, uint32_t* result_len) {
return Compress(data, len, result, result_len,
brotli::BrotliParams::MODE_TEXT);
}
bool ReadLongDirectory(Buffer* file, std::vector<Table>* tables,
size_t num_tables) {
for (size_t i = 0; i < num_tables; ++i) {
Table* table = &(*tables)[i];
if (!file->ReadU32(&table->tag) ||
!file->ReadU32(&table->flags) ||
!file->ReadU32(&table->src_length) ||
!file->ReadU32(&table->transform_length) ||
!file->ReadU32(&table->dst_length)) {
return FONT_COMPRESSION_FAILURE();
}
}
return true;
}
int KnownTableIndex(uint32_t tag) {
for (int i = 0; i < 63; ++i) {
if (tag == kKnownTags[i]) return i;
}
return 63;
}
void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) {
uint8_t flag_byte = KnownTableIndex(table.tag);
dst[(*offset)++] = flag_byte;
// The index here is treated as a set of flag bytes because
// bits 6 and 7 of the byte are reserved for future use as flags.
// 0x3f or 63 means an arbitrary table tag.
if ((flag_byte & 0x3f) == 0x3f) {
StoreU32(table.tag, offset, dst);
}
StoreBase128(table.src_length, offset, dst);
if ((table.flags & kWoff2FlagsTransform) != 0) {
StoreBase128(table.transform_length, offset, dst);
}
}
size_t TableEntrySize(const Table& table) {
uint8_t flag_byte = KnownTableIndex(table.tag);
size_t size = ((flag_byte & 0x3f) != 0x3f) ? 1 : 5;
size += Base128Size(table.src_length);
if ((table.flags & kWoff2FlagsTransform) != 0) {
size += Base128Size(table.transform_length);
}
return size;
}
size_t ComputeWoff2Length(const std::vector<Table>& tables,
size_t extended_metadata_length) {
size_t size = kWoff2HeaderSize;
for (const auto& table : tables) {
size += TableEntrySize(table);
}
for (const auto& table : tables) {
size += table.dst_length;
size = Round4(size);
}
size += extended_metadata_length;
return size;
}
size_t ComputeTTFLength(const std::vector<Table>& tables) {
size_t size = 12 + 16 * tables.size(); // sfnt header
for (const auto& table : tables) {
size += Round4(table.src_length);
}
return size;
}
size_t ComputeTotalTransformLength(const Font& font) {
size_t total = 0;
for (const auto& i : font.tables) {
const Font::Table& table = i.second;
if (table.tag & 0x80808080 || !font.FindTable(table.tag ^ 0x80808080)) {
// Count transformed tables and non-transformed tables that do not have
// transformed versions.
total += table.length;
}
}
return total;
}
} // namespace
size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length) {
return MaxWOFF2CompressedSize(data, length, "");
}
size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length,
const string& extended_metadata) {
// Except for the header size, which is 32 bytes larger in woff2 format,
// all other parts should be smaller (table header in short format,
// transformations and compression). Just to be sure, we will give some
// headroom anyway.
return length + 1024 + extended_metadata.length();
}
uint32_t CompressedBufferSize(uint32_t original_size) {
return 1.2 * original_size + 10240;
}
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
uint8_t *result, size_t *result_length) {
return ConvertTTFToWOFF2(data, length, result, result_length, "");
}
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
uint8_t *result, size_t *result_length,
const string& extended_metadata) {
Font font;
if (!ReadFont(data, length, &font)) {
fprintf(stderr, "Parsing of the input font failed.\n");
return false;
}
if (!NormalizeFont(&font)) {
fprintf(stderr, "Font normalization failed.\n");
return false;
}
if (!TransformGlyfAndLocaTables(&font)) {
fprintf(stderr, "Font transformation failed.\n");
return false;
}
const Font::Table* head_table = font.FindTable(kHeadTableTag);
if (head_table == NULL) {
fprintf(stderr, "Missing head table.\n");
return false;
}
// Although the compressed size of each table in the final woff2 file won't
// be larger than its transform_length, we have to allocate a large enough
// buffer for the compressor, since the compressor can potentially increase
// the size. If the compressor overflows this, it should return false and
// then this function will also return false.
size_t total_transform_length = ComputeTotalTransformLength(font);
size_t compression_buffer_size = CompressedBufferSize(total_transform_length);
std::vector<uint8_t> compression_buf(compression_buffer_size);
uint32_t total_compressed_length = compression_buffer_size;
// Collect all transformed data into one place.
std::vector<uint8_t> transform_buf(total_transform_length);
size_t transform_offset = 0;
for (const auto& i : font.tables) {
if (i.second.tag & 0x80808080) continue;
const Font::Table* table = font.FindTable(i.second.tag ^ 0x80808080);
if (table == NULL) table = &i.second;
StoreBytes(table->data, table->length,
&transform_offset, &transform_buf[0]);
}
// Compress all transformed data in one stream.
if (!Woff2Compress(transform_buf.data(), total_transform_length,
&compression_buf[0],
&total_compressed_length)) {
fprintf(stderr, "Compression of combined table failed.\n");
return false;
}
// Compress the extended metadata
uint32_t compressed_metadata_buf_length =
CompressedBufferSize(extended_metadata.length());
std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length);
if (extended_metadata.length() > 0) {
if (!TextCompress((const uint8_t*)extended_metadata.data(),
extended_metadata.length(),
compressed_metadata_buf.data(),
&compressed_metadata_buf_length)) {
fprintf(stderr, "Compression of extended metadata failed.\n");
return false;
}
} else {
compressed_metadata_buf_length = 0;
}
std::vector<Table> tables;
for (const auto& i : font.tables) {
const Font::Table& src_table = i.second;
if (src_table.tag & 0x80808080) {
// This is a transformed table, we will write it together with the
// original version.
continue;
}
Table table;
table.tag = src_table.tag;
table.flags = 0;
table.src_length = src_table.length;
table.transform_length = src_table.length;
const uint8_t* transformed_data = src_table.data;
const Font::Table* transformed_table =
font.FindTable(src_table.tag ^ 0x80808080);
if (transformed_table != NULL) {
table.flags |= kWoff2FlagsTransform;
table.transform_length = transformed_table->length;
transformed_data = transformed_table->data;
}
if (tables.empty()) {
table.dst_length = total_compressed_length;
table.dst_data = &compression_buf[0];
} else {
table.dst_length = 0;
table.dst_data = NULL;
table.flags |= kWoff2FlagsContinueStream;
}
tables.push_back(table);
}
size_t woff2_length =
ComputeWoff2Length(tables, compressed_metadata_buf_length);
if (woff2_length > *result_length) {
fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n",
*result_length, woff2_length);
return false;
}
*result_length = woff2_length;
size_t offset = 0;
StoreU32(kWoff2Signature, &offset, result);
StoreU32(font.flavor, &offset, result);
StoreU32(woff2_length, &offset, result);
Store16(tables.size(), &offset, result);
Store16(0, &offset, result); // reserved
StoreU32(ComputeTTFLength(tables), &offset, result);
StoreU32(total_compressed_length, &offset, result);
StoreBytes(head_table->data + 4, 4, &offset, result); // font revision
if (compressed_metadata_buf_length > 0) {
StoreU32(woff2_length - compressed_metadata_buf_length,
&offset, result); // metaOffset
StoreU32(compressed_metadata_buf_length, &offset, result); // metaLength
StoreU32(extended_metadata.length(), &offset, result); // metaOrigLength
} else {
StoreU32(0, &offset, result); // metaOffset
StoreU32(0, &offset, result); // metaLength
StoreU32(0, &offset, result); // metaOrigLength
}
StoreU32(0, &offset, result); // privOffset
StoreU32(0, &offset, result); // privLength
for (const auto& table : tables) {
StoreTableEntry(table, &offset, result);
}
for (const auto& table : tables) {
StoreBytes(table.dst_data, table.dst_length, &offset, result);
offset = Round4(offset);
}
StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length,
&offset, result);
if (*result_length != offset) {
fprintf(stderr, "Mismatch between computed and actual length "
"(%zd vs %zd)\n", *result_length, offset);
return false;
}
return true;
}
} // namespace woff2
// Copyright 2014 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.
//
// Library for converting WOFF2 format font files to their TTF versions.
#ifndef WOFF2_WOFF2_ENC_H_
#define WOFF2_WOFF2_ENC_H_
#include <stddef.h>
#include <inttypes.h>
#include <string>
using std::string;
namespace woff2 {
// Returns an upper bound on the size of the compressed file.
size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length);
size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length,
const string& extended_metadata);
// Compresses the font into the target buffer. *result_length should be at least
// the value returned by MaxWOFF2CompressedSize(), upon return, it is set to the
// actual compressed size. Returns true on successful compression.
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
uint8_t *result, size_t *result_length);
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
uint8_t *result, size_t *result_length,
const string& extended_metadata);
} // namespace woff2
#endif // WOFF2_WOFF2_ENC_H_
# Copyright 2015 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
{
'targets': [
{
'target_name': 'woff2_dec',
'type': 'static_library',
'include_dirs': [
'src',
'<(DEPTH)/third_party/brotli/dec',
],
'dependencies': [
'<(DEPTH)/third_party/brotli/brotli.gyp:brotli',
],
'sources': [
'src/buffer.h',
'src/round.h',
'src/store_bytes.h',
'src/table_tags.cc',
'src/table_tags.h',
'src/woff2_common.h',
'src/woff2_dec.cc',
'src/woff2_dec.h',
],
# TODO(ksakamoto): http://crbug.com/167187
'msvs_disabled_warnings': [
4267,
],
},
],
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment