Merge tag 'v6.5.6-lts' into tqtc/lts-6.5-opensource

Qt 6.5.6-lts release

Change-Id: I643db9810dfb5cb32ace8f069ebd77d43234ec8f
This commit is contained in:
Tarja Sundqvist 2025-03-30 14:34:44 +03:00
commit c8b4df9358
293 changed files with 5289 additions and 1612 deletions

View File

@ -8,7 +8,7 @@ if (NOT DEFINED QT_SUPERBUILD OR DEFINED QT_REPO_MODULE_VERSION)
list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1") list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
endif() endif()
set(QT_REPO_MODULE_VERSION "6.5.5") set(QT_REPO_MODULE_VERSION "6.5.6")
set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1") set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
set(QT_COPYRIGHT_YEAR "2023") set(QT_COPYRIGHT_YEAR "2023")

5
LICENSES/blessing.txt Normal file
View File

@ -0,0 +1,5 @@
The author disclaims copyright to this source code. In place of a legal notice, here is a blessing:
May you do good and not evil.
May you find forgiveness for yourself and forgive others.
May you share freely, never taking more than you give.

View File

@ -1,4 +1,5 @@
@echo off @echo off
setlocal
:: The directory of this script is the expanded absolute path of the "$qt_prefix/bin" directory. :: The directory of this script is the expanded absolute path of the "$qt_prefix/bin" directory.
set script_dir_path=%~dp0 set script_dir_path=%~dp0

View File

@ -19,9 +19,9 @@ if not exist "%module_root%\CMakeLists.txt" (
set cmake_scripts_dir=%script_dir_path%\@__relative_path_to_cmake_scripts_dir@ set cmake_scripts_dir=%script_dir_path%\@__relative_path_to_cmake_scripts_dir@
echo %*>config.opt.in echo %*>config.opt.in
call "%script_dir_path%"\qt-cmake.bat -DSKIP_ARGS=1 -DIN_FILE=config.opt.in -DOUT_FILE=config.opt ^ call "%script_dir_path%\qt-cmake.bat" -DSKIP_ARGS=1 -DIN_FILE=config.opt.in -DOUT_FILE=config.opt ^
-P "%cmake_scripts_dir%\QtWriteArgsFile.cmake" -P "%cmake_scripts_dir%\QtWriteArgsFile.cmake"
call "%script_dir_path%"\qt-cmake-private.bat -DOPTFILE=config.opt -DMODULE_ROOT="%module_root%" ^ call "%script_dir_path%\qt-cmake-private.bat" -DOPTFILE=config.opt -DMODULE_ROOT="%module_root%" ^
-DCMAKE_COMMAND="%script_dir_path%\qt-cmake-private.bat" ^ -DCMAKE_COMMAND="%script_dir_path%\qt-cmake-private.bat" ^
-P "%cmake_scripts_dir%\QtProcessConfigureArgs.cmake" -P "%cmake_scripts_dir%\QtProcessConfigureArgs.cmake"
goto :eof goto :eof

View File

@ -253,7 +253,8 @@ function(qt_internal_android_dependencies target)
# Module plugins # Module plugins
if(module_plugin_types) if(module_plugin_types)
foreach(plugin IN LISTS module_plugin_types) foreach(plugin IN LISTS module_plugin_types)
string(APPEND file_contents "<bundled file=\"${INSTALL_PLUGINSDIR}/${plugin}\" />\n") string(APPEND file_contents
"<bundled file=\"${INSTALL_PLUGINSDIR}/${plugin}\" type=\"plugin_dir\"/>\n")
endforeach() endforeach()
endif() endif()

View File

@ -6,7 +6,8 @@ function(qt_internal_validate_cmake_generator)
if(NOT warning_shown if(NOT warning_shown
AND NOT CMAKE_GENERATOR MATCHES "Ninja" AND NOT CMAKE_GENERATOR MATCHES "Ninja"
AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING) AND NOT QT_SILENCE_CMAKE_GENERATOR_WARNING
AND NOT DEFINED ENV{QT_SILENCE_CMAKE_GENERATOR_WARNING})
set_property(GLOBAL PROPERTY _qt_validate_cmake_generator_warning_shown TRUE) set_property(GLOBAL PROPERTY _qt_validate_cmake_generator_warning_shown TRUE)
message(WARNING message(WARNING
"The officially supported CMake generator for building Qt is " "The officially supported CMake generator for building Qt is "

View File

@ -63,6 +63,8 @@ if(NOT DEFINED QT_CMAKE_EXPORT_NAMESPACE)
set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@) set(QT_CMAKE_EXPORT_NAMESPACE @QT_CMAKE_EXPORT_NAMESPACE@)
endif() endif()
__qt_internal_collect_additional_module_paths()
# Propagate sanitizer flags to both internal Qt builds and user projects. # Propagate sanitizer flags to both internal Qt builds and user projects.
# Allow opt-out in case if downstream projects handle it in a different way. # Allow opt-out in case if downstream projects handle it in a different way.
set(QT_CONFIGURED_SANITIZER_OPTIONS "@ECM_ENABLE_SANITIZERS@") set(QT_CONFIGURED_SANITIZER_OPTIONS "@ECM_ENABLE_SANITIZERS@")

View File

@ -922,6 +922,11 @@ macro(qt_internal_compute_features_from_possible_inputs)
qt_internal_compute_feature_value_from_possible_input(no_prefix) qt_internal_compute_feature_value_from_possible_input(no_prefix)
endmacro() endmacro()
# Builds either a string of source code or a whole project to determine whether the build is
# successful.
#
# Sets a TEST_${name}_OUTPUT variable with the build output, to the scope of the calling function.
# Sets a TEST_${name} cache variable to either TRUE or FALSE if the build is successful or not.
function(qt_config_compile_test name) function(qt_config_compile_test name)
if(DEFINED "TEST_${name}") if(DEFINED "TEST_${name}")
return() return()
@ -1044,8 +1049,11 @@ function(qt_config_compile_test name)
get_filename_component(arg_PROJECT_PATH "${arg_PROJECT_PATH}" REALPATH) get_filename_component(arg_PROJECT_PATH "${arg_PROJECT_PATH}" REALPATH)
endif() endif()
try_compile(HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" try_compile(
"${name}" CMAKE_FLAGS ${flags} ${arg_CMAKE_FLAGS}) HAVE_${name} "${CMAKE_BINARY_DIR}/config.tests/${name}" "${arg_PROJECT_PATH}" "${name}"
CMAKE_FLAGS ${flags} ${arg_CMAKE_FLAGS}
OUTPUT_VARIABLE try_compile_output
)
if(${HAVE_${name}}) if(${HAVE_${name}})
set(status_label "Success") set(status_label "Success")
@ -1110,7 +1118,19 @@ function(qt_config_compile_test name)
set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}") set(_save_CMAKE_REQUIRED_LIBRARIES "${CMAKE_REQUIRED_LIBRARIES}")
set(CMAKE_REQUIRED_LIBRARIES "${arg_LIBRARIES}") set(CMAKE_REQUIRED_LIBRARIES "${arg_LIBRARIES}")
check_cxx_source_compiles("${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name})
# OUTPUT_VARIABLE is an internal undocumented variable of check_cxx_source_compiles
# since 3.23. Allow an opt out in case this breaks in the future.
set(try_compile_output "")
set(output_var "")
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.23"
AND NOT QT_INTERNAL_NO_TRY_COMPILE_OUTPUT_VARIABLE)
set(output_var OUTPUT_VARIABLE try_compile_output)
endif()
check_cxx_source_compiles(
"${arg_UNPARSED_ARGUMENTS} ${arg_CODE}" HAVE_${name} ${output_var}
)
set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}") set(CMAKE_REQUIRED_LIBRARIES "${_save_CMAKE_REQUIRED_LIBRARIES}")
set(CMAKE_C_STANDARD "${_save_CMAKE_C_STANDARD}") set(CMAKE_C_STANDARD "${_save_CMAKE_C_STANDARD}")
@ -1122,6 +1142,7 @@ function(qt_config_compile_test name)
endif() endif()
endif() endif()
set(TEST_${name}_OUTPUT "${try_compile_output}" PARENT_SCOPE)
set(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}") set(TEST_${name} "${HAVE_${name}}" CACHE INTERNAL "${arg_LABEL}")
endfunction() endfunction()

View File

@ -1,6 +1,23 @@
# Copyright (C) 2022 The Qt Company Ltd. # Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause # SPDX-License-Identifier: BSD-3-Clause
# Sets '${var}' to a genex that extracts the target's property.
# Sets 'have_${var}' to a genex that checks that the property has a
# non-empty value.
macro(qt_internal_genex_get_property var target property)
set(${var} "$<TARGET_PROPERTY:${target},${property}>")
set(have_${var} "$<BOOL:${${var}}>")
endmacro()
# Sets '${var}' to a genex that will join the given property values
# using '${glue}' and will surround the entire output with '${prefix}'
# and '${suffix}'.
macro(qt_internal_genex_get_joined_property var target property prefix suffix glue)
qt_internal_genex_get_property("${var}" "${target}" "${property}")
set(${var}
"$<${have_${var}}:${prefix}$<JOIN:${${var}},${glue}>${suffix}>")
endmacro()
# This function generates LD version script for the target and uses it in the target linker line. # This function generates LD version script for the target and uses it in the target linker line.
# Function has two modes dependending on the specified arguments. # Function has two modes dependending on the specified arguments.
# Arguments: # Arguments:
@ -33,9 +50,21 @@ function(qt_internal_add_linker_version_script target)
endif() endif()
string(APPEND contents "};\n") string(APPEND contents "};\n")
set(current "Qt_${PROJECT_VERSION_MAJOR}") set(current "Qt_${PROJECT_VERSION_MAJOR}")
string(APPEND contents "${current} { *; };\n") string(APPEND contents "${current} {\n *;")
get_target_property(target_type ${target} TYPE)
if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
set(genex_prefix "\n ")
set(genex_glue "$<SEMICOLON>\n ")
set(genex_suffix "$<SEMICOLON>")
qt_internal_genex_get_joined_property(
linker_exports "${target}" _qt_extra_linker_script_exports
"${genex_prefix}" "${genex_suffix}" "${genex_glue}"
)
string(APPEND contents "${linker_exports}")
endif()
string(APPEND contents "\n};\n")
get_target_property(type ${target} TYPE)
if(NOT target_type STREQUAL "INTERFACE_LIBRARY") if(NOT target_type STREQUAL "INTERFACE_LIBRARY")
set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>") set(property_genex "$<TARGET_PROPERTY:${target},_qt_extra_linker_script_content>")
set(check_genex "$<BOOL:${property_genex}>") set(check_genex "$<BOOL:${property_genex}>")
@ -336,7 +365,7 @@ function(qt_internal_enable_unicode_defines)
set(no_unicode_condition set(no_unicode_condition
"$<NOT:$<BOOL:$<TARGET_PROPERTY:QT_NO_UNICODE_DEFINES>>>") "$<NOT:$<BOOL:$<TARGET_PROPERTY:QT_NO_UNICODE_DEFINES>>>")
target_compile_definitions(Platform target_compile_definitions(Platform
INTERFACE "$<${no_unicode_condition}:UNICODE;_UNICODE>") INTERFACE "$<${no_unicode_condition}:UNICODE$<SEMICOLON>_UNICODE>")
endif() endif()
endfunction() endfunction()

View File

@ -25,10 +25,13 @@ endfunction()
# The function checks if add_custom_command has the support of the DEPFILE argument. # The function checks if add_custom_command has the support of the DEPFILE argument.
function(_qt_internal_check_depfile_support out_var) function(_qt_internal_check_depfile_support out_var)
if(CMAKE_GENERATOR MATCHES "Ninja" OR if(CMAKE_GENERATOR MATCHES "Ninja" OR
CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles" (CMAKE_VERSION VERSION_GREATER_EQUAL 3.20 AND CMAKE_GENERATOR MATCHES "Makefiles")
OR CMAKE_VERSION VERSION_GREATER_EQUAL 3.21 OR (CMAKE_VERSION VERSION_GREATER_EQUAL 3.21
AND (CMAKE_GENERATOR MATCHES "Xcode" AND (CMAKE_GENERATOR MATCHES "Xcode"
OR CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)" AND CMAKE_MATCH_1 GREATER_EQUAL 12)) OR (CMAKE_GENERATOR MATCHES "Visual Studio ([0-9]+)" AND CMAKE_MATCH_1 GREATER_EQUAL 12)
)
)
)
set(${out_var} TRUE) set(${out_var} TRUE)
else() else()
set(${out_var} FALSE) set(${out_var} FALSE)
@ -78,6 +81,22 @@ function(__qt_internal_collect_additional_prefix_paths out_var prefixes_var)
set("${out_var}" "${additional_packages_prefix_paths}" PARENT_SCOPE) set("${out_var}" "${additional_packages_prefix_paths}" PARENT_SCOPE)
endfunction() endfunction()
# Collects CMAKE_MODULE_PATH from QT_ADDITIONAL_PACKAGES_PREFIX_PATH
function(__qt_internal_collect_additional_module_paths)
if(__qt_additional_module_paths_set)
return()
endif()
foreach(prefix_path IN LISTS QT_ADDITIONAL_PACKAGES_PREFIX_PATH)
list(APPEND CMAKE_MODULE_PATH "${prefix_path}/${QT_CMAKE_EXPORT_NAMESPACE}")
# TODO: Need to consider the INSTALL_LIBDIR value when collecting CMAKE_MODULE_PATH.
# See QTBUG-123039.
list(APPEND CMAKE_MODULE_PATH "${prefix_path}/lib/cmake/${QT_CMAKE_EXPORT_NAMESPACE}")
endforeach()
list(REMOVE_DUPLICATES CMAKE_MODULE_PATH)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" PARENT_SCOPE)
set(__qt_additional_module_paths_set TRUE PARENT_SCOPE)
endfunction()
# Take a list of prefix paths ending with "/lib/cmake", and return a list of absolute paths with # Take a list of prefix paths ending with "/lib/cmake", and return a list of absolute paths with
# "/lib/cmake" removed. # "/lib/cmake" removed.
function(__qt_internal_prefix_paths_to_roots out_var prefix_paths) function(__qt_internal_prefix_paths_to_roots out_var prefix_paths)

View File

@ -187,7 +187,7 @@ function(__qt_internal_get_plugin_import_macro plugin_target out_var)
set(class_name "${class_name_prefixed}") set(class_name "${class_name_prefixed}")
endif() endif()
set(${out_var} "Q_IMPORT_PLUGIN(${class_name})" PARENT_SCOPE) set(${out_var} "Q_IMPORT_PLUGIN(${class_name})\n" PARENT_SCOPE)
endfunction() endfunction()
function(__qt_internal_get_plugin_include_prelude out_var) function(__qt_internal_get_plugin_include_prelude out_var)

View File

@ -16,6 +16,8 @@
# Custom compilation flags. # Custom compilation flags.
# EXTRA_LINKER_SCRIPT_CONTENT # EXTRA_LINKER_SCRIPT_CONTENT
# Extra content that should be appended to a target linker script. Applicable for ld only. # Extra content that should be appended to a target linker script. Applicable for ld only.
# EXTRA_LINKER_SCRIPT_EXPORTS
# Extra content that should be added to export section of the linker script.
# NO_PCH_SOURCES # NO_PCH_SOURCES
# Skip the specified source files by PRECOMPILE_HEADERS feature. # Skip the specified source files by PRECOMPILE_HEADERS feature.
function(qt_internal_extend_target target) function(qt_internal_extend_target target)
@ -47,6 +49,7 @@ function(qt_internal_extend_target target)
CONDITION CONDITION
CONDITION_INDEPENDENT_SOURCES CONDITION_INDEPENDENT_SOURCES
COMPILE_FLAGS COMPILE_FLAGS
EXTRA_LINKER_SCRIPT_EXPORTS
) )
cmake_parse_arguments(PARSE_ARGV 1 arg cmake_parse_arguments(PARSE_ARGV 1 arg
@ -243,6 +246,10 @@ function(qt_internal_extend_target target)
set_target_properties(${target} PROPERTIES set_target_properties(${target} PROPERTIES
_qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}") _qt_extra_linker_script_content "${arg_EXTRA_LINKER_SCRIPT_CONTENT}")
endif() endif()
if(arg_EXTRA_LINKER_SCRIPT_EXPORTS)
set_target_properties(${target} PROPERTIES
_qt_extra_linker_script_exports "${arg_EXTRA_LINKER_SCRIPT_EXPORTS}")
endif()
endfunction() endfunction()
# Given CMAKE_CONFIG and ALL_CMAKE_CONFIGS, determines if a directory suffix needs to be appended # Given CMAKE_CONFIG and ALL_CMAKE_CONFIGS, determines if a directory suffix needs to be appended

View File

@ -8,6 +8,7 @@ The following table describes the mapping of configure options to CMake argument
| -extprefix /opt/qt6 | -DCMAKE_STAGING_PREFIX=/opt/qt6 | | | -extprefix /opt/qt6 | -DCMAKE_STAGING_PREFIX=/opt/qt6 | |
| -bindir <dir> | -DINSTALL_BINDIR=<dir> | similar for -headerdir -libdir and so on | | -bindir <dir> | -DINSTALL_BINDIR=<dir> | similar for -headerdir -libdir and so on |
| -hostdatadir <dir> | -DINSTALL_MKSPECSDIR=<dir> | | | -hostdatadir <dir> | -DINSTALL_MKSPECSDIR=<dir> | |
| -qt-host-path <dir> | -DQT_HOST_PATH=<dir> | |
| -help | n/a | Handled by configure[.bat]. | | -help | n/a | Handled by configure[.bat]. |
| -verbose | --log-level=STATUS | Sets the CMake log level to STATUS. The default one is NOTICE. | | -verbose | --log-level=STATUS | Sets the CMake log level to STATUS. The default one is NOTICE. |
| -continue | | | | -continue | | |
@ -168,8 +169,8 @@ The following table describes the mapping of configure options to CMake argument
| -xkbcommon | -DFEATURE_xkbcommon=ON | | | -xkbcommon | -DFEATURE_xkbcommon=ON | |
| -gif | -DFEATURE_gif=ON | | | -gif | -DFEATURE_gif=ON | |
| -ico | -DFEATURE_ico=ON | | | -ico | -DFEATURE_ico=ON | |
| -libpng | -DFEATURE_png=ON | | | -libpng | -DFEATURE_png=ON | |
| -libjpeg | -DFEATURE_jpeg=ON | | | -libjpeg | -DFEATURE_jpeg=ON | |
| -sql-<driver> | -DFEATURE_sql_<driver>=ON | | | -sql-<driver> | -DFEATURE_sql_<driver>=ON | |
| -sqlite [qt/system] | -DFEATURE_system_sqlite=OFF/ON | | | -sqlite [qt/system] | -DFEATURE_system_sqlite=OFF/ON | |
| -disable-deprecated-up-to <hex_version> | -DQT_DISABLE_DEPRECATED_UP_TO=<hex_version> | | | -disable-deprecated-up-to <hex_version> | -DQT_DISABLE_DEPRECATED_UP_TO=<hex_version> | |

View File

@ -52,19 +52,22 @@ instructions:
property: host.os property: host.os
equals_value: Windows equals_value: Windows
# Keep the testrun quiet for ASAN testruns, since there are FAILs happening all over the place...
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: CTEST_ARGS variableName: CTEST_ARGS
variableValue: "-V" variableValue: "-V"
# Keep the testrun quiet for ASAN testruns, since there are FAILs happening all over the place
disable_if: disable_if:
condition: property condition: property
property: features property: features
contains_value: UseAddressSanitizer contains_value: UseAddressSanitizer
# ...and only print the output from a failing test, i.e. test with ASAN errors.
# Always print the output from a failing test, even when ctest is not in verbose mode
- type: EnvironmentVariable - type: EnvironmentVariable
variableName: CTEST_OUTPUT_ON_FAILURE variableName: CTEST_OUTPUT_ON_FAILURE
variableValue: "1" variableValue: "1"
enable_if:
condition: property
property: features
contains_value: UseAddressSanitizer
- type: AppendToEnvironmentVariable - type: AppendToEnvironmentVariable
variableName: CTEST_ARGS variableName: CTEST_ARGS

View File

@ -83,7 +83,12 @@ set REDO_FILE_PATH=%TOPQTDIR%\config.redo.last
set REDO_TMP_FILE_PATH=%TOPQTDIR%\config.redo.in set REDO_TMP_FILE_PATH=%TOPQTDIR%\config.redo.in
set FRESH_REQUESTED_ARG= set FRESH_REQUESTED_ARG=
if not defined redoing ( if not defined redoing (
echo.%*>"%OPT_TMP_FILE_PATH%" :: The '.' in 'echo.%*' ensures we don't print "echo is off" when no arguments are passed
:: https://devblogs.microsoft.com/oldnewthing/20170802-00/?p=96735
:: The space before the '>' makes sure that when we have a digit at the end of the args, we
:: don't accidentally concatenate it with the '>' resulting in '0>' or '2>' which redirects
:: into the file from a stream different than stdout, leading to broken or empty content.
echo.%* >"%OPT_TMP_FILE_PATH%"
cmake -DIN_FILE="%OPT_TMP_FILE_PATH%" -DOUT_FILE="%OPT_FILE_PATH%" -DIGNORE_ARGS=-top-level -P "%QTSRC%\cmake\QtWriteArgsFile.cmake" cmake -DIN_FILE="%OPT_TMP_FILE_PATH%" -DOUT_FILE="%OPT_FILE_PATH%" -DIGNORE_ARGS=-top-level -P "%QTSRC%\cmake\QtWriteArgsFile.cmake"
) else ( ) else (

View File

@ -1315,16 +1315,17 @@ the result of the build in config.tests/x86intrin and report at https://bugrepor
]=] ]=]
) )
else() else()
string(CONCAT error_message
"x86 intrinsics support missing. Check your compiler settings.\n"
"If this is an error, report at https://bugreports.qt.io with your compiler ID and "
"version, and this output:\n"
"\n"
"${TEST_x86intrin_OUTPUT}"
)
qt_configure_add_report_entry( qt_configure_add_report_entry(
TYPE ERROR TYPE ERROR
CONDITION (NOT QT_FEATURE_x86intrin) CONDITION (NOT QT_FEATURE_x86intrin)
MESSAGE [========[ MESSAGE "${error_message}"
x86 intrinsics support missing. Check your compiler settings. If this is an
error, report at https://bugreports.qt.io with your compiler ID and version,
and this output:
${TEST_x86intrin_OUTPUT}
]========]
) )
endif() endif()
endif() endif()

View File

@ -1,5 +1,6 @@
QT += core gui QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
QT += core-private # For Notification permission request
SOURCES += \ SOURCES += \
main.cpp \ main.cpp \

View File

@ -9,7 +9,7 @@
\brief Demonstrates communication among instances of an application. \brief Demonstrates communication among instances of an application.
\e Chat is a \l{Qt D-Bus} example which demonstrates a simple chat system \e Chat is a \l{Qt D-Bus} example which demonstrates a simple chat system
among instances of an application. Users connect and send message to among instances of an application. Users connect and send messages to
each other. each other.
\image dbus-chat-example.webp \image dbus-chat-example.webp

View File

@ -34,7 +34,10 @@ void AddressWidget::showAddEntryDialog()
//! [3] //! [3]
void AddressWidget::addEntry(const QString &name, const QString &address) void AddressWidget::addEntry(const QString &name, const QString &address)
{ {
if (!table->getContacts().contains({ name, address })) { if (!name.front().isLetter()) {
QMessageBox::information(this, tr("Invalid name"),
tr("The name must start with a letter."));
} else if (!table->getContacts().contains({ name, address })) {
table->insertRows(0, 1, QModelIndex()); table->insertRows(0, 1, QModelIndex());
QModelIndex index = table->index(0, 0, QModelIndex()); QModelIndex index = table->index(0, 0, QModelIndex());

View File

@ -26,7 +26,7 @@ load(emcc_ver)
} }
# Declare async functions # Declare async functions
QMAKE_LFLAGS += -s \'ASYNCIFY_IMPORTS=qt_asyncify_suspend_js,qt_asyncify_resume_js\' QMAKE_LFLAGS += -s ASYNCIFY_IMPORTS=qt_asyncify_suspend_js,qt_asyncify_resume_js
EMCC_COMMON_LFLAGS += \ EMCC_COMMON_LFLAGS += \
-s WASM=1 \ -s WASM=1 \

View File

@ -188,7 +188,7 @@ Option::parseCommandLine(QStringList &args, QMakeCmdLineParserState &state)
return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR; return Option::QMAKE_CMDLINE_SHOW_USAGE | Option::QMAKE_CMDLINE_ERROR;
} }
if (!globals->qtconf.isEmpty()) if (!globals->qtconf.isEmpty())
QLibraryInfoPrivate::qtconfManualPath = &globals->qtconf; QLibraryInfoPrivate::setQtconfManualPath(&globals->qtconf);
if (cmdRet == QMakeGlobals::ArgumentsOk) if (cmdRet == QMakeGlobals::ArgumentsOk)
break; break;
Q_ASSERT(cmdRet == QMakeGlobals::ArgumentUnknown); Q_ASSERT(cmdRet == QMakeGlobals::ArgumentUnknown);

View File

