pax_global_header00006660000000000000000000000064132023624000014502gustar00rootroot0000000000000052 comment=1bccf208bca986e53a647dfe4811322adb06ecf8 woff2-1.0.2/000077500000000000000000000000001320236240000125255ustar00rootroot00000000000000woff2-1.0.2/.gitignore000066400000000000000000000000661320236240000145170ustar00rootroot00000000000000*.o *.a /woff2_compress /woff2_decompress /woff2_info woff2-1.0.2/.gitmodules000066400000000000000000000001201320236240000146730ustar00rootroot00000000000000[submodule "brotli"] path = brotli url = https://github.com/google/brotli.git woff2-1.0.2/CMakeLists.txt000066400000000000000000000214511320236240000152700ustar00rootroot00000000000000# Copyright 2017 Igalia S.L. All Rights Reserved. # # Distributed under MIT license. # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT # Ubuntu 12.04 LTS has CMake 2.8.7, and is an important target since # several CI services, such as Travis and Drone, use it. Solaris 11 # has 2.8.6, and it's not difficult to support if you already have to # support 2.8.7. cmake_minimum_required(VERSION 2.8.6) project(woff2) include(GNUInstallDirs) # Build options option(BUILD_SHARED_LIBS "Build shared libraries" ON) option(CANONICAL_PREFIXES "Canonical prefixes" OFF) option(NOISY_LOGGING "Noisy logging" ON) # Version information set(WOFF2_VERSION 1.0.2) # When building shared libraries it is important to set the correct rpath # See https://cmake.org/Wiki/CMake_RPATH_handling#Always_full_RPATH set(CMAKE_SKIP_BUILD_RPATH FALSE) set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" isSystemDir) if ("${isSystemDir}" STREQUAL "-1") set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") endif() # Find Brotli dependencies set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") find_package(BrotliDec) if (NOT BROTLIDEC_FOUND) message(FATAL_ERROR "librotlidec is needed to build woff2.") endif () find_package(BrotliEnc) if (NOT BROTLIENC_FOUND) message(FATAL_ERROR "librotlienc is needed to build woff2.") endif () # Set compiler flags if (NOT CANONICAL_PREFIXES) add_definitions(-no-canonical-prefixes) endif () if (NOISY_LOGGING) add_definitions(-DFONT_COMPRESSION_BIN) endif () add_definitions(-D__STDC_FORMAT_MACROS) set(COMMON_FLAGS -fno-omit-frame-pointer) if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") add_definitions(-DOS_MACOSX) else () set(COMMON_FLAGS "${COMMON_FLAG} -fno-omit-frame-pointer") endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAG}") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAG}") set(CMAKE_CXX_STANDARD 11) # Set search path for our private/public headers as well as Brotli headers include_directories("src" "include" "${BROTLIDEC_INCLUDE_DIRS}" "${BROTLIENC_INCLUDE_DIRS}") # Common part used by decoder and encoder add_library(woff2common src/table_tags.cc src/variable_length.cc src/woff2_common.cc) # WOFF2 Decoder add_library(woff2dec src/woff2_dec.cc src/woff2_out.cc) target_link_libraries(woff2dec woff2common "${BROTLIDEC_LIBRARIES}") add_executable(woff2_decompress src/woff2_decompress.cc) target_link_libraries(woff2_decompress woff2dec) # WOFF2 Encoder add_library(woff2enc src/font.cc src/glyph.cc src/normalize.cc src/transform.cc src/woff2_enc.cc) target_link_libraries(woff2enc woff2common "${BROTLIENC_LIBRARIES}") add_executable(woff2_compress src/woff2_compress.cc) target_link_libraries(woff2_compress woff2enc) # WOFF2 info add_executable(woff2_info src/woff2_info.cc) target_link_libraries(woff2_info woff2common) foreach(lib woff2common woff2dec woff2enc) set_target_properties(${lib} PROPERTIES SOVERSION ${WOFF2_VERSION} VERSION ${WOFF2_VERSION} POSITION_INDEPENDENT_CODE TRUE) endforeach() # Fuzzer libraries add_library(convert_woff2ttf_fuzzer STATIC src/convert_woff2ttf_fuzzer.cc) target_link_libraries(convert_woff2ttf_fuzzer woff2dec) add_library(convert_woff2ttf_fuzzer_new_entry STATIC src/convert_woff2ttf_fuzzer_new_entry.cc) target_link_libraries(convert_woff2ttf_fuzzer_new_entry woff2dec) # PC files include(CMakeParseArguments) function(generate_pkg_config_path outvar path) string(LENGTH "${path}" path_length) set(path_args ${ARGV}) list(REMOVE_AT path_args 0 1) list(LENGTH path_args path_args_remaining) set("${outvar}" "${path}") while(path_args_remaining GREATER 1) list(GET path_args 0 name) list(GET path_args 1 value) get_filename_component(value_full "${value}" ABSOLUTE) string(LENGTH "${value}" value_length) if(path_length EQUAL value_length AND path STREQUAL value) set("${outvar}" "\${${name}}") break() elseif(path_length GREATER value_length) # We might be in a subdirectory of the value, but we have to be # careful about a prefix matching but not being a subdirectory # (for example, /usr/lib64 is not a subdirectory of /usr/lib). # We'll do this by making sure the next character is a directory # separator. string(SUBSTRING "${path}" ${value_length} 1 sep) if(sep STREQUAL "/") string(SUBSTRING "${path}" 0 ${value_length} s) if(s STREQUAL value) string(SUBSTRING "${path}" "${value_length}" -1 suffix) set("${outvar}" "\${${name}}${suffix}") break() endif() endif() endif() list(REMOVE_AT path_args 0 1) list(LENGTH path_args path_args_remaining) endwhile() set("${outvar}" "${${outvar}}" PARENT_SCOPE) endfunction(generate_pkg_config_path) function(generate_pkg_config output_file) set (options) set (oneValueArgs NAME DESCRIPTION URL VERSION PREFIX LIBDIR INCLUDEDIR) set (multiValueArgs DEPENDS DEPENDS_PRIVATE CFLAGS LIBRARIES) cmake_parse_arguments(GEN_PKG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) unset (options) unset (oneValueArgs) unset (multiValueArgs) if(NOT GEN_PKG_PREFIX) set(GEN_PKG_PREFIX "${CMAKE_INSTALL_PREFIX}") endif() if(NOT GEN_PKG_LIBDIR) set(GEN_PKG_LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") endif() generate_pkg_config_path(GEN_PKG_LIBDIR "${GEN_PKG_LIBDIR}" prefix "${GEN_PKG_PREFIX}") if(NOT GEN_PKG_INCLUDEDIR) set(GEN_PKG_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") endif() generate_pkg_config_path(GEN_PKG_INCLUDEDIR "${GEN_PKG_INCLUDEDIR}" prefix "${GEN_PKG_PREFIX}") file(WRITE "${output_file}" "prefix=${GEN_PKG_PREFIX}\n") file(APPEND "${output_file}" "libdir=${GEN_PKG_LIBDIR}\n") file(APPEND "${output_file}" "includedir=${GEN_PKG_INCLUDEDIR}\n") file(APPEND "${output_file}" "\n") if(GEN_PKG_NAME) file(APPEND "${output_file}" "Name: ${GEN_PKG_NAME}\n") else() file(APPEND "${output_file}" "Name: ${CMAKE_PROJECT_NAME}\n") endif() if(GEN_PKG_DESCRIPTION) file(APPEND "${output_file}" "Description: ${GEN_PKG_DESCRIPTION}\n") endif() if(GEN_PKG_URL) file(APPEND "${output_file}" "URL: ${GEN_PKG_URL}\n") endif() if(GEN_PKG_VERSION) file(APPEND "${output_file}" "Version: ${GEN_PKG_VERSION}\n") endif() if(GEN_PKG_DEPENDS) file(APPEND "${output_file}" "Requires: ${GEN_PKG_DEPENDS}\n") endif() if(GEN_PKG_DEPENDS_PRIVATE) file(APPEND "${output_file}" "Requires.private:") foreach(lib ${GEN_PKG_DEPENDS_PRIVATE}) file(APPEND "${output_file}" " ${lib}") endforeach() file(APPEND "${output_file}" "\n") endif() if(GEN_PKG_LIBRARIES) set(libs) file(APPEND "${output_file}" "Libs: -L\${libdir}") foreach(lib ${GEN_PKG_LIBRARIES}) file(APPEND "${output_file}" " -l${lib}") endforeach() file(APPEND "${output_file}" "\n") endif() file(APPEND "${output_file}" "Cflags: -I\${includedir}") if(GEN_PKG_CFLAGS) foreach(cflag ${GEN_PKG_CFLAGS}) file(APPEND "${output_file}" " ${cflag}") endforeach() endif() file(APPEND "${output_file}" "\n") endfunction(generate_pkg_config) generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2common.pc" NAME libwoff2common DESCRIPTION "Shared data used by libwoff2 and libwoff2dec libraries" URL "https://github.com/google/woff2" VERSION "${WOFF2_VERSION}" LIBRARIES woff2common) generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2dec.pc" NAME libwoff2dec DESCRIPTION "WOFF2 decoder library" URL "https://github.com/google/woff2" VERSION "${WOFF2_VERSION}" DEPENDS libbrotlidec DEPENDS_PRIVATE libwoff2common LIBRARIES woff2dec) generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2enc.pc" NAME libwoff2enc DESCRIPTION "WOFF2 encoder library" URL "https://github.com/google/woff2" VERSION "${WOFF2_VERSION}" DEPENDS libbrotlienc DEPENDS_PRIVATE libwoff2common LIBRARIES woff2enc) # Installation if (NOT BUILD_SHARED_LIBS) install( TARGETS woff2_decompress woff2_compress woff2_info RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) endif() install( TARGETS woff2common woff2dec woff2enc ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ) install( DIRECTORY include/woff2 DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" ) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2common.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2dec.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2enc.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") woff2-1.0.2/CONTRIBUTING.md000066400000000000000000000026541320236240000147650ustar00rootroot00000000000000Want to contribute? Great! First, read this page (including the small print at the end). ### Before you contribute Before we can use your code, you must sign the [Google Individual Contributor License Agreement] (https://cla.developers.google.com/about/google-individual) (CLA), which you can do online. The CLA is necessary mainly because you own the copyright to your changes, even after your contribution becomes part of our codebase, so we need your permission to use and distribute your code. We also need to be sure of various other things—for instance that you'll tell us if you know that your code infringes on other people's patents. You don't have to sign the CLA until after you've submitted your code for review and a member has approved it, but you must do it before we can put your code into our codebase. Before you start working on a larger contribution, you should get in touch with us first through the issue tracker with your idea so that we can help out and possibly guide you. Coordinating up front makes it much easier to avoid frustration later on. ### Code reviews All submissions, including submissions by project members, require review. We use Github pull requests for this purpose. ### The small print Contributions made by corporations are covered by a different agreement than the one above, the [Software Grant and Corporate Contributor License Agreement] (https://cla.developers.google.com/about/google-corporate). woff2-1.0.2/LICENSE000066400000000000000000000020571320236240000135360ustar00rootroot00000000000000Copyright (c) 2013-2017 by the WOFF2 Authors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. woff2-1.0.2/Makefile000066400000000000000000000031711320236240000141670ustar00rootroot00000000000000OS := $(shell uname) CPPFLAGS = -I./brotli/c/include/ -I./src -I./include AR ?= ar CC ?= gcc CXX ?= g++ # It's helpful to be able to turn these off for fuzzing CANONICAL_PREFIXES ?= -no-canonical-prefixes NOISY_LOGGING ?= -DFONT_COMPRESSION_BIN COMMON_FLAGS = -fno-omit-frame-pointer $(CANONICAL_PREFIXES) $(NOISY_LOGGING) -D __STDC_FORMAT_MACROS ARFLAGS = crf ifeq ($(OS), Darwin) CPPFLAGS += -DOS_MACOSX ARFLAGS = cr else COMMON_FLAGS += -fno-tree-vrp endif CFLAGS += $(COMMON_FLAGS) CXXFLAGS += $(COMMON_FLAGS) -std=c++11 SRCDIR = src OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \ woff2_dec.o woff2_enc.o woff2_common.o woff2_out.o \ variable_length.o BROTLI = brotli BROTLIOBJ = $(BROTLI)/bin/obj/c ENCOBJ = $(BROTLIOBJ)/enc/*.o DECOBJ = $(BROTLIOBJ)/dec/*.o COMMONOBJ = $(BROTLIOBJ)/common/*.o OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ)) EXECUTABLES=woff2_compress woff2_decompress woff2_info EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES)) ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry ARCHIVE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(ARCHIVES)) ifeq (,$(wildcard $(BROTLI)/*)) $(error Brotli dependency not found : you must initialize the Git submodule) endif all : $(OBJS) $(EXECUTABLES) $(ARCHIVES) $(ARCHIVES) : $(ARCHIVE_OBJS) $(OBJS) deps $(AR) $(ARFLAGS) $(SRCDIR)/$@.a $(OBJS) \ $(COMMONOBJ) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o $(EXECUTABLES) : $(EXE_OBJS) deps $(CXX) $(LFLAGS) $(OBJS) $(COMMONOBJ) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o -o $@ deps : $(MAKE) -C $(BROTLI) lib clean : rm -f $(OBJS) $(EXE_OBJS) $(EXECUTABLES) $(MAKE) -C $(BROTLI) clean woff2-1.0.2/README.md000066400000000000000000000032731320236240000140110ustar00rootroot00000000000000This 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 the MIT 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. The build process depends on the g++ compiler. ## Build On a standard Unix-style environment: ``` git clone --recursive https://github.com/google/woff2.git cd woff2 make clean all ``` Alternatively, if Brotli is already installed on your system you can use CMake to build executables and libraries: ``` git clone https://github.com/google/woff2.git cd woff2 mkdir out cd out cmake .. make make install ``` By default, shared libraries are built. To use static linkage, do: ``` cd woff2 mkdir out-static cmake -DBUILD_SHARED_LIBS=OFF .. make make install ``` ## Run Ensure the binaries from the build process are in your $PATH, then: ``` 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. woff2-1.0.2/brotli/000077500000000000000000000000001320236240000140205ustar00rootroot00000000000000woff2-1.0.2/cmake/000077500000000000000000000000001320236240000136055ustar00rootroot00000000000000woff2-1.0.2/cmake/FindBrotliDec.cmake000066400000000000000000000016311320236240000172600ustar00rootroot00000000000000# Copyright 2017 Igalia S.L. All Rights Reserved. # # Distributed under MIT license. # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT # Try to find BrotliDec. Once done, this will define # # BROTLIDEC_FOUND - system has BrotliDec. # BROTLIDEC_INCLUDE_DIRS - the BrotliDec include directories # BROTLIDEC_LIBRARIES - link these to use BrotliDec. find_package(PkgConfig) pkg_check_modules(PC_BROTLIDEC libbrotlidec) find_path(BROTLIDEC_INCLUDE_DIRS NAMES brotli/decode.h HINTS ${PC_BROTLIDEC_INCLUDEDIR} ) find_library(BROTLIDEC_LIBRARIES NAMES brotlidec HINTS ${PC_BROTLIDEC_LIBDIR} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(BrotliDec REQUIRED_VARS BROTLIDEC_INCLUDE_DIRS BROTLIDEC_LIBRARIES FOUND_VAR BROTLIDEC_FOUND VERSION_VAR PC_BROTLIDEC_VERSION) mark_as_advanced( BROTLIDEC_INCLUDE_DIRS BROTLIDEC_LIBRARIES ) woff2-1.0.2/cmake/FindBrotliEnc.cmake000066400000000000000000000016311320236240000172720ustar00rootroot00000000000000# Copyright 2017 Igalia S.L. All Rights Reserved. # # Distributed under MIT license. # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT # Try to find BrotliEnc. Once done, this will define # # BROTLIENC_FOUND - system has BrotliEnc. # BROTLIENC_INCLUDE_DIRS - the BrotliEnc include directories # BROTLIENC_LIBRARIES - link these to use BrotliEnc. find_package(PkgConfig) pkg_check_modules(PC_BROTLIENC libbrotlienc) find_path(BROTLIENC_INCLUDE_DIRS NAMES brotli/encode.h HINTS ${PC_BROTLIENC_INCLUDEDIR} ) find_library(BROTLIENC_LIBRARIES NAMES brotlienc HINTS ${PC_BROTLIENC_LIBDIR} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(BrotliEnc REQUIRED_VARS BROTLIENC_INCLUDE_DIRS BROTLIENC_LIBRARIES FOUND_VAR BROTLIENC_FOUND VERSION_VAR PC_BROTLIENC_VERSION) mark_as_advanced( BROTLIENC_INCLUDE_DIRS BROTLIENC_LIBRARIES ) woff2-1.0.2/include/000077500000000000000000000000001320236240000141505ustar00rootroot00000000000000woff2-1.0.2/include/woff2/000077500000000000000000000000001320236240000151735ustar00rootroot00000000000000woff2-1.0.2/include/woff2/decode.h000066400000000000000000000022231320236240000165660ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Library for converting WOFF2 format font files to their TTF versions. */ #ifndef WOFF2_WOFF2_DEC_H_ #define WOFF2_WOFF2_DEC_H_ #include #include #include 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. // DEPRECATED; please prefer the version that takes a WOFF2Out* bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, const uint8_t *data, size_t length); // Decompresses the font into out. Returns true on success. // Works even if WOFF2Header totalSfntSize is wrong. // Please prefer this API. bool ConvertWOFF2ToTTF(const uint8_t *data, size_t length, WOFF2Out* out); } // namespace woff2 #endif // WOFF2_WOFF2_DEC_H_ woff2-1.0.2/include/woff2/encode.h000066400000000000000000000026321320236240000166040ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Library for converting WOFF2 format font files to their TTF versions. */ #ifndef WOFF2_WOFF2_ENC_H_ #define WOFF2_WOFF2_ENC_H_ #include #include #include namespace woff2 { struct WOFF2Params { WOFF2Params() : extended_metadata(""), brotli_quality(11), allow_transforms(true) {} std::string extended_metadata; int brotli_quality; bool allow_transforms; }; // 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 std::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 WOFF2Params& params); } // namespace woff2 #endif // WOFF2_WOFF2_ENC_H_ woff2-1.0.2/include/woff2/output.h000066400000000000000000000044001320236240000167020ustar00rootroot00000000000000/* Copyright 2016 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Output buffer for WOFF2 decompression. */ #ifndef WOFF2_WOFF2_OUT_H_ #define WOFF2_WOFF2_OUT_H_ #include #include #include #include namespace woff2 { // Suggested max size for output. const size_t kDefaultMaxSize = 30 * 1024 * 1024; /** * Output interface for the woff2 decoding. * * Writes to arbitrary offsets are supported to facilitate updating offset * table and checksums after tables are ready. Reading the current size is * supported so a 'loca' table can be built up while writing glyphs. * * By default limits size to kDefaultMaxSize. */ class WOFF2Out { public: virtual ~WOFF2Out(void) {} // Append n bytes of data from buf. // Return true if all written, false otherwise. virtual bool Write(const void *buf, size_t n) = 0; // Write n bytes of data from buf at offset. // Return true if all written, false otherwise. virtual bool Write(const void *buf, size_t offset, size_t n) = 0; virtual size_t Size() = 0; }; /** * Expanding memory block for woff2 out. By default limited to kDefaultMaxSize. */ class WOFF2StringOut : public WOFF2Out { public: // Create a writer that writes its data to buf. // buf->size() will grow to at most max_size // buf may be sized (e.g. using EstimateWOFF2FinalSize) or empty. explicit WOFF2StringOut(std::string* buf); bool Write(const void *buf, size_t n) override; bool Write(const void *buf, size_t offset, size_t n) override; size_t Size() override { return offset_; } size_t MaxSize() { return max_size_; } void SetMaxSize(size_t max_size); private: std::string* buf_; size_t max_size_; size_t offset_; }; /** * Fixed memory block for woff2 out. */ class WOFF2MemoryOut : public WOFF2Out { public: // Create a writer that writes its data to buf. WOFF2MemoryOut(uint8_t* buf, size_t buf_size); bool Write(const void *buf, size_t n) override; bool Write(const void *buf, size_t offset, size_t n) override; size_t Size() override { return offset_; } private: uint8_t* buf_; size_t buf_size_; size_t offset_; }; } // namespace woff2 #endif // WOFF2_WOFF2_OUT_H_ woff2-1.0.2/src/000077500000000000000000000000001320236240000133145ustar00rootroot00000000000000woff2-1.0.2/src/buffer.h000066400000000000000000000101311320236240000147320ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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 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 #include #endif #include #include #include #include namespace woff2 { #if defined(_MSC_VER) || !defined(FONT_COMPRESSION_DEBUG) #define FONT_COMPRESSION_FAILURE() false #else #define FONT_COMPRESSION_FAILURE() \ woff2::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 *data, size_t len) : buffer_(data), length_(len), offset_(0) { } bool Skip(size_t n_bytes) { return Read(NULL, n_bytes); } bool Read(uint8_t *data, 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 (data) { std::memcpy(data, 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(value)); } bool ReadU24(uint32_t *value) { if (offset_ + 3 > length_) { return FONT_COMPRESSION_FAILURE(); } *value = static_cast(buffer_[offset_]) << 16 | static_cast(buffer_[offset_ + 1]) << 8 | static_cast(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(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_ woff2-1.0.2/src/convert_woff2ttf_fuzzer.cc000066400000000000000000000005021320236240000205260ustar00rootroot00000000000000#include #include #include // Entry point for LibFuzzer. extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { std::string buf; woff2::WOFF2StringOut out(&buf); out.SetMaxSize(30 * 1024 * 1024); woff2::ConvertWOFF2ToTTF(data, size, &out); return 0; } woff2-1.0.2/src/convert_woff2ttf_fuzzer_new_entry.cc000066400000000000000000000006711320236240000226270ustar00rootroot00000000000000#include #include extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_size) { // Decode using newer entry pattern. // Same pattern as woff2_decompress. std::string output(std::min(woff2::ComputeWOFF2FinalSize(data, data_size), woff2::kDefaultMaxSize), 0); woff2::WOFF2StringOut out(&output); woff2::ConvertWOFF2ToTTF(data, data_size, &out); return 0; } woff2-1.0.2/src/file.h000066400000000000000000000014301320236240000144020ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* File IO helpers. */ #ifndef WOFF2_FILE_H_ #define WOFF2_FILE_H_ #include #include namespace woff2 { using std::string; inline string GetFileContent(string filename) { std::ifstream ifs(filename.c_str(), std::ios::binary); return string( std::istreambuf_iterator(ifs.rdbuf()), std::istreambuf_iterator()); } inline void SetFileContents(string filename, string::iterator start, string::iterator end) { std::ofstream ofs(filename.c_str(), std::ios::binary); std::copy(start, end, std::ostream_iterator(ofs)); } } // namespace woff2 #endif // WOFF2_FILE_H_ woff2-1.0.2/src/font.cc000066400000000000000000000275661320236240000146110ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Font management utilities */ #include "./font.h" #include #include "./buffer.h" #include "./port.h" #include "./store_bytes.h" #include "./table_tags.h" #include "./woff2_common.h" namespace woff2 { Font::Table* Font::FindTable(uint32_t tag) { std::map::iterator it = tables.find(tag); return it == tables.end() ? 0 : &it->second; } const Font::Table* Font::FindTable(uint32_t tag) const { std::map::const_iterator it = tables.find(tag); return it == tables.end() ? 0 : &it->second; } std::vector Font::OutputOrderedTags() const { std::vector output_order; for (const auto& i : tables) { const Font::Table& table = i.second; // This is a transformed table, we will write it together with the // original version. if (table.tag & 0x80808080) { continue; } output_order.push_back(table.tag); } // Alphabetize then put loca immediately after glyf auto glyf_loc = std::find(output_order.begin(), output_order.end(), kGlyfTableTag); auto loca_loc = std::find(output_order.begin(), output_order.end(), kLocaTableTag); if (glyf_loc != output_order.end() && loca_loc != output_order.end()) { output_order.erase(loca_loc); output_order.insert(std::find(output_order.begin(), output_order.end(), kGlyfTableTag) + 1, kLocaTableTag); } return output_order; } bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len, Font* font) { // 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->ReadU16(&font->num_tables) || !file->Skip(6)) { return FONT_COMPRESSION_FAILURE(); } std::map intervals; for (uint16_t i = 0; i < font->num_tables; ++i) { Font::Table table; table.flag_byte = 0; table.reuse_of = NULL; 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; } // Sanity check key tables const Font::Table* head_table = font->FindTable(kHeadTableTag); if (head_table != NULL && head_table->length < 52) { return FONT_COMPRESSION_FAILURE(); } return true; } bool ReadCollectionFont(Buffer* file, const uint8_t* data, size_t len, Font* font, std::map* all_tables) { if (!file->ReadU32(&font->flavor)) { return FONT_COMPRESSION_FAILURE(); } if (!ReadTrueTypeFont(file, data, len, font)) { return FONT_COMPRESSION_FAILURE(); } for (auto& entry : font->tables) { Font::Table& table = entry.second; if (all_tables->find(table.offset) == all_tables->end()) { (*all_tables)[table.offset] = font->FindTable(table.tag); } else { table.reuse_of = (*all_tables)[table.offset]; if (table.tag != table.reuse_of->tag) { return FONT_COMPRESSION_FAILURE(); } } } return true; } bool ReadTrueTypeCollection(Buffer* file, const uint8_t* data, size_t len, FontCollection* font_collection) { uint32_t num_fonts; if (!file->ReadU32(&font_collection->header_version) || !file->ReadU32(&num_fonts)) { return FONT_COMPRESSION_FAILURE(); } std::vector offsets; for (size_t i = 0; i < num_fonts; i++) { uint32_t offset; if (!file->ReadU32(&offset)) { return FONT_COMPRESSION_FAILURE(); } offsets.push_back(offset); } font_collection->fonts.resize(offsets.size()); std::vector::iterator font_it = font_collection->fonts.begin(); std::map all_tables; for (const auto offset : offsets) { file->set_offset(offset); Font& font = *font_it++; if (!ReadCollectionFont(file, data, len, &font, &all_tables)) { return FONT_COMPRESSION_FAILURE(); } } return true; } bool ReadFont(const uint8_t* data, size_t len, Font* font) { Buffer file(data, len); if (!file.ReadU32(&font->flavor)) { return FONT_COMPRESSION_FAILURE(); } if (font->flavor == kTtcFontFlavor) { return FONT_COMPRESSION_FAILURE(); } return ReadTrueTypeFont(&file, data, len, font); } bool ReadFontCollection(const uint8_t* data, size_t len, FontCollection* font_collection) { Buffer file(data, len); if (!file.ReadU32(&font_collection->flavor)) { return FONT_COMPRESSION_FAILURE(); } if (font_collection->flavor != kTtcFontFlavor) { font_collection->fonts.resize(1); Font& font = font_collection->fonts[0]; font.flavor = font_collection->flavor; return ReadTrueTypeFont(&file, data, len, &font); } return ReadTrueTypeCollection(&file, data, len, font_collection); } 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; } size_t FontCollectionFileSize(const FontCollection& font_collection) { size_t max_offset = 0; for (auto& font : font_collection.fonts) { // font file size actually just finds max offset max_offset = std::max(max_offset, FontFileSize(font)); } return max_offset; } bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) { size_t offset = 0; return WriteFont(font, &offset, dst, dst_size); } bool WriteTableRecord(const Font::Table* table, size_t* offset, uint8_t* dst, size_t dst_size) { if (dst_size < *offset + kSfntEntrySize) { return FONT_COMPRESSION_FAILURE(); } if (table->IsReused()) { table = table->reuse_of; } StoreU32(table->tag, offset, dst); StoreU32(table->checksum, offset, dst); StoreU32(table->offset, offset, dst); StoreU32(table->length, offset, dst); return true; } bool WriteTable(const Font::Table& table, size_t* offset, uint8_t* dst, size_t dst_size) { if (!WriteTableRecord(&table, offset, dst, dst_size)) { return false; } // Write the actual table data if it's the first time we've seen it if (!table.IsReused()) { 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; } bool WriteFont(const Font& font, size_t* offset, uint8_t* dst, size_t dst_size) { if (dst_size < 12ULL + 16ULL * font.num_tables) { return FONT_COMPRESSION_FAILURE(); } 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) { if (!WriteTable(i.second, offset, dst, dst_size)) { return false; } } return true; } bool WriteFontCollection(const FontCollection& font_collection, uint8_t* dst, size_t dst_size) { size_t offset = 0; // It's simpler if this just a simple sfnt if (font_collection.flavor != kTtcFontFlavor) { return WriteFont(font_collection.fonts[0], &offset, dst, dst_size); } // Write TTC header StoreU32(kTtcFontFlavor, &offset, dst); StoreU32(font_collection.header_version, &offset, dst); StoreU32(font_collection.fonts.size(), &offset, dst); // Offset Table, zeroed for now size_t offset_table = offset; // where to write offsets later for (size_t i = 0; i < font_collection.fonts.size(); i++) { StoreU32(0, &offset, dst); } if (font_collection.header_version == 0x00020000) { StoreU32(0, &offset, dst); // ulDsigTag StoreU32(0, &offset, dst); // ulDsigLength StoreU32(0, &offset, dst); // ulDsigOffset } // Write fonts and their offsets. for (size_t i = 0; i < font_collection.fonts.size(); i++) { const auto& font = font_collection.fonts[i]; StoreU32(offset, &offset_table, dst); if (!WriteFont(font, &offset, dst, dst_size)) { return false; } } 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 loca_record_size = (index_fmt == 0 ? 2 : 4); if (loca_table->length < loca_record_size) { return 0; } return (loca_table->length / loca_record_size) - 1; } 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 Font::Table::IsReused() const { return this->reuse_of != NULL; } 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::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 woff2-1.0.2/src/font.h000066400000000000000000000066651320236240000144500ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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 #include #include #include 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 buffer; // If we've seen this tag/offset before, pointer to the first time we saw it // If this is the first time we've seen this table, NULL // Intended use is to bypass re-processing tables Font::Table* reuse_of; uint8_t flag_byte; // Is this table reused by a TTC bool IsReused() const; }; std::map tables; std::vector OutputOrderedTags() const; Table* FindTable(uint32_t tag); const Table* FindTable(uint32_t tag) const; }; // Accomodates both singular (OTF, TTF) and collection (TTC) fonts struct FontCollection { uint32_t flavor; uint32_t header_version; // (offset, first use of table*) pairs std::map tables; std::vector fonts; }; // 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. Does NOT support collections. bool ReadFont(const uint8_t* data, size_t len, Font* font); // 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. Supports collections. bool ReadFontCollection(const uint8_t* data, size_t len, FontCollection* fonts); // Returns the file size of the font. size_t FontFileSize(const Font& font); size_t FontCollectionFileSize(const FontCollection& 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); // Write the font at a specific offset bool WriteFont(const Font& font, size_t* offset, uint8_t* dst, size_t dst_size); bool WriteFontCollection(const FontCollection& font_collection, 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_ woff2-1.0.2/src/glyph.cc000066400000000000000000000260051320236240000147510ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Glyph manipulation */ #include "./glyph.h" #include #include #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::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(); } // 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) { // Empty glyph. return true; } 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 > 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 (size_t 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 (size_t 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 (size_t 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::max() || end_point > std::numeric_limits::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::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 woff2-1.0.2/src/glyph.h000066400000000000000000000033341320236240000146130ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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 #include #include 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 > 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_ woff2-1.0.2/src/normalize.cc000066400000000000000000000216321320236240000156270ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Glyph normalization */ #include "./normalize.h" #include #include #include "./buffer.h" #include "./port.h" #include "./font.h" #include "./glyph.h" #include "./round.h" #include "./store_bytes.h" #include "./table_tags.h" #include "./woff2_common.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); } } } // 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 = num_glyphs ? &glyf_table->buffer[0] : NULL; 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(); } 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::max() || glyf_offset + static_cast(glyf_dst_size) < glyf_offset || (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) { return FONT_COMPRESSION_FAILURE(); } glyf_offset += glyf_dst_size; } StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); glyf_table->buffer.resize(glyf_offset); glyf_table->data = glyf_offset ? &glyf_table->buffer[0] : NULL; glyf_table->length = glyf_offset; loca_table->data = loca_offset ? &loca_table->buffer[0] : NULL; return true; } } // namespace namespace { bool MakeEditableBuffer(Font* font, int tableTag) { Font::Table* table = font->FindTable(tableTag); if (table == NULL) { return FONT_COMPRESSION_FAILURE(); } if (table->IsReused()) { return true; } int sz = Round4(table->length); table->buffer.resize(sz); uint8_t* buf = &table->buffer[0]; memcpy(buf, table->data, table->length); if (PREDICT_FALSE(sz > table->length)) { memset(buf + table->length, 0, sz - table->length); } table->data = buf; return true; } } // namespace bool NormalizeGlyphs(Font* font) { 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(); } // If you don't have glyf/loca this transform isn't very interesting if (loca_table == NULL && glyf_table == NULL) { return true; } // It would be best if you didn't have just one of glyf/loca if ((glyf_table == NULL) != (loca_table == NULL)) { return FONT_COMPRESSION_FAILURE(); } // Must share neither or both loca & glyf if (loca_table->IsReused() != glyf_table->IsReused()) { return FONT_COMPRESSION_FAILURE(); } if (loca_table->IsReused()) { return true; } 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 tag : font->OutputOrderedTags()) { auto& table = font->tables[tag]; table.offset = offset; offset += Round4(table.length); } return true; } namespace { 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) { const Font::Table* table = &i.second; if (table->IsReused()) { table = table->reuse_of; } checksum += table->tag; checksum += table->checksum; checksum += table->offset; checksum += table->length; } return checksum; } } // namespace bool FixChecksums(Font* font) { Font::Table* head_table = font->FindTable(kHeadTableTag); if (head_table == NULL) { return FONT_COMPRESSION_FAILURE(); } if (head_table->reuse_of != NULL) { head_table = head_table->reuse_of; } if (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; uint32_t head_checksum = 0; for (auto& i : font->tables) { Font::Table* table = &i.second; if (table->IsReused()) { table = table->reuse_of; } table->checksum = ComputeULongSum(table->data, table->length); file_checksum += table->checksum; if (table->tag == kHeadTableTag) { head_checksum = table->checksum; } } file_checksum += ComputeHeaderChecksum(*font); offset = 8; StoreU32(0xb1b0afba - file_checksum, &offset, head_buf); return true; } namespace { bool MarkTransformed(Font* font) { Font::Table* head_table = font->FindTable(kHeadTableTag); if (head_table == NULL) { return FONT_COMPRESSION_FAILURE(); } if (head_table->reuse_of != NULL) { head_table = head_table->reuse_of; } if (head_table->length < 17) { return FONT_COMPRESSION_FAILURE(); } // set bit 11 of head table 'flags' to indicate that font has undergone // lossless modifying transform int head_flags = head_table->data[16]; head_table->buffer[16] = head_flags | 0x08; return true; } } // namespace bool NormalizeWithoutFixingChecksums(Font* font) { return (MakeEditableBuffer(font, kHeadTableTag) && RemoveDigitalSignature(font) && MarkTransformed(font) && NormalizeGlyphs(font) && NormalizeOffsets(font)); } bool NormalizeFont(Font* font) { return (NormalizeWithoutFixingChecksums(font) && FixChecksums(font)); } bool NormalizeFontCollection(FontCollection* font_collection) { if (font_collection->fonts.size() == 1) { return NormalizeFont(&font_collection->fonts[0]); } uint32_t offset = CollectionHeaderSize(font_collection->header_version, font_collection->fonts.size()); for (auto& font : font_collection->fonts) { if (!NormalizeWithoutFixingChecksums(&font)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Font normalization failed.\n"); #endif return FONT_COMPRESSION_FAILURE(); } offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables; } // Start table offsets after TTC Header and Sfnt Headers for (auto& font : font_collection->fonts) { for (auto tag : font.OutputOrderedTags()) { Font::Table& table = font.tables[tag]; if (table.IsReused()) { table.offset = table.reuse_of->offset; } else { table.offset = offset; offset += Round4(table.length); } } } // Now we can fix the checksums for (auto& font : font_collection->fonts) { if (!FixChecksums(&font)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Failed to fix checksums\n"); #endif return FONT_COMPRESSION_FAILURE(); } } return true; } } // namespace woff2 woff2-1.0.2/src/normalize.h000066400000000000000000000024251320236240000154700ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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; struct FontCollection; // 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); bool NormalizeFontCollection(FontCollection* font_collection); } // namespace woff2 #endif // WOFF2_NORMALIZE_H_ woff2-1.0.2/src/port.h000066400000000000000000000032121320236240000144470ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Helper function for bit twiddling and macros for branch prediction. */ #ifndef WOFF2_PORT_H_ #define WOFF2_PORT_H_ #include 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 /* Compatibility with non-clang compilers. */ #ifndef __has_builtin #define __has_builtin(x) 0 #endif #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || \ (defined(__llvm__) && __has_builtin(__builtin_expect)) #define PREDICT_FALSE(x) (__builtin_expect(x, 0)) #define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) #else #define PREDICT_FALSE(x) (x) #define PREDICT_TRUE(x) (x) #endif #if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \ (defined(M_ARM) && (M_ARM == 7)) || \ defined(__aarch64__) || defined(__ARM64_ARCH_8__) || defined(__i386) || \ defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) #define WOFF_LITTLE_ENDIAN #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) #define WOFF_BIG_ENDIAN #endif /* endianness */ #endif /* CPU whitelist */ #endif // WOFF2_PORT_H_ woff2-1.0.2/src/round.h000066400000000000000000000011131320236240000146100ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Helper for rounding */ #ifndef WOFF2_ROUND_H_ #define WOFF2_ROUND_H_ #include 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 T Round4(T value) { if (std::numeric_limits::max() - value < 3) { return value; } return (value + 3) & ~3; } } // namespace woff2 #endif // WOFF2_ROUND_H_ woff2-1.0.2/src/store_bytes.h000066400000000000000000000035101320236240000160260ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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 #include #include #include "./port.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) { #if defined(WOFF_LITTLE_ENDIAN) *reinterpret_cast(dst + offset) = ((x & 0xFF) << 8) | ((x & 0xFF00) >> 8); #elif defined(WOFF_BIG_ENDIAN) *reinterpret_cast(dst + offset) = static_cast(x); #else dst[offset] = x >> 8; dst[offset + 1] = x; #endif 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) { #if defined(WOFF_LITTLE_ENDIAN) *reinterpret_cast(dst + *offset) = ((val & 0xFF) << 8) | ((val & 0xFF00) >> 8); *offset += 2; #elif defined(WOFF_BIG_ENDIAN) *reinterpret_cast(dst + *offset) = static_cast(val); *offset += 2; #else dst[(*offset)++] = val >> 8; dst[(*offset)++] = val; #endif } 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_ woff2-1.0.2/src/table_tags.cc000066400000000000000000000047761320236240000157460ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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 woff2-1.0.2/src/table_tags.h000066400000000000000000000014471320236240000156000ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Font table tags */ #ifndef WOFF2_TABLE_TAGS_H_ #define WOFF2_TABLE_TAGS_H_ #include 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; static const uint32_t kHmtxTableTag = 0x686d7478; static const uint32_t kHheaTableTag = 0x68686561; static const uint32_t kMaxpTableTag = 0x6d617870; extern const uint32_t kKnownTags[]; } // namespace woff2 #endif // WOFF2_TABLE_TAGS_H_ woff2-1.0.2/src/transform.cc000066400000000000000000000314551320236240000156460ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Library for preprocessing fonts as part of the WOFF 2.0 conversion. */ #include "./transform.h" #include // for std::abs #include "./buffer.h" #include "./font.h" #include "./glyph.h" #include "./table_tags.h" #include "./variable_length.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* 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* out, const std::vector& in) { for (size_t i = 0; i < in.size(); ++i) { out->push_back(in[i]); } } void WriteUShort(std::vector* out, int value) { out->push_back(value >> 8); out->push_back(value & 255); } void WriteLong(std::vector* 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); } // Glyf table preprocessing, based on // GlyfEncoder.java class GlyfEncoder { public: explicit GlyfEncoder(int num_glyphs) : 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* 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); } bool ShouldWriteSimpleGlyphBbox(const Glyph& glyph) { if (glyph.contours.empty() || glyph.contours[0].empty()) { return glyph.x_min || glyph.y_min || glyph.x_max || glyph.y_max; } 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; } } if (glyph.x_min != x_min) return true; if (glyph.y_min != y_min) return true; if (glyph.x_max != x_max) return true; if (glyph.y_max != y_max) return true; return false; } void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) { int num_contours = glyph.contours.size(); WriteUShort(&n_contour_stream_, num_contours); if (ShouldWriteSimpleGlyphBbox(glyph)) { WriteBbox(glyph_id, glyph); } 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); 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 n_contour_stream_; std::vector n_points_stream_; std::vector flag_byte_stream_; std::vector composite_stream_; std::vector bbox_bitmap_; std::vector bbox_stream_; std::vector glyph_stream_; std::vector instruction_stream_; int n_glyphs_; }; } // namespace bool TransformGlyfAndLocaTables(Font* font) { // no transform for CFF const Font::Table* glyf_table = font->FindTable(kGlyfTableTag); const Font::Table* loca_table = font->FindTable(kLocaTableTag); // If you don't have glyf/loca this transform isn't very interesting if (loca_table == NULL && glyf_table == NULL) { return true; } // It would be best if you didn't have just one of glyf/loca if ((glyf_table == NULL) != (loca_table == NULL)) { return FONT_COMPRESSION_FAILURE(); } // Must share neither or both loca & glyf if (loca_table->IsReused() != glyf_table->IsReused()) { return FONT_COMPRESSION_FAILURE(); } if (loca_table->IsReused()) { 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; } // See https://www.microsoft.com/typography/otspec/hmtx.htm // See WOFF2 spec, 5.4. Transformed hmtx table format bool TransformHmtxTable(Font* font) { const Font::Table* glyf_table = font->FindTable(kGlyfTableTag); const Font::Table* hmtx_table = font->FindTable(kHmtxTableTag); const Font::Table* hhea_table = font->FindTable(kHheaTableTag); // If you don't have hmtx or a glyf not much is going to happen here if (hmtx_table == NULL || glyf_table == NULL) { return true; } // hmtx without hhea doesn't make sense if (hhea_table == NULL) { return FONT_COMPRESSION_FAILURE(); } // Skip 34 to reach 'hhea' numberOfHMetrics Buffer hhea_buf(hhea_table->data, hhea_table->length); uint16_t num_hmetrics; if (!hhea_buf.Skip(34) || !hhea_buf.ReadU16(&num_hmetrics)) { return FONT_COMPRESSION_FAILURE(); } // Must have at least one hMetric if (num_hmetrics < 1) { return FONT_COMPRESSION_FAILURE(); } int num_glyphs = NumGlyphs(*font); // Most fonts can be transformed; assume it's a go until proven otherwise std::vector advance_widths; std::vector proportional_lsbs; std::vector monospace_lsbs; bool remove_proportional_lsb = true; bool remove_monospace_lsb = (num_glyphs - num_hmetrics) > 0; Buffer hmtx_buf(hmtx_table->data, hmtx_table->length); 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(); } uint16_t advance_width = 0; int16_t lsb = 0; if (i < num_hmetrics) { // [0, num_hmetrics) are proportional hMetrics if (!hmtx_buf.ReadU16(&advance_width)) { return FONT_COMPRESSION_FAILURE(); } if (!hmtx_buf.ReadS16(&lsb)) { return FONT_COMPRESSION_FAILURE(); } if (glyph_size > 0 && glyph.x_min != lsb) { remove_proportional_lsb = false; } advance_widths.push_back(advance_width); proportional_lsbs.push_back(lsb); } else { // [num_hmetrics, num_glyphs) are monospace leftSideBearing's if (!hmtx_buf.ReadS16(&lsb)) { return FONT_COMPRESSION_FAILURE(); } if (glyph_size > 0 && glyph.x_min != lsb) { remove_monospace_lsb = false; } monospace_lsbs.push_back(lsb); } // If we know we can't optimize, bail out completely if (!remove_proportional_lsb && !remove_monospace_lsb) { return true; } } Font::Table* transformed_hmtx = &font->tables[kHmtxTableTag ^ 0x80808080]; uint8_t flags = 0; size_t transformed_size = 1 + 2 * advance_widths.size(); if (remove_proportional_lsb) { flags |= 1; } else { transformed_size += 2 * proportional_lsbs.size(); } if (remove_monospace_lsb) { flags |= 1 << 1; } else { transformed_size += 2 * monospace_lsbs.size(); } transformed_hmtx->buffer.reserve(transformed_size); std::vector* out = &transformed_hmtx->buffer; WriteBytes(out, &flags, 1); for (uint16_t advance_width : advance_widths) { WriteUShort(out, advance_width); } if (!remove_proportional_lsb) { for (int16_t lsb : proportional_lsbs) { WriteUShort(out, lsb); } } if (!remove_monospace_lsb) { for (int16_t lsb : monospace_lsbs) { WriteUShort(out, lsb); } } transformed_hmtx->tag = kHmtxTableTag ^ 0x80808080; transformed_hmtx->flag_byte = 1 << 6; transformed_hmtx->length = transformed_hmtx->buffer.size(); transformed_hmtx->data = transformed_hmtx->buffer.data(); return true; } } // namespace woff2 woff2-1.0.2/src/transform.h000066400000000000000000000014001320236240000154730ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* 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); // Apply transformation to hmtx table if applicable for this font. bool TransformHmtxTable(Font* font); } // namespace woff2 #endif // WOFF2_TRANSFORM_H_ woff2-1.0.2/src/variable_length.cc000066400000000000000000000061501320236240000167530ustar00rootroot00000000000000/* Copyright 2015 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Helper functions for woff2 variable length types: 255UInt16 and UIntBase128 */ #include "./variable_length.h" namespace woff2 { size_t Size255UShort(uint16_t value) { size_t result = 3; if (value < 253) { result = 1; } else if (value < 762) { result = 2; } else { result = 3; } return result; } void Write255UShort(std::vector* 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); } } void Store255UShort(int val, size_t* offset, uint8_t* dst) { std::vector packed; Write255UShort(&packed, val); for (uint8_t packed_byte : packed) { dst[(*offset)++] = packed_byte; } } // 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(); } // Leading zeros are invalid. if (i == 0 && code == 0x80) { 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(); } 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 (size_t i = 0; i < size; ++i) { int b = static_cast((len >> (7 * (size - i - 1))) & 0x7f); if (i < size - 1) { b |= 0x80; } dst[(*offset)++] = b; } } } // namespace woff2 woff2-1.0.2/src/variable_length.h000066400000000000000000000014531320236240000166160ustar00rootroot00000000000000/* Copyright 2015 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Helper functions for woff2 variable length types: 255UInt16 and UIntBase128 */ #ifndef WOFF2_VARIABLE_LENGTH_H_ #define WOFF2_VARIABLE_LENGTH_H_ #include #include #include "./buffer.h" namespace woff2 { size_t Size255UShort(uint16_t value); bool Read255UShort(Buffer* buf, unsigned int* value); void Write255UShort(std::vector* out, int value); void Store255UShort(int val, size_t* offset, uint8_t* dst); size_t Base128Size(size_t n); bool ReadBase128(Buffer* buf, uint32_t* value); void StoreBase128(size_t len, size_t* offset, uint8_t* dst); } // namespace woff2 #endif // WOFF2_VARIABLE_LENGTH_H_ woff2-1.0.2/src/woff2_common.cc000066400000000000000000000030131320236240000162130ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Helpers common across multiple parts of woff2 */ #include #include "./woff2_common.h" #include "./port.h" namespace woff2 { uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { uint32_t checksum = 0; size_t aligned_size = size & ~3; for (size_t i = 0; i < aligned_size; i += 4) { #if defined(WOFF_LITTLE_ENDIAN) uint32_t v = *reinterpret_cast(buf + i); checksum += (((v & 0xFF) << 24) | ((v & 0xFF00) << 8) | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24)); #elif defined(WOFF_BIG_ENDIAN) checksum += *reinterpret_cast(buf + i); #else checksum += (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3]; #endif } // treat size not aligned on 4 as if it were padded to 4 with 0's if (size != aligned_size) { uint32_t v = 0; for (size_t i = aligned_size; i < size; ++i) { v |= buf[i] << (24 - 8 * (i & 3)); } checksum += v; } return checksum; } size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts) { size_t size = 0; if (header_version == 0x00020000) { size += 12; // ulDsig{Tag,Length,Offset} } if (header_version == 0x00010000 || header_version == 0x00020000) { size += 12 // TTCTag, Version, numFonts + 4 * num_fonts; // OffsetTable[numFonts] } return size; } } // namespace woff2 woff2-1.0.2/src/woff2_common.h000066400000000000000000000026371320236240000160700ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Common definition for WOFF2 encoding/decoding */ #ifndef WOFF2_WOFF2_COMMON_H_ #define WOFF2_WOFF2_COMMON_H_ #include #include #include namespace woff2 { static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2" // Leave the first byte open to store flag_byte const unsigned int kWoff2FlagsTransform = 1 << 8; // TrueType Collection ID string: 'ttcf' static const uint32_t kTtcFontFlavor = 0x74746366; static const size_t kSfntHeaderSize = 12; static const size_t kSfntEntrySize = 16; 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; } }; // Size of the collection header. 0 if version indicates this isn't a // collection. Ref http://www.microsoft.com/typography/otspec/otff.htm, // True Type Collections size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts); // Compute checksum over size bytes of buf uint32_t ComputeULongSum(const uint8_t* buf, size_t size); } // namespace woff2 #endif // WOFF2_WOFF2_COMMON_H_ woff2-1.0.2/src/woff2_compress.cc000066400000000000000000000024431320236240000165640ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* A commandline tool for compressing ttf format files to woff2. */ #include #include "file.h" #include 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(input.data()); size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size()); string output(output_size, 0); uint8_t* output_data = reinterpret_cast(&output[0]); woff2::WOFF2Params params; if (!woff2::ConvertTTFToWOFF2(input_data, input.size(), output_data, &output_size, params)) { fprintf(stderr, "Compression failed.\n"); return 1; } output.resize(output_size); woff2::SetFileContents(outfilename, output.begin(), output.end()); return 0; } woff2-1.0.2/src/woff2_dec.cc000066400000000000000000001304201320236240000154610ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Library for converting WOFF2 format font files to their TTF versions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "./buffer.h" #include "./port.h" #include "./round.h" #include "./store_bytes.h" #include "./table_tags.h" #include "./variable_length.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 kCheckSumAdjustmentOffset = 8; const size_t kEndPtsOfContoursOffset = 10; const size_t kCompositeGlyphBegin = 10; // 98% of Google Fonts have no glyph above 5k bytes // Largest glyph ever observed was 72k bytes const size_t kDefaultGlyphBuf = 5120; // Over 14k test fonts the max compression ratio seen to date was ~20. // >100 suggests you wrote a bad uncompressed size. const float kMaxPlausibleCompressionRatio = 100.0; // metadata for a TTC font entry struct TtcFont { uint32_t flavor; uint32_t dst_offset; uint32_t header_checksum; std::vector table_indices; }; struct WOFF2Header { uint32_t flavor; uint32_t header_version; uint16_t num_tables; uint64_t compressed_offset; uint32_t compressed_length; uint32_t uncompressed_size; std::vector tables; // num_tables unique tables std::vector ttc_fonts; // metadata to help rebuild font }; /** * Accumulates data we may need to reconstruct a single font. One per font * created for a TTC. */ struct WOFF2FontInfo { uint16_t num_glyphs; uint16_t index_format; uint16_t num_hmetrics; std::vector x_mins; std::map table_entry_by_tag; }; // Accumulates metadata as we rebuild the font struct RebuildMetadata { uint32_t header_checksum; // set by WriteHeaders std::vector font_infos; // checksums for tables that have been written. // (tag, src_offset) => checksum. Need both because 0-length loca. std::map, uint32_t> checksums; }; 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, Point* result, size_t* in_bytes_consumed) { int x = 0; int y = 0; if (PREDICT_FALSE(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 (PREDICT_FALSE(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++ = {x, y, 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(unsigned int n_points, const 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 < n_points; ++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 (PREDICT_FALSE(flag_offset >= dst_size)) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; } if (PREDICT_FALSE(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 (PREDICT_FALSE(flag_offset >= dst_size)) { return FONT_COMPRESSION_FAILURE(); } dst[flag_offset++] = repeat_count; } unsigned int xy_bytes = x_bytes + y_bytes; if (PREDICT_FALSE(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 < n_points; ++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. // dst should point to the beginning of a 'glyf' record. void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { int x_min = 0; int y_min = 0; int x_max = 0; int y_max = 0; if (n_points > 0) { x_min = points[0].x; x_max = points[0].x; y_min = points[0].y; y_max = points[0].y; } for (unsigned int i = 1; i < n_points; ++i) { int x = points[i].x; int y = points[i].y; x_min = std::min(x, x_min); x_max = std::max(x, x_max); y_min = std::min(y, y_min); y_max = std::max(y, y_max); } 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); } bool SizeOfComposite(Buffer composite_stream, size_t* 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 (PREDICT_FALSE(!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 (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { return FONT_COMPRESSION_FAILURE(); } } *size = composite_stream.offset() - start_offset; *have_instructions = we_have_instructions; return true; } bool Pad4(WOFF2Out* out) { uint8_t zeroes[] = {0, 0, 0}; if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { return FONT_COMPRESSION_FAILURE(); } uint32_t pad_bytes = Round4(out->Size()) - out->Size(); if (pad_bytes > 0) { if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { return FONT_COMPRESSION_FAILURE(); } } return true; } // Build TrueType loca table bool StoreLoca(const std::vector& loca_values, int index_format, uint32_t* checksum, WOFF2Out* out) { // TODO(user) figure out what index format to use based on whether max // offset fits into uint16_t or not const uint64_t loca_size = loca_values.size(); const uint64_t offset_size = index_format ? 4 : 2; if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { return FONT_COMPRESSION_FAILURE(); } std::vector loca_content(loca_size * offset_size); uint8_t* dst = &loca_content[0]; 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); } } *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { return FONT_COMPRESSION_FAILURE(); } return true; } // Reconstruct entire glyf table based on transformed original bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, uint32_t* glyf_checksum, Table * loca_table, uint32_t* loca_checksum, WOFF2FontInfo* info, WOFF2Out* out) { static const int kNumSubStreams = 7; Buffer file(data, glyf_table->transform_length); uint32_t version; std::vector > substreams(kNumSubStreams); const size_t glyf_start = out->Size(); if (PREDICT_FALSE(!file.ReadU32(&version))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || !file.ReadU16(&info->index_format))) { return FONT_COMPRESSION_FAILURE(); } // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca // dst_length here is origLength in the spec uint32_t expected_loca_dst_length = (info->index_format ? 4 : 2) * (static_cast(info->num_glyphs) + 1); if (PREDICT_FALSE(loca_table->dst_length != expected_loca_dst_length)) { return FONT_COMPRESSION_FAILURE(); } unsigned int offset = (2 + kNumSubStreams) * 4; if (PREDICT_FALSE(offset > glyf_table->transform_length)) { return FONT_COMPRESSION_FAILURE(); } // Invariant from here on: data_size >= offset for (int i = 0; i < kNumSubStreams; ++i) { uint32_t substream_size; if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(substream_size > glyf_table->transform_length - 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 loca_values(info->num_glyphs + 1); std::vector n_points_vec; std::unique_ptr points; size_t points_size = 0; const uint8_t* bbox_bitmap = bbox_stream.buffer(); // Safe because num_glyphs is bounded unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; if (!bbox_stream.Skip(bitmap_length)) { return FONT_COMPRESSION_FAILURE(); } // Temp buffer for glyph's. size_t glyph_buf_size = kDefaultGlyphBuf; std::unique_ptr glyph_buf(new uint8_t[glyph_buf_size]); info->x_mins.resize(info->num_glyphs); for (unsigned int i = 0; i < info->num_glyphs; ++i) { size_t glyph_size = 0; uint16_t n_contours = 0; bool have_bbox = false; if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { have_bbox = true; } if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { return FONT_COMPRESSION_FAILURE(); } if (n_contours == 0xffff) { // composite glyph bool have_instructions = false; unsigned int instruction_size = 0; if (PREDICT_FALSE(!have_bbox)) { // composite glyphs must have an explicit bbox return FONT_COMPRESSION_FAILURE(); } size_t composite_size; if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, &have_instructions))) { return FONT_COMPRESSION_FAILURE(); } if (have_instructions) { if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { return FONT_COMPRESSION_FAILURE(); } } size_t size_needed = 12 + composite_size + instruction_size; if (PREDICT_FALSE(glyph_buf_size < size_needed)) { glyph_buf.reset(new uint8_t[size_needed]); glyph_buf_size = size_needed; } glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += 8; if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, composite_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += composite_size; if (have_instructions) { glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, instruction_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += instruction_size; } } else if (n_contours > 0) { // simple glyph n_points_vec.clear(); unsigned int total_n_points = 0; unsigned int n_points_contour; for (unsigned int j = 0; j < n_contours; ++j) { if (PREDICT_FALSE( !Read255UShort(&n_points_stream, &n_points_contour))) { return FONT_COMPRESSION_FAILURE(); } n_points_vec.push_back(n_points_contour); if (PREDICT_FALSE(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 (PREDICT_FALSE( 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 (points_size < total_n_points) { points_size = total_n_points; points.reset(new Point[points_size]); } if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, total_n_points, points.get(), &triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { return FONT_COMPRESSION_FAILURE(); } unsigned int instruction_size; if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(total_n_points >= (1 << 27) || instruction_size >= (1 << 30))) { return FONT_COMPRESSION_FAILURE(); } size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points + instruction_size; if (PREDICT_FALSE(glyph_buf_size < size_needed)) { glyph_buf.reset(new uint8_t[size_needed]); glyph_buf_size = size_needed; } glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); if (have_bbox) { if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { return FONT_COMPRESSION_FAILURE(); } } else { ComputeBbox(total_n_points, points.get(), glyph_buf.get()); } glyph_size = 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 (PREDICT_FALSE(end_point >= 65536)) { return FONT_COMPRESSION_FAILURE(); } glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); } glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, instruction_size))) { return FONT_COMPRESSION_FAILURE(); } glyph_size += instruction_size; if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, instruction_size, glyph_buf.get(), glyph_buf_size, &glyph_size))) { return FONT_COMPRESSION_FAILURE(); } } else { // n_contours == 0; empty glyph. Must NOT have a bbox. if (PREDICT_FALSE(have_bbox)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Empty glyph has a bbox\n"); #endif return FONT_COMPRESSION_FAILURE(); } } loca_values[i] = out->Size() - glyf_start; if (PREDICT_FALSE(!out->Write(glyph_buf.get(), glyph_size))) { return FONT_COMPRESSION_FAILURE(); } // TODO(user) Old code aligned glyphs ... but do we actually need to? if (PREDICT_FALSE(!Pad4(out))) { return FONT_COMPRESSION_FAILURE(); } *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); // We may need x_min to reconstruct 'hmtx' if (n_contours > 0) { Buffer x_min_buf(glyph_buf.get() + 2, 2); if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { return FONT_COMPRESSION_FAILURE(); } } } // glyf_table dst_offset was set by ReconstructFont glyf_table->dst_length = out->Size() - glyf_table->dst_offset; loca_table->dst_offset = out->Size(); // loca[n] will be equal the length of the glyph data ('glyf') table loca_values[info->num_glyphs] = glyf_table->dst_length; if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, out))) { return FONT_COMPRESSION_FAILURE(); } loca_table->dst_length = out->Size() - loca_table->dst_offset; return true; } Table* FindTable(std::vector* tables, uint32_t tag) { for (Table* table : *tables) { if (table->tag == tag) { return table; } } return NULL; } // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm bool ReadNumHMetrics(const uint8_t* data, size_t data_size, uint16_t* num_hmetrics) { // Skip 34 to reach 'hhea' numberOfHMetrics Buffer buffer(data, data_size); if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { return FONT_COMPRESSION_FAILURE(); } return true; } // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, size_t transformed_size, uint16_t num_glyphs, uint16_t num_hmetrics, const std::vector& x_mins, uint32_t* checksum, WOFF2Out* out) { Buffer hmtx_buff_in(transformed_buf, transformed_size); uint8_t hmtx_flags; if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { return FONT_COMPRESSION_FAILURE(); } std::vector advance_widths; std::vector lsbs; bool has_proportional_lsbs = (hmtx_flags & 1) == 0; bool has_monospace_lsbs = (hmtx_flags & 2) == 0; // Bits 2-7 are reserved and MUST be zero. if ((hmtx_flags & 0xFC) != 0) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Illegal hmtx flags; bits 2-7 must be 0\n"); #endif return FONT_COMPRESSION_FAILURE(); } // you say you transformed but there is little evidence of it if (has_proportional_lsbs && has_monospace_lsbs) { return FONT_COMPRESSION_FAILURE(); } assert(x_mins.size() == num_glyphs); // num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { return FONT_COMPRESSION_FAILURE(); } // https://www.microsoft.com/typography/otspec/hmtx.htm // "...only one entry need be in the array, but that entry is required." if (PREDICT_FALSE(num_hmetrics < 1)) { return FONT_COMPRESSION_FAILURE(); } for (uint16_t i = 0; i < num_hmetrics; i++) { uint16_t advance_width; if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { return FONT_COMPRESSION_FAILURE(); } advance_widths.push_back(advance_width); } for (uint16_t i = 0; i < num_hmetrics; i++) { int16_t lsb; if (has_proportional_lsbs) { if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { return FONT_COMPRESSION_FAILURE(); } } else { lsb = x_mins[i]; } lsbs.push_back(lsb); } for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { int16_t lsb; if (has_monospace_lsbs) { if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { return FONT_COMPRESSION_FAILURE(); } } else { lsb = x_mins[i]; } lsbs.push_back(lsb); } // bake me a shiny new hmtx table uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; std::vector hmtx_table(hmtx_output_size); uint8_t* dst = &hmtx_table[0]; size_t dst_offset = 0; for (uint32_t i = 0; i < num_glyphs; i++) { if (i < num_hmetrics) { Store16(advance_widths[i], &dst_offset, dst); } Store16(lsbs[i], &dst_offset, dst); } *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { return FONT_COMPRESSION_FAILURE(); } 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; BrotliDecoderResult result = BrotliDecoderDecompress( src_size, src_buf, &uncompressed_size, dst_buf); if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || uncompressed_size != dst_size)) { return FONT_COMPRESSION_FAILURE(); } return true; } bool ReadTableDirectory(Buffer* file, std::vector
* tables, size_t num_tables) { uint32_t src_offset = 0; for (size_t i = 0; i < num_tables; ++i) { Table* table = &(*tables)[i]; uint8_t flag_byte; if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { return FONT_COMPRESSION_FAILURE(); } uint32_t tag; if ((flag_byte & 0x3f) == 0x3f) { if (PREDICT_FALSE(!file->ReadU32(&tag))) { return FONT_COMPRESSION_FAILURE(); } } else { tag = kKnownTags[flag_byte & 0x3f]; } uint32_t flags = 0; uint8_t xform_version = (flag_byte >> 6) & 0x03; // 0 means xform for glyph/loca, non-0 for others if (tag == kGlyfTableTag || tag == kLocaTableTag) { if (xform_version == 0) { flags |= kWoff2FlagsTransform; } } else if (xform_version != 0) { flags |= kWoff2FlagsTransform; } flags |= xform_version; uint32_t dst_length; if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { return FONT_COMPRESSION_FAILURE(); } uint32_t transform_length = dst_length; if ((flags & kWoff2FlagsTransform) != 0) { if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { return FONT_COMPRESSION_FAILURE(); } } if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { return FONT_COMPRESSION_FAILURE(); } table->src_offset = src_offset; table->src_length = transform_length; src_offset += transform_length; table->tag = tag; table->flags = flags; table->transform_length = transform_length; table->dst_length = dst_length; } return true; } // Writes a single Offset Table entry size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, uint16_t num_tables) { offset = StoreU32(result, offset, flavor); // sfnt version offset = Store16(result, offset, num_tables); // 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); // searchRange offset = Store16(result, offset, max_pow2); // entrySelector // rangeShift offset = Store16(result, offset, (num_tables << 4) - output_search_range); return offset; } size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { offset = StoreU32(result, offset, tag); offset = StoreU32(result, offset, 0); offset = StoreU32(result, offset, 0); offset = StoreU32(result, offset, 0); return offset; } // First table goes after all the headers, table directory, etc uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { uint64_t offset = kSfntHeaderSize + kSfntEntrySize * static_cast(hdr.num_tables); if (hdr.header_version) { offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) + kSfntHeaderSize * hdr.ttc_fonts.size(); for (const auto& ttc_font : hdr.ttc_fonts) { offset += kSfntEntrySize * ttc_font.table_indices.size(); } } return offset; } std::vector Tables(WOFF2Header* hdr, size_t font_index) { std::vector tables; if (PREDICT_FALSE(hdr->header_version)) { for (auto index : hdr->ttc_fonts[font_index].table_indices) { tables.push_back(&hdr->tables[index]); } } else { for (auto& table : hdr->tables) { tables.push_back(&table); } } return tables; } // Offset tables assumed to have been written in with 0's initially. // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) bool ReconstructFont(uint8_t* transformed_buf, const uint32_t transformed_buf_size, RebuildMetadata* metadata, WOFF2Header* hdr, size_t font_index, WOFF2Out* out) { size_t dest_offset = out->Size(); uint8_t table_entry[12]; WOFF2FontInfo* info = &metadata->font_infos[font_index]; std::vector tables = Tables(hdr, font_index); // 'glyf' without 'loca' doesn't make sense const Table* glyf_table = FindTable(&tables, kGlyfTableTag); const Table* loca_table = FindTable(&tables, kLocaTableTag); if (PREDICT_FALSE(static_cast(glyf_table) != static_cast(loca_table))) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Cannot have just one of glyf/loca\n"); #endif return FONT_COMPRESSION_FAILURE(); } if (glyf_table != NULL) { if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform) != (loca_table->flags & kWoff2FlagsTransform))) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Cannot transform just one of glyf/loca\n"); #endif return FONT_COMPRESSION_FAILURE(); } } uint32_t font_checksum = metadata->header_checksum; if (hdr->header_version) { font_checksum = hdr->ttc_fonts[font_index].header_checksum; } uint32_t loca_checksum = 0; for (size_t i = 0; i < tables.size(); i++) { Table& table = *tables[i]; std::pair checksum_key = {table.tag, table.src_offset}; bool reused = metadata->checksums.find(checksum_key) != metadata->checksums.end(); if (PREDICT_FALSE(font_index == 0 && reused)) { return FONT_COMPRESSION_FAILURE(); } // TODO(user) a collection with optimized hmtx that reused glyf/loca // would fail. We don't optimize hmtx for collections yet. if (PREDICT_FALSE(static_cast(table.src_offset) + table.src_length > transformed_buf_size)) { return FONT_COMPRESSION_FAILURE(); } if (table.tag == kHheaTableTag) { if (!ReadNumHMetrics(transformed_buf + table.src_offset, table.src_length, &info->num_hmetrics)) { return FONT_COMPRESSION_FAILURE(); } } uint32_t checksum = 0; if (!reused) { if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { if (table.tag == kHeadTableTag) { if (PREDICT_FALSE(table.src_length < 12)) { return FONT_COMPRESSION_FAILURE(); } // checkSumAdjustment = 0 StoreU32(transformed_buf + table.src_offset, 8, 0); } table.dst_offset = dest_offset; checksum = ComputeULongSum(transformed_buf + table.src_offset, table.src_length); if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, table.src_length))) { return FONT_COMPRESSION_FAILURE(); } } else { if (table.tag == kGlyfTableTag) { table.dst_offset = dest_offset; Table* loca_table = FindTable(&tables, kLocaTableTag); if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, &table, &checksum, loca_table, &loca_checksum, info, out))) { return FONT_COMPRESSION_FAILURE(); } } else if (table.tag == kLocaTableTag) { // All the work was done by ReconstructGlyf. We already know checksum. checksum = loca_checksum; } else if (table.tag == kHmtxTableTag) { table.dst_offset = dest_offset; // Tables are sorted so all the info we need has been gathered. if (PREDICT_FALSE(!ReconstructTransformedHmtx( transformed_buf + table.src_offset, table.src_length, info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, out))) { return FONT_COMPRESSION_FAILURE(); } } else { return FONT_COMPRESSION_FAILURE(); // transform unknown } } metadata->checksums[checksum_key] = checksum; } else { checksum = metadata->checksums[checksum_key]; } font_checksum += checksum; // update the table entry with real values. StoreU32(table_entry, 0, checksum); StoreU32(table_entry, 4, table.dst_offset); StoreU32(table_entry, 8, table.dst_length); if (PREDICT_FALSE(!out->Write(table_entry, info->table_entry_by_tag[table.tag] + 4, 12))) { return FONT_COMPRESSION_FAILURE(); } // We replaced 0's. Update overall checksum. font_checksum += ComputeULongSum(table_entry, 12); if (PREDICT_FALSE(!Pad4(out))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(static_cast(table.dst_offset + table.dst_length) > out->Size())) { return FONT_COMPRESSION_FAILURE(); } dest_offset = out->Size(); } // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. Table* head_table = FindTable(&tables, kHeadTableTag); if (head_table) { if (PREDICT_FALSE(head_table->dst_length < 12)) { return FONT_COMPRESSION_FAILURE(); } uint8_t checksum_adjustment[4]; StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); if (PREDICT_FALSE(!out->Write(checksum_adjustment, head_table->dst_offset + 8, 4))) { return FONT_COMPRESSION_FAILURE(); } } return true; } bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { Buffer file(data, length); uint32_t signature; if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || !file.ReadU32(&hdr->flavor))) { return FONT_COMPRESSION_FAILURE(); } // TODO(user): Should call IsValidVersionTag() here. uint32_t reported_length; if (PREDICT_FALSE( !file.ReadU32(&reported_length) || length != reported_length)) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { return FONT_COMPRESSION_FAILURE(); } // We don't care about these fields of the header: // uint16_t reserved // uint32_t total_sfnt_size, we don't believe this, will compute later if (PREDICT_FALSE(!file.Skip(6))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { return FONT_COMPRESSION_FAILURE(); } // We don't care about these fields of the header: // uint16_t major_version, minor_version if (PREDICT_FALSE(!file.Skip(2 * 2))) { return FONT_COMPRESSION_FAILURE(); } uint32_t meta_offset; uint32_t meta_length; uint32_t meta_length_orig; if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || !file.ReadU32(&meta_length) || !file.ReadU32(&meta_length_orig))) { return FONT_COMPRESSION_FAILURE(); } if (meta_offset) { if (PREDICT_FALSE( meta_offset >= length || length - meta_offset < meta_length)) { return FONT_COMPRESSION_FAILURE(); } } uint32_t priv_offset; uint32_t priv_length; if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || !file.ReadU32(&priv_length))) { return FONT_COMPRESSION_FAILURE(); } if (priv_offset) { if (PREDICT_FALSE( priv_offset >= length || length - priv_offset < priv_length)) { return FONT_COMPRESSION_FAILURE(); } } hdr->tables.resize(hdr->num_tables); if (PREDICT_FALSE(!ReadTableDirectory( &file, &hdr->tables, hdr->num_tables))) { return FONT_COMPRESSION_FAILURE(); } // Before we sort for output the last table end is the uncompressed size. Table& last_table = hdr->tables.back(); hdr->uncompressed_size = last_table.src_offset + last_table.src_length; if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { return FONT_COMPRESSION_FAILURE(); } hdr->header_version = 0; if (hdr->flavor == kTtcFontFlavor) { if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(hdr->header_version != 0x00010000 && hdr->header_version != 0x00020000)) { return FONT_COMPRESSION_FAILURE(); } uint32_t num_fonts; if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { return FONT_COMPRESSION_FAILURE(); } hdr->ttc_fonts.resize(num_fonts); for (uint32_t i = 0; i < num_fonts; i++) { TtcFont& ttc_font = hdr->ttc_fonts[i]; uint32_t num_tables; if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { return FONT_COMPRESSION_FAILURE(); } ttc_font.table_indices.resize(num_tables); unsigned int glyf_idx = 0; unsigned int loca_idx = 0; for (uint32_t j = 0; j < num_tables; j++) { unsigned int table_idx; if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || table_idx >= hdr->tables.size()) { return FONT_COMPRESSION_FAILURE(); } ttc_font.table_indices[j] = table_idx; const Table& table = hdr->tables[table_idx]; if (table.tag == kLocaTableTag) { loca_idx = table_idx; } if (table.tag == kGlyfTableTag) { glyf_idx = table_idx; } } // if we have both glyf and loca make sure they are consecutive // if we have just one we'll reject the font elsewhere if (glyf_idx > 0 || loca_idx > 0) { if (PREDICT_FALSE(glyf_idx > loca_idx || loca_idx - glyf_idx != 1)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "TTC font %d has non-consecutive glyf/loca\n", i); #endif return FONT_COMPRESSION_FAILURE(); } } } } const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); hdr->compressed_offset = file.offset(); if (PREDICT_FALSE(hdr->compressed_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); uint64_t dst_offset = first_table_offset; if (PREDICT_FALSE(src_offset > length)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " "dst_offset %" PRIu64 "\n", src_offset, length, dst_offset); #endif return FONT_COMPRESSION_FAILURE(); } if (meta_offset) { if (PREDICT_FALSE(src_offset != meta_offset)) { return FONT_COMPRESSION_FAILURE(); } src_offset = Round4(meta_offset + meta_length); if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } if (priv_offset) { if (PREDICT_FALSE(src_offset != priv_offset)) { return FONT_COMPRESSION_FAILURE(); } src_offset = Round4(priv_offset + priv_length); if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { return FONT_COMPRESSION_FAILURE(); } } if (PREDICT_FALSE(src_offset != Round4(length))) { return FONT_COMPRESSION_FAILURE(); } return true; } // Write everything before the actual table data bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, WOFF2Header* hdr, WOFF2Out* out) { std::vector output(ComputeOffsetToFirstTable(*hdr), 0); // Re-order tables in output (OTSpec) order std::vector
sorted_tables(hdr->tables); if (hdr->header_version) { // collection; we have to sort the table offset vector in each font for (auto& ttc_font : hdr->ttc_fonts) { std::map sorted_index_by_tag; for (auto table_index : ttc_font.table_indices) { sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; } uint16_t index = 0; for (auto& i : sorted_index_by_tag) { ttc_font.table_indices[index++] = i.second; } } } else { // non-collection; we can just sort the tables std::sort(sorted_tables.begin(), sorted_tables.end()); } // Start building the font uint8_t* result = &output[0]; size_t offset = 0; if (hdr->header_version) { // TTC header offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag offset = StoreU32(result, offset, hdr->header_version); // FIXED Version offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts // Space for ULONG OffsetTable[numFonts] (zeroed initially) size_t offset_table = offset; // keep start of offset table for later for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { offset = StoreU32(result, offset, 0); // will fill real values in later } // space for DSIG fields for header v2 if (hdr->header_version == 0x00020000) { offset = StoreU32(result, offset, 0); // ULONG ulDsigTag offset = StoreU32(result, offset, 0); // ULONG ulDsigLength offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset } // write Offset Tables and store the location of each in TTC Header metadata->font_infos.resize(hdr->ttc_fonts.size()); for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { TtcFont& ttc_font = hdr->ttc_fonts[i]; // write Offset Table location into TTC Header offset_table = StoreU32(result, offset_table, offset); // write the actual offset table so our header doesn't lie ttc_font.dst_offset = offset; offset = StoreOffsetTable(result, offset, ttc_font.flavor, ttc_font.table_indices.size()); for (const auto table_index : ttc_font.table_indices) { uint32_t tag = hdr->tables[table_index].tag; metadata->font_infos[i].table_entry_by_tag[tag] = offset; offset = StoreTableEntry(result, offset, tag); } ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], offset - ttc_font.dst_offset); } } else { metadata->font_infos.resize(1); offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); for (uint16_t i = 0; i < hdr->num_tables; ++i) { metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; offset = StoreTableEntry(result, offset, sorted_tables[i].tag); } } if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { return FONT_COMPRESSION_FAILURE(); } metadata->header_checksum = ComputeULongSum(&output[0], output.size()); 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) { WOFF2MemoryOut out(result, result_length); return ConvertWOFF2ToTTF(data, length, &out); } bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, WOFF2Out* out) { RebuildMetadata metadata; WOFF2Header hdr; if (!ReadWOFF2Header(data, length, &hdr)) { return FONT_COMPRESSION_FAILURE(); } if (!WriteHeaders(data, length, &metadata, &hdr, out)) { return FONT_COMPRESSION_FAILURE(); } const float compression_ratio = (float) hdr.uncompressed_size / length; if (compression_ratio > kMaxPlausibleCompressionRatio) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Implausible compression ratio %.01f\n", compression_ratio); #endif return FONT_COMPRESSION_FAILURE(); } const uint8_t* src_buf = data + hdr.compressed_offset; std::vector uncompressed_buf(hdr.uncompressed_size); if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { return FONT_COMPRESSION_FAILURE(); } if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], hdr.uncompressed_size, src_buf, hdr.compressed_length))) { return FONT_COMPRESSION_FAILURE(); } for (size_t i = 0; i < metadata.font_infos.size(); i++) { if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], hdr.uncompressed_size, &metadata, &hdr, i, out))) { return FONT_COMPRESSION_FAILURE(); } } return true; } } // namespace woff2 woff2-1.0.2/src/woff2_decompress.cc000066400000000000000000000022611320236240000170730ustar00rootroot00000000000000/* Copyright 2013 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* A very simple commandline tool for decompressing woff2 format files to true type font files. */ #include #include "./file.h" #include 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"; // Note: update woff2_dec_fuzzer_new_entry.cc if this pattern changes. string input = woff2::GetFileContent(filename); const uint8_t* raw_input = reinterpret_cast(input.data()); string output(std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()), woff2::kDefaultMaxSize), 0); woff2::WOFF2StringOut out(&output); const bool ok = woff2::ConvertWOFF2ToTTF(raw_input, input.size(), &out); if (ok) { woff2::SetFileContents(outfilename, output.begin(), output.begin() + out.Size()); } return ok ? 0 : 1; } woff2-1.0.2/src/woff2_enc.cc000066400000000000000000000364201320236240000155000ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Library for converting TTF format font files to their WOFF2 versions. */ #include #include #include #include #include #include #include #include #include "./buffer.h" #include "./font.h" #include "./normalize.h" #include "./round.h" #include "./store_bytes.h" #include "./table_tags.h" #include "./transform.h" #include "./variable_length.h" #include "./woff2_common.h" namespace woff2 { namespace { using std::string; using std::vector; const size_t kWoff2HeaderSize = 48; const size_t kWoff2EntrySize = 20; bool Compress(const uint8_t* data, const size_t len, uint8_t* result, uint32_t* result_len, BrotliEncoderMode mode, int quality) { size_t compressed_len = *result_len; if (BrotliEncoderCompress(quality, BROTLI_DEFAULT_WINDOW, mode, 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, int quality) { return Compress(data, len, result, result_len, BROTLI_MODE_FONT, quality); } bool TextCompress(const uint8_t* data, const size_t len, uint8_t* result, uint32_t* result_len, int quality) { return Compress(data, len, result, result_len, BROTLI_MODE_TEXT, quality); } 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 = (table.flags & 0xC0) | 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 FontCollection& font_collection, const std::vector
& tables, std::map, uint16_t> index_by_tag_offset, size_t compressed_data_length, size_t extended_metadata_length) { size_t size = kWoff2HeaderSize; for (const auto& table : tables) { size += TableEntrySize(table); } // for collections only, collection tables if (font_collection.flavor == kTtcFontFlavor) { size += 4; // UInt32 Version of TTC Header size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts size += 4 * font_collection.fonts.size(); // UInt32 flavor for each for (const auto& font : font_collection.fonts) { size += Size255UShort(font.tables.size()); // 255UInt16 numTables for (const auto& entry : font.tables) { const Font::Table& table = entry.second; // no collection entry for xform table if (table.tag & 0x80808080) continue; std::pair tag_offset(table.tag, table.offset); uint16_t table_index = index_by_tag_offset[tag_offset]; size += Size255UShort(table_index); // 255UInt16 index entry } } } // compressed data size += compressed_data_length; size = Round4(size); size += extended_metadata_length; return size; } size_t ComputeUncompressedLength(const Font& font) { // sfnt header + offset table size_t size = 12 + 16 * font.num_tables; for (const auto& entry : font.tables) { const Font::Table& table = entry.second; if (table.tag & 0x80808080) continue; // xform tables don't stay if (table.IsReused()) continue; // don't have to pay twice size += Round4(table.length); } return size; } size_t ComputeUncompressedLength(const FontCollection& font_collection) { if (font_collection.flavor != kTtcFontFlavor) { return ComputeUncompressedLength(font_collection.fonts[0]); } size_t size = CollectionHeaderSize(font_collection.header_version, font_collection.fonts.size()); for (const auto& font : font_collection.fonts) { size += ComputeUncompressedLength(font); } 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.IsReused()) { continue; } 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 TransformFontCollection(FontCollection* font_collection) { for (auto& font : font_collection->fonts) { if (!TransformGlyfAndLocaTables(&font)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "glyf/loca transformation failed.\n"); #endif return FONT_COMPRESSION_FAILURE(); } } return true; } bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, uint8_t *result, size_t *result_length) { WOFF2Params params; return ConvertTTFToWOFF2(data, length, result, result_length, params); } bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, uint8_t *result, size_t *result_length, const WOFF2Params& params) { FontCollection font_collection; if (!ReadFontCollection(data, length, &font_collection)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Parsing of the input font failed.\n"); #endif return FONT_COMPRESSION_FAILURE(); } if (!NormalizeFontCollection(&font_collection)) { return FONT_COMPRESSION_FAILURE(); } if (params.allow_transforms && !TransformFontCollection(&font_collection)) { return FONT_COMPRESSION_FAILURE(); } else { // glyf/loca use 11 to flag "not transformed" for (auto& font : font_collection.fonts) { Font::Table* glyf_table = font.FindTable(kGlyfTableTag); Font::Table* loca_table = font.FindTable(kLocaTableTag); if (glyf_table) { glyf_table->flag_byte |= 0xc0; } if (loca_table) { loca_table->flag_byte |= 0xc0; } } } // 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 = 0; for (const auto& font : font_collection.fonts) { total_transform_length += ComputeTotalTransformLength(font); } size_t compression_buffer_size = CompressedBufferSize(total_transform_length); std::vector compression_buf(compression_buffer_size); uint32_t total_compressed_length = compression_buffer_size; // Collect all transformed data into one place in output order. std::vector transform_buf(total_transform_length); size_t transform_offset = 0; for (const auto& font : font_collection.fonts) { for (const auto tag : font.OutputOrderedTags()) { const Font::Table& original = font.tables.at(tag); if (original.IsReused()) continue; if (tag & 0x80808080) continue; const Font::Table* table_to_store = font.FindTable(tag ^ 0x80808080); if (table_to_store == NULL) table_to_store = &original; StoreBytes(table_to_store->data, table_to_store->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, params.brotli_quality)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Compression of combined table failed.\n"); #endif return FONT_COMPRESSION_FAILURE(); } #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Compressed %zu to %u.\n", total_transform_length, total_compressed_length); #endif // Compress the extended metadata // TODO(user): how does this apply to collections uint32_t compressed_metadata_buf_length = CompressedBufferSize(params.extended_metadata.length()); std::vector compressed_metadata_buf(compressed_metadata_buf_length); if (params.extended_metadata.length() > 0) { if (!TextCompress((const uint8_t*)params.extended_metadata.data(), params.extended_metadata.length(), compressed_metadata_buf.data(), &compressed_metadata_buf_length, params.brotli_quality)) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Compression of extended metadata failed.\n"); #endif return FONT_COMPRESSION_FAILURE(); } } else { compressed_metadata_buf_length = 0; } std::vector
tables; std::map, uint16_t> index_by_tag_offset; for (const auto& font : font_collection.fonts) { for (const auto tag : font.OutputOrderedTags()) { const Font::Table& src_table = font.tables.at(tag); if (src_table.IsReused()) { continue; } std::pair tag_offset(src_table.tag, src_table.offset); if (index_by_tag_offset.find(tag_offset) == index_by_tag_offset.end()) { index_by_tag_offset[tag_offset] = tables.size(); } else { return false; } Table table; table.tag = src_table.tag; table.flags = src_table.flag_byte; 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 = transformed_table->flag_byte; table.flags |= kWoff2FlagsTransform; table.transform_length = transformed_table->length; transformed_data = transformed_table->data; } tables.push_back(table); } } size_t woff2_length = ComputeWoff2Length(font_collection, tables, index_by_tag_offset, total_compressed_length, compressed_metadata_buf_length); if (woff2_length > *result_length) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", *result_length, woff2_length); #endif return FONT_COMPRESSION_FAILURE(); } *result_length = woff2_length; size_t offset = 0; // start of woff2 header (http://www.w3.org/TR/WOFF2/#woff20Header) StoreU32(kWoff2Signature, &offset, result); if (font_collection.flavor != kTtcFontFlavor) { StoreU32(font_collection.fonts[0].flavor, &offset, result); } else { StoreU32(kTtcFontFlavor, &offset, result); } StoreU32(woff2_length, &offset, result); Store16(tables.size(), &offset, result); Store16(0, &offset, result); // reserved // totalSfntSize StoreU32(ComputeUncompressedLength(font_collection), &offset, result); StoreU32(total_compressed_length, &offset, result); // totalCompressedSize // Let's just all be v1.0 Store16(1, &offset, result); // majorVersion Store16(0, &offset, result); // minorVersion 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(params.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 // end of woff2 header // table directory (http://www.w3.org/TR/WOFF2/#table_dir_format) for (const auto& table : tables) { StoreTableEntry(table, &offset, result); } // for collections only, collection table directory if (font_collection.flavor == kTtcFontFlavor) { StoreU32(font_collection.header_version, &offset, result); Store255UShort(font_collection.fonts.size(), &offset, result); for (const Font& font : font_collection.fonts) { uint16_t num_tables = 0; for (const auto& entry : font.tables) { const Font::Table& table = entry.second; if (table.tag & 0x80808080) continue; // don't write xform tables num_tables++; } Store255UShort(num_tables, &offset, result); StoreU32(font.flavor, &offset, result); for (const auto& entry : font.tables) { const Font::Table& table = entry.second; if (table.tag & 0x80808080) continue; // don't write xform tables // for reused tables, only the original has an updated offset uint32_t table_offset = table.IsReused() ? table.reuse_of->offset : table.offset; uint32_t table_length = table.IsReused() ? table.reuse_of->length : table.length; std::pair tag_offset(table.tag, table_offset); if (index_by_tag_offset.find(tag_offset) == index_by_tag_offset.end()) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Missing table index for offset 0x%08x\n", table_offset); #endif return FONT_COMPRESSION_FAILURE(); } uint16_t index = index_by_tag_offset[tag_offset]; Store255UShort(index, &offset, result); } } } // compressed data format (http://www.w3.org/TR/WOFF2/#table_format) StoreBytes(&compression_buf[0], total_compressed_length, &offset, result); offset = Round4(offset); StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, &offset, result); if (*result_length != offset) { #ifdef FONT_COMPRESSION_BIN fprintf(stderr, "Mismatch between computed and actual length " "(%zd vs %zd)\n", *result_length, offset); #endif return FONT_COMPRESSION_FAILURE(); } return true; } } // namespace woff2 woff2-1.0.2/src/woff2_info.cc000066400000000000000000000115131320236240000156620ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* A commandline tool for dumping info about a woff2 file. */ #include #include "file.h" #include "./woff2_common.h" #include "./buffer.h" #include "./font.h" #include "./table_tags.h" #include "./variable_length.h" std::string PrintTag(int tag) { if (tag & 0x80808080) { return std::string("_xfm"); // print _xfm for xform tables (else garbage) } char printable[] = { static_cast((tag >> 24) & 0xFF), static_cast((tag >> 16) & 0xFF), static_cast((tag >> 8) & 0xFF), static_cast(tag & 0xFF) }; return std::string(printable, 4); } 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); woff2::Buffer file(reinterpret_cast(input.data()), input.size()); printf("WOFF2Header\n"); uint32_t signature, flavor, length, totalSfntSize, totalCompressedSize; uint32_t metaOffset, metaLength, metaOrigLength, privOffset, privLength; uint16_t num_tables, reserved, major, minor; if (!file.ReadU32(&signature)) return 1; if (!file.ReadU32(&flavor)) return 1; if (!file.ReadU32(&length)) return 1; if (!file.ReadU16(&num_tables)) return 1; if (!file.ReadU16(&reserved)) return 1; if (!file.ReadU32(&totalSfntSize)) return 1; if (!file.ReadU32(&totalCompressedSize)) return 1; if (!file.ReadU16(&major)) return 1; if (!file.ReadU16(&minor)) return 1; if (!file.ReadU32(&metaOffset)) return 1; if (!file.ReadU32(&metaLength)) return 1; if (!file.ReadU32(&metaOrigLength)) return 1; if (!file.ReadU32(&privOffset)) return 1; if (!file.ReadU32(&privLength)) return 1; if (signature != 0x774F4632) { printf("Invalid signature: %08x\n", signature); return 1; } printf("signature 0x%08x\n", signature); printf("flavor 0x%08x\n", flavor); printf("length %d\n", length); printf("numTables %d\n", num_tables); printf("reserved %d\n", reserved); printf("totalSfntSize %d\n", totalSfntSize); printf("totalCompressedSize %d\n", totalCompressedSize); printf("majorVersion %d\n", major); printf("minorVersion %d\n", minor); printf("metaOffset %d\n", metaOffset); printf("metaLength %d\n", metaLength); printf("metaOrigLength %d\n", metaOrigLength); printf("privOffset %d\n", privOffset); printf("privLength %d\n", privLength); std::vector table_tags; printf("TableDirectory starts at +%zu\n", file.offset()); printf("Entry offset flags tag origLength txLength\n"); for (auto i = 0; i < num_tables; i++) { size_t offset = file.offset(); uint8_t flags; uint32_t tag, origLength, transformLength; if (!file.ReadU8(&flags)) return 1; if ((flags & 0x3f) == 0x3f) { if (!file.ReadU32(&tag)) return 1; } else { tag = woff2::kKnownTags[flags & 0x3f]; } table_tags.push_back(tag); if (!ReadBase128(&file, &origLength)) return 1; printf("%5d %6zu 0x%02x %s %10d", i, offset, flags, PrintTag(tag).c_str(), origLength); uint8_t xform_version = (flags >> 6) & 0x3; if (tag == woff2::kGlyfTableTag || tag == woff2::kLocaTableTag) { if (xform_version == 0) { if (!ReadBase128(&file, &transformLength)) return 1; printf(" %8d", transformLength); } } else if (xform_version > 0) { if (!ReadBase128(&file, &transformLength)) return 1; printf(" %8d", transformLength); } printf("\n"); } // Collection header if (flavor == woff2::kTtcFontFlavor) { uint32_t version, numFonts; if (!file.ReadU32(&version)) return 1; if (!woff2::Read255UShort(&file, &numFonts)) return 1; printf("CollectionHeader 0x%08x %d fonts\n", version, numFonts); for (auto i = 0; i < numFonts; i++) { uint32_t numTables, flavor; if (!woff2::Read255UShort(&file, &numTables)) return 1; if (!file.ReadU32(&flavor)) return 1; printf("CollectionFontEntry %d flavor 0x%08x %d tables\n", i, flavor, numTables); for (auto j = 0; j < numTables; j++) { uint32_t table_idx; if (!woff2::Read255UShort(&file, &table_idx)) return 1; if (table_idx >= table_tags.size()) return 1; printf(" %d %s (idx %d)\n", j, PrintTag(table_tags[table_idx]).c_str(), table_idx); } } } printf("TableDirectory ends at +%zu\n", file.offset()); return 0; } woff2-1.0.2/src/woff2_out.cc000066400000000000000000000030341320236240000155350ustar00rootroot00000000000000/* Copyright 2014 Google Inc. All Rights Reserved. Distributed under MIT license. See file LICENSE for detail or copy at https://opensource.org/licenses/MIT */ /* Output buffer for WOFF2 decompression. */ #include using std::string; namespace woff2 { WOFF2StringOut::WOFF2StringOut(string* buf) : buf_(buf), max_size_(kDefaultMaxSize), offset_(0) {} bool WOFF2StringOut::Write(const void *buf, size_t n) { return Write(buf, offset_, n); } bool WOFF2StringOut::Write(const void *buf, size_t offset, size_t n) { if (offset > max_size_ || n > max_size_ - offset) { return false; } if (offset == buf_->size()) { buf_->append(static_cast(buf), n); } else { if (offset + n > buf_->size()) { buf_->append(offset + n - buf_->size(), 0); } buf_->replace(offset, n, static_cast(buf), n); } offset_ = std::max(offset_, offset + n); return true; } void WOFF2StringOut::SetMaxSize(size_t max_size) { max_size_ = max_size; if (offset_ > max_size_) { offset_ = max_size_; } } WOFF2MemoryOut::WOFF2MemoryOut(uint8_t* buf, size_t buf_size) : buf_(buf), buf_size_(buf_size), offset_(0) {} bool WOFF2MemoryOut::Write(const void *buf, size_t n) { return Write(buf, offset_, n); } bool WOFF2MemoryOut::Write(const void *buf, size_t offset, size_t n) { if (offset > buf_size_ || n > buf_size_ - offset) { return false; } std::memcpy(buf_ + offset, buf, n); offset_ = std::max(offset_, offset + n); return true; } } // namespace woff2