├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── cmake ├── FindBrotliDec.cmake └── FindBrotliEnc.cmake ├── include └── woff2 │ ├── decode.h │ ├── encode.h │ └── output.h └── src ├── buffer.h ├── convert_woff2ttf_fuzzer.cc ├── convert_woff2ttf_fuzzer_new_entry.cc ├── file.h ├── font.cc ├── font.h ├── glyph.cc ├── glyph.h ├── normalize.cc ├── normalize.h ├── port.h ├── round.h ├── store_bytes.h ├── table_tags.cc ├── table_tags.h ├── transform.cc ├── transform.h ├── variable_length.cc ├── variable_length.h ├── woff2_common.cc ├── woff2_common.h ├── woff2_compress.cc ├── woff2_dec.cc ├── woff2_decompress.cc ├── woff2_enc.cc ├── woff2_info.cc └── woff2_out.cc /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | /woff2_compress 4 | /woff2_decompress 5 | /woff2_info 6 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "brotli"] 2 | path = brotli 3 | url = https://github.com/google/brotli.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Igalia S.L. All Rights Reserved. 2 | # 3 | # Distributed under MIT license. 4 | # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | 6 | # Ubuntu 12.04 LTS has CMake 2.8.7, and is an important target since 7 | # several CI services, such as Travis and Drone, use it. Solaris 11 8 | # has 2.8.6, and it's not difficult to support if you already have to 9 | # support 2.8.7. 10 | cmake_minimum_required(VERSION 2.8.6) 11 | 12 | project(woff2) 13 | 14 | include(GNUInstallDirs) 15 | 16 | # Build options 17 | option(BUILD_SHARED_LIBS "Build shared libraries" ON) 18 | option(CANONICAL_PREFIXES "Canonical prefixes" OFF) 19 | option(NOISY_LOGGING "Noisy logging" ON) 20 | 21 | # Version information 22 | set(WOFF2_VERSION 1.0.2) 23 | 24 | # When building shared libraries it is important to set the correct rpath 25 | # See https://cmake.org/Wiki/CMake_RPATH_handling#Always_full_RPATH 26 | set(CMAKE_SKIP_BUILD_RPATH FALSE) 27 | set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE) 28 | set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) 29 | list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_LIBDIR}" isSystemDir) 30 | if ("${isSystemDir}" STREQUAL "-1") 31 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_LIBDIR}") 32 | endif() 33 | 34 | # Find Brotli dependencies 35 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") 36 | find_package(BrotliDec) 37 | if (NOT BROTLIDEC_FOUND) 38 | message(FATAL_ERROR "librotlidec is needed to build woff2.") 39 | endif () 40 | find_package(BrotliEnc) 41 | if (NOT BROTLIENC_FOUND) 42 | message(FATAL_ERROR "librotlienc is needed to build woff2.") 43 | endif () 44 | 45 | # Set compiler flags 46 | if (NOT CANONICAL_PREFIXES) 47 | add_definitions(-no-canonical-prefixes) 48 | endif () 49 | if (NOISY_LOGGING) 50 | add_definitions(-DFONT_COMPRESSION_BIN) 51 | endif () 52 | add_definitions(-D__STDC_FORMAT_MACROS) 53 | set(COMMON_FLAGS -fno-omit-frame-pointer) 54 | 55 | if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 56 | add_definitions(-DOS_MACOSX) 57 | else () 58 | set(COMMON_FLAGS "${COMMON_FLAG} -fno-omit-frame-pointer") 59 | endif() 60 | 61 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${COMMON_FLAG}") 62 | 63 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${COMMON_FLAG}") 64 | set(CMAKE_CXX_STANDARD 11) 65 | 66 | # Set search path for our private/public headers as well as Brotli headers 67 | include_directories("src" "include" 68 | "${BROTLIDEC_INCLUDE_DIRS}" "${BROTLIENC_INCLUDE_DIRS}") 69 | 70 | # Common part used by decoder and encoder 71 | add_library(woff2common 72 | src/table_tags.cc 73 | src/variable_length.cc 74 | src/woff2_common.cc) 75 | 76 | # WOFF2 Decoder 77 | add_library(woff2dec 78 | src/woff2_dec.cc 79 | src/woff2_out.cc) 80 | target_link_libraries(woff2dec woff2common "${BROTLIDEC_LIBRARIES}") 81 | add_executable(woff2_decompress src/woff2_decompress.cc) 82 | target_link_libraries(woff2_decompress woff2dec) 83 | 84 | # WOFF2 Encoder 85 | add_library(woff2enc 86 | src/font.cc 87 | src/glyph.cc 88 | src/normalize.cc 89 | src/transform.cc 90 | src/woff2_enc.cc) 91 | target_link_libraries(woff2enc woff2common "${BROTLIENC_LIBRARIES}") 92 | add_executable(woff2_compress src/woff2_compress.cc) 93 | target_link_libraries(woff2_compress woff2enc) 94 | 95 | # WOFF2 info 96 | add_executable(woff2_info src/woff2_info.cc) 97 | target_link_libraries(woff2_info woff2common) 98 | 99 | foreach(lib woff2common woff2dec woff2enc) 100 | set_target_properties(${lib} PROPERTIES 101 | SOVERSION ${WOFF2_VERSION} 102 | VERSION ${WOFF2_VERSION} 103 | POSITION_INDEPENDENT_CODE TRUE) 104 | endforeach() 105 | 106 | # Fuzzer libraries 107 | add_library(convert_woff2ttf_fuzzer STATIC src/convert_woff2ttf_fuzzer.cc) 108 | target_link_libraries(convert_woff2ttf_fuzzer woff2dec) 109 | add_library(convert_woff2ttf_fuzzer_new_entry STATIC src/convert_woff2ttf_fuzzer_new_entry.cc) 110 | target_link_libraries(convert_woff2ttf_fuzzer_new_entry woff2dec) 111 | 112 | # PC files 113 | include(CMakeParseArguments) 114 | 115 | function(generate_pkg_config_path outvar path) 116 | string(LENGTH "${path}" path_length) 117 | 118 | set(path_args ${ARGV}) 119 | list(REMOVE_AT path_args 0 1) 120 | list(LENGTH path_args path_args_remaining) 121 | 122 | set("${outvar}" "${path}") 123 | 124 | while(path_args_remaining GREATER 1) 125 | list(GET path_args 0 name) 126 | list(GET path_args 1 value) 127 | 128 | get_filename_component(value_full "${value}" ABSOLUTE) 129 | string(LENGTH "${value}" value_length) 130 | 131 | if(path_length EQUAL value_length AND path STREQUAL value) 132 | set("${outvar}" "\${${name}}") 133 | break() 134 | elseif(path_length GREATER value_length) 135 | # We might be in a subdirectory of the value, but we have to be 136 | # careful about a prefix matching but not being a subdirectory 137 | # (for example, /usr/lib64 is not a subdirectory of /usr/lib). 138 | # We'll do this by making sure the next character is a directory 139 | # separator. 140 | string(SUBSTRING "${path}" ${value_length} 1 sep) 141 | if(sep STREQUAL "/") 142 | string(SUBSTRING "${path}" 0 ${value_length} s) 143 | if(s STREQUAL value) 144 | string(SUBSTRING "${path}" "${value_length}" -1 suffix) 145 | set("${outvar}" "\${${name}}${suffix}") 146 | break() 147 | endif() 148 | endif() 149 | endif() 150 | 151 | list(REMOVE_AT path_args 0 1) 152 | list(LENGTH path_args path_args_remaining) 153 | endwhile() 154 | 155 | set("${outvar}" "${${outvar}}" PARENT_SCOPE) 156 | endfunction(generate_pkg_config_path) 157 | 158 | function(generate_pkg_config output_file) 159 | set (options) 160 | set (oneValueArgs NAME DESCRIPTION URL VERSION PREFIX LIBDIR INCLUDEDIR) 161 | set (multiValueArgs DEPENDS DEPENDS_PRIVATE CFLAGS LIBRARIES) 162 | cmake_parse_arguments(GEN_PKG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) 163 | unset (options) 164 | unset (oneValueArgs) 165 | unset (multiValueArgs) 166 | 167 | if(NOT GEN_PKG_PREFIX) 168 | set(GEN_PKG_PREFIX "${CMAKE_INSTALL_PREFIX}") 169 | endif() 170 | 171 | if(NOT GEN_PKG_LIBDIR) 172 | set(GEN_PKG_LIBDIR "${CMAKE_INSTALL_FULL_LIBDIR}") 173 | endif() 174 | generate_pkg_config_path(GEN_PKG_LIBDIR "${GEN_PKG_LIBDIR}" 175 | prefix "${GEN_PKG_PREFIX}") 176 | 177 | if(NOT GEN_PKG_INCLUDEDIR) 178 | set(GEN_PKG_INCLUDEDIR "${CMAKE_INSTALL_FULL_INCLUDEDIR}") 179 | endif() 180 | generate_pkg_config_path(GEN_PKG_INCLUDEDIR "${GEN_PKG_INCLUDEDIR}" 181 | prefix "${GEN_PKG_PREFIX}") 182 | 183 | file(WRITE "${output_file}" "prefix=${GEN_PKG_PREFIX}\n") 184 | file(APPEND "${output_file}" "libdir=${GEN_PKG_LIBDIR}\n") 185 | file(APPEND "${output_file}" "includedir=${GEN_PKG_INCLUDEDIR}\n") 186 | file(APPEND "${output_file}" "\n") 187 | 188 | if(GEN_PKG_NAME) 189 | file(APPEND "${output_file}" "Name: ${GEN_PKG_NAME}\n") 190 | else() 191 | file(APPEND "${output_file}" "Name: ${CMAKE_PROJECT_NAME}\n") 192 | endif() 193 | 194 | if(GEN_PKG_DESCRIPTION) 195 | file(APPEND "${output_file}" "Description: ${GEN_PKG_DESCRIPTION}\n") 196 | endif() 197 | 198 | if(GEN_PKG_URL) 199 | file(APPEND "${output_file}" "URL: ${GEN_PKG_URL}\n") 200 | endif() 201 | 202 | if(GEN_PKG_VERSION) 203 | file(APPEND "${output_file}" "Version: ${GEN_PKG_VERSION}\n") 204 | endif() 205 | 206 | if(GEN_PKG_DEPENDS) 207 | file(APPEND "${output_file}" "Requires: ${GEN_PKG_DEPENDS}\n") 208 | endif() 209 | 210 | if(GEN_PKG_DEPENDS_PRIVATE) 211 | file(APPEND "${output_file}" "Requires.private:") 212 | foreach(lib ${GEN_PKG_DEPENDS_PRIVATE}) 213 | file(APPEND "${output_file}" " ${lib}") 214 | endforeach() 215 | file(APPEND "${output_file}" "\n") 216 | endif() 217 | 218 | if(GEN_PKG_LIBRARIES) 219 | set(libs) 220 | 221 | file(APPEND "${output_file}" "Libs: -L\${libdir}") 222 | foreach(lib ${GEN_PKG_LIBRARIES}) 223 | file(APPEND "${output_file}" " -l${lib}") 224 | endforeach() 225 | file(APPEND "${output_file}" "\n") 226 | endif() 227 | 228 | file(APPEND "${output_file}" "Cflags: -I\${includedir}") 229 | if(GEN_PKG_CFLAGS) 230 | foreach(cflag ${GEN_PKG_CFLAGS}) 231 | file(APPEND "${output_file}" " ${cflag}") 232 | endforeach() 233 | endif() 234 | file(APPEND "${output_file}" "\n") 235 | endfunction(generate_pkg_config) 236 | 237 | generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2common.pc" 238 | NAME libwoff2common 239 | DESCRIPTION "Shared data used by libwoff2 and libwoff2dec libraries" 240 | URL "https://github.com/google/woff2" 241 | VERSION "${WOFF2_VERSION}" 242 | LIBRARIES woff2common) 243 | 244 | generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2dec.pc" 245 | NAME libwoff2dec 246 | DESCRIPTION "WOFF2 decoder library" 247 | URL "https://github.com/google/woff2" 248 | VERSION "${WOFF2_VERSION}" 249 | DEPENDS libbrotlidec 250 | DEPENDS_PRIVATE libwoff2common 251 | LIBRARIES woff2dec) 252 | 253 | generate_pkg_config ("${CMAKE_CURRENT_BINARY_DIR}/libwoff2enc.pc" 254 | NAME libwoff2enc 255 | DESCRIPTION "WOFF2 encoder library" 256 | URL "https://github.com/google/woff2" 257 | VERSION "${WOFF2_VERSION}" 258 | DEPENDS libbrotlienc 259 | DEPENDS_PRIVATE libwoff2common 260 | LIBRARIES woff2enc) 261 | 262 | # Installation 263 | if (NOT BUILD_SHARED_LIBS) 264 | install( 265 | TARGETS woff2_decompress woff2_compress woff2_info 266 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 267 | ) 268 | endif() 269 | 270 | install( 271 | TARGETS woff2common woff2dec woff2enc 272 | ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" 273 | LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" 274 | RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" 275 | ) 276 | install( 277 | DIRECTORY include/woff2 278 | DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" 279 | ) 280 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2common.pc" 281 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 282 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2dec.pc" 283 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 284 | install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libwoff2enc.pc" 285 | DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") 286 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Want to contribute? Great! First, read this page (including the small print at 2 | the end). 3 | 4 | ### Before you contribute 5 | Before we can use your code, you must sign the 6 | [Google Individual Contributor License Agreement] 7 | (https://cla.developers.google.com/about/google-individual) 8 | (CLA), which you can do online. The CLA is necessary mainly because you own the 9 | copyright to your changes, even after your contribution becomes part of our 10 | codebase, so we need your permission to use and distribute your code. We also 11 | need to be sure of various other things—for instance that you'll tell us if you 12 | know that your code infringes on other people's patents. You don't have to sign 13 | the CLA until after you've submitted your code for review and a member has 14 | approved it, but you must do it before we can put your code into our codebase. 15 | Before you start working on a larger contribution, you should get in touch with 16 | us first through the issue tracker with your idea so that we can help out and 17 | possibly guide you. Coordinating up front makes it much easier to avoid 18 | frustration later on. 19 | 20 | ### Code reviews 21 | All submissions, including submissions by project members, require review. We 22 | use Github pull requests for this purpose. 23 | 24 | ### The small print 25 | Contributions made by corporations are covered by a different agreement than 26 | the one above, the [Software Grant and Corporate Contributor License Agreement] 27 | (https://cla.developers.google.com/about/google-corporate). 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013-2017 by the WOFF2 Authors. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OS := $(shell uname) 2 | 3 | CPPFLAGS = -I./brotli/c/include/ -I./src -I./include 4 | 5 | AR ?= ar 6 | CC ?= gcc 7 | CXX ?= g++ 8 | 9 | # It's helpful to be able to turn these off for fuzzing 10 | CANONICAL_PREFIXES ?= -no-canonical-prefixes 11 | NOISY_LOGGING ?= -DFONT_COMPRESSION_BIN 12 | COMMON_FLAGS = -fno-omit-frame-pointer $(CANONICAL_PREFIXES) $(NOISY_LOGGING) -D __STDC_FORMAT_MACROS 13 | 14 | ARFLAGS = crf 15 | 16 | ifeq ($(OS), Darwin) 17 | CPPFLAGS += -DOS_MACOSX 18 | ARFLAGS = cr 19 | else 20 | COMMON_FLAGS += -fno-tree-vrp 21 | endif 22 | 23 | 24 | CFLAGS += $(COMMON_FLAGS) 25 | CXXFLAGS += $(COMMON_FLAGS) -std=c++11 26 | 27 | SRCDIR = src 28 | 29 | OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \ 30 | woff2_dec.o woff2_enc.o woff2_common.o woff2_out.o \ 31 | variable_length.o 32 | 33 | BROTLI = brotli 34 | BROTLIOBJ = $(BROTLI)/bin/obj/c 35 | ENCOBJ = $(BROTLIOBJ)/enc/*.o 36 | DECOBJ = $(BROTLIOBJ)/dec/*.o 37 | COMMONOBJ = $(BROTLIOBJ)/common/*.o 38 | 39 | OBJS = $(patsubst %, $(SRCDIR)/%, $(OUROBJ)) 40 | EXECUTABLES=woff2_compress woff2_decompress woff2_info 41 | EXE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(EXECUTABLES)) 42 | ARCHIVES=convert_woff2ttf_fuzzer convert_woff2ttf_fuzzer_new_entry 43 | ARCHIVE_OBJS=$(patsubst %, $(SRCDIR)/%.o, $(ARCHIVES)) 44 | 45 | ifeq (,$(wildcard $(BROTLI)/*)) 46 | $(error Brotli dependency not found : you must initialize the Git submodule) 47 | endif 48 | 49 | all : $(OBJS) $(EXECUTABLES) $(ARCHIVES) 50 | 51 | $(ARCHIVES) : $(ARCHIVE_OBJS) $(OBJS) deps 52 | $(AR) $(ARFLAGS) $(SRCDIR)/$@.a $(OBJS) \ 53 | $(COMMONOBJ) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o 54 | 55 | $(EXECUTABLES) : $(EXE_OBJS) deps 56 | $(CXX) $(LFLAGS) $(OBJS) $(COMMONOBJ) $(ENCOBJ) $(DECOBJ) $(SRCDIR)/$@.o -o $@ 57 | 58 | deps : 59 | $(MAKE) -C $(BROTLI) lib 60 | 61 | clean : 62 | rm -f $(OBJS) $(EXE_OBJS) $(EXECUTABLES) 63 | $(MAKE) -C $(BROTLI) clean 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is a README for the font compression reference code. There are several 2 | compression related modules in this repository. 3 | 4 | brotli/ contains reference code for the Brotli byte-level compression 5 | algorithm. Note that it is licensed under the MIT license. 6 | 7 | src/ contains the C++ code for compressing and decompressing fonts. 8 | 9 | # Build & Run 10 | 11 | This document documents how to run the compression reference code. At this 12 | writing, the code, while it is intended to produce a bytestream that can be 13 | reconstructed into a working font, the reference decompression code is not 14 | done, and the exact format of that bytestream is subject to change. 15 | 16 | The build process depends on the g++ compiler. 17 | 18 | ## Build 19 | 20 | On a standard Unix-style environment: 21 | 22 | ``` 23 | git clone --recursive https://github.com/google/woff2.git 24 | cd woff2 25 | make clean all 26 | ``` 27 | 28 | Alternatively, if Brotli is already installed on your system you can use CMake 29 | to build executables and libraries: 30 | 31 | ``` 32 | git clone https://github.com/google/woff2.git 33 | cd woff2 34 | mkdir out 35 | cd out 36 | cmake .. 37 | make 38 | make install 39 | ``` 40 | 41 | By default, shared libraries are built. To use static linkage, do: 42 | 43 | ``` 44 | cd woff2 45 | mkdir out-static 46 | cmake -DBUILD_SHARED_LIBS=OFF .. 47 | make 48 | make install 49 | ``` 50 | 51 | ## Run 52 | 53 | Ensure the binaries from the build process are in your $PATH, then: 54 | 55 | ``` 56 | woff2_compress myfont.ttf 57 | woff2_decompress myfont.woff2 58 | ``` 59 | 60 | # References 61 | 62 | http://www.w3.org/TR/WOFF2/ 63 | http://www.w3.org/Submission/MTX/ 64 | 65 | Also please refer to documents (currently Google Docs): 66 | 67 | WOFF Ultra Condensed file format: proposals and discussion of wire format 68 | issues (PDF is in docs/ directory) 69 | 70 | WIFF Ultra Condensed: more discussion of results and compression techniques. 71 | This tool was used to prepare the data in that document. 72 | -------------------------------------------------------------------------------- /cmake/FindBrotliDec.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Igalia S.L. All Rights Reserved. 2 | # 3 | # Distributed under MIT license. 4 | # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | 6 | # Try to find BrotliDec. Once done, this will define 7 | # 8 | # BROTLIDEC_FOUND - system has BrotliDec. 9 | # BROTLIDEC_INCLUDE_DIRS - the BrotliDec include directories 10 | # BROTLIDEC_LIBRARIES - link these to use BrotliDec. 11 | 12 | find_package(PkgConfig) 13 | 14 | pkg_check_modules(PC_BROTLIDEC libbrotlidec) 15 | 16 | find_path(BROTLIDEC_INCLUDE_DIRS 17 | NAMES brotli/decode.h 18 | HINTS ${PC_BROTLIDEC_INCLUDEDIR} 19 | ) 20 | 21 | find_library(BROTLIDEC_LIBRARIES 22 | NAMES brotlidec 23 | HINTS ${PC_BROTLIDEC_LIBDIR} 24 | ) 25 | 26 | include(FindPackageHandleStandardArgs) 27 | find_package_handle_standard_args(BrotliDec 28 | REQUIRED_VARS BROTLIDEC_INCLUDE_DIRS BROTLIDEC_LIBRARIES 29 | FOUND_VAR BROTLIDEC_FOUND 30 | VERSION_VAR PC_BROTLIDEC_VERSION) 31 | 32 | mark_as_advanced( 33 | BROTLIDEC_INCLUDE_DIRS 34 | BROTLIDEC_LIBRARIES 35 | ) 36 | -------------------------------------------------------------------------------- /cmake/FindBrotliEnc.cmake: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Igalia S.L. All Rights Reserved. 2 | # 3 | # Distributed under MIT license. 4 | # See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | 6 | # Try to find BrotliEnc. Once done, this will define 7 | # 8 | # BROTLIENC_FOUND - system has BrotliEnc. 9 | # BROTLIENC_INCLUDE_DIRS - the BrotliEnc include directories 10 | # BROTLIENC_LIBRARIES - link these to use BrotliEnc. 11 | 12 | find_package(PkgConfig) 13 | 14 | pkg_check_modules(PC_BROTLIENC libbrotlienc) 15 | 16 | find_path(BROTLIENC_INCLUDE_DIRS 17 | NAMES brotli/encode.h 18 | HINTS ${PC_BROTLIENC_INCLUDEDIR} 19 | ) 20 | 21 | find_library(BROTLIENC_LIBRARIES 22 | NAMES brotlienc 23 | HINTS ${PC_BROTLIENC_LIBDIR} 24 | ) 25 | 26 | include(FindPackageHandleStandardArgs) 27 | find_package_handle_standard_args(BrotliEnc 28 | REQUIRED_VARS BROTLIENC_INCLUDE_DIRS BROTLIENC_LIBRARIES 29 | FOUND_VAR BROTLIENC_FOUND 30 | VERSION_VAR PC_BROTLIENC_VERSION) 31 | 32 | mark_as_advanced( 33 | BROTLIENC_INCLUDE_DIRS 34 | BROTLIENC_LIBRARIES 35 | ) 36 | -------------------------------------------------------------------------------- /include/woff2/decode.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for converting WOFF2 format font files to their TTF versions. */ 8 | 9 | #ifndef WOFF2_WOFF2_DEC_H_ 10 | #define WOFF2_WOFF2_DEC_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace woff2 { 17 | 18 | // Compute the size of the final uncompressed font, or 0 on error. 19 | size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length); 20 | 21 | // Decompresses the font into the target buffer. The result_length should 22 | // be the same as determined by ComputeFinalSize(). Returns true on successful 23 | // decompression. 24 | // DEPRECATED; please prefer the version that takes a WOFF2Out* 25 | bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, 26 | const uint8_t *data, size_t length); 27 | 28 | // Decompresses the font into out. Returns true on success. 29 | // Works even if WOFF2Header totalSfntSize is wrong. 30 | // Please prefer this API. 31 | bool ConvertWOFF2ToTTF(const uint8_t *data, size_t length, 32 | WOFF2Out* out); 33 | 34 | } // namespace woff2 35 | 36 | #endif // WOFF2_WOFF2_DEC_H_ 37 | -------------------------------------------------------------------------------- /include/woff2/encode.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for converting WOFF2 format font files to their TTF versions. */ 8 | 9 | #ifndef WOFF2_WOFF2_ENC_H_ 10 | #define WOFF2_WOFF2_ENC_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace woff2 { 17 | 18 | struct WOFF2Params { 19 | WOFF2Params() : extended_metadata(""), brotli_quality(11), 20 | allow_transforms(true) {} 21 | 22 | std::string extended_metadata; 23 | int brotli_quality; 24 | bool allow_transforms; 25 | }; 26 | 27 | // Returns an upper bound on the size of the compressed file. 28 | size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length); 29 | size_t MaxWOFF2CompressedSize(const uint8_t *data, size_t length, 30 | const std::string &extended_metadata); 31 | 32 | // Compresses the font into the target buffer. *result_length should be at least 33 | // the value returned by MaxWOFF2CompressedSize(), upon return, it is set to the 34 | // actual compressed size. Returns true on successful compression. 35 | bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, 36 | uint8_t *result, size_t *result_length); 37 | bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, 38 | uint8_t *result, size_t *result_length, 39 | const WOFF2Params& params); 40 | 41 | } // namespace woff2 42 | 43 | #endif // WOFF2_WOFF2_ENC_H_ 44 | -------------------------------------------------------------------------------- /include/woff2/output.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2016 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Output buffer for WOFF2 decompression. */ 8 | 9 | #ifndef WOFF2_WOFF2_OUT_H_ 10 | #define WOFF2_WOFF2_OUT_H_ 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | namespace woff2 { 18 | 19 | // Suggested max size for output. 20 | const size_t kDefaultMaxSize = 30 * 1024 * 1024; 21 | 22 | /** 23 | * Output interface for the woff2 decoding. 24 | * 25 | * Writes to arbitrary offsets are supported to facilitate updating offset 26 | * table and checksums after tables are ready. Reading the current size is 27 | * supported so a 'loca' table can be built up while writing glyphs. 28 | * 29 | * By default limits size to kDefaultMaxSize. 30 | */ 31 | class WOFF2Out { 32 | public: 33 | virtual ~WOFF2Out(void) {} 34 | 35 | // Append n bytes of data from buf. 36 | // Return true if all written, false otherwise. 37 | virtual bool Write(const void *buf, size_t n) = 0; 38 | 39 | // Write n bytes of data from buf at offset. 40 | // Return true if all written, false otherwise. 41 | virtual bool Write(const void *buf, size_t offset, size_t n) = 0; 42 | 43 | virtual size_t Size() = 0; 44 | }; 45 | 46 | /** 47 | * Expanding memory block for woff2 out. By default limited to kDefaultMaxSize. 48 | */ 49 | class WOFF2StringOut : public WOFF2Out { 50 | public: 51 | // Create a writer that writes its data to buf. 52 | // buf->size() will grow to at most max_size 53 | // buf may be sized (e.g. using EstimateWOFF2FinalSize) or empty. 54 | explicit WOFF2StringOut(std::string *buf); 55 | 56 | bool Write(const void *buf, size_t n) override; 57 | bool Write(const void *buf, size_t offset, size_t n) override; 58 | size_t Size() override { return offset_; } 59 | size_t MaxSize() { return max_size_; } 60 | void SetMaxSize(size_t max_size); 61 | private: 62 | std::string *buf_; 63 | size_t max_size_; 64 | size_t offset_; 65 | }; 66 | 67 | /** 68 | * Fixed memory block for woff2 out. 69 | */ 70 | class WOFF2MemoryOut : public WOFF2Out { 71 | public: 72 | // Create a writer that writes its data to buf. 73 | WOFF2MemoryOut(uint8_t* buf, size_t buf_size); 74 | 75 | bool Write(const void *buf, size_t n) override; 76 | bool Write(const void *buf, size_t offset, size_t n) override; 77 | size_t Size() override { return offset_; } 78 | private: 79 | uint8_t* buf_; 80 | size_t buf_size_; 81 | size_t offset_; 82 | }; 83 | 84 | } // namespace woff2 85 | 86 | #endif // WOFF2_WOFF2_OUT_H_ 87 | -------------------------------------------------------------------------------- /src/buffer.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* The parts of ots.h & opentype-sanitiser.h that we need, taken from the 8 | https://code.google.com/p/ots/ project. */ 9 | 10 | #ifndef WOFF2_BUFFER_H_ 11 | #define WOFF2_BUFFER_H_ 12 | 13 | #if defined(_WIN32) 14 | #include 15 | typedef signed char int8_t; 16 | typedef unsigned char uint8_t; 17 | typedef short int16_t; 18 | typedef unsigned short uint16_t; 19 | typedef int int32_t; 20 | typedef unsigned int uint32_t; 21 | typedef __int64 int64_t; 22 | typedef unsigned __int64 uint64_t; 23 | #define ntohl(x) _byteswap_ulong (x) 24 | #define ntohs(x) _byteswap_ushort (x) 25 | #define htonl(x) _byteswap_ulong (x) 26 | #define htons(x) _byteswap_ushort (x) 27 | #else 28 | #include 29 | #include 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | namespace woff2 { 38 | 39 | #if defined(_MSC_VER) || !defined(FONT_COMPRESSION_DEBUG) 40 | #define FONT_COMPRESSION_FAILURE() false 41 | #else 42 | #define FONT_COMPRESSION_FAILURE() \ 43 | woff2::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__) 44 | inline bool Failure(const char *f, int l, const char *fn) { 45 | fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); 46 | fflush(stderr); 47 | return false; 48 | } 49 | #endif 50 | 51 | // ----------------------------------------------------------------------------- 52 | // Buffer helper class 53 | // 54 | // This class perform some trival buffer operations while checking for 55 | // out-of-bounds errors. As a family they return false if anything is amiss, 56 | // updating the current offset otherwise. 57 | // ----------------------------------------------------------------------------- 58 | class Buffer { 59 | public: 60 | Buffer(const uint8_t *data, size_t len) 61 | : buffer_(data), 62 | length_(len), 63 | offset_(0) { } 64 | 65 | bool Skip(size_t n_bytes) { 66 | return Read(NULL, n_bytes); 67 | } 68 | 69 | bool Read(uint8_t *data, size_t n_bytes) { 70 | if (n_bytes > 1024 * 1024 * 1024) { 71 | return FONT_COMPRESSION_FAILURE(); 72 | } 73 | if ((offset_ + n_bytes > length_) || 74 | (offset_ > length_ - n_bytes)) { 75 | return FONT_COMPRESSION_FAILURE(); 76 | } 77 | if (data) { 78 | std::memcpy(data, buffer_ + offset_, n_bytes); 79 | } 80 | offset_ += n_bytes; 81 | return true; 82 | } 83 | 84 | inline bool ReadU8(uint8_t *value) { 85 | if (offset_ + 1 > length_) { 86 | return FONT_COMPRESSION_FAILURE(); 87 | } 88 | *value = buffer_[offset_]; 89 | ++offset_; 90 | return true; 91 | } 92 | 93 | bool ReadU16(uint16_t *value) { 94 | if (offset_ + 2 > length_) { 95 | return FONT_COMPRESSION_FAILURE(); 96 | } 97 | std::memcpy(value, buffer_ + offset_, sizeof(uint16_t)); 98 | *value = ntohs(*value); 99 | offset_ += 2; 100 | return true; 101 | } 102 | 103 | bool ReadS16(int16_t *value) { 104 | return ReadU16(reinterpret_cast(value)); 105 | } 106 | 107 | bool ReadU24(uint32_t *value) { 108 | if (offset_ + 3 > length_) { 109 | return FONT_COMPRESSION_FAILURE(); 110 | } 111 | *value = static_cast(buffer_[offset_]) << 16 | 112 | static_cast(buffer_[offset_ + 1]) << 8 | 113 | static_cast(buffer_[offset_ + 2]); 114 | offset_ += 3; 115 | return true; 116 | } 117 | 118 | bool ReadU32(uint32_t *value) { 119 | if (offset_ + 4 > length_) { 120 | return FONT_COMPRESSION_FAILURE(); 121 | } 122 | std::memcpy(value, buffer_ + offset_, sizeof(uint32_t)); 123 | *value = ntohl(*value); 124 | offset_ += 4; 125 | return true; 126 | } 127 | 128 | bool ReadS32(int32_t *value) { 129 | return ReadU32(reinterpret_cast(value)); 130 | } 131 | 132 | bool ReadTag(uint32_t *value) { 133 | if (offset_ + 4 > length_) { 134 | return FONT_COMPRESSION_FAILURE(); 135 | } 136 | std::memcpy(value, buffer_ + offset_, sizeof(uint32_t)); 137 | offset_ += 4; 138 | return true; 139 | } 140 | 141 | bool ReadR64(uint64_t *value) { 142 | if (offset_ + 8 > length_) { 143 | return FONT_COMPRESSION_FAILURE(); 144 | } 145 | std::memcpy(value, buffer_ + offset_, sizeof(uint64_t)); 146 | offset_ += 8; 147 | return true; 148 | } 149 | 150 | const uint8_t *buffer() const { return buffer_; } 151 | size_t offset() const { return offset_; } 152 | size_t length() const { return length_; } 153 | 154 | void set_offset(size_t newoffset) { offset_ = newoffset; } 155 | 156 | private: 157 | const uint8_t * const buffer_; 158 | const size_t length_; 159 | size_t offset_; 160 | }; 161 | 162 | } // namespace woff2 163 | 164 | #endif // WOFF2_BUFFER_H_ 165 | -------------------------------------------------------------------------------- /src/convert_woff2ttf_fuzzer.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | // Entry point for LibFuzzer. 7 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { 8 | std::string buf; 9 | woff2::WOFF2StringOut out(&buf); 10 | out.SetMaxSize(30 * 1024 * 1024); 11 | woff2::ConvertWOFF2ToTTF(data, size, &out); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /src/convert_woff2ttf_fuzzer_new_entry.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t data_size) { 5 | // Decode using newer entry pattern. 6 | // Same pattern as woff2_decompress. 7 | std::string output(std::min(woff2::ComputeWOFF2FinalSize(data, data_size), 8 | woff2::kDefaultMaxSize), 0); 9 | woff2::WOFF2StringOut out(&output); 10 | woff2::ConvertWOFF2ToTTF(data, data_size, &out); 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /src/file.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* File IO helpers. */ 8 | 9 | #ifndef WOFF2_FILE_H_ 10 | #define WOFF2_FILE_H_ 11 | 12 | #include 13 | #include 14 | 15 | namespace woff2 { 16 | 17 | inline std::string GetFileContent(std::string filename) { 18 | std::ifstream ifs(filename.c_str(), std::ios::binary); 19 | return std::string(std::istreambuf_iterator(ifs.rdbuf()), 20 | std::istreambuf_iterator()); 21 | } 22 | 23 | inline void SetFileContents(std::string filename, std::string::iterator start, 24 | std::string::iterator end) { 25 | std::ofstream ofs(filename.c_str(), std::ios::binary); 26 | std::copy(start, end, std::ostream_iterator(ofs)); 27 | } 28 | 29 | } // namespace woff2 30 | #endif // WOFF2_FILE_H_ 31 | -------------------------------------------------------------------------------- /src/font.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Font management utilities */ 8 | 9 | #include "./font.h" 10 | 11 | #include 12 | 13 | #include "./buffer.h" 14 | #include "./port.h" 15 | #include "./store_bytes.h" 16 | #include "./table_tags.h" 17 | #include "./woff2_common.h" 18 | 19 | namespace woff2 { 20 | 21 | Font::Table* Font::FindTable(uint32_t tag) { 22 | std::map::iterator it = tables.find(tag); 23 | return it == tables.end() ? 0 : &it->second; 24 | } 25 | 26 | const Font::Table* Font::FindTable(uint32_t tag) const { 27 | std::map::const_iterator it = tables.find(tag); 28 | return it == tables.end() ? 0 : &it->second; 29 | } 30 | 31 | std::vector Font::OutputOrderedTags() const { 32 | std::vector output_order; 33 | 34 | for (const auto& i : tables) { 35 | const Font::Table& table = i.second; 36 | // This is a transformed table, we will write it together with the 37 | // original version. 38 | if (table.tag & 0x80808080) { 39 | continue; 40 | } 41 | output_order.push_back(table.tag); 42 | } 43 | 44 | // Alphabetize then put loca immediately after glyf 45 | auto glyf_loc = std::find(output_order.begin(), output_order.end(), 46 | kGlyfTableTag); 47 | auto loca_loc = std::find(output_order.begin(), output_order.end(), 48 | kLocaTableTag); 49 | if (glyf_loc != output_order.end() && loca_loc != output_order.end()) { 50 | output_order.erase(loca_loc); 51 | output_order.insert(std::find(output_order.begin(), output_order.end(), 52 | kGlyfTableTag) + 1, kLocaTableTag); 53 | } 54 | 55 | return output_order; 56 | } 57 | 58 | bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len, 59 | Font* font) { 60 | // We don't care about the search_range, entry_selector and range_shift 61 | // fields, they will always be computed upon writing the font. 62 | if (!file->ReadU16(&font->num_tables) || 63 | !file->Skip(6)) { 64 | return FONT_COMPRESSION_FAILURE(); 65 | } 66 | 67 | std::map intervals; 68 | for (uint16_t i = 0; i < font->num_tables; ++i) { 69 | Font::Table table; 70 | table.flag_byte = 0; 71 | table.reuse_of = NULL; 72 | if (!file->ReadU32(&table.tag) || 73 | !file->ReadU32(&table.checksum) || 74 | !file->ReadU32(&table.offset) || 75 | !file->ReadU32(&table.length)) { 76 | return FONT_COMPRESSION_FAILURE(); 77 | } 78 | if ((table.offset & 3) != 0 || 79 | table.length > len || 80 | len - table.length < table.offset) { 81 | return FONT_COMPRESSION_FAILURE(); 82 | } 83 | intervals[table.offset] = table.length; 84 | table.data = data + table.offset; 85 | if (font->tables.find(table.tag) != font->tables.end()) { 86 | return FONT_COMPRESSION_FAILURE(); 87 | } 88 | font->tables[table.tag] = table; 89 | } 90 | 91 | // Check that tables are non-overlapping. 92 | uint32_t last_offset = 12UL + 16UL * font->num_tables; 93 | for (const auto& i : intervals) { 94 | if (i.first < last_offset || i.first + i.second < i.first) { 95 | return FONT_COMPRESSION_FAILURE(); 96 | } 97 | last_offset = i.first + i.second; 98 | } 99 | 100 | // Sanity check key tables 101 | const Font::Table* head_table = font->FindTable(kHeadTableTag); 102 | if (head_table != NULL && head_table->length < 52) { 103 | return FONT_COMPRESSION_FAILURE(); 104 | } 105 | 106 | return true; 107 | } 108 | 109 | bool ReadCollectionFont(Buffer* file, const uint8_t* data, size_t len, 110 | Font* font, 111 | std::map* all_tables) { 112 | if (!file->ReadU32(&font->flavor)) { 113 | return FONT_COMPRESSION_FAILURE(); 114 | } 115 | if (!ReadTrueTypeFont(file, data, len, font)) { 116 | return FONT_COMPRESSION_FAILURE(); 117 | } 118 | 119 | for (auto& entry : font->tables) { 120 | Font::Table& table = entry.second; 121 | 122 | if (all_tables->find(table.offset) == all_tables->end()) { 123 | (*all_tables)[table.offset] = font->FindTable(table.tag); 124 | } else { 125 | table.reuse_of = (*all_tables)[table.offset]; 126 | if (table.tag != table.reuse_of->tag) { 127 | return FONT_COMPRESSION_FAILURE(); 128 | } 129 | } 130 | 131 | } 132 | return true; 133 | } 134 | 135 | bool ReadTrueTypeCollection(Buffer* file, const uint8_t* data, size_t len, 136 | FontCollection* font_collection) { 137 | uint32_t num_fonts; 138 | 139 | if (!file->ReadU32(&font_collection->header_version) || 140 | !file->ReadU32(&num_fonts)) { 141 | return FONT_COMPRESSION_FAILURE(); 142 | } 143 | 144 | std::vector offsets; 145 | for (size_t i = 0; i < num_fonts; i++) { 146 | uint32_t offset; 147 | if (!file->ReadU32(&offset)) { 148 | return FONT_COMPRESSION_FAILURE(); 149 | } 150 | offsets.push_back(offset); 151 | } 152 | 153 | font_collection->fonts.resize(offsets.size()); 154 | std::vector::iterator font_it = font_collection->fonts.begin(); 155 | 156 | std::map all_tables; 157 | for (const auto offset : offsets) { 158 | file->set_offset(offset); 159 | Font& font = *font_it++; 160 | if (!ReadCollectionFont(file, data, len, &font, &all_tables)) { 161 | return FONT_COMPRESSION_FAILURE(); 162 | } 163 | } 164 | 165 | return true; 166 | } 167 | 168 | bool ReadFont(const uint8_t* data, size_t len, Font* font) { 169 | Buffer file(data, len); 170 | 171 | if (!file.ReadU32(&font->flavor)) { 172 | return FONT_COMPRESSION_FAILURE(); 173 | } 174 | 175 | if (font->flavor == kTtcFontFlavor) { 176 | return FONT_COMPRESSION_FAILURE(); 177 | } 178 | return ReadTrueTypeFont(&file, data, len, font); 179 | } 180 | 181 | bool ReadFontCollection(const uint8_t* data, size_t len, 182 | FontCollection* font_collection) { 183 | Buffer file(data, len); 184 | 185 | if (!file.ReadU32(&font_collection->flavor)) { 186 | return FONT_COMPRESSION_FAILURE(); 187 | } 188 | 189 | if (font_collection->flavor != kTtcFontFlavor) { 190 | font_collection->fonts.resize(1); 191 | Font& font = font_collection->fonts[0]; 192 | font.flavor = font_collection->flavor; 193 | return ReadTrueTypeFont(&file, data, len, &font); 194 | } 195 | return ReadTrueTypeCollection(&file, data, len, font_collection); 196 | } 197 | 198 | size_t FontFileSize(const Font& font) { 199 | size_t max_offset = 12ULL + 16ULL * font.num_tables; 200 | for (const auto& i : font.tables) { 201 | const Font::Table& table = i.second; 202 | size_t padding_size = (4 - (table.length & 3)) & 3; 203 | size_t end_offset = (padding_size + table.offset) + table.length; 204 | max_offset = std::max(max_offset, end_offset); 205 | } 206 | return max_offset; 207 | } 208 | 209 | size_t FontCollectionFileSize(const FontCollection& font_collection) { 210 | size_t max_offset = 0; 211 | for (auto& font : font_collection.fonts) { 212 | // font file size actually just finds max offset 213 | max_offset = std::max(max_offset, FontFileSize(font)); 214 | } 215 | return max_offset; 216 | } 217 | 218 | bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) { 219 | size_t offset = 0; 220 | return WriteFont(font, &offset, dst, dst_size); 221 | } 222 | 223 | bool WriteTableRecord(const Font::Table* table, size_t* offset, uint8_t* dst, 224 | size_t dst_size) { 225 | if (dst_size < *offset + kSfntEntrySize) { 226 | return FONT_COMPRESSION_FAILURE(); 227 | } 228 | if (table->IsReused()) { 229 | table = table->reuse_of; 230 | } 231 | StoreU32(table->tag, offset, dst); 232 | StoreU32(table->checksum, offset, dst); 233 | StoreU32(table->offset, offset, dst); 234 | StoreU32(table->length, offset, dst); 235 | return true; 236 | } 237 | 238 | bool WriteTable(const Font::Table& table, size_t* offset, uint8_t* dst, 239 | size_t dst_size) { 240 | if (!WriteTableRecord(&table, offset, dst, dst_size)) { 241 | return false; 242 | } 243 | 244 | // Write the actual table data if it's the first time we've seen it 245 | if (!table.IsReused()) { 246 | if (table.offset + table.length < table.offset || 247 | dst_size < table.offset + table.length) { 248 | return FONT_COMPRESSION_FAILURE(); 249 | } 250 | memcpy(dst + table.offset, table.data, table.length); 251 | size_t padding_size = (4 - (table.length & 3)) & 3; 252 | if (table.offset + table.length + padding_size < padding_size || 253 | dst_size < table.offset + table.length + padding_size) { 254 | return FONT_COMPRESSION_FAILURE(); 255 | } 256 | memset(dst + table.offset + table.length, 0, padding_size); 257 | } 258 | return true; 259 | } 260 | 261 | bool WriteFont(const Font& font, size_t* offset, uint8_t* dst, 262 | size_t dst_size) { 263 | if (dst_size < 12ULL + 16ULL * font.num_tables) { 264 | return FONT_COMPRESSION_FAILURE(); 265 | } 266 | StoreU32(font.flavor, offset, dst); 267 | Store16(font.num_tables, offset, dst); 268 | uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0; 269 | uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0; 270 | uint16_t range_shift = (font.num_tables << 4) - search_range; 271 | Store16(search_range, offset, dst); 272 | Store16(max_pow2, offset, dst); 273 | Store16(range_shift, offset, dst); 274 | 275 | for (const auto& i : font.tables) { 276 | if (!WriteTable(i.second, offset, dst, dst_size)) { 277 | return false; 278 | } 279 | } 280 | 281 | return true; 282 | } 283 | 284 | bool WriteFontCollection(const FontCollection& font_collection, uint8_t* dst, 285 | size_t dst_size) { 286 | size_t offset = 0; 287 | 288 | // It's simpler if this just a simple sfnt 289 | if (font_collection.flavor != kTtcFontFlavor) { 290 | return WriteFont(font_collection.fonts[0], &offset, dst, dst_size); 291 | } 292 | 293 | // Write TTC header 294 | StoreU32(kTtcFontFlavor, &offset, dst); 295 | StoreU32(font_collection.header_version, &offset, dst); 296 | StoreU32(font_collection.fonts.size(), &offset, dst); 297 | 298 | // Offset Table, zeroed for now 299 | size_t offset_table = offset; // where to write offsets later 300 | for (size_t i = 0; i < font_collection.fonts.size(); i++) { 301 | StoreU32(0, &offset, dst); 302 | } 303 | 304 | if (font_collection.header_version == 0x00020000) { 305 | StoreU32(0, &offset, dst); // ulDsigTag 306 | StoreU32(0, &offset, dst); // ulDsigLength 307 | StoreU32(0, &offset, dst); // ulDsigOffset 308 | } 309 | 310 | // Write fonts and their offsets. 311 | for (size_t i = 0; i < font_collection.fonts.size(); i++) { 312 | const auto& font = font_collection.fonts[i]; 313 | StoreU32(offset, &offset_table, dst); 314 | if (!WriteFont(font, &offset, dst, dst_size)) { 315 | return false; 316 | } 317 | } 318 | 319 | return true; 320 | } 321 | 322 | int NumGlyphs(const Font& font) { 323 | const Font::Table* head_table = font.FindTable(kHeadTableTag); 324 | const Font::Table* loca_table = font.FindTable(kLocaTableTag); 325 | if (head_table == NULL || loca_table == NULL || head_table->length < 52) { 326 | return 0; 327 | } 328 | int index_fmt = IndexFormat(font); 329 | int loca_record_size = (index_fmt == 0 ? 2 : 4); 330 | if (loca_table->length < loca_record_size) { 331 | return 0; 332 | } 333 | return (loca_table->length / loca_record_size) - 1; 334 | } 335 | 336 | int IndexFormat(const Font& font) { 337 | const Font::Table* head_table = font.FindTable(kHeadTableTag); 338 | if (head_table == NULL) { 339 | return 0; 340 | } 341 | return head_table->data[51]; 342 | } 343 | 344 | bool Font::Table::IsReused() const { 345 | return this->reuse_of != NULL; 346 | } 347 | 348 | bool GetGlyphData(const Font& font, int glyph_index, 349 | const uint8_t** glyph_data, size_t* glyph_size) { 350 | if (glyph_index < 0) { 351 | return FONT_COMPRESSION_FAILURE(); 352 | } 353 | const Font::Table* head_table = font.FindTable(kHeadTableTag); 354 | const Font::Table* loca_table = font.FindTable(kLocaTableTag); 355 | const Font::Table* glyf_table = font.FindTable(kGlyfTableTag); 356 | if (head_table == NULL || loca_table == NULL || glyf_table == NULL || 357 | head_table->length < 52) { 358 | return FONT_COMPRESSION_FAILURE(); 359 | } 360 | 361 | int index_fmt = IndexFormat(font); 362 | 363 | Buffer loca_buf(loca_table->data, loca_table->length); 364 | if (index_fmt == 0) { 365 | uint16_t offset1, offset2; 366 | if (!loca_buf.Skip(2 * glyph_index) || 367 | !loca_buf.ReadU16(&offset1) || 368 | !loca_buf.ReadU16(&offset2) || 369 | offset2 < offset1 || 370 | 2 * offset2 > glyf_table->length) { 371 | return FONT_COMPRESSION_FAILURE(); 372 | } 373 | *glyph_data = glyf_table->data + 2 * offset1; 374 | *glyph_size = 2 * (offset2 - offset1); 375 | } else { 376 | uint32_t offset1, offset2; 377 | if (!loca_buf.Skip(4 * glyph_index) || 378 | !loca_buf.ReadU32(&offset1) || 379 | !loca_buf.ReadU32(&offset2) || 380 | offset2 < offset1 || 381 | offset2 > glyf_table->length) { 382 | return FONT_COMPRESSION_FAILURE(); 383 | } 384 | *glyph_data = glyf_table->data + offset1; 385 | *glyph_size = offset2 - offset1; 386 | } 387 | return true; 388 | } 389 | 390 | bool RemoveDigitalSignature(Font* font) { 391 | std::map::iterator it = 392 | font->tables.find(kDsigTableTag); 393 | if (it != font->tables.end()) { 394 | font->tables.erase(it); 395 | font->num_tables = font->tables.size(); 396 | } 397 | return true; 398 | } 399 | 400 | } // namespace woff2 401 | -------------------------------------------------------------------------------- /src/font.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Data model for a font file in sfnt format, reading and writing functions and 8 | accessors for the glyph data. */ 9 | 10 | #ifndef WOFF2_FONT_H_ 11 | #define WOFF2_FONT_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | namespace woff2 { 19 | 20 | // Represents an sfnt font file. Only the table directory is parsed, for the 21 | // table data we only store a raw pointer, therefore a font object is valid only 22 | // as long the data from which it was parsed is around. 23 | struct Font { 24 | uint32_t flavor; 25 | uint16_t num_tables; 26 | 27 | struct Table { 28 | uint32_t tag; 29 | uint32_t checksum; 30 | uint32_t offset; 31 | uint32_t length; 32 | const uint8_t* data; 33 | 34 | // Buffer used to mutate the data before writing out. 35 | std::vector buffer; 36 | 37 | // If we've seen this tag/offset before, pointer to the first time we saw it 38 | // If this is the first time we've seen this table, NULL 39 | // Intended use is to bypass re-processing tables 40 | Font::Table* reuse_of; 41 | 42 | uint8_t flag_byte; 43 | 44 | // Is this table reused by a TTC 45 | bool IsReused() const; 46 | }; 47 | std::map tables; 48 | std::vector OutputOrderedTags() const; 49 | 50 | Table* FindTable(uint32_t tag); 51 | const Table* FindTable(uint32_t tag) const; 52 | }; 53 | 54 | // Accomodates both singular (OTF, TTF) and collection (TTC) fonts 55 | struct FontCollection { 56 | uint32_t flavor; 57 | uint32_t header_version; 58 | // (offset, first use of table*) pairs 59 | std::map tables; 60 | std::vector fonts; 61 | }; 62 | 63 | // Parses the font from the given data. Returns false on parsing failure or 64 | // buffer overflow. The font is valid only so long the input data pointer is 65 | // valid. Does NOT support collections. 66 | bool ReadFont(const uint8_t* data, size_t len, Font* font); 67 | 68 | // Parses the font from the given data. Returns false on parsing failure or 69 | // buffer overflow. The font is valid only so long the input data pointer is 70 | // valid. Supports collections. 71 | bool ReadFontCollection(const uint8_t* data, size_t len, FontCollection* fonts); 72 | 73 | // Returns the file size of the font. 74 | size_t FontFileSize(const Font& font); 75 | size_t FontCollectionFileSize(const FontCollection& font); 76 | 77 | // Writes the font into the specified dst buffer. The dst_size should be the 78 | // same as returned by FontFileSize(). Returns false upon buffer overflow (which 79 | // should not happen if dst_size was computed by FontFileSize()). 80 | bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size); 81 | // Write the font at a specific offset 82 | bool WriteFont(const Font& font, size_t* offset, uint8_t* dst, size_t dst_size); 83 | 84 | bool WriteFontCollection(const FontCollection& font_collection, uint8_t* dst, 85 | size_t dst_size); 86 | 87 | // Returns the number of glyphs in the font. 88 | // NOTE: Currently this works only for TrueType-flavored fonts, will return 89 | // zero for CFF-flavored fonts. 90 | int NumGlyphs(const Font& font); 91 | 92 | // Returns the index format of the font 93 | int IndexFormat(const Font& font); 94 | 95 | // Sets *glyph_data and *glyph_size to point to the location of the glyph data 96 | // with the given index. Returns false if the glyph is not found. 97 | bool GetGlyphData(const Font& font, int glyph_index, 98 | const uint8_t** glyph_data, size_t* glyph_size); 99 | 100 | // Removes the digital signature (DSIG) table 101 | bool RemoveDigitalSignature(Font* font); 102 | 103 | } // namespace woff2 104 | 105 | #endif // WOFF2_FONT_H_ 106 | -------------------------------------------------------------------------------- /src/glyph.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Glyph manipulation */ 8 | 9 | #include "./glyph.h" 10 | 11 | #include 12 | #include 13 | #include "./buffer.h" 14 | #include "./store_bytes.h" 15 | 16 | namespace woff2 { 17 | 18 | static const int32_t kFLAG_ONCURVE = 1; 19 | static const int32_t kFLAG_XSHORT = 1 << 1; 20 | static const int32_t kFLAG_YSHORT = 1 << 2; 21 | static const int32_t kFLAG_REPEAT = 1 << 3; 22 | static const int32_t kFLAG_XREPEATSIGN = 1 << 4; 23 | static const int32_t kFLAG_YREPEATSIGN = 1 << 5; 24 | static const int32_t kFLAG_OVERLAP_SIMPLE = 1 << 6; 25 | static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; 26 | static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3; 27 | static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5; 28 | static const int32_t kFLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; 29 | static const int32_t kFLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; 30 | static const int32_t kFLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; 31 | 32 | bool ReadCompositeGlyphData(Buffer* buffer, Glyph* glyph) { 33 | glyph->have_instructions = false; 34 | glyph->composite_data = buffer->buffer() + buffer->offset(); 35 | size_t start_offset = buffer->offset(); 36 | uint16_t flags = kFLAG_MORE_COMPONENTS; 37 | while (flags & kFLAG_MORE_COMPONENTS) { 38 | if (!buffer->ReadU16(&flags)) { 39 | return FONT_COMPRESSION_FAILURE(); 40 | } 41 | glyph->have_instructions |= (flags & kFLAG_WE_HAVE_INSTRUCTIONS) != 0; 42 | size_t arg_size = 2; // glyph index 43 | if (flags & kFLAG_ARG_1_AND_2_ARE_WORDS) { 44 | arg_size += 4; 45 | } else { 46 | arg_size += 2; 47 | } 48 | if (flags & kFLAG_WE_HAVE_A_SCALE) { 49 | arg_size += 2; 50 | } else if (flags & kFLAG_WE_HAVE_AN_X_AND_Y_SCALE) { 51 | arg_size += 4; 52 | } else if (flags & kFLAG_WE_HAVE_A_TWO_BY_TWO) { 53 | arg_size += 8; 54 | } 55 | if (!buffer->Skip(arg_size)) { 56 | return FONT_COMPRESSION_FAILURE(); 57 | } 58 | } 59 | if (buffer->offset() - start_offset > std::numeric_limits::max()) { 60 | return FONT_COMPRESSION_FAILURE(); 61 | } 62 | glyph->composite_data_size = buffer->offset() - start_offset; 63 | return true; 64 | } 65 | 66 | bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) { 67 | Buffer buffer(data, len); 68 | 69 | int16_t num_contours; 70 | if (!buffer.ReadS16(&num_contours)) { 71 | return FONT_COMPRESSION_FAILURE(); 72 | } 73 | 74 | // Read the bounding box. 75 | if (!buffer.ReadS16(&glyph->x_min) || 76 | !buffer.ReadS16(&glyph->y_min) || 77 | !buffer.ReadS16(&glyph->x_max) || 78 | !buffer.ReadS16(&glyph->y_max)) { 79 | return FONT_COMPRESSION_FAILURE(); 80 | } 81 | 82 | if (num_contours == 0) { 83 | // Empty glyph. 84 | return true; 85 | } 86 | 87 | if (num_contours > 0) { 88 | // Simple glyph. 89 | glyph->contours.resize(num_contours); 90 | 91 | // Read the number of points per contour. 92 | uint16_t last_point_index = 0; 93 | for (int i = 0; i < num_contours; ++i) { 94 | uint16_t point_index; 95 | if (!buffer.ReadU16(&point_index)) { 96 | return FONT_COMPRESSION_FAILURE(); 97 | } 98 | uint16_t num_points = point_index - last_point_index + (i == 0 ? 1 : 0); 99 | glyph->contours[i].resize(num_points); 100 | last_point_index = point_index; 101 | } 102 | 103 | // Read the instructions. 104 | if (!buffer.ReadU16(&glyph->instructions_size)) { 105 | return FONT_COMPRESSION_FAILURE(); 106 | } 107 | glyph->instructions_data = data + buffer.offset(); 108 | if (!buffer.Skip(glyph->instructions_size)) { 109 | return FONT_COMPRESSION_FAILURE(); 110 | } 111 | 112 | // Read the run-length coded flags. 113 | std::vector > flags(num_contours); 114 | { 115 | uint8_t flag = 0; 116 | uint8_t flag_repeat = 0; 117 | for (int i = 0; i < num_contours; ++i) { 118 | flags[i].resize(glyph->contours[i].size()); 119 | for (size_t j = 0; j < glyph->contours[i].size(); ++j) { 120 | if (flag_repeat == 0) { 121 | if (!buffer.ReadU8(&flag)) { 122 | return FONT_COMPRESSION_FAILURE(); 123 | } 124 | if (flag & kFLAG_REPEAT) { 125 | if (!buffer.ReadU8(&flag_repeat)) { 126 | return FONT_COMPRESSION_FAILURE(); 127 | } 128 | } 129 | } else { 130 | flag_repeat--; 131 | } 132 | flags[i][j] = flag; 133 | glyph->contours[i][j].on_curve = flag & kFLAG_ONCURVE; 134 | } 135 | } 136 | } 137 | 138 | if (!flags.empty() && !flags[0].empty()) { 139 | glyph->overlap_simple_flag_set = (flags[0][0] & kFLAG_OVERLAP_SIMPLE); 140 | } 141 | 142 | // Read the x coordinates. 143 | int prev_x = 0; 144 | for (int i = 0; i < num_contours; ++i) { 145 | for (size_t j = 0; j < glyph->contours[i].size(); ++j) { 146 | uint8_t flag = flags[i][j]; 147 | if (flag & kFLAG_XSHORT) { 148 | // single byte x-delta coord value 149 | uint8_t x_delta; 150 | if (!buffer.ReadU8(&x_delta)) { 151 | return FONT_COMPRESSION_FAILURE(); 152 | } 153 | int sign = (flag & kFLAG_XREPEATSIGN) ? 1 : -1; 154 | glyph->contours[i][j].x = prev_x + sign * x_delta; 155 | } else { 156 | // double byte x-delta coord value 157 | int16_t x_delta = 0; 158 | if (!(flag & kFLAG_XREPEATSIGN)) { 159 | if (!buffer.ReadS16(&x_delta)) { 160 | return FONT_COMPRESSION_FAILURE(); 161 | } 162 | } 163 | glyph->contours[i][j].x = prev_x + x_delta; 164 | } 165 | prev_x = glyph->contours[i][j].x; 166 | } 167 | } 168 | 169 | // Read the y coordinates. 170 | int prev_y = 0; 171 | for (int i = 0; i < num_contours; ++i) { 172 | for (size_t j = 0; j < glyph->contours[i].size(); ++j) { 173 | uint8_t flag = flags[i][j]; 174 | if (flag & kFLAG_YSHORT) { 175 | // single byte y-delta coord value 176 | uint8_t y_delta; 177 | if (!buffer.ReadU8(&y_delta)) { 178 | return FONT_COMPRESSION_FAILURE(); 179 | } 180 | int sign = (flag & kFLAG_YREPEATSIGN) ? 1 : -1; 181 | glyph->contours[i][j].y = prev_y + sign * y_delta; 182 | } else { 183 | // double byte y-delta coord value 184 | int16_t y_delta = 0; 185 | if (!(flag & kFLAG_YREPEATSIGN)) { 186 | if (!buffer.ReadS16(&y_delta)) { 187 | return FONT_COMPRESSION_FAILURE(); 188 | } 189 | } 190 | glyph->contours[i][j].y = prev_y + y_delta; 191 | } 192 | prev_y = glyph->contours[i][j].y; 193 | } 194 | } 195 | } else if (num_contours == -1) { 196 | // Composite glyph. 197 | if (!ReadCompositeGlyphData(&buffer, glyph)) { 198 | return FONT_COMPRESSION_FAILURE(); 199 | } 200 | // Read the instructions. 201 | if (glyph->have_instructions) { 202 | if (!buffer.ReadU16(&glyph->instructions_size)) { 203 | return FONT_COMPRESSION_FAILURE(); 204 | } 205 | glyph->instructions_data = data + buffer.offset(); 206 | if (!buffer.Skip(glyph->instructions_size)) { 207 | return FONT_COMPRESSION_FAILURE(); 208 | } 209 | } else { 210 | glyph->instructions_size = 0; 211 | } 212 | } else { 213 | return FONT_COMPRESSION_FAILURE(); 214 | } 215 | return true; 216 | } 217 | 218 | namespace { 219 | 220 | void StoreBbox(const Glyph& glyph, size_t* offset, uint8_t* dst) { 221 | Store16(glyph.x_min, offset, dst); 222 | Store16(glyph.y_min, offset, dst); 223 | Store16(glyph.x_max, offset, dst); 224 | Store16(glyph.y_max, offset, dst); 225 | } 226 | 227 | void StoreInstructions(const Glyph& glyph, size_t* offset, uint8_t* dst) { 228 | Store16(glyph.instructions_size, offset, dst); 229 | StoreBytes(glyph.instructions_data, glyph.instructions_size, offset, dst); 230 | } 231 | 232 | bool StoreEndPtsOfContours(const Glyph& glyph, size_t* offset, uint8_t* dst) { 233 | int end_point = -1; 234 | for (const auto& contour : glyph.contours) { 235 | end_point += contour.size(); 236 | if (contour.size() > std::numeric_limits::max() || 237 | end_point > std::numeric_limits::max()) { 238 | return FONT_COMPRESSION_FAILURE(); 239 | } 240 | Store16(end_point, offset, dst); 241 | } 242 | return true; 243 | } 244 | 245 | bool StorePoints(const Glyph& glyph, size_t* offset, 246 | uint8_t* dst, size_t dst_size) { 247 | int previous_flag = -1; 248 | int repeat_count = 0; 249 | int last_x = 0; 250 | int last_y = 0; 251 | size_t x_bytes = 0; 252 | size_t y_bytes = 0; 253 | 254 | // Store the flags and calculate the total size of the x and y coordinates. 255 | for (const auto& contour : glyph.contours) { 256 | for (const auto& point : contour) { 257 | int flag = point.on_curve ? kFLAG_ONCURVE : 0; 258 | if (previous_flag == -1 && glyph.overlap_simple_flag_set) { 259 | // First flag needs to have overlap simple bit set. 260 | flag = flag | kFLAG_OVERLAP_SIMPLE; 261 | } 262 | int dx = point.x - last_x; 263 | int dy = point.y - last_y; 264 | if (dx == 0) { 265 | flag |= kFLAG_XREPEATSIGN; 266 | } else if (dx > -256 && dx < 256) { 267 | flag |= kFLAG_XSHORT | (dx > 0 ? kFLAG_XREPEATSIGN : 0); 268 | x_bytes += 1; 269 | } else { 270 | x_bytes += 2; 271 | } 272 | if (dy == 0) { 273 | flag |= kFLAG_YREPEATSIGN; 274 | } else if (dy > -256 && dy < 256) { 275 | flag |= kFLAG_YSHORT | (dy > 0 ? kFLAG_YREPEATSIGN : 0); 276 | y_bytes += 1; 277 | } else { 278 | y_bytes += 2; 279 | } 280 | if (flag == previous_flag && repeat_count != 255) { 281 | dst[*offset - 1] |= kFLAG_REPEAT; 282 | repeat_count++; 283 | } else { 284 | if (repeat_count != 0) { 285 | if (*offset >= dst_size) { 286 | return FONT_COMPRESSION_FAILURE(); 287 | } 288 | dst[(*offset)++] = repeat_count; 289 | } 290 | if (*offset >= dst_size) { 291 | return FONT_COMPRESSION_FAILURE(); 292 | } 293 | dst[(*offset)++] = flag; 294 | repeat_count = 0; 295 | } 296 | last_x = point.x; 297 | last_y = point.y; 298 | previous_flag = flag; 299 | } 300 | } 301 | if (repeat_count != 0) { 302 | if (*offset >= dst_size) { 303 | return FONT_COMPRESSION_FAILURE(); 304 | } 305 | dst[(*offset)++] = repeat_count; 306 | } 307 | 308 | if (*offset + x_bytes + y_bytes > dst_size) { 309 | return FONT_COMPRESSION_FAILURE(); 310 | } 311 | 312 | // Store the x and y coordinates. 313 | size_t x_offset = *offset; 314 | size_t y_offset = *offset + x_bytes; 315 | last_x = 0; 316 | last_y = 0; 317 | for (const auto& contour : glyph.contours) { 318 | for (const auto& point : contour) { 319 | int dx = point.x - last_x; 320 | int dy = point.y - last_y; 321 | if (dx == 0) { 322 | // pass 323 | } else if (dx > -256 && dx < 256) { 324 | dst[x_offset++] = std::abs(dx); 325 | } else { 326 | Store16(dx, &x_offset, dst); 327 | } 328 | if (dy == 0) { 329 | // pass 330 | } else if (dy > -256 && dy < 256) { 331 | dst[y_offset++] = std::abs(dy); 332 | } else { 333 | Store16(dy, &y_offset, dst); 334 | } 335 | last_x += dx; 336 | last_y += dy; 337 | } 338 | } 339 | *offset = y_offset; 340 | return true; 341 | } 342 | 343 | } // namespace 344 | 345 | bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size) { 346 | size_t offset = 0; 347 | if (glyph.composite_data_size > 0) { 348 | // Composite glyph. 349 | if (*dst_size < ((10ULL + glyph.composite_data_size) + 350 | ((glyph.have_instructions ? 2ULL : 0) + 351 | glyph.instructions_size))) { 352 | return FONT_COMPRESSION_FAILURE(); 353 | } 354 | Store16(-1, &offset, dst); 355 | StoreBbox(glyph, &offset, dst); 356 | StoreBytes(glyph.composite_data, glyph.composite_data_size, &offset, dst); 357 | if (glyph.have_instructions) { 358 | StoreInstructions(glyph, &offset, dst); 359 | } 360 | } else if (glyph.contours.size() > 0) { 361 | // Simple glyph. 362 | if (glyph.contours.size() > std::numeric_limits::max()) { 363 | return FONT_COMPRESSION_FAILURE(); 364 | } 365 | if (*dst_size < ((12ULL + 2 * glyph.contours.size()) + 366 | glyph.instructions_size)) { 367 | return FONT_COMPRESSION_FAILURE(); 368 | } 369 | Store16(glyph.contours.size(), &offset, dst); 370 | StoreBbox(glyph, &offset, dst); 371 | if (!StoreEndPtsOfContours(glyph, &offset, dst)) { 372 | return FONT_COMPRESSION_FAILURE(); 373 | } 374 | StoreInstructions(glyph, &offset, dst); 375 | if (!StorePoints(glyph, &offset, dst, *dst_size)) { 376 | return FONT_COMPRESSION_FAILURE(); 377 | } 378 | } 379 | *dst_size = offset; 380 | return true; 381 | } 382 | 383 | } // namespace woff2 384 | -------------------------------------------------------------------------------- /src/glyph.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Data model and I/O for glyph data within sfnt format files for the purpose of 8 | performing the preprocessing step of the WOFF 2.0 conversion. */ 9 | 10 | #ifndef WOFF2_GLYPH_H_ 11 | #define WOFF2_GLYPH_H_ 12 | 13 | #include 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | namespace woff2 { 20 | 21 | // Represents a parsed simple or composite glyph. The composite glyph data and 22 | // instructions are un-parsed and we keep only pointers to the raw data, 23 | // therefore the glyph is valid only so long the data from which it was parsed 24 | // is around. 25 | class Glyph { 26 | public: 27 | Glyph() 28 | : instructions_size(0), 29 | overlap_simple_flag_set(false), 30 | composite_data_size(0) {} 31 | 32 | // Bounding box. 33 | int16_t x_min; 34 | int16_t x_max; 35 | int16_t y_min; 36 | int16_t y_max; 37 | 38 | // Instructions. 39 | uint16_t instructions_size; 40 | const uint8_t* instructions_data; 41 | 42 | // Flags. 43 | bool overlap_simple_flag_set; 44 | 45 | // Data model for simple glyphs. 46 | struct Point { 47 | int x; 48 | int y; 49 | bool on_curve; 50 | }; 51 | std::vector > contours; 52 | 53 | // Data for composite glyphs. 54 | const uint8_t* composite_data; 55 | uint32_t composite_data_size; 56 | bool have_instructions; 57 | }; 58 | 59 | // Parses the glyph from the given data. Returns false on parsing failure or 60 | // buffer overflow. The glyph is valid only so long the input data pointer is 61 | // valid. 62 | bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph); 63 | 64 | // Stores the glyph into the specified dst buffer. The *dst_size is the buffer 65 | // size on entry and is set to the actual (unpadded) stored size on exit. 66 | // Returns false on buffer overflow. 67 | bool StoreGlyph(const Glyph& glyph, uint8_t* dst, size_t* dst_size); 68 | 69 | } // namespace woff2 70 | 71 | #endif // WOFF2_GLYPH_H_ 72 | -------------------------------------------------------------------------------- /src/normalize.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Glyph normalization */ 8 | 9 | #include "./normalize.h" 10 | 11 | #include 12 | #include 13 | 14 | #include "./buffer.h" 15 | #include "./port.h" 16 | #include "./font.h" 17 | #include "./glyph.h" 18 | #include "./round.h" 19 | #include "./store_bytes.h" 20 | #include "./table_tags.h" 21 | #include "./woff2_common.h" 22 | 23 | namespace woff2 { 24 | 25 | namespace { 26 | 27 | void StoreLoca(int index_fmt, uint32_t value, size_t* offset, uint8_t* dst) { 28 | if (index_fmt == 0) { 29 | Store16(value >> 1, offset, dst); 30 | } else { 31 | StoreU32(value, offset, dst); 32 | } 33 | } 34 | 35 | } // namespace 36 | 37 | namespace { 38 | 39 | bool WriteNormalizedLoca(int index_fmt, int num_glyphs, Font* font) { 40 | Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 41 | Font::Table* loca_table = font->FindTable(kLocaTableTag); 42 | 43 | int glyph_sz = index_fmt == 0 ? 2 : 4; 44 | loca_table->buffer.resize(Round4(num_glyphs + 1) * glyph_sz); 45 | loca_table->length = (num_glyphs + 1) * glyph_sz; 46 | 47 | uint8_t* glyf_dst = num_glyphs ? &glyf_table->buffer[0] : NULL; 48 | uint8_t* loca_dst = &loca_table->buffer[0]; 49 | uint32_t glyf_offset = 0; 50 | size_t loca_offset = 0; 51 | 52 | for (int i = 0; i < num_glyphs; ++i) { 53 | StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 54 | Glyph glyph; 55 | const uint8_t* glyph_data; 56 | size_t glyph_size; 57 | if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || 58 | (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { 59 | return FONT_COMPRESSION_FAILURE(); 60 | } 61 | size_t glyf_dst_size = glyf_table->buffer.size() - glyf_offset; 62 | if (!StoreGlyph(glyph, glyf_dst + glyf_offset, &glyf_dst_size)) { 63 | return FONT_COMPRESSION_FAILURE(); 64 | } 65 | glyf_dst_size = Round4(glyf_dst_size); 66 | if (glyf_dst_size > std::numeric_limits::max() || 67 | glyf_offset + static_cast(glyf_dst_size) < glyf_offset || 68 | (index_fmt == 0 && glyf_offset + glyf_dst_size >= (1UL << 17))) { 69 | return FONT_COMPRESSION_FAILURE(); 70 | } 71 | glyf_offset += glyf_dst_size; 72 | } 73 | 74 | StoreLoca(index_fmt, glyf_offset, &loca_offset, loca_dst); 75 | 76 | glyf_table->buffer.resize(glyf_offset); 77 | glyf_table->data = glyf_offset ? &glyf_table->buffer[0] : NULL; 78 | glyf_table->length = glyf_offset; 79 | loca_table->data = loca_offset ? &loca_table->buffer[0] : NULL; 80 | 81 | return true; 82 | } 83 | 84 | } // namespace 85 | 86 | namespace { 87 | 88 | bool MakeEditableBuffer(Font* font, int tableTag) { 89 | Font::Table* table = font->FindTable(tableTag); 90 | if (table == NULL) { 91 | return FONT_COMPRESSION_FAILURE(); 92 | } 93 | if (table->IsReused()) { 94 | return true; 95 | } 96 | int sz = Round4(table->length); 97 | table->buffer.resize(sz); 98 | uint8_t* buf = &table->buffer[0]; 99 | memcpy(buf, table->data, table->length); 100 | if (PREDICT_FALSE(sz > table->length)) { 101 | memset(buf + table->length, 0, sz - table->length); 102 | } 103 | table->data = buf; 104 | return true; 105 | } 106 | 107 | } // namespace 108 | 109 | bool NormalizeGlyphs(Font* font) { 110 | Font::Table* head_table = font->FindTable(kHeadTableTag); 111 | Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 112 | Font::Table* loca_table = font->FindTable(kLocaTableTag); 113 | if (head_table == NULL) { 114 | return FONT_COMPRESSION_FAILURE(); 115 | } 116 | // If you don't have glyf/loca this transform isn't very interesting 117 | if (loca_table == NULL && glyf_table == NULL) { 118 | return true; 119 | } 120 | // It would be best if you didn't have just one of glyf/loca 121 | if ((glyf_table == NULL) != (loca_table == NULL)) { 122 | return FONT_COMPRESSION_FAILURE(); 123 | } 124 | // Must share neither or both loca & glyf 125 | if (loca_table->IsReused() != glyf_table->IsReused()) { 126 | return FONT_COMPRESSION_FAILURE(); 127 | } 128 | if (loca_table->IsReused()) { 129 | return true; 130 | } 131 | 132 | int index_fmt = head_table->data[51]; 133 | int num_glyphs = NumGlyphs(*font); 134 | 135 | // We need to allocate a bit more than its original length for the normalized 136 | // glyf table, since it can happen that the glyphs in the original table are 137 | // 2-byte aligned, while in the normalized table they are 4-byte aligned. 138 | // That gives a maximum of 2 bytes increase per glyph. However, there is no 139 | // theoretical guarantee that the total size of the flags plus the coordinates 140 | // is the smallest possible in the normalized version, so we have to allow 141 | // some general overhead. 142 | // TODO(user) Figure out some more precise upper bound on the size of 143 | // the overhead. 144 | size_t max_normalized_glyf_size = 1.1 * glyf_table->length + 2 * num_glyphs; 145 | 146 | glyf_table->buffer.resize(max_normalized_glyf_size); 147 | 148 | // if we can't write a loca using short's (index_fmt 0) 149 | // try again using longs (index_fmt 1) 150 | if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) { 151 | if (index_fmt != 0) { 152 | return FONT_COMPRESSION_FAILURE(); 153 | } 154 | 155 | // Rewrite loca with 4-byte entries & update head to match 156 | index_fmt = 1; 157 | if (!WriteNormalizedLoca(index_fmt, num_glyphs, font)) { 158 | return FONT_COMPRESSION_FAILURE(); 159 | } 160 | head_table->buffer[51] = 1; 161 | } 162 | 163 | return true; 164 | } 165 | 166 | bool NormalizeOffsets(Font* font) { 167 | uint32_t offset = 12 + 16 * font->num_tables; 168 | for (auto tag : font->OutputOrderedTags()) { 169 | auto& table = font->tables[tag]; 170 | table.offset = offset; 171 | offset += Round4(table.length); 172 | } 173 | return true; 174 | } 175 | 176 | namespace { 177 | 178 | uint32_t ComputeHeaderChecksum(const Font& font) { 179 | uint32_t checksum = font.flavor; 180 | uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0; 181 | uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0; 182 | uint16_t range_shift = (font.num_tables << 4) - search_range; 183 | checksum += (font.num_tables << 16 | search_range); 184 | checksum += (max_pow2 << 16 | range_shift); 185 | for (const auto& i : font.tables) { 186 | const Font::Table* table = &i.second; 187 | if (table->IsReused()) { 188 | table = table->reuse_of; 189 | } 190 | checksum += table->tag; 191 | checksum += table->checksum; 192 | checksum += table->offset; 193 | checksum += table->length; 194 | } 195 | return checksum; 196 | } 197 | 198 | } // namespace 199 | 200 | bool FixChecksums(Font* font) { 201 | Font::Table* head_table = font->FindTable(kHeadTableTag); 202 | if (head_table == NULL) { 203 | return FONT_COMPRESSION_FAILURE(); 204 | } 205 | if (head_table->reuse_of != NULL) { 206 | head_table = head_table->reuse_of; 207 | } 208 | if (head_table->length < 12) { 209 | return FONT_COMPRESSION_FAILURE(); 210 | } 211 | 212 | uint8_t* head_buf = &head_table->buffer[0]; 213 | size_t offset = 8; 214 | StoreU32(0, &offset, head_buf); 215 | uint32_t file_checksum = 0; 216 | uint32_t head_checksum = 0; 217 | for (auto& i : font->tables) { 218 | Font::Table* table = &i.second; 219 | if (table->IsReused()) { 220 | table = table->reuse_of; 221 | } 222 | table->checksum = ComputeULongSum(table->data, table->length); 223 | file_checksum += table->checksum; 224 | 225 | if (table->tag == kHeadTableTag) { 226 | head_checksum = table->checksum; 227 | } 228 | } 229 | 230 | file_checksum += ComputeHeaderChecksum(*font); 231 | offset = 8; 232 | StoreU32(0xb1b0afba - file_checksum, &offset, head_buf); 233 | 234 | return true; 235 | } 236 | 237 | namespace { 238 | bool MarkTransformed(Font* font) { 239 | Font::Table* head_table = font->FindTable(kHeadTableTag); 240 | if (head_table == NULL) { 241 | return FONT_COMPRESSION_FAILURE(); 242 | } 243 | if (head_table->reuse_of != NULL) { 244 | head_table = head_table->reuse_of; 245 | } 246 | if (head_table->length < 17) { 247 | return FONT_COMPRESSION_FAILURE(); 248 | } 249 | // set bit 11 of head table 'flags' to indicate that font has undergone 250 | // lossless modifying transform 251 | int head_flags = head_table->data[16]; 252 | head_table->buffer[16] = head_flags | 0x08; 253 | return true; 254 | } 255 | } // namespace 256 | 257 | 258 | bool NormalizeWithoutFixingChecksums(Font* font) { 259 | return (MakeEditableBuffer(font, kHeadTableTag) && 260 | RemoveDigitalSignature(font) && 261 | MarkTransformed(font) && 262 | NormalizeGlyphs(font) && 263 | NormalizeOffsets(font)); 264 | } 265 | 266 | bool NormalizeFont(Font* font) { 267 | return (NormalizeWithoutFixingChecksums(font) && 268 | FixChecksums(font)); 269 | } 270 | 271 | bool NormalizeFontCollection(FontCollection* font_collection) { 272 | if (font_collection->fonts.size() == 1) { 273 | return NormalizeFont(&font_collection->fonts[0]); 274 | } 275 | 276 | uint32_t offset = CollectionHeaderSize(font_collection->header_version, 277 | font_collection->fonts.size()); 278 | for (auto& font : font_collection->fonts) { 279 | if (!NormalizeWithoutFixingChecksums(&font)) { 280 | #ifdef FONT_COMPRESSION_BIN 281 | fprintf(stderr, "Font normalization failed.\n"); 282 | #endif 283 | return FONT_COMPRESSION_FAILURE(); 284 | } 285 | offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables; 286 | } 287 | 288 | // Start table offsets after TTC Header and Sfnt Headers 289 | for (auto& font : font_collection->fonts) { 290 | for (auto tag : font.OutputOrderedTags()) { 291 | Font::Table& table = font.tables[tag]; 292 | if (table.IsReused()) { 293 | table.offset = table.reuse_of->offset; 294 | } else { 295 | table.offset = offset; 296 | offset += Round4(table.length); 297 | } 298 | } 299 | } 300 | 301 | // Now we can fix the checksums 302 | for (auto& font : font_collection->fonts) { 303 | if (!FixChecksums(&font)) { 304 | #ifdef FONT_COMPRESSION_BIN 305 | fprintf(stderr, "Failed to fix checksums\n"); 306 | #endif 307 | return FONT_COMPRESSION_FAILURE(); 308 | } 309 | } 310 | 311 | return true; 312 | } 313 | 314 | } // namespace woff2 315 | -------------------------------------------------------------------------------- /src/normalize.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Functions for normalizing fonts. Since the WOFF 2.0 decoder creates font 8 | files in normalized form, the WOFF 2.0 conversion is guaranteed to be 9 | lossless (in a bitwise sense) only for normalized font files. */ 10 | 11 | #ifndef WOFF2_NORMALIZE_H_ 12 | #define WOFF2_NORMALIZE_H_ 13 | 14 | namespace woff2 { 15 | 16 | struct Font; 17 | struct FontCollection; 18 | 19 | // Changes the offset fields of the table headers so that the data for the 20 | // tables will be written in order of increasing tag values, without any gaps 21 | // other than the 4-byte padding. 22 | bool NormalizeOffsets(Font* font); 23 | 24 | // Changes the checksum fields of the table headers and the checksum field of 25 | // the head table so that it matches the current data. 26 | bool FixChecksums(Font* font); 27 | 28 | // Parses each of the glyphs in the font and writes them again to the glyf 29 | // table in normalized form, as defined by the StoreGlyph() function. Changes 30 | // the loca table accordigly. 31 | bool NormalizeGlyphs(Font* font); 32 | 33 | // Performs all of the normalization steps above. 34 | bool NormalizeFont(Font* font); 35 | bool NormalizeFontCollection(FontCollection* font_collection); 36 | 37 | } // namespace woff2 38 | 39 | #endif // WOFF2_NORMALIZE_H_ 40 | -------------------------------------------------------------------------------- /src/port.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helper function for bit twiddling and macros for branch prediction. */ 8 | 9 | #ifndef WOFF2_PORT_H_ 10 | #define WOFF2_PORT_H_ 11 | 12 | #include 13 | 14 | namespace woff2 { 15 | 16 | typedef unsigned int uint32; 17 | 18 | inline int Log2Floor(uint32 n) { 19 | #if defined(__GNUC__) 20 | return n == 0 ? -1 : 31 ^ __builtin_clz(n); 21 | #else 22 | if (n == 0) 23 | return -1; 24 | int log = 0; 25 | uint32 value = n; 26 | for (int i = 4; i >= 0; --i) { 27 | int shift = (1 << i); 28 | uint32 x = value >> shift; 29 | if (x != 0) { 30 | value = x; 31 | log += shift; 32 | } 33 | } 34 | assert(value == 1); 35 | return log; 36 | #endif 37 | } 38 | 39 | } // namespace woff2 40 | 41 | /* Compatibility with non-clang compilers. */ 42 | #ifndef __has_builtin 43 | #define __has_builtin(x) 0 44 | #endif 45 | 46 | #if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ > 95) || \ 47 | (defined(__llvm__) && __has_builtin(__builtin_expect)) 48 | #define PREDICT_FALSE(x) (__builtin_expect(x, 0)) 49 | #define PREDICT_TRUE(x) (__builtin_expect(!!(x), 1)) 50 | #else 51 | #define PREDICT_FALSE(x) (x) 52 | #define PREDICT_TRUE(x) (x) 53 | #endif 54 | 55 | #if (defined(__ARM_ARCH) && (__ARM_ARCH == 7)) || \ 56 | (defined(M_ARM) && (M_ARM == 7)) || \ 57 | defined(__aarch64__) || defined(__ARM64_ARCH_8__) || defined(__i386) || \ 58 | defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) 59 | #if defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) 60 | #define WOFF_LITTLE_ENDIAN 61 | #elif defined(__BYTE_ORDER__) && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) 62 | #define WOFF_BIG_ENDIAN 63 | #endif /* endianness */ 64 | #endif /* CPU whitelist */ 65 | 66 | #endif // WOFF2_PORT_H_ 67 | -------------------------------------------------------------------------------- /src/round.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helper for rounding */ 8 | 9 | #ifndef WOFF2_ROUND_H_ 10 | #define WOFF2_ROUND_H_ 11 | 12 | #include 13 | 14 | namespace woff2 { 15 | 16 | // Round a value up to the nearest multiple of 4. Don't round the value in the 17 | // case that rounding up overflows. 18 | template T Round4(T value) { 19 | if (std::numeric_limits::max() - value < 3) { 20 | return value; 21 | } 22 | return (value + 3) & ~3; 23 | } 24 | 25 | } // namespace woff2 26 | 27 | #endif // WOFF2_ROUND_H_ 28 | -------------------------------------------------------------------------------- /src/store_bytes.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helper functions for storing integer values into byte streams. 8 | No bounds checking is performed, that is the responsibility of the caller. */ 9 | 10 | #ifndef WOFF2_STORE_BYTES_H_ 11 | #define WOFF2_STORE_BYTES_H_ 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | #include "./port.h" 18 | 19 | namespace woff2 { 20 | 21 | inline size_t StoreU32(uint8_t* dst, size_t offset, uint32_t x) { 22 | dst[offset] = x >> 24; 23 | dst[offset + 1] = x >> 16; 24 | dst[offset + 2] = x >> 8; 25 | dst[offset + 3] = x; 26 | return offset + 4; 27 | } 28 | 29 | inline size_t Store16(uint8_t* dst, size_t offset, int x) { 30 | dst[offset] = x >> 8; 31 | dst[offset + 1] = x; 32 | return offset + 2; 33 | } 34 | 35 | inline void StoreU32(uint32_t val, size_t* offset, uint8_t* dst) { 36 | dst[(*offset)++] = val >> 24; 37 | dst[(*offset)++] = val >> 16; 38 | dst[(*offset)++] = val >> 8; 39 | dst[(*offset)++] = val; 40 | } 41 | 42 | inline void Store16(int val, size_t* offset, uint8_t* dst) { 43 | dst[(*offset)++] = val >> 8; 44 | dst[(*offset)++] = val; 45 | } 46 | 47 | inline void StoreBytes(const uint8_t* data, size_t len, 48 | size_t* offset, uint8_t* dst) { 49 | memcpy(&dst[*offset], data, len); 50 | *offset += len; 51 | } 52 | 53 | } // namespace woff2 54 | 55 | #endif // WOFF2_STORE_BYTES_H_ 56 | -------------------------------------------------------------------------------- /src/table_tags.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Font table tags */ 8 | 9 | #include "./table_tags.h" 10 | 11 | namespace woff2 { 12 | 13 | // Note that the byte order is big-endian, not the same as ots.cc 14 | #define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d) 15 | 16 | const uint32_t kKnownTags[63] = { 17 | TAG('c', 'm', 'a', 'p'), // 0 18 | TAG('h', 'e', 'a', 'd'), // 1 19 | TAG('h', 'h', 'e', 'a'), // 2 20 | TAG('h', 'm', 't', 'x'), // 3 21 | TAG('m', 'a', 'x', 'p'), // 4 22 | TAG('n', 'a', 'm', 'e'), // 5 23 | TAG('O', 'S', '/', '2'), // 6 24 | TAG('p', 'o', 's', 't'), // 7 25 | TAG('c', 'v', 't', ' '), // 8 26 | TAG('f', 'p', 'g', 'm'), // 9 27 | TAG('g', 'l', 'y', 'f'), // 10 28 | TAG('l', 'o', 'c', 'a'), // 11 29 | TAG('p', 'r', 'e', 'p'), // 12 30 | TAG('C', 'F', 'F', ' '), // 13 31 | TAG('V', 'O', 'R', 'G'), // 14 32 | TAG('E', 'B', 'D', 'T'), // 15 33 | TAG('E', 'B', 'L', 'C'), // 16 34 | TAG('g', 'a', 's', 'p'), // 17 35 | TAG('h', 'd', 'm', 'x'), // 18 36 | TAG('k', 'e', 'r', 'n'), // 19 37 | TAG('L', 'T', 'S', 'H'), // 20 38 | TAG('P', 'C', 'L', 'T'), // 21 39 | TAG('V', 'D', 'M', 'X'), // 22 40 | TAG('v', 'h', 'e', 'a'), // 23 41 | TAG('v', 'm', 't', 'x'), // 24 42 | TAG('B', 'A', 'S', 'E'), // 25 43 | TAG('G', 'D', 'E', 'F'), // 26 44 | TAG('G', 'P', 'O', 'S'), // 27 45 | TAG('G', 'S', 'U', 'B'), // 28 46 | TAG('E', 'B', 'S', 'C'), // 29 47 | TAG('J', 'S', 'T', 'F'), // 30 48 | TAG('M', 'A', 'T', 'H'), // 31 49 | TAG('C', 'B', 'D', 'T'), // 32 50 | TAG('C', 'B', 'L', 'C'), // 33 51 | TAG('C', 'O', 'L', 'R'), // 34 52 | TAG('C', 'P', 'A', 'L'), // 35 53 | TAG('S', 'V', 'G', ' '), // 36 54 | TAG('s', 'b', 'i', 'x'), // 37 55 | TAG('a', 'c', 'n', 't'), // 38 56 | TAG('a', 'v', 'a', 'r'), // 39 57 | TAG('b', 'd', 'a', 't'), // 40 58 | TAG('b', 'l', 'o', 'c'), // 41 59 | TAG('b', 's', 'l', 'n'), // 42 60 | TAG('c', 'v', 'a', 'r'), // 43 61 | TAG('f', 'd', 's', 'c'), // 44 62 | TAG('f', 'e', 'a', 't'), // 45 63 | TAG('f', 'm', 't', 'x'), // 46 64 | TAG('f', 'v', 'a', 'r'), // 47 65 | TAG('g', 'v', 'a', 'r'), // 48 66 | TAG('h', 's', 't', 'y'), // 49 67 | TAG('j', 'u', 's', 't'), // 50 68 | TAG('l', 'c', 'a', 'r'), // 51 69 | TAG('m', 'o', 'r', 't'), // 52 70 | TAG('m', 'o', 'r', 'x'), // 53 71 | TAG('o', 'p', 'b', 'd'), // 54 72 | TAG('p', 'r', 'o', 'p'), // 55 73 | TAG('t', 'r', 'a', 'k'), // 56 74 | TAG('Z', 'a', 'p', 'f'), // 57 75 | TAG('S', 'i', 'l', 'f'), // 58 76 | TAG('G', 'l', 'a', 't'), // 59 77 | TAG('G', 'l', 'o', 'c'), // 60 78 | TAG('F', 'e', 'a', 't'), // 61 79 | TAG('S', 'i', 'l', 'l'), // 62 80 | }; 81 | 82 | } // namespace woff2 83 | -------------------------------------------------------------------------------- /src/table_tags.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Font table tags */ 8 | 9 | #ifndef WOFF2_TABLE_TAGS_H_ 10 | #define WOFF2_TABLE_TAGS_H_ 11 | 12 | #include 13 | 14 | namespace woff2 { 15 | 16 | // Tags of popular tables. 17 | static const uint32_t kGlyfTableTag = 0x676c7966; 18 | static const uint32_t kHeadTableTag = 0x68656164; 19 | static const uint32_t kLocaTableTag = 0x6c6f6361; 20 | static const uint32_t kDsigTableTag = 0x44534947; 21 | static const uint32_t kCffTableTag = 0x43464620; 22 | static const uint32_t kHmtxTableTag = 0x686d7478; 23 | static const uint32_t kHheaTableTag = 0x68686561; 24 | static const uint32_t kMaxpTableTag = 0x6d617870; 25 | 26 | extern const uint32_t kKnownTags[]; 27 | 28 | } // namespace woff2 29 | 30 | #endif // WOFF2_TABLE_TAGS_H_ 31 | -------------------------------------------------------------------------------- /src/transform.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for preprocessing fonts as part of the WOFF 2.0 conversion. */ 8 | 9 | #include "./transform.h" 10 | 11 | #include // for std::abs 12 | 13 | #include "./buffer.h" 14 | #include "./font.h" 15 | #include "./glyph.h" 16 | #include "./table_tags.h" 17 | #include "./variable_length.h" 18 | 19 | namespace woff2 { 20 | 21 | namespace { 22 | 23 | const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; 24 | const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; 25 | const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; 26 | 27 | void WriteBytes(std::vector* out, const uint8_t* data, size_t len) { 28 | if (len == 0) return; 29 | size_t offset = out->size(); 30 | out->resize(offset + len); 31 | memcpy(&(*out)[offset], data, len); 32 | } 33 | 34 | void WriteBytes(std::vector* out, const std::vector& in) { 35 | for (size_t i = 0; i < in.size(); ++i) { 36 | out->push_back(in[i]); 37 | } 38 | } 39 | 40 | void WriteUShort(std::vector* out, int value) { 41 | out->push_back(value >> 8); 42 | out->push_back(value & 255); 43 | } 44 | 45 | void WriteLong(std::vector* out, int value) { 46 | out->push_back((value >> 24) & 255); 47 | out->push_back((value >> 16) & 255); 48 | out->push_back((value >> 8) & 255); 49 | out->push_back(value & 255); 50 | } 51 | 52 | // Glyf table preprocessing, based on 53 | // GlyfEncoder.java 54 | class GlyfEncoder { 55 | public: 56 | explicit GlyfEncoder(int num_glyphs) 57 | : n_glyphs_(num_glyphs) { 58 | bbox_bitmap_.resize(((num_glyphs + 31) >> 5) << 2); 59 | } 60 | 61 | bool Encode(int glyph_id, const Glyph& glyph) { 62 | if (glyph.composite_data_size > 0) { 63 | WriteCompositeGlyph(glyph_id, glyph); 64 | } else if (glyph.contours.size() > 0) { 65 | WriteSimpleGlyph(glyph_id, glyph); 66 | } else { 67 | WriteUShort(&n_contour_stream_, 0); 68 | } 69 | return true; 70 | } 71 | 72 | void GetTransformedGlyfBytes(std::vector* result) { 73 | WriteUShort(result, 0); // Version 74 | WriteUShort(result, overlap_bitmap_.empty() 75 | ? 0x00 76 | : FLAG_OVERLAP_SIMPLE_BITMAP); // Flags 77 | WriteUShort(result, n_glyphs_); 78 | WriteUShort(result, 0); // index_format, will be set later 79 | WriteLong(result, n_contour_stream_.size()); 80 | WriteLong(result, n_points_stream_.size()); 81 | WriteLong(result, flag_byte_stream_.size()); 82 | WriteLong(result, glyph_stream_.size()); 83 | WriteLong(result, composite_stream_.size()); 84 | WriteLong(result, bbox_bitmap_.size() + bbox_stream_.size()); 85 | WriteLong(result, instruction_stream_.size()); 86 | WriteBytes(result, n_contour_stream_); 87 | WriteBytes(result, n_points_stream_); 88 | WriteBytes(result, flag_byte_stream_); 89 | WriteBytes(result, glyph_stream_); 90 | WriteBytes(result, composite_stream_); 91 | WriteBytes(result, bbox_bitmap_); 92 | WriteBytes(result, bbox_stream_); 93 | WriteBytes(result, instruction_stream_); 94 | if (!overlap_bitmap_.empty()) { 95 | WriteBytes(result, overlap_bitmap_); 96 | } 97 | } 98 | 99 | private: 100 | void WriteInstructions(const Glyph& glyph) { 101 | Write255UShort(&glyph_stream_, glyph.instructions_size); 102 | WriteBytes(&instruction_stream_, 103 | glyph.instructions_data, glyph.instructions_size); 104 | } 105 | 106 | bool ShouldWriteSimpleGlyphBbox(const Glyph& glyph) { 107 | if (glyph.contours.empty() || glyph.contours[0].empty()) { 108 | return glyph.x_min || glyph.y_min || glyph.x_max || glyph.y_max; 109 | } 110 | 111 | int16_t x_min = glyph.contours[0][0].x; 112 | int16_t y_min = glyph.contours[0][0].y; 113 | int16_t x_max = x_min; 114 | int16_t y_max = y_min; 115 | for (const auto& contour : glyph.contours) { 116 | for (const auto& point : contour) { 117 | if (point.x < x_min) x_min = point.x; 118 | if (point.x > x_max) x_max = point.x; 119 | if (point.y < y_min) y_min = point.y; 120 | if (point.y > y_max) y_max = point.y; 121 | } 122 | } 123 | 124 | if (glyph.x_min != x_min) 125 | return true; 126 | if (glyph.y_min != y_min) 127 | return true; 128 | if (glyph.x_max != x_max) 129 | return true; 130 | if (glyph.y_max != y_max) 131 | return true; 132 | 133 | return false; 134 | } 135 | 136 | void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) { 137 | if (glyph.overlap_simple_flag_set) { 138 | EnsureOverlapBitmap(); 139 | overlap_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7); 140 | } 141 | int num_contours = glyph.contours.size(); 142 | WriteUShort(&n_contour_stream_, num_contours); 143 | if (ShouldWriteSimpleGlyphBbox(glyph)) { 144 | WriteBbox(glyph_id, glyph); 145 | } 146 | for (int i = 0; i < num_contours; i++) { 147 | Write255UShort(&n_points_stream_, glyph.contours[i].size()); 148 | } 149 | int lastX = 0; 150 | int lastY = 0; 151 | for (int i = 0; i < num_contours; i++) { 152 | int num_points = glyph.contours[i].size(); 153 | for (int j = 0; j < num_points; j++) { 154 | int x = glyph.contours[i][j].x; 155 | int y = glyph.contours[i][j].y; 156 | int dx = x - lastX; 157 | int dy = y - lastY; 158 | WriteTriplet(glyph.contours[i][j].on_curve, dx, dy); 159 | lastX = x; 160 | lastY = y; 161 | } 162 | } 163 | if (num_contours > 0) { 164 | WriteInstructions(glyph); 165 | } 166 | } 167 | 168 | void WriteCompositeGlyph(int glyph_id, const Glyph& glyph) { 169 | WriteUShort(&n_contour_stream_, -1); 170 | WriteBbox(glyph_id, glyph); 171 | WriteBytes(&composite_stream_, 172 | glyph.composite_data, 173 | glyph.composite_data_size); 174 | if (glyph.have_instructions) { 175 | WriteInstructions(glyph); 176 | } 177 | } 178 | 179 | void WriteBbox(int glyph_id, const Glyph& glyph) { 180 | bbox_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7); 181 | WriteUShort(&bbox_stream_, glyph.x_min); 182 | WriteUShort(&bbox_stream_, glyph.y_min); 183 | WriteUShort(&bbox_stream_, glyph.x_max); 184 | WriteUShort(&bbox_stream_, glyph.y_max); 185 | } 186 | 187 | void WriteTriplet(bool on_curve, int x, int y) { 188 | int abs_x = std::abs(x); 189 | int abs_y = std::abs(y); 190 | int on_curve_bit = on_curve ? 0 : 128; 191 | int x_sign_bit = (x < 0) ? 0 : 1; 192 | int y_sign_bit = (y < 0) ? 0 : 1; 193 | int xy_sign_bits = x_sign_bit + 2 * y_sign_bit; 194 | if (x == 0 && abs_y < 1280) { 195 | flag_byte_stream_.push_back(on_curve_bit + 196 | ((abs_y & 0xf00) >> 7) + y_sign_bit); 197 | glyph_stream_.push_back(abs_y & 0xff); 198 | } else if (y == 0 && abs_x < 1280) { 199 | flag_byte_stream_.push_back(on_curve_bit + 10 + 200 | ((abs_x & 0xf00) >> 7) + x_sign_bit); 201 | glyph_stream_.push_back(abs_x & 0xff); 202 | } else if (abs_x < 65 && abs_y < 65) { 203 | flag_byte_stream_.push_back(on_curve_bit + 20 + 204 | ((abs_x - 1) & 0x30) + 205 | (((abs_y - 1) & 0x30) >> 2) + 206 | xy_sign_bits); 207 | glyph_stream_.push_back((((abs_x - 1) & 0xf) << 4) | ((abs_y - 1) & 0xf)); 208 | } else if (abs_x < 769 && abs_y < 769) { 209 | flag_byte_stream_.push_back(on_curve_bit + 84 + 210 | 12 * (((abs_x - 1) & 0x300) >> 8) + 211 | (((abs_y - 1) & 0x300) >> 6) + xy_sign_bits); 212 | glyph_stream_.push_back((abs_x - 1) & 0xff); 213 | glyph_stream_.push_back((abs_y - 1) & 0xff); 214 | } else if (abs_x < 4096 && abs_y < 4096) { 215 | flag_byte_stream_.push_back(on_curve_bit + 120 + xy_sign_bits); 216 | glyph_stream_.push_back(abs_x >> 4); 217 | glyph_stream_.push_back(((abs_x & 0xf) << 4) | (abs_y >> 8)); 218 | glyph_stream_.push_back(abs_y & 0xff); 219 | } else { 220 | flag_byte_stream_.push_back(on_curve_bit + 124 + xy_sign_bits); 221 | glyph_stream_.push_back(abs_x >> 8); 222 | glyph_stream_.push_back(abs_x & 0xff); 223 | glyph_stream_.push_back(abs_y >> 8); 224 | glyph_stream_.push_back(abs_y & 0xff); 225 | } 226 | } 227 | 228 | void EnsureOverlapBitmap() { 229 | if (overlap_bitmap_.empty()) { 230 | overlap_bitmap_.resize((n_glyphs_ + 7) >> 3); 231 | } 232 | } 233 | 234 | std::vector n_contour_stream_; 235 | std::vector n_points_stream_; 236 | std::vector flag_byte_stream_; 237 | std::vector composite_stream_; 238 | std::vector bbox_bitmap_; 239 | std::vector bbox_stream_; 240 | std::vector glyph_stream_; 241 | std::vector instruction_stream_; 242 | std::vector overlap_bitmap_; 243 | int n_glyphs_; 244 | }; 245 | 246 | } // namespace 247 | 248 | bool TransformGlyfAndLocaTables(Font* font) { 249 | // no transform for CFF 250 | const Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 251 | const Font::Table* loca_table = font->FindTable(kLocaTableTag); 252 | 253 | // If you don't have glyf/loca this transform isn't very interesting 254 | if (loca_table == NULL && glyf_table == NULL) { 255 | return true; 256 | } 257 | // It would be best if you didn't have just one of glyf/loca 258 | if ((glyf_table == NULL) != (loca_table == NULL)) { 259 | return FONT_COMPRESSION_FAILURE(); 260 | } 261 | // Must share neither or both loca & glyf 262 | if (loca_table->IsReused() != glyf_table->IsReused()) { 263 | return FONT_COMPRESSION_FAILURE(); 264 | } 265 | if (loca_table->IsReused()) { 266 | return true; 267 | } 268 | 269 | Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080]; 270 | Font::Table* transformed_loca = &font->tables[kLocaTableTag ^ 0x80808080]; 271 | 272 | int num_glyphs = NumGlyphs(*font); 273 | GlyfEncoder encoder(num_glyphs); 274 | for (int i = 0; i < num_glyphs; ++i) { 275 | Glyph glyph; 276 | const uint8_t* glyph_data; 277 | size_t glyph_size; 278 | if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || 279 | (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { 280 | return FONT_COMPRESSION_FAILURE(); 281 | } 282 | encoder.Encode(i, glyph); 283 | } 284 | encoder.GetTransformedGlyfBytes(&transformed_glyf->buffer); 285 | 286 | const Font::Table* head_table = font->FindTable(kHeadTableTag); 287 | if (head_table == NULL || head_table->length < 52) { 288 | return FONT_COMPRESSION_FAILURE(); 289 | } 290 | transformed_glyf->buffer[7] = head_table->data[51]; // index_format 291 | 292 | transformed_glyf->tag = kGlyfTableTag ^ 0x80808080; 293 | transformed_glyf->length = transformed_glyf->buffer.size(); 294 | transformed_glyf->data = transformed_glyf->buffer.data(); 295 | 296 | transformed_loca->tag = kLocaTableTag ^ 0x80808080; 297 | transformed_loca->length = 0; 298 | transformed_loca->data = NULL; 299 | 300 | return true; 301 | } 302 | 303 | // See https://www.microsoft.com/typography/otspec/hmtx.htm 304 | // See WOFF2 spec, 5.4. Transformed hmtx table format 305 | bool TransformHmtxTable(Font* font) { 306 | const Font::Table* glyf_table = font->FindTable(kGlyfTableTag); 307 | const Font::Table* hmtx_table = font->FindTable(kHmtxTableTag); 308 | const Font::Table* hhea_table = font->FindTable(kHheaTableTag); 309 | 310 | // If you don't have hmtx or a glyf not much is going to happen here 311 | if (hmtx_table == NULL || glyf_table == NULL) { 312 | return true; 313 | } 314 | 315 | // hmtx without hhea doesn't make sense 316 | if (hhea_table == NULL) { 317 | return FONT_COMPRESSION_FAILURE(); 318 | } 319 | 320 | // Skip 34 to reach 'hhea' numberOfHMetrics 321 | Buffer hhea_buf(hhea_table->data, hhea_table->length); 322 | uint16_t num_hmetrics; 323 | if (!hhea_buf.Skip(34) || !hhea_buf.ReadU16(&num_hmetrics)) { 324 | return FONT_COMPRESSION_FAILURE(); 325 | } 326 | 327 | // Must have at least one hMetric 328 | if (num_hmetrics < 1) { 329 | return FONT_COMPRESSION_FAILURE(); 330 | } 331 | 332 | int num_glyphs = NumGlyphs(*font); 333 | 334 | // Most fonts can be transformed; assume it's a go until proven otherwise 335 | std::vector advance_widths; 336 | std::vector proportional_lsbs; 337 | std::vector monospace_lsbs; 338 | 339 | bool remove_proportional_lsb = true; 340 | bool remove_monospace_lsb = (num_glyphs - num_hmetrics) > 0; 341 | 342 | Buffer hmtx_buf(hmtx_table->data, hmtx_table->length); 343 | for (int i = 0; i < num_glyphs; i++) { 344 | Glyph glyph; 345 | const uint8_t* glyph_data; 346 | size_t glyph_size; 347 | if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) || 348 | (glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) { 349 | return FONT_COMPRESSION_FAILURE(); 350 | } 351 | 352 | uint16_t advance_width = 0; 353 | int16_t lsb = 0; 354 | 355 | if (i < num_hmetrics) { 356 | // [0, num_hmetrics) are proportional hMetrics 357 | if (!hmtx_buf.ReadU16(&advance_width)) { 358 | return FONT_COMPRESSION_FAILURE(); 359 | } 360 | 361 | if (!hmtx_buf.ReadS16(&lsb)) { 362 | return FONT_COMPRESSION_FAILURE(); 363 | } 364 | 365 | if (glyph_size > 0 && glyph.x_min != lsb) { 366 | remove_proportional_lsb = false; 367 | } 368 | 369 | advance_widths.push_back(advance_width); 370 | proportional_lsbs.push_back(lsb); 371 | } else { 372 | // [num_hmetrics, num_glyphs) are monospace leftSideBearing's 373 | if (!hmtx_buf.ReadS16(&lsb)) { 374 | return FONT_COMPRESSION_FAILURE(); 375 | } 376 | if (glyph_size > 0 && glyph.x_min != lsb) { 377 | remove_monospace_lsb = false; 378 | } 379 | monospace_lsbs.push_back(lsb); 380 | } 381 | 382 | // If we know we can't optimize, bail out completely 383 | if (!remove_proportional_lsb && !remove_monospace_lsb) { 384 | return true; 385 | } 386 | } 387 | 388 | Font::Table* transformed_hmtx = &font->tables[kHmtxTableTag ^ 0x80808080]; 389 | 390 | uint8_t flags = 0; 391 | size_t transformed_size = 1 + 2 * advance_widths.size(); 392 | if (remove_proportional_lsb) { 393 | flags |= 1; 394 | } else { 395 | transformed_size += 2 * proportional_lsbs.size(); 396 | } 397 | if (remove_monospace_lsb) { 398 | flags |= 1 << 1; 399 | } else { 400 | transformed_size += 2 * monospace_lsbs.size(); 401 | } 402 | 403 | transformed_hmtx->buffer.reserve(transformed_size); 404 | std::vector* out = &transformed_hmtx->buffer; 405 | WriteBytes(out, &flags, 1); 406 | for (uint16_t advance_width : advance_widths) { 407 | WriteUShort(out, advance_width); 408 | } 409 | 410 | if (!remove_proportional_lsb) { 411 | for (int16_t lsb : proportional_lsbs) { 412 | WriteUShort(out, lsb); 413 | } 414 | } 415 | if (!remove_monospace_lsb) { 416 | for (int16_t lsb : monospace_lsbs) { 417 | WriteUShort(out, lsb); 418 | } 419 | } 420 | 421 | transformed_hmtx->tag = kHmtxTableTag ^ 0x80808080; 422 | transformed_hmtx->flag_byte = 1 << 6; 423 | transformed_hmtx->length = transformed_hmtx->buffer.size(); 424 | transformed_hmtx->data = transformed_hmtx->buffer.data(); 425 | 426 | 427 | return true; 428 | } 429 | 430 | } // namespace woff2 431 | -------------------------------------------------------------------------------- /src/transform.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for preprocessing fonts as part of the WOFF 2.0 conversion. */ 8 | 9 | #ifndef WOFF2_TRANSFORM_H_ 10 | #define WOFF2_TRANSFORM_H_ 11 | 12 | #include "./font.h" 13 | 14 | namespace woff2 { 15 | 16 | // Adds the transformed versions of the glyf and loca tables to the font. The 17 | // transformed loca table has zero length. The tag of the transformed tables is 18 | // derived from the original tag by flipping the MSBs of every byte. 19 | bool TransformGlyfAndLocaTables(Font* font); 20 | 21 | // Apply transformation to hmtx table if applicable for this font. 22 | bool TransformHmtxTable(Font* font); 23 | 24 | } // namespace woff2 25 | 26 | #endif // WOFF2_TRANSFORM_H_ 27 | -------------------------------------------------------------------------------- /src/variable_length.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helper functions for woff2 variable length types: 255UInt16 and UIntBase128 */ 8 | 9 | #include "./variable_length.h" 10 | 11 | namespace woff2 { 12 | 13 | size_t Size255UShort(uint16_t value) { 14 | size_t result = 3; 15 | if (value < 253) { 16 | result = 1; 17 | } else if (value < 762) { 18 | result = 2; 19 | } else { 20 | result = 3; 21 | } 22 | return result; 23 | } 24 | 25 | void Write255UShort(std::vector* out, int value) { 26 | if (value < 253) { 27 | out->push_back(value); 28 | } else if (value < 506) { 29 | out->push_back(255); 30 | out->push_back(value - 253); 31 | } else if (value < 762) { 32 | out->push_back(254); 33 | out->push_back(value - 506); 34 | } else { 35 | out->push_back(253); 36 | out->push_back(value >> 8); 37 | out->push_back(value & 0xff); 38 | } 39 | } 40 | 41 | void Store255UShort(int val, size_t* offset, uint8_t* dst) { 42 | std::vector packed; 43 | Write255UShort(&packed, val); 44 | for (uint8_t packed_byte : packed) { 45 | dst[(*offset)++] = packed_byte; 46 | } 47 | } 48 | 49 | // Based on section 6.1.1 of MicroType Express draft spec 50 | bool Read255UShort(Buffer* buf, unsigned int* value) { 51 | static const int kWordCode = 253; 52 | static const int kOneMoreByteCode2 = 254; 53 | static const int kOneMoreByteCode1 = 255; 54 | static const int kLowestUCode = 253; 55 | uint8_t code = 0; 56 | if (!buf->ReadU8(&code)) { 57 | return FONT_COMPRESSION_FAILURE(); 58 | } 59 | if (code == kWordCode) { 60 | uint16_t result = 0; 61 | if (!buf->ReadU16(&result)) { 62 | return FONT_COMPRESSION_FAILURE(); 63 | } 64 | *value = result; 65 | return true; 66 | } else if (code == kOneMoreByteCode1) { 67 | uint8_t result = 0; 68 | if (!buf->ReadU8(&result)) { 69 | return FONT_COMPRESSION_FAILURE(); 70 | } 71 | *value = result + kLowestUCode; 72 | return true; 73 | } else if (code == kOneMoreByteCode2) { 74 | uint8_t result = 0; 75 | if (!buf->ReadU8(&result)) { 76 | return FONT_COMPRESSION_FAILURE(); 77 | } 78 | *value = result + kLowestUCode * 2; 79 | return true; 80 | } else { 81 | *value = code; 82 | return true; 83 | } 84 | } 85 | 86 | bool ReadBase128(Buffer* buf, uint32_t* value) { 87 | uint32_t result = 0; 88 | for (size_t i = 0; i < 5; ++i) { 89 | uint8_t code = 0; 90 | if (!buf->ReadU8(&code)) { 91 | return FONT_COMPRESSION_FAILURE(); 92 | } 93 | // Leading zeros are invalid. 94 | if (i == 0 && code == 0x80) { 95 | return FONT_COMPRESSION_FAILURE(); 96 | } 97 | // If any of the top seven bits are set then we're about to overflow. 98 | if (result & 0xfe000000) { 99 | return FONT_COMPRESSION_FAILURE(); 100 | } 101 | result = (result << 7) | (code & 0x7f); 102 | if ((code & 0x80) == 0) { 103 | *value = result; 104 | return true; 105 | } 106 | } 107 | // Make sure not to exceed the size bound 108 | return FONT_COMPRESSION_FAILURE(); 109 | } 110 | 111 | size_t Base128Size(size_t n) { 112 | size_t size = 1; 113 | for (; n >= 128; n >>= 7) ++size; 114 | return size; 115 | } 116 | 117 | void StoreBase128(size_t len, size_t* offset, uint8_t* dst) { 118 | size_t size = Base128Size(len); 119 | for (size_t i = 0; i < size; ++i) { 120 | int b = static_cast((len >> (7 * (size - i - 1))) & 0x7f); 121 | if (i < size - 1) { 122 | b |= 0x80; 123 | } 124 | dst[(*offset)++] = b; 125 | } 126 | } 127 | 128 | } // namespace woff2 129 | 130 | -------------------------------------------------------------------------------- /src/variable_length.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2015 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helper functions for woff2 variable length types: 255UInt16 and UIntBase128 */ 8 | 9 | #ifndef WOFF2_VARIABLE_LENGTH_H_ 10 | #define WOFF2_VARIABLE_LENGTH_H_ 11 | 12 | #include 13 | #include 14 | #include "./buffer.h" 15 | 16 | namespace woff2 { 17 | 18 | size_t Size255UShort(uint16_t value); 19 | bool Read255UShort(Buffer* buf, unsigned int* value); 20 | void Write255UShort(std::vector* out, int value); 21 | void Store255UShort(int val, size_t* offset, uint8_t* dst); 22 | 23 | size_t Base128Size(size_t n); 24 | bool ReadBase128(Buffer* buf, uint32_t* value); 25 | void StoreBase128(size_t len, size_t* offset, uint8_t* dst); 26 | 27 | } // namespace woff2 28 | 29 | #endif // WOFF2_VARIABLE_LENGTH_H_ 30 | 31 | -------------------------------------------------------------------------------- /src/woff2_common.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Helpers common across multiple parts of woff2 */ 8 | 9 | #include 10 | 11 | #include "./woff2_common.h" 12 | 13 | #include "./port.h" 14 | 15 | namespace woff2 { 16 | 17 | 18 | uint32_t ComputeULongSum(const uint8_t* buf, size_t size) { 19 | uint32_t checksum = 0; 20 | size_t aligned_size = size & ~3; 21 | for (size_t i = 0; i < aligned_size; i += 4) { 22 | checksum += 23 | (buf[i] << 24) | (buf[i + 1] << 16) | (buf[i + 2] << 8) | buf[i + 3]; 24 | } 25 | 26 | // treat size not aligned on 4 as if it were padded to 4 with 0's 27 | if (size != aligned_size) { 28 | uint32_t v = 0; 29 | for (size_t i = aligned_size; i < size; ++i) { 30 | v |= buf[i] << (24 - 8 * (i & 3)); 31 | } 32 | checksum += v; 33 | } 34 | 35 | return checksum; 36 | } 37 | 38 | size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts) { 39 | size_t size = 0; 40 | if (header_version == 0x00020000) { 41 | size += 12; // ulDsig{Tag,Length,Offset} 42 | } 43 | if (header_version == 0x00010000 || header_version == 0x00020000) { 44 | size += 12 // TTCTag, Version, numFonts 45 | + 4 * num_fonts; // OffsetTable[numFonts] 46 | } 47 | return size; 48 | } 49 | 50 | } // namespace woff2 51 | -------------------------------------------------------------------------------- /src/woff2_common.h: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Common definition for WOFF2 encoding/decoding */ 8 | 9 | #ifndef WOFF2_WOFF2_COMMON_H_ 10 | #define WOFF2_WOFF2_COMMON_H_ 11 | 12 | #include 13 | #include 14 | 15 | #include 16 | 17 | namespace woff2 { 18 | 19 | static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2" 20 | 21 | // Leave the first byte open to store flag_byte 22 | const unsigned int kWoff2FlagsTransform = 1 << 8; 23 | 24 | // TrueType Collection ID string: 'ttcf' 25 | static const uint32_t kTtcFontFlavor = 0x74746366; 26 | 27 | static const size_t kSfntHeaderSize = 12; 28 | static const size_t kSfntEntrySize = 16; 29 | 30 | struct Point { 31 | int x; 32 | int y; 33 | bool on_curve; 34 | }; 35 | 36 | struct Table { 37 | uint32_t tag; 38 | uint32_t flags; 39 | uint32_t src_offset; 40 | uint32_t src_length; 41 | 42 | uint32_t transform_length; 43 | 44 | uint32_t dst_offset; 45 | uint32_t dst_length; 46 | const uint8_t* dst_data; 47 | 48 | bool operator<(const Table& other) const { 49 | return tag < other.tag; 50 | } 51 | }; 52 | 53 | 54 | // Size of the collection header. 0 if version indicates this isn't a 55 | // collection. Ref http://www.microsoft.com/typography/otspec/otff.htm, 56 | // True Type Collections 57 | size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts); 58 | 59 | // Compute checksum over size bytes of buf 60 | uint32_t ComputeULongSum(const uint8_t* buf, size_t size); 61 | 62 | } // namespace woff2 63 | 64 | #endif // WOFF2_WOFF2_COMMON_H_ 65 | -------------------------------------------------------------------------------- /src/woff2_compress.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* A commandline tool for compressing ttf format files to woff2. */ 8 | 9 | #include 10 | 11 | #include "file.h" 12 | #include 13 | 14 | 15 | int main(int argc, char **argv) { 16 | if (argc != 2) { 17 | fprintf(stderr, "One argument, the input filename, must be provided.\n"); 18 | return 1; 19 | } 20 | 21 | std::string filename(argv[1]); 22 | std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; 23 | fprintf(stdout, "Processing %s => %s\n", 24 | filename.c_str(), outfilename.c_str()); 25 | std::string input = woff2::GetFileContent(filename); 26 | 27 | const uint8_t* input_data = reinterpret_cast(input.data()); 28 | size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size()); 29 | std::string output(output_size, 0); 30 | uint8_t* output_data = reinterpret_cast(&output[0]); 31 | 32 | woff2::WOFF2Params params; 33 | if (!woff2::ConvertTTFToWOFF2(input_data, input.size(), 34 | output_data, &output_size, params)) { 35 | fprintf(stderr, "Compression failed.\n"); 36 | return 1; 37 | } 38 | output.resize(output_size); 39 | 40 | woff2::SetFileContents(outfilename, output.begin(), output.end()); 41 | 42 | return 0; 43 | } 44 | -------------------------------------------------------------------------------- /src/woff2_dec.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for converting WOFF2 format font files to their TTF versions. */ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | 22 | #include 23 | #include "./buffer.h" 24 | #include "./port.h" 25 | #include "./round.h" 26 | #include "./store_bytes.h" 27 | #include "./table_tags.h" 28 | #include "./variable_length.h" 29 | #include "./woff2_common.h" 30 | 31 | namespace woff2 { 32 | 33 | namespace { 34 | 35 | // simple glyph flags 36 | const int kGlyfOnCurve = 1 << 0; 37 | const int kGlyfXShort = 1 << 1; 38 | const int kGlyfYShort = 1 << 2; 39 | const int kGlyfRepeat = 1 << 3; 40 | const int kGlyfThisXIsSame = 1 << 4; 41 | const int kGlyfThisYIsSame = 1 << 5; 42 | const int kOverlapSimple = 1 << 6; 43 | 44 | // composite glyph flags 45 | // See CompositeGlyph.java in sfntly for full definitions 46 | const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; 47 | const int FLAG_WE_HAVE_A_SCALE = 1 << 3; 48 | const int FLAG_MORE_COMPONENTS = 1 << 5; 49 | const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; 50 | const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; 51 | const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; 52 | 53 | // glyf flags 54 | const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; 55 | 56 | const size_t kCheckSumAdjustmentOffset = 8; 57 | 58 | const size_t kEndPtsOfContoursOffset = 10; 59 | const size_t kCompositeGlyphBegin = 10; 60 | 61 | // 98% of Google Fonts have no glyph above 5k bytes 62 | // Largest glyph ever observed was 72k bytes 63 | const size_t kDefaultGlyphBuf = 5120; 64 | 65 | // Over 14k test fonts the max compression ratio seen to date was ~20. 66 | // >100 suggests you wrote a bad uncompressed size. 67 | const float kMaxPlausibleCompressionRatio = 100.0; 68 | 69 | // metadata for a TTC font entry 70 | struct TtcFont { 71 | uint32_t flavor; 72 | uint32_t dst_offset; 73 | uint32_t header_checksum; 74 | std::vector table_indices; 75 | }; 76 | 77 | struct WOFF2Header { 78 | uint32_t flavor; 79 | uint32_t header_version; 80 | uint16_t num_tables; 81 | uint64_t compressed_offset; 82 | uint32_t compressed_length; 83 | uint32_t uncompressed_size; 84 | std::vector tables; // num_tables unique tables 85 | std::vector ttc_fonts; // metadata to help rebuild font 86 | }; 87 | 88 | /** 89 | * Accumulates data we may need to reconstruct a single font. One per font 90 | * created for a TTC. 91 | */ 92 | struct WOFF2FontInfo { 93 | uint16_t num_glyphs; 94 | uint16_t index_format; 95 | uint16_t num_hmetrics; 96 | std::vector x_mins; 97 | std::map table_entry_by_tag; 98 | }; 99 | 100 | // Accumulates metadata as we rebuild the font 101 | struct RebuildMetadata { 102 | uint32_t header_checksum; // set by WriteHeaders 103 | std::vector font_infos; 104 | // checksums for tables that have been written. 105 | // (tag, src_offset) => checksum. Need both because 0-length loca. 106 | std::map, uint32_t> checksums; 107 | }; 108 | 109 | int WithSign(int flag, int baseval) { 110 | // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) 111 | return (flag & 1) ? baseval : -baseval; 112 | } 113 | 114 | bool _SafeIntAddition(int a, int b, int* result) { 115 | if (PREDICT_FALSE( 116 | ((a > 0) && (b > std::numeric_limits::max() - a)) || 117 | ((a < 0) && (b < std::numeric_limits::min() - a)))) { 118 | return false; 119 | } 120 | *result = a + b; 121 | return true; 122 | } 123 | 124 | bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, 125 | unsigned int n_points, Point* result, size_t* in_bytes_consumed) { 126 | int x = 0; 127 | int y = 0; 128 | 129 | if (PREDICT_FALSE(n_points > in_size)) { 130 | return FONT_COMPRESSION_FAILURE(); 131 | } 132 | unsigned int triplet_index = 0; 133 | 134 | for (unsigned int i = 0; i < n_points; ++i) { 135 | uint8_t flag = flags_in[i]; 136 | bool on_curve = !(flag >> 7); 137 | flag &= 0x7f; 138 | unsigned int n_data_bytes; 139 | if (flag < 84) { 140 | n_data_bytes = 1; 141 | } else if (flag < 120) { 142 | n_data_bytes = 2; 143 | } else if (flag < 124) { 144 | n_data_bytes = 3; 145 | } else { 146 | n_data_bytes = 4; 147 | } 148 | if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || 149 | triplet_index + n_data_bytes < triplet_index)) { 150 | return FONT_COMPRESSION_FAILURE(); 151 | } 152 | int dx, dy; 153 | if (flag < 10) { 154 | dx = 0; 155 | dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]); 156 | } else if (flag < 20) { 157 | dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]); 158 | dy = 0; 159 | } else if (flag < 84) { 160 | int b0 = flag - 20; 161 | int b1 = in[triplet_index]; 162 | dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); 163 | dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); 164 | } else if (flag < 120) { 165 | int b0 = flag - 84; 166 | dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]); 167 | dy = WithSign(flag >> 1, 168 | 1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]); 169 | } else if (flag < 124) { 170 | int b2 = in[triplet_index + 1]; 171 | dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4)); 172 | dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]); 173 | } else { 174 | dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]); 175 | dy = WithSign(flag >> 1, 176 | (in[triplet_index + 2] << 8) + in[triplet_index + 3]); 177 | } 178 | triplet_index += n_data_bytes; 179 | if (!_SafeIntAddition(x, dx, &x)) { 180 | return false; 181 | } 182 | if (!_SafeIntAddition(y, dy, &y)) { 183 | return false; 184 | } 185 | *result++ = {x, y, on_curve}; 186 | } 187 | *in_bytes_consumed = triplet_index; 188 | return true; 189 | } 190 | 191 | // This function stores just the point data. On entry, dst points to the 192 | // beginning of a simple glyph. Returns true on success. 193 | bool StorePoints(unsigned int n_points, const Point* points, 194 | unsigned int n_contours, unsigned int instruction_length, 195 | bool has_overlap_bit, uint8_t* dst, size_t dst_size, 196 | size_t* glyph_size) { 197 | // I believe that n_contours < 65536, in which case this is safe. However, a 198 | // comment and/or an assert would be good. 199 | unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + 200 | instruction_length; 201 | int last_flag = -1; 202 | int repeat_count = 0; 203 | int last_x = 0; 204 | int last_y = 0; 205 | unsigned int x_bytes = 0; 206 | unsigned int y_bytes = 0; 207 | 208 | for (unsigned int i = 0; i < n_points; ++i) { 209 | const Point& point = points[i]; 210 | int flag = point.on_curve ? kGlyfOnCurve : 0; 211 | if (has_overlap_bit && i == 0) { 212 | flag |= kOverlapSimple; 213 | } 214 | 215 | int dx = point.x - last_x; 216 | int dy = point.y - last_y; 217 | if (dx == 0) { 218 | flag |= kGlyfThisXIsSame; 219 | } else if (dx > -256 && dx < 256) { 220 | flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0); 221 | x_bytes += 1; 222 | } else { 223 | x_bytes += 2; 224 | } 225 | if (dy == 0) { 226 | flag |= kGlyfThisYIsSame; 227 | } else if (dy > -256 && dy < 256) { 228 | flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0); 229 | y_bytes += 1; 230 | } else { 231 | y_bytes += 2; 232 | } 233 | 234 | if (flag == last_flag && repeat_count != 255) { 235 | dst[flag_offset - 1] |= kGlyfRepeat; 236 | repeat_count++; 237 | } else { 238 | if (repeat_count != 0) { 239 | if (PREDICT_FALSE(flag_offset >= dst_size)) { 240 | return FONT_COMPRESSION_FAILURE(); 241 | } 242 | dst[flag_offset++] = repeat_count; 243 | } 244 | if (PREDICT_FALSE(flag_offset >= dst_size)) { 245 | return FONT_COMPRESSION_FAILURE(); 246 | } 247 | dst[flag_offset++] = flag; 248 | repeat_count = 0; 249 | } 250 | last_x = point.x; 251 | last_y = point.y; 252 | last_flag = flag; 253 | } 254 | 255 | if (repeat_count != 0) { 256 | if (PREDICT_FALSE(flag_offset >= dst_size)) { 257 | return FONT_COMPRESSION_FAILURE(); 258 | } 259 | dst[flag_offset++] = repeat_count; 260 | } 261 | unsigned int xy_bytes = x_bytes + y_bytes; 262 | if (PREDICT_FALSE(xy_bytes < x_bytes || 263 | flag_offset + xy_bytes < flag_offset || 264 | flag_offset + xy_bytes > dst_size)) { 265 | return FONT_COMPRESSION_FAILURE(); 266 | } 267 | 268 | int x_offset = flag_offset; 269 | int y_offset = flag_offset + x_bytes; 270 | last_x = 0; 271 | last_y = 0; 272 | for (unsigned int i = 0; i < n_points; ++i) { 273 | int dx = points[i].x - last_x; 274 | if (dx == 0) { 275 | // pass 276 | } else if (dx > -256 && dx < 256) { 277 | dst[x_offset++] = std::abs(dx); 278 | } else { 279 | // will always fit for valid input, but overflow is harmless 280 | x_offset = Store16(dst, x_offset, dx); 281 | } 282 | last_x += dx; 283 | int dy = points[i].y - last_y; 284 | if (dy == 0) { 285 | // pass 286 | } else if (dy > -256 && dy < 256) { 287 | dst[y_offset++] = std::abs(dy); 288 | } else { 289 | y_offset = Store16(dst, y_offset, dy); 290 | } 291 | last_y += dy; 292 | } 293 | *glyph_size = y_offset; 294 | return true; 295 | } 296 | 297 | // Compute the bounding box of the coordinates, and store into a glyf buffer. 298 | // A precondition is that there are at least 10 bytes available. 299 | // dst should point to the beginning of a 'glyf' record. 300 | void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { 301 | int x_min = 0; 302 | int y_min = 0; 303 | int x_max = 0; 304 | int y_max = 0; 305 | 306 | if (n_points > 0) { 307 | x_min = points[0].x; 308 | x_max = points[0].x; 309 | y_min = points[0].y; 310 | y_max = points[0].y; 311 | } 312 | for (unsigned int i = 1; i < n_points; ++i) { 313 | int x = points[i].x; 314 | int y = points[i].y; 315 | x_min = std::min(x, x_min); 316 | x_max = std::max(x, x_max); 317 | y_min = std::min(y, y_min); 318 | y_max = std::max(y, y_max); 319 | } 320 | size_t offset = 2; 321 | offset = Store16(dst, offset, x_min); 322 | offset = Store16(dst, offset, y_min); 323 | offset = Store16(dst, offset, x_max); 324 | offset = Store16(dst, offset, y_max); 325 | } 326 | 327 | 328 | bool SizeOfComposite(Buffer composite_stream, size_t* size, 329 | bool* have_instructions) { 330 | size_t start_offset = composite_stream.offset(); 331 | bool we_have_instructions = false; 332 | 333 | uint16_t flags = FLAG_MORE_COMPONENTS; 334 | while (flags & FLAG_MORE_COMPONENTS) { 335 | if (PREDICT_FALSE(!composite_stream.ReadU16(&flags))) { 336 | return FONT_COMPRESSION_FAILURE(); 337 | } 338 | we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; 339 | size_t arg_size = 2; // glyph index 340 | if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { 341 | arg_size += 4; 342 | } else { 343 | arg_size += 2; 344 | } 345 | if (flags & FLAG_WE_HAVE_A_SCALE) { 346 | arg_size += 2; 347 | } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { 348 | arg_size += 4; 349 | } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { 350 | arg_size += 8; 351 | } 352 | if (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { 353 | return FONT_COMPRESSION_FAILURE(); 354 | } 355 | } 356 | 357 | *size = composite_stream.offset() - start_offset; 358 | *have_instructions = we_have_instructions; 359 | 360 | return true; 361 | } 362 | 363 | bool Pad4(WOFF2Out* out) { 364 | uint8_t zeroes[] = {0, 0, 0}; 365 | if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { 366 | return FONT_COMPRESSION_FAILURE(); 367 | } 368 | uint32_t pad_bytes = Round4(out->Size()) - out->Size(); 369 | if (pad_bytes > 0) { 370 | if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { 371 | return FONT_COMPRESSION_FAILURE(); 372 | } 373 | } 374 | return true; 375 | } 376 | 377 | // Build TrueType loca table 378 | bool StoreLoca(const std::vector& loca_values, int index_format, 379 | uint32_t* checksum, WOFF2Out* out) { 380 | // TODO(user) figure out what index format to use based on whether max 381 | // offset fits into uint16_t or not 382 | const uint64_t loca_size = loca_values.size(); 383 | const uint64_t offset_size = index_format ? 4 : 2; 384 | if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { 385 | return FONT_COMPRESSION_FAILURE(); 386 | } 387 | std::vector loca_content(loca_size * offset_size); 388 | uint8_t* dst = &loca_content[0]; 389 | size_t offset = 0; 390 | for (size_t i = 0; i < loca_values.size(); ++i) { 391 | uint32_t value = loca_values[i]; 392 | if (index_format) { 393 | offset = StoreU32(dst, offset, value); 394 | } else { 395 | offset = Store16(dst, offset, value >> 1); 396 | } 397 | } 398 | *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); 399 | if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { 400 | return FONT_COMPRESSION_FAILURE(); 401 | } 402 | return true; 403 | } 404 | 405 | // Reconstruct entire glyf table based on transformed original 406 | bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, 407 | uint32_t* glyf_checksum, Table * loca_table, 408 | uint32_t* loca_checksum, WOFF2FontInfo* info, 409 | WOFF2Out* out) { 410 | static const int kNumSubStreams = 7; 411 | Buffer file(data, glyf_table->transform_length); 412 | uint16_t version; 413 | std::vector > substreams(kNumSubStreams); 414 | const size_t glyf_start = out->Size(); 415 | 416 | if (PREDICT_FALSE(!file.ReadU16(&version))) { 417 | return FONT_COMPRESSION_FAILURE(); 418 | } 419 | 420 | uint16_t flags; 421 | if (PREDICT_FALSE(!file.ReadU16(&flags))) { 422 | return FONT_COMPRESSION_FAILURE(); 423 | } 424 | bool has_overlap_bitmap = (flags & FLAG_OVERLAP_SIMPLE_BITMAP); 425 | 426 | if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || 427 | !file.ReadU16(&info->index_format))) { 428 | return FONT_COMPRESSION_FAILURE(); 429 | } 430 | 431 | // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca 432 | // dst_length here is origLength in the spec 433 | uint32_t expected_loca_dst_length = (info->index_format ? 4 : 2) 434 | * (static_cast(info->num_glyphs) + 1); 435 | if (PREDICT_FALSE(loca_table->dst_length != expected_loca_dst_length)) { 436 | return FONT_COMPRESSION_FAILURE(); 437 | } 438 | 439 | unsigned int offset = (2 + kNumSubStreams) * 4; 440 | if (PREDICT_FALSE(offset > glyf_table->transform_length)) { 441 | return FONT_COMPRESSION_FAILURE(); 442 | } 443 | // Invariant from here on: data_size >= offset 444 | for (int i = 0; i < kNumSubStreams; ++i) { 445 | uint32_t substream_size; 446 | if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { 447 | return FONT_COMPRESSION_FAILURE(); 448 | } 449 | if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { 450 | return FONT_COMPRESSION_FAILURE(); 451 | } 452 | substreams[i] = std::make_pair(data + offset, substream_size); 453 | offset += substream_size; 454 | } 455 | Buffer n_contour_stream(substreams[0].first, substreams[0].second); 456 | Buffer n_points_stream(substreams[1].first, substreams[1].second); 457 | Buffer flag_stream(substreams[2].first, substreams[2].second); 458 | Buffer glyph_stream(substreams[3].first, substreams[3].second); 459 | Buffer composite_stream(substreams[4].first, substreams[4].second); 460 | Buffer bbox_stream(substreams[5].first, substreams[5].second); 461 | Buffer instruction_stream(substreams[6].first, substreams[6].second); 462 | 463 | const uint8_t* overlap_bitmap = nullptr; 464 | unsigned int overlap_bitmap_length = 0; 465 | if (has_overlap_bitmap) { 466 | overlap_bitmap_length = (info->num_glyphs + 7) >> 3; 467 | overlap_bitmap = data + offset; 468 | if (PREDICT_FALSE(overlap_bitmap_length > 469 | glyf_table->transform_length - offset)) { 470 | return FONT_COMPRESSION_FAILURE(); 471 | } 472 | } 473 | 474 | std::vector loca_values(info->num_glyphs + 1); 475 | std::vector n_points_vec; 476 | std::unique_ptr points; 477 | size_t points_size = 0; 478 | const uint8_t* bbox_bitmap = bbox_stream.buffer(); 479 | // Safe because num_glyphs is bounded 480 | unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; 481 | if (!bbox_stream.Skip(bitmap_length)) { 482 | return FONT_COMPRESSION_FAILURE(); 483 | } 484 | 485 | // Temp buffer for glyph's. 486 | size_t glyph_buf_size = kDefaultGlyphBuf; 487 | std::unique_ptr glyph_buf(new uint8_t[glyph_buf_size]); 488 | 489 | info->x_mins.resize(info->num_glyphs); 490 | for (unsigned int i = 0; i < info->num_glyphs; ++i) { 491 | size_t glyph_size = 0; 492 | uint16_t n_contours = 0; 493 | bool have_bbox = false; 494 | if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { 495 | have_bbox = true; 496 | } 497 | if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { 498 | return FONT_COMPRESSION_FAILURE(); 499 | } 500 | 501 | if (n_contours == 0xffff) { 502 | // composite glyph 503 | bool have_instructions = false; 504 | unsigned int instruction_size = 0; 505 | if (PREDICT_FALSE(!have_bbox)) { 506 | // composite glyphs must have an explicit bbox 507 | return FONT_COMPRESSION_FAILURE(); 508 | } 509 | 510 | size_t composite_size; 511 | if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, 512 | &have_instructions))) { 513 | return FONT_COMPRESSION_FAILURE(); 514 | } 515 | if (have_instructions) { 516 | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { 517 | return FONT_COMPRESSION_FAILURE(); 518 | } 519 | } 520 | 521 | size_t size_needed = 12 + composite_size + instruction_size; 522 | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { 523 | glyph_buf.reset(new uint8_t[size_needed]); 524 | glyph_buf_size = size_needed; 525 | } 526 | 527 | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); 528 | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { 529 | return FONT_COMPRESSION_FAILURE(); 530 | } 531 | glyph_size += 8; 532 | 533 | if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, 534 | composite_size))) { 535 | return FONT_COMPRESSION_FAILURE(); 536 | } 537 | glyph_size += composite_size; 538 | if (have_instructions) { 539 | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); 540 | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, 541 | instruction_size))) { 542 | return FONT_COMPRESSION_FAILURE(); 543 | } 544 | glyph_size += instruction_size; 545 | } 546 | } else if (n_contours > 0) { 547 | // simple glyph 548 | n_points_vec.clear(); 549 | unsigned int total_n_points = 0; 550 | unsigned int n_points_contour; 551 | for (unsigned int j = 0; j < n_contours; ++j) { 552 | if (PREDICT_FALSE( 553 | !Read255UShort(&n_points_stream, &n_points_contour))) { 554 | return FONT_COMPRESSION_FAILURE(); 555 | } 556 | n_points_vec.push_back(n_points_contour); 557 | if (PREDICT_FALSE(total_n_points + n_points_contour < total_n_points)) { 558 | return FONT_COMPRESSION_FAILURE(); 559 | } 560 | total_n_points += n_points_contour; 561 | } 562 | unsigned int flag_size = total_n_points; 563 | if (PREDICT_FALSE( 564 | flag_size > flag_stream.length() - flag_stream.offset())) { 565 | return FONT_COMPRESSION_FAILURE(); 566 | } 567 | const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); 568 | const uint8_t* triplet_buf = glyph_stream.buffer() + 569 | glyph_stream.offset(); 570 | size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); 571 | size_t triplet_bytes_consumed = 0; 572 | if (points_size < total_n_points) { 573 | points_size = total_n_points; 574 | points.reset(new Point[points_size]); 575 | } 576 | if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, 577 | total_n_points, points.get(), &triplet_bytes_consumed))) { 578 | return FONT_COMPRESSION_FAILURE(); 579 | } 580 | if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { 581 | return FONT_COMPRESSION_FAILURE(); 582 | } 583 | if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { 584 | return FONT_COMPRESSION_FAILURE(); 585 | } 586 | unsigned int instruction_size; 587 | if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { 588 | return FONT_COMPRESSION_FAILURE(); 589 | } 590 | 591 | if (PREDICT_FALSE(total_n_points >= (1 << 27) 592 | || instruction_size >= (1 << 30))) { 593 | return FONT_COMPRESSION_FAILURE(); 594 | } 595 | size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points 596 | + instruction_size; 597 | if (PREDICT_FALSE(glyph_buf_size < size_needed)) { 598 | glyph_buf.reset(new uint8_t[size_needed]); 599 | glyph_buf_size = size_needed; 600 | } 601 | 602 | glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); 603 | if (have_bbox) { 604 | if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { 605 | return FONT_COMPRESSION_FAILURE(); 606 | } 607 | } else { 608 | ComputeBbox(total_n_points, points.get(), glyph_buf.get()); 609 | } 610 | glyph_size = kEndPtsOfContoursOffset; 611 | int end_point = -1; 612 | for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { 613 | end_point += n_points_vec[contour_ix]; 614 | if (PREDICT_FALSE(end_point >= 65536)) { 615 | return FONT_COMPRESSION_FAILURE(); 616 | } 617 | glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); 618 | } 619 | 620 | glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); 621 | if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, 622 | instruction_size))) { 623 | return FONT_COMPRESSION_FAILURE(); 624 | } 625 | glyph_size += instruction_size; 626 | 627 | bool has_overlap_bit = 628 | has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); 629 | 630 | if (PREDICT_FALSE(!StorePoints( 631 | total_n_points, points.get(), n_contours, instruction_size, 632 | has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { 633 | return FONT_COMPRESSION_FAILURE(); 634 | } 635 | } else { 636 | // n_contours == 0; empty glyph. Must NOT have a bbox. 637 | if (PREDICT_FALSE(have_bbox)) { 638 | #ifdef FONT_COMPRESSION_BIN 639 | fprintf(stderr, "Empty glyph has a bbox\n"); 640 | #endif 641 | return FONT_COMPRESSION_FAILURE(); 642 | } 643 | } 644 | 645 | loca_values[i] = out->Size() - glyf_start; 646 | if (PREDICT_FALSE(!out->Write(glyph_buf.get(), glyph_size))) { 647 | return FONT_COMPRESSION_FAILURE(); 648 | } 649 | 650 | // TODO(user) Old code aligned glyphs ... but do we actually need to? 651 | if (PREDICT_FALSE(!Pad4(out))) { 652 | return FONT_COMPRESSION_FAILURE(); 653 | } 654 | 655 | *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); 656 | 657 | // We may need x_min to reconstruct 'hmtx' 658 | if (n_contours > 0) { 659 | Buffer x_min_buf(glyph_buf.get() + 2, 2); 660 | if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { 661 | return FONT_COMPRESSION_FAILURE(); 662 | } 663 | } 664 | } 665 | 666 | // glyf_table dst_offset was set by ReconstructFont 667 | glyf_table->dst_length = out->Size() - glyf_table->dst_offset; 668 | loca_table->dst_offset = out->Size(); 669 | // loca[n] will be equal the length of the glyph data ('glyf') table 670 | loca_values[info->num_glyphs] = glyf_table->dst_length; 671 | if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, 672 | out))) { 673 | return FONT_COMPRESSION_FAILURE(); 674 | } 675 | loca_table->dst_length = out->Size() - loca_table->dst_offset; 676 | 677 | return true; 678 | } 679 | 680 | Table* FindTable(std::vector* tables, uint32_t tag) { 681 | for (Table* table : *tables) { 682 | if (table->tag == tag) { 683 | return table; 684 | } 685 | } 686 | return NULL; 687 | } 688 | 689 | // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm 690 | bool ReadNumHMetrics(const uint8_t* data, size_t data_size, 691 | uint16_t* num_hmetrics) { 692 | // Skip 34 to reach 'hhea' numberOfHMetrics 693 | Buffer buffer(data, data_size); 694 | if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { 695 | return FONT_COMPRESSION_FAILURE(); 696 | } 697 | return true; 698 | } 699 | 700 | // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format 701 | bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, 702 | size_t transformed_size, 703 | uint16_t num_glyphs, 704 | uint16_t num_hmetrics, 705 | const std::vector& x_mins, 706 | uint32_t* checksum, 707 | WOFF2Out* out) { 708 | Buffer hmtx_buff_in(transformed_buf, transformed_size); 709 | 710 | uint8_t hmtx_flags; 711 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { 712 | return FONT_COMPRESSION_FAILURE(); 713 | } 714 | 715 | std::vector advance_widths; 716 | std::vector lsbs; 717 | bool has_proportional_lsbs = (hmtx_flags & 1) == 0; 718 | bool has_monospace_lsbs = (hmtx_flags & 2) == 0; 719 | 720 | // Bits 2-7 are reserved and MUST be zero. 721 | if ((hmtx_flags & 0xFC) != 0) { 722 | #ifdef FONT_COMPRESSION_BIN 723 | fprintf(stderr, "Illegal hmtx flags; bits 2-7 must be 0\n"); 724 | #endif 725 | return FONT_COMPRESSION_FAILURE(); 726 | } 727 | 728 | // you say you transformed but there is little evidence of it 729 | if (has_proportional_lsbs && has_monospace_lsbs) { 730 | return FONT_COMPRESSION_FAILURE(); 731 | } 732 | 733 | assert(x_mins.size() == num_glyphs); 734 | 735 | // num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. 736 | if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { 737 | return FONT_COMPRESSION_FAILURE(); 738 | } 739 | 740 | // https://www.microsoft.com/typography/otspec/hmtx.htm 741 | // "...only one entry need be in the array, but that entry is required." 742 | if (PREDICT_FALSE(num_hmetrics < 1)) { 743 | return FONT_COMPRESSION_FAILURE(); 744 | } 745 | 746 | for (uint16_t i = 0; i < num_hmetrics; i++) { 747 | uint16_t advance_width; 748 | if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { 749 | return FONT_COMPRESSION_FAILURE(); 750 | } 751 | advance_widths.push_back(advance_width); 752 | } 753 | 754 | for (uint16_t i = 0; i < num_hmetrics; i++) { 755 | int16_t lsb; 756 | if (has_proportional_lsbs) { 757 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { 758 | return FONT_COMPRESSION_FAILURE(); 759 | } 760 | } else { 761 | lsb = x_mins[i]; 762 | } 763 | lsbs.push_back(lsb); 764 | } 765 | 766 | for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { 767 | int16_t lsb; 768 | if (has_monospace_lsbs) { 769 | if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { 770 | return FONT_COMPRESSION_FAILURE(); 771 | } 772 | } else { 773 | lsb = x_mins[i]; 774 | } 775 | lsbs.push_back(lsb); 776 | } 777 | 778 | // bake me a shiny new hmtx table 779 | uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; 780 | std::vector hmtx_table(hmtx_output_size); 781 | uint8_t* dst = &hmtx_table[0]; 782 | size_t dst_offset = 0; 783 | for (uint32_t i = 0; i < num_glyphs; i++) { 784 | if (i < num_hmetrics) { 785 | Store16(advance_widths[i], &dst_offset, dst); 786 | } 787 | Store16(lsbs[i], &dst_offset, dst); 788 | } 789 | 790 | *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); 791 | if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { 792 | return FONT_COMPRESSION_FAILURE(); 793 | } 794 | 795 | return true; 796 | } 797 | 798 | bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, 799 | const uint8_t* src_buf, size_t src_size) { 800 | size_t uncompressed_size = dst_size; 801 | BrotliDecoderResult result = BrotliDecoderDecompress( 802 | src_size, src_buf, &uncompressed_size, dst_buf); 803 | if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || 804 | uncompressed_size != dst_size)) { 805 | return FONT_COMPRESSION_FAILURE(); 806 | } 807 | return true; 808 | } 809 | 810 | bool ReadTableDirectory(Buffer* file, std::vector
* tables, 811 | size_t num_tables) { 812 | uint32_t src_offset = 0; 813 | for (size_t i = 0; i < num_tables; ++i) { 814 | Table* table = &(*tables)[i]; 815 | uint8_t flag_byte; 816 | if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { 817 | return FONT_COMPRESSION_FAILURE(); 818 | } 819 | uint32_t tag; 820 | if ((flag_byte & 0x3f) == 0x3f) { 821 | if (PREDICT_FALSE(!file->ReadU32(&tag))) { 822 | return FONT_COMPRESSION_FAILURE(); 823 | } 824 | } else { 825 | tag = kKnownTags[flag_byte & 0x3f]; 826 | } 827 | uint32_t flags = 0; 828 | uint8_t xform_version = (flag_byte >> 6) & 0x03; 829 | 830 | // 0 means xform for glyph/loca, non-0 for others 831 | if (tag == kGlyfTableTag || tag == kLocaTableTag) { 832 | if (xform_version == 0) { 833 | flags |= kWoff2FlagsTransform; 834 | } 835 | } else if (xform_version != 0) { 836 | flags |= kWoff2FlagsTransform; 837 | } 838 | flags |= xform_version; 839 | 840 | uint32_t dst_length; 841 | if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { 842 | return FONT_COMPRESSION_FAILURE(); 843 | } 844 | uint32_t transform_length = dst_length; 845 | if ((flags & kWoff2FlagsTransform) != 0) { 846 | if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { 847 | return FONT_COMPRESSION_FAILURE(); 848 | } 849 | if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { 850 | return FONT_COMPRESSION_FAILURE(); 851 | } 852 | } 853 | if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { 854 | return FONT_COMPRESSION_FAILURE(); 855 | } 856 | table->src_offset = src_offset; 857 | table->src_length = transform_length; 858 | src_offset += transform_length; 859 | 860 | table->tag = tag; 861 | table->flags = flags; 862 | table->transform_length = transform_length; 863 | table->dst_length = dst_length; 864 | } 865 | return true; 866 | } 867 | 868 | // Writes a single Offset Table entry 869 | size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, 870 | uint16_t num_tables) { 871 | offset = StoreU32(result, offset, flavor); // sfnt version 872 | offset = Store16(result, offset, num_tables); // num_tables 873 | unsigned max_pow2 = 0; 874 | while (1u << (max_pow2 + 1) <= num_tables) { 875 | max_pow2++; 876 | } 877 | const uint16_t output_search_range = (1u << max_pow2) << 4; 878 | offset = Store16(result, offset, output_search_range); // searchRange 879 | offset = Store16(result, offset, max_pow2); // entrySelector 880 | // rangeShift 881 | offset = Store16(result, offset, (num_tables << 4) - output_search_range); 882 | return offset; 883 | } 884 | 885 | size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { 886 | offset = StoreU32(result, offset, tag); 887 | offset = StoreU32(result, offset, 0); 888 | offset = StoreU32(result, offset, 0); 889 | offset = StoreU32(result, offset, 0); 890 | return offset; 891 | } 892 | 893 | // First table goes after all the headers, table directory, etc 894 | uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { 895 | uint64_t offset = kSfntHeaderSize + 896 | kSfntEntrySize * static_cast(hdr.num_tables); 897 | if (hdr.header_version) { 898 | offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) 899 | + kSfntHeaderSize * hdr.ttc_fonts.size(); 900 | for (const auto& ttc_font : hdr.ttc_fonts) { 901 | offset += kSfntEntrySize * ttc_font.table_indices.size(); 902 | } 903 | } 904 | return offset; 905 | } 906 | 907 | std::vector Tables(WOFF2Header* hdr, size_t font_index) { 908 | std::vector tables; 909 | if (PREDICT_FALSE(hdr->header_version)) { 910 | for (auto index : hdr->ttc_fonts[font_index].table_indices) { 911 | tables.push_back(&hdr->tables[index]); 912 | } 913 | } else { 914 | for (auto& table : hdr->tables) { 915 | tables.push_back(&table); 916 | } 917 | } 918 | return tables; 919 | } 920 | 921 | // Offset tables assumed to have been written in with 0's initially. 922 | // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) 923 | bool ReconstructFont(uint8_t* transformed_buf, 924 | const uint32_t transformed_buf_size, 925 | RebuildMetadata* metadata, 926 | WOFF2Header* hdr, 927 | size_t font_index, 928 | WOFF2Out* out) { 929 | size_t dest_offset = out->Size(); 930 | uint8_t table_entry[12]; 931 | WOFF2FontInfo* info = &metadata->font_infos[font_index]; 932 | std::vector tables = Tables(hdr, font_index); 933 | 934 | // 'glyf' without 'loca' doesn't make sense 935 | const Table* glyf_table = FindTable(&tables, kGlyfTableTag); 936 | const Table* loca_table = FindTable(&tables, kLocaTableTag); 937 | if (PREDICT_FALSE(static_cast(glyf_table) != 938 | static_cast(loca_table))) { 939 | #ifdef FONT_COMPRESSION_BIN 940 | fprintf(stderr, "Cannot have just one of glyf/loca\n"); 941 | #endif 942 | return FONT_COMPRESSION_FAILURE(); 943 | } 944 | 945 | if (glyf_table != NULL) { 946 | if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform) 947 | != (loca_table->flags & kWoff2FlagsTransform))) { 948 | #ifdef FONT_COMPRESSION_BIN 949 | fprintf(stderr, "Cannot transform just one of glyf/loca\n"); 950 | #endif 951 | return FONT_COMPRESSION_FAILURE(); 952 | } 953 | } 954 | 955 | uint32_t font_checksum = metadata->header_checksum; 956 | if (hdr->header_version) { 957 | font_checksum = hdr->ttc_fonts[font_index].header_checksum; 958 | } 959 | 960 | uint32_t loca_checksum = 0; 961 | for (size_t i = 0; i < tables.size(); i++) { 962 | Table& table = *tables[i]; 963 | 964 | std::pair checksum_key = {table.tag, table.src_offset}; 965 | bool reused = metadata->checksums.find(checksum_key) 966 | != metadata->checksums.end(); 967 | if (PREDICT_FALSE(font_index == 0 && reused)) { 968 | return FONT_COMPRESSION_FAILURE(); 969 | } 970 | 971 | // TODO(user) a collection with optimized hmtx that reused glyf/loca 972 | // would fail. We don't optimize hmtx for collections yet. 973 | if (PREDICT_FALSE(static_cast(table.src_offset) + table.src_length 974 | > transformed_buf_size)) { 975 | return FONT_COMPRESSION_FAILURE(); 976 | } 977 | 978 | if (table.tag == kHheaTableTag) { 979 | if (!ReadNumHMetrics(transformed_buf + table.src_offset, 980 | table.src_length, &info->num_hmetrics)) { 981 | return FONT_COMPRESSION_FAILURE(); 982 | } 983 | } 984 | 985 | uint32_t checksum = 0; 986 | if (!reused) { 987 | if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { 988 | if (table.tag == kHeadTableTag) { 989 | if (PREDICT_FALSE(table.src_length < 12)) { 990 | return FONT_COMPRESSION_FAILURE(); 991 | } 992 | // checkSumAdjustment = 0 993 | StoreU32(transformed_buf + table.src_offset, 8, 0); 994 | } 995 | table.dst_offset = dest_offset; 996 | checksum = ComputeULongSum(transformed_buf + table.src_offset, 997 | table.src_length); 998 | if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, 999 | table.src_length))) { 1000 | return FONT_COMPRESSION_FAILURE(); 1001 | } 1002 | } else { 1003 | if (table.tag == kGlyfTableTag) { 1004 | table.dst_offset = dest_offset; 1005 | 1006 | Table* loca_table = FindTable(&tables, kLocaTableTag); 1007 | if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, 1008 | &table, &checksum, loca_table, &loca_checksum, info, out))) { 1009 | return FONT_COMPRESSION_FAILURE(); 1010 | } 1011 | } else if (table.tag == kLocaTableTag) { 1012 | // All the work was done by ReconstructGlyf. We already know checksum. 1013 | checksum = loca_checksum; 1014 | } else if (table.tag == kHmtxTableTag) { 1015 | table.dst_offset = dest_offset; 1016 | // Tables are sorted so all the info we need has been gathered. 1017 | if (PREDICT_FALSE(!ReconstructTransformedHmtx( 1018 | transformed_buf + table.src_offset, table.src_length, 1019 | info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, 1020 | out))) { 1021 | return FONT_COMPRESSION_FAILURE(); 1022 | } 1023 | } else { 1024 | return FONT_COMPRESSION_FAILURE(); // transform unknown 1025 | } 1026 | } 1027 | metadata->checksums[checksum_key] = checksum; 1028 | } else { 1029 | checksum = metadata->checksums[checksum_key]; 1030 | } 1031 | font_checksum += checksum; 1032 | 1033 | // update the table entry with real values. 1034 | StoreU32(table_entry, 0, checksum); 1035 | StoreU32(table_entry, 4, table.dst_offset); 1036 | StoreU32(table_entry, 8, table.dst_length); 1037 | if (PREDICT_FALSE(!out->Write(table_entry, 1038 | info->table_entry_by_tag[table.tag] + 4, 12))) { 1039 | return FONT_COMPRESSION_FAILURE(); 1040 | } 1041 | 1042 | // We replaced 0's. Update overall checksum. 1043 | font_checksum += ComputeULongSum(table_entry, 12); 1044 | 1045 | if (PREDICT_FALSE(!Pad4(out))) { 1046 | return FONT_COMPRESSION_FAILURE(); 1047 | } 1048 | 1049 | if (PREDICT_FALSE(static_cast(table.dst_offset + table.dst_length) 1050 | > out->Size())) { 1051 | return FONT_COMPRESSION_FAILURE(); 1052 | } 1053 | dest_offset = out->Size(); 1054 | } 1055 | 1056 | // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. 1057 | Table* head_table = FindTable(&tables, kHeadTableTag); 1058 | if (head_table) { 1059 | if (PREDICT_FALSE(head_table->dst_length < 12)) { 1060 | return FONT_COMPRESSION_FAILURE(); 1061 | } 1062 | uint8_t checksum_adjustment[4]; 1063 | StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); 1064 | if (PREDICT_FALSE(!out->Write(checksum_adjustment, 1065 | head_table->dst_offset + 8, 4))) { 1066 | return FONT_COMPRESSION_FAILURE(); 1067 | } 1068 | } 1069 | 1070 | return true; 1071 | } 1072 | 1073 | bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { 1074 | Buffer file(data, length); 1075 | 1076 | uint32_t signature; 1077 | if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || 1078 | !file.ReadU32(&hdr->flavor))) { 1079 | return FONT_COMPRESSION_FAILURE(); 1080 | } 1081 | 1082 | // TODO(user): Should call IsValidVersionTag() here. 1083 | 1084 | uint32_t reported_length; 1085 | if (PREDICT_FALSE( 1086 | !file.ReadU32(&reported_length) || length != reported_length)) { 1087 | return FONT_COMPRESSION_FAILURE(); 1088 | } 1089 | if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { 1090 | return FONT_COMPRESSION_FAILURE(); 1091 | } 1092 | 1093 | // We don't care about these fields of the header: 1094 | // uint16_t reserved 1095 | // uint32_t total_sfnt_size, we don't believe this, will compute later 1096 | if (PREDICT_FALSE(!file.Skip(6))) { 1097 | return FONT_COMPRESSION_FAILURE(); 1098 | } 1099 | if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { 1100 | return FONT_COMPRESSION_FAILURE(); 1101 | } 1102 | // We don't care about these fields of the header: 1103 | // uint16_t major_version, minor_version 1104 | if (PREDICT_FALSE(!file.Skip(2 * 2))) { 1105 | return FONT_COMPRESSION_FAILURE(); 1106 | } 1107 | uint32_t meta_offset; 1108 | uint32_t meta_length; 1109 | uint32_t meta_length_orig; 1110 | if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || 1111 | !file.ReadU32(&meta_length) || 1112 | !file.ReadU32(&meta_length_orig))) { 1113 | return FONT_COMPRESSION_FAILURE(); 1114 | } 1115 | if (meta_offset) { 1116 | if (PREDICT_FALSE( 1117 | meta_offset >= length || length - meta_offset < meta_length)) { 1118 | return FONT_COMPRESSION_FAILURE(); 1119 | } 1120 | } 1121 | uint32_t priv_offset; 1122 | uint32_t priv_length; 1123 | if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || 1124 | !file.ReadU32(&priv_length))) { 1125 | return FONT_COMPRESSION_FAILURE(); 1126 | } 1127 | if (priv_offset) { 1128 | if (PREDICT_FALSE( 1129 | priv_offset >= length || length - priv_offset < priv_length)) { 1130 | return FONT_COMPRESSION_FAILURE(); 1131 | } 1132 | } 1133 | hdr->tables.resize(hdr->num_tables); 1134 | if (PREDICT_FALSE(!ReadTableDirectory( 1135 | &file, &hdr->tables, hdr->num_tables))) { 1136 | return FONT_COMPRESSION_FAILURE(); 1137 | } 1138 | 1139 | // Before we sort for output the last table end is the uncompressed size. 1140 | Table& last_table = hdr->tables.back(); 1141 | hdr->uncompressed_size = last_table.src_offset + last_table.src_length; 1142 | if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { 1143 | return FONT_COMPRESSION_FAILURE(); 1144 | } 1145 | 1146 | hdr->header_version = 0; 1147 | 1148 | if (hdr->flavor == kTtcFontFlavor) { 1149 | if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { 1150 | return FONT_COMPRESSION_FAILURE(); 1151 | } 1152 | if (PREDICT_FALSE(hdr->header_version != 0x00010000 1153 | && hdr->header_version != 0x00020000)) { 1154 | return FONT_COMPRESSION_FAILURE(); 1155 | } 1156 | uint32_t num_fonts; 1157 | if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { 1158 | return FONT_COMPRESSION_FAILURE(); 1159 | } 1160 | hdr->ttc_fonts.resize(num_fonts); 1161 | 1162 | for (uint32_t i = 0; i < num_fonts; i++) { 1163 | TtcFont& ttc_font = hdr->ttc_fonts[i]; 1164 | uint32_t num_tables; 1165 | if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { 1166 | return FONT_COMPRESSION_FAILURE(); 1167 | } 1168 | if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { 1169 | return FONT_COMPRESSION_FAILURE(); 1170 | } 1171 | 1172 | ttc_font.table_indices.resize(num_tables); 1173 | 1174 | 1175 | unsigned int glyf_idx = 0; 1176 | unsigned int loca_idx = 0; 1177 | 1178 | for (uint32_t j = 0; j < num_tables; j++) { 1179 | unsigned int table_idx; 1180 | if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || 1181 | table_idx >= hdr->tables.size()) { 1182 | return FONT_COMPRESSION_FAILURE(); 1183 | } 1184 | ttc_font.table_indices[j] = table_idx; 1185 | 1186 | const Table& table = hdr->tables[table_idx]; 1187 | if (table.tag == kLocaTableTag) { 1188 | loca_idx = table_idx; 1189 | } 1190 | if (table.tag == kGlyfTableTag) { 1191 | glyf_idx = table_idx; 1192 | } 1193 | 1194 | } 1195 | 1196 | // if we have both glyf and loca make sure they are consecutive 1197 | // if we have just one we'll reject the font elsewhere 1198 | if (glyf_idx > 0 || loca_idx > 0) { 1199 | if (PREDICT_FALSE(glyf_idx > loca_idx || loca_idx - glyf_idx != 1)) { 1200 | #ifdef FONT_COMPRESSION_BIN 1201 | fprintf(stderr, "TTC font %d has non-consecutive glyf/loca\n", i); 1202 | #endif 1203 | return FONT_COMPRESSION_FAILURE(); 1204 | } 1205 | } 1206 | } 1207 | } 1208 | 1209 | const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); 1210 | 1211 | hdr->compressed_offset = file.offset(); 1212 | if (PREDICT_FALSE(hdr->compressed_offset > 1213 | std::numeric_limits::max())) { 1214 | return FONT_COMPRESSION_FAILURE(); 1215 | } 1216 | uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); 1217 | uint64_t dst_offset = first_table_offset; 1218 | 1219 | 1220 | if (PREDICT_FALSE(src_offset > length)) { 1221 | #ifdef FONT_COMPRESSION_BIN 1222 | fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " 1223 | "dst_offset %" PRIu64 "\n", 1224 | src_offset, length, dst_offset); 1225 | #endif 1226 | return FONT_COMPRESSION_FAILURE(); 1227 | } 1228 | if (meta_offset) { 1229 | if (PREDICT_FALSE(src_offset != meta_offset)) { 1230 | return FONT_COMPRESSION_FAILURE(); 1231 | } 1232 | src_offset = Round4(meta_offset + meta_length); 1233 | if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { 1234 | return FONT_COMPRESSION_FAILURE(); 1235 | } 1236 | } 1237 | 1238 | if (priv_offset) { 1239 | if (PREDICT_FALSE(src_offset != priv_offset)) { 1240 | return FONT_COMPRESSION_FAILURE(); 1241 | } 1242 | src_offset = Round4(priv_offset + priv_length); 1243 | if (PREDICT_FALSE(src_offset > std::numeric_limits::max())) { 1244 | return FONT_COMPRESSION_FAILURE(); 1245 | } 1246 | } 1247 | 1248 | if (PREDICT_FALSE(src_offset != Round4(length))) { 1249 | return FONT_COMPRESSION_FAILURE(); 1250 | } 1251 | 1252 | return true; 1253 | } 1254 | 1255 | // Write everything before the actual table data 1256 | bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, 1257 | WOFF2Header* hdr, WOFF2Out* out) { 1258 | std::vector output(ComputeOffsetToFirstTable(*hdr), 0); 1259 | 1260 | // Re-order tables in output (OTSpec) order 1261 | std::vector
sorted_tables(hdr->tables); 1262 | if (hdr->header_version) { 1263 | // collection; we have to sort the table offset vector in each font 1264 | for (auto& ttc_font : hdr->ttc_fonts) { 1265 | std::map sorted_index_by_tag; 1266 | for (auto table_index : ttc_font.table_indices) { 1267 | sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; 1268 | } 1269 | uint16_t index = 0; 1270 | for (auto& i : sorted_index_by_tag) { 1271 | ttc_font.table_indices[index++] = i.second; 1272 | } 1273 | } 1274 | } else { 1275 | // non-collection; we can just sort the tables 1276 | std::sort(sorted_tables.begin(), sorted_tables.end()); 1277 | } 1278 | 1279 | // Start building the font 1280 | uint8_t* result = &output[0]; 1281 | size_t offset = 0; 1282 | if (hdr->header_version) { 1283 | // TTC header 1284 | offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag 1285 | offset = StoreU32(result, offset, hdr->header_version); // FIXED Version 1286 | offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts 1287 | // Space for ULONG OffsetTable[numFonts] (zeroed initially) 1288 | size_t offset_table = offset; // keep start of offset table for later 1289 | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { 1290 | offset = StoreU32(result, offset, 0); // will fill real values in later 1291 | } 1292 | // space for DSIG fields for header v2 1293 | if (hdr->header_version == 0x00020000) { 1294 | offset = StoreU32(result, offset, 0); // ULONG ulDsigTag 1295 | offset = StoreU32(result, offset, 0); // ULONG ulDsigLength 1296 | offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset 1297 | } 1298 | 1299 | // write Offset Tables and store the location of each in TTC Header 1300 | metadata->font_infos.resize(hdr->ttc_fonts.size()); 1301 | for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { 1302 | TtcFont& ttc_font = hdr->ttc_fonts[i]; 1303 | 1304 | // write Offset Table location into TTC Header 1305 | offset_table = StoreU32(result, offset_table, offset); 1306 | 1307 | // write the actual offset table so our header doesn't lie 1308 | ttc_font.dst_offset = offset; 1309 | offset = StoreOffsetTable(result, offset, ttc_font.flavor, 1310 | ttc_font.table_indices.size()); 1311 | 1312 | for (const auto table_index : ttc_font.table_indices) { 1313 | uint32_t tag = hdr->tables[table_index].tag; 1314 | metadata->font_infos[i].table_entry_by_tag[tag] = offset; 1315 | offset = StoreTableEntry(result, offset, tag); 1316 | } 1317 | 1318 | ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], 1319 | offset - ttc_font.dst_offset); 1320 | } 1321 | } else { 1322 | metadata->font_infos.resize(1); 1323 | offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); 1324 | for (uint16_t i = 0; i < hdr->num_tables; ++i) { 1325 | metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; 1326 | offset = StoreTableEntry(result, offset, sorted_tables[i].tag); 1327 | } 1328 | } 1329 | 1330 | if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { 1331 | return FONT_COMPRESSION_FAILURE(); 1332 | } 1333 | metadata->header_checksum = ComputeULongSum(&output[0], output.size()); 1334 | return true; 1335 | } 1336 | 1337 | } // namespace 1338 | 1339 | size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { 1340 | Buffer file(data, length); 1341 | uint32_t total_length; 1342 | 1343 | if (!file.Skip(16) || 1344 | !file.ReadU32(&total_length)) { 1345 | return 0; 1346 | } 1347 | return total_length; 1348 | } 1349 | 1350 | bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, 1351 | const uint8_t *data, size_t length) { 1352 | WOFF2MemoryOut out(result, result_length); 1353 | return ConvertWOFF2ToTTF(data, length, &out); 1354 | } 1355 | 1356 | bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, 1357 | WOFF2Out* out) { 1358 | RebuildMetadata metadata; 1359 | WOFF2Header hdr; 1360 | if (!ReadWOFF2Header(data, length, &hdr)) { 1361 | return FONT_COMPRESSION_FAILURE(); 1362 | } 1363 | 1364 | if (!WriteHeaders(data, length, &metadata, &hdr, out)) { 1365 | return FONT_COMPRESSION_FAILURE(); 1366 | } 1367 | 1368 | const float compression_ratio = (float) hdr.uncompressed_size / length; 1369 | if (compression_ratio > kMaxPlausibleCompressionRatio) { 1370 | #ifdef FONT_COMPRESSION_BIN 1371 | fprintf(stderr, "Implausible compression ratio %.01f\n", compression_ratio); 1372 | #endif 1373 | return FONT_COMPRESSION_FAILURE(); 1374 | } 1375 | 1376 | const uint8_t* src_buf = data + hdr.compressed_offset; 1377 | std::vector uncompressed_buf(hdr.uncompressed_size); 1378 | if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { 1379 | return FONT_COMPRESSION_FAILURE(); 1380 | } 1381 | if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], 1382 | hdr.uncompressed_size, src_buf, 1383 | hdr.compressed_length))) { 1384 | return FONT_COMPRESSION_FAILURE(); 1385 | } 1386 | 1387 | for (size_t i = 0; i < metadata.font_infos.size(); i++) { 1388 | if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], 1389 | hdr.uncompressed_size, 1390 | &metadata, &hdr, i, out))) { 1391 | return FONT_COMPRESSION_FAILURE(); 1392 | } 1393 | } 1394 | 1395 | return true; 1396 | } 1397 | 1398 | } // namespace woff2 1399 | -------------------------------------------------------------------------------- /src/woff2_decompress.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2013 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* A very simple commandline tool for decompressing woff2 format files to true 8 | type font files. */ 9 | 10 | #include 11 | 12 | #include "./file.h" 13 | #include 14 | 15 | 16 | int main(int argc, char **argv) { 17 | if (argc != 2) { 18 | fprintf(stderr, "One argument, the input filename, must be provided.\n"); 19 | return 1; 20 | } 21 | 22 | std::string filename(argv[1]); 23 | std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf"; 24 | 25 | // Note: update woff2_dec_fuzzer_new_entry.cc if this pattern changes. 26 | std::string input = woff2::GetFileContent(filename); 27 | const uint8_t* raw_input = reinterpret_cast(input.data()); 28 | std::string output( 29 | std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()), 30 | woff2::kDefaultMaxSize), 31 | 0); 32 | woff2::WOFF2StringOut out(&output); 33 | 34 | const bool ok = woff2::ConvertWOFF2ToTTF(raw_input, input.size(), &out); 35 | 36 | if (ok) { 37 | woff2::SetFileContents(outfilename, output.begin(), 38 | output.begin() + out.Size()); 39 | } 40 | return ok ? 0 : 1; 41 | } 42 | -------------------------------------------------------------------------------- /src/woff2_enc.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Library for converting TTF format font files to their WOFF2 versions. */ 8 | 9 | #include 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include 19 | #include "./buffer.h" 20 | #include "./font.h" 21 | #include "./normalize.h" 22 | #include "./round.h" 23 | #include "./store_bytes.h" 24 | #include "./table_tags.h" 25 | #include "./transform.h" 26 | #include "./variable_length.h" 27 | #include "./woff2_common.h" 28 | 29 | namespace woff2 { 30 | 31 | 32 | namespace { 33 | 34 | const size_t kWoff2HeaderSize = 48; 35 | const size_t kWoff2EntrySize = 20; 36 | 37 | bool Compress(const uint8_t* data, const size_t len, uint8_t* result, 38 | uint32_t* result_len, BrotliEncoderMode mode, int quality) { 39 | size_t compressed_len = *result_len; 40 | if (BrotliEncoderCompress(quality, BROTLI_DEFAULT_WINDOW, mode, len, data, 41 | &compressed_len, result) == 0) { 42 | return false; 43 | } 44 | *result_len = compressed_len; 45 | return true; 46 | } 47 | 48 | bool Woff2Compress(const uint8_t* data, const size_t len, 49 | uint8_t* result, uint32_t* result_len, 50 | int quality) { 51 | return Compress(data, len, result, result_len, 52 | BROTLI_MODE_FONT, quality); 53 | } 54 | 55 | bool TextCompress(const uint8_t* data, const size_t len, 56 | uint8_t* result, uint32_t* result_len, 57 | int quality) { 58 | return Compress(data, len, result, result_len, 59 | BROTLI_MODE_TEXT, quality); 60 | } 61 | 62 | int KnownTableIndex(uint32_t tag) { 63 | for (int i = 0; i < 63; ++i) { 64 | if (tag == kKnownTags[i]) return i; 65 | } 66 | return 63; 67 | } 68 | 69 | void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { 70 | uint8_t flag_byte = (table.flags & 0xC0) | KnownTableIndex(table.tag); 71 | dst[(*offset)++] = flag_byte; 72 | // The index here is treated as a set of flag bytes because 73 | // bits 6 and 7 of the byte are reserved for future use as flags. 74 | // 0x3f or 63 means an arbitrary table tag. 75 | if ((flag_byte & 0x3f) == 0x3f) { 76 | StoreU32(table.tag, offset, dst); 77 | } 78 | StoreBase128(table.src_length, offset, dst); 79 | if ((table.flags & kWoff2FlagsTransform) != 0) { 80 | StoreBase128(table.transform_length, offset, dst); 81 | } 82 | } 83 | 84 | size_t TableEntrySize(const Table& table) { 85 | uint8_t flag_byte = KnownTableIndex(table.tag); 86 | size_t size = ((flag_byte & 0x3f) != 0x3f) ? 1 : 5; 87 | size += Base128Size(table.src_length); 88 | if ((table.flags & kWoff2FlagsTransform) != 0) { 89 | size += Base128Size(table.transform_length); 90 | } 91 | return size; 92 | } 93 | 94 | size_t ComputeWoff2Length(const FontCollection& font_collection, 95 | const std::vector
& tables, 96 | std::map, uint16_t> 97 | index_by_tag_offset, 98 | size_t compressed_data_length, 99 | size_t extended_metadata_length) { 100 | size_t size = kWoff2HeaderSize; 101 | 102 | for (const auto& table : tables) { 103 | size += TableEntrySize(table); 104 | } 105 | 106 | // for collections only, collection tables 107 | if (font_collection.flavor == kTtcFontFlavor) { 108 | size += 4; // UInt32 Version of TTC Header 109 | size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts 110 | 111 | size += 4 * font_collection.fonts.size(); // UInt32 flavor for each 112 | 113 | for (const auto& font : font_collection.fonts) { 114 | size += Size255UShort(font.tables.size()); // 255UInt16 numTables 115 | for (const auto& entry : font.tables) { 116 | const Font::Table& table = entry.second; 117 | // no collection entry for xform table 118 | if (table.tag & 0x80808080) continue; 119 | 120 | std::pair tag_offset(table.tag, table.offset); 121 | uint16_t table_index = index_by_tag_offset[tag_offset]; 122 | size += Size255UShort(table_index); // 255UInt16 index entry 123 | } 124 | } 125 | } 126 | 127 | // compressed data 128 | size += compressed_data_length; 129 | size = Round4(size); 130 | 131 | size += extended_metadata_length; 132 | return size; 133 | } 134 | 135 | size_t ComputeUncompressedLength(const Font& font) { 136 | // sfnt header + offset table 137 | size_t size = 12 + 16 * font.num_tables; 138 | for (const auto& entry : font.tables) { 139 | const Font::Table& table = entry.second; 140 | if (table.tag & 0x80808080) continue; // xform tables don't stay 141 | if (table.IsReused()) continue; // don't have to pay twice 142 | size += Round4(table.length); 143 | } 144 | return size; 145 | } 146 | 147 | size_t ComputeUncompressedLength(const FontCollection& font_collection) { 148 | if (font_collection.flavor != kTtcFontFlavor) { 149 | return ComputeUncompressedLength(font_collection.fonts[0]); 150 | } 151 | size_t size = CollectionHeaderSize(font_collection.header_version, 152 | font_collection.fonts.size()); 153 | for (const auto& font : font_collection.fonts) { 154 | size += ComputeUncompressedLength(font); 155 | } 156 | return size; 157 | } 158 | 159 | size_t ComputeTotalTransformLength(const Font& font) { 160 | size_t total = 0; 161 | for (const auto& i : font.tables) { 162 | const Font::Table& table = i.second; 163 | if (table.IsReused()) { 164 | continue; 165 | } 166 | if (table.tag & 0x80808080 || !font.FindTable(table.tag ^ 0x80808080)) { 167 | // Count transformed tables and non-transformed tables that do not have 168 | // transformed versions. 169 | total += table.length; 170 | } 171 | } 172 | return total; 173 | } 174 | 175 | } // namespace 176 | 177 | size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length) { 178 | return MaxWOFF2CompressedSize(data, length, ""); 179 | } 180 | 181 | size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length, 182 | const std::string& extended_metadata) { 183 | // Except for the header size, which is 32 bytes larger in woff2 format, 184 | // all other parts should be smaller (table header in short format, 185 | // transformations and compression). Just to be sure, we will give some 186 | // headroom anyway. 187 | return length + 1024 + extended_metadata.length(); 188 | } 189 | 190 | uint32_t CompressedBufferSize(uint32_t original_size) { 191 | return 1.2 * original_size + 10240; 192 | } 193 | 194 | bool TransformFontCollection(FontCollection* font_collection) { 195 | for (auto& font : font_collection->fonts) { 196 | if (!TransformGlyfAndLocaTables(&font)) { 197 | #ifdef FONT_COMPRESSION_BIN 198 | fprintf(stderr, "glyf/loca transformation failed.\n"); 199 | #endif 200 | return FONT_COMPRESSION_FAILURE(); 201 | } 202 | } 203 | 204 | return true; 205 | } 206 | 207 | bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, 208 | uint8_t *result, size_t *result_length) { 209 | WOFF2Params params; 210 | return ConvertTTFToWOFF2(data, length, result, result_length, 211 | params); 212 | } 213 | 214 | bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, 215 | uint8_t *result, size_t *result_length, 216 | const WOFF2Params& params) { 217 | FontCollection font_collection; 218 | if (!ReadFontCollection(data, length, &font_collection)) { 219 | #ifdef FONT_COMPRESSION_BIN 220 | fprintf(stderr, "Parsing of the input font failed.\n"); 221 | #endif 222 | return FONT_COMPRESSION_FAILURE(); 223 | } 224 | 225 | if (!NormalizeFontCollection(&font_collection)) { 226 | return FONT_COMPRESSION_FAILURE(); 227 | } 228 | 229 | if (params.allow_transforms && !TransformFontCollection(&font_collection)) { 230 | return FONT_COMPRESSION_FAILURE(); 231 | } else { 232 | // glyf/loca use 11 to flag "not transformed" 233 | for (auto& font : font_collection.fonts) { 234 | Font::Table* glyf_table = font.FindTable(kGlyfTableTag); 235 | Font::Table* loca_table = font.FindTable(kLocaTableTag); 236 | if (glyf_table) { 237 | glyf_table->flag_byte |= 0xc0; 238 | } 239 | if (loca_table) { 240 | loca_table->flag_byte |= 0xc0; 241 | } 242 | } 243 | } 244 | 245 | // Although the compressed size of each table in the final woff2 file won't 246 | // be larger than its transform_length, we have to allocate a large enough 247 | // buffer for the compressor, since the compressor can potentially increase 248 | // the size. If the compressor overflows this, it should return false and 249 | // then this function will also return false. 250 | 251 | size_t total_transform_length = 0; 252 | for (const auto& font : font_collection.fonts) { 253 | total_transform_length += ComputeTotalTransformLength(font); 254 | } 255 | size_t compression_buffer_size = CompressedBufferSize(total_transform_length); 256 | std::vector compression_buf(compression_buffer_size); 257 | uint32_t total_compressed_length = compression_buffer_size; 258 | 259 | // Collect all transformed data into one place in output order. 260 | std::vector transform_buf(total_transform_length); 261 | size_t transform_offset = 0; 262 | for (const auto& font : font_collection.fonts) { 263 | for (const auto tag : font.OutputOrderedTags()) { 264 | const Font::Table& original = font.tables.at(tag); 265 | if (original.IsReused()) continue; 266 | if (tag & 0x80808080) continue; 267 | const Font::Table* table_to_store = font.FindTable(tag ^ 0x80808080); 268 | if (table_to_store == NULL) table_to_store = &original; 269 | 270 | StoreBytes(table_to_store->data, table_to_store->length, 271 | &transform_offset, &transform_buf[0]); 272 | } 273 | } 274 | 275 | // Compress all transformed data in one stream. 276 | if (!Woff2Compress(transform_buf.data(), total_transform_length, 277 | &compression_buf[0], 278 | &total_compressed_length, 279 | params.brotli_quality)) { 280 | #ifdef FONT_COMPRESSION_BIN 281 | fprintf(stderr, "Compression of combined table failed.\n"); 282 | #endif 283 | return FONT_COMPRESSION_FAILURE(); 284 | } 285 | 286 | #ifdef FONT_COMPRESSION_BIN 287 | fprintf(stderr, "Compressed %zu to %u.\n", total_transform_length, 288 | total_compressed_length); 289 | #endif 290 | 291 | // Compress the extended metadata 292 | // TODO(user): how does this apply to collections 293 | uint32_t compressed_metadata_buf_length = 294 | CompressedBufferSize(params.extended_metadata.length()); 295 | std::vector compressed_metadata_buf(compressed_metadata_buf_length); 296 | 297 | if (params.extended_metadata.length() > 0) { 298 | if (!TextCompress((const uint8_t*)params.extended_metadata.data(), 299 | params.extended_metadata.length(), 300 | compressed_metadata_buf.data(), 301 | &compressed_metadata_buf_length, 302 | params.brotli_quality)) { 303 | #ifdef FONT_COMPRESSION_BIN 304 | fprintf(stderr, "Compression of extended metadata failed.\n"); 305 | #endif 306 | return FONT_COMPRESSION_FAILURE(); 307 | } 308 | } else { 309 | compressed_metadata_buf_length = 0; 310 | } 311 | 312 | std::vector
tables; 313 | std::map, uint16_t> index_by_tag_offset; 314 | 315 | for (const auto& font : font_collection.fonts) { 316 | 317 | for (const auto tag : font.OutputOrderedTags()) { 318 | const Font::Table& src_table = font.tables.at(tag); 319 | if (src_table.IsReused()) { 320 | continue; 321 | } 322 | 323 | std::pair tag_offset(src_table.tag, src_table.offset); 324 | if (index_by_tag_offset.find(tag_offset) == index_by_tag_offset.end()) { 325 | index_by_tag_offset[tag_offset] = tables.size(); 326 | } else { 327 | return false; 328 | } 329 | 330 | Table table; 331 | table.tag = src_table.tag; 332 | table.flags = src_table.flag_byte; 333 | table.src_length = src_table.length; 334 | table.transform_length = src_table.length; 335 | const uint8_t* transformed_data = src_table.data; 336 | const Font::Table* transformed_table = 337 | font.FindTable(src_table.tag ^ 0x80808080); 338 | if (transformed_table != NULL) { 339 | table.flags = transformed_table->flag_byte; 340 | table.flags |= kWoff2FlagsTransform; 341 | table.transform_length = transformed_table->length; 342 | transformed_data = transformed_table->data; 343 | 344 | } 345 | tables.push_back(table); 346 | } 347 | } 348 | 349 | size_t woff2_length = ComputeWoff2Length(font_collection, tables, 350 | index_by_tag_offset, total_compressed_length, 351 | compressed_metadata_buf_length); 352 | if (woff2_length > *result_length) { 353 | #ifdef FONT_COMPRESSION_BIN 354 | fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", 355 | *result_length, woff2_length); 356 | #endif 357 | return FONT_COMPRESSION_FAILURE(); 358 | } 359 | *result_length = woff2_length; 360 | 361 | size_t offset = 0; 362 | 363 | // start of woff2 header (http://www.w3.org/TR/WOFF2/#woff20Header) 364 | StoreU32(kWoff2Signature, &offset, result); 365 | if (font_collection.flavor != kTtcFontFlavor) { 366 | StoreU32(font_collection.fonts[0].flavor, &offset, result); 367 | } else { 368 | StoreU32(kTtcFontFlavor, &offset, result); 369 | } 370 | StoreU32(woff2_length, &offset, result); 371 | Store16(tables.size(), &offset, result); 372 | Store16(0, &offset, result); // reserved 373 | // totalSfntSize 374 | StoreU32(ComputeUncompressedLength(font_collection), &offset, result); 375 | StoreU32(total_compressed_length, &offset, result); // totalCompressedSize 376 | 377 | // Let's just all be v1.0 378 | Store16(1, &offset, result); // majorVersion 379 | Store16(0, &offset, result); // minorVersion 380 | if (compressed_metadata_buf_length > 0) { 381 | StoreU32(woff2_length - compressed_metadata_buf_length, 382 | &offset, result); // metaOffset 383 | StoreU32(compressed_metadata_buf_length, &offset, result); // metaLength 384 | StoreU32(params.extended_metadata.length(), 385 | &offset, result); // metaOrigLength 386 | } else { 387 | StoreU32(0, &offset, result); // metaOffset 388 | StoreU32(0, &offset, result); // metaLength 389 | StoreU32(0, &offset, result); // metaOrigLength 390 | } 391 | StoreU32(0, &offset, result); // privOffset 392 | StoreU32(0, &offset, result); // privLength 393 | // end of woff2 header 394 | 395 | // table directory (http://www.w3.org/TR/WOFF2/#table_dir_format) 396 | for (const auto& table : tables) { 397 | StoreTableEntry(table, &offset, result); 398 | } 399 | 400 | // for collections only, collection table directory 401 | if (font_collection.flavor == kTtcFontFlavor) { 402 | StoreU32(font_collection.header_version, &offset, result); 403 | Store255UShort(font_collection.fonts.size(), &offset, result); 404 | for (const Font& font : font_collection.fonts) { 405 | 406 | uint16_t num_tables = 0; 407 | for (const auto& entry : font.tables) { 408 | const Font::Table& table = entry.second; 409 | if (table.tag & 0x80808080) continue; // don't write xform tables 410 | num_tables++; 411 | } 412 | Store255UShort(num_tables, &offset, result); 413 | 414 | StoreU32(font.flavor, &offset, result); 415 | for (const auto& entry : font.tables) { 416 | const Font::Table& table = entry.second; 417 | if (table.tag & 0x80808080) continue; // don't write xform tables 418 | 419 | // for reused tables, only the original has an updated offset 420 | uint32_t table_offset = 421 | table.IsReused() ? table.reuse_of->offset : table.offset; 422 | uint32_t table_length = 423 | table.IsReused() ? table.reuse_of->length : table.length; 424 | std::pair tag_offset(table.tag, table_offset); 425 | if (index_by_tag_offset.find(tag_offset) == index_by_tag_offset.end()) { 426 | #ifdef FONT_COMPRESSION_BIN 427 | fprintf(stderr, "Missing table index for offset 0x%08x\n", 428 | table_offset); 429 | #endif 430 | return FONT_COMPRESSION_FAILURE(); 431 | } 432 | uint16_t index = index_by_tag_offset[tag_offset]; 433 | Store255UShort(index, &offset, result); 434 | 435 | } 436 | 437 | } 438 | } 439 | 440 | // compressed data format (http://www.w3.org/TR/WOFF2/#table_format) 441 | 442 | StoreBytes(&compression_buf[0], total_compressed_length, &offset, result); 443 | offset = Round4(offset); 444 | 445 | StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, 446 | &offset, result); 447 | 448 | if (*result_length != offset) { 449 | #ifdef FONT_COMPRESSION_BIN 450 | fprintf(stderr, "Mismatch between computed and actual length " 451 | "(%zd vs %zd)\n", *result_length, offset); 452 | #endif 453 | return FONT_COMPRESSION_FAILURE(); 454 | } 455 | return true; 456 | } 457 | 458 | } // namespace woff2 459 | -------------------------------------------------------------------------------- /src/woff2_info.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* A commandline tool for dumping info about a woff2 file. */ 8 | 9 | #include 10 | 11 | #include "file.h" 12 | #include "./woff2_common.h" 13 | #include "./buffer.h" 14 | #include "./font.h" 15 | #include "./table_tags.h" 16 | #include "./variable_length.h" 17 | 18 | std::string PrintTag(int tag) { 19 | if (tag & 0x80808080) { 20 | return std::string("_xfm"); // print _xfm for xform tables (else garbage) 21 | } 22 | char printable[] = { 23 | static_cast((tag >> 24) & 0xFF), 24 | static_cast((tag >> 16) & 0xFF), 25 | static_cast((tag >> 8) & 0xFF), 26 | static_cast(tag & 0xFF) 27 | }; 28 | return std::string(printable, 4); 29 | } 30 | 31 | int main(int argc, char **argv) { 32 | if (argc != 2) { 33 | fprintf(stderr, "One argument, the input filename, must be provided.\n"); 34 | return 1; 35 | } 36 | 37 | std::string filename(argv[1]); 38 | std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; 39 | fprintf(stdout, "Processing %s => %s\n", 40 | filename.c_str(), outfilename.c_str()); 41 | std::string input = woff2::GetFileContent(filename); 42 | 43 | woff2::Buffer file(reinterpret_cast(input.data()), 44 | input.size()); 45 | 46 | printf("WOFF2Header\n"); 47 | uint32_t signature, flavor, length, totalSfntSize, totalCompressedSize; 48 | uint32_t metaOffset, metaLength, metaOrigLength, privOffset, privLength; 49 | uint16_t num_tables, reserved, major, minor; 50 | if (!file.ReadU32(&signature)) return 1; 51 | if (!file.ReadU32(&flavor)) return 1; 52 | if (!file.ReadU32(&length)) return 1; 53 | if (!file.ReadU16(&num_tables)) return 1; 54 | if (!file.ReadU16(&reserved)) return 1; 55 | if (!file.ReadU32(&totalSfntSize)) return 1; 56 | if (!file.ReadU32(&totalCompressedSize)) return 1; 57 | if (!file.ReadU16(&major)) return 1; 58 | if (!file.ReadU16(&minor)) return 1; 59 | if (!file.ReadU32(&metaOffset)) return 1; 60 | if (!file.ReadU32(&metaLength)) return 1; 61 | if (!file.ReadU32(&metaOrigLength)) return 1; 62 | if (!file.ReadU32(&privOffset)) return 1; 63 | if (!file.ReadU32(&privLength)) return 1; 64 | 65 | if (signature != 0x774F4632) { 66 | printf("Invalid signature: %08x\n", signature); 67 | return 1; 68 | } 69 | printf("signature 0x%08x\n", signature); 70 | printf("flavor 0x%08x\n", flavor); 71 | printf("length %d\n", length); 72 | printf("numTables %d\n", num_tables); 73 | printf("reserved %d\n", reserved); 74 | printf("totalSfntSize %d\n", totalSfntSize); 75 | printf("totalCompressedSize %d\n", totalCompressedSize); 76 | printf("majorVersion %d\n", major); 77 | printf("minorVersion %d\n", minor); 78 | printf("metaOffset %d\n", metaOffset); 79 | printf("metaLength %d\n", metaLength); 80 | printf("metaOrigLength %d\n", metaOrigLength); 81 | printf("privOffset %d\n", privOffset); 82 | printf("privLength %d\n", privLength); 83 | 84 | std::vector table_tags; 85 | printf("TableDirectory starts at +%zu\n", file.offset()); 86 | printf("Entry offset flags tag origLength txLength\n"); 87 | for (auto i = 0; i < num_tables; i++) { 88 | size_t offset = file.offset(); 89 | uint8_t flags; 90 | uint32_t tag, origLength, transformLength; 91 | if (!file.ReadU8(&flags)) return 1; 92 | if ((flags & 0x3f) == 0x3f) { 93 | if (!file.ReadU32(&tag)) return 1; 94 | } else { 95 | tag = woff2::kKnownTags[flags & 0x3f]; 96 | } 97 | table_tags.push_back(tag); 98 | if (!ReadBase128(&file, &origLength)) return 1; 99 | 100 | printf("%5d %6zu 0x%02x %s %10d", i, offset, flags, 101 | PrintTag(tag).c_str(), origLength); 102 | 103 | uint8_t xform_version = (flags >> 6) & 0x3; 104 | if (tag == woff2::kGlyfTableTag || tag == woff2::kLocaTableTag) { 105 | if (xform_version == 0) { 106 | if (!ReadBase128(&file, &transformLength)) return 1; 107 | printf(" %8d", transformLength); 108 | } 109 | } else if (xform_version > 0) { 110 | if (!ReadBase128(&file, &transformLength)) return 1; 111 | printf(" %8d", transformLength); 112 | } 113 | printf("\n"); 114 | } 115 | 116 | // Collection header 117 | if (flavor == woff2::kTtcFontFlavor) { 118 | uint32_t version, numFonts; 119 | if (!file.ReadU32(&version)) return 1; 120 | if (!woff2::Read255UShort(&file, &numFonts)) return 1; 121 | printf("CollectionHeader 0x%08x %d fonts\n", version, numFonts); 122 | 123 | for (auto i = 0; i < numFonts; i++) { 124 | uint32_t numTables, flavor; 125 | if (!woff2::Read255UShort(&file, &numTables)) return 1; 126 | if (!file.ReadU32(&flavor)) return 1; 127 | printf("CollectionFontEntry %d flavor 0x%08x %d tables\n", i, flavor, 128 | numTables); 129 | for (auto j = 0; j < numTables; j++) { 130 | uint32_t table_idx; 131 | if (!woff2::Read255UShort(&file, &table_idx)) return 1; 132 | if (table_idx >= table_tags.size()) return 1; 133 | printf(" %d %s (idx %d)\n", j, 134 | PrintTag(table_tags[table_idx]).c_str(), table_idx); 135 | } 136 | } 137 | } 138 | 139 | printf("TableDirectory ends at +%zu\n", file.offset()); 140 | 141 | return 0; 142 | } 143 | -------------------------------------------------------------------------------- /src/woff2_out.cc: -------------------------------------------------------------------------------- 1 | /* Copyright 2014 Google Inc. All Rights Reserved. 2 | 3 | Distributed under MIT license. 4 | See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 | */ 6 | 7 | /* Output buffer for WOFF2 decompression. */ 8 | 9 | #include 10 | 11 | namespace woff2 { 12 | 13 | WOFF2StringOut::WOFF2StringOut(std::string *buf) 14 | : buf_(buf), max_size_(kDefaultMaxSize), offset_(0) {} 15 | 16 | bool WOFF2StringOut::Write(const void *buf, size_t n) { 17 | return Write(buf, offset_, n); 18 | } 19 | 20 | bool WOFF2StringOut::Write(const void *buf, size_t offset, size_t n) { 21 | if (offset > max_size_ || n > max_size_ - offset) { 22 | return false; 23 | } 24 | if (offset == buf_->size()) { 25 | buf_->append(static_cast(buf), n); 26 | } else { 27 | if (offset + n > buf_->size()) { 28 | buf_->append(offset + n - buf_->size(), 0); 29 | } 30 | buf_->replace(offset, n, static_cast(buf), n); 31 | } 32 | offset_ = std::max(offset_, offset + n); 33 | 34 | return true; 35 | } 36 | 37 | void WOFF2StringOut::SetMaxSize(size_t max_size) { 38 | max_size_ = max_size; 39 | if (offset_ > max_size_) { 40 | offset_ = max_size_; 41 | } 42 | } 43 | 44 | WOFF2MemoryOut::WOFF2MemoryOut(uint8_t* buf, size_t buf_size) 45 | : buf_(buf), 46 | buf_size_(buf_size), 47 | offset_(0) {} 48 | 49 | bool WOFF2MemoryOut::Write(const void *buf, size_t n) { 50 | return Write(buf, offset_, n); 51 | } 52 | 53 | bool WOFF2MemoryOut::Write(const void *buf, size_t offset, size_t n) { 54 | if (offset > buf_size_ || n > buf_size_ - offset) { 55 | return false; 56 | } 57 | std::memcpy(buf_ + offset, buf, n); 58 | offset_ = std::max(offset_, offset + n); 59 | 60 | return true; 61 | } 62 | 63 | } // namespace woff2 64 | --------------------------------------------------------------------------------