@ -56,6 +56,7 @@ qt_internal_add_3rdparty_library(BundledHarfbuzz
src/hb-subset-cff1.cc src/hb-subset-cff1.cc
src/hb-subset-cff2.cc src/hb-subset-cff2.cc
src/hb-subset-input.cc src/hb-subset-input.cc
src/hb-subset-instancer-iup.cc src/hb-subset-instancer-iup.hh
src/hb-subset-instancer-solver.cc src/hb-subset-instancer-solver.cc
src/hb-subset-plan.cc src/hb-subset-plan.cc
src/hb-subset-plan-member-list.hh src/hb-subset-plan-member-list.hh

View File

@ -2,7 +2,7 @@
[![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main) [![CircleCI Build Status](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main.svg?style=svg)](https://circleci.com/gh/harfbuzz/harfbuzz/tree/main)
[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html) [![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz) [![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://www.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=harfbuzz/harfbuzz&amp;utm_campaign=Badge_Grade) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://app.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz) [![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) [![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)
@ -72,9 +72,9 @@ For a comparison of old vs new HarfBuzz memory consumption see [this][10].
## Name ## Name
HarfBuzz (حرف‌باز) is my Persian translation of “[OpenType][1]”, HarfBuzz (حرف‌باز) is the literal Persian translation of “[OpenType][1]”,
transliterated using the Latin script. It sports a second meaning, but that transliterated using the Latin script. It also means "talkative" or
aint translatable. "glib" (also a nod to the GNOME project where HarfBuzz originates from).
> Background: Originally there was this font format called TrueType. People and > Background: Originally there was this font format called TrueType. People and
> companies started calling their type engines all things ending in Type: > companies started calling their type engines all things ending in Type:

View File

@ -7,8 +7,8 @@
"Description": "HarfBuzz is an OpenType text shaping engine.", "Description": "HarfBuzz is an OpenType text shaping engine.",
"Homepage": "http://harfbuzz.org", "Homepage": "http://harfbuzz.org",
"Version": "8.3.0", "Version": "8.4.0",
"DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/8.3.0", "DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/8.4.0",
"License": "MIT License", "License": "MIT License",
"LicenseId": "MIT", "LicenseId": "MIT",

View File

@ -68,7 +68,7 @@ public:
hb_font_t *font; hb_font_t *font;
unsigned int palette_index; unsigned int palette_index;
hb_color_t foreground; hb_color_t foreground;
VarStoreInstancer &instancer; ItemVarStoreInstancer &instancer;
hb_map_t current_glyphs; hb_map_t current_glyphs;
hb_map_t current_layers; hb_map_t current_layers;
int depth_left = HB_MAX_NESTING_LEVEL; int depth_left = HB_MAX_NESTING_LEVEL;
@ -80,7 +80,7 @@ public:
hb_font_t *font_, hb_font_t *font_,
unsigned int palette_, unsigned int palette_,
hb_color_t foreground_, hb_color_t foreground_,
VarStoreInstancer &instancer_) : ItemVarStoreInstancer &instancer_) :
base (base_), base (base_),
funcs (funcs_), funcs (funcs_),
data (data_), data (data_),
@ -245,7 +245,7 @@ struct Variable
{ value.closurev1 (c); } { value.closurev1 (c); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
if (!value.subset (c, instancer, varIdxBase)) return_trace (false); if (!value.subset (c, instancer, varIdxBase)) return_trace (false);
@ -270,7 +270,7 @@ struct Variable
void get_color_stop (hb_paint_context_t *c, void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *stop, hb_color_stop_t *stop,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
value.get_color_stop (c, stop, varIdxBase, instancer); value.get_color_stop (c, stop, varIdxBase, instancer);
} }
@ -305,7 +305,7 @@ struct NoVariable
{ value.closurev1 (c); } { value.closurev1 (c); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
return_trace (value.subset (c, instancer, varIdxBase)); return_trace (value.subset (c, instancer, varIdxBase));
@ -325,7 +325,7 @@ struct NoVariable
void get_color_stop (hb_paint_context_t *c, void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *stop, hb_color_stop_t *stop,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer); value.get_color_stop (c, stop, VarIdx::NO_VARIATION, instancer);
} }
@ -348,7 +348,7 @@ struct ColorStop
{ c->add_palette_index (paletteIndex); } { c->add_palette_index (paletteIndex); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -374,7 +374,7 @@ struct ColorStop
void get_color_stop (hb_paint_context_t *c, void get_color_stop (hb_paint_context_t *c,
hb_color_stop_t *out, hb_color_stop_t *out,
uint32_t varIdx, uint32_t varIdx,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
out->offset = stopOffset.to_float(instancer (varIdx, 0)); out->offset = stopOffset.to_float(instancer (varIdx, 0));
out->color = c->get_color (paletteIndex, out->color = c->get_color (paletteIndex,
@ -410,7 +410,7 @@ struct ColorLine
} }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -439,7 +439,7 @@ struct ColorLine
unsigned int start, unsigned int start,
unsigned int *count, unsigned int *count,
hb_color_stop_t *color_stops, hb_color_stop_t *color_stops,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
unsigned int len = stops.len; unsigned int len = stops.len;
@ -543,7 +543,7 @@ struct Affine2x3
} }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -588,7 +588,7 @@ struct PaintColrLayers
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const const ItemVarStoreInstancer &instancer HB_UNUSED) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -620,7 +620,7 @@ struct PaintSolid
{ c->add_palette_index (paletteIndex); } { c->add_palette_index (paletteIndex); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -669,7 +669,7 @@ struct PaintLinearGradient
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -736,7 +736,7 @@ struct PaintRadialGradient
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -803,7 +803,7 @@ struct PaintSweepGradient
{ (this+colorLine).closurev1 (c); } { (this+colorLine).closurev1 (c); }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -863,7 +863,7 @@ struct PaintGlyph
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -906,7 +906,7 @@ struct PaintColrGlyph
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer HB_UNUSED) const const ItemVarStoreInstancer &instancer HB_UNUSED) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -936,7 +936,7 @@ struct PaintTransform
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -975,7 +975,7 @@ struct PaintTranslate
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1024,7 +1024,7 @@ struct PaintScale
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1073,7 +1073,7 @@ struct PaintScaleAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1132,7 +1132,7 @@ struct PaintScaleUniform
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1176,7 +1176,7 @@ struct PaintScaleUniformAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1232,7 +1232,7 @@ struct PaintRotate
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1276,7 +1276,7 @@ struct PaintRotateAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1332,7 +1332,7 @@ struct PaintSkew
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1381,7 +1381,7 @@ struct PaintSkewAroundCenter
HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const; HB_INTERNAL void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1440,7 +1440,7 @@ struct PaintComposite
void closurev1 (hb_colrv1_closure_context_t* c) const; void closurev1 (hb_colrv1_closure_context_t* c) const;
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (this); auto *out = c->serializer->embed (this);
@ -1491,7 +1491,7 @@ struct ClipBoxFormat1
return_trace (c->check_struct (this)); return_trace (c->check_struct (this));
} }
void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer HB_UNUSED) const void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer HB_UNUSED) const
{ {
clip_box.xMin = xMin; clip_box.xMin = xMin;
clip_box.yMin = yMin; clip_box.yMin = yMin;
@ -1500,7 +1500,7 @@ struct ClipBoxFormat1
} }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
uint32_t varIdxBase) const uint32_t varIdxBase) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
@ -1533,7 +1533,7 @@ struct ClipBoxFormat1
struct ClipBoxFormat2 : Variable<ClipBoxFormat1> struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
{ {
void get_clip_box (ClipBoxData &clip_box, const VarStoreInstancer &instancer) const void get_clip_box (ClipBoxData &clip_box, const ItemVarStoreInstancer &instancer) const
{ {
value.get_clip_box(clip_box, instancer); value.get_clip_box(clip_box, instancer);
if (instancer) if (instancer)
@ -1549,7 +1549,7 @@ struct ClipBoxFormat2 : Variable<ClipBoxFormat1>
struct ClipBox struct ClipBox
{ {
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
switch (u.format) { switch (u.format) {
@ -1572,7 +1572,7 @@ struct ClipBox
} }
bool get_extents (hb_glyph_extents_t *extents, bool get_extents (hb_glyph_extents_t *extents,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
ClipBoxData clip_box; ClipBoxData clip_box;
switch (u.format) { switch (u.format) {
@ -1608,7 +1608,7 @@ struct ClipRecord
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const void *base, const void *base,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this); auto *out = c->serializer->embed (*this);
@ -1625,7 +1625,7 @@ struct ClipRecord
bool get_extents (hb_glyph_extents_t *extents, bool get_extents (hb_glyph_extents_t *extents,
const void *base, const void *base,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
return (base+clipBox).get_extents (extents, instancer); return (base+clipBox).get_extents (extents, instancer);
} }
@ -1642,7 +1642,7 @@ DECLARE_NULL_NAMESPACE_BYTES (OT, ClipRecord);
struct ClipList struct ClipList
{ {
unsigned serialize_clip_records (hb_subset_context_t *c, unsigned serialize_clip_records (hb_subset_context_t *c,
const VarStoreInstancer &instancer, const ItemVarStoreInstancer &instancer,
const hb_set_t& gids, const hb_set_t& gids,
const hb_map_t& gid_offset_map) const const hb_map_t& gid_offset_map) const
{ {
@ -1695,7 +1695,7 @@ struct ClipList
} }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this); auto *out = c->serializer->start_embed (*this);
@ -1735,7 +1735,7 @@ struct ClipList
bool bool
get_extents (hb_codepoint_t gid, get_extents (hb_codepoint_t gid,
hb_glyph_extents_t *extents, hb_glyph_extents_t *extents,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
auto *rec = clips.as_array ().bsearch (gid); auto *rec = clips.as_array ().bsearch (gid);
if (rec) if (rec)
@ -1855,7 +1855,7 @@ struct BaseGlyphPaintRecord
bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map, bool serialize (hb_serialize_context_t *s, const hb_map_t* glyph_map,
const void* src_base, hb_subset_context_t *c, const void* src_base, hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
auto *out = s->embed (this); auto *out = s->embed (this);
@ -1884,7 +1884,7 @@ struct BaseGlyphPaintRecord
struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord> struct BaseGlyphList : SortedArray32Of<BaseGlyphPaintRecord>
{ {
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -1916,7 +1916,7 @@ struct LayerList : Array32OfOffset32To<Paint>
{ return this+(*this)[i]; } { return this+(*this)[i]; }
bool subset (hb_subset_context_t *c, bool subset (hb_subset_context_t *c,
const VarStoreInstancer &instancer) const const ItemVarStoreInstancer &instancer) const
{ {
TRACE_SUBSET (this); TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (this); auto *out = c->serializer->start_embed (this);
@ -2206,7 +2206,7 @@ struct COLR
auto snap = c->serializer->snapshot (); auto snap = c->serializer->snapshot ();
if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false); if (!c->serializer->allocate_size<void> (5 * HBUINT32::static_size)) return_trace (false);
VarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr, ItemVarStoreInstancer instancer (varStore ? &(this+varStore) : nullptr,
varIdxMap ? &(this+varIdxMap) : nullptr, varIdxMap ? &(this+varIdxMap) : nullptr,
c->plan->normalized_coords.as_array ()); c->plan->normalized_coords.as_array ());
@ -2250,7 +2250,7 @@ struct COLR
if (version != 1) if (version != 1)
return false; return false;
VarStoreInstancer instancer (&(this+varStore), ItemVarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap), &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
@ -2301,7 +2301,7 @@ struct COLR
bool get_clip (hb_codepoint_t glyph, bool get_clip (hb_codepoint_t glyph,
hb_glyph_extents_t *extents, hb_glyph_extents_t *extents,
const VarStoreInstancer instancer) const const ItemVarStoreInstancer instancer) const
{ {
return (this+clipList).get_extents (glyph, return (this+clipList).get_extents (glyph,
extents, extents,
@ -2312,7 +2312,7 @@ struct COLR
bool bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
{ {
VarStoreInstancer instancer (&(this+varStore), ItemVarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap), &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
@ -2327,7 +2327,7 @@ struct COLR
{ {
// COLRv1 glyph // COLRv1 glyph
VarStoreInstancer instancer (&(this+varStore), ItemVarStoreInstancer instancer (&(this+varStore),
&(this+varIdxMap), &(this+varIdxMap),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
@ -2413,7 +2413,7 @@ struct COLR
Offset32To<LayerList> layerList; Offset32To<LayerList> layerList;
Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL) Offset32To<ClipList> clipList; // Offset to ClipList table (may be NULL)
Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL) Offset32To<DeltaSetIndexMap> varIdxMap; // Offset to DeltaSetIndexMap table (may be NULL)
Offset32To<VariationStore> varStore; Offset32To<ItemVariationStore> varStore;
public: public:
DEFINE_SIZE_MIN (14); DEFINE_SIZE_MIN (14);
}; };

View File

@ -189,7 +189,7 @@ struct CaretValueFormat3
friend struct CaretValue; friend struct CaretValue;
hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction, hb_position_t get_caret_value (hb_font_t *font, hb_direction_t direction,
const VariationStore &var_store) const const ItemVariationStore &var_store) const
{ {
return HB_DIRECTION_IS_HORIZONTAL (direction) ? return HB_DIRECTION_IS_HORIZONTAL (direction) ?
font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) : font->em_scale_x (coordinate) + (this+deviceTable).get_x_delta (font, var_store) :
@ -251,7 +251,7 @@ struct CaretValue
hb_position_t get_caret_value (hb_font_t *font, hb_position_t get_caret_value (hb_font_t *font,
hb_direction_t direction, hb_direction_t direction,
hb_codepoint_t glyph_id, hb_codepoint_t glyph_id,
const VariationStore &var_store) const const ItemVariationStore &var_store) const
{ {
switch (u.format) { switch (u.format) {
case 1: return u.format1.get_caret_value (font, direction); case 1: return u.format1.get_caret_value (font, direction);
@ -316,7 +316,7 @@ struct LigGlyph
unsigned get_lig_carets (hb_font_t *font, unsigned get_lig_carets (hb_font_t *font,
hb_direction_t direction, hb_direction_t direction,
hb_codepoint_t glyph_id, hb_codepoint_t glyph_id,
const VariationStore &var_store, const ItemVariationStore &var_store,
unsigned start_offset, unsigned start_offset,
unsigned *caret_count /* IN/OUT */, unsigned *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const hb_position_t *caret_array /* OUT */) const
@ -372,7 +372,7 @@ struct LigCaretList
unsigned int get_lig_carets (hb_font_t *font, unsigned int get_lig_carets (hb_font_t *font,
hb_direction_t direction, hb_direction_t direction,
hb_codepoint_t glyph_id, hb_codepoint_t glyph_id,
const VariationStore &var_store, const ItemVariationStore &var_store,
unsigned int start_offset, unsigned int start_offset,
unsigned int *caret_count /* IN/OUT */, unsigned int *caret_count /* IN/OUT */,
hb_position_t *caret_array /* OUT */) const hb_position_t *caret_array /* OUT */) const
@ -609,7 +609,7 @@ struct GDEFVersion1_2
* definitions--from beginning of GDEF * definitions--from beginning of GDEF
* header (may be NULL). Introduced * header (may be NULL). Introduced
* in version 0x00010002. */ * in version 0x00010002. */
Offset32To<VariationStore> Offset32To<ItemVariationStore>
varStore; /* Offset to the table of Item Variation varStore; /* Offset to the table of Item Variation
* Store--from beginning of GDEF * Store--from beginning of GDEF
* header (may be NULL). Introduced * header (may be NULL). Introduced
@ -663,21 +663,16 @@ struct GDEFVersion1_2
auto *out = c->serializer->start_embed (*this); auto *out = c->serializer->start_embed (*this);
if (unlikely (!c->serializer->extend_min (out))) return_trace (false); if (unlikely (!c->serializer->extend_min (out))) return_trace (false);
out->version.major = version.major; // Push var store first (if it's needed) so that it's last in the
out->version.minor = version.minor; // serialization order. Some font consumers assume that varstore runs to
bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true); // the end of the GDEF table.
bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this); // See: https://github.com/harfbuzz/harfbuzz/issues/4636
bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_markglyphsetsdef = false;
auto snapshot_version0 = c->serializer->snapshot (); auto snapshot_version0 = c->serializer->snapshot ();
if (version.to_int () >= 0x00010002u) if (unlikely (version.to_int () >= 0x00010002u && !c->serializer->embed (markGlyphSetsDef)))
{ return_trace (false);
if (unlikely (!c->serializer->embed (markGlyphSetsDef))) return_trace (false);
subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
}
bool subset_varstore = false; bool subset_varstore = false;
unsigned varstore_index = (unsigned) -1;
auto snapshot_version2 = c->serializer->snapshot (); auto snapshot_version2 = c->serializer->snapshot ();
if (version.to_int () >= 0x00010003u) if (version.to_int () >= 0x00010003u)
{ {
@ -690,35 +685,58 @@ struct GDEFVersion1_2
{ {
item_variations_t item_vars; item_variations_t item_vars;
if (item_vars.instantiate (this+varStore, c->plan, true, true, if (item_vars.instantiate (this+varStore, c->plan, true, true,
c->plan->gdef_varstore_inner_maps.as_array ())) c->plan->gdef_varstore_inner_maps.as_array ())) {
subset_varstore = out->varStore.serialize_serialize (c->serializer, subset_varstore = out->varStore.serialize_serialize (c->serializer,
item_vars.has_long_word (), item_vars.has_long_word (),
c->plan->axis_tags, c->plan->axis_tags,
item_vars.get_region_list (), item_vars.get_region_list (),
item_vars.get_vardata_encodings ()); item_vars.get_vardata_encodings ());
varstore_index = c->serializer->last_added_child_index();
}
remap_varidx_after_instantiation (item_vars.get_varidx_map (), remap_varidx_after_instantiation (item_vars.get_varidx_map (),
c->plan->layout_variation_idx_delta_map); c->plan->layout_variation_idx_delta_map);
} }
} }
else else
{
subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ()); subset_varstore = out->varStore.serialize_subset (c, varStore, this, c->plan->gdef_varstore_inner_maps.as_array ());
varstore_index = c->serializer->last_added_child_index();
}
} }
out->version.major = version.major;
out->version.minor = version.minor;
if (!subset_varstore && version.to_int () >= 0x00010002u) {
c->serializer->revert (snapshot_version2);
}
bool subset_markglyphsetsdef = false;
if (version.to_int () >= 0x00010002u)
{
subset_markglyphsetsdef = out->markGlyphSetsDef.serialize_subset (c, markGlyphSetsDef, this);
}
if (subset_varstore) if (subset_varstore)
{ {
out->version.minor = 3; out->version.minor = 3;
c->plan->has_gdef_varstore = true; c->plan->has_gdef_varstore = true;
} else if (subset_markglyphsetsdef) { } else if (subset_markglyphsetsdef) {
out->version.minor = 2; out->version.minor = 2;
c->serializer->revert (snapshot_version2);
} else { } else {
out->version.minor = 0; out->version.minor = 0;
c->serializer->revert (snapshot_version0); c->serializer->revert (snapshot_version0);
} }
bool subset_glyphclassdef = out->glyphClassDef.serialize_subset (c, glyphClassDef, this, nullptr, false, true);
bool subset_attachlist = out->attachList.serialize_subset (c, attachList, this);
bool subset_markattachclassdef = out->markAttachClassDef.serialize_subset (c, markAttachClassDef, this, nullptr, false, true);
bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this); bool subset_ligcaretlist = out->ligCaretList.serialize_subset (c, ligCaretList, this);
if (subset_varstore && varstore_index != (unsigned) -1) {
c->serializer->repack_last(varstore_index);
}
return_trace (subset_glyphclassdef || subset_attachlist || return_trace (subset_glyphclassdef || subset_attachlist ||
subset_ligcaretlist || subset_markattachclassdef || subset_ligcaretlist || subset_markattachclassdef ||
(out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) || (out->version.to_int () >= 0x00010002u && subset_markglyphsetsdef) ||
@ -884,14 +902,14 @@ struct GDEF
default: return false; default: return false;
} }
} }
const VariationStore &get_var_store () const const ItemVariationStore &get_var_store () const
{ {
switch (u.version.major) { switch (u.version.major) {
case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(VariationStore); case 1: return u.version.to_int () >= 0x00010003u ? this+u.version1.varStore : Null(ItemVariationStore);
#ifndef HB_NO_BEYOND_64K #ifndef HB_NO_BEYOND_64K
case 2: return this+u.version2.varStore; case 2: return this+u.version2.varStore;
#endif #endif
default: return Null(VariationStore); default: return Null(ItemVariationStore);
} }
} }
@ -1011,9 +1029,9 @@ struct GDEF
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map /* OUT */) const
{ {
if (!has_var_store ()) return; if (!has_var_store ()) return;
const VariationStore &var_store = get_var_store (); const ItemVariationStore &var_store = get_var_store ();
float *store_cache = var_store.create_cache (); float *store_cache = var_store.create_cache ();
unsigned new_major = 0, new_minor = 0; unsigned new_major = 0, new_minor = 0;
unsigned last_major = (layout_variation_indices->get_min ()) >> 16; unsigned last_major = (layout_variation_indices->get_min ()) >> 16;
for (unsigned idx : layout_variation_indices->iter ()) for (unsigned idx : layout_variation_indices->iter ())

View File

@ -324,17 +324,8 @@ struct PairPosFormat2_4 : ValueBase
} }
} }
const hb_set_t &glyphset = *c->plan->glyphset_gsub (); bool ret = out->coverage.serialize_subset(c, coverage, this);
const hb_map_t &glyph_map = *c->plan->glyph_map; return_trace (out->class1Count && out->class2Count && ret);
auto it =
+ hb_iter (this+coverage)
| hb_filter (glyphset)
| hb_map_retains_sorting (glyph_map)
;
out->coverage.serialize_serialize (c->serializer, it);
return_trace (out->class1Count && out->class2Count && bool (it));
} }

View File

@ -116,7 +116,7 @@ struct ValueFormat : HBUINT16
if (!use_x_device && !use_y_device) return ret; if (!use_x_device && !use_y_device) return ret;
const VariationStore &store = c->var_store; const ItemVariationStore &store = c->var_store;
auto *cache = c->var_store_cache; auto *cache = c->var_store_cache;
/* pixel -> fractional pixel */ /* pixel -> fractional pixel */

View File

@ -240,7 +240,8 @@ struct CompositeGlyphRecord
} }
if (is_anchored ()) tx = ty = 0; if (is_anchored ()) tx = ty = 0;
trans.init ((float) tx, (float) ty); /* set is_end_point flag to true, used by IUP delta optimization */
trans.init ((float) tx, (float) ty, true);
{ {
const F2DOT14 *points = (const F2DOT14 *) p; const F2DOT14 *points = (const F2DOT14 *) p;

View File

@ -103,6 +103,9 @@ struct Glyph
} }
} }
bool is_composite () const
{ return type == COMPOSITE; }
bool get_all_points_without_var (const hb_face_t *face, bool get_all_points_without_var (const hb_face_t *face,
contour_point_vector_t &points /* OUT */) const contour_point_vector_t &points /* OUT */) const
{ {

View File

@ -38,7 +38,7 @@ _write_loca (IteratorIn&& it,
unsigned padded_size = *it++; unsigned padded_size = *it++;
offset += padded_size; offset += padded_size;
DEBUG_MSG (SUBSET, nullptr, "loca entry gid %u offset %u padded-size %u", gid, offset, padded_size); DEBUG_MSG (SUBSET, nullptr, "loca entry gid %" PRIu32 " offset %u padded-size %u", gid, offset, padded_size);
value = offset >> right_shift; value = offset >> right_shift;
*dest++ = value; *dest++ = value;

View File

@ -134,20 +134,23 @@ struct ClassDef : public OT::ClassDef
struct class_def_size_estimator_t struct class_def_size_estimator_t
{ {
// TODO(garretrieger): update to support beyond64k coverage/classdef tables.
constexpr static unsigned class_def_format1_base_size = 6;
constexpr static unsigned class_def_format2_base_size = 4;
constexpr static unsigned coverage_base_size = 4;
constexpr static unsigned bytes_per_range = 6;
constexpr static unsigned bytes_per_glyph = 2;
template<typename It> template<typename It>
class_def_size_estimator_t (It glyph_and_class) class_def_size_estimator_t (It glyph_and_class)
: gids_consecutive (true), num_ranges_per_class (), glyphs_per_class () : num_ranges_per_class (), glyphs_per_class ()
{ {
unsigned last_gid = (unsigned) -1; reset();
for (auto p : + glyph_and_class) for (auto p : + glyph_and_class)
{ {
unsigned gid = p.first; unsigned gid = p.first;
unsigned klass = p.second; unsigned klass = p.second;
if (last_gid != (unsigned) -1 && gid != last_gid + 1)
gids_consecutive = false;
last_gid = gid;
hb_set_t* glyphs; hb_set_t* glyphs;
if (glyphs_per_class.has (klass, &glyphs) && glyphs) { if (glyphs_per_class.has (klass, &glyphs) && glyphs) {
glyphs->add (gid); glyphs->add (gid);
@ -177,28 +180,54 @@ struct class_def_size_estimator_t
} }
} }
// Incremental increase in the Coverage and ClassDef table size void reset() {
// (worst case) if all glyphs associated with 'klass' were added. class_def_1_size = class_def_format1_base_size;
unsigned incremental_coverage_size (unsigned klass) const class_def_2_size = class_def_format2_base_size;
{ included_glyphs.clear();
// Coverage takes 2 bytes per glyph worst case, included_classes.clear();
return 2 * glyphs_per_class.get (klass).get_population ();
} }
// Incremental increase in the Coverage and ClassDef table size // Compute the size of coverage for all glyphs added via 'add_class_def_size'.
// (worst case) if all glyphs associated with 'klass' were added. unsigned coverage_size () const
unsigned incremental_class_def_size (unsigned klass) const
{ {
// ClassDef takes 6 bytes per range unsigned format1_size = coverage_base_size + bytes_per_glyph * included_glyphs.get_population();
unsigned class_def_2_size = 6 * num_ranges_per_class.get (klass); unsigned format2_size = coverage_base_size + bytes_per_range * num_glyph_ranges();
if (gids_consecutive) return hb_min(format1_size, format2_size);
{ }
// ClassDef1 takes 2 bytes per glyph, but only can be used
// when gids are consecutive. // Compute the new size of the ClassDef table if all glyphs associated with 'klass' were added.
return hb_min (2 * glyphs_per_class.get (klass).get_population (), class_def_2_size); unsigned add_class_def_size (unsigned klass)
{
if (!included_classes.has(klass)) {
hb_set_t* glyphs = nullptr;
if (glyphs_per_class.has(klass, &glyphs)) {
included_glyphs.union_(*glyphs);
}
class_def_1_size = class_def_format1_base_size;
if (!included_glyphs.is_empty()) {
unsigned min_glyph = included_glyphs.get_min();
unsigned max_glyph = included_glyphs.get_max();
class_def_1_size += bytes_per_glyph * (max_glyph - min_glyph + 1);
}
class_def_2_size += bytes_per_range * num_ranges_per_class.get (klass);
included_classes.add(klass);
} }
return class_def_2_size; return hb_min (class_def_1_size, class_def_2_size);
}
unsigned num_glyph_ranges() const {
hb_codepoint_t start = HB_SET_VALUE_INVALID;
hb_codepoint_t end = HB_SET_VALUE_INVALID;
unsigned count = 0;
while (included_glyphs.next_range (&start, &end)) {
count++;
}
return count;
} }
bool in_error () bool in_error ()
@ -214,9 +243,12 @@ struct class_def_size_estimator_t
} }
private: private:
bool gids_consecutive;
hb_hashmap_t<unsigned, unsigned> num_ranges_per_class; hb_hashmap_t<unsigned, unsigned> num_ranges_per_class;
hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class; hb_hashmap_t<unsigned, hb_set_t> glyphs_per_class;
hb_set_t included_classes;
hb_set_t included_glyphs;
unsigned class_def_1_size;
unsigned class_def_2_size;
}; };

View File

@ -195,6 +195,15 @@ struct graph_t
return incoming_edges_; return incoming_edges_;
} }
unsigned incoming_edges_from_parent (unsigned parent_index) const {
if (single_parent != (unsigned) -1) {
return single_parent == parent_index ? 1 : 0;
}
unsigned* count;
return parents.has(parent_index, &count) ? *count : 0;
}
void reset_parents () void reset_parents ()
{ {
incoming_edges_ = 0; incoming_edges_ = 0;
@ -334,6 +343,16 @@ struct graph_t
return true; return true;
} }
bool give_max_priority ()
{
bool result = false;
while (!has_max_priority()) {
result = true;
priority++;
}
return result;
}
bool has_max_priority () const { bool has_max_priority () const {
return priority >= 3; return priority >= 3;
} }
@ -1023,6 +1042,11 @@ struct graph_t
* Creates a copy of child and re-assigns the link from * Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects * parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated. * linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parent_idx, this
* will do nothing and return the original child_idx.
*/ */
unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx)
{ {
@ -1036,18 +1060,20 @@ struct graph_t
* Creates a copy of child and re-assigns the link from * Creates a copy of child and re-assigns the link from
* parent to the clone. The copy is a shallow copy, objects * parent to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated. * linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parent_idx,
* duplication isn't possible and this will return -1.
*/ */
unsigned duplicate (unsigned parent_idx, unsigned child_idx) unsigned duplicate (unsigned parent_idx, unsigned child_idx)
{ {
update_parents (); update_parents ();
unsigned links_to_child = 0; const auto& child = vertices_[child_idx];
for (const auto& l : vertices_[parent_idx].obj.all_links ()) unsigned links_to_child = child.incoming_edges_from_parent(parent_idx);
{
if (l.objidx == child_idx) links_to_child++;
}
if (vertices_[child_idx].incoming_edges () <= links_to_child) if (child.incoming_edges () <= links_to_child)
{ {
// Can't duplicate this node, doing so would orphan the original one as all remaining links // Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent. // to child are from parent.
@ -1060,7 +1086,7 @@ struct graph_t
parent_idx, child_idx); parent_idx, child_idx);
unsigned clone_idx = duplicate (child_idx); unsigned clone_idx = duplicate (child_idx);
if (clone_idx == (unsigned) -1) return false; if (clone_idx == (unsigned) -1) return -1;
// duplicate shifts the root node idx, so if parent_idx was root update it. // duplicate shifts the root node idx, so if parent_idx was root update it.
if (parent_idx == clone_idx) parent_idx++; if (parent_idx == clone_idx) parent_idx++;
@ -1076,6 +1102,62 @@ struct graph_t
return clone_idx; return clone_idx;
} }
/*
* Creates a copy of child and re-assigns the links from
* parents to the clone. The copy is a shallow copy, objects
* linked from child are not duplicated.
*
* Returns the index of the newly created duplicate.
*
* If the child_idx only has incoming edges from parents,
* duplication isn't possible or duplication fails and this will
* return -1.
*/
unsigned duplicate (const hb_set_t* parents, unsigned child_idx)
{
if (parents->is_empty()) {
return -1;
}
update_parents ();
const auto& child = vertices_[child_idx];
unsigned links_to_child = 0;
unsigned last_parent = parents->get_max();
unsigned first_parent = parents->get_min();
for (unsigned parent_idx : *parents) {
links_to_child += child.incoming_edges_from_parent(parent_idx);
}
if (child.incoming_edges () <= links_to_child)
{
// Can't duplicate this node, doing so would orphan the original one as all remaining links
// to child are from parent.
DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
return -1;
}
DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u, ..., %u => %u", first_parent, last_parent, child_idx);
unsigned clone_idx = duplicate (child_idx);
if (clone_idx == (unsigned) -1) return false;
for (unsigned parent_idx : *parents) {
// duplicate shifts the root node idx, so if parent_idx was root update it.
if (parent_idx == clone_idx) parent_idx++;
auto& parent = vertices_[parent_idx];
for (auto& l : parent.obj.all_links_writer ())
{
if (l.objidx != child_idx)
continue;
reassign_link (l, parent_idx, clone_idx);
}
}
return clone_idx;
}
/* /*
* Adds a new node to the graph, not connected to anything. * Adds a new node to the graph, not connected to anything.

View File

@ -247,8 +247,8 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
for (unsigned i = 0; i < class1_count; i++) for (unsigned i = 0; i < class1_count; i++)
{ {
unsigned accumulated_delta = class1_record_size; unsigned accumulated_delta = class1_record_size;
coverage_size += estimator.incremental_coverage_size (i); class_def_1_size = estimator.add_class_def_size (i);
class_def_1_size += estimator.incremental_class_def_size (i); coverage_size = estimator.coverage_size ();
max_coverage_size = hb_max (max_coverage_size, coverage_size); max_coverage_size = hb_max (max_coverage_size, coverage_size);
max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size);
@ -280,8 +280,10 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType
split_points.push (i); split_points.push (i);
// split does not include i, so add the size for i when we reset the size counters. // split does not include i, so add the size for i when we reset the size counters.
accumulated = base_size + accumulated_delta; accumulated = base_size + accumulated_delta;
coverage_size = 4 + estimator.incremental_coverage_size (i);
class_def_1_size = 4 + estimator.incremental_class_def_size (i); estimator.reset();
class_def_1_size = estimator.add_class_def_size(i);
coverage_size = estimator.coverage_size();
visited.clear (); // node sharing isn't allowed between splits. visited.clear (); // node sharing isn't allowed between splits.
} }
} }

View File

@ -26,27 +26,119 @@
#include "gsubgpos-context.hh" #include "gsubgpos-context.hh"
#include "classdef-graph.hh" #include "classdef-graph.hh"
#include "hb-iter.hh"
#include "hb-serialize.hh"
typedef hb_codepoint_pair_t gid_and_class_t; typedef hb_codepoint_pair_t gid_and_class_t;
typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t; typedef hb_vector_t<gid_and_class_t> gid_and_class_list_t;
template<typename It>
static unsigned actual_class_def_size(It glyph_and_class) {
char buffer[100];
hb_serialize_context_t serializer(buffer, 100);
OT::ClassDef_serialize (&serializer, glyph_and_class);
serializer.end_serialize ();
assert(!serializer.in_error());
static bool incremental_size_is (const gid_and_class_list_t& list, unsigned klass, hb_blob_t* blob = serializer.copy_blob();
unsigned cov_expected, unsigned class_def_expected) unsigned size = hb_blob_get_length(blob);
hb_blob_destroy(blob);
return size;
}
static unsigned actual_class_def_size(gid_and_class_list_t consecutive_map, hb_vector_t<unsigned> classes) {
auto filtered_it =
+ consecutive_map.as_sorted_array().iter()
| hb_filter([&] (unsigned c) {
for (unsigned klass : classes) {
if (c == klass) {
return true;
}
}
return false;
}, hb_second);
return actual_class_def_size(+ filtered_it);
}
template<typename It>
static unsigned actual_coverage_size(It glyphs) {
char buffer[100];
hb_serialize_context_t serializer(buffer, 100);
OT::Layout::Common::Coverage_serialize (&serializer, glyphs);
serializer.end_serialize ();
assert(!serializer.in_error());
hb_blob_t* blob = serializer.copy_blob();
unsigned size = hb_blob_get_length(blob);
hb_blob_destroy(blob);
return size;
}
static unsigned actual_coverage_size(gid_and_class_list_t consecutive_map, hb_vector_t<unsigned> classes) {
auto filtered_it =
+ consecutive_map.as_sorted_array().iter()
| hb_filter([&] (unsigned c) {
for (unsigned klass : classes) {
if (c == klass) {
return true;
}
}
return false;
}, hb_second);
return actual_coverage_size(+ filtered_it | hb_map_retains_sorting(hb_first));
}
static bool check_coverage_size(graph::class_def_size_estimator_t& estimator,
const gid_and_class_list_t& map,
hb_vector_t<unsigned> klasses)
{ {
graph::class_def_size_estimator_t estimator (list.iter ()); unsigned result = estimator.coverage_size();
unsigned expected = actual_coverage_size(map, klasses);
if (result != expected) {
printf ("FAIL: estimated coverage expected size %u but was %u\n", expected, result);
return false;
}
return true;
}
unsigned result = estimator.incremental_coverage_size (klass); static bool check_add_class_def_size(graph::class_def_size_estimator_t& estimator,
if (result != cov_expected) const gid_and_class_list_t& map,
{ unsigned klass, hb_vector_t<unsigned> klasses)
printf ("FAIL: coverage expected size %u but was %u\n", cov_expected, result); {
unsigned result = estimator.add_class_def_size(klass);
unsigned expected = actual_class_def_size(map, klasses);
if (result != expected) {
printf ("FAIL: estimated class def expected size %u but was %u\n", expected, result);
return false; return false;
} }
result = estimator.incremental_class_def_size (klass); return check_coverage_size(estimator, map, klasses);
if (result != class_def_expected) }
static bool check_add_class_def_size (const gid_and_class_list_t& list, unsigned klass)
{
graph::class_def_size_estimator_t estimator (list.iter ());
unsigned result = estimator.add_class_def_size (klass);
auto filtered_it =
+ list.as_sorted_array().iter()
| hb_filter([&] (unsigned c) {
return c == klass;
}, hb_second);
unsigned expected = actual_class_def_size(filtered_it);
if (result != expected)
{ {
printf ("FAIL: class def expected size %u but was %u\n", class_def_expected, result); printf ("FAIL: class def expected size %u but was %u\n", expected, result);
return false;
}
auto cov_it = + filtered_it | hb_map_retains_sorting(hb_first);
result = estimator.coverage_size ();
expected = actual_coverage_size(cov_it);
if (result != expected)
{
printf ("FAIL: coverage expected size %u but was %u\n", expected, result);
return false; return false;
} }
@ -57,43 +149,45 @@ static void test_class_and_coverage_size_estimates ()
{ {
gid_and_class_list_t empty = { gid_and_class_list_t empty = {
}; };
assert (incremental_size_is (empty, 0, 0, 0)); assert (check_add_class_def_size (empty, 0));
assert (incremental_size_is (empty, 1, 0, 0)); assert (check_add_class_def_size (empty, 1));
gid_and_class_list_t class_zero = { gid_and_class_list_t class_zero = {
{5, 0}, {5, 0},
}; };
assert (incremental_size_is (class_zero, 0, 2, 0)); assert (check_add_class_def_size (class_zero, 0));
gid_and_class_list_t consecutive = { gid_and_class_list_t consecutive = {
{4, 0}, {4, 0},
{5, 0}, {5, 0},
{6, 1}, {6, 1},
{7, 1}, {7, 1},
{8, 2}, {8, 2},
{9, 2}, {9, 2},
{10, 2}, {10, 2},
{11, 2}, {11, 2},
}; };
assert (incremental_size_is (consecutive, 0, 4, 0)); assert (check_add_class_def_size (consecutive, 0));
assert (incremental_size_is (consecutive, 1, 4, 4)); assert (check_add_class_def_size (consecutive, 1));
assert (incremental_size_is (consecutive, 2, 8, 6)); assert (check_add_class_def_size (consecutive, 2));
gid_and_class_list_t non_consecutive = { gid_and_class_list_t non_consecutive = {
{4, 0}, {4, 0},
{5, 0}, {6, 0},
{6, 1}, {8, 1},
{7, 1}, {10, 1},
{9, 2}, {9, 2},
{10, 2}, {10, 2},
{11, 2}, {11, 2},
{12, 2}, {13, 2},
}; };
assert (incremental_size_is (non_consecutive, 0, 4, 0)); assert (check_add_class_def_size (non_consecutive, 0));
assert (incremental_size_is (non_consecutive, 1, 4, 6)); assert (check_add_class_def_size (non_consecutive, 1));
assert (incremental_size_is (non_consecutive, 2, 8, 6)); assert (check_add_class_def_size (non_consecutive, 2));
gid_and_class_list_t multiple_ranges = { gid_and_class_list_t multiple_ranges = {
{4, 0}, {4, 0},
@ -108,12 +202,95 @@ static void test_class_and_coverage_size_estimates ()
{12, 1}, {12, 1},
{13, 1}, {13, 1},
}; };
assert (incremental_size_is (multiple_ranges, 0, 4, 0)); assert (check_add_class_def_size (multiple_ranges, 0));
assert (incremental_size_is (multiple_ranges, 1, 2 * 6, 3 * 6)); assert (check_add_class_def_size (multiple_ranges, 1));
}
static void test_running_class_and_coverage_size_estimates () {
// #### With consecutive gids: switches formats ###
gid_and_class_list_t consecutive_map = {
// range 1-4 (f1: 8 bytes), (f2: 6 bytes)
{1, 1},
{2, 1},
{3, 1},
{4, 1},
// (f1: 2 bytes), (f2: 6 bytes)
{5, 2},
// (f1: 14 bytes), (f2: 6 bytes)
{6, 3},
{7, 3},
{8, 3},
{9, 3},
{10, 3},
{11, 3},
{12, 3},
};
graph::class_def_size_estimator_t estimator1(consecutive_map.iter());
assert(check_add_class_def_size(estimator1, consecutive_map, 1, {1}));
assert(check_add_class_def_size(estimator1, consecutive_map, 2, {1, 2}));
assert(check_add_class_def_size(estimator1, consecutive_map, 2, {1, 2})); // check that adding the same class again works
assert(check_add_class_def_size(estimator1, consecutive_map, 3, {1, 2, 3}));
estimator1.reset();
assert(check_add_class_def_size(estimator1, consecutive_map, 2, {2}));
assert(check_add_class_def_size(estimator1, consecutive_map, 3, {2, 3}));
// #### With non-consecutive gids: always uses format 2 ###
gid_and_class_list_t non_consecutive_map = {
// range 1-4 (f1: 8 bytes), (f2: 6 bytes)
{1, 1},
{2, 1},
{3, 1},
{4, 1},
// (f1: 2 bytes), (f2: 12 bytes)
{6, 2},
{8, 2},
// (f1: 14 bytes), (f2: 6 bytes)
{9, 3},
{10, 3},
{11, 3},
{12, 3},
{13, 3},
{14, 3},
{15, 3},
};
graph::class_def_size_estimator_t estimator2(non_consecutive_map.iter());
assert(check_add_class_def_size(estimator2, non_consecutive_map, 1, {1}));
assert(check_add_class_def_size(estimator2, non_consecutive_map, 2, {1, 2}));
assert(check_add_class_def_size(estimator2, non_consecutive_map, 3, {1, 2, 3}));
estimator2.reset();
assert(check_add_class_def_size(estimator2, non_consecutive_map, 2, {2}));
assert(check_add_class_def_size(estimator2, non_consecutive_map, 3, {2, 3}));
}
static void test_running_class_size_estimates_with_locally_consecutive_glyphs () {
gid_and_class_list_t map = {
{1, 1},
{6, 2},
{7, 3},
};
graph::class_def_size_estimator_t estimator(map.iter());
assert(check_add_class_def_size(estimator, map, 1, {1}));
assert(check_add_class_def_size(estimator, map, 2, {1, 2}));
assert(check_add_class_def_size(estimator, map, 3, {1, 2, 3}));
estimator.reset();
assert(check_add_class_def_size(estimator, map, 2, {2}));
assert(check_add_class_def_size(estimator, map, 3, {2, 3}));
} }
int int
main (int argc, char **argv) main (int argc, char **argv)
{ {
test_class_and_coverage_size_estimates (); test_class_and_coverage_size_estimates ();
test_running_class_and_coverage_size_estimates ();
test_running_class_size_estimates_with_locally_consecutive_glyphs ();
} }

View File

@ -54,6 +54,7 @@
#include "hb-subset-cff1.cc" #include "hb-subset-cff1.cc"
#include "hb-subset-cff2.cc" #include "hb-subset-cff2.cc"
#include "hb-subset-input.cc" #include "hb-subset-input.cc"
#include "hb-subset-instancer-iup.cc"
#include "hb-subset-instancer-solver.cc" #include "hb-subset-instancer-solver.cc"
#include "hb-subset-plan.cc" #include "hb-subset-plan.cc"
#include "hb-subset-repacker.cc" #include "hb-subset-repacker.cc"

View File

@ -552,6 +552,7 @@ struct LigatureSubtable
{ {
DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE;
if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
} }

View File

@ -671,7 +671,7 @@ struct hb_pair_t
return 0; return 0;
} }
friend void swap (hb_pair_t& a, hb_pair_t& b) friend void swap (hb_pair_t& a, hb_pair_t& b) noexcept
{ {
hb_swap (a.first, b.first); hb_swap (a.first, b.first);
hb_swap (a.second, b.second); hb_swap (a.second, b.second);
@ -1053,6 +1053,18 @@ _hb_cmp_method (const void *pkey, const void *pval, Ts... ds)
return val.cmp (key, ds...); return val.cmp (key, ds...);
} }
template <typename K, typename V>
static int
_hb_cmp_operator (const void *pkey, const void *pval)
{
const K& key = * (const K*) pkey;
const V& val = * (const V*) pval;
if (key < val) return -1;
if (key > val) return 1;
return 0;
}
template <typename V, typename K, typename ...Ts> template <typename V, typename K, typename ...Ts>
static inline bool static inline bool
hb_bsearch_impl (unsigned *pos, /* Out */ hb_bsearch_impl (unsigned *pos, /* Out */

View File

@ -39,10 +39,10 @@ struct hb_bit_set_invertible_t
hb_bit_set_invertible_t () = default; hb_bit_set_invertible_t () = default;
hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default; hb_bit_set_invertible_t (const hb_bit_set_invertible_t& o) = default;
hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) : hb_bit_set_invertible_t () { hb_swap (*this, other); } hb_bit_set_invertible_t (hb_bit_set_invertible_t&& other) noexcept : hb_bit_set_invertible_t () { hb_swap (*this, other); }
hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default; hb_bit_set_invertible_t& operator= (const hb_bit_set_invertible_t& o) = default;
hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) { hb_swap (*this, other); return *this; } hb_bit_set_invertible_t& operator= (hb_bit_set_invertible_t&& other) noexcept { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) friend void swap (hb_bit_set_invertible_t &a, hb_bit_set_invertible_t &b) noexcept
{ {
if (likely (!a.s.successful || !b.s.successful)) if (likely (!a.s.successful || !b.s.successful))
return; return;

View File

@ -38,10 +38,10 @@ struct hb_bit_set_t
~hb_bit_set_t () = default; ~hb_bit_set_t () = default;
hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); } hb_bit_set_t (const hb_bit_set_t& other) : hb_bit_set_t () { set (other, true); }
hb_bit_set_t ( hb_bit_set_t&& other) : hb_bit_set_t () { hb_swap (*this, other); } hb_bit_set_t ( hb_bit_set_t&& other) noexcept : hb_bit_set_t () { hb_swap (*this, other); }
hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; } hb_bit_set_t& operator= (const hb_bit_set_t& other) { set (other); return *this; }
hb_bit_set_t& operator= (hb_bit_set_t&& other) { hb_swap (*this, other); return *this; } hb_bit_set_t& operator= (hb_bit_set_t&& other) noexcept { hb_swap (*this, other); return *this; }
friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) friend void swap (hb_bit_set_t &a, hb_bit_set_t &b) noexcept
{ {
if (likely (!a.successful || !b.successful)) if (likely (!a.successful || !b.successful))
return; return;

View File

@ -598,6 +598,11 @@ _open_resource_fork (const char *file_name, hb_mapped_file_t *file)
* Creates a new blob containing the data from the * Creates a new blob containing the data from the
* specified binary font file. * specified binary font file.
* *
* The filename is passed directly to the system on all platforms,
* except on Windows, where the filename is interpreted as UTF-8.
* Only if the filename is not valid UTF-8, it will be interpreted
* according to the system codepage.
*
* Returns: An #hb_blob_t pointer with the content of the file, * Returns: An #hb_blob_t pointer with the content of the file,
* or hb_blob_get_empty() if failed. * or hb_blob_get_empty() if failed.
* *
@ -617,6 +622,11 @@ hb_blob_create_from_file (const char *file_name)
* Creates a new blob containing the data from the * Creates a new blob containing the data from the
* specified binary font file. * specified binary font file.
* *
* The filename is passed directly to the system on all platforms,
* except on Windows, where the filename is interpreted as UTF-8.
* Only if the filename is not valid UTF-8, it will be interpreted
* according to the system codepage.
*
* Returns: An #hb_blob_t pointer with the content of the file, * Returns: An #hb_blob_t pointer with the content of the file,
* or `NULL` if failed. * or `NULL` if failed.
* *
@ -672,10 +682,19 @@ fail_without_close:
if (unlikely (!file)) return nullptr; if (unlikely (!file)) return nullptr;
HANDLE fd; HANDLE fd;
int conversion;
unsigned int size = strlen (file_name) + 1; unsigned int size = strlen (file_name) + 1;
wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size); wchar_t * wchar_file_name = (wchar_t *) hb_malloc (sizeof (wchar_t) * size);
if (unlikely (!wchar_file_name)) goto fail_without_close; if (unlikely (!wchar_file_name)) goto fail_without_close;
mbstowcs (wchar_file_name, file_name, size);
/* Assume file name is given in UTF-8 encoding */
conversion = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, file_name, -1, wchar_file_name, size);
if (conversion <= 0)
{
/* Conversion failed due to invalid UTF-8 characters,
Repeat conversion based on system code page */
mbstowcs(wchar_file_name, file_name, size);
}
#if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP) #if !WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP) && WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
{ {
CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 }; CREATEFILE2_EXTENDED_PARAMETERS ceparams = { 0 };

View File

@ -149,7 +149,7 @@ buffer_verify_unsafe_to_break (hb_buffer_t *buffer,
} }
assert (text_start < text_end); assert (text_start < text_end);
if (0) if (false)
printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
hb_buffer_clear_contents (fragment); hb_buffer_clear_contents (fragment);
@ -288,7 +288,7 @@ buffer_verify_unsafe_to_concat (hb_buffer_t *buffer,
} }
assert (text_start < text_end); assert (text_start < text_end);
if (0) if (false)
printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end); printf("start %u end %u text start %u end %u\n", start, end, text_start, text_end);
#if 0 #if 0

View File

@ -309,6 +309,7 @@ hb_buffer_t::clear ()
deallocate_var_all (); deallocate_var_all ();
serial = 0; serial = 0;
random_state = 1;
scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT; scratch_flags = HB_BUFFER_SCRATCH_FLAG_DEFAULT;
} }
@ -1359,6 +1360,49 @@ hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer)
return buffer->not_found; return buffer->not_found;
} }
/**
* hb_buffer_set_random_state:
* @buffer: An #hb_buffer_t
* @state: the new random state
*
* Sets the random state of the buffer. The state changes
* every time a glyph uses randomness (eg. the `rand`
* OpenType feature). This function together with
* hb_buffer_get_random_state() allow for transferring
* the current random state to a subsequent buffer, to
* get better randomness distribution.
*
* Defaults to 1 and when buffer contents are cleared.
* A value of 0 disables randomness during shaping.
*
* Since: 8.4.0
**/
void
hb_buffer_set_random_state (hb_buffer_t *buffer,
unsigned state)
{
if (unlikely (hb_object_is_immutable (buffer)))
return;
buffer->random_state = state;
}
/**
* hb_buffer_get_random_state:
* @buffer: An #hb_buffer_t
*
* See hb_buffer_set_random_state().
*
* Return value:
* The @buffer random state
*
* Since: 8.4.0
**/
unsigned
hb_buffer_get_random_state (const hb_buffer_t *buffer)
{
return buffer->random_state;
}
/** /**
* hb_buffer_clear_contents: * hb_buffer_clear_contents:

View File

@ -487,6 +487,12 @@ hb_buffer_set_not_found_glyph (hb_buffer_t *buffer,
HB_EXTERN hb_codepoint_t HB_EXTERN hb_codepoint_t
hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer); hb_buffer_get_not_found_glyph (const hb_buffer_t *buffer);
HB_EXTERN void
hb_buffer_set_random_state (hb_buffer_t *buffer,
unsigned state);
HB_EXTERN unsigned
hb_buffer_get_random_state (const hb_buffer_t *buffer);
/* /*
* Content API. * Content API.

View File

@ -116,6 +116,7 @@ struct hb_buffer_t
uint8_t allocated_var_bits; uint8_t allocated_var_bits;
uint8_t serial; uint8_t serial;
uint32_t random_state;
hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */ hb_buffer_scratch_flags_t scratch_flags; /* Have space-fallback, etc. */
unsigned int max_len; /* Maximum allowed len. */ unsigned int max_len; /* Maximum allowed len. */
int max_ops; /* Maximum allowed operations. */ int max_ops; /* Maximum allowed operations. */

View File

@ -54,8 +54,8 @@ struct top_dict_values_t : dict_values_t<OPSTR>
} }
void fini () { dict_values_t<OPSTR>::fini (); } void fini () { dict_values_t<OPSTR>::fini (); }
unsigned int charStringsOffset; int charStringsOffset;
unsigned int FDArrayOffset; int FDArrayOffset;
}; };
struct dict_opset_t : opset_t<number_t> struct dict_opset_t : opset_t<number_t>
@ -157,11 +157,11 @@ struct top_dict_opset_t : dict_opset_t
{ {
switch (op) { switch (op) {
case OpCode_CharStrings: case OpCode_CharStrings:
dictval.charStringsOffset = env.argStack.pop_uint (); dictval.charStringsOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
case OpCode_FDArray: case OpCode_FDArray:
dictval.FDArrayOffset = env.argStack.pop_uint (); dictval.FDArrayOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
case OpCode_FontMatrix: case OpCode_FontMatrix:

View File

@ -168,7 +168,7 @@ struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs>
protected: protected:
const int *coords; const int *coords;
unsigned int num_coords; unsigned int num_coords;
const CFF2VariationStore *varStore; const CFF2ItemVariationStore *varStore;
unsigned int region_count; unsigned int region_count;
unsigned int ivs; unsigned int ivs;
hb_vector_t<float> scalars; hb_vector_t<float> scalars;

View File

@ -996,7 +996,7 @@ hb_feature_to_string (hb_feature_t *feature,
if (feature->value > 1) if (feature->value > 1)
{ {
s[len++] = '='; s[len++] = '=';
len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%u", feature->value)); len += hb_max (0, snprintf (s + len, ARRAY_LENGTH (s) - len, "%" PRIu32, feature->value));
} }
assert (len < ARRAY_LENGTH (s)); assert (len < ARRAY_LENGTH (s));
len = hb_min (len, size - 1); len = hb_min (len, size - 1);

View File

@ -47,14 +47,10 @@
# endif /* !__cplusplus */ # endif /* !__cplusplus */
#endif #endif
#if defined (_SVR4) || defined (SVR4) || defined (__OpenBSD__) || \ #if defined (_AIX)
defined (_sgi) || defined (__sun) || defined (sun) || \
defined (__digital__) || defined (__HP_cc)
# include <inttypes.h>
#elif defined (_AIX)
# include <sys/inttypes.h> # include <sys/inttypes.h>
#elif defined (_MSC_VER) && _MSC_VER < 1600 #elif defined (_MSC_VER) && _MSC_VER < 1600
/* VS 2010 (_MSC_VER 1600) has stdint.h */ /* VS 2010 (_MSC_VER 1600) has stdint.h */
typedef __int8 int8_t; typedef __int8 int8_t;
typedef unsigned __int8 uint8_t; typedef unsigned __int8 uint8_t;
typedef __int16 int16_t; typedef __int16 int16_t;
@ -63,10 +59,11 @@ typedef __int32 int32_t;
typedef unsigned __int32 uint32_t; typedef unsigned __int32 uint32_t;
typedef __int64 int64_t; typedef __int64 int64_t;
typedef unsigned __int64 uint64_t; typedef unsigned __int64 uint64_t;
#elif defined (__KERNEL__) #elif defined (_MSC_VER) && _MSC_VER < 1800
# include <linux/types.h> /* VS 2013 (_MSC_VER 1800) has inttypes.h */
#else
# include <stdint.h> # include <stdint.h>
#else
# include <inttypes.h>
#endif #endif
#if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) #if defined(__GNUC__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))

View File

@ -56,15 +56,15 @@ struct shared_ptr
explicit shared_ptr (T *p = nullptr) : p (p) {} explicit shared_ptr (T *p = nullptr) : p (p) {}
shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {} shared_ptr (const shared_ptr &o) : p (v::reference (o.p)) {}
shared_ptr (shared_ptr &&o) : p (o.p) { o.p = nullptr; } shared_ptr (shared_ptr &&o) noexcept : p (o.p) { o.p = nullptr; }
shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; } shared_ptr& operator = (const shared_ptr &o) { if (p != o.p) { destroy (); p = o.p; reference (); } return *this; }
shared_ptr& operator = (shared_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } shared_ptr& operator = (shared_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
~shared_ptr () { v::destroy (p); p = nullptr; } ~shared_ptr () { v::destroy (p); p = nullptr; }
T* get() const { return p; } T* get() const { return p; }
void swap (shared_ptr &o) { std::swap (p, o.p); } void swap (shared_ptr &o) noexcept { std::swap (p, o.p); }
friend void swap (shared_ptr &a, shared_ptr &b) { std::swap (a.p, b.p); } friend void swap (shared_ptr &a, shared_ptr &b) noexcept { std::swap (a.p, b.p); }
operator T * () const { return p; } operator T * () const { return p; }
T& operator * () const { return *get (); } T& operator * () const { return *get (); }
@ -98,16 +98,16 @@ struct unique_ptr
explicit unique_ptr (T *p = nullptr) : p (p) {} explicit unique_ptr (T *p = nullptr) : p (p) {}
unique_ptr (const unique_ptr &o) = delete; unique_ptr (const unique_ptr &o) = delete;
unique_ptr (unique_ptr &&o) : p (o.p) { o.p = nullptr; } unique_ptr (unique_ptr &&o) noexcept : p (o.p) { o.p = nullptr; }
unique_ptr& operator = (const unique_ptr &o) = delete; unique_ptr& operator = (const unique_ptr &o) = delete;
unique_ptr& operator = (unique_ptr &&o) { v::destroy (p); p = o.p; o.p = nullptr; return *this; } unique_ptr& operator = (unique_ptr &&o) noexcept { v::destroy (p); p = o.p; o.p = nullptr; return *this; }
~unique_ptr () { v::destroy (p); p = nullptr; } ~unique_ptr () { v::destroy (p); p = nullptr; }
T* get() const { return p; } T* get() const { return p; }
T* release () { T* v = p; p = nullptr; return v; } T* release () { T* v = p; p = nullptr; return v; }
void swap (unique_ptr &o) { std::swap (p, o.p); } void swap (unique_ptr &o) noexcept { std::swap (p, o.p); }
friend void swap (unique_ptr &a, unique_ptr &b) { std::swap (a.p, b.p); } friend void swap (unique_ptr &a, unique_ptr &b) noexcept { std::swap (a.p, b.p); }
operator T * () const { return p; } operator T * () const { return p; }
T& operator * () const { return *get (); } T& operator * () const { return *get (); }

View File

@ -173,7 +173,7 @@ _hb_directwrite_shaper_face_data_create (hb_face_t *face)
t_DWriteCreateFactory p_DWriteCreateFactory; t_DWriteCreateFactory p_DWriteCreateFactory;
#if defined(__GNUC__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type" #pragma GCC diagnostic ignored "-Wcast-function-type"
#endif #endif
@ -181,7 +181,7 @@ _hb_directwrite_shaper_face_data_create (hb_face_t *face)
p_DWriteCreateFactory = (t_DWriteCreateFactory) p_DWriteCreateFactory = (t_DWriteCreateFactory)
GetProcAddress (data->dwrite_dll, "DWriteCreateFactory"); GetProcAddress (data->dwrite_dll, "DWriteCreateFactory");
#if defined(__GNUC__) #if defined(__GNUC__) || defined(__clang__)
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#endif #endif

View File

@ -0,0 +1,119 @@
/*
* Copyright © 2022 Red Hat, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_FEATURES_H
#define HB_FEATURES_H
HB_BEGIN_DECLS
/**
* SECTION: hb-features
* @title: hb-features
* @short_description: Feature detection
* @include: hb-features.h
*
* Macros for detecting optional HarfBuzz features at build time.
**/
/**
* HB_HAS_CAIRO:
*
* Defined if Harfbuzz has been built with cairo support.
*/
#
/**
* HB_HAS_CORETEXT:
*
* Defined if Harfbuzz has been built with CoreText support.
*/
#undef HB_HAS_CORETEXT
/**
* HB_HAS_DIRECTWRITE:
*
* Defined if Harfbuzz has been built with DirectWrite support.
*/
#undef HB_HAS_DIRECTWRITE
/**
* HB_HAS_FREETYPE:
*
* Defined if Harfbuzz has been built with Freetype support.
*/
#define HB_HAS_FREETYPE 1
/**
* HB_HAS_GDI:
*
* Defined if Harfbuzz has been built with GDI support.
*/
#undef HB_HAS_GDI
/**
* HB_HAS_GLIB:
*
* Defined if Harfbuzz has been built with GLib support.
*/
#define HB_HAS_GLIB 1
/**
* HB_HAS_GOBJECT:
*
* Defined if Harfbuzz has been built with GObject support.
*/
#undef HB_HAS_GOBJECT
/**
* HB_HAS_GRAPHITE:
*
* Defined if Harfbuzz has been built with Graphite support.
*/
#undef HB_HAS_GRAPHITE
/**
* HB_HAS_ICU:
*
* Defined if Harfbuzz has been built with ICU support.
*/
#undef HB_HAS_ICU
/**
* HB_HAS_UNISCRIBE:
*
* Defined if Harfbuzz has been built with Uniscribe support.
*/
#undef HB_HAS_UNISCRIBE
/**
* HB_HAS_WASM:
*
* Defined if Harfbuzz has been built with WebAssembly support.
*/
#undef HB_HAS_WASM
HB_END_DECLS
#endif /* HB_FEATURES_H */

View File

@ -651,7 +651,7 @@ struct hb_font_t
{ {
if (get_glyph_name (glyph, s, size)) return; if (get_glyph_name (glyph, s, size)) return;
if (size && snprintf (s, size, "gid%u", glyph) < 0) if (size && snprintf (s, size, "gid%" PRIu32, glyph) < 0)
*s = '\0'; *s = '\0';
} }

View File

@ -224,7 +224,7 @@ _hb_ft_hb_font_check_changed (hb_font_t *font,
* *
* Sets the FT_Load_Glyph load flags for the specified #hb_font_t. * Sets the FT_Load_Glyph load flags for the specified #hb_font_t.
* *
* For more information, see * For more information, see
* <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx> * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
* *
* This function works with #hb_font_t objects created by * This function works with #hb_font_t objects created by
@ -252,7 +252,7 @@ hb_ft_font_set_load_flags (hb_font_t *font, int load_flags)
* *
* Fetches the FT_Load_Glyph load flags of the specified #hb_font_t. * Fetches the FT_Load_Glyph load flags of the specified #hb_font_t.
* *
* For more information, see * For more information, see
* <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx> * <https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_load_xxx>
* *
* This function works with #hb_font_t objects created by * This function works with #hb_font_t objects created by
@ -1118,10 +1118,10 @@ _hb_ft_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data
* This variant of the function does not provide any life-cycle management. * This variant of the function does not provide any life-cycle management.
* *
* Most client programs should use hb_ft_face_create_referenced() * Most client programs should use hb_ft_face_create_referenced()
* (or, perhaps, hb_ft_face_create_cached()) instead. * (or, perhaps, hb_ft_face_create_cached()) instead.
* *
* If you know you have valid reasons not to use hb_ft_face_create_referenced(), * If you know you have valid reasons not to use hb_ft_face_create_referenced(),
* then it is the client program's responsibility to destroy @ft_face * then it is the client program's responsibility to destroy @ft_face
* after the #hb_face_t face object has been destroyed. * after the #hb_face_t face object has been destroyed.
* *
* Return value: (transfer full): the new #hb_face_t face object * Return value: (transfer full): the new #hb_face_t face object
@ -1215,7 +1215,7 @@ hb_ft_face_finalize (void *arg)
hb_face_t * hb_face_t *
hb_ft_face_create_cached (FT_Face ft_face) hb_ft_face_create_cached (FT_Face ft_face)
{ {
if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize)) if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != hb_ft_face_finalize))
{ {
if (ft_face->generic.finalizer) if (ft_face->generic.finalizer)
ft_face->generic.finalizer (ft_face); ft_face->generic.finalizer (ft_face);
@ -1241,13 +1241,13 @@ hb_ft_face_create_cached (FT_Face ft_face)
* This variant of the function does not provide any life-cycle management. * This variant of the function does not provide any life-cycle management.
* *
* Most client programs should use hb_ft_font_create_referenced() * Most client programs should use hb_ft_font_create_referenced()
* instead. * instead.
* *
* If you know you have valid reasons not to use hb_ft_font_create_referenced(), * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
* then it is the client program's responsibility to destroy @ft_face * then it is the client program's responsibility to destroy @ft_face
* after the #hb_font_t font object has been destroyed. * after the #hb_font_t font object has been destroyed.
* *
* HarfBuzz will use the @destroy callback on the #hb_font_t font object * HarfBuzz will use the @destroy callback on the #hb_font_t font object
* if it is supplied when you use this function. However, even if @destroy * if it is supplied when you use this function. However, even if @destroy
* is provided, it is the client program's responsibility to destroy @ft_face, * is provided, it is the client program's responsibility to destroy @ft_face,
* and it is the client program's responsibility to ensure that @ft_face is * and it is the client program's responsibility to ensure that @ft_face is

View File

@ -93,15 +93,16 @@ hb_icu_script_to_script (UScriptCode script)
UScriptCode UScriptCode
hb_icu_script_from_script (hb_script_t script) hb_icu_script_from_script (hb_script_t script)
{ {
UScriptCode out = USCRIPT_INVALID_CODE;
if (unlikely (script == HB_SCRIPT_INVALID)) if (unlikely (script == HB_SCRIPT_INVALID))
return USCRIPT_INVALID_CODE; return out;
unsigned int numScriptCode = 1 + u_getIntPropertyMaxValue (UCHAR_SCRIPT); UErrorCode icu_err = U_ZERO_ERROR;
for (unsigned int i = 0; i < numScriptCode; i++) const unsigned char buf[5] = {HB_UNTAG (script), 0};
if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script)) uscript_getCode ((const char *) buf, &out, 1, &icu_err);
return (UScriptCode) i;
return USCRIPT_UNKNOWN; return out;
} }

View File

@ -106,7 +106,7 @@
#endif #endif
#ifndef HB_COLRV1_MAX_EDGE_COUNT #ifndef HB_COLRV1_MAX_EDGE_COUNT
#define HB_COLRV1_MAX_EDGE_COUNT 65536 #define HB_COLRV1_MAX_EDGE_COUNT 2048
#endif #endif

View File

@ -70,9 +70,9 @@ struct hb_hashmap_t
alloc (o.population); hb_copy (o, *this); alloc (o.population); hb_copy (o, *this);
} }
hb_hashmap_t (hb_hashmap_t&& o) : hb_hashmap_t () { hb_swap (*this, o); } hb_hashmap_t (hb_hashmap_t&& o) noexcept : hb_hashmap_t () { hb_swap (*this, o); }
hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; } hb_hashmap_t& operator= (const hb_hashmap_t& o) { reset (); alloc (o.population); hb_copy (o, *this); return *this; }
hb_hashmap_t& operator= (hb_hashmap_t&& o) { hb_swap (*this, o); return *this; } hb_hashmap_t& operator= (hb_hashmap_t&& o) noexcept { hb_swap (*this, o); return *this; }
hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t () hb_hashmap_t (std::initializer_list<hb_pair_t<K, V>> lst) : hb_hashmap_t ()
{ {
@ -137,26 +137,23 @@ struct hb_hashmap_t
}; };
hb_object_header_t header; hb_object_header_t header;
unsigned int successful : 1; /* Allocations successful */ bool successful; /* Allocations successful */
unsigned int population : 31; /* Not including tombstones. */ unsigned short max_chain_length;
unsigned int population; /* Not including tombstones. */
unsigned int occupancy; /* Including tombstones. */ unsigned int occupancy; /* Including tombstones. */
unsigned int mask; unsigned int mask;
unsigned int prime; unsigned int prime;
unsigned int max_chain_length;
item_t *items; item_t *items;
friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) friend void swap (hb_hashmap_t& a, hb_hashmap_t& b) noexcept
{ {
if (unlikely (!a.successful || !b.successful)) if (unlikely (!a.successful || !b.successful))
return; return;
unsigned tmp = a.population; hb_swap (a.max_chain_length, b.max_chain_length);
a.population = b.population; hb_swap (a.population, b.population);
b.population = tmp;
//hb_swap (a.population, b.population);
hb_swap (a.occupancy, b.occupancy); hb_swap (a.occupancy, b.occupancy);
hb_swap (a.mask, b.mask); hb_swap (a.mask, b.mask);
hb_swap (a.prime, b.prime); hb_swap (a.prime, b.prime);
hb_swap (a.max_chain_length, b.max_chain_length);
hb_swap (a.items, b.items); hb_swap (a.items, b.items);
} }
void init () void init ()
@ -164,10 +161,10 @@ struct hb_hashmap_t
hb_object_init (this); hb_object_init (this);
successful = true; successful = true;
max_chain_length = 0;
population = occupancy = 0; population = occupancy = 0;
mask = 0; mask = 0;
prime = 0; prime = 0;
max_chain_length = 0;
items = nullptr; items = nullptr;
} }
void fini () void fini ()
@ -558,7 +555,7 @@ struct hb_map_t : hb_hashmap_t<hb_codepoint_t,
~hb_map_t () = default; ~hb_map_t () = default;
hb_map_t () : hashmap () {} hb_map_t () : hashmap () {}
hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {} hb_map_t (const hb_map_t &o) : hashmap ((hashmap &) o) {}
hb_map_t (hb_map_t &&o) : hashmap (std::move ((hashmap &) o)) {} hb_map_t (hb_map_t &&o) noexcept : hashmap (std::move ((hashmap &) o)) {}
hb_map_t& operator= (const hb_map_t&) = default; hb_map_t& operator= (const hb_map_t&) = default;
hb_map_t& operator= (hb_map_t&&) = default; hb_map_t& operator= (hb_map_t&&) = default;
hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {} hb_map_t (std::initializer_list<hb_codepoint_pair_t> lst) : hashmap (lst) {}

View File

@ -325,7 +325,7 @@ retry:
hb_user_data_array_t *user_data = obj->header.user_data.get_acquire (); hb_user_data_array_t *user_data = obj->header.user_data.get_acquire ();
if (unlikely (!user_data)) if (unlikely (!user_data))
{ {
user_data = (hb_user_data_array_t *) hb_calloc (sizeof (hb_user_data_array_t), 1); user_data = (hb_user_data_array_t *) hb_calloc (1, sizeof (hb_user_data_array_t));
if (unlikely (!user_data)) if (unlikely (!user_data))
return false; return false;
user_data->init (); user_data->init ();

View File

@ -985,6 +985,13 @@ struct SortedArrayOf : ArrayOf<Type, LenType>
return_trace (ret); return_trace (ret);
} }
SortedArrayOf* copy (hb_serialize_context_t *c) const
{
TRACE_SERIALIZE (this);
SortedArrayOf* out = reinterpret_cast<SortedArrayOf *> (ArrayOf<Type, LenType>::copy (c));
return_trace (out);
}
template <typename T> template <typename T>
Type &bsearch (const T &x, Type &not_found = Crap (Type)) Type &bsearch (const T &x, Type &not_found = Crap (Type))
{ return *as_array ().bsearch (x, &not_found); } { return *as_array ().bsearch (x, &not_found); }

View File

@ -41,10 +41,21 @@ using namespace OT;
using objidx_t = hb_serialize_context_t::objidx_t; using objidx_t = hb_serialize_context_t::objidx_t;
using whence_t = hb_serialize_context_t::whence_t; using whence_t = hb_serialize_context_t::whence_t;
/* utility macro */ /* CFF offsets can technically be negative */
template<typename Type> template<typename Type, typename ...Ts>
static inline const Type& StructAtOffsetOrNull (const void *P, unsigned int offset) static inline const Type& StructAtOffsetOrNull (const void *P, int offset, hb_sanitize_context_t &sc, Ts&&... ds)
{ return offset ? StructAtOffset<Type> (P, offset) : Null (Type); } {
if (!offset) return Null (Type);
const char *p = (const char *) P + offset;
if (!sc.check_point (p)) return Null (Type);
const Type &obj = *reinterpret_cast<const Type *> (p);
if (!obj.sanitize (&sc, std::forward<Ts> (ds)...)) return Null (Type);
return obj;
}
struct code_pair_t struct code_pair_t
{ {

View File

@ -763,9 +763,9 @@ struct cff1_top_dict_values_t : top_dict_values_t<cff1_top_dict_val_t>
unsigned int ros_supplement; unsigned int ros_supplement;
unsigned int cidCount; unsigned int cidCount;
unsigned int EncodingOffset; int EncodingOffset;
unsigned int CharsetOffset; int CharsetOffset;
unsigned int FDSelectOffset; int FDSelectOffset;
table_info_t privateDictInfo; table_info_t privateDictInfo;
}; };
@ -821,24 +821,24 @@ struct cff1_top_dict_opset_t : top_dict_opset_t<cff1_top_dict_val_t>
break; break;
case OpCode_Encoding: case OpCode_Encoding:
dictval.EncodingOffset = env.argStack.pop_uint (); dictval.EncodingOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
if (unlikely (dictval.EncodingOffset == 0)) return; if (unlikely (dictval.EncodingOffset == 0)) return;
break; break;
case OpCode_charset: case OpCode_charset:
dictval.CharsetOffset = env.argStack.pop_uint (); dictval.CharsetOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
if (unlikely (dictval.CharsetOffset == 0)) return; if (unlikely (dictval.CharsetOffset == 0)) return;
break; break;
case OpCode_FDSelect: case OpCode_FDSelect:
dictval.FDSelectOffset = env.argStack.pop_uint (); dictval.FDSelectOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
case OpCode_Private: case OpCode_Private:
dictval.privateDictInfo.offset = env.argStack.pop_uint (); dictval.privateDictInfo.offset = env.argStack.pop_int ();
dictval.privateDictInfo.size = env.argStack.pop_uint (); dictval.privateDictInfo.size = env.argStack.pop_uint ();
env.clear_args (); env.clear_args ();
break; break;
@ -913,7 +913,7 @@ struct cff1_private_dict_values_base_t : dict_values_t<VAL>
} }
void fini () { dict_values_t<VAL>::fini (); } void fini () { dict_values_t<VAL>::fini (); }
unsigned int subrsOffset; int subrsOffset;
const CFF1Subrs *localSubrs; const CFF1Subrs *localSubrs;
}; };
@ -948,7 +948,7 @@ struct cff1_private_dict_opset_t : dict_opset_t
env.clear_args (); env.clear_args ();
break; break;
case OpCode_Subrs: case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint (); dictval.subrsOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
@ -990,7 +990,7 @@ struct cff1_private_dict_opset_subset_t : dict_opset_t
break; break;
case OpCode_Subrs: case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint (); dictval.subrsOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
@ -1090,8 +1090,8 @@ struct cff1
goto fail; goto fail;
hb_barrier (); hb_barrier ();
topDictIndex = &StructAtOffset<CFF1TopDictIndex> (nameIndex, nameIndex->get_size ()); topDictIndex = &StructAtOffsetOrNull<CFF1TopDictIndex> (nameIndex, nameIndex->get_size (), sc);
if ((topDictIndex == &Null (CFF1TopDictIndex)) || !topDictIndex->sanitize (&sc) || (topDictIndex->count == 0)) if (topDictIndex == &Null (CFF1TopDictIndex) || (topDictIndex->count == 0))
goto fail; goto fail;
hb_barrier (); hb_barrier ();
@ -1108,20 +1108,18 @@ struct cff1
charset = &Null (Charset); charset = &Null (Charset);
else else
{ {
charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset); charset = &StructAtOffsetOrNull<Charset> (cff, topDict.CharsetOffset, sc, &num_charset_entries);
if (unlikely ((charset == &Null (Charset)) || !charset->sanitize (&sc, &num_charset_entries))) goto fail; if (unlikely (charset == &Null (Charset))) goto fail;
hb_barrier ();
} }
fdCount = 1; fdCount = 1;
if (is_CID ()) if (is_CID ())
{ {
fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset); fdArray = &StructAtOffsetOrNull<CFF1FDArray> (cff, topDict.FDArrayOffset, sc);
fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset); fdSelect = &StructAtOffsetOrNull<CFF1FDSelect> (cff, topDict.FDSelectOffset, sc, fdArray->count);
if (unlikely ((fdArray == &Null (CFF1FDArray)) || !fdArray->sanitize (&sc) || if (unlikely (fdArray == &Null (CFF1FDArray) ||
(fdSelect == &Null (CFF1FDSelect)) || !fdSelect->sanitize (&sc, fdArray->count))) fdSelect == &Null (CFF1FDSelect)))
goto fail; goto fail;
hb_barrier ();
fdCount = fdArray->count; fdCount = fdArray->count;
} }
@ -1140,27 +1138,19 @@ struct cff1
{ {
if (!is_predef_encoding ()) if (!is_predef_encoding ())
{ {
encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset); encoding = &StructAtOffsetOrNull<Encoding> (cff, topDict.EncodingOffset, sc);
if (unlikely ((encoding == &Null (Encoding)) || !encoding->sanitize (&sc))) goto fail; if (unlikely (encoding == &Null (Encoding))) goto fail;
hb_barrier ();
} }
} }
stringIndex = &StructAtOffset<CFF1StringIndex> (topDictIndex, topDictIndex->get_size ()); stringIndex = &StructAtOffsetOrNull<CFF1StringIndex> (topDictIndex, topDictIndex->get_size (), sc);
if ((stringIndex == &Null (CFF1StringIndex)) || !stringIndex->sanitize (&sc)) if (stringIndex == &Null (CFF1StringIndex))
goto fail; goto fail;
hb_barrier ();
globalSubrs = &StructAtOffset<CFF1Subrs> (stringIndex, stringIndex->get_size ()); globalSubrs = &StructAtOffsetOrNull<CFF1Subrs> (stringIndex, stringIndex->get_size (), sc);
if ((globalSubrs != &Null (CFF1Subrs)) && !globalSubrs->sanitize (&sc)) charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset, sc);
if (charStrings == &Null (CFF1CharStrings))
goto fail; goto fail;
hb_barrier ();
charStrings = &StructAtOffsetOrNull<CFF1CharStrings> (cff, topDict.charStringsOffset);
if ((charStrings == &Null (CFF1CharStrings)) || unlikely (!charStrings->sanitize (&sc)))
goto fail;
hb_barrier ();
num_glyphs = charStrings->count; num_glyphs = charStrings->count;
if (num_glyphs != sc.get_num_glyphs ()) if (num_glyphs != sc.get_num_glyphs ())
@ -1188,19 +1178,13 @@ struct cff1
font->init (); font->init ();
if (unlikely (!font_interp.interpret (*font))) goto fail; if (unlikely (!font_interp.interpret (*font))) goto fail;
PRIVDICTVAL *priv = &privateDicts[i]; PRIVDICTVAL *priv = &privateDicts[i];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
hb_barrier ();
num_interp_env_t env2 (privDictStr); num_interp_env_t env2 (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env2);
priv->init (); priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) goto fail; if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset, sc);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
goto fail;
hb_barrier ();
} }
} }
else /* non-CID */ else /* non-CID */
@ -1208,18 +1192,13 @@ struct cff1
cff1_top_dict_values_t *font = &topDict; cff1_top_dict_values_t *font = &topDict;
PRIVDICTVAL *priv = &privateDicts[0]; PRIVDICTVAL *priv = &privateDicts[0];
const hb_ubytes_t privDictStr = StructAtOffset<UnsizedByteStr> (cff, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
hb_barrier ();
num_interp_env_t env (privDictStr); num_interp_env_t env (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL> priv_interp (env);
priv->init (); priv->init ();
if (unlikely (!priv_interp.interpret (*priv))) goto fail; if (unlikely (!priv_interp.interpret (*priv))) goto fail;
priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset); priv->localSubrs = &StructAtOffsetOrNull<CFF1Subrs> (&privDictStr, priv->subrsOffset, sc);
if (priv->localSubrs != &Null (CFF1Subrs) &&
unlikely (!priv->localSubrs->sanitize (&sc)))
goto fail;
hb_barrier (); hb_barrier ();
} }
@ -1437,7 +1416,7 @@ struct cff1
hb_sorted_vector_t<gname_t> *names = glyph_names.get_acquire (); hb_sorted_vector_t<gname_t> *names = glyph_names.get_acquire ();
if (unlikely (!names)) if (unlikely (!names))
{ {
names = (hb_sorted_vector_t<gname_t> *) hb_calloc (sizeof (hb_sorted_vector_t<gname_t>), 1); names = (hb_sorted_vector_t<gname_t> *) hb_calloc (1, sizeof (hb_sorted_vector_t<gname_t>));
if (likely (names)) if (likely (names))
{ {
names->init (); names->init ();

View File

@ -111,7 +111,7 @@ struct CFF2FDSelect
DEFINE_SIZE_MIN (2); DEFINE_SIZE_MIN (2);
}; };
struct CFF2VariationStore struct CFF2ItemVariationStore
{ {
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -122,11 +122,11 @@ struct CFF2VariationStore
varStore.sanitize (c)); varStore.sanitize (c));
} }
bool serialize (hb_serialize_context_t *c, const CFF2VariationStore *varStore) bool serialize (hb_serialize_context_t *c, const CFF2ItemVariationStore *varStore)
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
unsigned int size_ = varStore->get_size (); unsigned int size_ = varStore->get_size ();
CFF2VariationStore *dest = c->allocate_size<CFF2VariationStore> (size_); CFF2ItemVariationStore *dest = c->allocate_size<CFF2ItemVariationStore> (size_);
if (unlikely (!dest)) return_trace (false); if (unlikely (!dest)) return_trace (false);
hb_memcpy (dest, varStore, size_); hb_memcpy (dest, varStore, size_);
return_trace (true); return_trace (true);
@ -135,9 +135,9 @@ struct CFF2VariationStore
unsigned int get_size () const { return HBUINT16::static_size + size; } unsigned int get_size () const { return HBUINT16::static_size + size; }
HBUINT16 size; HBUINT16 size;
VariationStore varStore; ItemVariationStore varStore;
DEFINE_SIZE_MIN (2 + VariationStore::min_size); DEFINE_SIZE_MIN (2 + ItemVariationStore::min_size);
}; };
struct cff2_top_dict_values_t : top_dict_values_t<> struct cff2_top_dict_values_t : top_dict_values_t<>
@ -150,8 +150,8 @@ struct cff2_top_dict_values_t : top_dict_values_t<>
} }
void fini () { top_dict_values_t<>::fini (); } void fini () { top_dict_values_t<>::fini (); }
unsigned int vstoreOffset; int vstoreOffset;
unsigned int FDSelectOffset; int FDSelectOffset;
}; };
struct cff2_top_dict_opset_t : top_dict_opset_t<> struct cff2_top_dict_opset_t : top_dict_opset_t<>
@ -169,11 +169,11 @@ struct cff2_top_dict_opset_t : top_dict_opset_t<>
break; break;
case OpCode_vstore: case OpCode_vstore:
dictval.vstoreOffset = env.argStack.pop_uint (); dictval.vstoreOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
case OpCode_FDSelect: case OpCode_FDSelect:
dictval.FDSelectOffset = env.argStack.pop_uint (); dictval.FDSelectOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
@ -241,7 +241,7 @@ struct cff2_private_dict_values_base_t : dict_values_t<VAL>
} }
void fini () { dict_values_t<VAL>::fini (); } void fini () { dict_values_t<VAL>::fini (); }
unsigned int subrsOffset; int subrsOffset;
const CFF2Subrs *localSubrs; const CFF2Subrs *localSubrs;
unsigned int ivs; unsigned int ivs;
}; };
@ -295,7 +295,7 @@ struct cff2_private_dict_opset_t : dict_opset_t
env.clear_args (); env.clear_args ();
break; break;
case OpCode_Subrs: case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint (); dictval.subrsOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
case OpCode_vsindexdict: case OpCode_vsindexdict:
@ -344,7 +344,7 @@ struct cff2_private_dict_opset_subset_t : dict_opset_t
return; return;
case OpCode_Subrs: case OpCode_Subrs:
dictval.subrsOffset = env.argStack.pop_uint (); dictval.subrsOffset = env.argStack.pop_int ();
env.clear_args (); env.clear_args ();
break; break;
@ -426,18 +426,15 @@ struct cff2
if (unlikely (!top_interp.interpret (topDict))) goto fail; if (unlikely (!top_interp.interpret (topDict))) goto fail;
} }
globalSubrs = &StructAtOffset<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize); globalSubrs = &StructAtOffsetOrNull<CFF2Subrs> (cff2, cff2->topDict + cff2->topDictSize, sc);
varStore = &StructAtOffsetOrNull<CFF2VariationStore> (cff2, topDict.vstoreOffset); varStore = &StructAtOffsetOrNull<CFF2ItemVariationStore> (cff2, topDict.vstoreOffset, sc);
charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset); charStrings = &StructAtOffsetOrNull<CFF2CharStrings> (cff2, topDict.charStringsOffset, sc);
fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset); fdArray = &StructAtOffsetOrNull<CFF2FDArray> (cff2, topDict.FDArrayOffset, sc);
fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset); fdSelect = &StructAtOffsetOrNull<CFF2FDSelect> (cff2, topDict.FDSelectOffset, sc, fdArray->count);
if (((varStore != &Null (CFF2VariationStore)) && unlikely (!varStore->sanitize (&sc))) || if (charStrings == &Null (CFF2CharStrings) ||
(charStrings == &Null (CFF2CharStrings)) || unlikely (!charStrings->sanitize (&sc)) || globalSubrs == &Null (CFF2Subrs) ||
(globalSubrs == &Null (CFF2Subrs)) || unlikely (!globalSubrs->sanitize (&sc)) || fdArray == &Null (CFF2FDArray))
(fdArray == &Null (CFF2FDArray)) || unlikely (!fdArray->sanitize (&sc)) ||
!hb_barrier () ||
(((fdSelect != &Null (CFF2FDSelect)) && unlikely (!fdSelect->sanitize (&sc, fdArray->count)))))
goto fail; goto fail;
num_glyphs = charStrings->count; num_glyphs = charStrings->count;
@ -462,19 +459,13 @@ struct cff2
font->init (); font->init ();
if (unlikely (!font_interp.interpret (*font))) goto fail; if (unlikely (!font_interp.interpret (*font))) goto fail;
const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset).as_ubytes (font->privateDictInfo.size); const hb_ubytes_t privDictStr = StructAtOffsetOrNull<UnsizedByteStr> (cff2, font->privateDictInfo.offset, sc, font->privateDictInfo.size).as_ubytes (font->privateDictInfo.size);
if (unlikely (!privDictStr.sanitize (&sc))) goto fail;
hb_barrier ();
cff2_priv_dict_interp_env_t env2 (privDictStr); cff2_priv_dict_interp_env_t env2 (privDictStr);
dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2); dict_interpreter_t<PRIVOPSET, PRIVDICTVAL, cff2_priv_dict_interp_env_t> priv_interp (env2);
privateDicts[i].init (); privateDicts[i].init ();
if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail; if (unlikely (!priv_interp.interpret (privateDicts[i]))) goto fail;
privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset); privateDicts[i].localSubrs = &StructAtOffsetOrNull<CFF2Subrs> (&privDictStr[0], privateDicts[i].subrsOffset, sc);
if (privateDicts[i].localSubrs != &Null (CFF2Subrs) &&
unlikely (!privateDicts[i].localSubrs->sanitize (&sc)))
goto fail;
hb_barrier ();
} }
return; return;
@ -509,7 +500,7 @@ struct cff2
hb_blob_t *blob = nullptr; hb_blob_t *blob = nullptr;
cff2_top_dict_values_t topDict; cff2_top_dict_values_t topDict;
const CFF2Subrs *globalSubrs = nullptr; const CFF2Subrs *globalSubrs = nullptr;
const CFF2VariationStore *varStore = nullptr; const CFF2ItemVariationStore *varStore = nullptr;
const CFF2CharStrings *charStrings = nullptr; const CFF2CharStrings *charStrings = nullptr;
const CFF2FDArray *fdArray = nullptr; const CFF2FDArray *fdArray = nullptr;
const CFF2FDSelect *fdSelect = nullptr; const CFF2FDSelect *fdSelect = nullptr;

View File

@ -41,6 +41,30 @@
namespace OT { namespace OT {
static inline uint8_t unicode_to_macroman (hb_codepoint_t u)
{
uint16_t mapping[] = {
0x00C4, 0x00C5, 0x00C7, 0x00C9, 0x00D1, 0x00D6, 0x00DC, 0x00E1,
0x00E0, 0x00E2, 0x00E4, 0x00E3, 0x00E5, 0x00E7, 0x00E9, 0x00E8,
0x00EA, 0x00EB, 0x00ED, 0x00EC, 0x00EE, 0x00EF, 0x00F1, 0x00F3,
0x00F2, 0x00F4, 0x00F6, 0x00F5, 0x00FA, 0x00F9, 0x00FB, 0x00FC,
0x2020, 0x00B0, 0x00A2, 0x00A3, 0x00A7, 0x2022, 0x00B6, 0x00DF,
0x00AE, 0x00A9, 0x2122, 0x00B4, 0x00A8, 0x2260, 0x00C6, 0x00D8,
0x221E, 0x00B1, 0x2264, 0x2265, 0x00A5, 0x00B5, 0x2202, 0x2211,
0x220F, 0x03C0, 0x222B, 0x00AA, 0x00BA, 0x03A9, 0x00E6, 0x00F8,
0x00BF, 0x00A1, 0x00AC, 0x221A, 0x0192, 0x2248, 0x2206, 0x00AB,
0x00BB, 0x2026, 0x00A0, 0x00C0, 0x00C3, 0x00D5, 0x0152, 0x0153,
0x2013, 0x2014, 0x201C, 0x201D, 0x2018, 0x2019, 0x00F7, 0x25CA,
0x00FF, 0x0178, 0x2044, 0x20AC, 0x2039, 0x203A, 0xFB01, 0xFB02,
0x2021, 0x00B7, 0x201A, 0x201E, 0x2030, 0x00C2, 0x00CA, 0x00C1,
0x00CB, 0x00C8, 0x00CD, 0x00CE, 0x00CF, 0x00CC, 0x00D3, 0x00D4,
0xF8FF, 0x00D2, 0x00DA, 0x00DB, 0x00D9, 0x0131, 0x02C6, 0x02DC,
0x00AF, 0x02D8, 0x02D9, 0x02DA, 0x00B8, 0x02DD, 0x02DB, 0x02C7
};
uint16_t *c = hb_bsearch (u, mapping, ARRAY_LENGTH (mapping), sizeof (mapping[0]),
_hb_cmp_operator<uint16_t, uint16_t>);
return c ? (c - mapping) + 0x7F : 0;
}
struct CmapSubtableFormat0 struct CmapSubtableFormat0
{ {
@ -1465,8 +1489,11 @@ struct EncodingRecord
int ret; int ret;
ret = platformID.cmp (other.platformID); ret = platformID.cmp (other.platformID);
if (ret) return ret; if (ret) return ret;
ret = encodingID.cmp (other.encodingID); if (other.encodingID != 0xFFFF)
if (ret) return ret; {
ret = encodingID.cmp (other.encodingID);
if (ret) return ret;
}
return 0; return 0;
} }
@ -1814,9 +1841,13 @@ struct cmap
c->plan)); c->plan));
} }
const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const const CmapSubtable *find_best_subtable (bool *symbol = nullptr,
bool *mac = nullptr,
bool *macroman = nullptr) const
{ {
if (symbol) *symbol = false; if (symbol) *symbol = false;
if (mac) *mac = false;
if (macroman) *macroman = false;
const CmapSubtable *subtable; const CmapSubtable *subtable;
@ -1841,6 +1872,20 @@ struct cmap
if ((subtable = this->find_subtable (0, 1))) return subtable; if ((subtable = this->find_subtable (0, 1))) return subtable;
if ((subtable = this->find_subtable (0, 0))) return subtable; if ((subtable = this->find_subtable (0, 0))) return subtable;
/* MacRoman subtable. */
if ((subtable = this->find_subtable (1, 0)))
{
if (mac) *mac = true;
if (macroman) *macroman = true;
return subtable;
}
/* Any other Mac subtable; we just map ASCII for these. */
if ((subtable = this->find_subtable (1, 0xFFFF)))
{
if (mac) *mac = true;
return subtable;
}
/* Meh. */ /* Meh. */
return &Null (CmapSubtable); return &Null (CmapSubtable);
} }
@ -1852,8 +1897,8 @@ struct cmap
accelerator_t (hb_face_t *face) accelerator_t (hb_face_t *face)
{ {
this->table = hb_sanitize_context_t ().reference_table<cmap> (face); this->table = hb_sanitize_context_t ().reference_table<cmap> (face);
bool symbol; bool symbol, mac, macroman;
this->subtable = table->find_best_subtable (&symbol); this->subtable = table->find_best_subtable (&symbol, &mac, &macroman);
this->subtable_uvs = &Null (CmapSubtableFormat14); this->subtable_uvs = &Null (CmapSubtableFormat14);
{ {
const CmapSubtable *st = table->find_subtable (0, 5); const CmapSubtable *st = table->find_subtable (0, 5);
@ -1862,6 +1907,7 @@ struct cmap
} }
this->get_glyph_data = subtable; this->get_glyph_data = subtable;
#ifndef HB_NO_CMAP_LEGACY_SUBTABLES
if (unlikely (symbol)) if (unlikely (symbol))
{ {
switch ((unsigned) face->table.OS2->get_font_page ()) { switch ((unsigned) face->table.OS2->get_font_page ()) {
@ -1881,7 +1927,16 @@ struct cmap
break; break;
} }
} }
else if (unlikely (macroman))
{
this->get_glyph_funcZ = get_glyph_from_macroman<CmapSubtable>;
}
else if (unlikely (mac))
{
this->get_glyph_funcZ = get_glyph_from_ascii<CmapSubtable>;
}
else else
#endif
{ {
switch (subtable->u.format) { switch (subtable->u.format) {
/* Accelerate format 4 and format 12. */ /* Accelerate format 4 and format 12. */
@ -1924,7 +1979,7 @@ struct cmap
hb_codepoint_t *glyph, hb_codepoint_t *glyph,
cache_t *cache = nullptr) const cache_t *cache = nullptr) const
{ {
if (unlikely (!this->get_glyph_funcZ)) return 0; if (unlikely (!this->get_glyph_funcZ)) return false;
return _cached_get (unicode, glyph, cache); return _cached_get (unicode, glyph, cache);
} }
@ -2006,6 +2061,28 @@ struct cmap
return false; return false;
} }
template <typename Type>
HB_INTERNAL static bool get_glyph_from_ascii (const void *obj,
hb_codepoint_t codepoint,
hb_codepoint_t *glyph)
{
const Type *typed_obj = (const Type *) obj;
return codepoint < 0x80 && typed_obj->get_glyph (codepoint, glyph);
}
template <typename Type>
HB_INTERNAL static bool get_glyph_from_macroman (const void *obj,
hb_codepoint_t codepoint,
hb_codepoint_t *glyph)
{
if (get_glyph_from_ascii<Type> (obj, codepoint, glyph))
return true;
const Type *typed_obj = (const Type *) obj;
unsigned c = unicode_to_macroman (codepoint);
return c && typed_obj->get_glyph (c, glyph);
}
private: private:
hb_nonnull_ptr_t<const CmapSubtable> subtable; hb_nonnull_ptr_t<const CmapSubtable> subtable;
hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs; hb_nonnull_ptr_t<const CmapSubtableFormat14> subtable_uvs;
@ -2035,28 +2112,6 @@ struct cmap
return &(this+result.subtable); return &(this+result.subtable);
} }
const EncodingRecord *find_encodingrec (unsigned int platform_id,
unsigned int encoding_id) const
{
EncodingRecord key;
key.platformID = platform_id;
key.encodingID = encoding_id;
return encodingRecord.as_array ().bsearch (key);
}
bool find_subtable (unsigned format) const
{
auto it =
+ hb_iter (encodingRecord)
| hb_map (&EncodingRecord::subtable)
| hb_map (hb_add (this))
| hb_filter ([&] (const CmapSubtable& _) { return _.u.format == format; })
;
return it.len ();
}
public: public:
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const

View File

@ -208,12 +208,12 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE)
const OT::HVAR &HVAR = *hmtx.var_table; const OT::HVAR &HVAR = *hmtx.var_table;
const OT::VariationStore &varStore = &HVAR + HVAR.varStore; const OT::ItemVariationStore &varStore = &HVAR + HVAR.varStore;
OT::VariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr; OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords * count >= 128 ? varStore.create_cache () : nullptr;
bool use_cache = font->num_coords; bool use_cache = font->num_coords;
#else #else
OT::VariationStore::cache_t *varStore_cache = nullptr; OT::ItemVariationStore::cache_t *varStore_cache = nullptr;
bool use_cache = false; bool use_cache = false;
#endif #endif
@ -277,7 +277,7 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
} }
#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE)
OT::VariationStore::destroy_cache (varStore_cache); OT::ItemVariationStore::destroy_cache (varStore_cache);
#endif #endif
if (font->x_strength && !font->embolden_in_place) if (font->x_strength && !font->embolden_in_place)
@ -313,10 +313,10 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
{ {
#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE)
const OT::VVAR &VVAR = *vmtx.var_table; const OT::VVAR &VVAR = *vmtx.var_table;
const OT::VariationStore &varStore = &VVAR + VVAR.varStore; const OT::ItemVariationStore &varStore = &VVAR + VVAR.varStore;
OT::VariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr; OT::ItemVariationStore::cache_t *varStore_cache = font->num_coords ? varStore.create_cache () : nullptr;
#else #else
OT::VariationStore::cache_t *varStore_cache = nullptr; OT::ItemVariationStore::cache_t *varStore_cache = nullptr;
#endif #endif
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
@ -327,7 +327,7 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
} }
#if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE) #if !defined(HB_NO_VAR) && !defined(HB_NO_OT_FONT_ADVANCE_CACHE)
OT::VariationStore::destroy_cache (varStore_cache); OT::ItemVariationStore::destroy_cache (varStore_cache);
#endif #endif
} }
else else

View File

@ -145,6 +145,29 @@ struct hmtxvmtx
table->minTrailingBearing = min_rsb; table->minTrailingBearing = min_rsb;
table->maxExtent = max_extent; table->maxExtent = max_extent;
} }
if (T::is_horizontal)
{
const auto &OS2 = *c->plan->source->table.OS2;
if (OS2.has_data () &&
table->ascender == OS2.sTypoAscender &&
table->descender == OS2.sTypoDescender &&
table->lineGap == OS2.sTypoLineGap)
{
table->ascender = static_cast<int> (roundf (OS2.sTypoAscender +
MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_ASCENDER,
c->plan->normalized_coords.arrayZ,
c->plan->normalized_coords.length)));
table->descender = static_cast<int> (roundf (OS2.sTypoDescender +
MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_DESCENDER,
c->plan->normalized_coords.arrayZ,
c->plan->normalized_coords.length)));
table->lineGap = static_cast<int> (roundf (OS2.sTypoLineGap +
MVAR.get_var (HB_OT_METRICS_TAG_HORIZONTAL_LINE_GAP,
c->plan->normalized_coords.arrayZ,
c->plan->normalized_coords.length)));
}
}
} }
#endif #endif
@ -374,7 +397,7 @@ struct hmtxvmtx
unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph, unsigned get_advance_with_var_unscaled (hb_codepoint_t glyph,
hb_font_t *font, hb_font_t *font,
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ {
unsigned int advance = get_advance_without_var_unscaled (glyph); unsigned int advance = get_advance_without_var_unscaled (glyph);

View File

@ -46,6 +46,12 @@ struct BaseCoordFormat1
return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
} }
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
return_trace ((bool) c->serializer->embed (*this));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -67,6 +73,17 @@ struct BaseCoordFormat2
return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate); return HB_DIRECTION_IS_HORIZONTAL (direction) ? font->em_scale_y (coordinate) : font->em_scale_x (coordinate);
} }
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
return_trace (c->serializer->check_assign (out->referenceGlyph,
c->plan->glyph_map->get (referenceGlyph),
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -86,7 +103,7 @@ struct BaseCoordFormat2
struct BaseCoordFormat3 struct BaseCoordFormat3
{ {
hb_position_t get_coord (hb_font_t *font, hb_position_t get_coord (hb_font_t *font,
const VariationStore &var_store, const ItemVariationStore &var_store,
hb_direction_t direction) const hb_direction_t direction) const
{ {
const Device &device = this+deviceTable; const Device &device = this+deviceTable;
@ -96,6 +113,23 @@ struct BaseCoordFormat3
: font->em_scale_x (coordinate) + device.get_x_delta (font, var_store); : font->em_scale_x (coordinate) + device.get_x_delta (font, var_store);
} }
void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
{
unsigned varidx = (this+deviceTable).get_variation_index ();
varidx_set.add (varidx);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
return_trace (out->deviceTable.serialize_copy (c->serializer, deviceTable,
this, 0,
hb_serialize_context_t::Head,
&c->plan->base_variation_idx_map));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -120,7 +154,7 @@ struct BaseCoord
bool has_data () const { return u.format; } bool has_data () const { return u.format; }
hb_position_t get_coord (hb_font_t *font, hb_position_t get_coord (hb_font_t *font,
const VariationStore &var_store, const ItemVariationStore &var_store,
hb_direction_t direction) const hb_direction_t direction) const
{ {
switch (u.format) { switch (u.format) {
@ -131,6 +165,27 @@ struct BaseCoord
} }
} }
void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
{
switch (u.format) {
case 3: u.format3.collect_variation_indices (varidx_set);
default:return;
}
}
template <typename context_t, typename ...Ts>
typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const
{
if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value ();
TRACE_DISPATCH (this, u.format);
switch (u.format) {
case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...));
case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...));
case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...));
default:return_trace (c->default_return_value ());
}
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -161,12 +216,37 @@ struct FeatMinMaxRecord
bool has_data () const { return tag; } bool has_data () const { return tag; }
hb_tag_t get_feature_tag () const { return tag; }
void get_min_max (const BaseCoord **min, const BaseCoord **max) const void get_min_max (const BaseCoord **min, const BaseCoord **max) const
{ {
if (likely (min)) *min = &(this+minCoord); if (likely (min)) *min = &(this+minCoord);
if (likely (max)) *max = &(this+maxCoord); if (likely (max)) *max = &(this+maxCoord);
} }
void collect_variation_indices (const hb_subset_plan_t* plan,
const void *base,
hb_set_t& varidx_set /* OUT */) const
{
if (!plan->layout_features.has (tag))
return;
(base+minCoord).collect_variation_indices (varidx_set);
(base+maxCoord).collect_variation_indices (varidx_set);
}
bool subset (hb_subset_context_t *c,
const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
if (!(out->minCoord.serialize_subset (c, minCoord, base)))
return_trace (false);
return_trace (out->maxCoord.serialize_subset (c, maxCoord, base));
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -206,6 +286,39 @@ struct MinMax
} }
} }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{
(this+minCoord).collect_variation_indices (varidx_set);
(this+maxCoord).collect_variation_indices (varidx_set);
for (const FeatMinMaxRecord& record : featMinMaxRecords)
record.collect_variation_indices (plan, this, varidx_set);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
if (!(out->minCoord.serialize_subset (c, minCoord, this)) ||
!(out->maxCoord.serialize_subset (c, maxCoord, this)))
return_trace (false);
unsigned len = 0;
for (const FeatMinMaxRecord& _ : featMinMaxRecords)
{
hb_tag_t feature_tag = _.get_feature_tag ();
if (!c->plan->layout_features.has (feature_tag))
continue;
if (!_.subset (c, this)) return false;
len++;
}
return_trace (c->serializer->check_assign (out->featMinMaxRecords.len, len,
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -240,6 +353,26 @@ struct BaseValues
return this+baseCoords[baseline_tag_index]; return this+baseCoords[baseline_tag_index];
} }
void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const
{
for (const auto& _ : baseCoords)
(this+_).collect_variation_indices (varidx_set);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->defaultIndex = defaultIndex;
for (const auto& _ : baseCoords)
if (!subset_offset_array (c, out->baseCoords, this) (_))
return_trace (false);
return_trace (bool (out->baseCoords));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -270,6 +403,20 @@ struct BaseLangSysRecord
const MinMax &get_min_max () const { return this+minMax; } const MinMax &get_min_max () const { return this+minMax; }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{ (this+minMax).collect_variation_indices (plan, varidx_set); }
bool subset (hb_subset_context_t *c,
const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
return_trace (out->minMax.serialize_subset (c, minMax, base));
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -300,6 +447,35 @@ struct BaseScript
bool has_values () const { return baseValues; } bool has_values () const { return baseValues; }
bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ } bool has_min_max () const { return defaultMinMax; /* TODO What if only per-language is present? */ }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{
(this+baseValues).collect_variation_indices (varidx_set);
(this+defaultMinMax).collect_variation_indices (plan, varidx_set);
for (const BaseLangSysRecord& _ : baseLangSysRecords)
_.collect_variation_indices (plan, varidx_set);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
if (baseValues && !out->baseValues.serialize_subset (c, baseValues, this))
return_trace (false);
if (defaultMinMax && !out->defaultMinMax.serialize_subset (c, defaultMinMax, this))
return_trace (false);
for (const auto& _ : baseLangSysRecords)
if (!_.subset (c, this)) return_trace (false);
return_trace (c->serializer->check_assign (out->baseLangSysRecords.len, baseLangSysRecords.len,
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -332,9 +508,31 @@ struct BaseScriptRecord
bool has_data () const { return baseScriptTag; } bool has_data () const { return baseScriptTag; }
hb_tag_t get_script_tag () const { return baseScriptTag; }
const BaseScript &get_base_script (const BaseScriptList *list) const const BaseScript &get_base_script (const BaseScriptList *list) const
{ return list+baseScript; } { return list+baseScript; }
void collect_variation_indices (const hb_subset_plan_t* plan,
const void* list,
hb_set_t& varidx_set /* OUT */) const
{
if (!plan->layout_scripts.has (baseScriptTag))
return;
(list+baseScript).collect_variation_indices (plan, varidx_set);
}
bool subset (hb_subset_context_t *c,
const void *base) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
return_trace (out->baseScript.serialize_subset (c, baseScript, base));
}
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -361,6 +559,33 @@ struct BaseScriptList
return record->has_data () ? record->get_base_script (this) : Null (BaseScript); return record->has_data () ? record->get_base_script (this) : Null (BaseScript);
} }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{
for (const BaseScriptRecord& _ : baseScriptRecords)
_.collect_variation_indices (plan, this, varidx_set);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
unsigned len = 0;
for (const BaseScriptRecord& _ : baseScriptRecords)
{
hb_tag_t script_tag = _.get_script_tag ();
if (!c->plan->layout_scripts.has (script_tag))
continue;
if (!_.subset (c, this)) return false;
len++;
}
return_trace (c->serializer->check_assign (out->baseScriptRecords.len, len,
HB_SERIALIZE_ERROR_INT_OVERFLOW));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -422,6 +647,20 @@ struct Axis
return true; return true;
} }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{ (this+baseScriptList).collect_variation_indices (plan, varidx_set); }
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->embed (*this);
if (unlikely (!out)) return_trace (false);
out->baseTagList.serialize_copy (c->serializer, baseTagList, this);
return_trace (out->baseScriptList.serialize_subset (c, baseScriptList, this));
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -453,8 +692,41 @@ struct BASE
const Axis &get_axis (hb_direction_t direction) const const Axis &get_axis (hb_direction_t direction) const
{ return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; } { return HB_DIRECTION_IS_VERTICAL (direction) ? this+vAxis : this+hAxis; }
const VariationStore &get_var_store () const bool has_var_store () const
{ return version.to_int () < 0x00010001u ? Null (VariationStore) : this+varStore; } { return version.to_int () >= 0x00010001u && varStore != 0; }
const ItemVariationStore &get_var_store () const
{ return version.to_int () < 0x00010001u ? Null (ItemVariationStore) : this+varStore; }
void collect_variation_indices (const hb_subset_plan_t* plan,
hb_set_t& varidx_set /* OUT */) const
{
(this+hAxis).collect_variation_indices (plan, varidx_set);
(this+vAxis).collect_variation_indices (plan, varidx_set);
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);
auto *out = c->serializer->start_embed (*this);
if (unlikely (!out || !c->serializer->extend_min (out))) return_trace (false);
out->version = version;
if (hAxis && !out->hAxis.serialize_subset (c, hAxis, this))
return_trace (false);
if (vAxis && !out->vAxis.serialize_subset (c, vAxis, this))
return_trace (false);
if (has_var_store ())
{
if (!c->serializer->allocate_size<Offset32To<ItemVariationStore>> (Offset32To<ItemVariationStore>::static_size))
return_trace (false);
return_trace (out->varStore.serialize_subset (c, varStore, this, c->plan->base_varstore_inner_maps.as_array ()));
}
return_trace (true);
}
bool get_baseline (hb_font_t *font, bool get_baseline (hb_font_t *font,
hb_tag_t baseline_tag, hb_tag_t baseline_tag,
@ -487,7 +759,7 @@ struct BASE
&min_coord, &max_coord)) &min_coord, &max_coord))
return false; return false;
const VariationStore &var_store = get_var_store (); const ItemVariationStore &var_store = get_var_store ();
if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction); if (likely (min && min_coord)) *min = min_coord->get_coord (font, var_store, direction);
if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction); if (likely (max && max_coord)) *max = max_coord->get_coord (font, var_store, direction);
return true; return true;
@ -510,7 +782,7 @@ struct BASE
* of BASE table (may be NULL) */ * of BASE table (may be NULL) */
Offset16To<Axis>vAxis; /* Offset to vertical Axis table, from beginning Offset16To<Axis>vAxis; /* Offset to vertical Axis table, from beginning
* of BASE table (may be NULL) */ * of BASE table (may be NULL) */
Offset32To<VariationStore> Offset32To<ItemVariationStore>
varStore; /* Offset to the table of Item Variation varStore; /* Offset to the table of Item Variation
* Store--from beginning of BASE * Store--from beginning of BASE
* header (may be NULL). Introduced * header (may be NULL). Introduced

View File

@ -188,7 +188,7 @@ struct hb_subset_layout_context_t :
unsigned lookup_index_count; unsigned lookup_index_count;
}; };
struct VariationStore; struct ItemVariationStore;
struct hb_collect_variation_indices_context_t : struct hb_collect_variation_indices_context_t :
hb_dispatch_context_t<hb_collect_variation_indices_context_t> hb_dispatch_context_t<hb_collect_variation_indices_context_t>
{ {
@ -3036,7 +3036,7 @@ struct VarData
DEFINE_SIZE_ARRAY (6, regionIndices); DEFINE_SIZE_ARRAY (6, regionIndices);
}; };
struct VariationStore struct ItemVariationStore
{ {
friend struct item_variations_t; friend struct item_variations_t;
using cache_t = VarRegionList::cache_t; using cache_t = VarRegionList::cache_t;
@ -3141,7 +3141,7 @@ struct VariationStore
} }
bool serialize (hb_serialize_context_t *c, bool serialize (hb_serialize_context_t *c,
const VariationStore *src, const ItemVariationStore *src,
const hb_array_t <const hb_inc_bimap_t> &inner_maps) const hb_array_t <const hb_inc_bimap_t> &inner_maps)
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
@ -3197,7 +3197,7 @@ struct VariationStore
return_trace (true); return_trace (true);
} }
VariationStore *copy (hb_serialize_context_t *c) const ItemVariationStore *copy (hb_serialize_context_t *c) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
auto *out = c->start_embed (this); auto *out = c->start_embed (this);
@ -3227,7 +3227,7 @@ struct VariationStore
return_trace (false); return_trace (false);
#endif #endif
VariationStore *varstore_prime = c->serializer->start_embed<VariationStore> (); ItemVariationStore *varstore_prime = c->serializer->start_embed<ItemVariationStore> ();
if (unlikely (!varstore_prime)) return_trace (false); if (unlikely (!varstore_prime)) return_trace (false);
varstore_prime->serialize (c->serializer, this, inner_maps); varstore_prime->serialize (c->serializer, this, inner_maps);
@ -4030,13 +4030,13 @@ struct VariationDevice
private: private:
hb_position_t get_x_delta (hb_font_t *font, hb_position_t get_x_delta (hb_font_t *font,
const VariationStore &store, const ItemVariationStore &store,
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ return font->em_scalef_x (get_delta (font, store, store_cache)); } { return font->em_scalef_x (get_delta (font, store, store_cache)); }
hb_position_t get_y_delta (hb_font_t *font, hb_position_t get_y_delta (hb_font_t *font,
const VariationStore &store, const ItemVariationStore &store,
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ return font->em_scalef_y (get_delta (font, store, store_cache)); } { return font->em_scalef_y (get_delta (font, store, store_cache)); }
VariationDevice* copy (hb_serialize_context_t *c, VariationDevice* copy (hb_serialize_context_t *c,
@ -4070,10 +4070,10 @@ struct VariationDevice
private: private:
float get_delta (hb_font_t *font, float get_delta (hb_font_t *font,
const VariationStore &store, const ItemVariationStore &store,
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ {
return store.get_delta (varIdx, font->coords, font->num_coords, (VariationStore::cache_t *) store_cache); return store.get_delta (varIdx, font->coords, font->num_coords, (ItemVariationStore::cache_t *) store_cache);
} }
protected: protected:
@ -4097,8 +4097,8 @@ struct DeviceHeader
struct Device struct Device
{ {
hb_position_t get_x_delta (hb_font_t *font, hb_position_t get_x_delta (hb_font_t *font,
const VariationStore &store=Null (VariationStore), const ItemVariationStore &store=Null (ItemVariationStore),
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ {
switch (u.b.format) switch (u.b.format)
{ {
@ -4115,8 +4115,8 @@ struct Device
} }
} }
hb_position_t get_y_delta (hb_font_t *font, hb_position_t get_y_delta (hb_font_t *font,
const VariationStore &store=Null (VariationStore), const ItemVariationStore &store=Null (ItemVariationStore),
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ {
switch (u.b.format) switch (u.b.format)
{ {

View File

@ -708,8 +708,8 @@ struct hb_ot_apply_context_t :
recurse_func_t recurse_func = nullptr; recurse_func_t recurse_func = nullptr;
const GDEF &gdef; const GDEF &gdef;
const GDEF::accelerator_t &gdef_accel; const GDEF::accelerator_t &gdef_accel;
const VariationStore &var_store; const ItemVariationStore &var_store;
VariationStore::cache_t *var_store_cache; ItemVariationStore::cache_t *var_store_cache;
hb_set_digest_t digest; hb_set_digest_t digest;
hb_direction_t direction; hb_direction_t direction;
@ -723,7 +723,6 @@ struct hb_ot_apply_context_t :
bool auto_zwj = true; bool auto_zwj = true;
bool per_syllable = false; bool per_syllable = false;
bool random = false; bool random = false;
uint32_t random_state = 1;
unsigned new_syllables = (unsigned) -1; unsigned new_syllables = (unsigned) -1;
signed last_base = -1; // GPOS uses signed last_base = -1; // GPOS uses
@ -766,7 +765,7 @@ struct hb_ot_apply_context_t :
~hb_ot_apply_context_t () ~hb_ot_apply_context_t ()
{ {
#ifndef HB_NO_VAR #ifndef HB_NO_VAR
VariationStore::destroy_cache (var_store_cache); ItemVariationStore::destroy_cache (var_store_cache);
#endif #endif
} }
@ -788,8 +787,8 @@ struct hb_ot_apply_context_t :
uint32_t random_number () uint32_t random_number ()
{ {
/* http://www.cplusplus.com/reference/random/minstd_rand/ */ /* http://www.cplusplus.com/reference/random/minstd_rand/ */
random_state = random_state * 48271 % 2147483647; buffer->random_state = buffer->random_state * 48271 % 2147483647;
return random_state; return buffer->random_state;
} }
bool match_properties_mark (hb_codepoint_t glyph, bool match_properties_mark (hb_codepoint_t glyph,

View File

@ -2127,7 +2127,7 @@ hb_ot_layout_get_font_extents (hb_font_t *font,
hb_tag_t language_tag, hb_tag_t language_tag,
hb_font_extents_t *extents) hb_font_extents_t *extents)
{ {
hb_position_t min, max; hb_position_t min = 0, max = 0;
if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE, if (font->face->table.BASE->get_min_max (font, direction, script_tag, language_tag, HB_TAG_NONE,
&min, &max)) &min, &max))
{ {

View File

@ -344,27 +344,20 @@ struct MathKern
const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount; const MathValueRecord* kernValue = mathValueRecordsZ.arrayZ + heightCount;
int sign = font->y_scale < 0 ? -1 : +1; int sign = font->y_scale < 0 ? -1 : +1;
/* The description of the MathKern table is a ambiguous, but interpreting /* According to OpenType spec (v1.9), except for the boundary cases, the index
* "between the two heights found at those indexes" for 0 < i < len as * chosen for kern value should be i such that
* * correctionHeight[i-1] <= correction_height < correctionHeight[i]
* correctionHeight[i-1] < correction_height <= correctionHeight[i] * We can use the binary search algorithm of std::upper_bound(). Or, we can
* * use the internal hb_bsearch_impl.
* makes the result consistent with the limit cases and we can just use the
* binary search algorithm of std::upper_bound:
*/ */
unsigned int i = 0; unsigned int pos;
unsigned int count = heightCount; auto cmp = +[](const void* key, const void* p,
while (count > 0) int sign, hb_font_t* font, const MathKern* mathKern) -> int {
{ return sign * *(hb_position_t*)key - sign * ((MathValueRecord*)p)->get_y_value(font, mathKern);
unsigned int half = count / 2; };
hb_position_t height = correctionHeight[i + half].get_y_value (font, this); unsigned int i = hb_bsearch_impl(&pos, correction_height, correctionHeight,
if (sign * height < sign * correction_height) heightCount, MathValueRecord::static_size,
{ cmp, sign, font, this) ? pos + 1 : pos;
i += half + 1;
count -= half + 1;
} else
count = half;
}
return kernValue[i].get_x_value (font, this); return kernValue[i].get_x_value (font, this);
} }

View File

@ -223,7 +223,7 @@ struct OS2
} }
} }
return num ? (unsigned) roundf (total_width / num) : 0; return num ? (unsigned) roundf ((double) total_width / (double) num) : 0;
} }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c) const
@ -284,12 +284,12 @@ struct OS2
os2_prime->usWidthClass = width_class; os2_prime->usWidthClass = width_class;
} }
if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES)
return_trace (true);
os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ()); os2_prime->usFirstCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_min ());
os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ()); os2_prime->usLastCharIndex = hb_min (0xFFFFu, c->plan->unicodes.get_max ());
if (c->plan->flags & HB_SUBSET_FLAGS_NO_PRUNE_UNICODE_RANGES)
return_trace (true);
_update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange); _update_unicode_ranges (&c->plan->unicodes, os2_prime->ulUnicodeRange);
return_trace (true); return_trace (true);

View File

@ -155,7 +155,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
#endif #endif
bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face); bool has_gpos = !disable_gpos && hb_ot_layout_has_positioning (face);
if (false) if (false)
; {}
#ifndef HB_NO_AAT_SHAPE #ifndef HB_NO_AAT_SHAPE
/* Prefer GPOS over kerx if GSUB is present; /* Prefer GPOS over kerx if GSUB is present;
* https://github.com/harfbuzz/harfbuzz/issues/3008 */ * https://github.com/harfbuzz/harfbuzz/issues/3008 */
@ -167,15 +167,16 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos)) if (!plan.apply_kerx && (!has_gpos_kern || !plan.apply_gpos))
{ {
if (false) {}
#ifndef HB_NO_AAT_SHAPE #ifndef HB_NO_AAT_SHAPE
if (has_kerx) else if (has_kerx)
plan.apply_kerx = true; plan.apply_kerx = true;
else
#endif #endif
#ifndef HB_NO_OT_KERN #ifndef HB_NO_OT_KERN
if (hb_ot_layout_has_kerning (face)) else if (hb_ot_layout_has_kerning (face))
plan.apply_kern = true; plan.apply_kern = true;
#endif #endif
else {}
} }
plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern); plan.apply_fallback_kern = !(plan.apply_gpos || plan.apply_kerx || plan.apply_kern);

View File

@ -560,9 +560,9 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%u,%u,%u)", DEBUG_MSG (ARABIC, nullptr, "%s stretch at (%u,%u,%u)",
step == MEASURE ? "measuring" : "cutting", context, start, end); step == MEASURE ? "measuring" : "cutting", context, start, end);
DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %d", start - context, w_total); DEBUG_MSG (ARABIC, nullptr, "rest of word: count=%u width %" PRId32, start - context, w_total);
DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%d", n_fixed, w_fixed); DEBUG_MSG (ARABIC, nullptr, "fixed tiles: count=%d width=%" PRId32, n_fixed, w_fixed);
DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%d", n_repeating, w_repeating); DEBUG_MSG (ARABIC, nullptr, "repeating tiles: count=%d width=%" PRId32, n_repeating, w_repeating);
/* Number of additional times to repeat each repeating tile. */ /* Number of additional times to repeat each repeating tile. */
int n_copies = 0; int n_copies = 0;
@ -602,7 +602,7 @@ apply_stch (const hb_ot_shape_plan_t *plan HB_UNUSED,
if (info[k - 1].arabic_shaping_action() == STCH_REPEATING) if (info[k - 1].arabic_shaping_action() == STCH_REPEATING)
repeat += n_copies; repeat += n_copies;
DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %u; j=%u", DEBUG_MSG (ARABIC, nullptr, "appending %u copies of glyph %" PRIu32 "; j=%u",
repeat, info[k - 1].codepoint, j); repeat, info[k - 1].codepoint, j);
pos[k - 1].x_advance = 0; pos[k - 1].x_advance = 0;
for (unsigned int n = 0; n < repeat; n++) for (unsigned int n = 0; n < repeat; n++)

View File

@ -349,7 +349,7 @@ struct AxisValueFormat4
struct AxisValue struct AxisValue
{ {
bool get_value (unsigned int axis_index) const float get_value (unsigned int axis_index) const
{ {
switch (u.format) switch (u.format)
{ {
@ -357,7 +357,7 @@ struct AxisValue
case 2: return u.format2.get_value (); case 2: return u.format2.get_value ();
case 3: return u.format3.get_value (); case 3: return u.format3.get_value ();
case 4: return u.format4.get_axis_record (axis_index).get_value (); case 4: return u.format4.get_axis_record (axis_index).get_value ();
default:return 0; default:return 0.f;
} }
} }
@ -485,7 +485,7 @@ struct STAT
hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets (); hb_array_t<const Offset16To<AxisValue>> axis_values = get_axis_value_offsets ();
for (unsigned int i = 0; i < axis_values.length; i++) for (unsigned int i = 0; i < axis_values.length; i++)
{ {
const AxisValue& axis_value = this+axis_values[i]; const AxisValue& axis_value = this+offsetToAxisValueOffsets+axis_values[i];
if (axis_value.get_axis_index () == axis_index) if (axis_value.get_axis_index () == axis_index)
{ {
if (value) if (value)

View File

@ -6,8 +6,8 @@
* *
* on files with these headers: * on files with these headers:
* *
* <meta name="updated_at" content="2022-09-30 11:47 PM" /> * <meta name="updated_at" content="2023-09-30 01:21 AM" />
* File-Date: 2023-08-02 * File-Date: 2024-03-07
*/ */
#ifndef HB_OT_TAG_TABLE_HH #ifndef HB_OT_TAG_TABLE_HH
@ -31,7 +31,7 @@ static const LangTag ot_languages2[] = {
{HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */ {HB_TAG('b','i',' ',' '), HB_TAG('B','I','S',' ')}, /* Bislama */
{HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */ {HB_TAG('b','i',' ',' '), HB_TAG('C','P','P',' ')}, /* Bislama -> Creoles */
{HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */ {HB_TAG('b','m',' ',' '), HB_TAG('B','M','B',' ')}, /* Bambara (Bamanankan) */
{HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bengali */ {HB_TAG('b','n',' ',' '), HB_TAG('B','E','N',' ')}, /* Bangla */
{HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */ {HB_TAG('b','o',' ',' '), HB_TAG('T','I','B',' ')}, /* Tibetan */
{HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */ {HB_TAG('b','r',' ',' '), HB_TAG('B','R','E',' ')}, /* Breton */
{HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */ {HB_TAG('b','s',' ',' '), HB_TAG('B','O','S',' ')}, /* Bosnian */
@ -64,7 +64,7 @@ static const LangTag ot_languages2[] = {
{HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */ {HB_TAG('f','r',' ',' '), HB_TAG('F','R','A',' ')}, /* French */
{HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */ {HB_TAG('f','y',' ',' '), HB_TAG('F','R','I',' ')}, /* Western Frisian -> Frisian */
{HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */ {HB_TAG('g','a',' ',' '), HB_TAG('I','R','I',' ')}, /* Irish */
{HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic (Gaelic) */ {HB_TAG('g','d',' ',' '), HB_TAG('G','A','E',' ')}, /* Scottish Gaelic */
{HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */ {HB_TAG('g','l',' ',' '), HB_TAG('G','A','L',' ')}, /* Galician */
{HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */ {HB_TAG('g','n',' ',' '), HB_TAG('G','U','A',' ')}, /* Guarani [macrolanguage] */
{HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */ {HB_TAG('g','u',' ',' '), HB_TAG('G','U','J',' ')}, /* Gujarati */
@ -132,7 +132,7 @@ static const LangTag ot_languages2[] = {
{HB_TAG('m','l',' ',' '), HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */ {HB_TAG('m','l',' ',' '), HB_TAG('M','A','L',' ')}, /* Malayalam -> Malayalam Traditional */
{HB_TAG('m','l',' ',' '), HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */ {HB_TAG('m','l',' ',' '), HB_TAG('M','L','R',' ')}, /* Malayalam -> Malayalam Reformed */
{HB_TAG('m','n',' ',' '), HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */ {HB_TAG('m','n',' ',' '), HB_TAG('M','N','G',' ')}, /* Mongolian [macrolanguage] */
{HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) */ {HB_TAG('m','o',' ',' '), HB_TAG('M','O','L',' ')}, /* Moldavian (retired code) -> Romanian (Moldova) */
{HB_TAG('m','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */ {HB_TAG('m','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Moldavian (retired code) -> Romanian */
{HB_TAG('m','r',' ',' '), HB_TAG('M','A','R',' ')}, /* Marathi */ {HB_TAG('m','r',' ',' '), HB_TAG('M','A','R',' ')}, /* Marathi */
{HB_TAG('m','s',' ',' '), HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */ {HB_TAG('m','s',' ',' '), HB_TAG('M','L','Y',' ')}, /* Malay [macrolanguage] */
@ -153,7 +153,7 @@ static const LangTag ot_languages2[] = {
{HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */ {HB_TAG('o','c',' ',' '), HB_TAG('O','C','I',' ')}, /* Occitan (post 1500) */
{HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */ {HB_TAG('o','j',' ',' '), HB_TAG('O','J','B',' ')}, /* Ojibwa [macrolanguage] -> Ojibway */
{HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */ {HB_TAG('o','m',' ',' '), HB_TAG('O','R','O',' ')}, /* Oromo [macrolanguage] */
{HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) [macrolanguage] */ {HB_TAG('o','r',' ',' '), HB_TAG('O','R','I',' ')}, /* Odia [macrolanguage] */
{HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */ {HB_TAG('o','s',' ',' '), HB_TAG('O','S','S',' ')}, /* Ossetian */
{HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */ {HB_TAG('p','a',' ',' '), HB_TAG('P','A','N',' ')}, /* Punjabi */
{HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */ {HB_TAG('p','i',' ',' '), HB_TAG('P','A','L',' ')}, /* Pali */
@ -166,7 +166,7 @@ static const LangTag ot_languages2[] = {
{HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */ {HB_TAG('r','o',' ',' '), HB_TAG('R','O','M',' ')}, /* Romanian */
{HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */ {HB_TAG('r','u',' ',' '), HB_TAG('R','U','S',' ')}, /* Russian */
{HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */ {HB_TAG('r','w',' ',' '), HB_TAG('R','U','A',' ')}, /* Kinyarwanda */
{HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit */ {HB_TAG('s','a',' ',' '), HB_TAG('S','A','N',' ')}, /* Sanskrit [macrolanguage] */
{HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */ {HB_TAG('s','c',' ',' '), HB_TAG('S','R','D',' ')}, /* Sardinian [macrolanguage] */
{HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */ {HB_TAG('s','d',' ',' '), HB_TAG('S','N','D',' ')}, /* Sindhi */
{HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */ {HB_TAG('s','e',' ',' '), HB_TAG('N','S','M',' ')}, /* Northern Sami */
@ -465,6 +465,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */ {HB_TAG('c','l','d',' '), HB_TAG('S','Y','R',' ')}, /* Chaldean Neo-Aramaic -> Syriac */
{HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */ {HB_TAG('c','l','e',' '), HB_TAG('C','C','H','N')}, /* Lealao Chinantec -> Chinantec */
{HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */ {HB_TAG('c','l','j',' '), HB_TAG('Q','I','N',' ')}, /* Laitu Chin -> Chin */
{HB_TAG('c','l','s',' '), HB_TAG('S','A','N',' ')}, /* Classical Sanskrit -> Sanskrit */
{HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */ {HB_TAG('c','l','t',' '), HB_TAG('Q','I','N',' ')}, /* Lautu Chin -> Chin */
{HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */ {HB_TAG('c','m','n',' '), HB_TAG('Z','H','S',' ')}, /* Mandarin Chinese -> Chinese, Simplified */
{HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */ {HB_TAG('c','m','r',' '), HB_TAG('Q','I','N',' ')}, /* Mro-Khimi Chin -> Chin */
@ -637,7 +638,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */ {HB_TAG('g','a','a',' '), HB_TAG('G','A','D',' ')}, /* Ga */
{HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */ {HB_TAG('g','a','c',' '), HB_TAG('C','P','P',' ')}, /* Mixed Great Andamanese -> Creoles */
{HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */ {HB_TAG('g','a','d',' '), HB_TAG_NONE }, /* Gaddang != Ga */
{HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic (Gaelic) */ {HB_TAG('g','a','e',' '), HB_TAG_NONE }, /* Guarequena != Scottish Gaelic */
/*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */ /*{HB_TAG('g','a','g',' '), HB_TAG('G','A','G',' ')},*/ /* Gagauz */
{HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */ {HB_TAG('g','a','l',' '), HB_TAG_NONE }, /* Galolen != Galician */
{HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */ {HB_TAG('g','a','n',' '), HB_TAG('Z','H','S',' ')}, /* Gan Chinese -> Chinese, Simplified */
@ -1160,7 +1161,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */ {HB_TAG('o','r','o',' '), HB_TAG_NONE }, /* Orokolo != Oromo */
{HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */ {HB_TAG('o','r','r',' '), HB_TAG('I','J','O',' ')}, /* Oruma -> Ijo */
{HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */ {HB_TAG('o','r','s',' '), HB_TAG('M','L','Y',' ')}, /* Orang Seletar -> Malay */
{HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia (formerly Oriya) */ {HB_TAG('o','r','y',' '), HB_TAG('O','R','I',' ')}, /* Odia */
{HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */ {HB_TAG('o','t','w',' '), HB_TAG('O','J','B',' ')}, /* Ottawa -> Ojibway */
{HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */ {HB_TAG('o','u','a',' '), HB_TAG('B','B','R',' ')}, /* Tagargrent -> Berber */
{HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */ {HB_TAG('p','a','a',' '), HB_TAG_NONE }, /* Papuan [collection] != Palestinian Aramaic */
@ -1395,7 +1396,7 @@ static const LangTag ot_languages3[] = {
/*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */ /*{HB_TAG('s','n','k',' '), HB_TAG('S','N','K',' ')},*/ /* Soninke */
{HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */ {HB_TAG('s','o','g',' '), HB_TAG_NONE }, /* Sogdian != Sodo Gurage */
/*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */ /*{HB_TAG('s','o','p',' '), HB_TAG('S','O','P',' ')},*/ /* Songe */
{HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia (formerly Oriya) */ {HB_TAG('s','p','v',' '), HB_TAG('O','R','I',' ')}, /* Sambalpuri -> Odia */
{HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */ {HB_TAG('s','p','y',' '), HB_TAG('K','A','L',' ')}, /* Sabaot -> Kalenjin */
{HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */ {HB_TAG('s','r','b',' '), HB_TAG_NONE }, /* Sora != Serbian */
{HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */ {HB_TAG('s','r','c',' '), HB_TAG('S','R','D',' ')}, /* Logudorese Sardinian -> Sardinian */
@ -1533,6 +1534,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */ {HB_TAG('v','l','s',' '), HB_TAG('F','L','E',' ')}, /* Vlaams -> Dutch (Flemish) */
{HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */ {HB_TAG('v','m','w',' '), HB_TAG('M','A','K',' ')}, /* Makhuwa */
/*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */ /*{HB_TAG('v','r','o',' '), HB_TAG('V','R','O',' ')},*/ /* Võro */
{HB_TAG('v','s','n',' '), HB_TAG('S','A','N',' ')}, /* Vedic Sanskrit -> Sanskrit */
{HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */ {HB_TAG('w','a','g',' '), HB_TAG_NONE }, /* Wa'ema != Wagdi */
/*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */ /*{HB_TAG('w','a','r',' '), HB_TAG('W','A','R',' ')},*/ /* Waray (Philippines) -> Waray-Waray */
{HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */ {HB_TAG('w','b','m',' '), HB_TAG('W','A',' ',' ')}, /* Wa */
@ -2643,7 +2645,7 @@ out:
/* Romanian; Moldova */ /* Romanian; Moldova */
unsigned int i; unsigned int i;
hb_tag_t possible_tags[] = { hb_tag_t possible_tags[] = {
HB_TAG('M','O','L',' '), /* Moldavian */ HB_TAG('M','O','L',' '), /* Romanian (Moldova) */
HB_TAG('R','O','M',' '), /* Romanian */ HB_TAG('R','O','M',' '), /* Romanian */
}; };
for (i = 0; i < 2 && i < *count; i++) for (i = 0; i < 2 && i < *count; i++)
@ -2920,7 +2922,7 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */ return hb_language_from_string ("mn", -1); /* Mongolian [macrolanguage] */
case HB_TAG('M','N','K',' '): /* Maninka */ case HB_TAG('M','N','K',' '): /* Maninka */
return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */ return hb_language_from_string ("man", -1); /* Mandingo [macrolanguage] */
case HB_TAG('M','O','L',' '): /* Moldavian */ case HB_TAG('M','O','L',' '): /* Romanian (Moldova) */
return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */ return hb_language_from_string ("ro-MD", -1); /* Romanian; Moldova */
case HB_TAG('M','O','N','T'): /* Thailand Mon */ case HB_TAG('M','O','N','T'): /* Thailand Mon */
return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */ return hb_language_from_string ("mnw-TH", -1); /* Mon; Thailand */
@ -2958,6 +2960,8 @@ hb_ot_ambiguous_tag_to_language (hb_tag_t tag)
return hb_language_from_string ("ro", -1); /* Romanian */ return hb_language_from_string ("ro", -1); /* Romanian */
case HB_TAG('R','O','Y',' '): /* Romany */ case HB_TAG('R','O','Y',' '): /* Romany */
return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */ return hb_language_from_string ("rom", -1); /* Romany [macrolanguage] */
case HB_TAG('S','A','N',' '): /* Sanskrit */
return hb_language_from_string ("sa", -1); /* Sanskrit [macrolanguage] */
case HB_TAG('S','Q','I',' '): /* Albanian */ case HB_TAG('S','Q','I',' '): /* Albanian */
return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */ return hb_language_from_string ("sq", -1); /* Albanian [macrolanguage] */
case HB_TAG('S','R','B',' '): /* Serbian */ case HB_TAG('S','R','B',' '): /* Serbian */

View File

@ -547,7 +547,7 @@ hb_ot_tag_to_language (hb_tag_t tag)
buf[3] = '-'; buf[3] = '-';
str += 4; str += 4;
} }
snprintf (str, 16, "x-hbot-%08x", tag); snprintf (str, 16, "x-hbot-%08" PRIx32, tag);
return hb_language_from_string (&*buf, -1); return hb_language_from_string (&*buf, -1);
} }
} }

View File

@ -57,7 +57,7 @@ struct avarV2Tail
protected: protected:
Offset32To<DeltaSetIndexMap> varIdxMap; /* Offset from the beginning of 'avar' table. */ Offset32To<DeltaSetIndexMap> varIdxMap; /* Offset from the beginning of 'avar' table. */
Offset32To<VariationStore> varStore; /* Offset from the beginning of 'avar' table. */ Offset32To<ItemVariationStore> varStore; /* Offset from the beginning of 'avar' table. */
public: public:
DEFINE_SIZE_STATIC (8); DEFINE_SIZE_STATIC (8);
@ -230,7 +230,7 @@ struct SegmentMaps : Array16Of<AxisValueMap>
* duplicates here */ * duplicates here */
if (mapping.must_include ()) if (mapping.must_include ())
continue; continue;
value_mappings.push (std::move (mapping)); value_mappings.push (mapping);
} }
AxisValueMap m; AxisValueMap m;
@ -343,7 +343,7 @@ struct avar
for (unsigned i = 0; i < coords_length; i++) for (unsigned i = 0; i < coords_length; i++)
coords[i] = out[i]; coords[i] = out[i];
OT::VariationStore::destroy_cache (var_store_cache); OT::ItemVariationStore::destroy_cache (var_store_cache);
#endif #endif
} }

View File

@ -28,6 +28,7 @@
#include "hb-ot-layout-common.hh" #include "hb-ot-layout-common.hh"
#include "hb-priority-queue.hh" #include "hb-priority-queue.hh"
#include "hb-subset-instancer-iup.hh"
namespace OT { namespace OT {
@ -221,9 +222,9 @@ struct DeltaSetIndexMap
}; };
struct VarStoreInstancer struct ItemVarStoreInstancer
{ {
VarStoreInstancer (const VariationStore *varStore, ItemVarStoreInstancer (const ItemVariationStore *varStore,
const DeltaSetIndexMap *varIdxMap, const DeltaSetIndexMap *varIdxMap,
hb_array_t<int> coords) : hb_array_t<int> coords) :
varStore (varStore), varIdxMap (varIdxMap), coords (coords) {} varStore (varStore), varIdxMap (varIdxMap), coords (coords) {}
@ -235,7 +236,7 @@ struct VarStoreInstancer
float operator() (uint32_t varIdx, unsigned short offset = 0) const float operator() (uint32_t varIdx, unsigned short offset = 0) const
{ return coords ? varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords) : 0; } { return coords ? varStore->get_delta (varIdxMap ? varIdxMap->map (VarIdx::add (varIdx, offset)) : varIdx + offset, coords) : 0; }
const VariationStore *varStore; const ItemVariationStore *varStore;
const DeltaSetIndexMap *varIdxMap; const DeltaSetIndexMap *varIdxMap;
hb_array_t<int> coords; hb_array_t<int> coords;
}; };
@ -460,7 +461,7 @@ struct tuple_delta_t
tuple_delta_t () = default; tuple_delta_t () = default;
tuple_delta_t (const tuple_delta_t& o) = default; tuple_delta_t (const tuple_delta_t& o) = default;
friend void swap (tuple_delta_t& a, tuple_delta_t& b) friend void swap (tuple_delta_t& a, tuple_delta_t& b) noexcept
{ {
hb_swap (a.axis_tuples, b.axis_tuples); hb_swap (a.axis_tuples, b.axis_tuples);
hb_swap (a.indices, b.indices); hb_swap (a.indices, b.indices);
@ -471,10 +472,10 @@ struct tuple_delta_t
hb_swap (a.compiled_peak_coords, b.compiled_peak_coords); hb_swap (a.compiled_peak_coords, b.compiled_peak_coords);
} }
tuple_delta_t (tuple_delta_t&& o) : tuple_delta_t () tuple_delta_t (tuple_delta_t&& o) noexcept : tuple_delta_t ()
{ hb_swap (*this, o); } { hb_swap (*this, o); }
tuple_delta_t& operator = (tuple_delta_t&& o) tuple_delta_t& operator = (tuple_delta_t&& o) noexcept
{ {
hb_swap (*this, o); hb_swap (*this, o);
return *this; return *this;
@ -609,7 +610,9 @@ struct tuple_delta_t
const hb_map_t& axes_old_index_tag_map, const hb_map_t& axes_old_index_tag_map,
const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map) const hb_hashmap_t<const hb_vector_t<char>*, unsigned>* shared_tuples_idx_map)
{ {
if (!compiled_deltas) return false; /* compiled_deltas could be empty after iup delta optimization, we can skip
* compiling this tuple and return true */
if (!compiled_deltas) return true;
unsigned cur_axis_count = axes_index_map.get_population (); unsigned cur_axis_count = axes_index_map.get_population ();
/* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */ /* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */
@ -723,22 +726,28 @@ struct tuple_delta_t
} }
bool compile_deltas () bool compile_deltas ()
{ return compile_deltas (indices, deltas_x, deltas_y, compiled_deltas); }
bool compile_deltas (const hb_vector_t<bool> &point_indices,
const hb_vector_t<float> &x_deltas,
const hb_vector_t<float> &y_deltas,
hb_vector_t<char> &compiled_deltas /* OUT */)
{ {
hb_vector_t<int> rounded_deltas; hb_vector_t<int> rounded_deltas;
if (unlikely (!rounded_deltas.alloc (indices.length))) if (unlikely (!rounded_deltas.alloc (point_indices.length)))
return false; return false;
for (unsigned i = 0; i < indices.length; i++) for (unsigned i = 0; i < point_indices.length; i++)
{ {
if (!indices[i]) continue; if (!point_indices[i]) continue;
int rounded_delta = (int) roundf (deltas_x[i]); int rounded_delta = (int) roundf (x_deltas.arrayZ[i]);
rounded_deltas.push (rounded_delta); rounded_deltas.push (rounded_delta);
} }
if (!rounded_deltas) return false; if (!rounded_deltas) return true;
/* allocate enough memories 3 * num_deltas */ /* allocate enough memories 3 * num_deltas */
unsigned alloc_len = 3 * rounded_deltas.length; unsigned alloc_len = 3 * rounded_deltas.length;
if (deltas_y) if (y_deltas)
alloc_len *= 2; alloc_len *= 2;
if (unlikely (!compiled_deltas.resize (alloc_len))) return false; if (unlikely (!compiled_deltas.resize (alloc_len))) return false;
@ -746,14 +755,14 @@ struct tuple_delta_t
unsigned i = 0; unsigned i = 0;
unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas); unsigned encoded_len = encode_delta_run (i, compiled_deltas.as_array (), rounded_deltas);
if (deltas_y) if (y_deltas)
{ {
/* reuse the rounded_deltas vector, check that deltas_y have the same num of deltas as deltas_x */ /* reuse the rounded_deltas vector, check that y_deltas have the same num of deltas as x_deltas */
unsigned j = 0; unsigned j = 0;
for (unsigned idx = 0; idx < indices.length; idx++) for (unsigned idx = 0; idx < point_indices.length; idx++)
{ {
if (!indices[idx]) continue; if (!point_indices[idx]) continue;
int rounded_delta = (int) roundf (deltas_y[idx]); int rounded_delta = (int) roundf (y_deltas.arrayZ[idx]);
if (j >= rounded_deltas.length) return false; if (j >= rounded_deltas.length) return false;
@ -761,7 +770,7 @@ struct tuple_delta_t
} }
if (j != rounded_deltas.length) return false; if (j != rounded_deltas.length) return false;
/* reset i because we reuse rounded_deltas for deltas_y */ /* reset i because we reuse rounded_deltas for y_deltas */
i = 0; i = 0;
encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas); encoded_len += encode_delta_run (i, compiled_deltas.as_array ().sub_array (encoded_len), rounded_deltas);
} }
@ -1020,6 +1029,171 @@ struct tuple_delta_t
return true; return true;
} }
bool optimize (const contour_point_vector_t& contour_points,
bool is_composite,
float tolerance = 0.5f)
{
unsigned count = contour_points.length;
if (deltas_x.length != count ||
deltas_y.length != count)
return false;
hb_vector_t<bool> opt_indices;
hb_vector_t<int> rounded_x_deltas, rounded_y_deltas;
if (unlikely (!rounded_x_deltas.alloc (count) ||
!rounded_y_deltas.alloc (count)))
return false;
for (unsigned i = 0; i < count; i++)
{
int rounded_x_delta = (int) roundf (deltas_x.arrayZ[i]);
int rounded_y_delta = (int) roundf (deltas_y.arrayZ[i]);
rounded_x_deltas.push (rounded_x_delta);
rounded_y_deltas.push (rounded_y_delta);
}
if (!iup_delta_optimize (contour_points, rounded_x_deltas, rounded_y_deltas, opt_indices, tolerance))
return false;
unsigned ref_count = 0;
for (bool ref_flag : opt_indices)
ref_count += ref_flag;
if (ref_count == count) return true;
hb_vector_t<float> opt_deltas_x, opt_deltas_y;
bool is_comp_glyph_wo_deltas = (is_composite && ref_count == 0);
if (is_comp_glyph_wo_deltas)
{
if (unlikely (!opt_deltas_x.resize (count) ||
!opt_deltas_y.resize (count)))
return false;
opt_indices.arrayZ[0] = true;
for (unsigned i = 1; i < count; i++)
opt_indices.arrayZ[i] = false;
}
hb_vector_t<char> opt_point_data;
if (!compile_point_set (opt_indices, opt_point_data))
return false;
hb_vector_t<char> opt_deltas_data;
if (!compile_deltas (opt_indices,
is_comp_glyph_wo_deltas ? opt_deltas_x : deltas_x,
is_comp_glyph_wo_deltas ? opt_deltas_y : deltas_y,
opt_deltas_data))
return false;
hb_vector_t<char> point_data;
if (!compile_point_set (indices, point_data))
return false;
hb_vector_t<char> deltas_data;
if (!compile_deltas (indices, deltas_x, deltas_y, deltas_data))
return false;
if (opt_point_data.length + opt_deltas_data.length < point_data.length + deltas_data.length)
{
indices.fini ();
indices = std::move (opt_indices);
if (is_comp_glyph_wo_deltas)
{
deltas_x.fini ();
deltas_x = std::move (opt_deltas_x);
deltas_y.fini ();
deltas_y = std::move (opt_deltas_y);
}
}
return !indices.in_error () && !deltas_x.in_error () && !deltas_y.in_error ();
}
static bool compile_point_set (const hb_vector_t<bool> &point_indices,
hb_vector_t<char>& compiled_points /* OUT */)
{
unsigned num_points = 0;
for (bool i : point_indices)
if (i) num_points++;
/* when iup optimization is enabled, num of referenced points could be 0 */
if (!num_points) return true;
unsigned indices_length = point_indices.length;
/* If the points set consists of all points in the glyph, it's encoded with a
* single zero byte */
if (num_points == indices_length)
return compiled_points.resize (1);
/* allocate enough memories: 2 bytes for count + 3 bytes for each point */
unsigned num_bytes = 2 + 3 *num_points;
if (unlikely (!compiled_points.resize (num_bytes, false)))
return false;
unsigned pos = 0;
/* binary data starts with the total number of reference points */
if (num_points < 0x80)
compiled_points.arrayZ[pos++] = num_points;
else
{
compiled_points.arrayZ[pos++] = ((num_points >> 8) | 0x80);
compiled_points.arrayZ[pos++] = num_points & 0xFF;
}
const unsigned max_run_length = 0x7F;
unsigned i = 0;
unsigned last_value = 0;
unsigned num_encoded = 0;
while (i < indices_length && num_encoded < num_points)
{
unsigned run_length = 0;
unsigned header_pos = pos;
compiled_points.arrayZ[pos++] = 0;
bool use_byte_encoding = false;
bool new_run = true;
while (i < indices_length && num_encoded < num_points &&
run_length <= max_run_length)
{
// find out next referenced point index
while (i < indices_length && !point_indices[i])
i++;
if (i >= indices_length) break;
unsigned cur_value = i;
unsigned delta = cur_value - last_value;
if (new_run)
{
use_byte_encoding = (delta <= 0xFF);
new_run = false;
}
if (use_byte_encoding && delta > 0xFF)
break;
if (use_byte_encoding)
compiled_points.arrayZ[pos++] = delta;
else
{
compiled_points.arrayZ[pos++] = delta >> 8;
compiled_points.arrayZ[pos++] = delta & 0xFF;
}
i++;
last_value = cur_value;
run_length++;
num_encoded++;
}
if (use_byte_encoding)
compiled_points.arrayZ[header_pos] = run_length - 1;
else
compiled_points.arrayZ[header_pos] = (run_length - 1) | 0x80;
}
return compiled_points.resize (pos, false);
}
static float infer_delta (float target_val, float prev_val, float next_val, float prev_delta, float next_delta) static float infer_delta (float target_val, float prev_val, float next_val, float prev_delta, float next_delta)
{ {
if (prev_val == next_val) if (prev_val == next_val)
@ -1071,41 +1245,41 @@ struct TupleVariationData
private: private:
/* referenced point set->compiled point data map */ /* referenced point set->compiled point data map */
hb_hashmap_t<const hb_vector_t<bool>*, hb_bytes_t> point_data_map; hb_hashmap_t<const hb_vector_t<bool>*, hb_vector_t<char>> point_data_map;
/* referenced point set-> count map, used in finding shared points */ /* referenced point set-> count map, used in finding shared points */
hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map; hb_hashmap_t<const hb_vector_t<bool>*, unsigned> point_set_count_map;
/* empty for non-gvar tuples. /* empty for non-gvar tuples.
* shared_points_bytes is just a copy of some value in the point_data_map, * shared_points_bytes is a pointer to some value in the point_data_map,
* which will be freed during map destruction. Save it for serialization, so * which will be freed during map destruction. Save it for serialization, so
* no need to do find_shared_points () again */ * no need to do find_shared_points () again */
hb_bytes_t shared_points_bytes; hb_vector_t<char> *shared_points_bytes = nullptr;
/* total compiled byte size as TupleVariationData format, initialized to its /* total compiled byte size as TupleVariationData format, initialized to its
* min_size: 4 */ * min_size: 4 */
unsigned compiled_byte_size = 4; unsigned compiled_byte_size = 4;
/* for gvar iup delta optimization: whether this is a composite glyph */
bool is_composite = false;
public: public:
tuple_variations_t () = default; tuple_variations_t () = default;
tuple_variations_t (const tuple_variations_t&) = delete; tuple_variations_t (const tuple_variations_t&) = delete;
tuple_variations_t& operator=(const tuple_variations_t&) = delete; tuple_variations_t& operator=(const tuple_variations_t&) = delete;
tuple_variations_t (tuple_variations_t&&) = default; tuple_variations_t (tuple_variations_t&&) = default;
tuple_variations_t& operator=(tuple_variations_t&&) = default; tuple_variations_t& operator=(tuple_variations_t&&) = default;
~tuple_variations_t () { fini (); } ~tuple_variations_t () = default;
void fini ()
{
for (auto _ : point_data_map.values ())
_.fini ();
point_set_count_map.fini ();
tuple_vars.fini ();
}
explicit operator bool () const { return bool (tuple_vars); } explicit operator bool () const { return bool (tuple_vars); }
unsigned get_var_count () const unsigned get_var_count () const
{ {
unsigned count = tuple_vars.length; unsigned count = 0;
if (shared_points_bytes.length) /* when iup delta opt is enabled, compiled_deltas could be empty and we
* should skip this tuple */
for (auto& tuple: tuple_vars)
if (tuple.compiled_deltas) count++;
if (shared_points_bytes && shared_points_bytes->length)
count |= TupleVarCount::SharedPointNumbers; count |= TupleVarCount::SharedPointNumbers;
return count; return count;
} }
@ -1119,26 +1293,27 @@ struct TupleVariationData
bool is_gvar, bool is_gvar,
const hb_map_t *axes_old_index_tag_map, const hb_map_t *axes_old_index_tag_map,
const hb_vector_t<unsigned> &shared_indices, const hb_vector_t<unsigned> &shared_indices,
const hb_array_t<const F2DOT14> shared_tuples) const hb_array_t<const F2DOT14> shared_tuples,
bool is_composite_glyph)
{ {
do do
{ {
const HBUINT8 *p = iterator.get_serialized_data (); const HBUINT8 *p = iterator.get_serialized_data ();
unsigned int length = iterator.current_tuple->get_data_size (); unsigned int length = iterator.current_tuple->get_data_size ();
if (unlikely (!iterator.var_data_bytes.check_range (p, length))) if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
{ fini (); return false; } return false;
hb_hashmap_t<hb_tag_t, Triple> axis_tuples; hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples) if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples)
|| axis_tuples.is_empty ()) || axis_tuples.is_empty ())
{ fini (); return false; } return false;
hb_vector_t<unsigned> private_indices; hb_vector_t<unsigned> private_indices;
bool has_private_points = iterator.current_tuple->has_private_points (); bool has_private_points = iterator.current_tuple->has_private_points ();
const HBUINT8 *end = p + length; const HBUINT8 *end = p + length;
if (has_private_points && if (has_private_points &&
!TupleVariationData::unpack_points (p, private_indices, end)) !TupleVariationData::unpack_points (p, private_indices, end))
{ fini (); return false; } return false;
const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices; const hb_vector_t<unsigned> &indices = has_private_points ? private_indices : shared_indices;
bool apply_to_all = (indices.length == 0); bool apply_to_all = (indices.length == 0);
@ -1148,24 +1323,24 @@ struct TupleVariationData
if (unlikely (!deltas_x.resize (num_deltas, false) || if (unlikely (!deltas_x.resize (num_deltas, false) ||
!TupleVariationData::unpack_deltas (p, deltas_x, end))) !TupleVariationData::unpack_deltas (p, deltas_x, end)))
{ fini (); return false; } return false;
hb_vector_t<int> deltas_y; hb_vector_t<int> deltas_y;
if (is_gvar) if (is_gvar)
{ {
if (unlikely (!deltas_y.resize (num_deltas, false) || if (unlikely (!deltas_y.resize (num_deltas, false) ||
!TupleVariationData::unpack_deltas (p, deltas_y, end))) !TupleVariationData::unpack_deltas (p, deltas_y, end)))
{ fini (); return false; } return false;
} }
tuple_delta_t var; tuple_delta_t var;
var.axis_tuples = std::move (axis_tuples); var.axis_tuples = std::move (axis_tuples);
if (unlikely (!var.indices.resize (point_count) || if (unlikely (!var.indices.resize (point_count) ||
!var.deltas_x.resize (point_count, false))) !var.deltas_x.resize (point_count, false)))
{ fini (); return false; } return false;
if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false))) if (is_gvar && unlikely (!var.deltas_y.resize (point_count, false)))
{ fini (); return false; } return false;
for (unsigned i = 0; i < num_deltas; i++) for (unsigned i = 0; i < num_deltas; i++)
{ {
@ -1178,6 +1353,8 @@ struct TupleVariationData
} }
tuple_vars.push (std::move (var)); tuple_vars.push (std::move (var));
} while (iterator.move_to_next ()); } while (iterator.move_to_next ());
is_composite = is_composite_glyph;
return true; return true;
} }
@ -1261,7 +1438,7 @@ struct TupleVariationData
unsigned new_len = new_vars.length + out.length; unsigned new_len = new_vars.length + out.length;
if (unlikely (!new_vars.alloc (new_len, false))) if (unlikely (!new_vars.alloc (new_len, false)))
{ fini (); return false;} return false;
for (unsigned i = 0; i < out.length; i++) for (unsigned i = 0; i < out.length; i++)
new_vars.push (std::move (out[i])); new_vars.push (std::move (out[i]));
@ -1272,8 +1449,9 @@ struct TupleVariationData
return true; return true;
} }
/* merge tuple variations with overlapping tents */ /* merge tuple variations with overlapping tents, if iup delta optimization
void merge_tuple_variations () * is enabled, add default deltas to contour_points */
bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr)
{ {
hb_vector_t<tuple_delta_t> new_vars; hb_vector_t<tuple_delta_t> new_vars;
hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m; hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m;
@ -1281,7 +1459,15 @@ struct TupleVariationData
for (const tuple_delta_t& var : tuple_vars) for (const tuple_delta_t& var : tuple_vars)
{ {
/* if all axes are pinned, drop the tuple variation */ /* if all axes are pinned, drop the tuple variation */
if (var.axis_tuples.is_empty ()) continue; if (var.axis_tuples.is_empty ())
{
/* if iup_delta_optimize is enabled, add deltas to contour coords */
if (contour_points && !contour_points->add_deltas (var.deltas_x,
var.deltas_y,
var.indices))
return false;
continue;
}
unsigned *idx; unsigned *idx;
if (m.has (&(var.axis_tuples), &idx)) if (m.has (&(var.axis_tuples), &idx))
@ -1291,98 +1477,14 @@ struct TupleVariationData
else else
{ {
new_vars.push (var); new_vars.push (var);
m.set (&(var.axis_tuples), i); if (!m.set (&(var.axis_tuples), i))
return false;
i++; i++;
} }
} }
tuple_vars.fini (); tuple_vars.fini ();
tuple_vars = std::move (new_vars); tuple_vars = std::move (new_vars);
} return true;
hb_bytes_t compile_point_set (const hb_vector_t<bool> &point_indices)
{
unsigned num_points = 0;
for (bool i : point_indices)
if (i) num_points++;
unsigned indices_length = point_indices.length;
/* If the points set consists of all points in the glyph, it's encoded with a
* single zero byte */
if (num_points == indices_length)
{
char *p = (char *) hb_calloc (1, sizeof (char));
if (unlikely (!p)) return hb_bytes_t ();
return hb_bytes_t (p, 1);
}
/* allocate enough memories: 2 bytes for count + 3 bytes for each point */
unsigned num_bytes = 2 + 3 *num_points;
char *p = (char *) hb_calloc (num_bytes, sizeof (char));
if (unlikely (!p)) return hb_bytes_t ();
unsigned pos = 0;
/* binary data starts with the total number of reference points */
if (num_points < 0x80)
p[pos++] = num_points;
else
{
p[pos++] = ((num_points >> 8) | 0x80);
p[pos++] = num_points & 0xFF;
}
const unsigned max_run_length = 0x7F;
unsigned i = 0;
unsigned last_value = 0;
unsigned num_encoded = 0;
while (i < indices_length && num_encoded < num_points)
{
unsigned run_length = 0;
unsigned header_pos = pos;
p[pos++] = 0;
bool use_byte_encoding = false;
bool new_run = true;
while (i < indices_length && num_encoded < num_points &&
run_length <= max_run_length)
{
// find out next referenced point index
while (i < indices_length && !point_indices[i])
i++;
if (i >= indices_length) break;
unsigned cur_value = i;
unsigned delta = cur_value - last_value;
if (new_run)
{
use_byte_encoding = (delta <= 0xFF);
new_run = false;
}
if (use_byte_encoding && delta > 0xFF)
break;
if (use_byte_encoding)
p[pos++] = delta;
else
{
p[pos++] = delta >> 8;
p[pos++] = delta & 0xFF;
}
i++;
last_value = cur_value;
run_length++;
num_encoded++;
}
if (use_byte_encoding)
p[header_pos] = run_length - 1;
else
p[header_pos] = (run_length - 1) | 0x80;
}
return hb_bytes_t (p, pos);
} }
/* compile all point set and store byte data in a point_set->hb_bytes_t hashmap, /* compile all point set and store byte data in a point_set->hb_bytes_t hashmap,
@ -1402,11 +1504,11 @@ struct TupleVariationData
continue; continue;
} }
hb_bytes_t compiled_data = compile_point_set (*points_set); hb_vector_t<char> compiled_point_data;
if (unlikely (compiled_data == hb_bytes_t ())) if (!tuple_delta_t::compile_point_set (*points_set, compiled_point_data))
return false; return false;
if (!point_data_map.set (points_set, compiled_data) || if (!point_data_map.set (points_set, std::move (compiled_point_data)) ||
!point_set_count_map.set (points_set, 1)) !point_set_count_map.set (points_set, 1))
return false; return false;
} }
@ -1414,31 +1516,33 @@ struct TupleVariationData
} }
/* find shared points set which saves most bytes */ /* find shared points set which saves most bytes */
hb_bytes_t find_shared_points () void find_shared_points ()
{ {
unsigned max_saved_bytes = 0; unsigned max_saved_bytes = 0;
hb_bytes_t res{};
for (const auto& _ : point_data_map.iter ()) for (const auto& _ : point_data_map.iter_ref ())
{ {
const hb_vector_t<bool>* points_set = _.first; const hb_vector_t<bool>* points_set = _.first;
unsigned data_length = _.second.length; unsigned data_length = _.second.length;
if (!data_length) continue;
unsigned *count; unsigned *count;
if (unlikely (!point_set_count_map.has (points_set, &count) || if (unlikely (!point_set_count_map.has (points_set, &count) ||
*count <= 1)) *count <= 1))
return hb_bytes_t (); {
shared_points_bytes = nullptr;
return;
}
unsigned saved_bytes = data_length * ((*count) -1); unsigned saved_bytes = data_length * ((*count) -1);
if (saved_bytes > max_saved_bytes) if (saved_bytes > max_saved_bytes)
{ {
max_saved_bytes = saved_bytes; max_saved_bytes = saved_bytes;
res = _.second; shared_points_bytes = &(_.second);
} }
} }
return res;
} }
bool calc_inferred_deltas (contour_point_vector_t& contour_points) bool calc_inferred_deltas (const contour_point_vector_t& contour_points)
{ {
for (tuple_delta_t& var : tuple_vars) for (tuple_delta_t& var : tuple_vars)
if (!var.calc_inferred_deltas (contour_points)) if (!var.calc_inferred_deltas (contour_points))
@ -1447,10 +1551,21 @@ struct TupleVariationData
return true; return true;
} }
bool iup_optimize (const contour_point_vector_t& contour_points)
{
for (tuple_delta_t& var : tuple_vars)
{
if (!var.optimize (contour_points, is_composite))
return false;
}
return true;
}
public: public:
bool instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location, bool instantiate (const hb_hashmap_t<hb_tag_t, Triple>& normalized_axes_location,
const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances, const hb_hashmap_t<hb_tag_t, TripleDistances>& axes_triple_distances,
contour_point_vector_t* contour_points = nullptr) contour_point_vector_t* contour_points = nullptr,
bool optimize = false)
{ {
if (!tuple_vars) return true; if (!tuple_vars) return true;
if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances)) if (!change_tuple_variations_axis_limits (normalized_axes_location, axes_triple_distances))
@ -1460,7 +1575,14 @@ struct TupleVariationData
if (!calc_inferred_deltas (*contour_points)) if (!calc_inferred_deltas (*contour_points))
return false; return false;
merge_tuple_variations (); /* if iup delta opt is on, contour_points can't be null */
if (optimize && !contour_points)
return false;
if (!merge_tuple_variations (optimize ? contour_points : nullptr))
return false;
if (optimize && !iup_optimize (*contour_points)) return false;
return !tuple_vars.in_error (); return !tuple_vars.in_error ();
} }
@ -1475,21 +1597,27 @@ struct TupleVariationData
if (use_shared_points) if (use_shared_points)
{ {
shared_points_bytes = find_shared_points (); find_shared_points ();
compiled_byte_size += shared_points_bytes.length; if (shared_points_bytes)
compiled_byte_size += shared_points_bytes->length;
} }
// compile delta and tuple var header for each tuple variation // compile delta and tuple var header for each tuple variation
for (auto& tuple: tuple_vars) for (auto& tuple: tuple_vars)
{ {
const hb_vector_t<bool>* points_set = &(tuple.indices); const hb_vector_t<bool>* points_set = &(tuple.indices);
hb_bytes_t *points_data; hb_vector_t<char> *points_data;
if (unlikely (!point_data_map.has (points_set, &points_data))) if (unlikely (!point_data_map.has (points_set, &points_data)))
return false; return false;
/* when iup optimization is enabled, num of referenced points could be 0
* and thus the compiled points bytes is empty, we should skip compiling
* this tuple */
if (!points_data->length)
continue;
if (!tuple.compile_deltas ()) if (!tuple.compile_deltas ())
return false; return false;
unsigned points_data_length = (*points_data != shared_points_bytes) ? points_data->length : 0; unsigned points_data_length = (points_data != shared_points_bytes) ? points_data->length : 0;
if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map, if (!tuple.compile_tuple_var_header (axes_index_map, points_data_length, axes_old_index_tag_map,
shared_tuples_idx_map)) shared_tuples_idx_map))
return false; return false;
@ -1513,18 +1641,24 @@ struct TupleVariationData
bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const bool serialize_var_data (hb_serialize_context_t *c, bool is_gvar) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
if (is_gvar) if (is_gvar && shared_points_bytes)
shared_points_bytes.copy (c); {
hb_bytes_t s (shared_points_bytes->arrayZ, shared_points_bytes->length);
s.copy (c);
}
for (const auto& tuple: tuple_vars) for (const auto& tuple: tuple_vars)
{ {
const hb_vector_t<bool>* points_set = &(tuple.indices); const hb_vector_t<bool>* points_set = &(tuple.indices);
hb_bytes_t *point_data; hb_vector_t<char> *point_data;
if (!point_data_map.has (points_set, &point_data)) if (!point_data_map.has (points_set, &point_data))
return_trace (false); return_trace (false);
if (!is_gvar || *point_data != shared_points_bytes) if (!is_gvar || point_data != shared_points_bytes)
point_data->copy (c); {
hb_bytes_t s (point_data->arrayZ, point_data->length);
s.copy (c);
}
tuple.compiled_deltas.as_array ().copy (c); tuple.compiled_deltas.as_array ().copy (c);
if (c->in_error ()) return_trace (false); if (c->in_error ()) return_trace (false);
@ -1711,13 +1845,15 @@ struct TupleVariationData
const hb_map_t *axes_old_index_tag_map, const hb_map_t *axes_old_index_tag_map,
const hb_vector_t<unsigned> &shared_indices, const hb_vector_t<unsigned> &shared_indices,
const hb_array_t<const F2DOT14> shared_tuples, const hb_array_t<const F2DOT14> shared_tuples,
tuple_variations_t& tuple_variations /* OUT */) const tuple_variations_t& tuple_variations, /* OUT */
bool is_composite_glyph = false) const
{ {
return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount, return tuple_variations.create_from_tuple_var_data (iterator, tupleVarCount,
point_count, is_gvar, point_count, is_gvar,
axes_old_index_tag_map, axes_old_index_tag_map,
shared_indices, shared_indices,
shared_tuples); shared_tuples,
is_composite_glyph);
} }
bool serialize (hb_serialize_context_t *c, bool serialize (hb_serialize_context_t *c,
@ -1831,7 +1967,7 @@ struct item_variations_t
const hb_map_t& get_varidx_map () const const hb_map_t& get_varidx_map () const
{ return varidx_map; } { return varidx_map; }
bool instantiate (const VariationStore& varStore, bool instantiate (const ItemVariationStore& varStore,
const hb_subset_plan_t *plan, const hb_subset_plan_t *plan,
bool optimize=true, bool optimize=true,
bool use_no_variation_idx=true, bool use_no_variation_idx=true,
@ -1845,7 +1981,7 @@ struct item_variations_t
} }
/* keep below APIs public only for unit test: test-item-varstore */ /* keep below APIs public only for unit test: test-item-varstore */
bool create_from_item_varstore (const VariationStore& varStore, bool create_from_item_varstore (const ItemVariationStore& varStore,
const hb_map_t& axes_old_index_tag_map, const hb_map_t& axes_old_index_tag_map,
const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ()) const hb_array_t <const hb_inc_bimap_t> inner_maps = hb_array_t<const hb_inc_bimap_t> ())
{ {

View File

@ -101,10 +101,15 @@ struct glyph_variations_t
continue; continue;
} }
bool is_composite_glyph = false;
#ifdef HB_EXPERIMENTAL_API
is_composite_glyph = plan->composite_new_gids.has (new_gid);
#endif
if (!p->decompile_tuple_variations (all_contour_points->length, true /* is_gvar */, if (!p->decompile_tuple_variations (all_contour_points->length, true /* is_gvar */,
iterator, &(plan->axes_old_index_tag_map), iterator, &(plan->axes_old_index_tag_map),
shared_indices, shared_tuples, shared_indices, shared_tuples,
tuple_vars /* OUT */)) tuple_vars, /* OUT */
is_composite_glyph))
return false; return false;
glyph_variations.push (std::move (tuple_vars)); glyph_variations.push (std::move (tuple_vars));
} }
@ -114,13 +119,17 @@ struct glyph_variations_t
bool instantiate (const hb_subset_plan_t *plan) bool instantiate (const hb_subset_plan_t *plan)
{ {
unsigned count = plan->new_to_old_gid_list.length; unsigned count = plan->new_to_old_gid_list.length;
bool iup_optimize = false;
#ifdef HB_EXPERIMENTAL_API
iup_optimize = plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS;
#endif
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
{ {
hb_codepoint_t new_gid = plan->new_to_old_gid_list[i].first; hb_codepoint_t new_gid = plan->new_to_old_gid_list[i].first;
contour_point_vector_t *all_points; contour_point_vector_t *all_points;
if (!plan->new_gid_contour_points_map.has (new_gid, &all_points)) if (!plan->new_gid_contour_points_map.has (new_gid, &all_points))
return false; return false;
if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points)) if (!glyph_variations[i].instantiate (plan->axes_location, plan->axes_triple_distances, all_points, iup_optimize))
return false; return false;
} }
return true; return true;
@ -340,7 +349,8 @@ struct gvar
const glyph_variations_t& glyph_vars, const glyph_variations_t& glyph_vars,
Iterator it, Iterator it,
unsigned axis_count, unsigned axis_count,
unsigned num_glyphs) const unsigned num_glyphs,
bool force_long_offsets) const
{ {
TRACE_SERIALIZE (this); TRACE_SERIALIZE (this);
gvar *out = c->allocate_min<gvar> (); gvar *out = c->allocate_min<gvar> ();
@ -352,7 +362,7 @@ struct gvar
out->glyphCountX = hb_min (0xFFFFu, num_glyphs); out->glyphCountX = hb_min (0xFFFFu, num_glyphs);
unsigned glyph_var_data_size = glyph_vars.compiled_byte_size (); unsigned glyph_var_data_size = glyph_vars.compiled_byte_size ();
bool long_offset = glyph_var_data_size & ~0xFFFFu; bool long_offset = glyph_var_data_size & ~0xFFFFu || force_long_offsets;
out->flags = long_offset ? 1 : 0; out->flags = long_offset ? 1 : 0;
HBUINT8 *glyph_var_data_offsets = c->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false); HBUINT8 *glyph_var_data_offsets = c->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1), false);
@ -393,7 +403,12 @@ struct gvar
unsigned axis_count = c->plan->axes_index_map.get_population (); unsigned axis_count = c->plan->axes_index_map.get_population ();
unsigned num_glyphs = c->plan->num_output_glyphs (); unsigned num_glyphs = c->plan->num_output_glyphs ();
auto it = hb_iter (c->plan->new_to_old_gid_list); auto it = hb_iter (c->plan->new_to_old_gid_list);
return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs));
bool force_long_offsets = false;
#ifdef HB_EXPERIMENTAL_API
force_long_offsets = c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS;
#endif
return_trace (serialize (c->serializer, glyph_vars, it, axis_count, num_glyphs, force_long_offsets));
} }
bool subset (hb_subset_context_t *c) const bool subset (hb_subset_context_t *c) const
@ -429,7 +444,7 @@ struct gvar
} }
bool long_offset = (subset_data_size & ~0xFFFFu); bool long_offset = (subset_data_size & ~0xFFFFu);
#ifdef HB_EXPERIMENTAL_API #ifdef HB_EXPERIMENTAL_API
long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS); long_offset = long_offset || (c->plan->flags & HB_SUBSET_FLAGS_IFTB_REQUIREMENTS);
#endif #endif
out->flags = long_offset ? 1 : 0; out->flags = long_offset ? 1 : 0;

View File

@ -188,7 +188,7 @@ struct hvarvvar_subset_plan_t
~hvarvvar_subset_plan_t() { fini (); } ~hvarvvar_subset_plan_t() { fini (); }
void init (const hb_array_t<const DeltaSetIndexMap *> &index_maps, void init (const hb_array_t<const DeltaSetIndexMap *> &index_maps,
const VariationStore &_var_store, const ItemVariationStore &_var_store,
const hb_subset_plan_t *plan) const hb_subset_plan_t *plan)
{ {
index_map_plans.resize (index_maps.length); index_map_plans.resize (index_maps.length);
@ -263,7 +263,7 @@ struct hvarvvar_subset_plan_t
hb_inc_bimap_t outer_map; hb_inc_bimap_t outer_map;
hb_vector_t<hb_inc_bimap_t> inner_maps; hb_vector_t<hb_inc_bimap_t> inner_maps;
hb_vector_t<index_map_subset_plan_t> index_map_plans; hb_vector_t<index_map_subset_plan_t> index_map_plans;
const VariationStore *var_store; const ItemVariationStore *var_store;
protected: protected:
hb_vector_t<hb_set_t *> inner_sets; hb_vector_t<hb_set_t *> inner_sets;
@ -296,7 +296,7 @@ struct HVARVVAR
rsbMap.sanitize (c, this)); rsbMap.sanitize (c, this));
} }
const VariationStore& get_var_store () const const ItemVariationStore& get_var_store () const
{ return this+varStore; } { return this+varStore; }
void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const void listup_index_maps (hb_vector_t<const DeltaSetIndexMap *> &index_maps) const
@ -384,7 +384,7 @@ struct HVARVVAR
float get_advance_delta_unscaled (hb_codepoint_t glyph, float get_advance_delta_unscaled (hb_codepoint_t glyph,
const int *coords, unsigned int coord_count, const int *coords, unsigned int coord_count,
VariationStore::cache_t *store_cache = nullptr) const ItemVariationStore::cache_t *store_cache = nullptr) const
{ {
uint32_t varidx = (this+advMap).map (glyph); uint32_t varidx = (this+advMap).map (glyph);
return (this+varStore).get_delta (varidx, return (this+varStore).get_delta (varidx,
@ -405,7 +405,7 @@ struct HVARVVAR
public: public:
FixedVersion<>version; /* Version of the metrics variation table FixedVersion<>version; /* Version of the metrics variation table
* initially set to 0x00010000u */ * initially set to 0x00010000u */
Offset32To<VariationStore> Offset32To<ItemVariationStore>
varStore; /* Offset to item variation store table. */ varStore; /* Offset to item variation store table. */
Offset32To<DeltaSetIndexMap> Offset32To<DeltaSetIndexMap>
advMap; /* Offset to advance var-idx mapping. */ advMap; /* Offset to advance var-idx mapping. */

View File

@ -56,7 +56,7 @@ struct VariationValueRecord
public: public:
Tag valueTag; /* Four-byte tag identifying a font-wide measure. */ Tag valueTag; /* Four-byte tag identifying a font-wide measure. */
VarIdx varIdx; /* Outer/inner index into VariationStore item. */ VarIdx varIdx; /* Outer/inner index into ItemVariationStore item. */
public: public:
DEFINE_SIZE_STATIC (8); DEFINE_SIZE_STATIC (8);
@ -106,7 +106,7 @@ struct MVAR
out->valueRecordCount = valueRecordCount; out->valueRecordCount = valueRecordCount;
item_variations_t item_vars; item_variations_t item_vars;
const VariationStore& src_var_store = this+varStore; const ItemVariationStore& src_var_store = this+varStore;
if (!item_vars.instantiate (src_var_store, c->plan)) if (!item_vars.instantiate (src_var_store, c->plan))
return_trace (false); return_trace (false);
@ -159,7 +159,7 @@ protected:
HBUINT16 valueRecordSize;/* The size in bytes of each value record — HBUINT16 valueRecordSize;/* The size in bytes of each value record —
* must be greater than zero. */ * must be greater than zero. */
HBUINT16 valueRecordCount;/* The number of value records — may be zero. */ HBUINT16 valueRecordCount;/* The number of value records — may be zero. */
Offset16To<VariationStore> Offset16To<ItemVariationStore>
varStore; /* Offset to item variation store table. */ varStore; /* Offset to item variation store table. */
UnsizedArrayOf<HBUINT8> UnsizedArrayOf<HBUINT8>
valuesZ; /* Array of value records. The records must be valuesZ; /* Array of value records. The records must be

View File

@ -163,7 +163,7 @@ struct hb_priority_queue_t
goto repeat; goto repeat;
} }
void swap (unsigned a, unsigned b) void swap (unsigned a, unsigned b) noexcept
{ {
assert (a < heap.length); assert (a < heap.length);
assert (b < heap.length); assert (b < heap.length);

View File

@ -238,6 +238,54 @@ bool _try_isolating_subgraphs (const hb_vector_t<graph::overflow_record_t>& over
return true; return true;
} }
static inline
bool _resolve_shared_overflow(const hb_vector_t<graph::overflow_record_t>& overflows,
int overflow_index,
graph_t& sorted_graph)
{
const graph::overflow_record_t& r = overflows[overflow_index];
// Find all of the parents in overflowing links that link to this
// same child node. We will then try duplicating the child node and
// re-assigning all of these parents to the duplicate.
hb_set_t parents;
parents.add(r.parent);
for (int i = overflow_index - 1; i >= 0; i--) {
const graph::overflow_record_t& r2 = overflows[i];
if (r2.child == r.child) {
parents.add(r2.parent);
}
}
unsigned result = sorted_graph.duplicate(&parents, r.child);
if (result == (unsigned) -1 && parents.get_population() > 2) {
// All links to the child are overflowing, so we can't include all
// in the duplication. Remove one parent from the duplication.
// Remove the lowest index parent, which will be the closest to the child.
parents.del(parents.get_min());
result = sorted_graph.duplicate(&parents, r.child);
}
if (result == (unsigned) -1) return result;
if (parents.get_population() > 1) {
// If the duplicated node has more than one parent pre-emptively raise it's priority to the maximum.
// This will place it close to the parents. Node's with only one parent, don't need this as normal overflow
// resolution will raise priority if needed.
//
// Reasoning: most of the parents to this child are likely at the same layer in the graph. Duplicating
// the child will theoretically allow it to be placed closer to it's parents. However, due to the shortest
// distance sort by default it's placement will remain in the same layer, thus it will remain in roughly the
// same position (and distance from parents) as the original child node. The overflow resolution will attempt
// to move nodes closer, but only for non-shared nodes. Since this node is shared, it will simply be given
// further duplication which defeats the attempt to duplicate with multiple parents. To fix this we
// pre-emptively raise priority now which allows the duplicated node to pack into the same layer as it's parents.
sorted_graph.vertices_[result].give_max_priority();
}
return result;
}
static inline static inline
bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows, bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
hb_set_t& priority_bumped_parents, hb_set_t& priority_bumped_parents,
@ -254,7 +302,7 @@ bool _process_overflows (const hb_vector_t<graph::overflow_record_t>& overflows,
{ {
// The child object is shared, we may be able to eliminate the overflow // The child object is shared, we may be able to eliminate the overflow
// by duplicating it. // by duplicating it.
if (sorted_graph.duplicate (r.parent, r.child) == (unsigned) -1) continue; if (!_resolve_shared_overflow(overflows, i, sorted_graph)) continue;
return true; return true;
} }
@ -388,7 +436,7 @@ template<typename T>
inline hb_blob_t* inline hb_blob_t*
hb_resolve_overflows (const T& packed, hb_resolve_overflows (const T& packed,
hb_tag_t table_tag, hb_tag_t table_tag,
unsigned max_rounds = 20, unsigned max_rounds = 32,
bool recalculate_extensions = false) { bool recalculate_extensions = false) {
graph_t sorted_graph (packed); graph_t sorted_graph (packed);
if (sorted_graph.in_error ()) if (sorted_graph.in_error ())

View File

@ -91,7 +91,27 @@ struct hb_serialize_context_t
} }
#endif #endif
friend void swap (object_t& a, object_t& b) bool add_virtual_link (objidx_t objidx)
{
if (!objidx)
return false;
auto& link = *virtual_links.push ();
if (virtual_links.in_error ())
return false;
link.objidx = objidx;
// Remaining fields were previously zero'd by push():
// link.width = 0;
// link.is_signed = 0;
// link.whence = 0;
// link.position = 0;
// link.bias = 0;
return true;
}
friend void swap (object_t& a, object_t& b) noexcept
{ {
hb_swap (a.head, b.head); hb_swap (a.head, b.head);
hb_swap (a.tail, b.tail); hb_swap (a.tail, b.tail);
@ -156,9 +176,9 @@ struct hb_serialize_context_t
object_t *next; object_t *next;
auto all_links () const HB_AUTO_RETURN auto all_links () const HB_AUTO_RETURN
(( hb_concat (this->real_links, this->virtual_links) )); (( hb_concat (real_links, virtual_links) ));
auto all_links_writer () HB_AUTO_RETURN auto all_links_writer () HB_AUTO_RETURN
(( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) )); (( hb_concat (real_links.writer (), virtual_links.writer ()) ));
}; };
struct snapshot_t struct snapshot_t
@ -469,16 +489,40 @@ struct hb_serialize_context_t
assert (current); assert (current);
auto& link = *current->virtual_links.push (); if (!current->add_virtual_link(objidx))
if (current->virtual_links.in_error ())
err (HB_SERIALIZE_ERROR_OTHER); err (HB_SERIALIZE_ERROR_OTHER);
}
link.width = 0; objidx_t last_added_child_index() const {
link.objidx = objidx; if (unlikely (in_error ())) return (objidx_t) -1;
link.is_signed = 0;
link.whence = 0; assert (current);
link.position = 0; if (!bool(current->real_links)) {
link.bias = 0; return (objidx_t) -1;
}
return current->real_links[current->real_links.length - 1].objidx;
}
// For the current object ensure that the sub-table bytes for child objidx are always placed
// after the subtable bytes for any other existing children. This only ensures that the
// repacker will not move the target subtable before the other children
// (by adding virtual links). It is up to the caller to ensure the initial serialization
// order is correct.
void repack_last(objidx_t objidx) {
if (unlikely (in_error ())) return;
if (!objidx)
return;
assert (current);
for (auto& l : current->real_links) {
if (l.objidx == objidx) {
continue;
}
packed[l.objidx]->add_virtual_link(objidx);
}
} }
template <typename T> template <typename T>

View File

@ -44,10 +44,10 @@ struct hb_sparseset_t
~hb_sparseset_t () { fini (); } ~hb_sparseset_t () { fini (); }
hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); } hb_sparseset_t (const hb_sparseset_t& other) : hb_sparseset_t () { set (other); }
hb_sparseset_t (hb_sparseset_t&& other) : hb_sparseset_t () { s = std::move (other.s); } hb_sparseset_t (hb_sparseset_t&& other) noexcept : hb_sparseset_t () { s = std::move (other.s); }
hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; } hb_sparseset_t& operator = (const hb_sparseset_t& other) { set (other); return *this; }
hb_sparseset_t& operator = (hb_sparseset_t&& other) { s = std::move (other.s); return *this; } hb_sparseset_t& operator = (hb_sparseset_t&& other) noexcept { s = std::move (other.s); return *this; }
friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) { hb_swap (a.s, b.s); } friend void swap (hb_sparseset_t& a, hb_sparseset_t& b) noexcept { hb_swap (a.s, b.s); }
hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t () hb_sparseset_t (std::initializer_list<hb_codepoint_t> lst) : hb_sparseset_t ()
{ {
@ -166,7 +166,7 @@ struct hb_set_t : hb_sparseset_t<hb_bit_set_invertible_t>
~hb_set_t () = default; ~hb_set_t () = default;
hb_set_t () : sparseset () {}; hb_set_t () : sparseset () {};
hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {}; hb_set_t (const hb_set_t &o) : sparseset ((sparseset &) o) {};
hb_set_t (hb_set_t&& o) : sparseset (std::move ((sparseset &) o)) {} hb_set_t (hb_set_t&& o) noexcept : sparseset (std::move ((sparseset &) o)) {}
hb_set_t& operator = (const hb_set_t&) = default; hb_set_t& operator = (const hb_set_t&) = default;
hb_set_t& operator = (hb_set_t&&) = default; hb_set_t& operator = (hb_set_t&&) = default;
hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {} hb_set_t (std::initializer_list<hb_codepoint_t> lst) : sparseset (lst) {}

View File

@ -248,7 +248,7 @@ struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs
struct cff2_private_blend_encoder_param_t struct cff2_private_blend_encoder_param_t
{ {
cff2_private_blend_encoder_param_t (hb_serialize_context_t *c, cff2_private_blend_encoder_param_t (hb_serialize_context_t *c,
const CFF2VariationStore *varStore, const CFF2ItemVariationStore *varStore,
hb_array_t<int> normalized_coords) : hb_array_t<int> normalized_coords) :
c (c), varStore (varStore), normalized_coords (normalized_coords) {} c (c), varStore (varStore), normalized_coords (normalized_coords) {}
@ -284,7 +284,7 @@ struct cff2_private_blend_encoder_param_t
unsigned ivs = 0; unsigned ivs = 0;
unsigned region_count = 0; unsigned region_count = 0;
hb_vector_t<float> scalars; hb_vector_t<float> scalars;
const CFF2VariationStore *varStore = nullptr; const CFF2ItemVariationStore *varStore = nullptr;
hb_array_t<int> normalized_coords; hb_array_t<int> normalized_coords;
}; };
@ -378,7 +378,7 @@ struct cff2_private_dict_blend_opset_t : dict_opset_t
struct cff2_private_dict_op_serializer_t : op_serializer_t struct cff2_private_dict_op_serializer_t : op_serializer_t
{ {
cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_, cff2_private_dict_op_serializer_t (bool desubroutinize_, bool drop_hints_, bool pinned_,
const CFF::CFF2VariationStore* varStore_, const CFF::CFF2ItemVariationStore* varStore_,
hb_array_t<int> normalized_coords_) hb_array_t<int> normalized_coords_)
: desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_), : desubroutinize (desubroutinize_), drop_hints (drop_hints_), pinned (pinned_),
varStore (varStore_), normalized_coords (normalized_coords_) {} varStore (varStore_), normalized_coords (normalized_coords_) {}
@ -416,7 +416,7 @@ struct cff2_private_dict_op_serializer_t : op_serializer_t
const bool desubroutinize; const bool desubroutinize;
const bool drop_hints; const bool drop_hints;
const bool pinned; const bool pinned;
const CFF::CFF2VariationStore* varStore; const CFF::CFF2ItemVariationStore* varStore;
hb_array_t<int> normalized_coords; hb_array_t<int> normalized_coords;
}; };
@ -628,10 +628,10 @@ OT::cff2::accelerator_subset_t::serialize (hb_serialize_context_t *c,
} }
/* variation store */ /* variation store */
if (varStore != &Null (CFF2VariationStore) && if (varStore != &Null (CFF2ItemVariationStore) &&
!plan.pinned) !plan.pinned)
{ {
auto *dest = c->push<CFF2VariationStore> (); auto *dest = c->push<CFF2ItemVariationStore> ();
if (unlikely (!dest->serialize (c, varStore))) if (unlikely (!dest->serialize (c, varStore)))
{ {
c->pop_discard (); c->pop_discard ();

View File

@ -24,6 +24,7 @@
* Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod * Google Author(s): Garret Rieger, Rod Sheeter, Behdad Esfahbod
*/ */
#include "hb-subset-instancer-solver.hh"
#include "hb-subset.hh" #include "hb-subset.hh"
#include "hb-set.hh" #include "hb-set.hh"
#include "hb-utf.hh" #include "hb-utf.hh"
@ -50,7 +51,6 @@ hb_subset_input_t::hb_subset_input_t ()
HB_TAG ('k', 'e', 'r', 'n'), HB_TAG ('k', 'e', 'r', 'n'),
// Copied from fontTools: // Copied from fontTools:
HB_TAG ('B', 'A', 'S', 'E'),
HB_TAG ('J', 'S', 'T', 'F'), HB_TAG ('J', 'S', 'T', 'F'),
HB_TAG ('D', 'S', 'I', 'G'), HB_TAG ('D', 'S', 'I', 'G'),
HB_TAG ('E', 'B', 'D', 'T'), HB_TAG ('E', 'B', 'D', 'T'),
@ -417,6 +417,46 @@ hb_subset_input_keep_everything (hb_subset_input_t *input)
} }
#ifndef HB_NO_VAR #ifndef HB_NO_VAR
/**
* hb_subset_input_pin_all_axes_to_default: (skip)
* @input: a #hb_subset_input_t object.
* @face: a #hb_face_t object.
*
* Pin all axes to default locations in the given subset input object.
*
* All axes in a font must be pinned. Additionally, `CFF2` table, if present,
* will be de-subroutinized.
*
* Return value: `true` if success, `false` otherwise
*
* Since: 8.3.1
**/
HB_EXTERN hb_bool_t
hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input,
hb_face_t *face)
{
unsigned axis_count = hb_ot_var_get_axis_count (face);
if (!axis_count) return false;
hb_ot_var_axis_info_t *axis_infos = (hb_ot_var_axis_info_t *) hb_calloc (axis_count, sizeof (hb_ot_var_axis_info_t));
if (unlikely (!axis_infos)) return false;
(void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axis_infos);
for (unsigned i = 0; i < axis_count; i++)
{
hb_tag_t axis_tag = axis_infos[i].tag;
float default_val = axis_infos[i].default_value;
if (!input->axes_location.set (axis_tag, Triple (default_val, default_val, default_val)))
{
hb_free (axis_infos);
return false;
}
}
hb_free (axis_infos);
return true;
}
/** /**
* hb_subset_input_pin_axis_to_default: (skip) * hb_subset_input_pin_axis_to_default: (skip)
* @input: a #hb_subset_input_t object. * @input: a #hb_subset_input_t object.
@ -481,16 +521,13 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
* @input: a #hb_subset_input_t object. * @input: a #hb_subset_input_t object.
* @face: a #hb_face_t object. * @face: a #hb_face_t object.
* @axis_tag: Tag of the axis * @axis_tag: Tag of the axis
* @axis_min_value: Minimum value of the axis variation range to set * @axis_min_value: Minimum value of the axis variation range to set, if NaN the existing min will be used.
* @axis_max_value: Maximum value of the axis variation range to set * @axis_max_value: Maximum value of the axis variation range to set if NaN the existing max will be used.
* @axis_def_value: Default value of the axis variation range to set, in case of * @axis_def_value: Default value of the axis variation range to set, if NaN the existing default will be used.
* null, it'll be determined automatically
* *
* Restricting the range of variation on an axis in the given subset input object. * Restricting the range of variation on an axis in the given subset input object.
* New min/default/max values will be clamped if they're not within the fvar axis range. * New min/default/max values will be clamped if they're not within the fvar axis range.
* If the new default value is null: *
* If the fvar axis default value is within the new range, then new default
* value is the same as original default value.
* If the fvar axis default value is not within the new range, the new default * If the fvar axis default value is not within the new range, the new default
* value will be changed to the new min or max value, whichever is closer to the fvar * value will be changed to the new min or max value, whichever is closer to the fvar
* axis default. * axis default.
@ -509,21 +546,57 @@ hb_subset_input_set_axis_range (hb_subset_input_t *input,
hb_tag_t axis_tag, hb_tag_t axis_tag,
float axis_min_value, float axis_min_value,
float axis_max_value, float axis_max_value,
float *axis_def_value /* IN, maybe NULL */) float axis_def_value)
{ {
if (axis_min_value > axis_max_value)
return false;
hb_ot_var_axis_info_t axis_info; hb_ot_var_axis_info_t axis_info;
if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info)) if (!hb_ot_var_find_axis_info (face, axis_tag, &axis_info))
return false; return false;
float new_min_val = hb_clamp(axis_min_value, axis_info.min_value, axis_info.max_value); float min = !std::isnan(axis_min_value) ? axis_min_value : axis_info.min_value;
float new_max_val = hb_clamp(axis_max_value, axis_info.min_value, axis_info.max_value); float max = !std::isnan(axis_max_value) ? axis_max_value : axis_info.max_value;
float new_default_val = axis_def_value ? *axis_def_value : axis_info.default_value; float def = !std::isnan(axis_def_value) ? axis_def_value : axis_info.default_value;
new_default_val = hb_clamp(new_default_val, new_min_val, new_max_val);
if (min > max)
return false;
float new_min_val = hb_clamp(min, axis_info.min_value, axis_info.max_value);
float new_max_val = hb_clamp(max, axis_info.min_value, axis_info.max_value);
float new_default_val = hb_clamp(def, new_min_val, new_max_val);
return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val)); return input->axes_location.set (axis_tag, Triple (new_min_val, new_default_val, new_max_val));
} }
/**
* hb_subset_input_get_axis_range: (skip)
* @input: a #hb_subset_input_t object.
* @axis_tag: Tag of the axis
* @axis_min_value: Set to the previously configured minimum value of the axis variation range.
* @axis_max_value: Set to the previously configured maximum value of the axis variation range.
* @axis_def_value: Set to the previously configured default value of the axis variation range.
*
* Gets the axis range assigned by previous calls to hb_subset_input_set_axis_range.
*
* Return value: `true` if a range has been set for this axis tag, `false` otherwise.
*
* XSince: EXPERIMENTAL
**/
HB_EXTERN hb_bool_t
hb_subset_input_get_axis_range (hb_subset_input_t *input,
hb_tag_t axis_tag,
float *axis_min_value,
float *axis_max_value,
float *axis_def_value)
{
Triple* triple;
if (!input->axes_location.has(axis_tag, &triple)) {
return false;
}
*axis_min_value = triple->minimum;
*axis_def_value = triple->middle;
*axis_max_value = triple->maximum;
return true;
}
#endif #endif
#endif #endif

View File

@ -0,0 +1,532 @@
/*
* Copyright © 2024 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#include "hb-subset-instancer-iup.hh"
/* This file is a straight port of the following:
*
* https://github.com/fonttools/fonttools/blob/main/Lib/fontTools/varLib/iup.py
*
* Where that file returns optimzied deltas vector, we return optimized
* referenced point indices.
*/
constexpr static unsigned MAX_LOOKBACK = 8;
static void _iup_contour_bound_forced_set (const hb_array_t<const contour_point_t> contour_points,
const hb_array_t<const int> x_deltas,
const hb_array_t<const int> y_deltas,
hb_set_t& forced_set, /* OUT */
float tolerance = 0.f)
{
unsigned len = contour_points.length;
unsigned next_i = 0;
for (int i = len - 1; i >= 0; i--)
{
unsigned last_i = (len + i -1) % len;
for (unsigned j = 0; j < 2; j++)
{
float cj, lcj, ncj;
int dj, ldj, ndj;
if (j == 0)
{
cj = contour_points.arrayZ[i].x;
dj = x_deltas.arrayZ[i];
lcj = contour_points.arrayZ[last_i].x;
ldj = x_deltas.arrayZ[last_i];
ncj = contour_points.arrayZ[next_i].x;
ndj = x_deltas.arrayZ[next_i];
}
else
{
cj = contour_points.arrayZ[i].y;
dj = y_deltas.arrayZ[i];
lcj = contour_points.arrayZ[last_i].y;
ldj = y_deltas.arrayZ[last_i];
ncj = contour_points.arrayZ[next_i].y;
ndj = y_deltas.arrayZ[next_i];
}
float c1, c2;
int d1, d2;
if (lcj <= ncj)
{
c1 = lcj;
c2 = ncj;
d1 = ldj;
d2 = ndj;
}
else
{
c1 = ncj;
c2 = lcj;
d1 = ndj;
d2 = ldj;
}
bool force = false;
if (c1 == c2)
{
if (abs (d1 - d2) > tolerance && abs (dj) > tolerance)
force = true;
}
else if (c1 <= cj && cj <= c2)
{
if (!(hb_min (d1, d2) - tolerance <= dj &&
dj <= hb_max (d1, d2) + tolerance))
force = true;
}
else
{
if (d1 != d2)
{
if (cj < c1)
{
if (abs (dj) > tolerance &&
abs (dj - d1) > tolerance &&
((dj - tolerance < d1) != (d1 < d2)))
force = true;
}
else
{
if (abs (dj) > tolerance &&
abs (dj - d2) > tolerance &&
((d2 < dj + tolerance) != (d1 < d2)))
force = true;
}
}
}
if (force)
{
forced_set.add (i);
break;
}
}
next_i = i;
}
}
template <typename T,
hb_enable_if (hb_is_trivially_copyable (T))>
static bool rotate_array (const hb_array_t<const T>& org_array,
int k,
hb_vector_t<T>& out)
{
unsigned n = org_array.length;
if (!n) return true;
if (unlikely (!out.resize (n, false)))
return false;
unsigned item_size = hb_static_size (T);
if (k < 0)
k = n - (-k) % n;
else
k %= n;
hb_memcpy ((void *) out.arrayZ, (const void *) (org_array.arrayZ + n - k), k * item_size);
hb_memcpy ((void *) (out.arrayZ + k), (const void *) org_array.arrayZ, (n - k) * item_size);
return true;
}
static bool rotate_set (const hb_set_t& org_set,
int k,
unsigned n,
hb_set_t& out)
{
if (!n) return false;
k %= n;
if (k < 0)
k = n + k;
if (k == 0)
{
out.set (org_set);
}
else
{
for (auto v : org_set)
out.add ((v + k) % n);
}
return !out.in_error ();
}
/* Given two reference coordinates (start and end of contour_points array),
* output interpolated deltas for points in between */
static bool _iup_segment (const hb_array_t<const contour_point_t> contour_points,
const hb_array_t<const int> x_deltas,
const hb_array_t<const int> y_deltas,
const contour_point_t& p1, const contour_point_t& p2,
int p1_dx, int p2_dx,
int p1_dy, int p2_dy,
hb_vector_t<float>& interp_x_deltas, /* OUT */
hb_vector_t<float>& interp_y_deltas /* OUT */)
{
unsigned n = contour_points.length;
if (unlikely (!interp_x_deltas.resize (n, false) ||
!interp_y_deltas.resize (n, false)))
return false;
for (unsigned j = 0; j < 2; j++)
{
float x1, x2, d1, d2;
float *out;
if (j == 0)
{
x1 = p1.x;
x2 = p2.x;
d1 = p1_dx;
d2 = p2_dx;
out = interp_x_deltas.arrayZ;
}
else
{
x1 = p1.y;
x2 = p2.y;
d1 = p1_dy;
d2 = p2_dy;
out = interp_y_deltas.arrayZ;
}
if (x1 == x2)
{
if (d1 == d2)
{
for (unsigned i = 0; i < n; i++)
out[i] = d1;
}
else
{
for (unsigned i = 0; i < n; i++)
out[i] = 0.f;
}
continue;
}
if (x1 > x2)
{
hb_swap (x1, x2);
hb_swap (d1, d2);
}
float scale = (d2 - d1) / (x2 - x1);
for (unsigned i = 0; i < n; i++)
{
float x = j == 0 ? contour_points.arrayZ[i].x : contour_points.arrayZ[i].y;
float d;
if (x <= x1)
d = d1;
else if (x >= x2)
d = d2;
else
d = d1 + (x - x1) * scale;
out[i] = d;
}
}
return true;
}
static bool _can_iup_in_between (const hb_array_t<const contour_point_t> contour_points,
const hb_array_t<const int> x_deltas,
const hb_array_t<const int> y_deltas,
const contour_point_t& p1, const contour_point_t& p2,
int p1_dx, int p2_dx,
int p1_dy, int p2_dy,
float tolerance)
{
hb_vector_t<float> interp_x_deltas, interp_y_deltas;
if (!_iup_segment (contour_points, x_deltas, y_deltas,
p1, p2, p1_dx, p2_dx, p1_dy, p2_dy,
interp_x_deltas, interp_y_deltas))
return false;
unsigned num = contour_points.length;
for (unsigned i = 0; i < num; i++)
{
float dx = x_deltas.arrayZ[i] - interp_x_deltas.arrayZ[i];
float dy = y_deltas.arrayZ[i] - interp_y_deltas.arrayZ[i];
if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance)
return false;
}
return true;
}
static bool _iup_contour_optimize_dp (const contour_point_vector_t& contour_points,
const hb_vector_t<int>& x_deltas,
const hb_vector_t<int>& y_deltas,
const hb_set_t& forced_set,
float tolerance,
unsigned lookback,
hb_vector_t<unsigned>& costs, /* OUT */
hb_vector_t<int>& chain /* OUT */)
{
unsigned n = contour_points.length;
if (unlikely (!costs.resize (n, false) ||
!chain.resize (n, false)))
return false;
lookback = hb_min (lookback, MAX_LOOKBACK);
for (unsigned i = 0; i < n; i++)
{
unsigned best_cost = (i == 0 ? 1 : costs.arrayZ[i-1] + 1);
costs.arrayZ[i] = best_cost;
chain.arrayZ[i] = (i == 0 ? -1 : i - 1);
if (i > 0 && forced_set.has (i - 1))
continue;
int lookback_index = hb_max ((int) i - (int) lookback + 1, -1);
for (int j = i - 2; j >= lookback_index; j--)
{
unsigned cost = j == -1 ? 1 : costs.arrayZ[j] + 1;
/* num points between i and j */
unsigned num_points = i - j - 1;
unsigned p1 = (j == -1 ? n - 1 : j);
if (cost < best_cost &&
_can_iup_in_between (contour_points.as_array ().sub_array (j + 1, num_points),
x_deltas.as_array ().sub_array (j + 1, num_points),
y_deltas.as_array ().sub_array (j + 1, num_points),
contour_points.arrayZ[p1], contour_points.arrayZ[i],
x_deltas.arrayZ[p1], x_deltas.arrayZ[i],
y_deltas.arrayZ[p1], y_deltas.arrayZ[i],
tolerance))
{
best_cost = cost;
costs.arrayZ[i] = best_cost;
chain.arrayZ[i] = j;
}
if (j > 0 && forced_set.has (j))
break;
}
}
return true;
}
static bool _iup_contour_optimize (const hb_array_t<const contour_point_t> contour_points,
const hb_array_t<const int> x_deltas,
const hb_array_t<const int> y_deltas,
hb_array_t<bool> opt_indices, /* OUT */
float tolerance = 0.f)
{
unsigned n = contour_points.length;
if (opt_indices.length != n ||
x_deltas.length != n ||
y_deltas.length != n)
return false;
bool all_within_tolerance = true;
for (unsigned i = 0; i < n; i++)
{
int dx = x_deltas.arrayZ[i];
int dy = y_deltas.arrayZ[i];
if (sqrtf ((float)dx * dx + (float)dy * dy) > tolerance)
{
all_within_tolerance = false;
break;
}
}
/* If all are within tolerance distance, do nothing, opt_indices is
* initilized to false */
if (all_within_tolerance)
return true;
/* If there's exactly one point, return it */
if (n == 1)
{
opt_indices.arrayZ[0] = true;
return true;
}
/* If all deltas are exactly the same, return just one (the first one) */
bool all_deltas_are_equal = true;
for (unsigned i = 1; i < n; i++)
if (x_deltas.arrayZ[i] != x_deltas.arrayZ[0] ||
y_deltas.arrayZ[i] != y_deltas.arrayZ[0])
{
all_deltas_are_equal = false;
break;
}
if (all_deltas_are_equal)
{
opt_indices.arrayZ[0] = true;
return true;
}
/* else, solve the general problem using Dynamic Programming */
hb_set_t forced_set;
_iup_contour_bound_forced_set (contour_points, x_deltas, y_deltas, forced_set, tolerance);
if (!forced_set.is_empty ())
{
int k = n - 1 - forced_set.get_max ();
if (k < 0)
return false;
hb_vector_t<int> rot_x_deltas, rot_y_deltas;
contour_point_vector_t rot_points;
hb_set_t rot_forced_set;
if (!rotate_array (contour_points, k, rot_points) ||
!rotate_array (x_deltas, k, rot_x_deltas) ||
!rotate_array (y_deltas, k, rot_y_deltas) ||
!rotate_set (forced_set, k, n, rot_forced_set))
return false;
hb_vector_t<unsigned> costs;
hb_vector_t<int> chain;
if (!_iup_contour_optimize_dp (rot_points, rot_x_deltas, rot_y_deltas,
rot_forced_set, tolerance, n,
costs, chain))
return false;
hb_set_t solution;
int index = n - 1;
while (index != -1)
{
solution.add (index);
index = chain.arrayZ[index];
}
if (solution.is_empty () ||
forced_set.get_population () > solution.get_population ())
return false;
for (unsigned i : solution)
opt_indices.arrayZ[i] = true;
hb_vector_t<bool> rot_indices;
const hb_array_t<const bool> opt_indices_array (opt_indices.arrayZ, opt_indices.length);
rotate_array (opt_indices_array, -k, rot_indices);
for (unsigned i = 0; i < n; i++)
opt_indices.arrayZ[i] = rot_indices.arrayZ[i];
}
else
{
hb_vector_t<int> repeat_x_deltas, repeat_y_deltas;
contour_point_vector_t repeat_points;
if (unlikely (!repeat_x_deltas.resize (n * 2, false) ||
!repeat_y_deltas.resize (n * 2, false) ||
!repeat_points.resize (n * 2, false)))
return false;
unsigned contour_point_size = hb_static_size (contour_point_t);
for (unsigned i = 0; i < n; i++)
{
hb_memcpy ((void *) repeat_x_deltas.arrayZ, (const void *) x_deltas.arrayZ, n * sizeof (float));
hb_memcpy ((void *) (repeat_x_deltas.arrayZ + n), (const void *) x_deltas.arrayZ, n * sizeof (float));
hb_memcpy ((void *) repeat_y_deltas.arrayZ, (const void *) y_deltas.arrayZ, n * sizeof (float));
hb_memcpy ((void *) (repeat_y_deltas.arrayZ + n), (const void *) y_deltas.arrayZ, n * sizeof (float));
hb_memcpy ((void *) repeat_points.arrayZ, (const void *) contour_points.arrayZ, n * contour_point_size);
hb_memcpy ((void *) (repeat_points.arrayZ + n), (const void *) contour_points.arrayZ, n * contour_point_size);
}
hb_vector_t<unsigned> costs;
hb_vector_t<int> chain;
if (!_iup_contour_optimize_dp (repeat_points, repeat_x_deltas, repeat_y_deltas,
forced_set, tolerance, n,
costs, chain))
return false;
unsigned best_cost = n + 1;
int len = costs.length;
hb_set_t best_sol;
for (int start = n - 1; start < len; start++)
{
hb_set_t solution;
int i = start;
int lookback = start - (int) n;
while (i > lookback)
{
solution.add (i % n);
i = chain.arrayZ[i];
}
if (i == lookback)
{
unsigned cost_i = i < 0 ? 0 : costs.arrayZ[i];
unsigned cost = costs.arrayZ[start] - cost_i;
if (cost <= best_cost)
{
best_sol.set (solution);
best_cost = cost;
}
}
}
for (unsigned i = 0; i < n; i++)
if (best_sol.has (i))
opt_indices.arrayZ[i] = true;
}
return true;
}
bool iup_delta_optimize (const contour_point_vector_t& contour_points,
const hb_vector_t<int>& x_deltas,
const hb_vector_t<int>& y_deltas,
hb_vector_t<bool>& opt_indices, /* OUT */
float tolerance)
{
if (!opt_indices.resize (contour_points.length))
return false;
hb_vector_t<unsigned> end_points;
unsigned count = contour_points.length;
if (unlikely (!end_points.alloc (count)))
return false;
for (unsigned i = 0; i < count - 4; i++)
if (contour_points.arrayZ[i].is_end_point)
end_points.push (i);
/* phantom points */
for (unsigned i = count - 4; i < count; i++)
end_points.push (i);
if (end_points.in_error ()) return false;
unsigned start = 0;
for (unsigned end : end_points)
{
unsigned len = end - start + 1;
if (!_iup_contour_optimize (contour_points.as_array ().sub_array (start, len),
x_deltas.as_array ().sub_array (start, len),
y_deltas.as_array ().sub_array (start, len),
opt_indices.as_array ().sub_array (start, len),
tolerance))
return false;
start = end + 1;
}
return true;
}

View File

@ -0,0 +1,37 @@
/*
* Copyright © 2024 Google, Inc.
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*/
#ifndef HB_SUBSET_INSTANCER_IUP_HH
#define HB_SUBSET_INSTANCER_IUP_HH
#include "hb-subset-plan.hh"
/* given contour points and deltas, optimize a set of referenced points within error
* tolerance. Returns optimized referenced point indices */
HB_INTERNAL bool iup_delta_optimize (const contour_point_vector_t& contour_points,
const hb_vector_t<int>& x_deltas,
const hb_vector_t<int>& y_deltas,
hb_vector_t<bool>& opt_indices, /* OUT */
float tolerance = 0.f);
#endif /* HB_SUBSET_INSTANCER_IUP_HH */

View File

@ -256,7 +256,10 @@ _solve (Triple tent, Triple axisLimit, bool negative = false)
*/ */
float newUpper = peak + (1 - gain) * (upper - peak); float newUpper = peak + (1 - gain) * (upper - peak);
assert (axisMax <= newUpper); // Because outGain > gain assert (axisMax <= newUpper); // Because outGain > gain
if (newUpper <= axisDef + (axisMax - axisDef) * 2) /* Disabled because ots doesn't like us:
* https://github.com/fonttools/fonttools/issues/3350 */
if (false && (newUpper <= axisDef + (axisMax - axisDef) * 2))
{ {
upper = newUpper; upper = newUpper;
if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper) if (!negative && axisDef + (axisMax - axisDef) * MAX_F2DOT14 < upper)

View File

@ -140,6 +140,15 @@ HB_SUBSET_PLAN_MEMBER (mutable hb_vector_t<unsigned>, bounds_height_vec)
//map: new_gid -> contour points vector //map: new_gid -> contour points vector
HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, contour_point_vector_t>), new_gid_contour_points_map) HB_SUBSET_PLAN_MEMBER (mutable hb_hashmap_t E(<hb_codepoint_t, contour_point_vector_t>), new_gid_contour_points_map)
//new gids set for composite glyphs
HB_SUBSET_PLAN_MEMBER (hb_set_t, composite_new_gids)
//Old BASE item variation index -> (New varidx, 0) mapping
HB_SUBSET_PLAN_MEMBER (hb_hashmap_t E(<unsigned, hb_pair_t E(<unsigned, int>)>), base_variation_idx_map)
//BASE table varstore retained varidx mapping
HB_SUBSET_PLAN_MEMBER (hb_vector_t<hb_inc_bimap_t>, base_varstore_inner_maps)
#ifdef HB_EXPERIMENTAL_API #ifdef HB_EXPERIMENTAL_API
// name table overrides map: hb_ot_name_record_ids_t-> name string new value or // name table overrides map: hb_ot_name_record_ids_t-> name string new value or
// None to indicate should remove // None to indicate should remove

View File

@ -32,6 +32,7 @@
#include "hb-ot-cmap-table.hh" #include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh" #include "hb-ot-glyf-table.hh"
#include "hb-ot-layout-base-table.hh"
#include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gdef-table.hh"
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gsub-table.hh"
@ -431,6 +432,52 @@ _collect_layout_variation_indices (hb_subset_plan_t* plan)
gdef.destroy (); gdef.destroy ();
gpos.destroy (); gpos.destroy ();
} }
#ifndef HB_NO_BASE
/* used by BASE table only, delta is always set to 0 in the output map */
static inline void
_remap_variation_indices (const hb_set_t& indices,
unsigned subtable_count,
hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>>& variation_idx_delta_map /* OUT */)
{
unsigned new_major = 0, new_minor = 0;
unsigned last_major = (indices.get_min ()) >> 16;
for (unsigned idx : indices)
{
uint16_t major = idx >> 16;
if (major >= subtable_count) break;
if (major != last_major)
{
new_minor = 0;
++new_major;
}
unsigned new_idx = (new_major << 16) + new_minor;
variation_idx_delta_map.set (idx, hb_pair_t<unsigned, int> (new_idx, 0));
++new_minor;
last_major = major;
}
}
static inline void
_collect_base_variation_indices (hb_subset_plan_t* plan)
{
hb_blob_ptr_t<OT::BASE> base = plan->source_table<OT::BASE> ();
if (!base->has_var_store ())
{
base.destroy ();
return;
}
hb_set_t varidx_set;
base->collect_variation_indices (plan, varidx_set);
unsigned subtable_count = base->get_var_store ().get_sub_table_count ();
base.destroy ();
_remap_variation_indices (varidx_set, subtable_count, plan->base_variation_idx_map);
_generate_varstore_inner_maps (varidx_set, subtable_count, plan->base_varstore_inner_maps);
}
#endif
#endif #endif
static inline void static inline void
@ -994,8 +1041,8 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
OT::cff2::accelerator_t cff2 (plan->source); OT::cff2::accelerator_t cff2 (plan->source);
if (!cff2.is_valid ()) return; if (!cff2.is_valid ()) return;
hb_font_t *font = nullptr; hb_font_t *font = _get_hb_font_with_variations (plan);
if (unlikely (!plan->check_success (font = _get_hb_font_with_variations (plan)))) if (unlikely (!plan->check_success (font != nullptr)))
{ {
hb_font_destroy (font); hb_font_destroy (font);
return; return;
@ -1073,8 +1120,8 @@ _update_instance_metrics_map_from_cff2 (hb_subset_plan_t *plan)
static bool static bool
_get_instance_glyphs_contour_points (hb_subset_plan_t *plan) _get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
{ {
/* contour_points vector only needed for updating gvar table (infer delta) /* contour_points vector only needed for updating gvar table (infer delta and
* during partial instancing */ * iup delta optimization) during partial instancing */
if (plan->user_axes_location.is_empty () || plan->all_axes_pinned) if (plan->user_axes_location.is_empty () || plan->all_axes_pinned)
return true; return true;
@ -1092,10 +1139,17 @@ _get_instance_glyphs_contour_points (hb_subset_plan_t *plan)
} }
hb_codepoint_t old_gid = _.second; hb_codepoint_t old_gid = _.second;
if (unlikely (!glyf.glyph_for_gid (old_gid).get_all_points_without_var (plan->source, all_points))) auto glyph = glyf.glyph_for_gid (old_gid);
if (unlikely (!glyph.get_all_points_without_var (plan->source, all_points)))
return false; return false;
if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points))) if (unlikely (!plan->new_gid_contour_points_map.set (new_gid, all_points)))
return false; return false;
#ifdef HB_EXPERIMENTAL_API
/* composite new gids are only needed by iup delta optimization */
if ((plan->flags & HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS) && glyph.is_composite ())
plan->composite_new_gids.add (new_gid);
#endif
} }
return true; return true;
} }
@ -1205,6 +1259,13 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face,
if (!drop_tables.has (HB_OT_TAG_GDEF)) if (!drop_tables.has (HB_OT_TAG_GDEF))
_remap_used_mark_sets (this, used_mark_sets_map); _remap_used_mark_sets (this, used_mark_sets_map);
#ifndef HB_NO_VAR
#ifndef HB_NO_BASE
if (!drop_tables.has (HB_OT_TAG_BASE))
_collect_base_variation_indices (this);
#endif
#endif
if (unlikely (in_error ())) if (unlikely (in_error ()))
return; return;

View File

@ -78,6 +78,13 @@ struct contour_point_t
y = x * matrix[1] + y * matrix[3]; y = x * matrix[1] + y * matrix[3];
x = x_; x = x_;
} }
void add_delta (float delta_x, float delta_y)
{
x += delta_x;
y += delta_y;
}
HB_ALWAYS_INLINE HB_ALWAYS_INLINE
void translate (const contour_point_t &p) { x += p.x; y += p.y; } void translate (const contour_point_t &p) { x += p.x; y += p.y; }
@ -99,6 +106,22 @@ struct contour_point_vector_t : hb_vector_t<contour_point_t>
unsigned count = a.length; unsigned count = a.length;
hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0])); hb_memcpy (arrayZ, a.arrayZ, count * sizeof (arrayZ[0]));
} }
bool add_deltas (const hb_vector_t<float> deltas_x,
const hb_vector_t<float> deltas_y,
const hb_vector_t<bool> indices)
{
if (indices.length != deltas_x.length ||
indices.length != deltas_y.length)
return false;
for (unsigned i = 0; i < indices.length; i++)
{
if (!indices.arrayZ[i]) continue;
arrayZ[i].add_delta (deltas_x.arrayZ[i], deltas_y.arrayZ[i]);
}
return true;
}
}; };
namespace OT { namespace OT {
@ -147,7 +170,7 @@ struct hb_subset_plan_t
bool gsub_insert_catch_all_feature_variation_rec; bool gsub_insert_catch_all_feature_variation_rec;
bool gpos_insert_catch_all_feature_variation_rec; bool gpos_insert_catch_all_feature_variation_rec;
// whether GDEF VarStore is retained // whether GDEF ItemVariationStore is retained
mutable bool has_gdef_varstore; mutable bool has_gdef_varstore;
#define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name; #define HB_SUBSET_PLAN_MEMBER(Type, Name) Type Name;

View File

@ -48,6 +48,7 @@
#include "hb-ot-cff2-table.hh" #include "hb-ot-cff2-table.hh"
#include "hb-ot-vorg-table.hh" #include "hb-ot-vorg-table.hh"
#include "hb-ot-name-table.hh" #include "hb-ot-name-table.hh"
#include "hb-ot-layout-base-table.hh"
#include "hb-ot-layout-gsub-table.hh" #include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gpos-table.hh" #include "hb-ot-layout-gpos-table.hh"
#include "hb-ot-var-avar-table.hh" #include "hb-ot-var-avar-table.hh"
@ -503,6 +504,7 @@ _subset_table (hb_subset_plan_t *plan,
case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf); case HB_OT_TAG_CBLC: return _subset<const OT::CBLC> (plan, buf);
case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */ case HB_OT_TAG_CBDT: return true; /* skip CBDT, handled by CBLC */
case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf); case HB_OT_TAG_MATH: return _subset<const OT::MATH> (plan, buf);
case HB_OT_TAG_BASE: return _subset<const OT::BASE> (plan, buf);
#ifndef HB_NO_SUBSET_CFF #ifndef HB_NO_SUBSET_CFF
case HB_OT_TAG_CFF1: return _subset<const OT::cff1> (plan, buf); case HB_OT_TAG_CFF1: return _subset<const OT::cff1> (plan, buf);
@ -548,6 +550,7 @@ _subset_table (hb_subset_plan_t *plan,
} }
#endif #endif
return _passthrough (plan, tag); return _passthrough (plan, tag);
default: default:
if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED) if (plan->flags & HB_SUBSET_FLAGS_PASSTHROUGH_UNRECOGNIZED)
return _passthrough (plan, tag); return _passthrough (plan, tag);

View File

@ -76,6 +76,8 @@ typedef struct hb_subset_plan_t hb_subset_plan_t;
* @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset * @HB_SUBSET_FLAGS_IFTB_REQUIREMENTS: If set enforce requirements on the output subset
* to allow it to be used with incremental font transfer IFTB patches. Primarily, * to allow it to be used with incremental font transfer IFTB patches. Primarily,
* this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL * this forces all outline data to use long (32 bit) offsets. Since: EXPERIMENTAL
* @HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS: If set perform IUP delta optimization on the
* remaining gvar table's deltas. Since: EXPERIMENTAL
* *
* List of boolean properties that can be configured on the subset input. * List of boolean properties that can be configured on the subset input.
* *
@ -95,6 +97,7 @@ typedef enum { /*< flags >*/
HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u, HB_SUBSET_FLAGS_NO_LAYOUT_CLOSURE = 0x00000200u,
#ifdef HB_EXPERIMENTAL_API #ifdef HB_EXPERIMENTAL_API
HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000400u, HB_SUBSET_FLAGS_IFTB_REQUIREMENTS = 0x00000400u,
HB_SUBSET_FLAGS_OPTIMIZE_IUP_DELTAS = 0x00000800u,
#endif #endif
} hb_subset_flags_t; } hb_subset_flags_t;
@ -170,6 +173,10 @@ HB_EXTERN void
hb_subset_input_set_flags (hb_subset_input_t *input, hb_subset_input_set_flags (hb_subset_input_t *input,
unsigned value); unsigned value);
HB_EXTERN hb_bool_t
hb_subset_input_pin_all_axes_to_default (hb_subset_input_t *input,
hb_face_t *face);
HB_EXTERN hb_bool_t HB_EXTERN hb_bool_t
hb_subset_input_pin_axis_to_default (hb_subset_input_t *input, hb_subset_input_pin_axis_to_default (hb_subset_input_t *input,
hb_face_t *face, hb_face_t *face,
@ -182,13 +189,20 @@ hb_subset_input_pin_axis_location (hb_subset_input_t *input,
float axis_value); float axis_value);
#ifdef HB_EXPERIMENTAL_API #ifdef HB_EXPERIMENTAL_API
HB_EXTERN hb_bool_t
hb_subset_input_get_axis_range (hb_subset_input_t *input,
hb_tag_t axis_tag,
float *axis_min_value,
float *axis_max_value,
float *axis_def_value);
HB_EXTERN hb_bool_t HB_EXTERN hb_bool_t
hb_subset_input_set_axis_range (hb_subset_input_t *input, hb_subset_input_set_axis_range (hb_subset_input_t *input,
hb_face_t *face, hb_face_t *face,
hb_tag_t axis_tag, hb_tag_t axis_tag,
float axis_min_value, float axis_min_value,
float axis_max_value, float axis_max_value,
float *axis_def_value); float axis_def_value);
HB_EXTERN hb_bool_t HB_EXTERN hb_bool_t
hb_subset_input_override_name_table (hb_subset_input_t *input, hb_subset_input_override_name_table (hb_subset_input_t *input,

View File

@ -78,7 +78,7 @@ struct hb_vector_t
if (unlikely (in_error ())) return; if (unlikely (in_error ())) return;
copy_array (o); copy_array (o);
} }
hb_vector_t (hb_vector_t &&o) hb_vector_t (hb_vector_t &&o) noexcept
{ {
allocated = o.allocated; allocated = o.allocated;
length = o.length; length = o.length;
@ -122,7 +122,7 @@ struct hb_vector_t
resize (0); resize (0);
} }
friend void swap (hb_vector_t& a, hb_vector_t& b) friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept
{ {
hb_swap (a.allocated, b.allocated); hb_swap (a.allocated, b.allocated);
hb_swap (a.length, b.length); hb_swap (a.length, b.length);
@ -139,7 +139,7 @@ struct hb_vector_t
return *this; return *this;
} }
hb_vector_t& operator = (hb_vector_t &&o) hb_vector_t& operator = (hb_vector_t &&o) noexcept
{ {
hb_swap (*this, o); hb_swap (*this, o);
return *this; return *this;

View File

@ -47,7 +47,7 @@ HB_BEGIN_DECLS
* *
* The minor component of the library version available at compile-time. * The minor component of the library version available at compile-time.
*/ */
#define HB_VERSION_MINOR 3 #define HB_VERSION_MINOR 4
/** /**
* HB_VERSION_MICRO: * HB_VERSION_MICRO:
* *
@ -60,7 +60,7 @@ HB_BEGIN_DECLS
* *
* A string literal containing the library version available at compile-time. * A string literal containing the library version available at compile-time.
*/ */
#define HB_VERSION_STRING "8.3.0" #define HB_VERSION_STRING "8.4.0"
/** /**
* HB_VERSION_ATLEAST: * HB_VERSION_ATLEAST:

View File

@ -240,7 +240,7 @@ acquire_shape_plan (hb_face_t *face,
goto fail; goto fail;
} }
func = wasm_runtime_lookup_function (module_inst, "shape_plan_create", nullptr); func = wasm_runtime_lookup_function (module_inst, "shape_plan_create");
if (func) if (func)
{ {
wasm_val_t results[1]; wasm_val_t results[1];
@ -297,7 +297,7 @@ release_shape_plan (const hb_wasm_face_data_t *face_data,
if (plan->wasm_shape_planptr) if (plan->wasm_shape_planptr)
{ {
auto *func = wasm_runtime_lookup_function (module_inst, "shape_plan_destroy", nullptr); auto *func = wasm_runtime_lookup_function (module_inst, "shape_plan_destroy");
if (func) if (func)
{ {
wasm_val_t arguments[1]; wasm_val_t arguments[1];
@ -395,7 +395,7 @@ retry:
goto fail; goto fail;
} }
func = wasm_runtime_lookup_function (module_inst, "shape", nullptr); func = wasm_runtime_lookup_function (module_inst, "shape");
if (unlikely (!func)) if (unlikely (!func))
{ {
DEBUG_MSG (WASM, module_inst, "Shape function not found."); DEBUG_MSG (WASM, module_inst, "Shape function not found.");

View File

@ -64,6 +64,7 @@
#pragma GCC diagnostic error "-Wbitwise-instead-of-logical" #pragma GCC diagnostic error "-Wbitwise-instead-of-logical"
#pragma GCC diagnostic error "-Wcast-align" #pragma GCC diagnostic error "-Wcast-align"
#pragma GCC diagnostic error "-Wcast-function-type" #pragma GCC diagnostic error "-Wcast-function-type"
#pragma GCC diagnostic error "-Wcast-function-type-strict"
#pragma GCC diagnostic error "-Wconstant-conversion" #pragma GCC diagnostic error "-Wconstant-conversion"
#pragma GCC diagnostic error "-Wcomma" #pragma GCC diagnostic error "-Wcomma"
#pragma GCC diagnostic error "-Wdelete-non-virtual-dtor" #pragma GCC diagnostic error "-Wdelete-non-virtual-dtor"
@ -177,6 +178,11 @@
#define HB_EXTERN __declspec (dllexport) extern #define HB_EXTERN __declspec (dllexport) extern
#endif #endif
// https://github.com/harfbuzz/harfbuzz/pull/4619
#ifndef __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS 1
#endif
#include "hb.h" #include "hb.h"
#define HB_H_IN #define HB_H_IN
#include "hb-ot.h" #include "hb-ot.h"
@ -212,6 +218,12 @@
#include <winapifamily.h> #include <winapifamily.h>
#endif #endif
#ifndef PRId32
# define PRId32 "d"
# define PRIu32 "u"
# define PRIx32 "x"
#endif
#define HB_PASTE1(a,b) a##b #define HB_PASTE1(a,b) a##b
#define HB_PASTE(a,b) HB_PASTE1(a,b) #define HB_PASTE(a,b) HB_PASTE1(a,b)

Some files were not shown because too many files have changed in this diff Show More