This commit is contained in:
Tim Eves 2023-04-18 01:40:17 +09:00 committed by GitHub
commit ad61c681a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
332 changed files with 802852 additions and 0 deletions

28
.gitattributes vendored Normal file
View File

@ -0,0 +1,28 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.cpp text
*.gdh text
*.gdl text
*.h text
*.json text
*.log text
*.md text
*.sh text
*.svg text
*.txt text
*.ttf binary
*.xml text
# Denote all files that are truly binary and should not be modified.
*.png binary
# Files to exclude from git archive
.gitattributes export-ignore
.gitignore export-ignore
# Exclude the font sources in the test/fonts dir
src/graphite2/tests/fonts/*/ export-ignore

31
.gitignore vendored Normal file
View File

@ -0,0 +1,31 @@
*~
*.bak
m4
autom4te.cache
config
configure
config.status
config.log
aclocal.m4
libtool
Makefile.in
Makefile
*.o
*.lo
*.Plo
*.swp
*.la
*.kdev4
*.orig
*.pyc
*.egg-info
.vscode
*.code-workspace
.settings
.tags
.tags_swap
.libs/
.deps/
.externalToolBuilders
src/graphite2/build*/**
builddir/**

37
src/graphite2/.gitattributes vendored Normal file
View File

@ -0,0 +1,37 @@
# Set the default behavior, in case people don't have core.autocrlf set.
* text=auto
# Explicitly declare text files you want to always be normalized and converted
# to native line endings on checkout.
*.c text
*.cfg text
*.cpp text
*.css text
*.gdh text
*.gdl text
*.h text
*.json text
*.log text
*.md text
*.odg binary
*.pdf binary
*.png binary
*.sh text
*.svg text
*.txt text
*.ttf binary
*.xml text
# Declare files that will always have CRLF line endings on checkout.
*.sln text eol=crlf
# Denote all files that are truly binary and should not be modified.
*.png binary
*.jpg binary
# Files to exclude from git archive
.gitattributes export-ignore
.gitignore export-ignore
# Exclude the font sources in the test/fonts dir
tests/fonts/*/ export-ignore

43
src/graphite2/.gitignore vendored Normal file
View File

@ -0,0 +1,43 @@
*~
*.bak
m4
autom4te.cache
config
configure
config.status
config.log
aclocal.m4
libtool
Makefile.in
Makefile
msvs9
vs9
nmake*
*.o
*.lo
*.Plo
*.swp
*.la
*.kdev4
*.orig
*.pyc
*.egg-info
.atom-build.yml
.atom-dbg.cson
.cproject
.project
.pydevproject
.vscode
.settings
.tags
.tags_swap
.libs/
.deps/
.externalToolBuilders
build*/**
release/
debug/
dev/
tests/private/**
*.tgz
*.sha1sum

View File

@ -0,0 +1,100 @@
# SPDX-License-Identifier: MIT
# Copyright 2010, SIL International, All rights reserved.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
cmake_policy(SET CMP0012 NEW)
include(TestBigEndian)
find_package(PythonInterp 3.6)
project(graphite2)
set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR})
if (NOT CMAKE_BUILD_TYPE)
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY VALUE Release)
endif()
option(BUILD_SHARED_LIBS "Make library a shared library instead of static" ON)
enable_language(CXX C)
set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED 99)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED 11)
include_directories(${PROJECT_SOURCE_DIR}/include)
enable_testing()
set(GRAPHITE2_VM_TYPE auto CACHE STRING "Choose the type of vm machine: Auto, Direct or Call.")
option(GRAPHITE2_NFILEFACE "Compile out the gr_make_file_face* APIs")
option(GRAPHITE2_NTRACING "Compile out log segment tracing capability" ON)
option(GRAPHITE2_TELEMETRY "Add memory usage telemetry")
set(GRAPHITE2_SANITIZERS "" CACHE STRING "Set compiler sanitizers passed to -fsanitize")
set(GRAPHITE2_FUZZING_ENGINE libFuzzer.a CACHE STRING "Fuzzing engine to link against for the fuzzers")
message(STATUS "Build: " ${CMAKE_BUILD_TYPE})
string(REPLACE "ON" "shared" _LIB_OBJECT_TYPE ${BUILD_SHARED_LIBS})
string(REPLACE "OFF" "static" _LIB_OBJECT_TYPE ${_LIB_OBJECT_TYPE})
string(REPLACE "ON" "disabled" _FILEFACE_SUPPORT ${GRAPHITE2_NFILEFACE})
string(REPLACE "OFF" "enabled" _FILEFACE_SUPPORT ${_FILEFACE_SUPPORT})
string(REPLACE "ON" "disabled" _TRACING_SUPPORT ${GRAPHITE2_NTRACING})
string(REPLACE "OFF" "enabled" _TRACING_SUPPORT ${_TRACING_SUPPORT})
message(STATUS "Building library: " ${_LIB_OBJECT_TYPE})
message(STATUS "File Face support: " ${_FILEFACE_SUPPORT})
message(STATUS "Tracing support: " ${_TRACING_SUPPORT})
if (GRAPHITE2_SANITIZERS)
string(STRIP ${GRAPHITE2_SANITIZERS} GRAPHITE2_SANITIZERS)
add_compile_options(-fsanitize=${GRAPHITE2_SANITIZERS} -fno-omit-frame-pointer -g -O1)
message(STATUS "Sanitizer support: " ${GRAPHITE2_SANITIZERS})
string(REGEX REPLACE "(,?fuzzer|fuzzer,?)" "" SANITIZERS_LINK ${GRAPHITE2_SANITIZERS})
if (GRAPHITE2_SANITIZERS MATCHES "fuzzer")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize-coverage=trace-pc-guard")
endif ()
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=${SANITIZERS_LINK}")
endif ()
string(TOLOWER ${GRAPHITE2_VM_TYPE} GRAPHITE2_VM_TYPE)
if (NOT GRAPHITE2_VM_TYPE MATCHES "auto|direct|call")
message(SEND_ERROR "unrecognised vm machine type: ${GRAPHITE2_VM_TYPE}. Only Auto, Direct or Call are available")
endif()
if (GRAPHITE2_VM_TYPE STREQUAL "auto")
if (CMAKE_BUILD_TYPE MATCHES "[Rr]el(ease|[Ww]ith[Dd]eb[Ii]nfo)")
set(GRAPHITE2_VM_TYPE "direct")
else(CMAKE_BUILD_TYPE MATCHES "[Rr]el(ease|[Ww]ith[Dd]eb[Ii]nfo)")
set(GRAPHITE2_VM_TYPE "call")
endif()
endif()
if (GRAPHITE2_VM_TYPE STREQUAL "direct" AND NOT (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang"))
message(WARNING "vm machine type direct can only be built using GCC")
set(GRAPHITE2_VM_TYPE "call")
endif()
message(STATUS "Using vm machine type: ${GRAPHITE2_VM_TYPE}")
if (BUILD_SHARED_LIBS)
if (PYTHONINTERP_FOUND)
EXEC_PROGRAM(${PYTHON_EXECUTABLE}
ARGS -c \"import ctypes\; print(ctypes.sizeof(ctypes.c_void_p)) \"
OUTPUT_VARIABLE PYTHON_SIZEOF_VOID_P)
if (NOT (PYTHON_VERSION_MAJOR GREATER 2 AND PYTHON_VERSION_MINOR GREATER 5))
message(WARNING "Python version 3.6 or greater required. Disabling some tests.")
elseif ((PYTHON_SIZEOF_VOID_P STREQUAL CMAKE_SIZEOF_VOID_P) AND (NOT GRAPHITE2_NFILEFACE) AND ((NOT GRAPHITE2_SANITIZERS MATCHES "address") OR CMAKE_COMPILER_IS_GNUCXX))
set(PYTHON_CTYPES_COMPATBILE TRUE)
else ()
if ((NOT GRAPHITE2_SANITIZERS MATCHES "address") OR CMAKE_COMPILER_IS_GNUCXX)
message(WARNING "Python ctypes is incompatible with built DLL. Disabling some tests.")
endif ()
endif ()
endif()
endif()
add_subdirectory(src)
add_subdirectory(tests)
if (NOT GRAPHITE2_NFILEFACE)
add_subdirectory(gr2fonttest)
endif()
set(version 3.0.1)
set(libdir ${CMAKE_INSTALL_PREFIX}/lib${LIB_SUFFIX})
set(includedir ${CMAKE_INSTALL_PREFIX}/include)
configure_file(graphite2.pc.in graphite2.pc)
install(FILES ${PROJECT_BINARY_DIR}/graphite2.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)

27
src/graphite2/COPYING Normal file
View File

@ -0,0 +1,27 @@
Graphite2 is licensed under the terms of the Mozilla
Public License (http://mozilla.org/MPL) or under the GNU General Public
License, as published by the Free Sofware Foundation; either version
2 of the license or (at your option) any later version.
Copyright © 2010-2021 SIL International
For full copyright notices consult the individual files in the package.
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.

View File

@ -0,0 +1,117 @@
# SPDX-License-Identifier: MIT
# Copyright 2011, SIL International, All rights reserved.
include(GetPrerequisites)
function(nolib_test LIBNAME OBJECTFILE)
string(REGEX REPLACE "[][^$.*+?|()-]" "\\\\\\0" LIBNAME_REGEX ${LIBNAME})
if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
add_test(NAME nolib-${LIBNAME}-${PROJECT_NAME}
COMMAND otool -L ${OBJECTFILE})
set_tests_properties(nolib-${LIBNAME}-${PROJECT_NAME} PROPERTIES
FAIL_REGULAR_EXPRESSION "${CMAKE_SHARED_LIBRARY_PREFIX}${LIBNAME_REGEX}[.0-9]+${CMAKE_SHARED_LIBRARY_SUFFIX}")
else (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
add_test(NAME nolib-${LIBNAME}-${PROJECT_NAME}
COMMAND readelf --dynamic ${OBJECTFILE})
set_tests_properties(nolib-${LIBNAME}-${PROJECT_NAME} PROPERTIES
FAIL_REGULAR_EXPRESSION "0x[0-9a-f]+ \\(NEEDED\\)[ \\t]+Shared library: \\[${CMAKE_SHARED_LIBRARY_PREFIX}${LIBNAME_REGEX}${CMAKE_SHARED_LIBRARY_SUFFIX}.*\\]")
endif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
endfunction(nolib_test)
MACRO(GET_TARGET_PROPERTY_WITH_DEFAULT _variable _target _property _default_value)
GET_TARGET_PROPERTY (${_variable} ${_target} ${_property})
IF (${_variable} MATCHES NOTFOUND)
SET (${_variable} ${_default_value})
ENDIF (${_variable} MATCHES NOTFOUND)
ENDMACRO (GET_TARGET_PROPERTY_WITH_DEFAULT)
FUNCTION(CREATE_LIBTOOL_FILE _target _install_DIR)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_static_lib ${_target} STATIC_LIB "")
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_dependency_libs ${_target} LT_DEPENDENCY_LIBS "")
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_current ${_target} LT_VERSION_CURRENT 0)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_age ${_target} LT_VERSION_AGE 0)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_revision ${_target} LT_VERSION_REVISION 0)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_installed ${_target} LT_INSTALLED yes)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_shouldnotlink ${_target} LT_SHOULDNOTLINK no)
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_dlopen ${_target} LT_DLOPEN "")
GET_TARGET_PROPERTY_WITH_DEFAULT(_target_dlpreopen ${_target} LT_DLPREOPEN "")
SET(_lanamewe ${CMAKE_SHARED_LIBRARY_PREFIX}${_target})
SET(_soname ${_lanamewe}${CMAKE_SHARED_LIBRARY_SUFFIX})
SET(_soext ${CMAKE_SHARED_LIBRARY_SUFFIX})
SET(_laname ${PROJECT_BINARY_DIR}/${_lanamewe}.la)
FILE(WRITE ${_laname} "# ${_lanamewe}.la - a libtool library file\n")
FILE(APPEND ${_laname} "# Generated by CMake ${CMAKE_VERSION} (like GNU libtool)\n")
FILE(APPEND ${_laname} "\n# Please DO NOT delete this file!\n# It is necessary for linking the library with libtool.\n\n" )
FILE(APPEND ${_laname} "# The name that we can dlopen(3).\n")
FILE(APPEND ${_laname} "dlname='${_soname}'\n\n")
FILE(APPEND ${_laname} "# Names of this library.\n")
if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
FILE(APPEND ${_laname} "library_names='${_lanamewe}.${_target_current}.${_target_revision}.${_target_age}${_soext} ${_lanamewe}.${_target_current}${_soext} ${_soname}'\n\n")
else (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
FILE(APPEND ${_laname} "library_names='${_soname}.${_target_current}.${_target_revision}.${_target_age} ${_soname}.${_target_current} ${_soname}'\n\n")
endif (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
FILE(APPEND ${_laname} "# The name of the static archive.\n")
FILE(APPEND ${_laname} "old_library='${_target_static_lib}'\n\n")
FILE(APPEND ${_laname} "# Libraries that this one depends upon.\n")
FILE(APPEND ${_laname} "dependency_libs='${_target_dependency_libs}'\n\n")
FILE(APPEND ${_laname} "# Names of additional weak libraries provided by this library\n")
FILE(APPEND ${_laname} "weak_library_names=\n\n")
FILE(APPEND ${_laname} "# Version information for ${_lanamewe}.\n")
FILE(APPEND ${_laname} "current=${_target_current}\n")
FILE(APPEND ${_laname} "age=${_target_age}\n")
FILE(APPEND ${_laname} "revision=${_target_revision}\n\n")
FILE(APPEND ${_laname} "# Is this an already installed library?\n")
FILE(APPEND ${_laname} "installed=${_target_installed}\n\n")
FILE(APPEND ${_laname} "# Should we warn about portability when linking against -modules?\n")
FILE(APPEND ${_laname} "shouldnotlink=${_target_shouldnotlink}\n\n")
FILE(APPEND ${_laname} "# Files to dlopen/dlpreopen\n")
FILE(APPEND ${_laname} "dlopen='${_target_dlopen}'\n")
FILE(APPEND ${_laname} "dlpreopen='${_target_dlpreopen}'\n\n")
FILE(APPEND ${_laname} "# Directory that this library needs to be installed in:\n")
FILE(APPEND ${_laname} "libdir='${CMAKE_INSTALL_PREFIX}${_install_DIR}'\n")
INSTALL( FILES ${_laname} DESTINATION ${CMAKE_INSTALL_PREFIX}${_install_DIR})
ENDFUNCTION(CREATE_LIBTOOL_FILE)
function(fonttest TESTNAME FONTFILE)
if (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.log)
set(PLATFORM_TEST_SUFFIX ${CMAKE_SYSTEM_NAME})
endif (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.log)
if (NOT GRAPHITE2_NFILEFACE)
add_test(NAME ${TESTNAME} COMMAND $<TARGET_FILE:gr2fonttest> -trace ${PROJECT_BINARY_DIR}/${TESTNAME}.json -log ${PROJECT_BINARY_DIR}/${TESTNAME}.log ${PROJECT_SOURCE_DIR}/fonts/${FONTFILE} -codes ${ARGN})
set_tests_properties(${TESTNAME} PROPERTIES TIMEOUT 3)
add_test(NAME ${TESTNAME}Output COMMAND ${CMAKE_COMMAND} -E compare_files ${PROJECT_BINARY_DIR}/${TESTNAME}.log ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${PLATFORM_TEST_SUFFIX}.log)
if ((NOT GRAPHITE2_NTRACING) AND PYTHONINTERP_FOUND)
add_test(NAME ${TESTNAME}Debug COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/jsoncmp ${PROJECT_BINARY_DIR}/${TESTNAME}.json ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}.json)
set_tests_properties(${TESTNAME}Debug PROPERTIES DEPENDS ${TESTNAME})
endif ((NOT GRAPHITE2_NTRACING) AND PYTHONINTERP_FOUND)
set_tests_properties(${TESTNAME}Output PROPERTIES DEPENDS ${TESTNAME})
endif (NOT GRAPHITE2_NFILEFACE)
endfunction(fonttest)
function(feattest TESTNAME FONTFILE)
if (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.log)
set(PLATFORM_TEST_SUFFIX ${CMAKE_SYSTEM_NAME})
endif (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.log)
if (NOT GRAPHITE2_NFILEFACE)
add_test(NAME ${TESTNAME} COMMAND $<TARGET_FILE:gr2fonttest> -log ${PROJECT_BINARY_DIR}/${TESTNAME}.log ${PROJECT_SOURCE_DIR}/fonts/${FONTFILE})
set_tests_properties(${TESTNAME} PROPERTIES TIMEOUT 3)
add_test(NAME ${TESTNAME}Output COMMAND ${CMAKE_COMMAND} -E compare_files ${PROJECT_BINARY_DIR}/${TESTNAME}.log ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${PLATFORM_TEST_SUFFIX}.log)
set_tests_properties(${TESTNAME}Output PROPERTIES DEPENDS ${TESTNAME})
endif (NOT GRAPHITE2_NFILEFACE)
endfunction(feattest)
function(cmptest TESTNAME FONTFILE TEXTFILE)
if (NOT GRAPHITE2_SANITIZERS)
if (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.json)
set(PLATFORM_TEST_SUFFIX ${CMAKE_SYSTEM_NAME})
endif (EXISTS ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${CMAKE_SYSTEM_NAME}.json)
if (PYTHON_CTYPES_COMPATBILE)
add_test(NAME ${TESTNAME} COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/fnttxtrender --graphite_library=$<TARGET_FILE:graphite2> -t ${PROJECT_SOURCE_DIR}/texts/${TEXTFILE} -o ${PROJECT_BINARY_DIR}/${TESTNAME}.json -c ${PROJECT_SOURCE_DIR}/standards/${TESTNAME}${PLATFORM_TEST_SUFFIX}.json ${ARGN} ${PROJECT_SOURCE_DIR}/fonts/${FONTFILE})
set_tests_properties(${TESTNAME} PROPERTIES ENVIRONMENT PYTHONPATH=${CMAKE_SOURCE_DIR}/python)
endif (PYTHON_CTYPES_COMPATBILE)
endif ()
endfunction(cmptest)

22
src/graphite2/LICENSE Normal file
View File

@ -0,0 +1,22 @@
MIT License
Copyright (c) 2010-2022 SIL International
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice (including the next
paragraph) shall be included in all copies or substantial portions of the
Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

View File

@ -0,0 +1 @@
---

View File

@ -0,0 +1,23 @@
# SPDX-License-Identifier: MIT
# Copyright 2010, SIL International, All rights reserved.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
project(gr2fonttest)
enable_testing()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -DUNICODE)
endif()
add_executable(gr2fonttest gr2FontTest.cpp UtfCodec.cpp)
target_link_libraries(gr2fonttest graphite2)
if (BUILD_SHARED_LIBS)
# copy the DLL so that gr2fonttest can find it
add_custom_target(${PROJECT_NAME}_copy_dll ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${graphite2_core_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}graphite2${CMAKE_SHARED_LIBRARY_SUFFIX} ${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
add_dependencies(${PROJECT_NAME}_copy_dll graphite2 gr2fonttest)
endif()
install(TARGETS gr2fonttest EXPORT gr2fonttest RUNTIME DESTINATION bin)

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "UtfCodec.h"
//using namespace graphite2;
namespace graphite2 {
}
using namespace graphite2;
const int8_t _utf_codec<8>::sz_lut[16] =
{
1,1,1,1,1,1,1,1, // 1 byte
0,0,0,0, // trailing byte
2,2, // 2 bytes
3, // 3 bytes
4 // 4 bytes
};
const byte _utf_codec<8>::mask_lut[5] = {0x7f, 0xff, 0x3f, 0x1f, 0x0f};

View File

@ -0,0 +1,189 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdlib>
#include "graphite2/Types.h"
typedef uint8_t byte;
namespace graphite2 {
typedef uint32_t uchar_t;
template <int N>
struct _utf_codec
{
typedef uchar_t codeunit_t;
static void put(codeunit_t * cp, const uchar_t , int8_t & len) throw();
static uchar_t get(const codeunit_t * cp, int8_t & len) throw();
};
template <>
struct _utf_codec<32>
{
private:
static const uchar_t limit = 0x110000;
public:
typedef uint32_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
*cp = usv; l = 1;
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
if (cp[0] < limit) { l = 1; return cp[0]; }
else { l = -1; return 0xFFFD; }
}
};
template <>
struct _utf_codec<16>
{
private:
static const int32_t lead_offset = 0xD800 - (0x10000 >> 10);
static const int32_t surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00;
public:
typedef uint16_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
if (usv < 0x10000) { l = 1; cp[0] = codeunit_t(usv); }
else
{
cp[0] = codeunit_t(lead_offset + (usv >> 10));
cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF));
l = 2;
}
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
const uint32_t uh = cp[0];
l = 1;
if (0xD800 > uh || uh > 0xDFFF) { return uh; }
const uint32_t ul = cp[1];
if (uh > 0xDBFF || 0xDC00 > ul || ul > 0xDFFF) { l = -1; return 0xFFFD; }
++l;
return (uh<<10) + ul + surrogate_offset;
}
};
template <>
struct _utf_codec<8>
{
private:
static const int8_t sz_lut[16];
static const byte mask_lut[5];
public:
typedef uint8_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
if (usv < 0x80) {l = 1; cp[0] = usv; return; }
if (usv < 0x0800) {l = 2; cp[0] = 0xC0 + (usv >> 6); cp[1] = 0x80 + (usv & 0x3F); return; }
if (usv < 0x10000) {l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F); cp[2] = 0x80 + (usv & 0x3F); return; }
else {l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; }
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
const int8_t seq_sz = sz_lut[*cp >> 4];
uchar_t u = *cp & mask_lut[seq_sz];
l = 1;
bool toolong = false;
switch(seq_sz) {
case 4: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong = (u < 0x10);
// no break
case 3: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x20);
// no break
case 2: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x80);
// no break
case 1: break;
case 0: l = -1; return 0xFFFD;
}
if (l != seq_sz || toolong)
{
l = -l;
return 0xFFFD;
}
return u;
}
};
template <typename C>
class _utf_iterator
{
typedef _utf_codec<sizeof(C)*8> codec;
C * cp;
mutable int8_t sl;
public:
typedef C codeunit_type;
typedef uchar_t value_type;
typedef uchar_t * pointer;
class reference
{
const _utf_iterator & _i;
reference(const _utf_iterator & i): _i(i) {}
public:
operator value_type () const throw () { return codec::get(_i.cp, _i.sl); }
reference & operator = (const value_type usv) throw() { codec::put(_i.cp, usv, _i.sl); return *this; }
friend class _utf_iterator;
};
_utf_iterator(const void * us=0) : cp(reinterpret_cast<C *>(const_cast<void *>(us))), sl(1) { }
_utf_iterator & operator ++ () { cp += abs(sl); return *this; }
_utf_iterator operator ++ (int) { _utf_iterator tmp(*this); operator++(); return tmp; }
bool operator == (const _utf_iterator & rhs) const throw() { return cp >= rhs.cp; }
bool operator != (const _utf_iterator & rhs) const throw() { return !operator==(rhs); }
reference operator * () const throw() { return *this; }
pointer operator ->() const throw() { return &operator *(); }
operator codeunit_type * () const throw() { return cp; }
bool error() const throw() { return sl < 1; }
};
template <typename C>
struct utf
{
typedef typename _utf_codec<sizeof(C)*8>::codeunit_t codeunit_t;
typedef _utf_iterator<C> iterator;
typedef _utf_iterator<const C> const_iterator;
};
typedef utf<uint32_t> utf32;
typedef utf<uint16_t> utf16;
typedef utf<uint8_t> utf8;
} // namespace graphite2

View File

@ -0,0 +1,780 @@
// SPDX-License-Identifier: MIT
// Copyright (C) 2005 www.thanlwinsoft.org, SIL International
/*
Responsibility: Keith Stribley, Martin Hosken
Description:
A simple console app that creates a segment using FileFont and dumps a
diagnostic table of the resulting glyph vector to the console.
If graphite has been built with -DTRACING then it will also produce a
diagnostic log of the segment creation in grSegmentLog.txt
*/
#include <cstdio>
#include <cstdlib>
#include <cassert>
#include <climits>
#include <iomanip>
#include <cstring>
#include "UtfCodec.h"
#include "graphite2/Types.h"
#include "graphite2/Segment.h"
#include "graphite2/Log.h"
class Gr2TextSrc
{
public:
Gr2TextSrc(const uint32_t* base, size_t len) : m_buff(base), m_len(len) { }
gr_encform utfEncodingForm() const { return gr_utf32; }
size_t getLength() const { return m_len; }
const void* get_utf_buffer_begin() const { return m_buff; }
private:
const uint32_t* m_buff;
size_t m_len;
};
#ifndef HAVE_STRTOF
float strtof(char * text, char ** /*ignore*/)
{
return static_cast<float>(atof(text));
}
#endif
#ifndef HAVE_STRTOL
long strtol(char * text, char ** /*ignore*/)
{
return atol(text);
}
#endif
class Parameters
{
public:
Parameters();
~Parameters();
void clear();
void closeLog();
bool loadFromArgs(int argc, char *argv[]);
int testFileFont() const;
gr_feature_val* parseFeatures(const gr_face * face) const;
void printFeatures(const gr_face * face) const;
public:
const char * fileName;
const char * features;
float pointSize;
int dpi;
bool lineStart;
bool lineEnd;
bool ws;
bool rtl;
bool useLineFill;
bool noprint;
int useCodes;
bool autoCodes;
int justification;
float width;
int textArgIndex;
unsigned int * pText32;
size_t charLength;
size_t offset;
FILE * log;
char * trace;
char * alltrace;
int codesize;
gr_face_options opts;
private : //defensive since log should not be copied
Parameters(const Parameters&);
Parameters& operator=(const Parameters&);
};
Parameters::Parameters()
{
log = stdout ;
clear();
}
Parameters::~Parameters()
{
free(pText32);
pText32 = NULL;
closeLog();
}
void Parameters::clear()
{
closeLog() ;
fileName = "";
features = nullptr;
pointSize = 12.0f;
dpi = 72;
lineStart = false;
lineEnd = false;
rtl = false;
ws = false;
useLineFill = false;
useCodes = 0;
autoCodes = false;
noprint = false;
justification = 0;
width = 100.0f;
pText32 = NULL;
textArgIndex = 0;
charLength = 0;
codesize = 4;
offset = 0;
log = stdout;
trace = NULL;
alltrace = NULL;
opts = gr_face_preloadAll;
}
void Parameters::closeLog()
{
if (log==stdout)
return ;
fclose(log);
log = stdout;
}
int lookup(size_t *map, size_t val);
namespace gr2 = graphite2;
template <typename utf>
size_t convertUtf(const void * src, unsigned int * & dest)
{
dest = static_cast<unsigned int *>(malloc(sizeof(unsigned int)*(strlen(reinterpret_cast<const char *>(src))+1)));
if (!dest)
return 0;
typename utf::const_iterator ui = src;
unsigned int * out = dest;
while ((*out = *ui) != 0 && !ui.error())
{
++ui;
++out;
}
if (ui.error())
{
free(dest);
dest = 0;
return size_t(-1);
}
return (out-dest);
}
bool Parameters::loadFromArgs(int argc, char *argv[])
{
int mainArgOffset = 0;
pText32 = NULL;
features = NULL;
log = stdout;
codesize = 4;
bool argError = false;
char* pText = NULL;
typedef enum
{
NONE,
POINT_SIZE,
DPI,
JUSTIFY,
LINE_START,
LINE_END,
LINE_FILL,
CODES,
AUTOCODES,
FEAT,
LOG,
TRACE,
ALLTRACE,
SIZE
} TestOptions;
TestOptions option = NONE;
char * pIntEnd = NULL;
char * pFloatEnd = NULL;
long lTestSize = 0;
float fTestSize = 0.0f;
for (int a = 1; a < argc; a++)
{
switch (option)
{
case DPI:
pIntEnd = NULL;
lTestSize = strtol(argv[a],&pIntEnd, 10);
if (lTestSize > 0 && lTestSize < INT_MAX && lTestSize != LONG_MAX)
{
dpi = lTestSize;
}
else
{
fprintf(stderr,"Invalid dpi %s\n", argv[a]);
}
option = NONE;
break;
case POINT_SIZE:
pFloatEnd = NULL;
fTestSize = strtof(argv[a],&pFloatEnd);
// what is a reasonable maximum here
if (fTestSize > 0 && fTestSize < 5000.0f)
{
pointSize = fTestSize;
}
else
{
fprintf(stderr,"Invalid point size %s\n", argv[a]);
argError = true;
}
option = NONE;
break;
case JUSTIFY:
pIntEnd = NULL;
justification = strtol(argv[a], &pIntEnd, 10);
if (justification <= 0)
{
fprintf(stderr, "Justification value must be > 0 but was %s\n", argv[a]);
justification = 0;
}
option = NONE;
break;
case LINE_FILL:
pFloatEnd = NULL;
fTestSize = strtof(argv[a],&pFloatEnd);
// what is a good max width?
if (fTestSize > 0 && fTestSize < 10000)
{
width = fTestSize;
}
else
{
fprintf(stderr,"Invalid line width %s\n", argv[a]);
argError = true;
}
option = NONE;
break;
case FEAT:
features = argv[a];
option = NONE;
break;
case LOG:
closeLog();
log = fopen(argv[a], "w");
if (log == NULL)
{
fprintf(stderr,"Failed to open %s\n", argv[a]);
log = stdout;
}
option = NONE;
break;
case TRACE:
trace = argv[a];
option = NONE;
break;
case ALLTRACE:
alltrace = argv[a];
option = NONE;
break;
case SIZE :
pIntEnd = NULL;
codesize = strtol(argv[a],&pIntEnd, 10);
option = NONE;
break;
default:
option = NONE;
if (argv[a][0] == '-')
{
if (strcmp(argv[a], "-pt") == 0)
{
option = POINT_SIZE;
}
else if (strcmp(argv[a], "-dpi") == 0)
{
option = DPI;
}
else if (strcmp(argv[a], "-ls") == 0)
{
option = NONE;
lineStart = true;
}
else if (strcmp(argv[a], "-le") == 0)
{
option = NONE;
lineEnd = true;
}
else if (strcmp(argv[a], "-le") == 0)
{
option = NONE;
lineEnd = true;
}
else if (strcmp(argv[a], "-rtl") == 0)
{
option = NONE;
rtl = true;
}
else if (strcmp(argv[a], "-ws") == 0)
{
option = NONE;
ws = true;
}
else if (strcmp(argv[a], "-feat") == 0)
{
option = FEAT;
}
else if (strcmp(argv[a], "-bytes") == 0)
{
option = SIZE;
}
else if (strcmp(argv[a], "-noprint") == 0)
{
noprint = true;
}
else if (strcmp(argv[a], "-codes") == 0)
{
option = NONE;
useCodes = 4;
// must be less than argc
//pText32 = new unsigned int[argc];
pText32 = (unsigned int *)malloc(sizeof(unsigned int) * argc);
fprintf(log, "Text codes\n");
}
else if (strcmp(argv[a], "-auto") == 0)
{
const unsigned kCodeLimit = 0x1000;
charLength = kCodeLimit - 1;
pText32 = (unsigned int *)malloc(sizeof(unsigned int) * kCodeLimit);
unsigned int i;
for (i = 1; i < kCodeLimit; ++i)
pText32[i - 1] = i;
pText32[charLength] = 0;
autoCodes = true;
option = NONE;
}
else if (strcmp(argv[a], "-linefill") == 0)
{
option = LINE_FILL;
useLineFill = true;
}
else if (strcmp(argv[a], "-j") == 0)
{
option = JUSTIFY;
}
else if (strcmp(argv[a], "-log") == 0)
{
option = LOG;
}
else if (strcmp(argv[a], "-trace") == 0)
{
option = TRACE;
}
else if (strcmp(argv[a], "-alltrace") == 0)
{
option = ALLTRACE;
}
else if (strcmp(argv[a], "-demand") == 0)
{
option = NONE;
opts = gr_face_default;
}
else
{
argError = true;
fprintf(stderr,"Unknown option %s\n",argv[a]);
}
}
else if (mainArgOffset == 0)
{
fileName = argv[a];
mainArgOffset++;
}
else if (useCodes)
{
pIntEnd = NULL;
mainArgOffset++;
unsigned int code = strtol(argv[a],&pIntEnd, 16);
if (code > 0)
{
// convert text to utfOut using iconv because its easier to debug string placements
assert(pText32);
pText32[charLength++] = code;
if (charLength % 10 == 0)
fprintf(log, "%4x\n",code);
else
fprintf(log, "%4x\t",code);
}
else
{
fprintf(stderr,"Invalid dpi %s\n", argv[a]);
}
}
else if (mainArgOffset == 1)
{
mainArgOffset++;
pText = argv[a];
textArgIndex = a;
}
else
{
argError = true;
fprintf(stderr,"too many arguments %s\n",argv[a]);
}
break;
}
}
if (mainArgOffset < 1) argError = true;
else if (mainArgOffset > 1)
{
if (!autoCodes && !useCodes && pText != NULL)
{
charLength = convertUtf<gr2::utf8>(pText, pText32);
if (!pText32)
{
if (charLength == ~0)
perror("decoding utf-8 data failed");
else
perror("insufficent memory for text buffer");
}
fprintf(log, "String has %d characters\n", (int)charLength);
size_t ci;
for (ci = 0; ci < 10 && ci < charLength; ci++)
{
fprintf(log, "%d\t", (int)ci);
}
fprintf(log, "\n");
for (ci = 0; ci < charLength; ci++)
{
fprintf(log, "%04x\t", (int)ci);
if (((ci + 1) % 10) == 0)
fprintf(log, "\n");
}
fprintf(log, "\n");
}
else
{
assert(pText32);
pText32[charLength] = 0;
fprintf(log, "\n");
}
}
return (argError) ? false : true;
}
typedef uint32_t tag_t;
void Parameters::printFeatures(const gr_face * face) const
{
uint16_t numFeatures = gr_face_n_fref(face);
fprintf(log, "%d features\n", numFeatures);
uint16_t langId = 0x0409;
for (uint16_t i = 0; i < numFeatures; i++)
{
const gr_feature_ref * f = gr_face_fref(face, i);
uint32_t length = 0;
char * label = reinterpret_cast<char *>(gr_fref_label(f, &langId, gr_utf8, &length));
const tag_t featId = gr_fref_id(f);
if (label)
if ((char(featId >> 24) >= 0x20 && char(featId >> 24) < 0x7F) &&
(char(featId >> 16) >= 0x20 && char(featId >> 16) < 0x7F) &&
(char(featId >> 8) >= 0x20 && char(featId >> 8) < 0x7F) &&
(char(featId) >= 0x20 && char(featId) < 0x7F))
{
fprintf(log, "%d %c%c%c%c %s\n", featId, featId >> 24, featId >> 16, featId >> 8, featId, label);
}
else
{
fprintf(log, "%d %s\n", featId, label);
}
else
fprintf(log, "%d\n", featId);
gr_label_destroy(reinterpret_cast<void*>(label));
uint16_t numSettings = gr_fref_n_values(f);
for (uint16_t j = 0; j < numSettings; j++)
{
int16_t value = gr_fref_value(f, j);
label = reinterpret_cast<char *>(gr_fref_value_label
(f, j, &langId, gr_utf8, &length));
fprintf(log, "\t%d\t%s\n", value, label);
gr_label_destroy(reinterpret_cast<void*>(label));
}
}
uint16_t numLangs = gr_face_n_languages(face);
fprintf(log, "Feature Languages:");
for (uint16_t i = 0; i < numLangs; i++)
{
const tag_t lang_id = gr_face_lang_by_index(face, i);
fprintf(log, "\t");
for (size_t j = 4; j; --j)
{
const unsigned char c = lang_id >> (j*8-8);
if ((c >= 0x20) && (c < 0x80))
fprintf(log, "%c", c);
}
}
fprintf(log, "\n");
}
gr_feature_val * Parameters::parseFeatures(const gr_face * face) const
{
gr_feature_val * featureList = NULL;
const char * pLang = NULL;
tag_t lang_id = 0;
if (features && (pLang = strstr(features, "lang=")))
{
pLang += 5;
for (int i = 4; i; --i)
{
lang_id <<= 8;
if (!*pLang || *pLang == '0' || *pLang == '&') continue;
lang_id |= *pLang++;
}
}
featureList = gr_face_featureval_for_lang(face, lang_id);
if (!features || strlen(features) == 0)
return featureList;
size_t featureLength = strlen(features);
const char * name = features;
const char * valueText = NULL;
size_t nameLength = 0;
int32_t value = 0;
const gr_feature_ref* ref = NULL;
tag_t feat_id = 0;
for (size_t i = 0; i < featureLength; i++)
{
switch (features[i])
{
case ',':
case '&':
assert(valueText);
value = atoi(valueText);
if (ref)
{
gr_fref_set_feature_value(ref, value, featureList);
ref = NULL;
}
valueText = NULL;
name = features + i + 1;
nameLength = 0;
feat_id = 0;
break;
case '=':
if (nameLength <= 4)
{
ref = gr_face_find_fref(face, feat_id);
}
if (!ref)
{
assert(name);
feat_id = atoi(name);
ref = gr_face_find_fref(face, feat_id);
}
valueText = features + i + 1;
name = NULL;
break;
default:
if (valueText == NULL)
{
if (nameLength < 4)
feat_id = feat_id << 8 | features[i];
}
break;
}
if (ref)
{
assert(valueText);
value = atoi(valueText);
gr_fref_set_feature_value(ref, value, featureList);
if (feat_id > 0x20000000)
{
fprintf(log, "%c%c%c%c=%d\n", feat_id >> 24, feat_id >> 16, feat_id >> 8, feat_id, value);
}
else
fprintf(log, "%u=%d\n", feat_id, value);
ref = NULL;
}
}
return featureList;
}
int Parameters::testFileFont() const
{
int returnCode = 0;
// try
{
gr_face *face = NULL;
FILE * testfile = fopen(fileName, "rb");
if (!testfile)
{
fprintf(stderr, "Unable to open font file\n");
return 4;
}
else
fclose(testfile);
if (alltrace) gr_start_logging(NULL, alltrace);
face = gr_make_file_face(fileName, opts);
// use the -trace option to specify a file
if (trace) gr_start_logging(face, trace);
if (!face)
{
fprintf(stderr, "Invalid font, failed to read or parse tables\n");
return 3;
}
if (charLength == 0)
{
printFeatures(face);
gr_stop_logging(face);
gr_face_destroy(face);
return 0;
}
gr_font *sizedFont = gr_make_font(pointSize * dpi / 72, face);
gr_feature_val * featureList = NULL;
Gr2TextSrc textSrc(pText32, charLength);
gr_segment* pSeg = NULL;
if (features)
featureList = parseFeatures(face);
if (codesize == 2)
{
unsigned short *pText16 = (unsigned short *)malloc((textSrc.getLength() * 2 + 1) * sizeof(unsigned short));
gr2::utf16::iterator ui = pText16;
unsigned int *p = pText32;
for (unsigned int i = 0; i < textSrc.getLength(); ++i)
{
*ui = *p++;
ui++;
}
*ui = 0;
pSeg = gr_make_seg(sizedFont, face, 0, features ? featureList : NULL, (gr_encform)codesize, pText16, textSrc.getLength(), rtl ? 1 : 0);
}
else if (codesize == 1)
{
unsigned char *pText8 = (unsigned char *)malloc((textSrc.getLength() + 1) * 4);
gr2::utf8::iterator ui = pText8;
unsigned int *p = pText32;
for (unsigned int i = 0; i < textSrc.getLength(); ++i)
{
*ui = *p++;
ui++;
}
*ui = 0;
pSeg = gr_make_seg(sizedFont, face, 0, features ? featureList : NULL, (gr_encform)codesize, pText8, textSrc.getLength(), rtl ? 1 : 0);
free(pText8);
}
else
pSeg = gr_make_seg(sizedFont, face, 0, features ? featureList : NULL, textSrc.utfEncodingForm(),
textSrc.get_utf_buffer_begin(), textSrc.getLength(), rtl ? 1 : 0);
if (pSeg && !noprint)
{
int i = 0;
float advanceWidth;
#ifndef NDEBUG
int numSlots = gr_seg_n_slots(pSeg);
#endif
// size_t *map = new size_t [seg.length() + 1];
if (justification > 0)
advanceWidth = gr_seg_justify(pSeg, gr_seg_first_slot(pSeg), sizedFont, gr_seg_advance_X(pSeg) * justification / 100., gr_justCompleteLine, NULL, NULL);
else
advanceWidth = gr_seg_advance_X(pSeg);
size_t *map = (size_t*)malloc((gr_seg_n_slots(pSeg) + 1) * sizeof(size_t));
for (const gr_slot* slot = gr_seg_first_slot(pSeg); slot; slot = gr_slot_next_in_segment(slot), ++i)
{ map[i] = (size_t)slot; }
map[i] = 0;
fprintf(log, "Segment length: %d\n", gr_seg_n_slots(pSeg));
fprintf(log, "pos gid attach\t x\t y\tins bw\t chars\t\tUnicode\t");
fprintf(log, "\n");
i = 0;
for (const gr_slot* slot = gr_seg_first_slot(pSeg); slot; slot = gr_slot_next_in_segment(slot), ++i)
{
// consistency check for last slot
assert((i + 1 < numSlots) || (slot == gr_seg_last_slot(pSeg)));
float orgX = gr_slot_origin_X(slot);
float orgY = gr_slot_origin_Y(slot);
const gr_char_info *cinfo = gr_seg_cinfo(pSeg, gr_slot_original(slot));
fprintf(log, "%02d %4d %3d@%d,%d\t%6.1f\t%6.1f\t%2d%4d\t%3d %3d\t",
i, gr_slot_gid(slot), lookup(map, (size_t)gr_slot_attached_to(slot)),
gr_slot_attr(slot, pSeg, gr_slatAttX, 0),
gr_slot_attr(slot, pSeg, gr_slatAttY, 0), orgX, orgY, gr_slot_can_insert_before(slot) ? 1 : 0,
cinfo ? gr_cinfo_break_weight(cinfo) : 0, gr_slot_before(slot), gr_slot_after(slot));
if (pText32 != NULL && gr_slot_before(slot) + offset < charLength
&& gr_slot_after(slot) + offset < charLength)
{
fprintf(log, "%7x\t%7x",
pText32[gr_slot_before(slot) + offset],
pText32[gr_slot_after(slot) + offset]);
}
fprintf(log, "\n");
}
assert(i == numSlots);
// assign last point to specify advance of the whole array
// position arrays must be one bigger than what countGlyphs() returned
fprintf(log, "Advance width = %6.1f\n", advanceWidth);
unsigned int numchar = gr_seg_n_cinfo(pSeg);
fprintf(log, "\nChar\tUnicode\tBefore\tAfter\tBase\n");
for (unsigned int j = 0; j < numchar; j++)
{
const gr_char_info *c = gr_seg_cinfo(pSeg, j);
fprintf(log, "%d\t%04X\t%d\t%d\t%d\n", j, gr_cinfo_unicode_char(c), gr_cinfo_before(c), gr_cinfo_after(c), int(gr_cinfo_base(c)));
}
free(map);
}
if (pSeg)
gr_seg_destroy(pSeg);
if (featureList) gr_featureval_destroy(featureList);
gr_font_destroy(sizedFont);
if (trace) gr_stop_logging(face);
gr_face_destroy(face);
if (alltrace) gr_stop_logging(NULL);
}
return returnCode;
}
int lookup(size_t *map, size_t val)
{
int i = 0;
for ( ; map[i] != val && map[i]; i++) {}
return map[i] ? i : -1;
}
int main(int argc, char *argv[])
{
Parameters parameters;
if (!parameters.loadFromArgs(argc, argv))
{
fprintf(stderr,"Usage: %s [options] fontfile utf8text \n",argv[0]);
fprintf(stderr,"Options: (default in brackets)\n");
fprintf(stderr,"-dpi d\tDots per Inch (72)\n");
fprintf(stderr,"-pt d\tPoint size (12)\n");
fprintf(stderr,"-codes\tEnter text as hex code points instead of utf8 (false)\n");
fprintf(stderr,"\te.g. %s font.ttf -codes 1000 102f\n",argv[0]);
fprintf(stderr,"-auto\tAutomatically generate a test string of all codes 1-0xFFF\n");
fprintf(stderr,"-noprint\tDon't print results\n");
//fprintf(stderr,"-ls\tStart of line = true (false)\n");
//fprintf(stderr,"-le\tEnd of line = true (false)\n");
fprintf(stderr,"-rtl\tRight to left = true (false)\n");
fprintf(stderr,"-j percentage\tJustify to percentage of string width\n");
//fprintf(stderr,"-ws\tAllow trailing whitespace = true (false)\n");
//fprintf(stderr,"-linefill w\tuse a LineFillSegment of width w (RangeSegment)\n");
fprintf(stderr,"\nIf a font, but no text is specified, then a list of features will be shown.\n");
fprintf(stderr,"-feat f=g\tSet feature f to value g. Separate multiple features with ,\n");
fprintf(stderr,"-log out.log\tSet log file to use rather than stdout\n");
fprintf(stderr,"-trace trace.json\tDefine a file for the JSON trace log\n");
fprintf(stderr,"-demand\tDemand load glyphs and cmap cache\n");
fprintf(stderr,"-bytes\tword size for character transfer [1,2,4] defaults to 4\n");
return 1;
}
return parameters.testFileFont();
}

View File

@ -0,0 +1,7 @@
Name: Graphite2
Description: Font rendering engine for Complex Scripts
Version: ${version}
Libs: -L${libdir} -lgraphite2
Cflags: -I${includedir}

View File

@ -0,0 +1,365 @@
/* SPDX-License-Identifier: MIT */
/* Copyright 2010, SIL International, All rights reserved. */
#pragma once
#include "graphite2/Types.h"
#define GR2_VERSION_MAJOR 1
#define GR2_VERSION_MINOR 3
#define GR2_VERSION_BUGFIX 13
#ifdef __cplusplus
extern "C"
{
#endif
typedef struct gr_face gr_face;
typedef struct gr_font gr_font;
typedef struct gr_feature_ref gr_feature_ref;
typedef struct gr_feature_val gr_feature_val;
/**
* Returns version information on this engine
*/
GR2_API void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix);
/**
* The Face Options allow the application to require that certain tables are
* read during face construction. This may be of concern if the appFaceHandle
* used in the gr_get_table_fn may change.
* The values can be combined
*/
enum gr_face_options {
/** No preload, no cmap caching, fail if the graphite tables are invalid */
gr_face_default = 0,
/** Dumb rendering will be enabled if the graphite tables are invalid. @deprecated Since 1.311 */
gr_face_dumbRendering = 1,
/** preload glyphs at construction time */
gr_face_preloadGlyphs = 2,
/** Cache the lookup from code point to glyph ID at construction time */
gr_face_cacheCmap = 4,
/** Preload everything */
gr_face_preloadAll = gr_face_preloadGlyphs | gr_face_cacheCmap
};
/** Holds information about a particular Graphite silf table that has been loaded */
struct gr_faceinfo {
uint16_t extra_ascent; /**< The extra_ascent in the GDL, in design units */
uint16_t extra_descent; /**< The extra_descent in the GDL, in design units */
uint16_t upem; /**< The design units for the font */
enum gr_space_contextuals {
gr_space_unknown = 0, /**< no information is known. */
gr_space_none = 1, /**< the space character never occurs in any rules. */
gr_space_left_only = 2, /**< the space character only occurs as the first element in a rule. */
gr_space_right_only = 3, /**< the space character only occurs as the last element in a rule. */
gr_space_either_only = 4, /**< the space character only occurs as the only element in a rule. */
gr_space_both = 5, /**< the space character may occur as the first or last element of a rule. */
gr_space_cross = 6 /**< the space character occurs in a rule not as a first or last element. */
} space_contextuals;
unsigned int has_bidi_pass : 1; /**< the table specifies that a bidirectional pass should run */
unsigned int line_ends : 1; /**< there are line end contextuals somewhere */
unsigned int justifies : 1; /**< there are .justify properties set somewhere on some glyphs */
};
typedef struct gr_faceinfo gr_faceinfo;
/** type describing function to retrieve font table information
*
* @return a pointer to the table in memory. The pointed to memory must exist as
* long as the gr_face which makes the call.
* @param appFaceHandle is the unique information passed to gr_make_face()
* @param name is a 32bit tag to the table name.
* @param len returned by this function to say how long the table is in memory.
*/
typedef const void *(*gr_get_table_fn)(const void* appFaceHandle, unsigned int name, size_t *len);
/** type describing function to release any resources allocated by the above get_table table function
*
* @param appFaceHandle is the unique information passed to gr_make_face()
* @param pointer to table memory returned by get_table.
*/
typedef void (*gr_release_table_fn)(const void* appFaceHandle, const void *table_buffer);
/** struct housing function pointers to manage font table buffers for the graphite engine. */
struct gr_face_ops
{
/** size in bytes of this structure */
size_t size;
/** a pointer to a function to request a table from the client. */
gr_get_table_fn get_table;
/** is a pointer to a function to notify the client the a table can be released.
* This can be NULL to signify that the client does not wish to do any release handling. */
gr_release_table_fn release_table;
};
typedef struct gr_face_ops gr_face_ops;
/** Create a gr_face object given application information and a table functions.
*
* @return gr_face or NULL if the font fails to load for some reason.
* @param appFaceHandle This is application specific information that is passed
* to the getTable function. The appFaceHandle must stay
* alive as long as the gr_face is alive.
* @param face_ops Pointer to face specific callback structure for table
* management. Must stay alive for the duration of the
* call only.
* @param faceOptions Bitfield describing various options. See enum gr_face_options for details.
*/
GR2_API gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *face_ops, unsigned int faceOptions);
/** @deprecated Since v1.2.0 in favour of gr_make_face_with_ops.
* Create a gr_face object given application information and a getTable function.
*
* @return gr_face or NULL if the font fails to load for some reason.
* @param appFaceHandle This is application specific information that is passed
* to the getTable function. The appFaceHandle must stay
* alive as long as the gr_face is alive.
* @param getTable Callback function to get table data.
* @param faceOptions Bitfield describing various options. See enum gr_face_options for details.
*/
GR2_DEPRECATED_API gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn getTable, unsigned int faceOptions);
/** @deprecated Since 1.3.7 this function is now an alias for gr_make_face_with_ops().
*
* Create a gr_face object given application information, with subsegmental caching support
*
* @return gr_face or NULL if the font fails to load.
* @param appFaceHandle is a pointer to application specific information that is passed to getTable.
* This may not be NULL and must stay alive as long as the gr_face is alive.
* @param face_ops Pointer to face specific callback structure for table management. Must stay
* alive for the duration of the call only.
* @param segCacheMaxSize Unused.
* @param faceOptions Bitfield of values from enum gr_face_options
*/
GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle, const gr_face_ops *face_ops, unsigned int segCacheMaxSize, unsigned int faceOptions);
/** @deprecated Since 1.3.7 this function is now an alias for gr_make_face().
*
* Create a gr_face object given application information, with subsegmental caching support.
* This function is deprecated as of v1.2.0 in favour of gr_make_face_with_seg_cache_and_ops.
*
* @return gr_face or NULL if the font fails to load.
* @param appFaceHandle is a pointer to application specific information that is passed to getTable.
* This may not be NULL and must stay alive as long as the gr_face is alive.
* @param getTable The function graphite calls to access font table data
* @param segCacheMaxSize How large the segment cache is.
* @param faceOptions Bitfield of values from enum gr_face_options
*/
GR2_DEPRECATED_API gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle, gr_get_table_fn getTable, unsigned int segCacheMaxSize, unsigned int faceOptions);
/** Convert a tag in a string into a uint32_t
*
* @return uint32_t tag, zero padded
* @param str a nul terminated string of which at most the first 4 characters are read
*/
GR2_API uint32_t gr_str_to_tag(const char *str);
/** Convert a uint32_t tag into a string
*
* @param tag contains the tag to convert
* @param str is a pointer to a char array of at least size 4 bytes. The first 4 bytes of this array
* will be overwritten by this function. No nul is appended.
*/
GR2_API void gr_tag_to_str(uint32_t tag, char *str);
/** Get feature values for a given language or default
*
* @return a copy of the default feature values for a given language. The application must call
* gr_featureval_destroy() to free this object when done.
* @param pFace The font face to get feature values from
* @param langname The language tag to get feature values for. If there is no such language or
* langname is 0, the default feature values for the font are returned.
* langname is right 0 padded and assumes lowercase. Thus the en langauge
* would be 0x656E0000. Langname may also be space padded, thus 0x656E2020.
*/
GR2_API gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, uint32_t langname);
/** Get feature reference for a given feature id from a face
*
* @return a feature reference corresponding to the given id. This data is part of the gr_face and
* will be freed when the face is destroyed.
* @param pFace Font face to get information on.
* @param featId Feature id tag to get reference to.
*/
GR2_API const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, uint32_t featId);
/** Returns number of feature references in a face **/
GR2_API uint16_t gr_face_n_fref(const gr_face* pFace);
/** Returns feature reference at given index in face **/
GR2_API const gr_feature_ref* gr_face_fref(const gr_face* pFace, uint16_t i);
/** Return number of languages the face knows about **/
GR2_API unsigned short gr_face_n_languages(const gr_face* pFace);
/** Returns a language id corresponding to a language of given index in the face **/
GR2_API uint32_t gr_face_lang_by_index(const gr_face* pFace, uint16_t i);
/** Destroy the given face and free its memory **/
GR2_API void gr_face_destroy(gr_face *face);
/** Returns the number of glyphs in the face **/
GR2_API unsigned short gr_face_n_glyphs(const gr_face* pFace);
/** Returns a faceinfo for the face and script **/
GR2_API const gr_faceinfo *gr_face_info(const gr_face *pFace, uint32_t script);
/** Returns whether the font supports a given Unicode character
*
* @return true if the character is supported.
* @param pFace face to test within
* @param usv Unicode Scalar Value of character to test
* @param script Tag of script for selecting which set of pseudo glyphs to test. May be NULL.
*/
GR2_API int gr_face_is_char_supported(const gr_face *pFace, uint32_t usv, uint32_t script);
#ifndef GRAPHITE2_NFILEFACE
/** Create gr_face from a font file
*
* @return gr_face that accesses a font file directly. Returns NULL on failure.
* @param filename Full path and filename to font file
* @param faceOptions Bitfile from enum gr_face_options to control face options.
*/
GR2_API gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions);
/** @deprecated Since 1.3.7. This function is now an alias for gr_make_file_face().
*
* Create gr_face from a font file, with subsegment caching support.
*
* @return gr_face that accesses a font file directly. Returns NULL on failure.
* @param filename Full path and filename to font file
* @param segCacheMaxSize Specifies how big to make the cache in segments.
* @param faceOptions Bitfield from enum gr_face_options to control face options.
*/
GR2_DEPRECATED_API gr_face* gr_make_file_face_with_seg_cache(const char *filename, unsigned int segCacheMaxSize, unsigned int faceOptions);
#endif // !GRAPHITE2_NFILEFACE
/** Create a font from a face
*
* @return gr_font Call font_destroy to free this font
* @param ppm Resolution of the font in pixels per em
* @param face Face this font corresponds to. This must stay alive as long as the font is alive.
*/
GR2_API gr_font* gr_make_font(float ppm, const gr_face *face);
/** query function to find the hinted advance of a glyph
*
* @param appFontHandle is the unique information passed to gr_make_font_with_advance()
* @param glyphid is the glyph to retireve the hinted advance for.
*/
typedef float (*gr_advance_fn)(const void* appFontHandle, uint16_t glyphid);
/** struct housing function pointers to manage font hinted metrics for the
* graphite engine. */
struct gr_font_ops
{
/** size of the structure in bytes to allow for future extensibility */
size_t size;
/** a pointer to a function to retrieve the hinted
* advance width of a glyph which the font cannot
* provide without client assistance. This can be
* NULL to signify no horizontal hinted metrics are necessary. */
gr_advance_fn glyph_advance_x;
/** a pointer to a function to retrieve the hinted
* advance height of a glyph which the font cannot
* provide without client assistance. This can be
* NULL to signify no horizontal hinted metrics are necessary. */
gr_advance_fn glyph_advance_y;
};
typedef struct gr_font_ops gr_font_ops;
/** Creates a font with hinted advance width query functions
*
* @return gr_font to be destroyed via font_destroy
* @param ppm size of font in pixels per em
* @param appFontHandle font specific information that must stay alive as long
* as the font does
* @param font_ops pointer font specific callback structure for hinted metrics.
* Need only stay alive for the duration of the call.
* @param face the face this font corresponds to. Must stay alive as long as
* the font does.
*/
GR2_API gr_font* gr_make_font_with_ops(float ppm, const void* appFontHandle, const gr_font_ops * font_ops, const gr_face *face);
/** Creates a font with hinted advance width query function.
* This function is deprecated. Use gr_make_font_with_ops instead.
*
* @return gr_font to be destroyed via font_destroy
* @param ppm size of font in pixels per em
* @param appFontHandle font specific information that must stay alive as long
* as the font does
* @param getAdvance callback function reference that returns horizontal advance in pixels for a glyph.
* @param face the face this font corresponds to. Must stay alive as long as
* the font does.
*/
GR2_API gr_font* gr_make_font_with_advance_fn(float ppm, const void* appFontHandle, gr_advance_fn getAdvance, const gr_face *face);
/** Free a font **/
GR2_API void gr_font_destroy(gr_font *font);
/** get a feature value
*
* @return value of specific feature or 0 if any problems.
* @param pfeatureref gr_feature_ref to the feature
* @param feats gr_feature_val containing all the values
*/
GR2_API uint16_t gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats);
/** set a feature value
*
* @return false if there were any problems (value out of range, etc.)
* @param pfeatureref gr_feature_ref to the feature
* @param val value to set the feature to
* @param pDest the gr_feature_val containing all the values for all the features
*/
GR2_API int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, uint16_t val, gr_feature_val* pDest);
/** Returns the id tag for a gr_feature_ref **/
GR2_API uint32_t gr_fref_id(const gr_feature_ref* pfeatureref);
/** Returns number of values a feature may take, given a gr_feature_ref **/
GR2_API uint16_t gr_fref_n_values(const gr_feature_ref* pfeatureref);
/** Returns the value associated with a particular value in a feature
*
* @return value
* @param pfeatureref gr_feature_ref of the feature of interest
* @param settingno Index up to the return value of gr_fref_n_values() of the value
*/
GR2_API int16_t gr_fref_value(const gr_feature_ref* pfeatureref, uint16_t settingno);
/** Returns a string of the UI name of a feature
*
* @return string of the UI name, in the encoding form requested. Call gr_label_destroy() after use.
* @param pfeatureref gr_feature_ref of the feature
* @param langId This is a pointer since the face may not support a string in the requested
* language. The actual language of the string is returned in langId
* @param utf Encoding form for the string
* @param length Used to return the length of the string returned in bytes.
*/
GR2_API void* gr_fref_label(const gr_feature_ref* pfeatureref, uint16_t *langId, enum gr_encform utf, uint32_t *length);
/** Return a UI string for a possible value of a feature
*
* @return string of the UI name, in the encoding form requested. nul terminated. Call gr_label_destroy()
* after use.
* @param pfeatureref gr_feature_ref of the feature
* @param settingno Value setting index
* @param langId This is a pointer to the requested language. The requested language id is
* replaced by the actual language id of the string returned.
* @param utf Encoding form for the string
* @param length Returns the length of the string returned in bytes.
*/
GR2_API void* gr_fref_value_label(const gr_feature_ref* pfeatureref, uint16_t settingno/*rather than a value*/, uint16_t *langId, enum gr_encform utf, uint32_t *length);
/** Destroy a previously returned label string **/
GR2_API void gr_label_destroy(void * label);
/** Copies a gr_feature_val **/
GR2_API gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures);
/** Destroys a gr_feature_val **/
GR2_API void gr_featureval_destroy(gr_feature_val *pfeatures);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,61 @@
/* SPDX-License-Identifier: MIT */
/* Copyright 2010, SIL International, All rights reserved. */
#pragma once
#include <graphite2/Types.h>
#include <graphite2/Font.h>
#include <stdio.h>
#ifdef __cplusplus
extern "C"
{
#endif
/** deprecated mechanism that doesn't do anything now. */
typedef enum {
GRLOG_NONE = 0x0,
GRLOG_FACE = 0x01,
GRLOG_SEGMENT = 0x02,
GRLOG_PASS = 0x04,
GRLOG_CACHE = 0x08,
GRLOG_OPCODE = 0x80,
GRLOG_ALL = 0xFF
} GrLogMask;
/** Start logging all segment creation and updates on the provided face. This
* is logged to a JSON file, see "Segment JSON Schema.txt" for a precise
* definition of the file
*
* @return true if the file was successfully created and logging is correctly
* initialised.
* @param face the gr_face whose segments you want to log to the given file
* @param log_path a utf8 encoded file name and path to log to.
*/
GR2_API bool gr_start_logging(gr_face * face, const char *log_path);
/** Stop logging on the given face. This will close the log file created by
* gr_start_logging.
*
* @param face the gr_face whose segments you want to stop logging
*/
GR2_API void gr_stop_logging(gr_face * face);
/** Start logging to a FILE object.
* This function is deprecated as of 1.2.0, use the _face versions instead.
*
* @return True on success
* @param logfile FILE reference to output logging to
* @param mask What aspects of logging to report (ignored)
*/
GR2_API bool graphite_start_logging(FILE * logFile, GrLogMask mask); //may not do anthing if disabled in the implementation of the engine.
/** Stop logging to a FILE object.
* This function is deprecated as of 1.2.0, use the _face versions instead.
*/
GR2_API void graphite_stop_logging();
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,437 @@
/* SPDX-License-Identifier: MIT */
/* Copyright 2010, SIL International, All rights reserved. */
#pragma once
#include "graphite2/Types.h"
#include "graphite2/Font.h"
#ifdef __cplusplus
extern "C"
{
#endif
enum gr_break_weight {
gr_breakNone = 0,
/* after break weights */
gr_breakWhitespace = 10,
gr_breakWord = 15,
gr_breakIntra = 20,
gr_breakLetter = 30,
gr_breakClip = 40,
/* before break weights */
gr_breakBeforeWhitespace = -10,
gr_breakBeforeWord = -15,
gr_breakBeforeIntra = -20,
gr_breakBeforeLetter = -30,
gr_breakBeforeClip = -40
};
enum gr_justFlags {
/// Indicates that this segment is a complete line
gr_justCompleteLine = 0,
/// Indicates that the start of the slot list is not at the start of a line
gr_justStartInline = 1,
/// Indicates that the end of the slot list is not at the end of a line
gr_justEndInline = 2
};
/** Used for looking up slot attributes. Most are already available in other functions **/
enum gr_attrCode {
/// adjusted glyph advance in x direction in design units
gr_slatAdvX = 0,
/// adjusted glyph advance in y direction (usually 0) in design units
gr_slatAdvY,
/// returns 0. Deprecated.
gr_slatAttTo,
/// This slot attaches to its parent at the given design units in the x direction
gr_slatAttX,
/// This slot attaches to its parent at the given design units in the y direction
gr_slatAttY,
/// This slot attaches to its parent at the given glyph point (not implemented)
gr_slatAttGpt,
/// x-direction adjustment from the given glyph point (not implemented)
gr_slatAttXOff,
/// y-direction adjustment from the given glyph point (not implemented)
gr_slatAttYOff,
/// Where on this glyph should align with the attachment point on the parent glyph in the x-direction.
gr_slatAttWithX,
/// Where on this glyph should align with the attachment point on the parent glyph in the y-direction
gr_slatAttWithY,
/// Which glyph point on this glyph should align with the attachment point on the parent glyph (not implemented).
gr_slatWithGpt,
/// Adjustment to gr_slatWithGpt in x-direction (not implemented)
gr_slatAttWithXOff,
/// Adjustment to gr_slatWithGpt in y-direction (not implemented)
gr_slatAttWithYOff,
/// Attach at given nesting level (not implemented)
gr_slatAttLevel,
/// Line break breakweight for this glyph
gr_slatBreak,
/// Ligature component reference (not implemented)
gr_slatCompRef,
/// bidi directionality of this glyph (not implemented)
gr_slatDir,
/// Whether insertion is allowed before this glyph
gr_slatInsert,
/// Final positioned position of this glyph relative to its parent in x-direction in pixels
gr_slatPosX,
/// Final positioned position of this glyph relative to its parent in y-direction in pixels
gr_slatPosY,
/// Amount to shift glyph by in x-direction design units
gr_slatShiftX,
/// Amount to shift glyph by in y-direction design units
gr_slatShiftY,
/// attribute user1
gr_slatUserDefnV1,
/// not implemented
gr_slatMeasureSol,
/// not implemented
gr_slatMeasureEol,
/// Amount this slot can stretch (not implemented)
gr_slatJStretch,
/// Amount this slot can shrink (not implemented)
gr_slatJShrink,
/// Granularity by which this slot can stretch or shrink (not implemented)
gr_slatJStep,
/// Justification weight for this glyph (not implemented)
gr_slatJWeight,
/// Amount this slot mush shrink or stretch in design units
gr_slatJWidth = 29,
/// SubSegment split point
gr_slatSegSplit = gr_slatJStretch + 29,
/// User defined attribute, see subattr for user attr number
gr_slatUserDefn,
/// Bidi level
gr_slatBidiLevel = 56,
/// Collision flags
gr_slatColFlags,
/// Collision constraint rectangle left (bl.x)
gr_slatColLimitblx,
/// Collision constraint rectangle lower (bl.y)
gr_slatColLimitbly,
/// Collision constraint rectangle right (tr.x)
gr_slatColLimittrx,
/// Collision constraint rectangle upper (tr.y)
gr_slatColLimittry,
/// Collision shift x
gr_slatColShiftx,
/// Collision shift y
gr_slatColShifty,
/// Collision margin
gr_slatColMargin,
/// Margin cost weight
gr_slatColMarginWt,
// Additional glyph that excludes movement near this one:
gr_slatColExclGlyph,
gr_slatColExclOffx,
gr_slatColExclOffy,
// Collision sequence enforcing attributes:
gr_slatSeqClass,
gr_slatSeqProxClass,
gr_slatSeqOrder,
gr_slatSeqAboveXoff,
gr_slatSeqAboveWt,
gr_slatSeqBelowXlim,
gr_slatSeqBelowWt,
gr_slatSeqValignHt,
gr_slatSeqValignWt,
/// not implemented
gr_slatMax,
/// not implemented
gr_slatNoEffect = gr_slatMax + 1
};
enum gr_bidirtl {
/// Underlying paragraph direction is RTL
gr_rtl = 1,
/// Set this to not run the bidi pass internally, even if the font asks for it.
/// This presumes that the segment is in a single direction. Most of the time
/// this bit should be set unless you know you are passing full paragraphs of text.
gr_nobidi = 2,
/// Disable auto mirroring for rtl text
gr_nomirror = 4
};
typedef struct gr_char_info gr_char_info;
typedef struct gr_segment gr_segment;
typedef struct gr_slot gr_slot;
/** Returns Unicode character for a charinfo.
*
* @param p Pointer to charinfo to return information on.
*/
GR2_API unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/);
/** Returns breakweight for a charinfo.
*
* @return Breakweight is a number between -50 and 50 indicating the cost of a
* break before or after this character. If the value < 0, the absolute value
* is this character's contribution to the overall breakweight before it. If the value
* > 0, then the value is this character's contribution to the overall breakweight after it.
* The overall breakweight between two characters is the maximum of the breakweight
* contributions from the characters either side of it. If a character makes no
* contribution to the breakweight on one side of it, the contribution is considered
* to be 0.
* @param p Pointer to charinfo to return information on.
*/
GR2_API int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/);
/** Returns the slot index that after this character is after in the slot stream
*
* In effect each character is associated with a set of slots and this returns
* the index of the last slot in the segment this character is associated with.
*
* @return after slot index between 0 and gr_seg_n_slots()
* @param p Pointer to charinfo to return information on.
*/
GR2_API int gr_cinfo_after(const gr_char_info* p/*not NULL*/);
/** Returns the slot index that before this character is before in the slot stream
*
* In effect each character is associated with a set of slots and this returns
* the index of the first slot in the segment this character is associated with.
*
* @return before slot index between 0 and gr_seg_n_slots()
* @param p Pointer to charinfo to return information on.
*/
GR2_API int gr_cinfo_before(const gr_char_info* p/*not NULL*/);
/** Returns the code unit index of this character in the input string
*
* @return code unit index between 0 and the end of the string
* @param p Pointer to charinfo to return information on.
*/
GR2_API size_t gr_cinfo_base(const gr_char_info* p/*not NULL*/);
/** Returns the number of unicode characters in a string.
*
* @return number of characters in the string
* @param enc Specifies the type of data in the string: utf8, utf16, utf32
* @param buffer_begin The start of the string
* @param buffer_end Measure up to the first nul or when end is reached, whichever is earliest.
* This parameter may be NULL.
* @param pError If there is a structural fault in the string, the location is returned
* in this variable. If no error occurs, pError will contain NULL. NULL
* may be passed for pError if no such information is required.
*/
GR2_API size_t gr_count_unicode_characters(enum gr_encform enc, const void* buffer_begin, const void* buffer_end, const void** pError);
/** Creates and returns a segment.
*
* @return a segment that needs seg_destroy called on it. May return NULL if bad problems
* in segment processing.
* @param font Gives the size of the font in pixels per em for final positioning. If
* NULL, positions are returned in design units, i.e. at a ppm of the upem
* of the face.
* @param face The face containing all the non-size dependent information.
* @param script This is a tag containing a script identifier that is used to choose
* which graphite table within the font to use. Maybe 0. Tag may be 4 chars
* NULL padded in LSBs or space padded in LSBs.
* @param pFeats Pointer to a feature values to be used for the segment. Only one
* feature values may be used for a segment. If NULL the default features
* for the font will be used.
* @param enc Specifies what encoding form the string is in (utf8, utf16, utf32)
* @param pStart Start of the string
* @param nChars Number of unicode characters to process in the string. The string will
* be processed either up to the first NULL or until nChars have been
* processed. nChars is also used to initialise the internal memory
* allocations of the segment. So it is wise not to make nChars too much
* greater than the actual number of characters being processed.
* @param dir Specifies whether the segment is processed right to left (1) or left to
* right (0) and whether to run the internal bidi pass, if a font requests it.
* See enum gr_bidirtl for details.
*/
GR2_API gr_segment* gr_make_seg(const gr_font* font, const gr_face* face, uint32_t script, const gr_feature_val* pFeats, enum gr_encform enc, const void* pStart, size_t nChars, int dir);
/** Destroys a segment, freeing the memory.
*
* @param p The segment to destroy
*/
GR2_API void gr_seg_destroy(gr_segment* p);
/** Returns the advance for the whole segment.
*
* Returns the width of the segment up to the next glyph origin after the segment
*/
GR2_API float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/);
/** Returns the height advance for the segment. **/
GR2_API float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/);
/** Returns the number of gr_char_infos in the segment. **/
GR2_API unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/);
/** Returns a gr_char_info at a given index in the segment. **/
GR2_API const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be <number_of_CharInfo*/);
/** Returns the number of glyph gr_slots in the segment. **/
GR2_API unsigned int gr_seg_n_slots(const gr_segment* pSeg/*not NULL*/); //one slot per glyph
/** Returns the first gr_slot in the segment.
*
* The first slot in a segment has a gr_slot_prev_in_segment() of NULL. Slots are owned
* by their segment and are destroyed along with the segment.
*/
GR2_API const gr_slot* gr_seg_first_slot(gr_segment* pSeg/*not NULL*/); //may give a base slot or a slot which is attached to another
/** Returns the last gr_slot in the segment.
*
* The last slot in a segment has a gr_slot_next_in_segment() of NULL
*/
GR2_API const gr_slot* gr_seg_last_slot(gr_segment* pSeg/*not NULL*/); //may give a base slot or a slot which is attached to another
/** Justifies a linked list of slots for a line to a given width
*
* Passed a pointer to the start of a linked list of slots corresponding to a line, as
* set up by gr_slot_linebreak_before, this function will position the glyphs in the line
* to take up the given width. It is possible to specify a subrange within the line to process.
* This allows skipping of line initial or final whitespace, for example. While this will ensure
* that the subrange fits width, the line will still be positioned with the first glyph of the
* line at 0. So the resulting positions may be beyond width.
*
* @return float The resulting width of the range of slots justified.
* @param pSeg Pointer to the segment
* @param pStart Pointer to the start of the line linked list (including skipped characters)
* @param pFont Font to use for positioning
* @param width Width in pixels in which to fit the line. If < 0. don't adjust natural width, just run justification passes
* to handle line end contextuals, if there are any.
* @param flags Indicates line ending types. Default is linked list is a full line
* @param pFirst If not NULL, the first slot in the list to be considered part of the line (so can skip)
* @param pLast If not NULL, the last slot to process in the line (allow say trailing whitespace to be skipped)
*/
GR2_API float gr_seg_justify(gr_segment* pSeg/*not NULL*/, const gr_slot* pStart/*not NULL*/, const gr_font *pFont, double width, enum gr_justFlags flags, const gr_slot* pFirst, const gr_slot* pLast);
/** Returns the next slot along in the segment.
*
* Slots are held in a linked list. This returns the next in the linked list. The slot
* may or may not be attached to another slot. Returns NULL at the end of the segment.
*/
GR2_API const gr_slot* gr_slot_next_in_segment(const gr_slot* p);
/** Returns the previous slot along in the segment.
*
* Slots are held in a doubly linked list. This returns the previos slot in the linked
* list. This slot may or may not be attached to it. Returns NULL at the start of the
* segment.
*/
GR2_API const gr_slot* gr_slot_prev_in_segment(const gr_slot* p);
/** Returns the attachment parent slot of this slot.
*
* Attached slots form a tree. This returns the parent of this slot in that tree. A
* base glyph which is not attached to another glyph, always returns NULL.
*/
GR2_API const gr_slot* gr_slot_attached_to(const gr_slot* p);
/** Returns the first slot attached to this slot.
*
* Attached slots form a singly linked list from the parent. This returns the first
* slot in that list. Note that this is a reference to another slot that is also in
* the main segment doubly linked list.
*
* if gr_slot_first_attachment(p) != NULL then gr_slot_attached_to(gr_slot_first_attachment(p)) == p.
*/
GR2_API const gr_slot* gr_slot_first_attachment(const gr_slot* p);
/** Returns the next slot attached to our attachment parent.
*
* This returns the next slot in the singly linked list of slots attached to this
* slot's parent. If there are no more such slots, NULL is returned. If there is
* no parent, i.e. the passed slot is a cluster base, then the next cluster base
* in graphical order (ltr, even for rtl text) is returned.
*
* if gr_slot_next_sibling_attachment(p) != NULL then gr_slot_attached_to(gr_slot_next_sibling_attachment(p)) == gr_slot_attached_to(p).
*/
GR2_API const gr_slot* gr_slot_next_sibling_attachment(const gr_slot* p);
/** Returns glyph id of the slot
*
* Each slot has a glyphid which is rendered at the position given by the slot. This
* glyphid is the real glyph to be rendered and never a pseudo glyph.
*/
GR2_API unsigned short gr_slot_gid(const gr_slot* p);
/** Returns X offset of glyph from start of segment **/
GR2_API float gr_slot_origin_X(const gr_slot* p);
/** Returns Y offset of glyph from start of segment **/
GR2_API float gr_slot_origin_Y(const gr_slot* p);
/** Returns the glyph advance for this glyph as adjusted for kerning
*
* @param p Slot to give results for
* @param face gr_face of the glyphs. May be NULL if unhinted advances used
* @param font gr_font to scale for pixel results. If NULL returns design
* units advance. If not NULL then returns pixel advance based
* on hinted or scaled glyph advances in the font. face must be
* passed for hinted advances to be used.
*/
GR2_API float gr_slot_advance_X(const gr_slot* p, const gr_face* face, const gr_font *font);
/** Returns the vertical advance for the glyph in the slot adjusted for kerning
*
* Returns design units unless font is not NULL in which case the pixel value
* is returned scaled for the given font
*/
GR2_API float gr_slot_advance_Y(const gr_slot* p, const gr_face* face, const gr_font *font);
/** Returns the gr_char_info index before us
*
* Returns the index of the gr_char_info that a cursor before this slot, would put
* an underlying cursor before. This may also be interpretted as each slot holding
* a set of char_infos that it is associated with and this function returning the
* index of the char_info with lowest index, from this set.
*/
GR2_API int gr_slot_before(const gr_slot* p/*not NULL*/);
/** Returns the gr_char_info index after us
*
* Returns the index of the gr_char_info that a cursor after this slot would put an
* underlying cursor after. This may also be interpretted as each slot holding a set
* of char_infos that it is associated with and this function returning the index of
* the char_info with the highest index, from this set.
*/
GR2_API int gr_slot_after(const gr_slot* p/*not NULL*/);
/** Returns the index of this slot in the segment
*
* Returns the index given to this slot during final positioning. This corresponds
* to the value returned br gr_cinfo_before() and gr_cinfo_after()
*/
GR2_API unsigned int gr_slot_index(const gr_slot* p/*not NULL*/);
/** Return a slot attribute value
*
* Given a slot and an attribute along with a possible subattribute, return the
* corresponding value in the slot. See enum gr_attrCode for details of each attribute.
*/
GR2_API int gr_slot_attr(const gr_slot* p/*not NULL*/, const gr_segment* pSeg/*not NULL*/, enum gr_attrCode index, uint8_t subindex); //tbd - do we need to expose this?
/** Returns whether text may be inserted before this glyph.
*
* This indicates whether a cursor can be put before this slot. It applies to
* base glyphs that have no parent as well as attached glyphs that have the
* .insert attribute explicitly set to true. This is the primary mechanism
* for identifying contiguous sequences of base plus diacritics.
*/
GR2_API int gr_slot_can_insert_before(const gr_slot* p);
/** Returns the original gr_char_info index this slot refers to.
*
* Each Slot has a gr_char_info that it originates from. This is that gr_char_info.
* The index is passed to gr_seg_cinfo(). This information is useful for testing.
*/
GR2_API int gr_slot_original(const gr_slot* p/*not NULL*/);
/** Breaks a segment into lines.
*
* Breaks the slot linked list at the given point in the linked list. It is up
* to the application to keep track of the first slot on each line.
*/
GR2_API void gr_slot_linebreak_before(gr_slot *p/*not NULL*/);
#ifdef __cplusplus
}
#endif

View File

@ -0,0 +1,48 @@
/* SPDX-License-Identifier: MIT */
/* Copyright 2010, SIL International, All rights reserved. */
#pragma once
#include <stddef.h>
#include <stdint.h>
enum gr_encform {
gr_utf8 = 1/*sizeof(uint8_t)*/, gr_utf16 = 2/*sizeof(uint16_t)*/, gr_utf32 = 4/*sizeof(uint32_t)*/
};
// Define API function declspec/attributes and how each supported compiler or OS
// allows us to specify them.
#if defined __GNUC__
#define _gr2_and ,
#define _gr2_tag_fn(a) __attribute__((a))
#define _gr2_deprecated_flag deprecated
#define _gr2_export_flag visibility("default")
#define _gr2_import_flag visibility("default")
#define _gr2_static_flag visibility("hidden")
#endif
#if defined _WIN32 || defined __CYGWIN__
#if defined __GNUC__ // These three will be redefined for Windows
#undef _gr2_export_flag
#undef _gr2_import_flag
#undef _gr2_static_flag
#else // How MSVC sepcifies function level attributes adn deprecation
#define _gr2_and
#define _gr2_tag_fn(a) __declspec(a)
#define _gr2_deprecated_flag deprecated
#endif
#define _gr2_export_flag dllexport
#define _gr2_import_flag dllimport
#define _gr2_static_flag
#endif
#if defined GRAPHITE2_STATIC
#define GR2_API _gr2_tag_fn(_gr2_static_flag)
#define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_static_flag)
#elif defined GRAPHITE2_EXPORTING
#define GR2_API _gr2_tag_fn(_gr2_export_flag)
#define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_export_flag)
#else
#define GR2_API _gr2_tag_fn(_gr2_import_flag)
#define GR2_DEPRECATED_API _gr2_tag_fn(_gr2_deprecated_flag _gr2_and _gr2_import_flag)
#endif

164
src/graphite2/python/fuzzbidi.py Executable file
View File

@ -0,0 +1,164 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
# Copyright (C) 2013 SIL International
import graphite2
import itertools
import math
import os
import random
import resource
import sys
import time
from multiprocessing import Pool, current_process
from argparse import ArgumentParser
class Estimator:
def __init__(self, max):
self.__base = time.time()
self.__max = float(max)
self.__m = 0
self.__t = 1
self.__st = self.__base
self.__ct = 0
self.modulus = 10
def sample(self, current):
ct = time.time()
if (ct - self.__st <= opts.status):
return None
else:
self.modulus = int(round(math.pow(
10,
math.floor(math.log10(current - self.__ct)))))
self.__ct = current
x = current / (ct-self.__base)
k = (ct-self.__base)/opts.status+1
m = self.__m + (x - self.__m)/k
s = (k-1) and (self.__t + (x - self.__m)*(x - m)) / (k-1)
m = self.bayes(x, s, self.__m, self.__t)
self.__t = s
self.__m = m
self.__st = ct
return (x, time.ctime(ct + (self.__max - current)/m) if m else 'never')
@staticmethod
def bayes(x, s, m, t):
st = s + t
return m*s/st + x*t/st
class MultiProc(object):
chunksize = 10
def __init__(self, doclass, *args):
self.pool = Pool(initializer=self.initialize,
initargs=[doclass, args],
processes=opts.cpus)
def initialize(self, doclass, args):
proc = current_process()
proc.inst = doclass(*args)
def execute(self, top):
if top == 0:
myiter = itertools.repeat(1)
else:
myiter = range(top)
e = Estimator(top)
try:
results = self.pool.imap_unordered(multiproctest, myiter,
self.chunksize)
for count, res in enumerate(results):
if count % e.modulus == 0:
sam = e.sample(count)
if sam:
sys.stdout.write("{0} - {1:8.2f}: {2}\r".format(
count,
sam[0],
(sam[1] if top else "")))
sys.stdout.flush()
yield (count, res)
except KeyboardInterrupt:
sys.stdout.write("\n")
sys.exit(0)
def multiproctest(val):
proc = current_process()
return proc.inst.test()
class Test(object):
chars = " %()-.3=[]A{}\u0627\u064b\u0663\u08f1" \
"\u200b\u202a\u202b\u202c\u202d\u202e\u2066\u2067\u2068\u2069"
seed = None
def __init__(self, grface, grfont):
self.random = random.Random()
self.random.seed(self.seed)
Test.seed = self.random.random()
self.grface = grface
self.font = grfont
self.rtl = 1
self.feats = {}
def test(self):
length = int(self.random.betavariate(2, 50) * 1000)
lenchars = len(self.chars) - 1
def g(x): return self.chars[self.random.randint(0, lenchars)]
text = "".join(map(g, range(length)))
pid = os.fork()
if pid < 0:
print("Failed to fork")
return (-1, text)
elif pid > 0:
(respid, status) = os.waitpid(pid, 0)
if (status & 0xFF):
return (status & 0x7F, text) # don't care abut the core
else:
return (0, text)
else:
self.subtest(text)
def subtest(self, text):
if opts.timeout:
resource.setrlimit(resource.RLIMIT_CPU,
(opts.timeout, opts.timeout))
if opts.memory:
mem = opts.memory * 1024 * 1024
resource.setrlimit(resource.RLIMIT_AS, (mem, mem))
graphite2.Segment(self.font, self.grface, "", text, self.rtl,
self.feats)
sys.exit(0)
p = ArgumentParser()
p.add_argument('fontname', help='font file')
p.add_argument('-m', '--max', type=int, default=0,
help="Number of tests to run [infinite]")
p.add_argument('--timeout', type=int,
help="limit subprocess time in seconds")
p.add_argument('--memory', type=int,
help="memory limit for subprocesses in MB")
p.add_argument('-s', '--status', type=int, default=10,
help="Update status every n seconds")
p.add_argument('-j', '--cpus', type=int,
help="Number of threads to run in parallel [num cpus]")
opts = p.parse_args()
try:
grface = graphite2.Face(opts.fontname)
except Exception as err:
print("Failed to load font {}: {}".format(opts.fontname, err),
file=sys.stderr, flush=True)
sys.exit(1)
grfont = graphite2.Font(grface, 16)
m = MultiProc(Test, grface, grfont)
for (count, res) in m.execute(opts.max):
if res[0]: print("{0},{1}".format(res[0], repr(res[1])))
print("\n")

View File

@ -0,0 +1,412 @@
# SPDX-License-Identifier: MIT OR MPL-2.0 OR GPL-2.0-or-later-2.1-or-later
# Copyright 2013, SIL International, All rights reserved.
import ctypes
import ctypes.util
import errno
import os
import sys
from ctypes import (byref,
c_char, c_char_p,
c_double, c_float,
c_int, c_int16,
c_size_t,
c_uint, c_uint8, c_uint16, c_uint32,
c_ushort,
c_void_p,
CFUNCTYPE, POINTER, Structure)
libpath = os.environ.get('PYGRAPHITE2_LIBRARY_PATH',
ctypes.util.find_library("graphite2"))
if libpath is None:
# find wheel's library
wheel = os.path.dirname(__file__)
if os.name == 'nt':
libpath = os.path.join(wheel, 'bin', 'graphite2.dll')
else:
libpath = os.path.join(wheel, 'lib', 'libgraphite2.so')
gr2 = ctypes.cdll.LoadLibrary(libpath)
def grversion():
a = c_int()
b = c_int()
c = c_int()
gr2.gr_engine_version(byref(a), byref(b), byref(c))
return (a.value, b.value, c.value)
def __check(result, func, args):
if not result:
raise RuntimeError(func.__name__ + ': returned ' + repr(result))
return result
def __idx_error(result, func, args):
if not result:
raise IndexError(func.__name__ + ': invalid index ' + repr(args[1]))
return result
def fn(name, res, *params, **kwds):
f = getattr(gr2, name)
f.restype = res
f.argtypes = params
errcheck = kwds.get('errcheck')
if errcheck: f.errcheck = errcheck
class FaceInfo(Structure):
_fields_ = [("extra_ascent", c_ushort),
("extra_descent", c_ushort),
("upem", c_ushort)]
tablefn = CFUNCTYPE(c_void_p, c_void_p, c_uint, POINTER(c_size_t))
advfn = CFUNCTYPE(c_float, c_void_p, c_ushort)
fn('gr_engine_version', None, POINTER(c_int), POINTER(c_int), POINTER(c_int))
fn('gr_make_face', c_void_p, c_void_p, tablefn, c_uint, errcheck=__check)
fn('gr_str_to_tag', c_uint32, c_char_p)
fn('gr_tag_to_str', None, c_uint32, POINTER(c_char))
fn('gr_face_featureval_for_lang', c_void_p, c_void_p, c_uint32,
errcheck=__check)
fn('gr_face_find_fref', c_void_p, c_void_p, c_uint32, errcheck=__idx_error)
fn('gr_face_n_fref', c_uint16, c_void_p)
fn('gr_face_fref', c_void_p, c_void_p, c_uint16, errcheck=__idx_error)
fn('gr_face_n_languages', c_ushort, c_void_p)
fn('gr_face_lang_by_index', c_uint32, c_void_p, c_uint16)
fn('gr_face_destroy', None, c_void_p)
fn('gr_face_n_glyphs', c_ushort, c_void_p)
fn('gr_face_info', POINTER(FaceInfo), c_void_p)
fn('gr_face_is_char_supported', c_int, c_void_p, c_uint32, c_uint32)
fn('gr_make_file_face', c_void_p, c_char_p, c_uint, errcheck=__check)
fn('gr_make_font', c_void_p, c_float, c_void_p, errcheck=__check)
fn('gr_make_font_with_advance_fn', c_void_p,
c_float, c_void_p, advfn, c_void_p, errcheck=__check)
fn('gr_font_destroy', None, c_void_p)
fn('gr_fref_feature_value', c_uint16, c_void_p, c_void_p)
fn('gr_fref_set_feature_value', c_int, c_void_p, c_uint16, c_void_p)
fn('gr_fref_id', c_uint32, c_void_p)
fn('gr_fref_n_values', c_uint16, c_void_p)
fn('gr_fref_value', c_int16, c_void_p, c_uint16)
fn('gr_fref_label', c_void_p,
c_void_p, POINTER(c_uint16), c_int, POINTER(c_uint32))
fn('gr_fref_value_label', c_void_p,
c_void_p, c_uint16, POINTER(c_uint16), c_int, POINTER(c_uint32))
fn('gr_label_destroy', None, c_void_p)
fn('gr_featureval_clone', c_void_p, c_void_p, errcheck=__check)
fn('gr_featureval_destroy', None, c_void_p)
fn('gr_cinfo_unicode_char', c_uint, c_void_p)
fn('gr_cinfo_break_weight', c_int, c_void_p)
fn('gr_cinfo_after', c_int, c_void_p)
fn('gr_cinfo_before', c_int, c_void_p)
fn('gr_cinfo_base', c_size_t, c_void_p)
fn('gr_count_unicode_characters', c_size_t,
c_int, c_void_p, c_void_p, POINTER(c_void_p))
fn('gr_make_seg', c_void_p,
c_void_p, c_void_p, c_uint32, c_void_p, c_int, c_void_p, c_size_t, c_int,
errcheck=__check)
fn('gr_seg_destroy', None, c_void_p)
fn('gr_seg_advance_X', c_float, c_void_p)
fn('gr_seg_advance_Y', c_float, c_void_p)
fn('gr_seg_n_cinfo', c_uint, c_void_p)
fn('gr_seg_cinfo', c_void_p, c_void_p, c_uint)
fn('gr_seg_n_slots', c_uint, c_void_p)
fn('gr_seg_first_slot', c_void_p, c_void_p)
fn('gr_seg_last_slot', c_void_p, c_void_p)
fn('gr_seg_justify', c_float,
c_void_p, c_void_p, c_void_p, c_double, c_int, c_void_p, c_void_p)
fn('gr_slot_next_in_segment', c_void_p, c_void_p)
fn('gr_slot_prev_in_segment', c_void_p, c_void_p)
fn('gr_slot_attached_to', c_void_p, c_void_p)
fn('gr_slot_first_attachment', c_void_p, c_void_p)
fn('gr_slot_next_sibling_attachment', c_void_p, c_void_p)
fn('gr_slot_gid', c_ushort, c_void_p)
fn('gr_slot_origin_X', c_float, c_void_p)
fn('gr_slot_origin_Y', c_float, c_void_p)
fn('gr_slot_advance_X', c_float, c_void_p)
fn('gr_slot_advance_Y', c_float, c_void_p)
fn('gr_slot_before', c_int, c_void_p)
fn('gr_slot_after', c_int, c_void_p)
fn('gr_slot_index', c_uint, c_void_p)
fn('gr_slot_attr', c_int, c_void_p, c_void_p, c_int, c_uint8)
fn('gr_slot_can_insert_before', c_int, c_void_p)
fn('gr_slot_original', c_int, c_void_p)
fn('gr_slot_linebreak_before', None, c_void_p)
(major, minor, debug) = grversion()
if major > 1 or minor > 1:
fn('gr_start_logging', c_int, c_void_p, c_char_p)
fn('gr_stop_logging', None, c_void_p)
else:
fn('graphite_start_logging', c_int, c_void_p, c_int)
fn('graphite_stop_logging', None)
def tag_to_str(num):
s = ctypes.create_string_buffer(b'\000' * 5)
gr2.gr_tag_to_str(num, s)
return bytes(s.value)
class Label(str):
def __new__(typename, ref, size):
v = ctypes.string_at(ref, size)
if sys.version_info.major > 2:
v = v.decode("utf-8")
return super(Label, typename).__new__(
typename, v)
def __init__(self, ref, size):
self.ref = ref
def __del__(self, __gr2=gr2):
if self.ref: __gr2.gr_label_destroy(self.ref)
class FeatureVal(object):
def __init__(self, fval):
self.fval = fval
def __del__(self, __gr2=gr2):
__gr2.gr_featureval_destroy(self.fval)
def get(self, fref):
return gr2.gr_fref_feature_value(fref.fref, self.fval)
def set(self, fref, val):
if not gr2.gr_fref_set_feature_value(fref.fref, val, self.fval):
raise ValueError('gr_fref_set_feature_value call failed')
class FeatureRef(object):
def __init__(self, fref, index=0):
self.fref = fref
self.index = index
def num(self):
return gr2.gr_fref_n_values(self.fref)
def val(self, ind):
return gr2.gr_fref_value(self.fref, ind)
def name(self, langid):
lngid = c_uint16(langid)
length = c_uint32(0)
res = gr2.gr_fref_label(self.fref, byref(lngid), 1, byref(length))
if res is None:
return Label("", 0)
return Label(res, length.value)
def label(self, ind, langid):
lngid = c_uint16(langid)
length = c_uint32(0)
res = gr2.gr_fref_value_label(self.fref, ind, byref(lngid), 1,
byref(length))
if res is None:
return Label("", 0)
return Label(res, length.value)
def tag(self):
return tag_to_str(gr2.gr_fref_id(self.fref))
class Face(object):
def __init__(self, data, options=0, fn=None):
self.face = None
if fn:
self.face = gr2.gr_make_face(bytes(data), fn, options)
else:
if not os.path.isfile(data):
raise FileNotFoundError(errno.ENOENT,
os.strerror(errno.ENOENT), data)
if hasattr(data, '__fspath__'): data = os.fspath(data)
self.face = gr2.gr_make_file_face(data.encode('utf_8'), options)
def __del__(self, __gr2=gr2):
__gr2.gr_face_destroy(self.face)
def get_upem(self):
finfo = gr2.gr_face_info(self.face)
return finfo.contents.upem
def num_glyphs(self):
return gr2.fr_face_n_glyphs(self.face)
def get_featureval(self, lang):
if isinstance(lang, bytes):
lang = gr2.gr_str_to_tag(lang)
return FeatureVal(gr2.gr_face_featureval_for_lang(self.face, lang))
def get_featureref(self, featid):
if isinstance(featid, bytes):
featid = gr2.gr_str_to_tag(featid)
return FeatureRef(gr2.gr_face_find_fref(self.face, featid))
@property
def featureRefs(self):
num = gr2.gr_face_n_fref(self.face)
for i in range(num):
yield FeatureRef(gr2.gr_face_fref(self.face, i), index=i)
@property
def featureLangs(self):
num = gr2.gr_face_n_languages(self.face)
for i in range(num):
yield gr2.gr_face_lang_by_index(self.face, i)
class Font(object):
def __init__(self, face, ppm, fn=None, data=None):
if fn:
self.font = gr2.gr_make_font_with_advance_fn(ppm, data, fn,
face.face)
else:
self.font = gr2.gr_make_font(ppm, face.face)
def __del__(self, __gr2=gr2):
__gr2.gr_font_destroy(self.font)
class CInfo(object):
def __init__(self, pcinfo):
self.cinfo = pcinfo
@property
def unicode(self):
return gr2.gr_cinfo_unicode_char(self.cinfo)
@property
def breakweight(self):
return gr2.gr_cinfo_break_weight(self.cinfo)
@property
def after(self):
return gr2.gr_cinfo_after(self.cinfo)
@property
def before(self):
return gr2.gr_cinfo_before(self.cinfo)
@property
def base(self):
return gr2.gr_cinfo_base(self.cinfo)
class Slot(object):
def __init__(self, s):
self.slot = s
def attached_to(self):
return Slot(gr2.gr_slot_attached_to(self.slot))
def children(self):
s = gr2.gr_slot_first_attachment(self.slot)
while s:
yield Slot(s)
s = gr2.gr_slot_next_sibling_attachment(s)
@property
def index(self):
return gr2.gr_slot_index(self.slot)
@property
def gid(self):
return gr2.gr_slot_gid(self.slot)
@property
def origin(self):
return (gr2.gr_slot_origin_X(self.slot),
gr2.gr_slot_origin_Y(self.slot))
@property
def advance(self):
return (gr2.gr_slot_advance_X(self.slot),
gr2.gr_slot_advance_Y(self.slot))
@property
def before(self):
return gr2.gr_slot_before(self.slot)
@property
def after(self):
return gr2.gr_slot_after(self.slot)
@property
def insert_before(self):
return gr2.gr_slot_can_insert_before(self.slot)
@property
def original(self):
return gr2.gr_slot_original(self.slot)
@property
def linebreak(self):
gr2.gr_slot_linebreak_before(self.slot)
def gettattr(self, seg, ind, subindex):
return gr2.gr_slot_attr(self.slot, seg.seg, ind, subindex)
class Segment(object):
def __init__(self, font, face,
scriptid, string, rtl,
length=None, feats=None):
if not length:
length = len(string)
if isinstance(scriptid, bytes):
scriptid = gr2.gr_str_to_tag(scriptid)
self.seg = gr2.gr_make_seg(font and font.font,
face.face,
scriptid,
feats and feats.fval,
1,
string.encode('utf_8'), length, rtl)
def __del__(self, __gr2=gr2):
__gr2.gr_seg_destroy(self.seg)
@property
def advance(self):
return (gr2.gr_seg_advance_X(self.seg),
gr2.gr_seg_advance_Y(self.seg))
@property
def num_cinfo(self):
return gr2.gr_seg_n_cinfo(self.seg)
def cinfo(self, ind):
return CInfo(gr2.gr_seg_cinfo(self.seg, ind))
@property
def num_slots(self):
return gr2.gr_seg_n_slots(self.seg)
@property
def slots(self):
s = gr2.gr_seg_first_slot(self.seg)
res = []
while s:
res.append(Slot(s))
s = gr2.gr_slot_next_in_segment(s)
return res
@property
def revslots(self):
s = gr2.gr_seg_last_slot(self.seg)
res = []
while s:
res.append(Slot(s))
s = gr2.gr_slot_prev_in_segment(s)
return res
def justify(self, start, font, width, flags, first=None, last=None):
gr2.gr_seg_justify(self.seg,
start.slot,
font.font,
width,
flags,
first and first.slot,
last and last.slot)

View File

@ -0,0 +1,86 @@
#!/usr/bin/env python3
import argparse
import enum
import graphite2
from argparse import ArgumentParser
class Dir(enum.IntEnum):
ltr = 0
rtl = 1
def usv(v): return int(v,16)
p = ArgumentParser()
p.add_argument('fontname', help='font file')
p.add_argument('codes', nargs='+', type=usv, help='USV codes')
p.add_argument('--ppm', type=int, default=96, help='Pixels per EM')
p.add_argument('--script', type=bytes, default=b'latn', help='Script tag')
p.add_argument('--dir', type=Dir, choices=[Dir.ltr, Dir.rtl],
default=Dir.ltr,
help='Text direction')
args = p.parse_args()
face = graphite2.Face(args.fontname)
font = graphite2.Font(face, args.ppm)
args.codes = ''.join(chr(c) for c in args.codes)
seg = graphite2.Segment(font, face, args.script, args.codes, args.dir)
print(f'Glyphs: {[s.gid for s in seg.slots]!r}')
def reordering_clusters(seg):
slots_first = seg.cinfo(0).before
slots_last = seg.cinfo(0).after
char_first = 0
for n in range(1,seg.num_cinfo):
ci = seg.cinfo(n)
slots_first = min(ci.before, slots_first)
if ci.before > slots_last:
yield (char_first, slots_last)
char_first = n
slots_first = ci.before
slots_last = max(ci.after, slots_last)
yield (char_first, slots_last)
print(list(reordering_clusters(seg)))
def clusters(seg):
slots = seg.slots
bounds = reordering_clusters(seg)
glyphs = [slots[0].index]
fc,ls = next(bounds)
for s in slots[1:]:
if s.index > ls:
fc_,ls = next(bounds)
if s.insert_before:
yield (fc_ - fc, glyphs)
fc = fc_
glyphs = []
glyphs.append(s.index)
yield (seg.num_cinfo - fc, glyphs)
print(list(clusters(seg)))
# 1a23 1a6e 1a55 1a60 1a75 1a26 1a23 1a6e 1a55 1a60 1a75 1a26
# [73, 4, 27, 76, 102, 73), 4, 27, 76, 102, 73, 4, 27, 76, 102]
# 1a23 1a6e 1a55 1a60 1a75 1a26: [73, 4, 27, 76, 102]
# 1a23 1a6e 1a55: [73]

View File

@ -0,0 +1,142 @@
# SPDX-License-Identifier: MIT
# Copyright 2010, SIL International, All rights reserved.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
project(graphite2_core)
cmake_policy(SET CMP0012 NEW)
INCLUDE(CheckCXXSourceCompiles)
set(GRAPHITE_API_CURRENT 3)
set(GRAPHITE_API_REVISION 2)
set(GRAPHITE_API_AGE 1)
set(GRAPHITE_VERSION ${GRAPHITE_API_CURRENT}.${GRAPHITE_API_REVISION}.${GRAPHITE_API_AGE})
set(GRAPHITE_SO_VERSION ${GRAPHITE_API_CURRENT})
include(TestBigEndian)
include_directories(${PROJECT_SOURCE_DIR})
set(FILEFACE FileFace.cpp)
if (GRAPHITE2_NFILEFACE)
add_definitions(-DGRAPHITE2_NFILEFACE)
set(FILEFACE)
endif()
set(TRACING json.cpp)
if (GRAPHITE2_NTRACING)
add_definitions(-DGRAPHITE2_NTRACING)
set(TRACING)
endif()
if (GRAPHITE2_TELEMETRY)
add_definitions(-DGRAPHITE2_TELEMETRY)
endif()
if (NOT BUILD_SHARED_LIBS)
add_definitions(-DGRAPHITE2_STATIC)
endif()
set(GRAPHITE_HEADERS
../include/graphite2/Font.h
../include/graphite2/Segment.h
../include/graphite2/Types.h
../include/graphite2/Log.h
)
file(GLOB PRIVATE_HEADERS inc/*.h)
add_library(graphite2
${GRAPHITE2_VM_TYPE}_machine.cpp
gr_char_info.cpp
gr_features.cpp
gr_face.cpp
gr_font.cpp
gr_logging.cpp
gr_segment.cpp
gr_slot.cpp
CmapCache.cpp
Code.cpp
Collider.cpp
Decompressor.cpp
Face.cpp
FeatureMap.cpp
Font.cpp
GlyphFace.cpp
GlyphCache.cpp
Intervals.cpp
Justifier.cpp
NameTable.cpp
Pass.cpp
Position.cpp
Segment.cpp
ShapingContext.cpp
Silf.cpp
Slot.cpp
SlotBuffer.cpp
Sparse.cpp
TtfUtil.cpp
UtfCodec.cpp
${FILEFACE}
${TRACING})
set_target_properties(graphite2 PROPERTIES PUBLIC_HEADER "${GRAPHITE_HEADERS}"
SOVERSION ${GRAPHITE_SO_VERSION}
VERSION ${GRAPHITE_VERSION}
LT_VERSION_CURRENT ${GRAPHITE_API_CURRENT}
LT_VERSION_REVISION ${GRAPHITE_API_REVISION}
LT_VERSION_AGE ${GRAPHITE_API_AGE})
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set_target_properties(graphite2 PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wendif-labels -Wshadow -Wctor-dtor-privacy -Wnon-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden"
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
LINKER_LANGUAGE C)
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86|i.86")
add_definitions(-mfpmath=sse -msse2)
endif()
if (CMAKE_COMPILER_IS_GNUCXX)
add_definitions(-Wno-class-memaccess -Wdouble-promotion)
endif()
message(STATUS "Compiler ID is: ${CMAKE_CXX_COMPILER_ID}")
if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang")
add_definitions(-Wimplicit-fallthrough -Wshorten-64-to-32)
endif()
if (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
target_link_libraries(graphite2 kernel32 msvcr90 mingw32 gcc user32)
else (${CMAKE_CXX_COMPILER} MATCHES ".*mingw.*")
if (GRAPHITE2_SANITIZERS)
target_link_libraries(graphite2 c gcc_s)
else ()
target_link_libraries(graphite2 c gcc)
endif ()
endif()
include(Graphite)
if (BUILD_SHARED_LIBS)
nolib_test(stdc++ $<TARGET_SONAME_FILE:graphite2>)
endif ()
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
CREATE_LIBTOOL_FILE(graphite2 "/lib${LIB_SUFFIX}")
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
set_target_properties(graphite2 PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wno-unknown-pragmas -Wimplicit-fallthrough -Wendif-labels -Wshadow -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor -fno-rtti -fno-exceptions -fvisibility=hidden -fvisibility-inlines-hidden"
LINK_FLAGS "-nodefaultlibs"
LINKER_LANGUAGE C)
if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86|i.86")
add_definitions(-mfpmath=sse -msse2)
endif()
target_link_libraries(graphite2 c)
include(Graphite)
nolib_test(stdc++ $<TARGET_SONAME_FILE:graphite2>)
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")
CREATE_LIBTOOL_FILE(graphite2 "/lib${LIB_SUFFIX}")
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set_target_properties(graphite2 PROPERTIES
COMPILE_DEFINITIONS "_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;UNICODE;GRAPHITE2_EXPORTING")
endif()
install(TARGETS graphite2 EXPORT graphite2 LIBRARY DESTINATION lib${LIB_SUFFIX} ARCHIVE DESTINATION lib${LIB_SUFFIX} PUBLIC_HEADER DESTINATION include/graphite2 RUNTIME DESTINATION bin)
install(EXPORT graphite2 DESTINATION share/graphite2 NAMESPACE gr2_)

View File

@ -0,0 +1,132 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Main.h"
#include "inc/CmapCache.h"
#include "inc/Face.h"
#include "inc/TtfTypes.h"
#include "inc/TtfUtil.h"
using namespace graphite2;
const void * bmp_subtable(const Face::Table & cmap)
{
const void * stbl;
if (!cmap.size()) return 0;
if (TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 1, cmap.size()), cmap + cmap.size())
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 3, cmap.size()), cmap + cmap.size())
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 2, cmap.size()), cmap + cmap.size())
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 1, cmap.size()), cmap + cmap.size())
|| TtfUtil::CheckCmapSubtable4(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 0, cmap.size()), cmap + cmap.size()))
return stbl;
return 0;
}
const void * smp_subtable(const Face::Table & cmap)
{
const void * stbl;
if (!cmap.size()) return 0;
if (TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 3, 10, cmap.size()), cmap + cmap.size())
|| TtfUtil::CheckCmapSubtable12(stbl = TtfUtil::FindCmapSubtable(cmap, 0, 4, cmap.size()), cmap + cmap.size()))
return stbl;
return 0;
}
template <unsigned int (*NextCodePoint)(const void *, unsigned int, int *),
uint16_t (*LookupCodePoint)(const void *, unsigned int, int)>
bool cache_subtable(uint16_t * blocks[], const void * cst, const unsigned int limit)
{
int rangeKey = 0;
uint32_t codePoint = NextCodePoint(cst, 0, &rangeKey),
prevCodePoint = 0;
while (codePoint < limit)
{
unsigned int block = codePoint >> 8;
if (!blocks[block])
{
blocks[block] = grzeroalloc<uint16_t>(0x100);
if (!blocks[block])
return false;
}
blocks[block][codePoint & 0xFF] = LookupCodePoint(cst, codePoint, rangeKey);
// prevent infinite loop
if (codePoint <= prevCodePoint)
codePoint = prevCodePoint + 1;
prevCodePoint = codePoint;
codePoint = NextCodePoint(cst, codePoint, &rangeKey);
}
return true;
}
CachedCmap::CachedCmap(const Face & face)
: m_isBmpOnly(true),
m_blocks(0)
{
const Face::Table cmap(face, Tag::cmap);
if (!cmap) return;
const void * bmp_cmap = bmp_subtable(cmap);
const void * smp_cmap = smp_subtable(cmap);
m_isBmpOnly = !smp_cmap;
m_blocks = grzeroalloc<uint16_t *>(m_isBmpOnly ? 0x100 : 0x1100);
if (m_blocks && smp_cmap)
{
if (!cache_subtable<TtfUtil::CmapSubtable12NextCodepoint, TtfUtil::CmapSubtable12Lookup>(m_blocks, smp_cmap, 0x10FFFF))
return;
}
if (m_blocks && bmp_cmap)
{
if (!cache_subtable<TtfUtil::CmapSubtable4NextCodepoint, TtfUtil::CmapSubtable4Lookup>(m_blocks, bmp_cmap, 0xFFFF))
return;
}
}
CachedCmap::~CachedCmap() throw()
{
if (!m_blocks) return;
unsigned int numBlocks = (m_isBmpOnly)? 0x100 : 0x1100;
for (unsigned int i = 0; i < numBlocks; i++)
free(m_blocks[i]);
free(m_blocks);
}
uint16_t CachedCmap::operator [] (const uint32_t usv) const throw()
{
if ((m_isBmpOnly && usv > 0xFFFF) || (usv > 0x10FFFF))
return 0;
const uint32_t block = 0xFFFF & (usv >> 8);
if (m_blocks[block])
return m_blocks[block][usv & 0xFF];
return 0;
};
CachedCmap::operator bool() const throw()
{
return m_blocks != 0;
}
DirectCmap::DirectCmap(const Face & face)
: _cmap(face, Tag::cmap),
_smp(smp_subtable(_cmap)),
_bmp(bmp_subtable(_cmap))
{
}
uint16_t DirectCmap::operator [] (const uint32_t usv) const throw()
{
return usv > 0xFFFF
? (_smp ? TtfUtil::CmapSubtable12Lookup(_smp, usv, 0) : 0)
: TtfUtil::CmapSubtable4Lookup(_bmp, usv, 0);
}
DirectCmap::operator bool () const throw()
{
return _cmap && _bmp;
}

759
src/graphite2/src/Code.cpp Normal file
View File

@ -0,0 +1,759 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This class represents loaded graphite stack machine code. It performs
// basic sanity checks, on the incoming code to prevent more obvious problems
// from crashing graphite.
// Author: Tim Eves
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstring>
#include "graphite2/Segment.h"
#include "inc/Code.h"
#include "inc/Face.h"
#include "inc/GlyphFace.h"
#include "inc/GlyphCache.h"
#include "inc/Machine.h"
#include "inc/Rule.h"
#include "inc/Silf.h"
#include "inc/ShapingContext.hpp"
#include <cstdio>
#ifdef NDEBUG
#ifdef __GNUC__
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
#endif
using namespace graphite2;
using namespace vm;
using attrCode = gr_attrCode;
namespace {
inline bool is_return(const instr i) {
const opcode_t * opmap = Machine::getOpcodeTable();
const instr pop_ret = *opmap[POP_RET].impl,
ret_zero = *opmap[RET_ZERO].impl,
ret_true = *opmap[RET_TRUE].impl;
return i == pop_ret || i == ret_zero || i == ret_true;
}
struct context
{
context(uint8_t ref=0) : codeRef(ref) {flags.changed=false; flags.referenced=false;}
struct {
uint8_t changed:1,
referenced:1;
} flags;
uint8_t codeRef;
};
} // end namespace
class Machine::Code::decoder
{
public:
struct limits;
static const int NUMCONTEXTS = 256;
decoder(limits & lims, Code &code, enum passtype pt) throw();
bool load(const byte * bc_begin, const byte * bc_end);
void apply_analysis(instr * const code, instr * code_end);
byte max_ref() { return _max_ref; }
int out_index() const { return _out_index; }
private:
void set_ref(int index) throw();
void set_noref(int index) throw();
void set_changed(int index) throw();
opcode fetch_opcode(const byte * bc);
void analyse_opcode(const opcode, const int8_t * const dp) throw();
bool emit_opcode(opcode opc, const byte * & bc);
bool validate_opcode(const byte opc, const byte * const bc);
bool valid_upto(const uint16_t limit, const uint16_t x) const throw();
bool test_context() const throw();
bool test_ref(int8_t index) const throw();
bool test_attr(attrCode attr) const throw();
void failure(const status_t s) const throw() { _code.failure(s); }
Code & _code;
int _out_index;
uint16_t _out_length;
instr * _instr;
byte * _data;
limits & _max;
enum passtype _passtype;
int _stack_depth;
bool _in_ctxt_item;
int16_t _slotref;
context _contexts[NUMCONTEXTS];
byte _max_ref;
};
struct Machine::Code::decoder::limits
{
const byte * bytecode;
const uint8_t pre_context;
const uint16_t rule_length,
classes,
glyf_attrs,
features;
const byte attrid[gr_slatMax];
};
inline Machine::Code::decoder::decoder(limits & lims, Code &code, enum passtype pt) throw()
: _code(code),
_out_index(code._constraint ? 0 : lims.pre_context),
_out_length(code._constraint ? 1 : lims.rule_length),
_instr(code._code), _data(code._data), _max(lims), _passtype(pt),
_stack_depth(0),
_in_ctxt_item(false),
_slotref(0),
_max_ref(0)
{ }
Machine::Code::Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
uint8_t pre_context, uint16_t rule_length, const Silf & silf, const Face & face,
enum passtype pt, byte * * const _out)
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0), _status(loaded),
_constraint(is_constraint), _modify(false), _delete(false), _own(_out==0)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _code_cat(face.tele.code);
#endif
assert(bytecode_begin != 0);
if (bytecode_begin == bytecode_end)
{
// ::new (this) Code();
return;
}
assert(bytecode_end > bytecode_begin);
const opcode_t * op_to_fn = Machine::getOpcodeTable();
// Allocate code and data target buffers, these sizes are a worst case
// estimate. Once we know their real sizes the we'll shrink them.
if (_out) _code = reinterpret_cast<instr *>(*_out);
else _code = static_cast<instr *>(malloc(estimateCodeDataOut(bytecode_end-bytecode_begin, 1, is_constraint ? 0 : rule_length)));
_data = reinterpret_cast<byte *>(_code + (bytecode_end - bytecode_begin));
if (!_code || !_data) {
failure(alloc_failed);
return;
}
decoder::limits lims = {
bytecode_end,
pre_context,
rule_length,
uint16_t(silf.numClasses()),
face.glyphs().numAttrs(),
face.numFeatures(),
{1,1,1,1,1,1,1,1,
1,1,1,1,1,1,1,255,
1,1,1,1,1,1,1,1,
1,1,1,1,1,1,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0, byte(silf.numUser())}
};
decoder dec(lims, *this, pt);
if(!dec.load(bytecode_begin, bytecode_end))
return;
// Is this an empty program?
if (_instr_count == 0)
{
release_buffers();
::new (this) Code();
return;
}
// When we reach the end check we've terminated it correctly
if (!is_return(_code[_instr_count-1])) {
failure(missing_return);
return;
}
assert((_constraint && immutable()) || !_constraint);
dec.apply_analysis(_code, _code + _instr_count);
_max_ref = dec.max_ref();
// Now we know exactly how much code and data the program really needs
// realloc the buffers to exactly the right size so we don't waste any
// memory.
assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_instr_count));
assert((bytecode_end - bytecode_begin) >= ptrdiff_t(_data_size));
memmove(_code + (_instr_count+1), _data, _data_size*sizeof(byte));
size_t const total_sz = ((_instr_count+1) + (_data_size + sizeof(instr)-1)/sizeof(instr))*sizeof(instr);
if (_out)
*_out += total_sz;
else
{
instr * const old_code = _code;
_code = static_cast<instr *>(realloc(_code, total_sz));
if (!_code) free(old_code);
}
_data = reinterpret_cast<byte *>(_code + (_instr_count+1));
if (!_code)
{
failure(alloc_failed);
return;
}
// Make this RET_ZERO, we should never reach this but just in case ...
_code[_instr_count] = op_to_fn[RET_ZERO].impl[_constraint];
#ifdef GRAPHITE2_TELEMETRY
telemetry::count_bytes(_data_size + (_instr_count+1)*sizeof(instr));
#endif
}
Machine::Code::~Code() throw ()
{
if (_own)
release_buffers();
}
bool Machine::Code::decoder::load(const byte * bc, const byte * bc_end)
{
_max.bytecode = bc_end;
while (bc < bc_end)
{
const opcode opc = fetch_opcode(bc++);
if (opc == vm::MAX_OPCODE)
return false;
analyse_opcode(opc, reinterpret_cast<const int8_t *>(bc));
if (!emit_opcode(opc, bc))
return false;
}
return bool(_code);
}
// Validation check and fixups.
//
opcode Machine::Code::decoder::fetch_opcode(const byte * bc)
{
const byte opc = *bc++;
// Do some basic sanity checks based on what we know about the opcode
if (!validate_opcode(opc, bc)) return MAX_OPCODE;
// And check its arguments as far as possible
switch (opcode(opc))
{
case NOP :
break;
case PUSH_BYTE :
case PUSH_BYTEU :
case PUSH_SHORT :
case PUSH_SHORTU :
case PUSH_LONG :
++_stack_depth;
break;
case ADD :
case SUB :
case MUL :
case DIV :
case MIN_ :
case MAX_ :
case AND :
case OR :
case EQUAL :
case NOT_EQ :
case LESS :
case GTR :
case LESS_EQ :
case GTR_EQ :
case BITOR :
case BITAND :
if (--_stack_depth <= 0)
failure(underfull_stack);
break;
case NEG :
case TRUNC8 :
case TRUNC16 :
case NOT :
case BITNOT :
case BITSET :
if (_stack_depth <= 0)
failure(underfull_stack);
break;
case COND :
_stack_depth -= 2;
if (_stack_depth <= 0)
failure(underfull_stack);
break;
case NEXT_N : // runtime checked
break;
case NEXT :
case COPY_NEXT :
++_out_index;
if (_out_index < -1 || _out_index > _out_length || _slotref > _max.rule_length)
failure(out_of_range_data);
break;
case PUT_GLYPH_8BIT_OBS :
valid_upto(_max.classes, bc[0]);
test_context();
break;
case PUT_SUBS_8BIT_OBS :
test_ref(int8_t(bc[0]));
valid_upto(_max.classes, bc[1]);
valid_upto(_max.classes, bc[2]);
test_context();
break;
case PUT_COPY :
test_ref(int8_t(bc[0]));
test_context();
break;
case INSERT :
if (_passtype >= PASS_TYPE_POSITIONING)
failure(invalid_opcode);
++_out_length;
if (_out_index < 0) ++_out_index;
if (_out_index < -1 || _out_index >= _out_length)
failure(out_of_range_data);
break;
case DELETE :
if (_passtype >= PASS_TYPE_POSITIONING)
failure(invalid_opcode);
if (_out_index < _max.pre_context)
failure(out_of_range_data);
--_out_index;
--_out_length;
if (_out_index < -1 || _out_index > _out_length)
failure(out_of_range_data);
break;
case ASSOC :
if (bc[0] == 0)
failure(out_of_range_data);
for (uint8_t num = bc[0]; num; --num)
test_ref(int8_t(bc[num]));
test_context();
break;
case CNTXT_ITEM :
valid_upto(_max.rule_length, _max.pre_context + int8_t(bc[0]));
if (bc + 2 + bc[1] >= _max.bytecode) failure(jump_past_end);
if (_in_ctxt_item) failure(nested_context_item);
break;
case ATTR_SET :
case ATTR_ADD :
case ATTR_SUB :
case ATTR_SET_SLOT :
if (--_stack_depth < 0)
failure(underfull_stack);
valid_upto(gr_slatMax, bc[0]);
if (attrCode(bc[0]) == gr_slatUserDefn) // use IATTR for user attributes
failure(out_of_range_data);
test_attr(attrCode(bc[0]));
test_context();
break;
case IATTR_SET_SLOT :
if (--_stack_depth < 0)
failure(underfull_stack);
if (valid_upto(gr_slatMax, bc[0]))
valid_upto(_max.attrid[bc[0]], bc[1]);
test_attr(attrCode(bc[0]));
test_context();
break;
case PUSH_SLOT_ATTR :
++_stack_depth;
valid_upto(gr_slatMax, bc[0]);
test_ref(int8_t(bc[1]));
if (attrCode(bc[0]) == gr_slatUserDefn) // use IATTR for user attributes
failure(out_of_range_data);
test_attr(attrCode(bc[0]));
break;
case PUSH_GLYPH_ATTR_OBS :
case PUSH_ATT_TO_GATTR_OBS :
++_stack_depth;
valid_upto(_max.glyf_attrs, bc[0]);
test_ref(int8_t(bc[1]));
break;
case PUSH_ATT_TO_GLYPH_METRIC :
case PUSH_GLYPH_METRIC :
++_stack_depth;
valid_upto(kgmetDescent, bc[0]);
test_ref(int8_t(bc[1]));
// level: dp[2] no check necessary
break;
case PUSH_FEAT :
++_stack_depth;
valid_upto(_max.features, bc[0]);
test_ref(int8_t(bc[1]));
break;
case PUSH_ISLOT_ATTR :
++_stack_depth;
if (valid_upto(gr_slatMax, bc[0]))
{
test_ref(int8_t(bc[1]));
valid_upto(_max.attrid[bc[0]], bc[2]);
}
test_attr(attrCode(bc[0]));
break;
case PUSH_IGLYPH_ATTR :// not implemented
++_stack_depth;
break;
case POP_RET :
if (--_stack_depth < 0)
failure(underfull_stack);
GR_FALLTHROUGH;
// no break
case RET_ZERO :
case RET_TRUE :
break;
case IATTR_SET :
case IATTR_ADD :
case IATTR_SUB :
if (--_stack_depth < 0)
failure(underfull_stack);
if (valid_upto(gr_slatMax, bc[0]))
valid_upto(_max.attrid[bc[0]], bc[1]);
test_attr(attrCode(bc[0]));
test_context();
break;
case PUSH_PROC_STATE : // dummy: dp[0] no check necessary
case PUSH_VERSION :
++_stack_depth;
break;
case PUT_SUBS :
test_ref(int8_t(bc[0]));
valid_upto(_max.classes, uint16_t(bc[1]<< 8) | bc[2]);
valid_upto(_max.classes, uint16_t(bc[3]<< 8) | bc[4]);
test_context();
break;
case PUT_SUBS2 : // not implemented
case PUT_SUBS3 : // not implemented
break;
case PUT_GLYPH :
valid_upto(_max.classes, uint16_t(bc[0]<< 8) | bc[1]);
test_context();
break;
case PUSH_GLYPH_ATTR :
case PUSH_ATT_TO_GLYPH_ATTR :
++_stack_depth;
valid_upto(_max.glyf_attrs, uint16_t(bc[0]<< 8) | bc[1]);
test_ref(int8_t(bc[2]));
break;
case SET_FEAT :
valid_upto(_max.features, bc[0]);
test_ref(int8_t(bc[1]));
break;
default:
failure(invalid_opcode);
break;
}
return bool(_code) ? opcode(opc) : MAX_OPCODE;
}
void Machine::Code::decoder::analyse_opcode(const opcode opc, const int8_t * arg) throw()
{
switch (opc)
{
case DELETE :
_code._delete = true;
break;
case ASSOC :
set_changed(0);
// for (uint8_t num = arg[0]; num; --num)
// _analysis.set_noref(num);
break;
case PUT_GLYPH_8BIT_OBS :
case PUT_GLYPH :
_code._modify = true;
set_changed(0);
break;
case ATTR_SET :
case ATTR_ADD :
case ATTR_SUB :
case ATTR_SET_SLOT :
case IATTR_SET_SLOT :
case IATTR_SET :
case IATTR_ADD :
case IATTR_SUB :
set_noref(0);
break;
case NEXT :
case COPY_NEXT :
++_slotref;
_contexts[_slotref] = context(uint8_t(_code._instr_count+1));
// if (_analysis.slotref > _analysis.max_ref) _analysis.max_ref = _analysis.slotref;
break;
case INSERT :
if (_slotref >= 0) --_slotref;
_code._modify = true;
break;
case PUT_SUBS_8BIT_OBS : // slotref on 1st parameter
case PUT_SUBS :
_code._modify = true;
set_changed(0);
GR_FALLTHROUGH;
// no break
case PUT_COPY :
if (arg[0] != 0) { set_changed(0); _code._modify = true; }
set_ref(arg[0]);
break;
case PUSH_GLYPH_ATTR_OBS :
case PUSH_SLOT_ATTR :
case PUSH_GLYPH_METRIC :
case PUSH_ATT_TO_GATTR_OBS :
case PUSH_ATT_TO_GLYPH_METRIC :
case PUSH_ISLOT_ATTR :
case PUSH_FEAT :
case SET_FEAT :
set_ref(arg[1]);
break;
case PUSH_ATT_TO_GLYPH_ATTR :
case PUSH_GLYPH_ATTR :
set_ref(arg[2]);
break;
default:
break;
}
}
bool Machine::Code::decoder::emit_opcode(opcode opc, const byte * & bc)
{
const opcode_t * op_to_fn = Machine::getOpcodeTable();
const opcode_t & op = op_to_fn[opc];
if (op.impl[_code._constraint] == 0)
{
failure(unimplemented_opcode_used);
return false;
}
const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
// Add this instruction
*_instr++ = op.impl[_code._constraint];
++_code._instr_count;
// Grab the parameters
if (param_sz) {
memcpy(_data, bc, param_sz * sizeof(byte));
bc += param_sz;
_data += param_sz;
_code._data_size += param_sz;
}
// recursively decode a context item so we can split the skip into
// instruction and data portions.
if (opc == CNTXT_ITEM)
{
assert(_out_index == 0);
_in_ctxt_item = true;
_out_index = _max.pre_context + int8_t(_data[-2]);
_slotref = int8_t(_data[-2]);
_out_length = _max.rule_length;
const size_t ctxt_start = _code._instr_count;
byte & instr_skip = _data[-1];
byte & data_skip = *_data++;
++_code._data_size;
const byte *curr_end = _max.bytecode;
if (load(bc, bc + instr_skip))
{
bc += instr_skip;
data_skip = instr_skip - byte(_code._instr_count - ctxt_start);
instr_skip = byte(_code._instr_count - ctxt_start);
_max.bytecode = curr_end;
_out_length = 1;
_out_index = 0;
_slotref = 0;
_in_ctxt_item = false;
}
else
{
_out_index = 0;
_slotref = 0;
return false;
}
}
return bool(_code);
}
void Machine::Code::decoder::apply_analysis(instr * const code, instr * code_end)
{
// insert TEMP_COPY commands for slots that need them (that change and are referenced later)
int tempcount = 0;
if (_code._constraint) return;
const instr temp_copy = Machine::getOpcodeTable()[TEMP_COPY].impl[0];
for (const context * c = _contexts, * const ce = c + _slotref; c < ce; ++c)
{
if (!c->flags.referenced || !c->flags.changed) continue;
instr * const tip = code + c->codeRef + tempcount;
memmove(tip+1, tip, (code_end - tip) * sizeof(instr));
*tip = temp_copy;
++code_end;
++tempcount;
_code._delete = true;
}
_code._instr_count = code_end - code;
}
inline
bool Machine::Code::decoder::validate_opcode(const byte opc, const byte * const bc)
{
if (opc >= MAX_OPCODE)
{
failure(invalid_opcode);
return false;
}
const opcode_t & op = Machine::getOpcodeTable()[opc];
if (op.impl[_code._constraint] == 0)
{
failure(unimplemented_opcode_used);
return false;
}
if (op.param_sz == VARARGS && bc >= _max.bytecode)
{
failure(arguments_exhausted);
return false;
}
const size_t param_sz = op.param_sz == VARARGS ? bc[0] + 1 : op.param_sz;
if (bc - 1 + param_sz >= _max.bytecode)
{
failure(arguments_exhausted);
return false;
}
return true;
}
bool Machine::Code::decoder::valid_upto(const uint16_t limit, const uint16_t x) const throw()
{
const bool t = (limit != 0) && (x < limit);
if (!t) failure(out_of_range_data);
return t;
}
inline
bool Machine::Code::decoder::test_ref(int8_t index) const throw()
{
if (_code._constraint && !_in_ctxt_item)
{
if (index > 0 || -index > _max.pre_context)
{
failure(out_of_range_data);
return false;
}
}
else
{
if (_max.rule_length == 0
|| (_slotref + _max.pre_context + index >= _max.rule_length)
|| (_slotref + _max.pre_context + index < 0))
{
failure(out_of_range_data);
return false;
}
}
return true;
}
bool Machine::Code::decoder::test_context() const throw()
{
if (_out_index >= _out_length || _out_index < 0 || _slotref >= NUMCONTEXTS - 1)
{
failure(out_of_range_data);
return false;
}
return true;
}
bool Machine::Code::decoder::test_attr(attrCode attr) const throw()
{
if (_passtype < PASS_TYPE_POSITIONING)
{
if (attr != gr_slatBreak && attr != gr_slatDir && attr != gr_slatUserDefn
&& attr != gr_slatCompRef)
{
failure(out_of_range_data);
return false;
}
}
return true;
}
inline
void Machine::Code::failure(const status_t s) throw() {
release_buffers();
_status = s;
}
inline
void Machine::Code::decoder::set_ref(int index) throw() {
if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
_contexts[index + _slotref].flags.referenced = true;
if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}
inline
void Machine::Code::decoder::set_noref(int index) throw() {
if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}
inline
void Machine::Code::decoder::set_changed(int index) throw() {
if (index + _slotref < 0 || index + _slotref >= NUMCONTEXTS) return;
_contexts[index + _slotref].flags.changed= true;
if (index + _slotref > _max_ref) _max_ref = index + _slotref;
}
void Machine::Code::release_buffers() throw()
{
if (_own)
free(_code);
_code = 0;
_data = 0;
_own = false;
}
int32_t Machine::Code::run(Machine & m, ShapingContext::map_t::iterator & slot_in, slotref & slot_out) const
{
// assert(_own);
assert(*this); // Check we are actually runnable
if (m.shaping_context().map.size() <= size_t(_max_ref + m.shaping_context().context())
|| !m.shaping_context().map[_max_ref + m.shaping_context().context()].is_valid())
{
m._status = Machine::slot_offset_out_bounds;
return 1;
// return m.run(_code, _data, map);
}
return m.run(_code, _data, slot_in, slot_out);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,102 @@
// SPDX-License-Identifier: MIT
// Copyright 2015, SIL International, All rights reserved.
#include <cassert>
#include "inc/Decompressor.h"
#include "inc/Compression.h"
using namespace lz4;
namespace {
inline
u32 read_literal(u8 const * &s, u8 const * const e, u32 l) {
if (l == 15 && s != e)
{
u8 b = 0;
do { l += b = *s++; } while(b==0xff && s != e);
}
return l;
}
bool read_sequence(u8 const * &src, u8 const * const end, u8 const * &literal,
u32 & literal_len, u32 & match_len, u32 & match_dist)
{
u8 const token = *src++;
literal_len = read_literal(src, end, token >> 4);
literal = src;
src += literal_len;
// Normal exit for end of stream, wrap arround check and parital match check.
if (src > end - sizeof(u16) || src < literal)
return false;
match_dist = *src++;
match_dist |= *src++ << 8;
match_len = read_literal(src, end, token & 0xf) + MINMATCH;
// Malformed stream check.
return src <= end-MINCODA;
}
}
int lz4::decompress(void const *in, size_t in_size, void *out, size_t out_size)
{
if (out_size <= in_size || in_size < MINSRCSIZE)
return -1;
u8 const * src = static_cast<u8 const *>(in),
* literal = 0,
* const src_end = src + in_size;
u8 * dst = static_cast<u8*>(out),
* const dst_end = dst + out_size;
// Check the in and out size hasn't wrapped around.
if (src >= src_end || dst >= dst_end)
return -1;
u32 literal_len = 0,
match_len = 0,
match_dist = 0;
while (read_sequence(src, src_end, literal, literal_len, match_len,
match_dist))
{
if (literal_len != 0)
{
// Copy in literal. At this point the a minimal literal + minminal
// match plus the coda (1 + 2 + 5) must be 8 bytes or more allowing
// us to remain within the src buffer for an overrun_copy on
// machines upto 64 bits.
if (align(literal_len) > out_size)
return -1;
dst = overrun_copy(dst, literal, literal_len);
out_size -= literal_len;
}
// Copy, possibly repeating, match from earlier in the
// decoded output.
u8 const * const pcpy = dst - match_dist;
if (pcpy < static_cast<u8*>(out)
|| match_len > unsigned(out_size - LASTLITERALS)
// Wrap around checks:
|| out_size < LASTLITERALS || pcpy >= dst)
return -1;
if (dst > pcpy+sizeof(unsigned long)
&& align(match_len) <= out_size)
dst = overrun_copy(dst, pcpy, match_len);
else
dst = safe_copy(dst, pcpy, match_len);
out_size -= match_len;
}
if (literal > src_end - literal_len || literal_len > out_size)
return -1;
dst = fast_copy(dst, literal, literal_len);
return int(dst - (u8*)out);
}

344
src/graphite2/src/Face.cpp Normal file
View File

@ -0,0 +1,344 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <cstring>
#include "graphite2/Segment.h"
#include "inc/CmapCache.h"
#include "inc/debug.h"
#include "inc/Decompressor.h"
#include "inc/Endian.h"
#include "inc/Face.h"
#include "inc/Font.h"
#include "inc/FileFace.h"
#include "inc/GlyphFace.h"
#include "inc/json.h"
#include "inc/Segment.h"
#include "inc/NameTable.h"
#include "inc/Error.h"
using namespace graphite2;
namespace
{
enum compression
{
NONE,
LZ4
};
}
Face::Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops)
: m_appFaceHandle(appFaceHandle),
m_pFileFace(NULL),
m_pGlyphFaceCache(NULL),
m_cmap(NULL),
m_pNames(NULL),
m_logger(NULL),
m_error(0), m_errcntxt(0),
m_silfs(NULL),
m_numSilf(0),
m_ascent(0),
m_descent(0)
{
memset(&m_ops, 0, sizeof m_ops);
memcpy(&m_ops, &ops, min(sizeof m_ops, ops.size));
}
Face::~Face()
{
setLogger(0);
delete m_pGlyphFaceCache;
delete m_cmap;
delete[] m_silfs;
#ifndef GRAPHITE2_NFILEFACE
delete m_pFileFace;
#endif
delete m_pNames;
}
float Face::default_glyph_advance(const void* font_ptr, uint16_t glyphid)
{
const Font & font = *reinterpret_cast<const Font *>(font_ptr);
return font.face().glyphs().glyph(glyphid)->theAdvance().x * font.scale();
}
bool Face::readGlyphs(uint32_t faceOptions)
{
Error e;
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _glyph_cat(tele.glyph);
#endif
error_context(EC_READGLYPHS);
m_pGlyphFaceCache = new GlyphCache(*this, faceOptions);
if (e.test(!m_pGlyphFaceCache, E_OUTOFMEM)
|| e.test(m_pGlyphFaceCache->numGlyphs() == 0, E_NOGLYPHS)
|| e.test(m_pGlyphFaceCache->unitsPerEm() == 0, E_BADUPEM))
{
return error(e);
}
if (faceOptions & gr_face_cacheCmap)
m_cmap = new CachedCmap(*this);
else
m_cmap = new DirectCmap(*this);
if (e.test(!m_cmap, E_OUTOFMEM) || e.test(!*m_cmap, E_BADCMAP))
return error(e);
if (faceOptions & gr_face_preloadGlyphs)
nameTable(); // preload the name table along with the glyphs.
return true;
}
bool Face::readGraphite(const Table & silf)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _silf_cat(tele.silf);
#endif
Error e;
error_context(EC_READSILF);
const byte * p = silf;
if (e.test(!p, E_NOSILF) || e.test(silf.size() < 20, E_BADSIZE)) return error(e);
const uint32_t version = be::read<uint32_t>(p);
if (e.test(version < 0x00020000, E_TOOOLD)) return error(e);
if (version >= 0x00030000)
be::skip<uint32_t>(p); // compilerVersion
m_numSilf = be::read<uint16_t>(p);
be::skip<uint16_t>(p); // reserved
bool havePasses = false;
m_silfs = new Silf[m_numSilf];
if (e.test(!m_silfs, E_OUTOFMEM)) return error(e);
for (int i = 0; i < m_numSilf; i++)
{
error_context(EC_ASILF + (i << 8));
const uint32_t offset = be::read<uint32_t>(p),
next = i == m_numSilf - 1 ? uint32_t(silf.size()) : be::peek<uint32_t>(p);
if (e.test(next > silf.size() || offset >= next, E_BADSIZE))
return error(e);
if (!m_silfs[i].readGraphite(silf + offset, next - offset, *this, version))
return false;
if (m_silfs[i].numPasses())
havePasses = true;
}
return havePasses;
}
bool Face::readFeatures()
{
return m_Sill.readFace(*this);
}
bool Face::runGraphite(Segment & seg, const Silf *aSilf) const
{
#if !defined GRAPHITE2_NTRACING
json * dbgout = logger();
if (dbgout)
{
*dbgout << json::object
<< "id" << objectid(seg)
<< "passes" << json::array;
}
#endif
// if ((seg.dir() & 1) != aSilf->dir())
// seg.reverseSlots();
if ((seg.dir() & 3) == 3 && aSilf->bidiPass() == 0xFF)
seg.doMirror(aSilf->aMirror());
bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true);
if (res)
{
seg.associateChars(0, seg.charInfoCount());
if (aSilf->flags() & 0x20)
res &= seg.initCollisions();
if (res)
res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false);
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
{
seg.positionSlots(nullptr, seg.slots().begin(), seg.slots().end(), seg.currdir());
*dbgout << json::item
<< json::close // Close up the passes array
<< "outputdir" << (seg.currdir() ? "rtl" : "ltr")
<< "output" << json::array;
for(auto & s: seg.slots())
*dbgout << dslot(&seg, &s);
*dbgout << json::close
<< "advance" << seg.advance()
<< "chars" << json::array;
for(size_t i = 0, n = seg.charInfoCount(); i != n; ++i)
*dbgout << json::flat << *seg.charinfo(int(i));
*dbgout << json::close // Close up the chars array
<< json::close; // Close up the segment object
}
#endif
return res;
}
void Face::setLogger(FILE * log_file GR_MAYBE_UNUSED)
{
#if !defined GRAPHITE2_NTRACING
delete m_logger;
m_logger = log_file ? new json(log_file) : 0;
#endif
}
const Silf *Face::chooseSilf(uint32_t script) const
{
if (m_numSilf == 0)
return NULL;
else if (m_numSilf == 1 || script == 0)
return m_silfs;
else // do more work here
return m_silfs;
}
uint16_t Face::findPseudo(uint32_t uid) const
{
return (m_numSilf) ? m_silfs[0].findPseudo(uid) : 0;
}
int32_t Face::getGlyphMetric(uint16_t gid, uint8_t metric) const
{
switch (metrics(metric))
{
case kgmetAscent : return m_ascent;
case kgmetDescent : return m_descent;
default:
if (gid >= glyphs().numGlyphs()) return 0;
return glyphs().glyph(gid)->getMetric(metric);
}
}
void Face::takeFileFace(FileFace* pFileFace GR_MAYBE_UNUSED/*takes ownership*/)
{
#ifndef GRAPHITE2_NFILEFACE
if (m_pFileFace==pFileFace)
return;
delete m_pFileFace;
m_pFileFace = pFileFace;
#endif
}
NameTable * Face::nameTable() const
{
if (m_pNames) return m_pNames;
const Table name(*this, Tag::name);
if (name)
m_pNames = new NameTable(name, name.size());
return m_pNames;
}
uint16_t Face::languageForLocale(const char * locale) const
{
nameTable();
if (m_pNames)
return m_pNames->getLanguageId(locale);
return 0;
}
Face::Table::Table(const Face & face, const Tag n, uint32_t version) throw()
: _f(&face), _sz(0), _compressed(false)
{
_p = static_cast<const byte *>((*_f->m_ops.get_table)(_f->m_appFaceHandle, n, &_sz));
if (!TtfUtil::CheckTable(n, _p, _sz))
{
release(); // Make sure we release the table buffer even if the table failed its checks
return;
}
if (be::peek<uint32_t>(_p) >= version)
decompress();
}
void Face::Table::release()
{
if (_compressed)
free(const_cast<byte *>(_p));
else if (_p && _f->m_ops.release_table)
(*_f->m_ops.release_table)(_f->m_appFaceHandle, _p);
_p = 0; _sz = 0;
}
Face::Table & Face::Table::operator = (const Table && rhs) throw()
{
if (this == &rhs) return *this;
release();
new (this) Table(std::move(rhs));
return *this;
}
Error Face::Table::decompress()
{
Error e;
if (e.test(_sz < 5 * sizeof(uint32_t), E_BADSIZE))
return e;
byte * uncompressed_table = 0;
size_t uncompressed_size = 0;
const byte * p = _p;
const uint32_t version = be::read<uint32_t>(p); // Table version number.
// The scheme is in the top 5 bits of the 1st uint32_t.
const uint32_t hdr = be::read<uint32_t>(p);
switch(compression(hdr >> 27))
{
case NONE: return e;
case LZ4:
{
uncompressed_size = hdr & 0x07ffffff;
uncompressed_table = gralloc<byte>(uncompressed_size);
if (!e.test(!uncompressed_table || uncompressed_size < 4, E_OUTOFMEM))
{
memset(uncompressed_table, 0, 4); // make sure version number is initialised
// coverity[forward_null : FALSE] - uncompressed_table has been checked so can't be null
// coverity[checked_return : FALSE] - we test e later
e.test(lz4::decompress(p, _sz - 2*sizeof(uint32_t), uncompressed_table, uncompressed_size) != signed(uncompressed_size), E_SHRINKERFAILED);
}
break;
}
default:
e.error(E_BADSCHEME);
};
// Check the uncompressed version number against the original.
if (!e)
// coverity[forward_null : FALSE] - uncompressed_table has already been tested so can't be null
// coverity[checked_return : FALSE] - we test e later
e.test(be::peek<uint32_t>(uncompressed_table) != version, E_SHRINKERFAILED);
// Tell the provider to release the compressed form since were replacing
// it anyway.
release();
if (e)
{
free(uncompressed_table);
uncompressed_table = 0;
uncompressed_size = 0;
}
_p = uncompressed_table;
_sz = uncompressed_size;
_compressed = true;
return e;
}

View File

@ -0,0 +1,269 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <cstring>
#include "inc/Main.h"
#include "inc/bits.h"
#include "inc/Endian.h"
#include "inc/FeatureMap.h"
#include "inc/FeatureVal.h"
#include "graphite2/Font.h"
#include "inc/TtfUtil.h"
#include <cstdlib>
#include "inc/Face.h"
using namespace graphite2;
namespace
{
static int cmpNameAndFeatures(const void *ap, const void *bp)
{
const NameAndFeatureRef & a = *static_cast<const NameAndFeatureRef *>(ap),
& b = *static_cast<const NameAndFeatureRef *>(bp);
return (a < b ? -1 : (b < a ? 1 : 0));
}
const size_t FEAT_HEADER = sizeof(uint32_t) + 2*sizeof(uint16_t) + sizeof(uint32_t),
FEATURE_SIZE = sizeof(uint32_t)
+ 2*sizeof(uint16_t)
+ sizeof(uint32_t)
+ 2*sizeof(uint16_t),
FEATURE_SETTING_SIZE = sizeof(int16_t) + sizeof(uint16_t);
uint16_t readFeatureSettings(const byte * p, FeatureSetting * s, size_t num_settings)
{
uint16_t max_val = 0;
for (FeatureSetting * const end = s + num_settings; s != end; ++s)
{
const int16_t value = be::read<int16_t>(p);
::new (s) FeatureSetting(value, be::read<uint16_t>(p));
if (uint16_t(value) > max_val) max_val = value;
}
return max_val;
}
}
FeatureRef::FeatureRef(const Face & face,
unsigned short & bits_offset, uint32_t max_val,
uint32_t name, uint16_t uiName, uint16_t flags,
FeatureSetting *settings, uint16_t num_set) throw()
: m_face(&face),
m_nameValues(settings),
m_mask(mask_over_val(max_val)),
m_max(max_val),
m_id(name),
m_nameid(uiName),
m_flags(flags),
m_numSet(num_set)
{
const uint8_t need_bits = bit_set_count(m_mask);
m_index = (bits_offset + need_bits) / SIZEOF_CHUNK;
if (m_index > bits_offset / SIZEOF_CHUNK)
bits_offset = m_index*SIZEOF_CHUNK;
m_bits = bits_offset % SIZEOF_CHUNK;
bits_offset += need_bits;
m_mask <<= m_bits;
}
FeatureRef::~FeatureRef() throw()
{
free(m_nameValues);
}
bool FeatureMap::readFeats(const Face & face)
{
const Face::Table feat(face, TtfUtil::Tag::Feat);
const byte * p = feat;
if (!p) return true;
if (feat.size() < FEAT_HEADER) return false;
const byte *const feat_start = p,
*const feat_end = p + feat.size();
const uint32_t version = be::read<uint32_t>(p);
m_numFeats = be::read<uint16_t>(p);
be::skip<uint16_t>(p);
be::skip<uint32_t>(p);
// Sanity checks
if (m_numFeats == 0) return true;
if (version < 0x00010000 ||
p + m_numFeats*FEATURE_SIZE > feat_end)
{ //defensive
m_numFeats = 0;
return false;
}
m_feats = new FeatureRef [m_numFeats];
uint16_t * const defVals = gralloc<uint16_t>(m_numFeats);
if (!defVals || !m_feats) return false;
unsigned short bits = 0; //to cause overflow on first Feature
for (int i = 0, ie = m_numFeats; i != ie; i++)
{
const uint32_t label = version < 0x00020000 ? be::read<uint16_t>(p) : be::read<uint32_t>(p);
const uint16_t num_settings = be::read<uint16_t>(p);
if (version >= 0x00020000)
be::skip<uint16_t>(p);
const uint32_t settings_offset = be::read<uint32_t>(p);
const uint16_t flags = be::read<uint16_t>(p),
uiName = be::read<uint16_t>(p);
if (settings_offset > size_t(feat_end - feat_start)
|| settings_offset + num_settings * FEATURE_SETTING_SIZE > size_t(feat_end - feat_start))
{
free(defVals);
return false;
}
FeatureSetting *uiSet;
uint32_t maxVal;
if (num_settings != 0)
{
uiSet = gralloc<FeatureSetting>(num_settings);
if (!uiSet)
{
free(defVals);
return false;
}
maxVal = readFeatureSettings(feat_start + settings_offset, uiSet, num_settings);
defVals[i] = uiSet[0].value();
}
else
{
uiSet = 0;
maxVal = 0xffffffff;
defVals[i] = 0;
}
::new (m_feats + i) FeatureRef (face, bits, maxVal,
label, uiName, flags,
uiSet, num_settings);
}
new (&m_defaultFeatures) Features(bits/(sizeof(uint32_t)*8) + 1, *this);
m_pNamedFeats = new NameAndFeatureRef[m_numFeats];
if (!m_pNamedFeats)
{
free(defVals);
return false;
}
for (int i = 0; i < m_numFeats; ++i)
{
m_feats[i].applyValToFeature(defVals[i], m_defaultFeatures);
m_pNamedFeats[i] = m_feats[i];
}
free(defVals);
qsort(m_pNamedFeats, m_numFeats, sizeof(NameAndFeatureRef), &cmpNameAndFeatures);
return true;
}
bool SillMap::readFace(const Face & face)
{
if (!m_FeatureMap.readFeats(face)) return false;
if (!readSill(face)) return false;
return true;
}
bool SillMap::readSill(const Face & face)
{
const Face::Table sill(face, TtfUtil::Tag::Sill);
const byte *p = sill;
if (!p) return true;
if (sill.size() < 12) return false;
if (be::read<uint32_t>(p) != 0x00010000UL) return false;
m_numLanguages = be::read<uint16_t>(p);
m_langFeats = new LangFeaturePair[m_numLanguages];
if (!m_langFeats || !m_FeatureMap.m_numFeats) { m_numLanguages = 0; return true; } //defensive
p += 6; // skip the fast search
if (sill.size() < m_numLanguages * 8U + 12) return false;
for (int i = 0; i < m_numLanguages; i++)
{
uint32_t langid = be::read<uint32_t>(p);
uint16_t numSettings = be::read<uint16_t>(p);
uint16_t offset = be::read<uint16_t>(p);
if (offset + 8U * numSettings > sill.size() && numSettings > 0) return false;
Features* feats = new Features(m_FeatureMap.m_defaultFeatures);
if (!feats) return false;
const byte *pLSet = sill + offset;
// Apply langauge specific settings
for (int j = 0; j < numSettings; j++)
{
uint32_t name = be::read<uint32_t>(pLSet);
uint16_t val = be::read<uint16_t>(pLSet);
pLSet += 2;
const FeatureRef* pRef = m_FeatureMap.findFeatureRef(name);
if (pRef) pRef->applyValToFeature(val, *feats);
}
// Add the language id feature which is always feature id 1
const FeatureRef* pRef = m_FeatureMap.findFeatureRef(1);
if (pRef) pRef->applyValToFeature(langid, *feats);
m_langFeats[i].m_lang = langid;
m_langFeats[i].m_pFeatures = feats;
}
return true;
}
Features* SillMap::cloneFeatures(uint32_t langname/*0 means default*/) const
{
if (langname)
{
// the number of languages in a font is usually small e.g. 8 in Doulos
// so this loop is not very expensive
for (uint16_t i = 0; i < m_numLanguages; i++)
{
if (m_langFeats[i].m_lang == langname)
return new Features(*m_langFeats[i].m_pFeatures);
}
}
return new Features (m_FeatureMap.m_defaultFeatures);
}
const FeatureRef *FeatureMap::findFeatureRef(uint32_t name) const
{
NameAndFeatureRef *it;
for (it = m_pNamedFeats; it < m_pNamedFeats + m_numFeats; ++it)
if (it->m_name == name)
return it->m_pFRef;
return NULL;
}
bool FeatureRef::applyValToFeature(uint32_t val, Features & pDest) const
{
if (val>maxVal() || !m_face)
return false;
if (pDest.m_pMap==NULL)
pDest.m_pMap = &m_face->theSill().theFeatureMap();
else
if (pDest.m_pMap!=&m_face->theSill().theFeatureMap())
return false; //incompatible
if (m_index >= pDest.size())
pDest.resize(m_index+1);
pDest[m_index] &= ~m_mask;
pDest[m_index] |= (uint32_t(val) << m_bits);
return true;
}
uint32_t FeatureRef::getFeatureVal(const Features& feats) const
{
if (m_index < feats.size() && m_face
&& &m_face->theSill().theFeatureMap()==feats.m_pMap)
return (feats[m_index] & m_mask) >> m_bits;
else
return 0;
}

View File

@ -0,0 +1,92 @@
// SPDX-License-Identifier: MIT
// Copyright 2012, SIL International, All rights reserved.
#include <cstring>
#include "inc/FileFace.h"
#ifndef GRAPHITE2_NFILEFACE
using namespace graphite2;
FileFace::FileFace(const char *filename)
: _file(fopen(filename, "rb")),
_file_len(0),
_header_tbl(NULL),
_table_dir(NULL)
{
if (!_file) return;
if (fseek(_file, 0, SEEK_END)) return;
_file_len = ftell(_file);
if (fseek(_file, 0, SEEK_SET)) return;
size_t tbl_offset, tbl_len;
// Get the header.
if (!TtfUtil::GetHeaderInfo(tbl_offset, tbl_len)) return;
if (fseek(_file, long(tbl_offset), SEEK_SET)) return;
_header_tbl = (TtfUtil::Sfnt::OffsetSubTable*)gralloc<char>(tbl_len);
if (_header_tbl)
{
if (fread(_header_tbl, 1, tbl_len, _file) != tbl_len) return;
if (!TtfUtil::CheckHeader(_header_tbl)) return;
}
// Get the table directory
if (!TtfUtil::GetTableDirInfo(_header_tbl, tbl_offset, tbl_len)) return;
_table_dir = (TtfUtil::Sfnt::OffsetSubTable::Entry*)gralloc<char>(tbl_len);
if (fseek(_file, long(tbl_offset), SEEK_SET)) return;
if (_table_dir && fread(_table_dir, 1, tbl_len, _file) != tbl_len)
{
free(_table_dir);
_table_dir = NULL;
}
return;
}
FileFace::~FileFace()
{
free(_table_dir);
free(_header_tbl);
if (_file)
fclose(_file);
}
const void *FileFace::get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len)
{
if (appFaceHandle == 0) return 0;
const FileFace & file_face = *static_cast<const FileFace *>(appFaceHandle);
void *tbl;
size_t tbl_offset, tbl_len;
if (!TtfUtil::GetTableInfo(name, file_face._header_tbl, file_face._table_dir, tbl_offset, tbl_len))
return 0;
if (tbl_offset > file_face._file_len || tbl_len > file_face._file_len - tbl_offset
|| fseek(file_face._file, long(tbl_offset), SEEK_SET) != 0)
return 0;
tbl = malloc(tbl_len);
if (!tbl || fread(tbl, 1, tbl_len, file_face._file) != tbl_len)
{
free(tbl);
return 0;
}
if (len) *len = tbl_len;
return tbl;
}
void FileFace::rel_table_fn(const void* appFaceHandle, const void *table_buffer)
{
if (appFaceHandle == 0) return;
free(const_cast<void *>(table_buffer));
}
const gr_face_ops FileFace::ops = { sizeof FileFace::ops, &FileFace::get_table_fn, &FileFace::rel_table_fn };
#endif //!GRAPHITE2_NFILEFACE

View File

@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Face.h"
#include "inc/Font.h"
#include "inc/GlyphCache.h"
using namespace graphite2;
Font::Font(float ppm, const Face & f, const void * appFontHandle, const gr_font_ops * ops)
: m_appFontHandle(appFontHandle ? appFontHandle : this),
m_face(f),
m_scale(ppm / f.glyphs().unitsPerEm()),
m_hinted(appFontHandle && ops && (ops->glyph_advance_x || ops->glyph_advance_y))
{
memset(&m_ops, 0, sizeof m_ops);
if (m_hinted && ops)
memcpy(&m_ops, ops, min(sizeof m_ops, ops->size));
else
m_ops.glyph_advance_x = &Face::default_glyph_advance;
size_t nGlyphs = f.glyphs().numGlyphs();
m_advances = gralloc<float>(nGlyphs);
if (m_advances)
{
for (float *advp = m_advances; nGlyphs; --nGlyphs, ++advp)
*advp = INVALID_ADVANCE;
}
}
/*virtual*/ Font::~Font()
{
free(m_advances);
}

View File

@ -0,0 +1,469 @@
// SPDX-License-Identifier: MIT
// Copyright 2012, SIL International, All rights reserved.
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Face.h" //for the tags
#include "inc/GlyphCache.h"
#include "inc/GlyphFace.h"
#include "inc/Endian.h"
#include "inc/bits.h"
using namespace graphite2;
namespace
{
// Iterator over version 1 or 2 glat entries which consist of a series of
// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+
// v1 |k|n|v1 |v2 |...|vN | or v2 | k | n |v1 |v2 |...|vN |
// +-+-+-+-+-+-+-+-+-+-+ +-+-+-+-+-+-+-+-+-+-+-+-+
// variable length structures.
template<typename W>
class _glat_iterator : public std::iterator<std::input_iterator_tag, std::pair<sparse::key_type, sparse::mapped_type> >
{
unsigned short key() const { return uint16_t(be::peek<W>(_e) + _n); }
unsigned int run() const { return be::peek<W>(_e+sizeof(W)); }
void advance_entry() { _n = 0; _e = _v; be::skip<W>(_v,2); }
public:
_glat_iterator(const void * glat=0) : _e(reinterpret_cast<const byte *>(glat)), _v(_e+2*sizeof(W)), _n(0) {}
_glat_iterator<W> & operator ++ () {
++_n; be::skip<uint16_t>(_v);
if (_n == run()) advance_entry();
return *this;
}
_glat_iterator<W> operator ++ (int) { _glat_iterator<W> tmp(*this); operator++(); return tmp; }
// This is strictly a >= operator. A true == operator could be
// implemented that test for overlap but it would be more expensive a
// test.
bool operator == (const _glat_iterator<W> & rhs) { return _v >= rhs._e - 1; }
bool operator != (const _glat_iterator<W> & rhs) { return !operator==(rhs); }
value_type operator * () const {
return value_type(key(), be::peek<uint16_t>(_v));
}
protected:
const byte * _e, * _v;
size_t _n;
};
typedef _glat_iterator<uint8_t> glat_iterator;
typedef _glat_iterator<uint16_t> glat2_iterator;
}
const SlantBox SlantBox::empty = {0,0,0,0};
class GlyphCache::Loader
{
public:
Loader(const Face & face); //return result indicates success. Do not use if failed.
operator bool () const throw();
unsigned short int units_per_em() const throw();
unsigned short int num_glyphs() const throw();
unsigned short int num_attrs() const throw();
bool has_boxes() const throw();
const GlyphFace * read_glyph(unsigned short gid, GlyphFace &, int *numsubs) const throw();
GlyphBox * read_box(uint16_t gid, GlyphBox *curr, const GlyphFace & face) const throw();
CLASS_NEW_DELETE;
private:
Face::Table _head,
_hhea,
_hmtx,
_glyf,
_loca,
m_pGlat,
m_pGloc;
bool _long_fmt;
bool _has_boxes;
unsigned short _num_glyphs_graphics, //i.e. boundary box and advance
_num_glyphs_attributes,
_num_attrs; // number of glyph attributes per glyph
};
GlyphCache::GlyphCache(const Face & face, const uint32_t face_options)
: _glyph_loader(new Loader(face)),
_glyphs(_glyph_loader && *_glyph_loader && _glyph_loader->num_glyphs()
? grzeroalloc<const GlyphFace *>(_glyph_loader->num_glyphs()) : 0),
_boxes(_glyph_loader && _glyph_loader->has_boxes() && _glyph_loader->num_glyphs()
? grzeroalloc<GlyphBox *>(_glyph_loader->num_glyphs()) : 0),
_num_glyphs(_glyphs ? _glyph_loader->num_glyphs() : 0),
_num_attrs(_glyphs ? _glyph_loader->num_attrs() : 0),
_upem(_glyphs ? _glyph_loader->units_per_em() : 0)
{
if ((face_options & gr_face_preloadGlyphs) && _glyph_loader && _glyphs)
{
int numsubs = 0;
GlyphFace * const glyphs = new GlyphFace [_num_glyphs];
if (!glyphs)
return;
// The 0 glyph is definately required.
_glyphs[0] = _glyph_loader->read_glyph(0, glyphs[0], &numsubs);
// glyphs[0] has the same address as the glyphs array just allocated,
// thus assigning the &glyphs[0] to _glyphs[0] means _glyphs[0] points
// to the entire array.
const GlyphFace * loaded = _glyphs[0];
for (uint16_t gid = 1; loaded && gid != _num_glyphs; ++gid)
_glyphs[gid] = loaded = _glyph_loader->read_glyph(gid, glyphs[gid], &numsubs);
if (!loaded)
{
_glyphs[0] = 0;
delete [] glyphs;
}
else if (numsubs > 0 && _boxes)
{
GlyphBox * boxes = (GlyphBox *)gralloc<char>(_num_glyphs * sizeof(GlyphBox) + numsubs * 8 * sizeof(float));
GlyphBox * currbox = boxes;
for (uint16_t gid = 0; currbox && gid != _num_glyphs; ++gid)
{
_boxes[gid] = currbox;
currbox = _glyph_loader->read_box(gid, currbox, *_glyphs[gid]);
}
if (!currbox)
{
free(boxes);
_boxes[0] = 0;
}
}
delete _glyph_loader;
_glyph_loader = 0;
// coverity[leaked_storage : FALSE] - calling read_glyph on index 0 saved
// glyphs as _glyphs[0]. Setting _glyph_loader to nullptr here flags that
// the dtor needs to call delete[] on _glyphs[0] to release what was allocated
// as glyphs
}
if (_glyphs && glyph(0) == 0)
{
free(_glyphs);
_glyphs = 0;
if (_boxes)
{
free(_boxes);
_boxes = 0;
}
_num_glyphs = _num_attrs = _upem = 0;
}
}
GlyphCache::~GlyphCache()
{
if (_glyphs)
{
if (_glyph_loader)
{
const GlyphFace * * g = _glyphs;
for(unsigned short n = _num_glyphs; n; --n, ++g)
delete *g;
}
else
delete [] _glyphs[0];
free(_glyphs);
}
if (_boxes)
{
if (_glyph_loader)
{
GlyphBox * * g = _boxes;
for (uint16_t n = _num_glyphs; n; --n, ++g)
free(*g);
}
else
free(_boxes[0]);
free(_boxes);
}
delete _glyph_loader;
}
const GlyphFace *GlyphCache::glyph(unsigned short glyphid) const //result may be changed by subsequent call with a different glyphid
{
if (glyphid >= numGlyphs())
return _glyphs[0];
const GlyphFace * & p = _glyphs[glyphid];
if (p == 0 && _glyph_loader)
{
int numsubs = 0;
GlyphFace * g = new GlyphFace();
if (g) p = _glyph_loader->read_glyph(glyphid, *g, &numsubs);
if (!p)
{
delete g;
return *_glyphs;
}
if (_boxes)
{
_boxes[glyphid] = (GlyphBox *)gralloc<char>(sizeof(GlyphBox) + 8 * numsubs * sizeof(float));
if (!_glyph_loader->read_box(glyphid, _boxes[glyphid], *_glyphs[glyphid]))
{
free(_boxes[glyphid]);
_boxes[glyphid] = 0;
}
}
}
return p;
}
GlyphCache::Loader::Loader(const Face & face)
: _head(face, Tag::head),
_hhea(face, Tag::hhea),
_hmtx(face, Tag::hmtx),
_glyf(face, Tag::glyf),
_loca(face, Tag::loca),
_long_fmt(false),
_has_boxes(false),
_num_glyphs_graphics(0),
_num_glyphs_attributes(0),
_num_attrs(0)
{
if (!operator bool())
return;
const Face::Table maxp = Face::Table(face, Tag::maxp);
if (!maxp) { _head = Face::Table(); return; }
_num_glyphs_graphics = static_cast<unsigned short>(TtfUtil::GlyphCount(maxp));
// This will fail if the number of glyphs is wildly out of range.
if (_glyf && TtfUtil::LocaLookup(_num_glyphs_graphics-1, _loca, _loca.size(), _head) == size_t(-2))
{
_head = Face::Table();
return;
}
if ((m_pGlat = Face::Table(face, Tag::Glat, 0x00030000)) == NULL
|| (m_pGloc = Face::Table(face, Tag::Gloc)) == NULL
|| m_pGloc.size() < 8)
{
_head = Face::Table();
return;
}
const byte * p = m_pGloc;
int version = be::read<uint32_t>(p);
const uint16_t flags = be::read<uint16_t>(p);
_num_attrs = be::read<uint16_t>(p);
// We can accurately calculate the number of attributed glyphs by
// subtracting the length of the attribids array (numAttribs long if present)
// and dividing by either 2 or 4 depending on shor or lonf format
_long_fmt = flags & 1;
ptrdiff_t tmpnumgattrs = (m_pGloc.size()
- (p - m_pGloc)
- sizeof(uint16_t)*(flags & 0x2 ? _num_attrs : 0))
/ (_long_fmt ? sizeof(uint32_t) : sizeof(uint16_t)) - 1;
if (version >= 0x00020000 || tmpnumgattrs < 0 || tmpnumgattrs > 65535
|| _num_attrs == 0 || _num_attrs > 0x3000 // is this hard limit appropriate?
|| _num_glyphs_graphics > tmpnumgattrs
|| m_pGlat.size() < 4)
{
_head = Face::Table();
return;
}
_num_glyphs_attributes = static_cast<unsigned short>(tmpnumgattrs);
p = m_pGlat;
version = be::read<uint32_t>(p);
if (version >= 0x00040000 || (version >= 0x00030000 && m_pGlat.size() < 8)) // reject Glat tables that are too new
{
_head = Face::Table();
return;
}
else if (version >= 0x00030000)
{
unsigned int glatflags = be::read<uint32_t>(p);
_has_boxes = glatflags & 1;
// delete this once the compiler is fixed
_has_boxes = true;
}
}
inline
GlyphCache::Loader::operator bool () const throw()
{
return _head && _hhea && _hmtx && !(bool(_glyf) != bool(_loca));
}
inline
unsigned short int GlyphCache::Loader::units_per_em() const throw()
{
return _head ? TtfUtil::DesignUnits(_head) : 0;
}
inline
unsigned short int GlyphCache::Loader::num_glyphs() const throw()
{
return max(_num_glyphs_graphics, _num_glyphs_attributes);
}
inline
unsigned short int GlyphCache::Loader::num_attrs() const throw()
{
return _num_attrs;
}
inline
bool GlyphCache::Loader::has_boxes () const throw()
{
return _has_boxes;
}
const GlyphFace * GlyphCache::Loader::read_glyph(unsigned short glyphid, GlyphFace & glyph, int *numsubs) const throw()
{
Rect bbox;
Position advance;
if (glyphid < _num_glyphs_graphics)
{
int nLsb;
unsigned int nAdvWid;
if (_glyf)
{
int xMin, yMin, xMax, yMax;
size_t locidx = TtfUtil::LocaLookup(glyphid, _loca, _loca.size(), _head);
void *pGlyph = TtfUtil::GlyfLookup(_glyf, locidx, _glyf.size());
if (pGlyph && TtfUtil::GlyfBox(pGlyph, xMin, yMin, xMax, yMax))
{
if ((xMin > xMax) || (yMin > yMax))
return 0;
bbox = Rect(Position(static_cast<float>(xMin), static_cast<float>(yMin)),
Position(static_cast<float>(xMax), static_cast<float>(yMax)));
}
}
if (TtfUtil::HorMetrics(glyphid, _hmtx, _hmtx.size(), _hhea, nLsb, nAdvWid))
advance = Position(static_cast<float>(nAdvWid), 0);
}
if (glyphid < _num_glyphs_attributes)
{
const byte * gloc = m_pGloc;
size_t glocs = 0, gloce = 0;
be::skip<uint32_t>(gloc);
be::skip<uint16_t>(gloc,2);
if (_long_fmt)
{
if (8 + glyphid * sizeof(uint32_t) > m_pGloc.size())
return 0;
be::skip<uint32_t>(gloc, glyphid);
glocs = be::read<uint32_t>(gloc);
gloce = be::peek<uint32_t>(gloc);
}
else
{
if (8 + glyphid * sizeof(uint16_t) > m_pGloc.size())
return 0;
be::skip<uint16_t>(gloc, glyphid);
glocs = be::read<uint16_t>(gloc);
gloce = be::peek<uint16_t>(gloc);
}
if (glocs >= m_pGlat.size() - 1 || gloce > m_pGlat.size())
return 0;
const uint32_t glat_version = be::peek<uint32_t>(m_pGlat);
if (glat_version >= 0x00030000)
{
if (glocs >= gloce)
return 0;
const byte * p = m_pGlat + glocs;
uint16_t bmap = be::read<uint16_t>(p);
int num = bit_set_count((uint32_t)bmap);
if (numsubs) *numsubs += num;
glocs += 6 + 8 * num;
if (glocs > gloce)
return 0;
}
if (glat_version < 0x00020000)
{
if (gloce - glocs < 2*sizeof(byte)+sizeof(uint16_t)
|| gloce - glocs > _num_attrs*(2*sizeof(byte)+sizeof(uint16_t)))
return 0;
new (&glyph) GlyphFace(bbox, advance, glat_iterator(m_pGlat + glocs), glat_iterator(m_pGlat + gloce));
}
else
{
if (gloce - glocs < 3*sizeof(uint16_t) // can a glyph have no attributes? why not?
|| gloce - glocs > _num_attrs*3*sizeof(uint16_t)
|| glocs > m_pGlat.size() - 2*sizeof(uint16_t))
return 0;
new (&glyph) GlyphFace(bbox, advance, glat2_iterator(m_pGlat + glocs), glat2_iterator(m_pGlat + gloce));
}
if (!glyph.attrs() || glyph.attrs().capacity() > _num_attrs)
return 0;
}
return &glyph;
}
inline float scale_to(uint8_t t, float zmin, float zmax)
{
return (zmin + t * (zmax - zmin) / 255);
}
Rect readbox(Rect &b, uint8_t zxmin, uint8_t zymin, uint8_t zxmax, uint8_t zymax)
{
return Rect(Position(scale_to(zxmin, b.bl.x, b.tr.x), scale_to(zymin, b.bl.y, b.tr.y)),
Position(scale_to(zxmax, b.bl.x, b.tr.x), scale_to(zymax, b.bl.y, b.tr.y)));
}
GlyphBox * GlyphCache::Loader::read_box(uint16_t gid, GlyphBox *curr, const GlyphFace & glyph) const throw()
{
if (gid >= _num_glyphs_attributes) return 0;
const byte * gloc = m_pGloc;
size_t glocs = 0, gloce = 0;
be::skip<uint32_t>(gloc);
be::skip<uint16_t>(gloc,2);
if (_long_fmt)
{
be::skip<uint32_t>(gloc, gid);
glocs = be::read<uint32_t>(gloc);
gloce = be::peek<uint32_t>(gloc);
}
else
{
be::skip<uint16_t>(gloc, gid);
glocs = be::read<uint16_t>(gloc);
gloce = be::peek<uint16_t>(gloc);
}
if (gloce > m_pGlat.size() || glocs + 6 >= gloce)
return 0;
const byte * p = m_pGlat + glocs;
uint16_t bmap = be::read<uint16_t>(p);
int num = bit_set_count((uint32_t)bmap);
Rect bbox = glyph.theBBox();
Rect diamax(Position(bbox.bl.x + bbox.bl.y, bbox.bl.x - bbox.tr.y),
Position(bbox.tr.x + bbox.tr.y, bbox.tr.x - bbox.bl.y));
Rect diabound = readbox(diamax, p[0], p[2], p[1], p[3]);
::new (curr) GlyphBox(num, bmap, &diabound);
be::skip<uint8_t>(p, 4);
if (glocs + 6 + num * 8 >= gloce)
return 0;
for (int i = 0; i < num * 2; ++i)
{
Rect box = readbox((i & 1) ? diamax : bbox, p[0], p[2], p[1], p[3]);
curr->addSubBox(i >> 1, i & 1, &box);
be::skip<uint8_t>(p, 4);
}
return (GlyphBox *)((char *)(curr) + sizeof(GlyphBox) + 2 * num * sizeof(Rect));
}

View File

@ -0,0 +1,25 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/GlyphFace.h"
using namespace graphite2;
int32_t GlyphFace::getMetric(uint8_t metric) const
{
switch (metrics(metric))
{
case kgmetLsb : return int32_t(m_bbox.bl.x);
case kgmetRsb : return int32_t(m_advance.x - m_bbox.tr.x);
case kgmetBbTop : return int32_t(m_bbox.tr.y);
case kgmetBbBottom : return int32_t(m_bbox.bl.y);
case kgmetBbLeft : return int32_t(m_bbox.bl.x);
case kgmetBbRight : return int32_t(m_bbox.tr.x);
case kgmetBbHeight : return int32_t(m_bbox.tr.y - m_bbox.bl.y);
case kgmetBbWidth : return int32_t(m_bbox.tr.x - m_bbox.bl.x);
case kgmetAdvWidth : return int32_t(m_advance.x);
case kgmetAdvHeight : return int32_t(m_advance.y);
default : return 0;
}
}

View File

@ -0,0 +1,276 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <algorithm>
#include <cmath>
#include <limits>
#include "inc/Main.h"
#include "inc/Intervals.h"
#include "inc/Segment.h"
#include "inc/Slot.h"
#include "inc/debug.h"
#include "inc/bits.h"
using namespace graphite2;
#include <cmath>
inline
Zones::Exclusion Zones::Exclusion::split_at(float p) {
Exclusion r(*this);
r.xm = x = p;
return r;
}
inline
void Zones::Exclusion::left_trim(float p) {
x = p;
}
inline
Zones::Exclusion & Zones::Exclusion::operator += (Exclusion const & rhs) {
c += rhs.c; sm += rhs.sm; smx += rhs.smx; open = false;
return *this;
}
inline
uint8_t Zones::Exclusion::outcode(float val) const {
float p = val;
//float d = std::numeric_limits<float>::epsilon();
float d = 0.;
return ((p - xm >= d) << 1) | (x - p > d);
}
void Zones::exclude_with_margins(float xmin, float xmax, int axis) {
remove(xmin, xmax);
weightedAxis(axis, xmin-_margin_len, xmin, 0, 0, _margin_weight, xmin-_margin_len, 0, 0, false);
weightedAxis(axis, xmax, xmax+_margin_len, 0, 0, _margin_weight, xmax+_margin_len, 0, 0, false);
}
namespace
{
inline
bool separated(float a, float b) {
return a != b;
//int exp;
//float res = frexpf(fabs(a - b), &exp);
//return (*(unsigned int *)(&res) > 4);
//return std::fabs(a-b) > std::numeric_limits<float>::epsilon(); // std::epsilon may not work. but 0.5 fails exising 64 bit tests
//return std::fabs(a-b) > 0.5f;
}
}
void Zones::insert(Exclusion e)
{
#if !defined GRAPHITE2_NTRACING
addDebug(&e);
#endif
e.x = max(e.x, _pos);
e.xm = min(e.xm, _posm);
if (e.x >= e.xm) return;
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie && e.x < e.xm; ++i)
{
const uint8_t oca = e.outcode(i->x),
ocb = e.outcode(i->xm);
if ((oca & ocb) != 0) continue;
switch (oca ^ ocb) // What kind of overlap?
{
case 0: // e completely covers i
// split e at i.x into e1,e2
// split e2 at i.mx into e2,e3
// drop e1 ,i+e2, e=e3
*i += e;
e.left_trim(i->xm);
break;
case 1: // e overlaps on the rhs of i
// split i at e->x into i1,i2
// split e at i.mx into e1,e2
// trim i1, insert i2+e1, e=e2
if (!separated(i->xm, e.x)) break;
if (separated(i->x,e.x)) { i = _exclusions.insert(i,i->split_at(e.x)); ++i; }
*i += e;
e.left_trim(i->xm);
break;
case 2: // e overlaps on the lhs of i
// split e at i->x into e1,e2
// split i at e.mx into i1,i2
// drop e1, insert e2+i1, trim i2
if (!separated(e.xm, i->x)) return;
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
*i += e;
return;
case 3: // i completely covers e
// split i at e.x into i1,i2
// split i2 at e.mx into i2,i3
// insert i1, insert e+i2
if (separated(e.xm, i->xm)) i = _exclusions.insert(i,i->split_at(e.xm));
i = _exclusions.insert(i, i->split_at(e.x));
*++i += e;
return;
}
ie = _exclusions.end();
}
}
void Zones::remove(float x, float xm)
{
#if !defined GRAPHITE2_NTRACING
removeDebug(x, xm);
#endif
x = max(x, _pos);
xm = min(xm, _posm);
if (x >= xm) return;
for (iterator i = _exclusions.begin(), ie = _exclusions.end(); i != ie; ++i)
{
const uint8_t oca = i->outcode(x),
ocb = i->outcode(xm);
if ((oca & ocb) != 0) continue;
switch (oca ^ ocb) // What kind of overlap?
{
case 0: // i completely covers e
if (separated(i->x, x)) { i = _exclusions.insert(i,i->split_at(x)); ++i; }
GR_FALLTHROUGH;
// no break
case 1: // i overlaps on the rhs of e
i->left_trim(xm);
return;
case 2: // i overlaps on the lhs of e
i->xm = x;
if (separated(i->x, i->xm)) break;
GR_FALLTHROUGH;
// no break
case 3: // e completely covers i
i = _exclusions.erase(i);
--i;
break;
}
ie = _exclusions.end();
}
}
Zones::const_iterator Zones::find_exclusion_under(float x) const
{
size_t l = 0, h = _exclusions.size();
while (l < h)
{
size_t const p = (l+h) >> 1;
switch (_exclusions[p].outcode(x))
{
case 0 : return _exclusions.begin()+p;
case 1 : h = p; break;
case 2 :
case 3 : l = p+1; break;
}
}
return _exclusions.begin()+l;
}
float Zones::closest(float origin, float & cost) const
{
float best_c = std::numeric_limits<float>::max(),
best_x = 0;
const const_iterator start = find_exclusion_under(origin);
// Forward scan looking for lowest cost
for (const_iterator i = start, ie = _exclusions.end(); i != ie; ++i)
if (i->track_cost(best_c, best_x, origin)) break;
// Backward scan looking for lowest cost
// We start from the exclusion to the immediate left of start since we've
// already tested start with the right most scan above.
for (const_iterator i = start-1, ie = _exclusions.begin()-1; i != ie; --i)
if (i->track_cost(best_c, best_x, origin)) break;
cost = (best_c == std::numeric_limits<float>::max() ? -1 : best_c);
return best_x;
}
// Cost and test position functions
bool Zones::Exclusion::track_cost(float & best_cost, float & best_pos, float origin) const {
const float p = test_position(origin),
localc = cost(p - origin);
if (open && localc > best_cost) return true;
if (localc < best_cost)
{
best_cost = localc;
best_pos = p;
}
return false;
}
inline
float Zones::Exclusion::cost(float p) const {
return (sm * p - 2 * smx) * p + c;
}
float Zones::Exclusion::test_position(float origin) const {
if (sm < 0)
{
// sigh, test both ends and perhaps the middle too!
float res = x;
float cl = cost(x);
if (x < origin && xm > origin)
{
float co = cost(origin);
if (co < cl)
{
cl = co;
res = origin;
}
}
float cr = cost(xm);
return cl > cr ? xm : res;
}
else
{
float zerox = smx / sm + origin;
if (zerox < x) return x;
else if (zerox > xm) return xm;
else return zerox;
}
}
#if !defined GRAPHITE2_NTRACING
void Zones::jsonDbgOut(Segment &) const {
if (_dbg)
{
for (Zones::idebugs s = dbgs_begin(), e = dbgs_end(); s != e; ++s)
{
*_dbg << json::flat << json::array
<< objectid(SlotBuffer::const_iterator::from(static_cast<Slot *>(s->_env[0])))
<< reinterpret_cast<ptrdiff_t>(s->_env[1]);
if (s->_isdel)
*_dbg << "remove" << Position(s->_excl.x, s->_excl.xm);
else
*_dbg << "exclude" << json::flat << json::array
<< s->_excl.x << s->_excl.xm
<< s->_excl.sm << s->_excl.smx << s->_excl.c
<< json::close;
*_dbg << json::close;
}
}
}
#endif

View File

@ -0,0 +1,270 @@
/* GRAPHITE2 LICENSING
Copyright 2012, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include "inc/Segment.h"
#include "graphite2/Font.h"
#include "inc/debug.h"
#include "inc/CharInfo.h"
#include "inc/Font.h"
#include "inc/Slot.h"
#include "inc/Main.h"
#include <cmath>
using namespace graphite2;
template<typename Callable>
typename std::result_of<Callable()>::type
Segment::subsegment(SlotBuffer::const_iterator first, SlotBuffer::const_iterator last, Callable body)
{
// SlotBuffer sub_slots/*(m_srope.num_user_attrs(), m_srope.num_just_levels())*/;
// sub_slots.splice(sub_slots.end(), m_srope, first, last);
// m_srope.swap(sub_slots);
auto retval = body();
// m_srope.swap(sub_slots);
// m_srope.splice(last, sub_slots);
return retval;
}
class JustifyTotal {
public:
JustifyTotal() : m_numGlyphs(0), m_tStretch(0), m_tShrink(0), m_tStep(0), m_tWeight(0) {}
void accumulate(Slot const &s, Segment const &seg, int level);
int weight() const { return m_tWeight; }
CLASS_NEW_DELETE
private:
int m_numGlyphs;
int m_tStretch;
int m_tShrink;
int m_tStep;
int m_tWeight;
};
void JustifyTotal::accumulate(Slot const &s, Segment const &seg, int level)
{
++m_numGlyphs;
m_tStretch += s.getJustify(seg, level, 0);
m_tShrink += s.getJustify(seg, level, 1);
m_tStep += s.getJustify(seg, level, 2);
m_tWeight += s.getJustify(seg, level, 3);
}
float Segment::justify(SlotBuffer::iterator pSlot, const Font *font, float width,
GR_MAYBE_UNUSED justFlags jflags,
SlotBuffer::iterator pFirst,
SlotBuffer::iterator pLast)
{
float currWidth = 0.0;
const float scale = font ? font->scale() : 1.0f;
Position res;
if (width < 0 && !(silf()->flags()))
return width;
--pLast;
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
{
reverseSlots();
std::swap(pFirst, pLast);
}
pFirst.to_base();
pLast.to_base();
const float base = pFirst->origin().x / scale;
width = width / scale;
if ((jflags & gr_justEndInline) == 0)
{
while (pLast != pFirst && pLast != slots().end())
{
Rect bbox = theGlyphBBoxTemporary(pLast->glyph());
if (bbox.bl.x != 0.f || bbox.bl.y != 0.f || bbox.tr.x != 0.f || bbox.tr.y == 0.f)
break;
--pLast;
}
}
int icount = 0;
int numLevels = silf()->numJustLevels();
if (!numLevels)
{
for (auto s = pSlot->children(); s != pSlot->end(); ++s)
{
CharInfo *c = charinfo(s->before());
if (isWhitespace(c->unicodeChar()))
{
s->setJustify(*this, 0, 3, 1);
s->setJustify(*this, 0, 2, 1);
s->setJustify(*this, 0, 0, -1);
++icount;
}
}
if (!icount)
{
for (auto s = pSlot->children(); s != pSlot->end(); ++s)
{
s->setJustify(*this, 0, 3, 1);
s->setJustify(*this, 0, 2, 1);
s->setJustify(*this, 0, 0, -1);
}
}
++numLevels;
}
vector<JustifyTotal> stats(numLevels);
for (auto s = pFirst->children(); s != pFirst->end(); ++s)
{
float w = s->origin().x / scale + s->advance() - base;
if (w > currWidth) currWidth = w;
for (int j = 0; j < numLevels; ++j)
stats[j].accumulate(*s, *this, j);
s->just(0);
}
for (int i = (width < 0.0f) ? -1 : numLevels - 1; i >= 0; --i)
{
float diff;
float error = 0.;
float diffpw;
int tWeight = stats[i].weight();
if (tWeight == 0) continue;
do {
error = 0.;
diff = width - currWidth;
diffpw = diff / tWeight;
tWeight = 0;
for (auto s = pFirst->children(); s != pFirst->end(); ++s) // don't include final glyph
{
int w = s->getJustify(*this, i, 3);
float pref = diffpw * w + error;
int step = s->getJustify(*this, i, 2);
if (!step) step = 1; // handle lazy font developers
if (pref > 0)
{
float max = uint16_t(s->getJustify(*this, i, 0));
if (i == 0) max -= s->just();
if (pref > max) pref = max;
else tWeight += w;
}
else
{
float max = uint16_t(s->getJustify(*this, i, 1));
if (i == 0) max += s->just();
if (-pref > max) pref = -max;
else tWeight += w;
}
int actual = int(pref / step) * step;
if (actual)
{
error += diffpw * w - actual;
if (i == 0)
s->just(s->just() + actual);
else
s->setJustify(*this, i, 4, actual);
}
}
currWidth += diff - error;
} while (i == 0 && int(std::abs(error)) > 0 && tWeight);
}
// Temporarily swap rope buffer.
auto rv = subsegment(pSlot, std::next(pLast), [&]()->float
{
if (silf()->flags() & 1)
{
if (addLineEnd(slots().begin()) == slots().end()
|| addLineEnd(slots().end()) == slots().end())
return -1.0;
}
// run justification passes here
#if !defined GRAPHITE2_NTRACING
json * const dbgout = m_face->logger();
if (dbgout)
*dbgout << json::object
<< "justifies" << objectid(*this)
<< "passes" << json::array;
#endif
if (m_silf->justificationPass() != m_silf->positionPass() && (width >= 0.f || (silf()->flags() & 1)))
m_silf->runGraphite(*this, m_silf->justificationPass(), m_silf->positionPass());
#if !defined GRAPHITE2_NTRACING
// FIX ME if (dbgout)
// {
// *dbgout << json::item << json::close; // Close up the passes array
// positionSlots(nullptr, pSlot, std::next(pLast), m_dir);
// auto lEnd = end ? decltype(slots().cend())::from(end) : slots().cend();
// *dbgout << "output" << json::array;
// for(auto t = pSlot; t != lEnd; ++t)
// *dbgout << dslot(this, &*t);
// *dbgout << json::close << json::close;
// }
#endif
return positionSlots(font, pSlot, std::next(pLast), m_dir).x;
});
if ((m_dir & 1) != m_silf->dir() && m_silf->bidiPass() != m_silf->numPasses())
reverseSlots();
return rv;
}
SlotBuffer::iterator Segment::addLineEnd(SlotBuffer::iterator pos)
{
auto eSlot = slots().insert(pos, Slot(numAttrs()));
if (eSlot == slots().end()) return eSlot;
const uint16_t gid = silf()->endLineGlyphid();
const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
eSlot->glyph(*this, gid, theGlyph);
if (pos != slots().end())
{
eSlot->before(pos->before());
if (eSlot != slots().begin())
eSlot->after(std::prev(eSlot)->after());
else
eSlot->after(pos->before());
}
else
{
pos = --slots().end();
eSlot->after(std::prev(eSlot)->after());
eSlot->before(pos->after());
}
return eSlot;
}
void Segment::delLineEnd(SlotBuffer::iterator s)
{
slots().erase(s);
}

View File

@ -0,0 +1,231 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Main.h"
#include "inc/Endian.h"
#include "inc/NameTable.h"
#include "inc/UtfCodec.h"
using namespace graphite2;
NameTable::NameTable(const void* data, size_t length, uint16_t platformId, uint16_t encodingID)
: m_platformId(0), m_encodingId(0), m_languageCount(0),
m_platformOffset(0), m_platformLastRecord(0), m_nameDataLength(0),
m_table(0), m_nameData(NULL)
{
void *pdata = gralloc<byte>(length);
if (!pdata) return;
memcpy(pdata, data, length);
m_table = reinterpret_cast<const TtfUtil::Sfnt::FontNames*>(pdata);
if ((length > sizeof(TtfUtil::Sfnt::FontNames)) &&
(length > sizeof(TtfUtil::Sfnt::FontNames) +
sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16_t>(m_table->count) - 1)))
{
uint16_t offset = be::swap<uint16_t>(m_table->string_offset);
if (offset < length)
{
m_nameData = reinterpret_cast<const uint8_t*>(pdata) + offset;
setPlatformEncoding(platformId, encodingID);
m_nameDataLength = uint16_t(length - offset);
return;
}
}
free(const_cast<TtfUtil::Sfnt::FontNames*>(m_table));
m_table = NULL;
}
uint16_t NameTable::setPlatformEncoding(uint16_t platformId, uint16_t encodingID)
{
if (!m_nameData) return 0;
uint16_t i = 0;
uint16_t count = be::swap<uint16_t>(m_table->count);
for (; i < count; i++)
{
if (be::swap<uint16_t>(m_table->name_record[i].platform_id) == platformId &&
be::swap<uint16_t>(m_table->name_record[i].platform_specific_id) == encodingID)
{
m_platformOffset = i;
break;
}
}
while ((++i < count) &&
(be::swap<uint16_t>(m_table->name_record[i].platform_id) == platformId) &&
(be::swap<uint16_t>(m_table->name_record[i].platform_specific_id) == encodingID))
{
m_platformLastRecord = i;
}
m_encodingId = encodingID;
m_platformId = platformId;
return 0;
}
void* NameTable::getName(uint16_t& languageId, uint16_t nameId, gr_encform enc, uint32_t& length)
{
uint16_t anyLang = 0;
uint16_t enUSLang = 0;
uint16_t bestLang = 0;
if (!m_table)
{
languageId = 0;
length = 0;
return NULL;
}
for (uint16_t i = m_platformOffset; i <= m_platformLastRecord; i++)
{
if (be::swap<uint16_t>(m_table->name_record[i].name_id) == nameId)
{
uint16_t langId = be::swap<uint16_t>(m_table->name_record[i].language_id);
if (langId == languageId)
{
bestLang = i;
break;
}
// MS language tags have the language in the lower byte, region in the higher
else if ((langId & 0xFF) == (languageId & 0xFF))
{
bestLang = i;
}
else if (langId == 0x409)
{
enUSLang = i;
}
else
{
anyLang = i;
}
}
}
if (!bestLang)
{
if (enUSLang) bestLang = enUSLang;
else
{
bestLang = anyLang;
if (!anyLang)
{
languageId = 0;
length = 0;
return NULL;
}
}
}
const TtfUtil::Sfnt::NameRecord & nameRecord = m_table->name_record[bestLang];
languageId = be::swap<uint16_t>(nameRecord.language_id);
uint16_t utf16Length = be::swap<uint16_t>(nameRecord.length);
uint16_t offset = be::swap<uint16_t>(nameRecord.offset);
if(offset + utf16Length > m_nameDataLength)
{
languageId = 0;
length = 0;
return NULL;
}
utf16Length >>= 1; // in utf16 units
utf16::codeunit_t * utf16Name = gralloc<utf16::codeunit_t>(utf16Length + 1);
if (!utf16Name)
{
languageId = 0;
length = 0;
return NULL;
}
const uint8_t* pName = m_nameData + offset;
for (size_t i = 0; i < utf16Length; i++)
{
utf16Name[i] = be::read<uint16_t>(pName);
}
utf16Name[utf16Length] = 0;
if (!utf16::validate(utf16Name, utf16Name + utf16Length))
{
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
switch (enc)
{
case gr_utf8:
{
utf8::codeunit_t* uniBuffer = gralloc<utf8::codeunit_t>(3 * utf16Length + 1);
if (!uniBuffer)
{
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
utf8::iterator d = uniBuffer;
for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
*d = *s;
length = uint32_t(d - uniBuffer);
uniBuffer[length] = 0;
free(utf16Name);
return uniBuffer;
}
case gr_utf16:
length = utf16Length;
return utf16Name;
case gr_utf32:
{
utf32::codeunit_t * uniBuffer = gralloc<utf32::codeunit_t>(utf16Length + 1);
if (!uniBuffer)
{
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
utf32::iterator d = uniBuffer;
for (utf16::const_iterator s = utf16Name, e = utf16Name + utf16Length; s != e; ++s, ++d)
*d = *s;
length = uint32_t(d - uniBuffer);
uniBuffer[length] = 0;
free(utf16Name);
return uniBuffer;
}
}
free(utf16Name);
languageId = 0;
length = 0;
return NULL;
}
uint16_t NameTable::getLanguageId(const char * bcp47Locale)
{
size_t localeLength = strlen(bcp47Locale);
uint16_t localeId = m_locale2Lang.getMsId(bcp47Locale);
if (m_table && (be::swap<uint16_t>(m_table->format) == 1))
{
const uint8_t * pLangEntries = reinterpret_cast<const uint8_t*>(m_table) +
sizeof(TtfUtil::Sfnt::FontNames)
+ sizeof(TtfUtil::Sfnt::NameRecord) * ( be::swap<uint16_t>(m_table->count) - 1);
uint16_t numLangEntries = be::read<uint16_t>(pLangEntries);
const TtfUtil::Sfnt::LangTagRecord * langTag =
reinterpret_cast<const TtfUtil::Sfnt::LangTagRecord*>(pLangEntries);
if (pLangEntries + numLangEntries * sizeof(TtfUtil::Sfnt::LangTagRecord) <= m_nameData)
{
for (uint16_t i = 0; i < numLangEntries; i++)
{
uint16_t offset = be::swap<uint16_t>(langTag[i].offset);
uint16_t length = be::swap<uint16_t>(langTag[i].length);
if ((offset + length <= m_nameDataLength) && (length == 2 * localeLength))
{
const uint8_t* pName = m_nameData + offset;
bool match = true;
for (size_t j = 0; j < localeLength; j++)
{
uint16_t code = be::read<uint16_t>(pName);
if ((code > 0x7F) || (code != bcp47Locale[j]))
{
match = false;
break;
}
}
if (match)
return 0x8000 + i;
}
}
}
}
return localeId;
}

1105
src/graphite2/src/Pass.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Position.h"
#include <cmath>
using namespace graphite2;
bool Rect::hitTest(Rect &other)
{
if (bl.x > other.tr.x) return false;
if (tr.x < other.bl.x) return false;
if (bl.y > other.tr.y) return false;
if (tr.y < other.bl.y) return false;
return true;
}
Position Rect::overlap(Position &offset, Rect &other, Position &othero)
{
float ax = (bl.x + offset.x) - (other.tr.x + othero.x);
float ay = (bl.y + offset.y) - (other.tr.y + othero.y);
float bx = (other.bl.x + othero.x) - (tr.x + offset.x);
float by = (other.bl.y + othero.y) - (tr.y + offset.y);
return Position((ax > bx ? ax : bx), (ay > by ? ay : by));
}
float boundmin(float move, float lim1, float lim2, float &error)
{
// error is always positive for easy comparison
if (move < lim1 && move < lim2)
{ error = 0.; return move; }
else if (lim1 < lim2)
{ error = std::fabs(move - lim1); return lim1; }
else
{ error = std::fabs(move - lim2); return lim2; }
}
#if 0
Position Rect::constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox)
{
// a = max, i = min, s = sum, d = diff
float eax, eay, eix, eiy, eas, eis, ead, eid;
float beste = INF;
Position res;
// calculate the movements in each direction and the error (amount of remaining overlap)
// first param is movement, second and third are movement over the constraining box
float ax = boundmin(obox.tr.x + other.x - box.bl.x - offset.x + 1, tr.x - offset.x, INF, &eax);
float ay = boundmin(obox.tr.y + other.y - box.bl.y - offset.y + 1, tr.y - offset.y, INF, &eay);
float ix = boundmin(obox.bl.x + other.x - box.tr.x - offset.x + 1, bl.x - offset.x, INF, &eix);
float iy = boundmin(obox.bl.y + other.y - box.tr.y - offset.y + 1, bl.y - offset.y, INF, &eiy);
float as = boundmin(ISQRT2 * (osdbox.tr.x + other.x + other.y - sdbox.bl.x - offset.x - offset.y) + 1, tr.x - offset.x, tr.y - offset.y, &eas);
float is = boundmin(ISQRT2 * (osdbox.bl.x + other.x + other.y - sdbox.tr.x - offset.x - offset.y) + 1, bl.x - offset.x, bl.y - offset.y, &eis);
float ad = boundmin(ISQRT2 * (osdbox.tr.y + other.x - other.y - sdbox.bl.y - offset.x + offset.y) + 1, tr.y - offset.y, tr.x - offset.x, &ead);
float id = boundmin(ISQRT2 * (osdbox.bl.y + other.x - other.y - sdbox.tr.y - offset.x + offset.y) + 1, bl.y - offset.y, bl.x - offset.x, &eid);
if (eax < beste)
{ res = Position(ax, 0); beste = eax; }
if (eay < beste)
{ res = Position(0, ay); beste = eay; }
if (eix < beste)
{ res = Position(ix, 0); beste = eix; }
if (eiy < beste)
{ res = Position(0, iy); beste = eiy; }
if (SQRT2 * (eas) < beste)
{ res = Position(as, ad); beste = SQRT2 * (eas); }
if (SQRT2 * (eis) < beste)
{ res = Position(is, is); beste = SQRT2 * (eis); }
if (SQRT2 * (ead) < beste)
{ res = Position(ad, ad); beste = SQRT2 * (ead); }
if (SQRT2 * (eid) < beste)
{ res = Position(id, id); beste = SQRT2 * (eid); }
return res;
}
#endif

View File

@ -0,0 +1,358 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/UtfCodec.h"
#include <cstring>
#include <cstdlib>
#include "inc/bits.h"
#include "inc/Segment.h"
#include "graphite2/Font.h"
#include "inc/CharInfo.h"
#include "inc/debug.h"
#include "inc/Font.h"
#include "inc/Slot.h"
#include "inc/Main.h"
#include "inc/CmapCache.h"
#include "inc/Collider.h"
#include "graphite2/Segment.h"
using namespace graphite2;
Segment::Segment(size_t numchars, const Face* face, uint32_t script, int textDir)
: //m_srope(face->chooseSilf(script)->numUser(), face->chooseSilf(script)->numJustLevels()),
m_charinfo(new CharInfo[numchars]),
m_collisions(NULL),
m_face(face),
m_silf(face->chooseSilf(script)),
m_bufSize(numchars + 10),
m_numGlyphs(numchars),
m_numCharinfo(numchars),
m_defaultOriginal(0),
m_dir(textDir),
m_flags(((m_silf->flags() & 0x20) != 0) << 1),
m_passBits(m_silf->aPassBits() ? -1 : 0)
{
m_bufSize = log_binary(numchars)+1;
}
Segment::~Segment()
{
delete[] m_charinfo;
free(m_collisions);
}
void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
{
auto const glyph = m_face->glyphs().glyphSafe(gid);
m_charinfo[id].init(cid);
m_charinfo[id].feats(iFeats);
m_charinfo[id].base(coffset);
m_charinfo[id].breakWeight(glyph ? glyph->attrs()[m_silf->aBreak()] : 0);
auto & aSlot = slots().emplace_back(numAttrs());
aSlot.glyph(*this, gid, glyph);
aSlot.original(id);
aSlot.before(id);
aSlot.after(id);
aSlot.generation() = slots().size();
aSlot.clusterhead(true);
if (glyph && m_silf->aPassBits())
m_passBits &= glyph->attrs()[m_silf->aPassBits()]
| (m_silf->numPasses() > 16 ? (glyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
}
SlotBuffer::iterator Segment::newSlot()
{
if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR)
return m_srope.end();
return m_srope.newSlot();
}
void Segment::freeSlot(SlotBuffer::iterator i)
{
m_srope.freeSlot(i);
}
// reverse the slots but keep diacritics in their same position after their bases
void Segment::reverseSlots()
{
m_dir = m_dir ^ 64; // invert the reverse flag
if (slots().empty()) return; // skip 0 or 1 glyph runs
// Ensure any unkown bidi classes are set for the reversal algorithm.
for (auto & slot: m_srope) {
if (slot.bidiClass() == -1)
slot.bidiClass(int8_t(glyphAttr(slot.gid(), m_silf->aBidi())));
}
m_srope.reverse();
}
void Segment::linkClusters()
{
#if 0
if (slots().empty()) return;
auto ls = &slots().front();
if (m_dir & 1)
{
for (auto &&s: slots())
{
if (!s.isBase()) continue;
s.sibling(ls);
ls = &s;
}
}
else
{
for (auto && s: slots())
{
if (!s.isBase()) continue;
ls->sibling(&s);
ls = &s;
}
}
#endif
}
Position Segment::positionSlots(Font const * font, SlotBuffer::iterator first, SlotBuffer::iterator last, bool isRtl, bool isFinal)
{
assert(first.is_valid() || first == slots().end());
if (slots().empty()) // only true for empty segments
return Position(0.,0.);
bool reorder = (currdir() != isRtl);
if (reorder)
{
reverseSlots();
--last; std::swap(first, last); ++last;
}
// Reset all the positions.
for (auto slot = first; slot != last; ++slot)
slot->origin() = Position();
// Phase one collect ranges.
for (auto slot = first; slot != last; ++slot)
{
auto const base = slot->base();
auto & clsb = const_cast<float &>(base->origin().x);
auto & crsb = const_cast<float &>(base->origin().y);
slot->update_cluster_metric(*this, isRtl, isFinal, clsb, crsb);
}
// Position each cluster either.
Position offset = {0.f, 0.f};
if (isRtl)
{
#if 0
Slot * base = nullptr;
float clsb = 0.f, crsb = 0.f;
auto cluster = --last;
for (auto slot = last, end=--first; slot != end; --slot)
{
slot->reset_origin();
auto const slot_base = slot->base();
if (base != slot_base)
{
offset.x += -clsb;
for (auto s = cluster; s != slot; --s)
{
s->origin(offset + s->origin());
if (s->origin().x < 0)
{
offset.x += -s->origin().x;
s->position_shift({-s->origin().x, 0.f});
}
}
offset.x += crsb;
base = slot_base;
cluster = slot;
clsb = std::numeric_limits<float>::infinity();
crsb = 0;
}
slot->update_cluster_metric(*this, isRtl, isFinal, clsb, crsb);
}
offset.x += -clsb;
for (auto s = cluster; s != first; --s)
s->position_shift(offset);
offset.x += crsb;
++last; ++first;
#else
// For the first visual cluster ensure initial x positions are
// never negative.
float clsb = 0.f;
for (auto slot = --last, end=--first; slot != end; --slot)
{
if (slot->isBase())
clsb = slot->origin().x;
if (-slot->origin().x > offset.x)
offset.x = -slot->origin().x;
if (slot->clusterhead()) break;
}
offset.x += clsb;
// Adjust all cluster bases
for (auto slot = last, end=first; slot != end; --slot)
{
if (!slot->isBase()) continue;
auto const clsb = slot->origin().x;
auto const crsb = slot->origin().y;
auto const shifts = slot->collision_shift(*this);
offset.x += -clsb;
// Subtract the slot shift as this is RtL.
slot->origin() = offset + shifts - slot->shift();
offset.x += crsb + shifts.x - slot->shift().x;
}
++last; ++first;
#endif
}
else
{
// For the first visual cluster ensure initial x positions are
// never negative.
for (auto slot = first; slot != last; ++slot)
{
if (-slot->origin().x > offset.x)
offset.x = -slot->origin().x;
if (slot->clusterhead()) break;
}
// Adjust all cluster bases
for (auto slot = first; slot != last; ++slot)
{
if (!slot->isBase()) continue;
auto const clsb = slot->origin().x;
auto const crsb = slot->origin().y;
auto const shifts = slot->collision_shift(*this);
offset.x += -clsb;
slot->origin() = offset + shifts + slot->shift();
offset.x += crsb + shifts.x;
}
}
// Shift all attached slots
for (auto slot = first; slot != last; ++slot)
{
if (slot->isBase()) continue;
auto base = slot->base();
slot->position_shift({base->origin().x, 0});
}
if (font && font->scale() != 1)
{
auto const scale = font->scale();
for (auto slot = first; slot != last; ++slot)
slot->scale_by(scale);
offset *= scale;
}
if (reorder)
reverseSlots();
return offset;
}
void Segment::associateChars(int offset, size_t numChars)
{
int i = 0, j = 0;
CharInfo *c, *cend;
for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
{
c->before(-1);
c->after(-1);
}
for (auto & s: slots())
{
j = s.before();
if (j >= 0) {
for (const int after = s.after(); j <= after; ++j)
{
c = charinfo(j);
if (c->before() == -1 || i < c->before()) c->before(i);
if (c->after() < i) c->after(i);
}
}
s.index(i++);
}
for (auto & s: slots())
{
int a;
for (a = s.after() + 1; a < offset + int(numChars) && charinfo(a)->after() < 0; ++a)
charinfo(a)->after(s.index());
s.after(--a);
for (a = s.before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
charinfo(a)->before(s.index());
s.before(++a);
}
}
template <typename utf_iter>
inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
{
const Cmap & cmap = face.cmap();
int slotid = 0;
const typename utf_iter::codeunit_type * const base = c;
for (; n_chars; --n_chars, ++c, ++slotid)
{
const uint32_t usv = *c;
uint16_t gid = cmap[usv];
if (!gid) gid = face.findPseudo(usv);
seg.appendSlot(slotid, usv, gid, fid, c - base);
}
}
bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
{
assert(face);
assert(pFeats);
if (!m_charinfo) return false;
// utf iterator is self recovering so we don't care about the error state of the iterator.
switch (enc)
{
case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
}
return true;
}
void Segment::doMirror(uint16_t aMirror)
{
for (auto & s: slots())
{
unsigned short g = glyphAttr(s.gid(), aMirror);
if (g && (!(dir() & 4) || !glyphAttr(s.gid(), aMirror + 1)))
s.glyph(*this, g);
}
}
bool Segment::initCollisions()
{
m_collisions = grzeroalloc<SlotCollision>(slotCount());
if (!m_collisions) return false;
for (auto & p: slots())
if (p.index() < slotCount())
::new (collisionInfo(p)) SlotCollision(*this, p);
else
return false;
return true;
}

View File

@ -0,0 +1,64 @@
/* GRAPHITE2 LICENSING
Copyright 2020, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#include "inc/Segment.h"
#include "inc/ShapingContext.hpp"
using namespace graphite2;
ShapingContext::ShapingContext(Segment & seg, uint8_t direction, size_t maxSize)
: segment(seg),
dbgout(seg.getFace() ? seg.getFace()->logger() : nullptr),
//in(seg.slots().num_user_attrs(), seg.slots().num_just_levels()),
dir(direction),
_max_size(int(maxSize)),
_precontext(0),
_highpassed(false)
{
map.reserve(MAX_SLOTS);
}
void ShapingContext::reset(SlotBuffer::iterator & slot, short unsigned int max_pre_ctxt)
{
int pre_ctxt = 0;
for (auto const end = segment.slots().begin(); pre_ctxt != max_pre_ctxt && slot != end; ++pre_ctxt, --slot);
_precontext = pre_ctxt;
map.clear();
in.clear();
}
void ShapingContext::collectGarbage(slotref &)
{
auto const end = map.begin()-1;
auto si = map.end()-1;
for(;si != end; --si) {
auto slot = *si;
if (slot != segment.slots().end()
&& slot->copied())
segment.freeSlot(slot);
}
}

415
src/graphite2/src/Silf.cpp Normal file
View File

@ -0,0 +1,415 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <cstdlib>
#include "graphite2/Segment.h"
#include "inc/debug.h"
#include "inc/Endian.h"
#include "inc/Silf.h"
#include "inc/Segment.h"
#include "inc/Rule.h"
#include "inc/Error.h"
#include "inc/ShapingContext.hpp"
using namespace graphite2;
namespace { static const uint32_t ERROROFFSET = 0xFFFFFFFF; }
Silf::Silf() throw()
: m_passes(0),
m_pseudos(0),
m_classOffsets(0),
m_classData(0),
m_justs(0),
m_numPasses(0),
m_numJusts(0),
m_sPass(0),
m_pPass(0),
m_jPass(0),
m_bPass(0),
m_flags(0),
m_dir(0),
m_aPseudo(0),
m_aBreak(0),
m_aUser(0),
m_aBidi(0),
m_aMirror(0),
m_aPassBits(0),
m_iMaxComp(0),
m_aCollision(0),
m_aLig(0),
m_numPseudo(0),
m_nClass(0),
m_nLinear(0),
m_gEndLine(0)
{
memset(&m_silfinfo, 0, sizeof m_silfinfo);
}
Silf::~Silf() throw()
{
releaseBuffers();
}
void Silf::releaseBuffers() throw()
{
delete [] m_passes;
delete [] m_pseudos;
free(m_classOffsets);
free(m_classData);
free(m_justs);
m_passes= 0;
m_pseudos = 0;
m_classOffsets = 0;
m_classData = 0;
m_justs = 0;
}
bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32_t version)
{
const byte * p = silf_start,
* const silf_end = p + lSilf;
Error e;
if (e.test(version >= 0x00060000, E_BADSILFVERSION))
{
releaseBuffers(); return face.error(e);
}
if (version >= 0x00030000)
{
if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
be::skip<int32_t>(p); // ruleVersion
be::skip<uint16_t>(p,2); // passOffset & pseudosOffset
}
else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
const uint16_t maxGlyph = be::read<uint16_t>(p);
m_silfinfo.extra_ascent = be::read<uint16_t>(p);
m_silfinfo.extra_descent = be::read<uint16_t>(p);
m_numPasses = be::read<uint8_t>(p);
m_sPass = be::read<uint8_t>(p);
m_pPass = be::read<uint8_t>(p);
m_jPass = be::read<uint8_t>(p);
m_bPass = be::read<uint8_t>(p);
m_flags = be::read<uint8_t>(p);
be::skip<uint8_t>(p,2); // max{Pre,Post}Context.
m_aPseudo = be::read<uint8_t>(p);
m_aBreak = be::read<uint8_t>(p);
m_aBidi = be::read<uint8_t>(p);
m_aMirror = be::read<uint8_t>(p);
m_aPassBits = be::read<uint8_t>(p);
// Read Justification levels.
m_numJusts = be::read<uint8_t>(p);
if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
|| e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
{
releaseBuffers(); return face.error(e);
}
if (m_numJusts)
{
m_justs = gralloc<Justinfo>(m_numJusts);
if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
for (uint8_t i = 0; i < m_numJusts; i++)
{
::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
be::skip<byte>(p,8);
}
}
if (e.test(p + sizeof(uint16_t) + sizeof(uint8_t)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
m_aLig = be::read<uint16_t>(p);
m_aUser = be::read<uint8_t>(p);
m_iMaxComp = be::read<uint8_t>(p);
m_dir = be::read<uint8_t>(p) - 1;
m_aCollision = be::read<uint8_t>(p);
be::skip<byte>(p,3);
be::skip<uint16_t>(p, be::read<uint8_t>(p)); // don't need critical features yet
be::skip<byte>(p); // reserved
if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
be::skip<uint32_t>(p, be::read<uint8_t>(p)); // don't use scriptTag array.
if (e.test(p + sizeof(uint16_t) + sizeof(uint32_t) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
m_gEndLine = be::read<uint16_t>(p); // lbGID
const byte * o_passes = p;
uint32_t passes_start = be::read<uint32_t>(p);
const size_t num_attrs = face.glyphs().numAttrs();
if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO)
|| e.test(m_aBreak >= num_attrs, E_BADABREAK)
|| e.test(m_aBidi >= num_attrs, E_BADABIDI)
|| e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
|| e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
|| e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= lSilf, E_BADPASSESSTART)
|| e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
|| e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
|| e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
|| e.test(m_aLig > 127, E_BADALIG))
{
releaseBuffers();
return face.error(e);
}
be::skip<uint32_t>(p, m_numPasses);
if (e.test(unsigned(p - silf_start) + sizeof(uint16_t) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
m_numPseudo = be::read<uint16_t>(p);
be::skip<uint16_t>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
m_pseudos = new Pseudo[m_numPseudo];
if (e.test(unsigned(p - silf_start) + m_numPseudo*(sizeof(uint32_t) + sizeof(uint16_t)) >= passes_start, E_BADNUMPSEUDO)
|| e.test(!m_pseudos, E_OUTOFMEM))
{
releaseBuffers(); return face.error(e);
}
for (int i = 0; i < m_numPseudo; i++)
{
m_pseudos[i].uid = be::read<uint32_t>(p);
m_pseudos[i].gid = be::read<uint16_t>(p);
}
const size_t clen = readClassMap(p, passes_start + silf_start - p, version, e);
m_passes = new Pass[m_numPasses];
if (e || e.test(clen > unsigned(passes_start + silf_start - p), E_BADPASSESSTART)
|| e.test(!m_passes, E_OUTOFMEM))
{ releaseBuffers(); return face.error(e); }
for (size_t i = 0; i < m_numPasses; ++i)
{
uint32_t pass_start = be::read<uint32_t>(o_passes);
uint32_t pass_end = be::peek<uint32_t>(o_passes);
face.error_context((face.error_context() & 0xFF00) + EC_ASILF + unsigned(i << 16));
if (e.test(pass_start > pass_end, E_BADPASSSTART)
|| e.test(pass_start < passes_start, E_BADPASSSTART)
|| e.test(pass_end > lSilf, E_BADPASSEND)) {
releaseBuffers(); return face.error(e);
}
enum passtype pt = PASS_TYPE_UNKNOWN;
if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
else pt = PASS_TYPE_LINEBREAK;
m_passes[i].init(this);
if (!m_passes[i].readPass(silf_start + pass_start, pass_end - pass_start, pass_start, face, pt,
version, e))
{
releaseBuffers();
return false;
}
}
// fill in gr_faceinfo
m_silfinfo.upem = face.glyphs().unitsPerEm();
m_silfinfo.has_bidi_pass = (m_bPass != 0xFF);
m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass);
m_silfinfo.line_ends = (m_flags & 1);
m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7);
return true;
}
template<typename T> inline uint32_t Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
{
const T cls_off = 2*sizeof(uint16_t) + sizeof(T)*(m_nClass+1);
const uint32_t max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16_t);
// Check that the last+1 offset is less than or equal to the class map length.
if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
|| e.test(max_off > (data_len - cls_off)/sizeof(uint16_t), E_HIGHCLASSOFFSET))
return ERROROFFSET;
// Read in all the offsets.
m_classOffsets = gralloc<uint32_t>(m_nClass+1);
if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
for (uint32_t * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
{
*o = (be::read<T>(p) - cls_off)/sizeof(uint16_t);
if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
return ERROROFFSET;
}
return max_off;
}
size_t Silf::readClassMap(const byte *p, size_t data_len, uint32_t version, Error &e)
{
if (e.test(data_len < sizeof(uint16_t)*2, E_BADCLASSSIZE)) return ERROROFFSET;
m_nClass = be::read<uint16_t>(p);
m_nLinear = be::read<uint16_t>(p);
// Check that numLinear < numClass,
// that there is at least enough data for numClasses offsets.
if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
|| e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32_t) : sizeof(uint16_t)) > (data_len - 4), E_CLASSESTOOBIG))
return ERROROFFSET;
uint32_t max_off;
if (version >= 0x00040000)
max_off = readClassOffsets<uint32_t>(p, data_len, e);
else
max_off = readClassOffsets<uint16_t>(p, data_len, e);
if (max_off == ERROROFFSET) return ERROROFFSET;
if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
return ERROROFFSET;
// Check the linear offsets are sane, these must be monotonically increasing.
assert(m_nClass >= m_nLinear);
for (const uint32_t *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
return ERROROFFSET;
// Fortunately the class data is all uint16s so we can decode these now
m_classData = gralloc<uint16_t>(max_off);
if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
for (uint16_t *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
*d = be::read<uint16_t>(p);
// Check the lookup class invariants for each non-linear class
for (const uint32_t *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
{
const uint16_t * lookup = m_classData + *o;
if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
|| e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
|| lookup[0] * 2 + *o + 4 > max_off // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
|| lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO) // rangeShift: numIDs - searchRange
|| e.test(((o[1] - *o) & 1) != 0, ERROROFFSET)) // glyphs are in pairs so difference must be even.
return ERROROFFSET;
}
return max_off;
}
uint16_t Silf::findPseudo(uint32_t uid) const
{
for (int i = 0; i < m_numPseudo; i++)
if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
return 0;
}
uint16_t Silf::findClassIndex(uint16_t cid, uint16_t gid) const
{
if (cid > m_nClass) return -1;
const uint16_t * cls = m_classData + m_classOffsets[cid];
if (cid < m_nLinear) // output class being used for input, shouldn't happen
{
for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
if (*cls == gid) return i;
return -1;
}
else
{
const uint16_t * min = cls + 4, // lookups array
* max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16_t pairs long
do
{
const uint16_t * p = min + (-2 & ((max-min)/2));
if (p[0] > gid) max = p;
else min = p;
}
while (max - min > 2);
return min[0] == gid ? min[1] : -1;
}
}
uint16_t Silf::getClassGlyph(uint16_t cid, unsigned int index) const
{
if (cid > m_nClass) return 0;
uint32_t loc = m_classOffsets[cid];
if (cid < m_nLinear)
{
if (index < m_classOffsets[cid + 1] - loc)
return m_classData[index + loc];
}
else // input class being used for output. Shouldn't happen
{
for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
if (m_classData[i + 1] == index) return m_classData[i];
}
return 0;
}
bool Silf::runGraphite(Segment &seg, uint8_t firstPass, uint8_t lastPass, int dobidi) const
{
size_t maxSize = seg.slotCount() * MAX_SEG_GROWTH_FACTOR;
ShapingContext ctxt(seg, m_dir, maxSize);
vm::Machine m(ctxt);
uint8_t lbidi = m_bPass;
#if !defined GRAPHITE2_NTRACING
json * const dbgout = seg.getFace()->logger();
#endif
if (lastPass == 0)
{
if (firstPass == lastPass && lbidi == 0xFF)
return true;
lastPass = m_numPasses;
}
if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
lastPass++;
else
lbidi = 0xFF;
for (size_t i = firstPass; i < lastPass; ++i)
{
// bidi and mirroring
if (i == lbidi)
{
#if !defined GRAPHITE2_NTRACING
if (dbgout)
{
*dbgout << json::item << json::object
// << "pindex" << i // for debugging
<< "id" << -1
<< "slotsdir" << (seg.currdir() ? "rtl" : "ltr")
<< "passdir" << (m_dir & 1 ? "rtl" : "ltr")
<< "slots" << json::array;
seg.positionSlots(nullptr, seg.slots().begin(), seg.slots().end(), seg.currdir());
for(auto & s: seg.slots())
*dbgout << dslot(&seg, &s);
*dbgout << json::close
<< "rules" << json::array << json::close
<< json::close;
}
#endif
if (seg.currdir() != (m_dir & 1))
seg.reverseSlots();
if (m_aMirror && (seg.dir() & 3) == 3)
seg.doMirror(m_aMirror);
--i;
lbidi = lastPass;
--lastPass;
continue;
}
#if !defined GRAPHITE2_NTRACING
if (dbgout)
{
*dbgout << json::item << json::object
// << "pindex" << i // for debugging
<< "id" << i+1
<< "slotsdir" << (seg.currdir() ? "rtl" : "ltr")
<< "passdir" << ((m_dir & 1) ^ m_passes[i].reverseDir() ? "rtl" : "ltr")
<< "slots" << json::array;
seg.positionSlots(nullptr, seg.slots().begin(), seg.slots().end(), seg.currdir());
for(auto & s: seg.slots())
*dbgout << dslot(&seg, &s);
*dbgout << json::close;
}
#endif
// test whether to reorder, prepare for positioning
bool reverse = (lbidi == 0xFF) && (seg.currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
if ((i >= 32 || (seg.passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
&& !m_passes[i].runGraphite(m, ctxt, reverse))
return false;
// only subsitution passes can change segment length, cached subsegments are short for their text
if (m.status() != vm::Machine::finished
|| (seg.slotCount() && seg.slotCount() > maxSize))
return false;
}
return true;
}

569
src/graphite2/src/Slot.cpp Normal file
View File

@ -0,0 +1,569 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/Segment.h"
#include "inc/CharInfo.h"
#include "inc/Collider.h"
#include "inc/Font.h"
#include "inc/ShapingContext.hpp"
#include "inc/Slot.h"
#include "inc/Silf.h"
#include "inc/Rule.h"
using namespace graphite2;
auto Slot::attributes::operator = (attributes const & rhs) -> attributes & {
if (this != &rhs) {
reserve(rhs.num_attrs(), rhs.num_justs());
if (!rhs.is_inline() && external) {
auto const sz = external->n_attrs
+ external->n_justs*NUMJUSTPARAMS + 1;
memcpy(external, rhs.external, sz*sizeof(uint16_t));
} else local = rhs.local;
}
return *this;
}
auto Slot::attributes::operator = (attributes && rhs) noexcept -> attributes & {
if (!is_inline()) free(external);
external = rhs.external;
rhs.external = nullptr;
return *this;
}
void Slot::attributes::reserve(size_t target_num_attrs, size_t target_num_justs) {
assert(target_num_attrs < 256);
assert(target_num_justs < 256);
if (num_attrs() >= target_num_attrs
&& num_justs() >= target_num_justs)
return;
if (target_num_justs > 0
|| target_num_attrs > sizeof local.data/sizeof *local.data)
{
auto const sz = target_num_attrs + target_num_justs*NUMJUSTPARAMS + 1;
if (is_inline()) { // Convert to non-inline form.
auto box = reinterpret_cast<decltype(external)>(grzeroalloc<int16_t>(sz));
if (box) {
if (local.n_attrs) memcpy(box->data, local.data, local.n_attrs*sizeof *local.data);
external = box;
external->n_attrs = target_num_attrs;
external->n_justs = target_num_justs;
}
} else { // Grow the existing buffer.
external = static_cast<decltype(external)>(realloc(external, sz*sizeof *local.data));
external->n_attrs = target_num_attrs;
external->n_justs = target_num_justs;
}
}
else local.n_attrs = target_num_attrs;
}
void Slot::init_just_infos(Segment const & seg)
{
auto const target_num_justs = seg.silf()->numJustLevels();
for (int i = target_num_justs - 1; i >= 0; --i)
{
Justinfo *justs = seg.silf()->justAttrs() + i;
int16_t *v = m_attrs.just_info() + i * NUMJUSTPARAMS;
v[0] = seg.glyphAttr(gid(), justs->attrStretch());
v[1] = seg.glyphAttr(gid(), justs->attrShrink());
v[2] = seg.glyphAttr(gid(), justs->attrStep());
v[3] = seg.glyphAttr(gid(), justs->attrWeight());
}
}
Slot & Slot::operator = (Slot && rhs) noexcept
{
if (this != &rhs)
{
Slot_data::operator=(std::move(rhs));
m_parent_offset = 0;
m_attrs = std::move(rhs.m_attrs);
#if !defined GRAPHITE2_NTRACING
m_gen = rhs.m_gen;
#endif
}
return *this;
}
Slot & Slot::operator = (Slot const & rhs)
{
if (this != &rhs)
{
Slot_data::operator=(rhs);
m_parent_offset = 0;
m_attrs = rhs.m_attrs;
#if !defined GRAPHITE2_NTRACING
m_gen = rhs.m_gen;
#endif
}
return *this;
}
void Slot::update(int /*numGrSlots*/, int numCharInfo, Position &relpos)
{
m_before += numCharInfo;
m_after += numCharInfo;
m_position = m_position + relpos;
}
Position Slot::collision_shift(Segment const & seg) const
{
auto const cinfo = seg.collisionInfo(*this);
return cinfo ? cinfo->offset() : Position{};
}
Position Slot::update_cluster_metric(Segment const & seg, bool const rtl, bool const is_final, float & clsb, float & crsb, unsigned depth)
{
// Bail out early if the attachment chain is too deep.
if (depth == 0) return Position();
// Incorporate shift into positioning, and cluster rsb calculations.
Position shift = {m_shift.x + m_just, m_shift.y};
auto const collision_info = seg.collisionInfo(*this);
if (is_final && collision_info) {
// if (!(collision_info->flags() & SlotCollision::COLL_KERN) || rtl)
shift += collision_info->offset();
}
// Only consider if design space advance is a non-zero whole unit.
auto const slot_adv = m_advance.x + m_just;
auto const parent = attachedTo();
auto pos = shift;
if (!parent) {
clsb = min(0.0f, clsb);
pos = {0,0};
shift = {0,0};
} else {
auto base = parent->update_cluster_metric(seg, rtl, is_final, clsb, crsb, depth-1);
m_position = (pos += base + m_attach - m_with);
if (m_advance.x >= 0.5f)
clsb = min(pos.x, clsb);
}
if (m_advance.x >= 0.5f)
// We only consider shift for attached glyphs not ourselves.
crsb = max(pos.x - shift.x + slot_adv, crsb);
return pos;
}
Position Slot::finalise(const Segment & seg, const Font *font, Position & base, Rect & bbox, uint8_t attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth)
{
// assert(false);
SlotCollision *coll = NULL;
if (depth > 100 || (attrLevel && m_attLevel > attrLevel)) return Position(0, 0);
float scale = font ? font->scale() : 1.0f;
Position shift(m_shift.x * (rtl * -2 + 1) + m_just, m_shift.y);
float tAdvance = m_advance.x + m_just;
if (isFinal && (coll = seg.collisionInfo(*this)))
{
const Position &collshift = coll->offset();
if (!(coll->flags() & SlotCollision::COLL_KERN) || rtl)
shift = shift + collshift;
}
const GlyphFace * glyphFace = seg.getFace()->glyphs().glyphSafe(glyph());
if (font)
{
scale = font->scale();
shift *= scale;
if (font->isHinted() && glyphFace)
tAdvance = (m_advance.x - glyphFace->theAdvance().x + m_just) * scale + font->advance(glyph());
else
tAdvance *= scale;
}
Position res;
m_position = base + shift;
if (isBase())
{
res = base + Position(tAdvance, m_advance.y * scale);
clusterMin = m_position.x;
}
else
{
float tAdv;
m_position += (m_attach - m_with) * scale;
tAdv = m_advance.x >= 0.5f ? m_position.x + tAdvance - shift.x : 0.f;
res = Position(tAdv, 0);
if ((m_advance.x >= 0.5f || m_position.x < 0) && m_position.x < clusterMin) clusterMin = m_position.x;
}
if (glyphFace)
{
Rect ourBbox = glyphFace->theBBox() * scale + m_position;
bbox = bbox.widen(ourBbox);
}
for (auto c = children(); c != end(); ++c)
{
auto tRes = c->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if ((isBase() || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
}
if (isParent())
{
Position tRes = children()->finalise(seg, font, m_position, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if ((isBase() || m_advance.x >= 0.5f) && tRes.x > res.x) res = tRes;
}
auto sibling = child_iterator(this);
if (!isBase() && ++sibling != end())
{
Position tRes = sibling->finalise(seg, font, base, bbox, attrLevel, clusterMin, rtl, isFinal, depth + 1);
if (tRes.x > res.x) res = tRes;
}
if (isBase() && clusterMin < base.x)
{
Position adj = Position(m_position.x - clusterMin, 0.);
res += adj;
m_position += adj;
if (isParent()) children()->floodShift(adj);
}
return res;
}
int32_t Slot::clusterMetric(const Segment & seg, metrics metric, uint8_t attrLevel, bool rtl) const
{
if (glyph() >= seg.getFace()->glyphs().numGlyphs())
return 0;
Rect bbox = seg.theGlyphBBoxTemporary(glyph());
auto & slot = const_cast<Slot &>(*this);
float range[2] = {0, 0};
for (auto s = cluster(); s != end(); ++s)
{
auto base = slot.update_cluster_metric(seg, rtl, false, range[0], range[1]);
bbox.widen(seg.getFace()->glyphs().glyphSafe(glyph())->theBBox() + base);
}
Position res = {range[1], m_advance.y};
// Position res = const_cast<Slot *>(this)->finalise(seg, NULL, base, bbox, attrLevel, clusterMin, rtl, false);
switch (metric)
{
case kgmetLsb :
return int32_t(bbox.bl.x);
case kgmetRsb :
return int32_t(res.x - bbox.tr.x);
case kgmetBbTop :
return int32_t(bbox.tr.y);
case kgmetBbBottom :
return int32_t(bbox.bl.y);
case kgmetBbLeft :
return int32_t(bbox.bl.x);
case kgmetBbRight :
return int32_t(bbox.tr.x);
case kgmetBbWidth :
return int32_t(bbox.tr.x - bbox.bl.x);
case kgmetBbHeight :
return int32_t(bbox.tr.y - bbox.bl.y);
case kgmetAdvWidth :
return int32_t(res.x);
case kgmetAdvHeight :
return int32_t(res.y);
default :
return 0;
}
}
#define SLOTGETCOLATTR(x) { SlotCollision *c = seg.collisionInfo(*this); return c ? int(c-> x) : 0; }
int Slot::getAttr(const Segment & seg, attrCode ind, uint8_t subindex) const
{
if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
{
int indx = ind - gr_slatJStretch;
return getJustify(seg, indx / 5, indx % 5);
}
switch (ind)
{
case gr_slatAdvX : return int(m_advance.x);
case gr_slatAdvY : return int(m_advance.y);
case gr_slatAttTo : return m_parent_offset ? 1 : 0;
case gr_slatAttX : return int(m_attach.x);
case gr_slatAttY : return int(m_attach.y);
case gr_slatAttXOff :
case gr_slatAttYOff : return 0;
case gr_slatAttWithX : return int(m_with.x);
case gr_slatAttWithY : return int(m_with.y);
case gr_slatAttWithXOff:
case gr_slatAttWithYOff:return 0;
case gr_slatAttLevel : return m_attLevel;
case gr_slatBreak : return seg.charinfo(m_original)->breakWeight();
case gr_slatCompRef : return 0;
case gr_slatDir : return seg.dir() & 1;
case gr_slatInsert : return insertBefore();
case gr_slatPosX : return int(m_position.x); // but need to calculate it
case gr_slatPosY : return int(m_position.y);
case gr_slatShiftX : return int(m_shift.x);
case gr_slatShiftY : return int(m_shift.y);
case gr_slatMeasureSol: return -1; // err what's this?
case gr_slatMeasureEol: return -1;
case gr_slatJWidth: return int(m_just);
case gr_slatUserDefnV1: subindex = 0; GR_FALLTHROUGH;
// no break
case gr_slatUserDefn : return subindex < m_attrs.num_attrs() ? m_attrs.user_attributes()[subindex] : 0;
case gr_slatSegSplit : return seg.charinfo(m_original)->flags() & 3;
case gr_slatBidiLevel: return m_bidiLevel;
case gr_slatColFlags : { SlotCollision *c = seg.collisionInfo(*this); return c ? c->flags() : 0; }
case gr_slatColLimitblx:SLOTGETCOLATTR(limit().bl.x)
case gr_slatColLimitbly:SLOTGETCOLATTR(limit().bl.y)
case gr_slatColLimittrx:SLOTGETCOLATTR(limit().tr.x)
case gr_slatColLimittry:SLOTGETCOLATTR(limit().tr.y)
case gr_slatColShiftx : SLOTGETCOLATTR(offset().x)
case gr_slatColShifty : SLOTGETCOLATTR(offset().y)
case gr_slatColMargin : SLOTGETCOLATTR(margin())
case gr_slatColMarginWt:SLOTGETCOLATTR(marginWt())
case gr_slatColExclGlyph:SLOTGETCOLATTR(exclGlyph())
case gr_slatColExclOffx:SLOTGETCOLATTR(exclOffset().x)
case gr_slatColExclOffy:SLOTGETCOLATTR(exclOffset().y)
case gr_slatSeqClass : SLOTGETCOLATTR(seqClass())
case gr_slatSeqProxClass:SLOTGETCOLATTR(seqProxClass())
case gr_slatSeqOrder : SLOTGETCOLATTR(seqOrder())
case gr_slatSeqAboveXoff:SLOTGETCOLATTR(seqAboveXoff())
case gr_slatSeqAboveWt: SLOTGETCOLATTR(seqAboveWt())
case gr_slatSeqBelowXlim:SLOTGETCOLATTR(seqBelowXlim())
case gr_slatSeqBelowWt: SLOTGETCOLATTR(seqBelowWt())
case gr_slatSeqValignHt:SLOTGETCOLATTR(seqValignHt())
case gr_slatSeqValignWt:SLOTGETCOLATTR(seqValignWt())
default : return 0;
}
}
#define SLOTCOLSETATTR(x) { \
SlotCollision *c = seg.collisionInfo(*this); \
if (c) { c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
#define SLOTCOLSETCOMPLEXATTR(t, y, x) { \
SlotCollision *c = seg.collisionInfo(*this); \
if (c) { \
const t &s = c-> y; \
c-> x ; c->setFlags(c->flags() & ~SlotCollision::COLL_KNOWN); } \
break; }
void Slot::setAttr(Segment & seg, attrCode ind, uint8_t subindex, int16_t value, const ShapingContext & ctxt)
{
if (ind == gr_slatUserDefnV1)
{
ind = gr_slatUserDefn;
subindex = 0;
if (seg.numAttrs() == 0)
return;
}
else if (ind >= gr_slatJStretch && ind < gr_slatJStretch + 20 && ind != gr_slatJWidth)
{
int indx = ind - gr_slatJStretch;
return setJustify(seg, indx / 5, indx % 5, value);
}
switch (ind)
{
case gr_slatAdvX : m_advance.x = value; break;
case gr_slatAdvY : m_advance.y = value; break;
case gr_slatAttTo :
{
const uint16_t idx = uint16_t(value);
if (idx < ctxt.map.size() && ctxt.map[idx].is_valid())
{
auto other = &*ctxt.map[idx];
if (other == this || other == attachedTo() || other->copied()) break;
if (!isBase()) { attachedTo()->remove_child(this); }
auto pOther = other;
int count = 0;
bool foundOther = false;
while (pOther)
{
++count;
if (pOther == this) foundOther = true;
pOther = pOther->attachedTo();
}
if (count < 100 && !foundOther && other->add_child(this))
{
attachTo(other);
if ((ctxt.dir != 0) ^ (idx > subindex))
m_with = Position(advance(), 0);
else // normal match to previous root
m_attach = Position(other->advance(), 0);
}
}
break;
}
case gr_slatAttX : m_attach.x = value; break;
case gr_slatAttY : m_attach.y = value; break;
case gr_slatAttXOff :
case gr_slatAttYOff : break;
case gr_slatAttWithX : m_with.x = value; break;
case gr_slatAttWithY : m_with.y = value; break;
case gr_slatAttWithXOff :
case gr_slatAttWithYOff : break;
case gr_slatAttLevel :
m_attLevel = byte(value);
break;
case gr_slatBreak :
seg.charinfo(m_original)->breakWeight(value);
break;
case gr_slatCompRef : break; // not sure what to do here
case gr_slatDir : break;
case gr_slatInsert :
insertBefore(value? true : false);
break;
case gr_slatPosX : break; // can't set these here
case gr_slatPosY : break;
case gr_slatShiftX : m_shift.x = value; break;
case gr_slatShiftY : m_shift.y = value; break;
case gr_slatMeasureSol : break;
case gr_slatMeasureEol : break;
case gr_slatJWidth : just(value); break;
case gr_slatSegSplit : seg.charinfo(m_original)->addflags(value & 3); break;
case gr_slatUserDefn : assert(subindex < m_attrs.num_attrs()); m_attrs.user_attributes()[subindex] = value; break;
case gr_slatColFlags : {
SlotCollision *c = seg.collisionInfo(*this);
if (c)
c->setFlags(value);
break; }
case gr_slatColLimitblx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(value, s.bl.y), s.tr)))
case gr_slatColLimitbly : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(Position(s.bl.x, value), s.tr)))
case gr_slatColLimittrx : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(value, s.tr.y))))
case gr_slatColLimittry : SLOTCOLSETCOMPLEXATTR(Rect, limit(), setLimit(Rect(s.bl, Position(s.tr.x, value))))
case gr_slatColMargin : SLOTCOLSETATTR(setMargin(value))
case gr_slatColMarginWt : SLOTCOLSETATTR(setMarginWt(value))
case gr_slatColExclGlyph : SLOTCOLSETATTR(setExclGlyph(value))
case gr_slatColExclOffx : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(value, s.y)))
case gr_slatColExclOffy : SLOTCOLSETCOMPLEXATTR(Position, exclOffset(), setExclOffset(Position(s.x, value)))
case gr_slatSeqClass : SLOTCOLSETATTR(setSeqClass(value))
case gr_slatSeqProxClass : SLOTCOLSETATTR(setSeqProxClass(value))
case gr_slatSeqOrder : SLOTCOLSETATTR(setSeqOrder(value))
case gr_slatSeqAboveXoff : SLOTCOLSETATTR(setSeqAboveXoff(value))
case gr_slatSeqAboveWt : SLOTCOLSETATTR(setSeqAboveWt(value))
case gr_slatSeqBelowXlim : SLOTCOLSETATTR(setSeqBelowXlim(value))
case gr_slatSeqBelowWt : SLOTCOLSETATTR(setSeqBelowWt(value))
case gr_slatSeqValignHt : SLOTCOLSETATTR(setSeqValignHt(value))
case gr_slatSeqValignWt : SLOTCOLSETATTR(setSeqValignWt(value))
default :
break;
}
}
int Slot::getJustify(const Segment & seg, uint8_t level, uint8_t subindex) const
{
if (level && level >= seg.silf()->numJustLevels()) return 0;
if (has_justify())
return m_attrs.just_info()[level * Slot::NUMJUSTPARAMS + subindex];
if (level >= seg.silf()->numJustLevels()) return 0;
Justinfo *jAttrs = seg.silf()->justAttrs() + level;
switch (subindex) {
case 0 : return seg.glyphAttr(gid(), jAttrs->attrStretch());
case 1 : return seg.glyphAttr(gid(), jAttrs->attrShrink());
case 2 : return seg.glyphAttr(gid(), jAttrs->attrStep());
case 3 : return seg.glyphAttr(gid(), jAttrs->attrWeight());
case 4 : return 0; // not been set yet, so clearly 0
default: return 0;
}
}
void Slot::setJustify(Segment & seg, uint8_t level, uint8_t subindex, int16_t value)
{
if (level && level >= seg.silf()->numJustLevels()) return;
if (!has_justify()) {
m_attrs.reserve(m_attrs.num_attrs(), std::max(1ul,seg.silf()->numJustLevels()));
init_just_infos(seg);
}
m_attrs.just_info()[level * Slot::NUMJUSTPARAMS + subindex] = value;
}
bool Slot::add_child(Slot *ap)
{
if (this == ap || ap->attachedTo() == this)
return false;
ap->attachTo(this);
m_flags.children = true;
for(auto first = min(ap, this)+1, last = max(ap, this)+1; first != last; ++first)
first->clusterhead(false);
return true;
}
bool Slot::remove_child(Slot *ap)
{
if (this == ap || !isParent() || !ap) return false;
if (ap->m_parent_offset > 0 && ap->m_flags.clusterhead)
{
for (auto first = min(ap, this)+1, last = max(ap, this)+1; first != last; ++first)
{
if (first->base() != ap)
{
first->clusterhead(true);
break;
}
}
}
ap->m_parent_offset = 0;
ap->m_flags.clusterhead = true;
m_flags.children = !m_flags.clusterhead || (children() != end());
return true;
}
void Slot::glyph(Segment & seg, uint16_t glyphid, const GlyphFace * theGlyph)
{
m_glyphid = glyphid;
m_bidiCls = -1;
if (!theGlyph)
{
theGlyph = seg.getFace()->glyphs().glyphSafe(glyphid);
if (!theGlyph)
{
m_realglyphid = 0;
m_advance = Position(0.,0.);
return;
}
}
m_realglyphid = theGlyph->attrs()[seg.silf()->aPseudo()];
if (m_realglyphid > seg.getFace()->glyphs().numGlyphs())
m_realglyphid = 0;
const GlyphFace *aGlyph = theGlyph;
if (m_realglyphid)
{
aGlyph = seg.getFace()->glyphs().glyphSafe(m_realglyphid);
if (!aGlyph) aGlyph = theGlyph;
}
m_advance = Position(aGlyph->theAdvance().x, 0.);
if (seg.silf()->aPassBits())
{
seg.mergePassBits(uint8_t(theGlyph->attrs()[seg.silf()->aPassBits()]));
if (seg.silf()->numPasses() > 16)
seg.mergePassBits(theGlyph->attrs()[seg.silf()->aPassBits()+1] << 16);
}
}
void Slot::floodShift(Position adj, int depth)
{
if (depth > 100)
return;
m_position += adj;
for (auto c = children(); c != end(); ++c)
c->floodShift(adj, depth + 1);
}
bool Slot::has_base(const Slot *base) const
{
for (auto p = attachedTo(); p; p = p->attachedTo())
if (p == base)
return true;
return false;
}

View File

@ -0,0 +1,121 @@
// /* GRAPHITE2 LICENSING
// Copyright 2010, SIL International
// All rights reserved.
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
// You should also have received a copy of the GNU Lesser General Public
// License along with this library in the file named "LICENSE".
// If not, write to the Free Software Foundation, 51 Franklin Street,
// Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
// internet at http://www.fsf.org/licenses/lgpl.html.
// Alternatively, the contents of this file may be used under the terms of the
// Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
// License, as published by the Free Software Foundation, either version 2
// of the License or (at your option) any later version.
// */
#include <cassert>
#include <cstddef>
#include <cstdint>
#include <utility>
#include "inc/SlotBuffer.h"
using namespace graphite2;
namespace
{
// @DEBUG_ATTRS: The number of extra per-slot user attributes beyond the
// number declared in by the font. Used to store persistent debug
// information that is not zeroed by SlotBuffer::_free_node().
// #if defined(GRAPHITE2_NTRACING)
constexpr size_t const DEBUG_ATTRS = 0;
// #else
// constexpr size_t const DEBUG_ATTRS = 1;
// #endif
}
SlotBuffer::SlotBuffer(SlotBuffer && rhs)
: _values{std::move(rhs._values)}
// _attrs_size{rhs._attrs_size}
{
}
SlotBuffer & SlotBuffer::operator = (SlotBuffer && rhs) {
_values = std::move(rhs._values);
return *this;
}
SlotBuffer::iterator SlotBuffer::insert(const_iterator pos, value_type const & slot)
{
return _values.insert(pos._i, slot);
}
SlotBuffer::iterator SlotBuffer::insert(const_iterator pos, value_type && slot)
{
return _values.insert(pos._i, std::forward<value_type>(slot));
}
void SlotBuffer::splice(const_iterator pos, SlotBuffer &other, const_iterator first, const_iterator last)
{
if (first != last)
{
auto l = std::distance(first, last);
auto i = _values.insert(pos._i, l, Slot());
while (first != last) *i = std::move(*first++);
other.erase(first, last);
}
}
auto SlotBuffer::erase(const_iterator pos) -> iterator
{
return _values.erase(pos._i);
}
auto SlotBuffer::erase(const_iterator first, const_iterator const last) -> iterator
{
return _values.erase(first._i, last._i);
}
namespace {
constexpr int8_t BIDI_MARK = 0x10;
template <class It>
inline It skip_bidi_mark(It first, It const last) {
while (first != last && first->bidiClass() == BIDI_MARK) ++first;
return first;
}
}
// reverse the clusters, but keep diacritics in their original order w.r.t their base character.
void SlotBuffer::reverse()
{
assert(!empty());
_storage out;
out.reserve(_values.size());
auto s = skip_bidi_mark(cbegin(), cend());
if (s == cend()) return;
while (s != cend())
{
auto const c = s;
s = skip_bidi_mark(++s, cend());
out.insert(out.cbegin(), c, s);
}
out.emplace_back(Slot(Slot::sentinal()));
_values = std::move(out);
}

View File

@ -0,0 +1,39 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
#include <cassert>
#include "inc/Sparse.h"
#include "inc/bits.h"
using namespace graphite2;
const sparse::chunk sparse::empty_chunk = {0,0};
sparse::~sparse() throw()
{
if (m_array.map == &empty_chunk) return;
free(m_array.values);
}
sparse::mapped_type sparse::operator [] (const key_type k) const throw()
{
mapped_type g = key_type(k/SIZEOF_CHUNK - m_nchunks) >> (sizeof k*8 - 1);
const chunk & c = m_array.map[g*k/SIZEOF_CHUNK];
const mask_t m = c.mask >> (SIZEOF_CHUNK - 1 - (k%SIZEOF_CHUNK));
g *= m & 1;
return g*m_array.values[g*(c.offset + bit_set_count(m >> 1))];
}
size_t sparse::capacity() const throw()
{
size_t n = m_nchunks,
s = 0;
for (const chunk *ci=m_array.map; n; --n, ++ci)
s += bit_set_count(ci->mask);
return s;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "inc/UtfCodec.h"
//using namespace graphite2;
namespace graphite2 {
}
using namespace graphite2;
const int8_t _utf_codec<8>::sz_lut[16] =
{
1,1,1,1,1,1,1,1, // 1 byte
0,0,0,0, // trailing byte
2,2, // 2 bytes
3, // 3 bytes
4 // 4 bytes
};
const byte _utf_codec<8>::mask_lut[5] = {0x7f, 0xff, 0x3f, 0x1f, 0x0f};

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This call threaded interpreter implmentation for machine.h
// Author: Tim Eves
// Build either this interpreter or the direct_machine implementation.
// The call threaded interpreter is portable across compilers and
// architectures as well as being useful to debug (you can set breakpoints on
// opcodes) but is slower that the direct threaded interpreter by a factor of 2
#include <cassert>
#include <cstring>
#include <graphite2/Segment.h>
#include "inc/Machine.h"
#include "inc/Segment.h"
#include "inc/ShapingContext.hpp"
#include "inc/Slot.h"
#include "inc/Rule.h"
#define registers vm::Machine::regbank & reg
// These are required by opcodes.h and should not be changed
#define STARTOP(name) bool name(registers) REGPARM(4);\
bool name(registers) {
#define ENDOP return (reg.sp - reg.sb)/Machine::STACK_MAX==0; \
}
#define EXIT(status) { push(status); return false; }
// This is required by opcode_table.h
#define do_(name) instr(name)
using namespace graphite2;
using namespace vm;
typedef bool (* ip_t)(registers);
// Pull in the opcode definitions
// We pull these into a private namespace so these otherwise common names dont
// pollute the toplevel namespace.
namespace {
#include "inc/opcodes.h"
}
Machine::stack_t Machine::run(const instr * program,
const byte * data,
const_slotref *& slot_in,
slotref & slot_out)
{
assert(program != nullptr);
// Declare virtual machine registers
regbank reg = {
program-1, // reg.ip
data, // reg.dp
_stack + Machine::STACK_GUARD, // reg.sp
_stack + Machine::STACK_GUARD, // reg.sb
_status, // reg.status
_ctxt, // reg.ctxt
_ctxt.segment, // reg.seg
slot_in, // reg.is
_ctxt.map.begin()+_ctxt.context(), // reg.isb
slot_out, // reg.os
0, // reg.flags
};
// Run the program
while ((reinterpret_cast<ip_t>(*++reg.ip))(reg)) {}
const stack_t ret = reg.sp == _stack+STACK_GUARD+1 ? *reg.sp-- : 0;
check_final_stack(reg.sp);
slot_in = reg.is;
slot_out = reg.os;
return ret;
}
// Pull in the opcode table
namespace {
#include "inc/opcode_table.h"
}
const opcode_t * Machine::getOpcodeTable() throw()
{
return opcode_table;
}

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This direct threaded interpreter implmentation for machine.h
// Author: Tim Eves
// Build either this interpreter or the call_machine implementation.
// The direct threaded interpreter is relies upon a gcc feature called
// labels-as-values so is only portable to compilers that support the
// extension (gcc only as far as I know) however it should build on any
// architecture gcc supports.
// This is twice as fast as the call threaded model and is likely faster on
// inorder processors with short pipelines and little branch prediction such
// as the ARM and possibly Atom chips.
#include <cassert>
#include <cstring>
#include "inc/Machine.h"
#include "inc/Segment.h"
#include "inc/ShapingContext.hpp"
#include "inc/Slot.h"
#include "inc/Rule.h"
#define STARTOP(name) name: {
#define ENDOP }; goto *((reg.sp - reg.sb)/Machine::STACK_MAX ? &&end : *++reg.ip);
#define EXIT(status) { push(status); goto end; }
#define do_(name) &&name
using namespace graphite2;
using namespace vm;
namespace {
// The GCC manual has this to say about labels as values:
// The &&foo expressions for the same label might have different values
// if the containing function is inlined or cloned. If a program relies
// on them being always the same, __attribute__((__noinline__,__noclone__))
// should be used to prevent inlining and cloning.
//
// is_return in Code.cpp relies on being able to do comparisons, so it needs
// them to be always the same.
//
// The GCC manual further adds:
// If &&foo is used in a static variable initializer, inlining and
// cloning is forbidden.
//
// In this file, &&foo *is* used in a static variable initializer, and it's not
// entirely clear whether this should prevent inlining of the function or not.
// In practice, though, clang 7 can end up inlining the function with ThinLTO,
// which breaks at least is_return. https://bugs.llvm.org/show_bug.cgi?id=39241
// So all in all, we need at least the __noinline__ attribute. __noclone__
// is not supported by clang.
__attribute__((__noinline__))
const void * direct_run(Machine::regbank * _reg = nullptr)
{
// We need to define and return to opcode table from within this function
// other inorder to take the addresses of the instruction bodies.
#include "inc/opcode_table.h"
if (_reg == nullptr)
return opcode_table;
auto & reg = *_reg;
// start the program
goto **reg.ip;
// Pull in the opcode definitions
#include "inc/opcodes.h"
end: return nullptr;
}
}
const opcode_t * Machine::getOpcodeTable() throw()
{
return static_cast<const opcode_t *>(direct_run());
}
Machine::stack_t Machine::run(const instr * program,
const byte * data,
const_slotref * & slot_in,
slotref & slot_out)
{
assert(program != 0);
// Declare virtual machine registers
regbank reg = {
program, // reg.ip
data, // reg.dp
_stack + Machine::STACK_GUARD, // reg.sp
_stack + Machine::STACK_GUARD, // reg.sb
_status, // reg.status
_ctxt, // reg.ctxt
_ctxt.segment, // reg.seg
slot_in, // reg.is
_ctxt.map.begin()+_ctxt.context(), // reg.isb
slot_out, // reg.os
0, // reg.flags
};
direct_run(&reg);
slot_in = reg.is;
slot_out = reg.os;
const stack_t ret = reg.sp == _stack+STACK_GUARD+1 ? *reg.sp-- : 0;
check_final_stack(reg.sp);
return ret;
}

View File

@ -0,0 +1,92 @@
# SPDX-License-Identifier: MIT
# Copyright 2011, SIL International, All rights reserved.
# Makefile helper file for those wanting to build Graphite2 using make
# The including makefile should set the following variables
# _NS Prefix to all variables this file creates (namespace)
# $(_NS)_MACHINE Set to direct or call. Set to direct if using gcc else
# set to call
# $(_NS)_BASE path to root of graphite2 project
#
# Returns:
# $(_NS)_SOURCES List of source files (with .cpp extension)
# $(_NS)_PRIVATE_HEADERS List of private header files (with .h extension)
# $(_NS)_PUBLIC_HEADERS List of public header files (with .h extension)
$(_NS)_SOURCES = \
$($(_NS)_BASE)/src/$($(_NS)_MACHINE)_machine.cpp \
$($(_NS)_BASE)/src/gr_char_info.cpp \
$($(_NS)_BASE)/src/gr_face.cpp \
$($(_NS)_BASE)/src/gr_features.cpp \
$($(_NS)_BASE)/src/gr_font.cpp \
$($(_NS)_BASE)/src/gr_logging.cpp \
$($(_NS)_BASE)/src/gr_segment.cpp \
$($(_NS)_BASE)/src/gr_slot.cpp \
$($(_NS)_BASE)/src/json.cpp \
$($(_NS)_BASE)/src/CmapCache.cpp \
$($(_NS)_BASE)/src/Code.cpp \
$($(_NS)_BASE)/src/Collider.cpp \
$($(_NS)_BASE)/src/Decompressor.cpp \
$($(_NS)_BASE)/src/Face.cpp \
$($(_NS)_BASE)/src/FeatureMap.cpp \
$($(_NS)_BASE)/src/FileFace.cpp \
$($(_NS)_BASE)/src/Font.cpp \
$($(_NS)_BASE)/src/GlyphCache.cpp \
$($(_NS)_BASE)/src/GlyphFace.cpp \
$($(_NS)_BASE)/src/Intervals.cpp \
$($(_NS)_BASE)/src/Justifier.cpp \
$($(_NS)_BASE)/src/NameTable.cpp \
$($(_NS)_BASE)/src/Pass.cpp \
$($(_NS)_BASE)/src/Position.cpp \
$($(_NS)_BASE)/src/Segment.cpp \
$($(_NS)_BASE)/src/Silf.cpp \
$($(_NS)_BASE)/src/Slot.cpp \
$($(_NS)_BASE)/src/Sparse.cpp \
$($(_NS)_BASE)/src/TtfUtil.cpp \
$($(_NS)_BASE)/src/UtfCodec.cpp
$(_NS)_PRIVATE_HEADERS = \
$($(_NS)_BASE)/src/inc/bits.h \
$($(_NS)_BASE)/src/inc/debug.h \
$($(_NS)_BASE)/src/inc/json.h \
$($(_NS)_BASE)/src/inc/CharInfo.h \
$($(_NS)_BASE)/src/inc/CmapCache.h \
$($(_NS)_BASE)/src/inc/Code.h \
$($(_NS)_BASE)/src/inc/Collider.h \
$($(_NS)_BASE)/src/inc/Compression.h \
$($(_NS)_BASE)/src/inc/Decompressor.h \
$($(_NS)_BASE)/src/inc/Endian.h \
$($(_NS)_BASE)/src/inc/Error.h \
$($(_NS)_BASE)/src/inc/Face.h \
$($(_NS)_BASE)/src/inc/FeatureMap.h \
$($(_NS)_BASE)/src/inc/FeatureVal.h \
$($(_NS)_BASE)/src/inc/FileFace.h \
$($(_NS)_BASE)/src/inc/Font.h \
$($(_NS)_BASE)/src/inc/GlyphCache.h \
$($(_NS)_BASE)/src/inc/GlyphFace.h \
$($(_NS)_BASE)/src/inc/Intervals.h \
$($(_NS)_BASE)/src/inc/list.hpp \
$($(_NS)_BASE)/src/inc/locale2lcid.h \
$($(_NS)_BASE)/src/inc/Machine.h \
$($(_NS)_BASE)/src/inc/Main.h \
$($(_NS)_BASE)/src/inc/NameTable.h \
$($(_NS)_BASE)/src/inc/opcode_table.h \
$($(_NS)_BASE)/src/inc/opcodes.h \
$($(_NS)_BASE)/src/inc/Pass.h \
$($(_NS)_BASE)/src/inc/Position.h \
$($(_NS)_BASE)/src/inc/Rule.h \
$($(_NS)_BASE)/src/inc/Segment.h \
$($(_NS)_BASE)/src/inc/Silf.h \
$($(_NS)_BASE)/src/inc/Slot.h \
$($(_NS)_BASE)/src/inc/Sparse.h \
$($(_NS)_BASE)/src/inc/TtfTypes.h \
$($(_NS)_BASE)/src/inc/TtfUtil.h \
$($(_NS)_BASE)/src/inc/UtfCodec.h \
$($(_NS)_BASE)/src/inc/vector.hpp
$(_NS)_PUBLIC_HEADERS = \
$($(_NS)_BASE)/include/graphite2/Font.h \
$($(_NS)_BASE)/include/graphite2/Log.h \
$($(_NS)_BASE)/include/graphite2/Segment.h \
$($(_NS)_BASE)/include/graphite2/Types.h

View File

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <cassert>
#include "graphite2/Segment.h"
#include "inc/CharInfo.h"
extern "C"
{
unsigned int gr_cinfo_unicode_char(const gr_char_info* p/*not NULL*/)
{
assert(p);
return p->unicodeChar();
}
int gr_cinfo_break_weight(const gr_char_info* p/*not NULL*/)
{
assert(p);
return p->breakWeight();
}
int gr_cinfo_after(const gr_char_info *p/*not NULL*/)
{
assert(p);
return p->after();
}
int gr_cinfo_before(const gr_char_info *p/*not NULL*/)
{
assert(p);
return p->before();
}
size_t gr_cinfo_base(const gr_char_info *p/*not NULL*/)
{
assert(p);
return p->base();
}
} // extern "C"

View File

@ -0,0 +1,233 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "graphite2/Font.h"
#include "inc/Face.h"
#include "inc/FileFace.h"
#include "inc/GlyphCache.h"
#include "inc/CmapCache.h"
#include "inc/Silf.h"
#include "inc/json.h"
using namespace graphite2;
#if !defined GRAPHITE2_NTRACING
extern json *global_log;
#endif
namespace
{
bool load_face(Face & face, unsigned int options)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::category _misc_cat(face.tele.misc);
#endif
Face::Table silf(face, Tag::Silf, 0x00050000);
if (!silf)
return false;
if (!face.readGlyphs(options))
return false;
if (silf)
{
if (!face.readFeatures() || !face.readGraphite(silf))
{
#if !defined GRAPHITE2_NTRACING
if (global_log)
{
*global_log << json::object
<< "type" << "fontload"
<< "failure" << face.error()
<< "context" << face.error_context()
<< json::close;
}
#endif
return false;
}
else
return true;
}
else
return false;
}
inline
uint32_t zeropad(const uint32_t x)
{
if (x == 0x20202020) return 0;
if ((x & 0x00FFFFFF) == 0x00202020) return x & 0xFF000000;
if ((x & 0x0000FFFF) == 0x00002020) return x & 0xFFFF0000;
if ((x & 0x000000FF) == 0x00000020) return x & 0xFFFFFF00;
return x;
}
}
extern "C" {
gr_face* gr_make_face_with_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int faceOptions)
//the appFaceHandle must stay alive all the time when the gr_face is alive. When finished with the gr_face, call destroy_face
{
if (ops == 0) return 0;
Face *res = new Face(appFaceHandle, *ops);
if (res && load_face(*res, faceOptions))
return static_cast<gr_face *>(res);
delete res;
return 0;
}
gr_face* gr_make_face(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int faceOptions)
{
const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL};
return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions);
}
gr_face* gr_make_face_with_seg_cache_and_ops(const void* appFaceHandle/*non-NULL*/, const gr_face_ops *ops, unsigned int , unsigned int faceOptions)
{
return gr_make_face_with_ops(appFaceHandle, ops, faceOptions);
}
gr_face* gr_make_face_with_seg_cache(const void* appFaceHandle/*non-NULL*/, gr_get_table_fn tablefn, unsigned int, unsigned int faceOptions)
{
const gr_face_ops ops = {sizeof(gr_face_ops), tablefn, NULL};
return gr_make_face_with_ops(appFaceHandle, &ops, faceOptions);
}
uint32_t gr_str_to_tag(const char *str)
{
uint32_t res = 0;
switch(max(strlen(str),size_t(4)))
{
case 4: res |= str[3]; GR_FALLTHROUGH;
case 3: res |= str[2] << 8; GR_FALLTHROUGH;
case 2: res |= str[1] << 16; GR_FALLTHROUGH;
case 1: res |= str[0] << 24; GR_FALLTHROUGH;
default: break;
}
return res;
}
void gr_tag_to_str(uint32_t tag, char *str)
{
if (!str) return;
*str++ = char(tag >> 24);
*str++ = char(tag >> 16);
*str++ = char(tag >> 8);
*str++ = char(tag);
*str = '\0';
}
gr_feature_val* gr_face_featureval_for_lang(const gr_face* pFace, uint32_t langname/*0 means clone default*/) //clones the features. if none for language, clones the default
{
assert(pFace);
langname = zeropad(langname);
return static_cast<gr_feature_val *>(pFace->theSill().cloneFeatures(langname));
}
const gr_feature_ref* gr_face_find_fref(const gr_face* pFace, uint32_t featId) //When finished with the FeatureRef, call destroy_FeatureRef
{
assert(pFace);
featId = zeropad(featId);
const FeatureRef* pRef = pFace->featureById(featId);
return static_cast<const gr_feature_ref*>(pRef);
}
unsigned short gr_face_n_fref(const gr_face* pFace)
{
assert(pFace);
return pFace->numFeatures();
}
const gr_feature_ref* gr_face_fref(const gr_face* pFace, uint16_t i) //When finished with the FeatureRef, call destroy_FeatureRef
{
assert(pFace);
const FeatureRef* pRef = pFace->feature(i);
return static_cast<const gr_feature_ref*>(pRef);
}
unsigned short gr_face_n_languages(const gr_face* pFace)
{
assert(pFace);
return pFace->theSill().numLanguages();
}
uint32_t gr_face_lang_by_index(const gr_face* pFace, uint16_t i)
{
assert(pFace);
return pFace->theSill().getLangName(i);
}
void gr_face_destroy(gr_face *face)
{
delete static_cast<Face*>(face);
}
uint16_t gr_face_name_lang_for_locale(gr_face *face, const char * locale)
{
if (face)
{
return face->languageForLocale(locale);
}
return 0;
}
unsigned short gr_face_n_glyphs(const gr_face* pFace)
{
return pFace->glyphs().numGlyphs();
}
const gr_faceinfo *gr_face_info(const gr_face *pFace, uint32_t script)
{
if (!pFace) return 0;
const Silf *silf = pFace->chooseSilf(script);
if (silf) return silf->silfInfo();
return 0;
}
int gr_face_is_char_supported(const gr_face* pFace, uint32_t usv, uint32_t script)
{
const Cmap & cmap = pFace->cmap();
uint16_t gid = cmap[usv];
if (!gid)
{
const Silf * silf = pFace->chooseSilf(script);
gid = silf->findPseudo(usv);
}
return (gid != 0);
}
#ifndef GRAPHITE2_NFILEFACE
gr_face* gr_make_file_face(const char *filename, unsigned int faceOptions)
{
FileFace* pFileFace = new FileFace(filename);
if (*pFileFace)
{
gr_face* pRes = gr_make_face_with_ops(pFileFace, &FileFace::ops, faceOptions);
if (pRes)
{
pRes->takeFileFace(pFileFace); //takes ownership
return pRes;
}
}
//error when loading
delete pFileFace;
return NULL;
}
gr_face* gr_make_file_face_with_seg_cache(const char* filename, unsigned int, unsigned int faceOptions) //returns NULL on failure. //TBD better error handling
//when finished with, call destroy_face
{
return gr_make_file_face(filename, faceOptions);
}
#endif //!GRAPHITE2_NFILEFACE
} // extern "C"

View File

@ -0,0 +1,115 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "graphite2/Font.h"
#include "inc/Face.h"
#include "inc/FeatureMap.h"
#include "inc/FeatureVal.h"
#include "inc/NameTable.h"
using namespace graphite2;
extern "C" {
uint16_t gr_fref_feature_value(const gr_feature_ref* pfeatureref, const gr_feature_val* feats) //returns 0 if either pointer is NULL
{
if (!pfeatureref || !feats) return 0;
return pfeatureref->getFeatureVal(*feats);
}
int gr_fref_set_feature_value(const gr_feature_ref* pfeatureref, uint16_t val, gr_feature_val* pDest)
{
if (!pfeatureref || !pDest) return 0;
return pfeatureref->applyValToFeature(val, *pDest);
}
uint32_t gr_fref_id(const gr_feature_ref* pfeatureref) //returns 0 if pointer is NULL
{
if (!pfeatureref)
return 0;
return pfeatureref->getId();
}
uint16_t gr_fref_n_values(const gr_feature_ref* pfeatureref)
{
if(!pfeatureref)
return 0;
return pfeatureref->getNumSettings();
}
int16_t gr_fref_value(const gr_feature_ref* pfeatureref, uint16_t settingno)
{
if(!pfeatureref || (settingno >= pfeatureref->getNumSettings()))
{
return 0;
}
return pfeatureref->getSettingValue(settingno);
}
void* gr_fref_label(const gr_feature_ref* pfeatureref, uint16_t *langId, gr_encform utf, uint32_t *length)
{
if(!pfeatureref)
{
langId = 0;
length = 0;
return NULL;
}
uint16_t label = pfeatureref->getNameId();
NameTable * names = pfeatureref->getFace().nameTable();
if (!names)
{
langId = 0;
length = 0;
return NULL;
}
return names->getName(*langId, label, utf, *length);
}
void* gr_fref_value_label(const gr_feature_ref*pfeatureref, uint16_t setting,
uint16_t *langId, gr_encform utf, uint32_t *length)
{
if(!pfeatureref || (setting >= pfeatureref->getNumSettings()))
{
langId = 0;
length = 0;
return NULL;
}
uint16_t label = pfeatureref->getSettingName(setting);
NameTable * names = pfeatureref->getFace().nameTable();
if (!names)
{
langId = 0;
length = 0;
return NULL;
}
return names->getName(*langId, label, utf, *length);
}
void gr_label_destroy(void * label)
{
free(label);
}
gr_feature_val* gr_featureval_clone(const gr_feature_val* pfeatures/*may be NULL*/)
{ //When finished with the Features, call features_destroy
return static_cast<gr_feature_val*>(pfeatures ? new Features(*pfeatures) : new Features);
}
void gr_featureval_destroy(gr_feature_val *p)
{
delete static_cast<Features*>(p);
}
} // extern "C"

View File

@ -0,0 +1,51 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "graphite2/Font.h"
#include "inc/Font.h"
using namespace graphite2;
extern "C" {
void gr_engine_version(int *nMajor, int *nMinor, int *nBugFix)
{
if (nMajor) *nMajor = GR2_VERSION_MAJOR;
if (nMinor) *nMinor = GR2_VERSION_MINOR;
if (nBugFix) *nBugFix = GR2_VERSION_BUGFIX;
}
gr_font* gr_make_font(float ppm/*pixels per em*/, const gr_face *face)
{
return gr_make_font_with_advance_fn(ppm, 0, 0, face);
}
gr_font* gr_make_font_with_ops(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, const gr_font_ops * font_ops, const gr_face * face/*needed for scaling*/)
{ //the appFontHandle must stay alive all the time when the gr_font is alive. When finished with the gr_font, call destroy_gr_font
if (face == 0 || ppm <= 0) return 0;
Font * const res = new Font(ppm, *face, appFontHandle, font_ops);
if (*res)
return static_cast<gr_font*>(res);
else
{
delete res;
return 0;
}
}
gr_font* gr_make_font_with_advance_fn(float ppm/*pixels per em*/, const void* appFontHandle/*non-NULL*/, gr_advance_fn getAdvance, const gr_face * face/*needed for scaling*/)
{
const gr_font_ops ops = {sizeof(gr_font_ops), getAdvance, NULL};
return gr_make_font_with_ops(ppm, appFontHandle, &ops, face);
}
void gr_font_destroy(gr_font *font)
{
delete static_cast<Font*>(font);
}
} // extern "C"

View File

@ -0,0 +1,236 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include <cstdio>
#include "graphite2/Log.h"
#include "inc/debug.h"
#include "inc/CharInfo.h"
#include "inc/Slot.h"
#include "inc/Segment.h"
#include "inc/json.h"
#include "inc/Collider.h"
#if defined _WIN32
#include "windows.h"
#endif
using namespace graphite2;
#if !defined GRAPHITE2_NTRACING
json *global_log = 0;
#endif
extern "C" {
bool gr_start_logging(GR_MAYBE_UNUSED gr_face * face, const char *log_path)
{
if (!log_path) return false;
#if !defined GRAPHITE2_NTRACING
gr_stop_logging(face);
#if defined _WIN32
int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, 0, 0);
if (n == 0 || n > MAX_PATH - 12) return false;
LPWSTR wlog_path = gralloc<WCHAR>(n);
if (!wlog_path) return false;
FILE *log = 0;
if (wlog_path && MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, log_path, -1, wlog_path, n))
log = _wfopen(wlog_path, L"wt");
free(wlog_path);
#else // _WIN32
FILE *log = fopen(log_path, "wt");
#endif // _WIN32
if (!log) return false;
if (face)
{
face->setLogger(log);
if (!face->logger()) return false;
*face->logger() << json::array;
#ifdef GRAPHITE2_TELEMETRY
*face->logger() << face->tele;
#endif
}
else
{
global_log = new json(log);
*global_log << json::array;
}
return true;
#else // GRAPHITE2_NTRACING
return false;
#endif // GRAPHITE2_NTRACING
}
bool graphite_start_logging(FILE * /* log */, GrLogMask /* mask */)
{
//#if !defined GRAPHITE2_NTRACING
// graphite_stop_logging();
//
// if (!log) return false;
//
// dbgout = new json(log);
// if (!dbgout) return false;
//
// *dbgout << json::array;
// return true;
//#else
return false;
//#endif
}
void gr_stop_logging(GR_MAYBE_UNUSED gr_face * face)
{
#if !defined GRAPHITE2_NTRACING
if (face && face->logger())
{
FILE * log = face->logger()->stream();
face->setLogger(0);
fclose(log);
}
else if (!face && global_log)
{
FILE * log = global_log->stream();
delete global_log;
global_log = 0;
fclose(log);
}
#endif
}
void graphite_stop_logging()
{
// if (dbgout) delete dbgout;
// dbgout = 0;
}
} // extern "C"
#ifdef GRAPHITE2_TELEMETRY
size_t * graphite2::telemetry::_category = 0UL;
#endif
#if !defined GRAPHITE2_NTRACING
#ifdef GRAPHITE2_TELEMETRY
json & graphite2::operator << (json & j, const telemetry & t) throw()
{
j << json::object
<< "type" << "telemetry"
<< "silf" << t.silf
<< "states" << t.states
<< "starts" << t.starts
<< "transitions" << t.transitions
<< "glyphs" << t.glyph
<< "code" << t.code
<< "misc" << t.misc
<< "total" << (t.silf + t.states + t.starts + t.transitions + t.glyph + t.code + t.misc)
<< json::close;
return j;
}
#else
json & graphite2::operator << (json & j, const telemetry &) throw()
{
return j;
}
#endif
json & graphite2::operator << (json & j, const CharInfo & ci) throw()
{
return j << json::object
<< "offset" << ci.base()
<< "unicode" << ci.unicodeChar()
<< "break" << ci.breakWeight()
<< "flags" << ci.flags()
<< "slot" << json::flat << json::object
<< "before" << ci.before()
<< "after" << ci.after()
<< json::close
<< json::close;
}
json & graphite2::operator << (json & j, const dslot & ds) throw()
{
assert(ds.first);
assert(ds.second);
const Segment & seg = *ds.first;
const Slot & s = *ds.second;
const SlotCollision *cslot = seg.collisionInfo(s);
j << json::object
<< "id" << objectid(SlotBuffer::const_iterator::from(ds.second))
<< "gid" << s.gid()
<< "charinfo" << json::flat << json::object
<< "original" << s.original()
<< "before" << s.before()
<< "after" << s.after()
<< json::close
<< "origin" << s.origin()
<< "shift" << Position(float(s.getAttr(seg, gr_slatShiftX, 0)),
float(s.getAttr(seg, gr_slatShiftY, 0)))
<< "advance" << s.advancePos()
<< "insert" << s.insertBefore()
<< "break" << s.getAttr(seg, gr_slatBreak, 0);
if (s.just() > 0)
j << "justification" << s.just();
if (s.bidiLevel() > 0)
j << "bidi" << s.bidiLevel();
if (!s.isBase())
j << "parent" << json::flat << json::object
<< "id" << objectid(SlotBuffer::const_iterator::from(s.attachedTo()))
<< "level" << s.getAttr(seg, gr_slatAttLevel, 0)
<< "offset" << s.attachOffset()
<< json::close;
j << "user" << json::flat << json::array;
for (size_t n = 0; n != seg.numAttrs(); ++n)
j << s.userAttrs()[n];
j << json::close;
if (s.isParent())
{
j << "children" << json::flat << json::array;
for (auto c = s.children(); c != s.end(); ++c)
j << objectid(SlotBuffer::const_iterator::from(&*c));
j << json::close;
}
if (cslot)
{
// Note: the reason for using Positions to lump together related attributes is to make the
// JSON output slightly more compact.
j << "collision" << json::flat << json::object
// << "shift" << cslot->shift() -- not used pass level, only within the collision routine itself
<< "offset" << cslot->offset()
<< "limit" << cslot->limit()
<< "flags" << cslot->flags()
<< "margin" << Position(cslot->margin(), cslot->marginWt())
<< "exclude" << cslot->exclGlyph()
<< "excludeoffset" << cslot->exclOffset();
if (cslot->seqOrder() != 0)
{
j << "seqclass" << Position(cslot->seqClass(), cslot->seqProxClass())
<< "seqorder" << cslot->seqOrder()
<< "seqabove" << Position(cslot->seqAboveXoff(), cslot->seqAboveWt())
<< "seqbelow" << Position(cslot->seqBelowXlim(), cslot->seqBelowWt())
<< "seqvalign" << Position(cslot->seqValignHt(), cslot->seqValignWt());
}
j << json::close;
}
return j << json::close;
}
void graphite2::objectid::set_name(void const * addr, uint16_t generation) noexcept
{
uint32_t const p = uint32_t(reinterpret_cast<size_t>(addr));
sprintf(name, "%.4x-%.2x-%.4hx", uint16_t(p >> 16), generation, uint16_t(p));
name[sizeof name-1] = 0;
}
#endif

View File

@ -0,0 +1,155 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "graphite2/Segment.h"
#include "inc/UtfCodec.h"
#include "inc/Segment.h"
#include "inc/Font.h"
using namespace graphite2;
namespace
{
gr_segment* makeAndInitialize(const Font *font, const Face *face, uint32_t script, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars, int dir)
{
if (script == 0x20202020) script = 0;
else if ((script & 0x00FFFFFF) == 0x00202020) script = script & 0xFF000000;
else if ((script & 0x0000FFFF) == 0x00002020) script = script & 0xFFFF0000;
else if ((script & 0x000000FF) == 0x00000020) script = script & 0xFFFFFF00;
// if (!font) return NULL;
Segment* pRes=new Segment(nChars, face, script, dir);
if (!pRes->read_text(face, pFeats, enc, pStart, nChars) || !pRes->runGraphite())
{
delete pRes;
return NULL;
}
pRes->finalise(font, true);
return static_cast<gr_segment*>(pRes);
}
template <typename utf_iter>
inline size_t count_unicode_chars(utf_iter first, const utf_iter last, const void **error)
{
size_t n_chars = 0;
uint32_t usv = 0;
if (last)
{
if (!first.validate(last))
{
if (error) *error = last - 1;
return 0;
}
for (;first != last; ++first, ++n_chars)
if ((usv = *first) == 0 || first.error()) break;
}
else
{
while ((usv = *first) != 0 && !first.error())
{
++first;
++n_chars;
}
}
if (error) *error = first.error() ? first : 0;
return n_chars;
}
}
extern "C" {
size_t gr_count_unicode_characters(gr_encform enc, const void* buffer_begin, const void* buffer_end/*don't go on or past end, If NULL then ignored*/, const void** pError) //Also stops on nul. Any nul is not in the count
{
assert(buffer_begin);
switch (enc)
{
case gr_utf8: return count_unicode_chars<utf8::const_iterator>(buffer_begin, buffer_end, pError); break;
case gr_utf16: return count_unicode_chars<utf16::const_iterator>(buffer_begin, buffer_end, pError); break;
case gr_utf32: return count_unicode_chars<utf32::const_iterator>(buffer_begin, buffer_end, pError); break;
default: return 0;
}
}
gr_segment* gr_make_seg(const gr_font *font, const gr_face *face, uint32_t script, const gr_feature_val* pFeats, gr_encform enc, const void* pStart, size_t nChars, int dir)
{
if (!face) return nullptr;
const gr_feature_val * tmp_feats = 0;
if (pFeats == 0)
pFeats = tmp_feats = static_cast<const gr_feature_val*>(face->theSill().cloneFeatures(0));
gr_segment * seg = makeAndInitialize(font, face, script, pFeats, enc, pStart, nChars, dir);
delete static_cast<const FeatureVal*>(tmp_feats);
return seg;
}
void gr_seg_destroy(gr_segment* p)
{
delete static_cast<Segment*>(p);
}
float gr_seg_advance_X(const gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return pSeg->advance().x;
}
float gr_seg_advance_Y(const gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return pSeg->advance().y;
}
unsigned int gr_seg_n_cinfo(const gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return static_cast<unsigned int>(pSeg->charInfoCount());
}
const gr_char_info* gr_seg_cinfo(const gr_segment* pSeg/*not NULL*/, unsigned int index/*must be <number_of_CharInfo*/)
{
assert(pSeg);
return static_cast<const gr_char_info*>(pSeg->charinfo(index));
}
unsigned int gr_seg_n_slots(const gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return static_cast<unsigned int>(pSeg->slotCount());
}
const gr_slot* gr_seg_first_slot(gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return pSeg->slots().begin().handle();
}
const gr_slot* gr_seg_last_slot(gr_segment* pSeg/*not NULL*/)
{
assert(pSeg);
return (--pSeg->slots().end()).handle();
}
float gr_seg_justify(gr_segment* pSeg/*not NULL*/, const gr_slot* pSlot/*not NULL*/, const gr_font *pFont, double width, enum gr_justFlags flags, const gr_slot *pFirst, const gr_slot *pLast)
{
assert(pSeg);
assert(pSlot);
auto const first = pFirst == nullptr ? pSeg->slots().begin() : const_cast<gr_slot *>(pFirst),
last = pLast == nullptr ? pSeg->slots().end() : ++SlotBuffer::iterator(const_cast<gr_slot *>(pLast));
return pSeg->justify(const_cast<gr_slot *>(pSlot), pFont, float(width), justFlags(flags), first, last);
}
} // extern "C"

View File

@ -0,0 +1,166 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#include "graphite2/Segment.h"
#include "inc/Segment.h"
#include "inc/Slot.h"
#include "inc/Font.h"
extern "C" {
const gr_slot* gr_slot_next_in_segment(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->index() == -1u ? nullptr : (++p).handle();
}
const gr_slot* gr_slot_prev_in_segment(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->index() == 0 ? nullptr : (--p).handle();
}
const gr_slot* gr_slot_attached_to(const gr_slot* h/*not NULL*/) //returns NULL iff base. If called repeatedly on result, will get to a base
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
auto slot = p->attachedTo();
return slot ? decltype(p)::from(slot).handle() : nullptr;
}
const gr_slot* gr_slot_first_attachment(const gr_slot* h/*not NULL*/) //returns NULL iff no attachments.
{ //if slot_first_attachment(p) is not NULL, then slot_attached_to(slot_first_attachment(p))==p.
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->children().handle();
}
const gr_slot* gr_slot_next_sibling_attachment(const gr_slot* h/*not NULL*/) //returns NULL iff no more attachments.
{ //if slot_next_sibling_attachment(p) is not NULL, then slot_attached_to(slot_next_sibling_attachment(p))==slot_attached_to(p).
assert(h);
graphite2::Slot::const_child_iterator p = h;
return (++p).handle();
}
unsigned short gr_slot_gid(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->glyph();
}
float gr_slot_origin_X(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->origin().x;
}
float gr_slot_origin_Y(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->origin().y;
}
float gr_slot_advance_X(const gr_slot* h/*not NULL*/, const gr_face *face, const gr_font *font)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
float scale = 1.0;
float res = p->advance();
if (font)
{
scale = font->scale();
int gid = p->glyph();
if (face && font->isHinted() && gid < face->glyphs().numGlyphs())
res = (res - face->glyphs().glyph(gid)->theAdvance().x) * scale + font->advance(gid);
else
res = res * scale;
}
return res;
}
float gr_slot_advance_Y(const gr_slot *h/*not NULL*/, GR_MAYBE_UNUSED const gr_face *face, const gr_font *font)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
float res = p->advancePos().y;
if (font)
return res * font->scale();
else
return res;
}
int gr_slot_before(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->before();
}
int gr_slot_after(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->after();
}
unsigned int gr_slot_index(const gr_slot *h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->index();
}
int gr_slot_attr(const gr_slot* h/*not NULL*/, const gr_segment* pSeg/*not NULL*/, gr_attrCode index, uint8_t subindex)
{
assert(h);
assert(pSeg);
graphite2::SlotBuffer::const_iterator p = h;
return p->getAttr(*pSeg, index, subindex);
}
int gr_slot_can_insert_before(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return (p->insertBefore())? 1 : 0;
}
int gr_slot_original(const gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::const_iterator p = h;
return p->original();
}
void gr_slot_linebreak_before(gr_slot* h/*not NULL*/)
{
assert(h);
graphite2::SlotBuffer::iterator p = h;
p->last(true);
}
#if 0 //what should this be
size_t id(const gr_slot* p/*not NULL*/)
{
return (size_t)p->id();
}
#endif
} // extern "C"

View File

@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "inc/Main.h"
namespace graphite2 {
class CharInfo
{
public:
CharInfo() : m_char(0), m_before(-1), m_after(-1), m_base(0), m_featureid(0), m_break(0), m_flags(0) {}
void init(int cid) { m_char = cid; }
unsigned int unicodeChar() const { return m_char; }
void feats(int offset) { m_featureid = offset; }
int fid() const { return m_featureid; }
int breakWeight() const { return m_break; }
void breakWeight(int val) { m_break = val; }
int after() const { return m_after; }
void after(int val) { m_after = val; }
int before() const { return m_before; }
void before(int val) { m_before = val; }
size_t base() const { return m_base; }
void base(size_t offset) { m_base = offset; }
void addflags(uint8_t val) { m_flags |= val; }
uint8_t flags() const { return m_flags; }
CLASS_NEW_DELETE
private:
int m_char; // Unicode character from character stream
int m_before; // slot index before us, comes before
int m_after; // slot index after us, comes after
size_t m_base; // offset into input string corresponding to this charinfo
uint8_t m_featureid; // index into features list in the segment
int8_t m_break; // breakweight coming from lb table
uint8_t m_flags; // 0,1 segment split.
};
} // namespace graphite2
struct gr_char_info : public graphite2::CharInfo {};

View File

@ -0,0 +1,59 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "inc/Main.h"
#include "inc/Face.h"
namespace graphite2 {
class Face;
class Cmap
{
public:
virtual ~Cmap() throw() {}
virtual uint16_t operator [] (const uint32_t) const throw() { return 0; }
virtual operator bool () const throw() { return false; }
CLASS_NEW_DELETE;
};
class DirectCmap : public Cmap
{
DirectCmap(const DirectCmap &);
DirectCmap & operator = (const DirectCmap &);
public:
DirectCmap(const Face &);
virtual uint16_t operator [] (const uint32_t usv) const throw();
virtual operator bool () const throw();
CLASS_NEW_DELETE;
private:
const Face::Table _cmap;
const void * _smp,
* _bmp;
};
class CachedCmap : public Cmap
{
CachedCmap(const CachedCmap &);
CachedCmap & operator = (const CachedCmap &);
public:
CachedCmap(const Face &);
virtual ~CachedCmap() throw();
virtual uint16_t operator [] (const uint32_t usv) const throw();
virtual operator bool () const throw();
CLASS_NEW_DELETE;
private:
bool m_isBmpOnly;
uint16_t ** m_blocks;
};
} // namespace graphite2

View File

@ -0,0 +1,148 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This class represents loaded graphite stack machine code. It performs
// basic sanity checks, on the incoming code to prevent more obvious problems
// from crashing graphite.
// Author: Tim Eves
#pragma once
#include <cassert>
#include <graphite2/Types.h>
#include "inc/Main.h"
#include "inc/Machine.h"
namespace graphite2 {
class Silf;
class Face;
enum passtype {
PASS_TYPE_UNKNOWN = 0,
PASS_TYPE_LINEBREAK,
PASS_TYPE_SUBSTITUTE,
PASS_TYPE_POSITIONING,
PASS_TYPE_JUSTIFICATION
};
namespace vm {
class Machine::Code
{
public:
enum status_t
{
loaded,
alloc_failed,
invalid_opcode,
unimplemented_opcode_used,
out_of_range_data,
jump_past_end,
arguments_exhausted,
missing_return,
nested_context_item,
underfull_stack
};
private:
class decoder;
instr * _code;
byte * _data;
size_t _data_size,
_instr_count;
byte _max_ref;
mutable status_t _status;
bool _constraint,
_modify,
_delete;
mutable bool _own;
void release_buffers() throw ();
void failure(const status_t) throw();
public:
static size_t estimateCodeDataOut(size_t num_bytecodes, int nRules, int nSlots);
Code() throw();
Code(bool is_constraint, const byte * bytecode_begin, const byte * const bytecode_end,
uint8_t pre_context, uint16_t rule_length, const Silf &, const Face &,
enum passtype pt, byte * * const _out = 0);
Code(const Machine::Code &) throw();
~Code() throw();
Code & operator=(const Code &rhs) throw();
operator bool () const throw() { return _code && status() == loaded; }
status_t status() const throw() { return _status; }
bool constraint() const throw() { return _constraint; }
size_t dataSize() const throw() { return _data_size; }
size_t instructionCount() const throw() { return _instr_count; }
bool immutable() const throw() { return !(_delete || _modify); }
bool deletes() const throw() { return _delete; }
size_t maxRef() const throw() { return _max_ref; }
void externalProgramMoved(ptrdiff_t) throw();
int32_t run(Machine &m, ShapingContext::map_t::iterator & slot_in, slotref & slot_out) const;
CLASS_NEW_DELETE;
};
inline
size_t Machine::Code::estimateCodeDataOut(size_t n_bc, int nRules, int nSlots)
{
// max is: all codes are instructions + 1 for each rule + max tempcopies
// allocate space for separate maximal code and data then merge them later
return (n_bc + nRules + nSlots) * sizeof(instr) + n_bc * sizeof(byte);
}
inline Machine::Code::Code() throw()
: _code(0), _data(0), _data_size(0), _instr_count(0), _max_ref(0),
_status(loaded), _constraint(false), _modify(false), _delete(false),
_own(false)
{
}
inline Machine::Code::Code(const Machine::Code &obj) throw ()
: _code(obj._code),
_data(obj._data),
_data_size(obj._data_size),
_instr_count(obj._instr_count),
_max_ref(obj._max_ref),
_status(obj._status),
_constraint(obj._constraint),
_modify(obj._modify),
_delete(obj._delete),
_own(obj._own)
{
obj._own = false;
}
inline Machine::Code & Machine::Code::operator=(const Machine::Code &rhs) throw() {
if (_instr_count > 0)
release_buffers();
_code = rhs._code;
_data = rhs._data;
_data_size = rhs._data_size;
_instr_count = rhs._instr_count;
_status = rhs._status;
_constraint = rhs._constraint;
_modify = rhs._modify;
_delete = rhs._delete;
_own = rhs._own;
rhs._own = false;
return *this;
}
inline void Machine::Code::externalProgramMoved(ptrdiff_t dist) throw()
{
if (_code && !_own)
{
_code += dist / signed(sizeof(instr));
_data += dist;
}
}
} // namespace vm
} // namespace graphite2

View File

@ -0,0 +1,224 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdint>
#include <iterator>
#include "inc/vector.hpp"
#include "inc/Position.h"
#include "inc/Intervals.h"
#include "inc/debug.h"
namespace graphite2 {
class json;
class Slot;
class Segment;
#define SLOTCOLSETUINTPROP(x, y) uint16_t x() const { return _ ##x; } void y (uint16_t v) { _ ##x = v; }
#define SLOTCOLSETINTPROP(x, y) int16_t x() const { return _ ##x; } void y (int16_t v) { _ ##x = v; }
#define SLOTCOLSETPOSITIONPROP(x, y) const Position &x() const { return _ ##x; } void y (const Position &v) { _ ##x = v; }
// Slot attributes related to collision-fixing
class SlotCollision
{
public:
enum {
// COLL_TESTONLY = 0, // default - test other glyphs for collision with this one, but don't move this one
COLL_FIX = 1, // fix collisions involving this glyph
COLL_IGNORE = 2, // ignore this glyph altogether
COLL_START = 4, // start of range of possible collisions
COLL_END = 8, // end of range of possible collisions
COLL_KERN = 16, // collisions with this glyph are fixed by adding kerning space after it
COLL_ISCOL = 32, // this glyph has a collision
COLL_KNOWN = 64, // we've figured out what's happening with this glyph
COLL_ISSPACE = 128, // treat this glyph as a space with regard to kerning
COLL_TEMPLOCK = 256, // Lock glyphs that have been given priority positioning
////COLL_JUMPABLE = 128, // moving glyphs may jump this stationary glyph in any direction - DELETE
////COLL_OVERLAP = 256, // use maxoverlap to restrict - DELETE
};
// Behavior for the collision.order attribute. To GDL this is an enum, to us it's a bitfield, with only 1 bit set
// Allows for easier inversion.
enum {
SEQ_ORDER_LEFTDOWN = 1,
SEQ_ORDER_RIGHTUP = 2,
SEQ_ORDER_NOABOVE = 4,
SEQ_ORDER_NOBELOW = 8,
SEQ_ORDER_NOLEFT = 16,
SEQ_ORDER_NORIGHT = 32
};
SlotCollision(Segment &seg, Slot &slot);
void initFromSlot(Segment &seg, Slot &slot);
const Rect &limit() const { return _limit; }
void setLimit(const Rect &r) { _limit = r; }
SLOTCOLSETPOSITIONPROP(shift, setShift)
SLOTCOLSETPOSITIONPROP(offset, setOffset)
SLOTCOLSETPOSITIONPROP(exclOffset, setExclOffset)
SLOTCOLSETUINTPROP(margin, setMargin)
SLOTCOLSETUINTPROP(marginWt, setMarginWt)
SLOTCOLSETUINTPROP(flags, setFlags)
SLOTCOLSETUINTPROP(exclGlyph, setExclGlyph)
SLOTCOLSETUINTPROP(seqClass, setSeqClass)
SLOTCOLSETUINTPROP(seqProxClass, setSeqProxClass)
SLOTCOLSETUINTPROP(seqOrder, setSeqOrder)
SLOTCOLSETINTPROP(seqAboveXoff, setSeqAboveXoff)
SLOTCOLSETUINTPROP(seqAboveWt, setSeqAboveWt)
SLOTCOLSETINTPROP(seqBelowXlim, setSeqBelowXlim)
SLOTCOLSETUINTPROP(seqBelowWt, setSeqBelowWt)
SLOTCOLSETUINTPROP(seqValignHt, setSeqValignHt)
SLOTCOLSETUINTPROP(seqValignWt, setSeqValignWt)
float getKern(int dir) const;
bool ignore() const;
private:
Rect _limit;
Position _shift; // adjustment within the given pass
Position _offset; // total adjustment for collisions
Position _exclOffset;
uint16_t _margin;
uint16_t _marginWt;
uint16_t _flags;
uint16_t _exclGlyph;
uint16_t _seqClass;
uint16_t _seqProxClass;
uint16_t _seqOrder;
int16_t _seqAboveXoff;
uint16_t _seqAboveWt;
int16_t _seqBelowXlim;
uint16_t _seqBelowWt;
uint16_t _seqValignHt;
uint16_t _seqValignWt;
}; // end of class SlotColllision
struct BBox;
struct SlantBox;
class ShiftCollider
{
public:
typedef std::pair<float, float> fpair;
typedef vector<fpair> vfpairs;
typedef vfpairs::iterator ivfpairs;
ShiftCollider(json *dbgout);
~ShiftCollider() throw() { };
bool initSlot(Segment & seg, Slot & aSlot, const Rect &constraint,
float margin, float marginMin, const Position &currShift,
const Position &currOffset, int dir, GR_MAYBE_UNUSED json * const dbgout);
bool mergeSlot(Segment & seg, Slot & slot, const SlotCollision *cinfo, const Position &currShift, bool isAfter,
bool sameCluster, bool &hasCol, bool isExclusion, GR_MAYBE_UNUSED json * const dbgout);
Position resolve(Segment &seg, bool &isCol, GR_MAYBE_UNUSED json * const dbgout);
void addBox_slope(bool isx, const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, float weight, float m, bool minright, int mode);
void removeBox(const Rect &box, const BBox &bb, const SlantBox &sb, const Position &org, int mode);
const Position &origin() const { return _origin; }
#if !defined GRAPHITE2_NTRACING
void outputJsonDbg(json * const dbgout, Segment &seg, int axis);
void outputJsonDbgStartSlot(json * const dbgout, Segment &seg);
void outputJsonDbgEndSlot(json * const dbgout, Position resultPos, int bestAxis, bool isCol);
void outputJsonDbgOneVector(json * const dbgout, Segment &seg, int axis, float tleft, float bestCost, float bestVal);
void outputJsonDbgRawRanges(json * const dbgout, int axis);
void outputJsonDbgRemovals(json * const dbgout, int axis, Segment &seg);
#endif
CLASS_NEW_DELETE;
protected:
Zones _ranges[4]; // possible movements in 4 directions (horizontally, vertically, diagonally);
Slot * _target; // the glyph to fix
Rect _limit;
Position _currShift;
Position _currOffset;
Position _origin; // Base for all relative calculations
float _margin;
float _marginWt;
float _len[4];
uint16_t _seqClass;
uint16_t _seqProxClass;
uint16_t _seqOrder;
//bool _scraping[4];
}; // end of class ShiftCollider
inline
ShiftCollider::ShiftCollider(GR_MAYBE_UNUSED json *dbgout)
: _target(0),
_margin(0.0),
_marginWt(0.0),
_seqClass(0),
_seqProxClass(0),
_seqOrder(0)
{
#if !defined GRAPHITE2_NTRACING
for (int i = 0; i < 4; ++i)
_ranges[i].setdebug(dbgout);
#endif
}
class KernCollider
{
public:
KernCollider(json *dbg);
~KernCollider() throw() { };
bool initSlot(Segment & seg, Slot & aSlot, const Rect &constraint, float margin,
const Position &currShift, const Position &offsetPrev, int dir,
float ymin, float ymax, json * const dbgout);
bool mergeSlot(Segment & seg, Slot & slot, const Position &currShift, float currSpace, int dir, json * const dbgout);
Position resolve(Segment & seg, Slot & slot, int dir, json * const dbgout);
void shift(const Position &mv, int dir);
CLASS_NEW_DELETE;
private:
Slot * _target; // the glyph to fix
Rect _limit;
float _margin;
Position _offsetPrev; // kern from a previous pass
Position _currShift; // NOT USED??
float _miny; // y-coordinates offset by global slot position
float _maxy;
vector<float> _edges; // edges of horizontal slices
float _sliceWidth; // width of each slice
float _mingap;
float _xbound; // max or min edge
bool _hit;
#if !defined GRAPHITE2_NTRACING
// Debugging
Segment * _seg;
vector<float> _nearEdges; // closest potential collision in each slice
vector<Slot*> _slotNear;
#endif
}; // end of class KernCollider
inline
float sqr(float x) {
return x * x;
}
inline
KernCollider::KernCollider(GR_MAYBE_UNUSED json *dbg)
: _target(0),
_margin(0.0f),
_miny(-1e38f),
_maxy(1e38f),
_sliceWidth(0.0f),
_mingap(0.0f),
_xbound(0.0),
_hit(false)
{
#if !defined GRAPHITE2_NTRACING
_seg = 0;
#endif
};
}; // end of namespace graphite2

View File

@ -0,0 +1,81 @@
// SPDX-License-Identifier: MIT
// Copyright 2015, SIL International, All rights reserved.
#pragma once
#include <cassert>
#include <cstddef>
#include <cstring>
namespace
{
#if defined(_MSC_VER)
typedef unsigned __int8 u8;
typedef unsigned __int16 u16;
typedef unsigned __int32 u32;
typedef unsigned __int64 u64;
#else
#include <stdint.h>
typedef uint8_t u8;
typedef uint16_t u16;
typedef uint32_t u32;
typedef uint64_t u64;
#endif
ptrdiff_t const MINMATCH = 4,
LASTLITERALS = 5,
MINCODA = LASTLITERALS+1,
MINSRCSIZE = 13;
template<int S>
inline
void unaligned_copy(void * d, void const * s) {
::memcpy(d, s, S);
}
inline
size_t align(size_t p) {
return (p + sizeof(unsigned long)-1) & ~(sizeof(unsigned long)-1);
}
inline
u8 * safe_copy(u8 * d, u8 const * s, size_t n) {
while (n--) *d++ = *s++;
return d;
}
inline
u8 * overrun_copy(u8 * d, u8 const * s, size_t n) {
size_t const WS = sizeof(unsigned long);
u8 const * e = s + n;
do
{
unaligned_copy<WS>(d, s);
d += WS;
s += WS;
}
while (s < e);
d-=(s-e);
return d;
}
inline
u8 * fast_copy(u8 * d, u8 const * s, size_t n) {
size_t const WS = sizeof(unsigned long);
size_t wn = n/WS;
while (wn--)
{
unaligned_copy<WS>(d, s);
d += WS;
s += WS;
}
n &= WS-1;
return safe_copy(d, s, n);
}
} // end of anonymous namespace

View File

@ -0,0 +1,31 @@
// SPDX-License-Identifier: MIT
// Copyright 2015, SIL International, All rights reserved.
#pragma once
#include <cstddef>
namespace lz4
{
// decompress an LZ4 block
// Parameters:
// @in - Input buffer containing an LZ4 block.
// @in_size - Size of the input LZ4 block in bytes.
// @out - Output buffer to hold decompressed results.
// @out_size - The size of the buffer pointed to by @out.
// Invariants:
// @in - This buffer must be at least 1 machine word in length,
// regardless of the actual LZ4 block size.
// @in_size - This must be at least 4 and must also be <= to the
// allocated buffer @in.
// @out - This must be bigger than the input buffer and at least
// 13 bytes.
// @out_size - Must always be big enough to hold the expected size.
// Return:
// -1 - Decompression failed.
// size - Actual number of bytes decompressed.
int decompress(void const *in, size_t in_size, void *out, size_t out_size);
} // end of namespace shrinker

View File

@ -0,0 +1,86 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
/*
Description:
A set of fast template based decoders for decoding values of any C integer
type up to long int size laid out with most significant byte first or least
significant byte first (aka big endian or little endian). These are CPU
byte order agnostic and will function the same regardless of the CPUs native
byte order.
Being template based means if the either le or be class is not used then
template code of unused functions will not be instantiated by the compiler
and thus shouldn't cause any overhead.
*/
#include <cstddef>
#pragma once
class be
{
template<int S>
inline static unsigned long int _peek(const unsigned char * p) {
return _peek<S/2>(p) << (S/2)*8 | _peek<S/2>(p+S/2);
}
public:
template<typename T>
inline static T peek(const void * p) {
return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
}
template<typename T>
inline static T read(const unsigned char * &p) {
const T r = T(_peek<sizeof(T)>(p));
p += sizeof r;
return r;
}
template<typename T>
inline static T swap(const T x) {
return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
}
template<typename T>
inline static void skip(const unsigned char * &p, size_t n=1) {
p += sizeof(T)*n;
}
};
template<>
inline unsigned long int be::_peek<1>(const unsigned char * p) { return *p; }
class le
{
template<int S>
inline static unsigned long int _peek(const unsigned char * p) {
return _peek<S/2>(p) | _peek<S/2>(p+S/2) << (S/2)*8;
}
public:
template<typename T>
inline static T peek(const void * p) {
return T(_peek<sizeof(T)>(static_cast<const unsigned char *>(p)));
}
template<typename T>
inline static T read(const unsigned char * &p) {
const T r = T(_peek<sizeof(T)>(p));
p += sizeof r;
return r;
}
template<typename T>
inline static T swap(const T x) {
return T(_peek<sizeof(T)>(reinterpret_cast<const unsigned char *>(&x)));
}
template<typename T>
inline static void skip(const unsigned char * &p, size_t n=1) {
p += sizeof(T)*n;
}
};
template<>
inline unsigned long int le::_peek<1>(const unsigned char * p) { return *p; }

View File

@ -0,0 +1,111 @@
// SPDX-License-Identifier: MIT
// Copyright 2013, SIL International, All rights reserved.
#pragma once
// numbers are explicitly assigned for future proofing
namespace graphite2
{
class Error
{
public:
Error() : _e(0) {};
operator bool() { return (_e != 0); }
int error() { return _e; }
void error(int e) { _e = e; }
bool test(bool pr, int err) { return (_e = int(pr) * err); }
private:
int _e;
};
enum errcontext {
EC_READGLYPHS = 1, // while reading glyphs
EC_READSILF = 2, // in Silf table
EC_ASILF = 3, // in Silf %d
EC_APASS = 4, // in Silf %d, pass %d
EC_PASSCCODE = 5, // in pass constraint code for Silf %d, pass %d
EC_ARULE = 6, // in Silf %d, pass %d, rule %d
EC_ASTARTS = 7, // in Silf %d, pass %d, start state %d
EC_ATRANS = 8, // in Silf %d, pass %d, fsm state %d
EC_ARULEMAP = 9 // in Silf %d, pass %d, state %d
};
enum errors {
E_OUTOFMEM = 1, // Out of memory
E_NOGLYPHS = 2, // There are no glyphs in the font
E_BADUPEM = 3, // The units per em for the font is bad (0)
E_BADCMAP = 4, // The font does not contain any useful cmaps
E_NOSILF = 5, // Missing Silf table
E_TOOOLD = 6, // Silf table version is too old
E_BADSIZE = 7, // context object has the wrong structural size
// Silf Subtable Errors take a Silf subtable number * 256 in the context
E_BADMAXGLYPH = 8, // Silf max glyph id is too high
E_BADNUMJUSTS = 9, // Number of Silf justification blocks is too high
E_BADENDJUSTS = 10, // Silf justification blocks take too much of the Silf table space
E_BADCRITFEATURES = 11, // Critical features section in a Silf table is too big
E_BADSCRIPTTAGS = 12, // Silf script tags area is too big
E_BADAPSEUDO = 13, // The pseudo glyph attribute number is too high
E_BADABREAK = 14, // The linebreak glyph attribute number is too high
E_BADABIDI = 15, // The bidi glyph attribute number is too high
E_BADAMIRROR = 16, // The mirrored glyph attribute number is too high
E_BADNUMPASSES = 17, // The number of passes is > 128
E_BADPASSESSTART = 18, // The Silf table is too small to hold any passes
E_BADPASSBOUND = 19, // The positioning pass number is too low or the substitution pass number is too high
E_BADPPASS = 20, // The positioning pass number is too high
E_BADSPASS = 21, // the substitution pass number is too high
E_BADJPASSBOUND = 22, // the justification pass must be higher than the positioning pass
E_BADJPASS = 23, // the justification pass is too high
E_BADALIG = 24, // the number of initial ligature component glyph attributes is too high
E_BADBPASS = 25, // the bidi pass number is specified and is either too high or too low
E_BADNUMPSEUDO = 26, // The number of pseudo glyphs is too high
E_BADCLASSSIZE = 27, // The size of the classes block is bad
E_TOOMANYLINEAR = 28, // The number of linear classes in the silf table is too high
E_CLASSESTOOBIG = 29, // There are too many classes for the space allocated in the Silf subtable
E_MISALIGNEDCLASSES = 30, // The class offsets in the class table don't line up with the number of classes
E_HIGHCLASSOFFSET = 31, // The class offsets point out of the class table
E_BADCLASSOFFSET = 32, // A class offset is less than one following it
E_BADCLASSLOOKUPINFO = 33, // The search header info for a non-linear class has wrong values in it
// Pass subtable errors. Context has pass number * 65536
E_BADPASSSTART = 34, // The start offset for a particular pass is bad
E_BADPASSEND = 35, // The end offset for a particular pass is bad
E_BADPASSLENGTH = 36, // The length of the pass is too small
E_BADNUMTRANS = 37, // The number of transition states in the fsm is bad
E_BADNUMSUCCESS = 38, // The number of success states in the fsm is bad
E_BADNUMSTATES = 39, // The number of states in the fsm is bad
E_NORANGES = 40, // There are no columns in the fsm
E_BADRULEMAPLEN = 41, // The size of the success state to rule mapping is bad
E_BADCTXTLENBOUNDS = 42, // The precontext maximum is greater than its minimum
E_BADCTXTLENS = 43, // The lists of rule lengths or pre context lengths is bad
E_BADPASSCCODEPTR = 44, // The pass constraint code position does not align with where the forward reference says it should be
E_BADRULECCODEPTR = 45, // The rule constraint code position does not align with where the forward reference says it should be
E_BADCCODELEN = 46, // Bad rule/pass constraint code length
E_BADACTIONCODEPTR = 47, // The action code position does not align with where the forward reference says it should be
E_MUTABLECCODE = 48, // Constraint code edits slots. It shouldn't.
E_BADSTATE = 49, // Bad state transition referencing an illegal state
E_BADRULEMAPPING = 50, // The structure of the rule mapping is bad
E_BADRANGE = 51, // Bad column range structure including a glyph in more than one column
E_BADRULENUM = 52, // A reference to a rule is out of range (too high)
E_BADACOLLISION = 53, // Bad Silf table collision attribute number (too high)
E_BADEMPTYPASS = 54, // Can't have empty passes (no rules) except for collision passes
E_BADSILFVERSION = 55, // The Silf table has a bad version (probably too high)
E_BADCOLLISIONPASS = 56, // Collision flags set on a non positioning pass
E_BADNUMCOLUMNS = 57, // Arbitrarily limit number of columns in fsm
// Code errors
E_CODEFAILURE = 60, // Base code error. The following subcodes must align with Machine::Code::status_t in Code.h
E_CODEALLOC = 61, // Out of memory
E_INVALIDOPCODE = 62, // Invalid op code
E_UNIMPOPCODE = 63, // Unimplemented op code encountered
E_OUTOFRANGECODE = 64, // Code argument out of range
E_BADJUMPCODE = 65, // Code jumps past end of op codes
E_CODEBADARGS = 66, // Code arguments exhausted
E_CODENORETURN = 67, // Missing return type op code at end of code
E_CODENESTEDCTXT = 68, // Nested context encountered in code
// Compression errors
E_BADSCHEME = 69,
E_SHRINKERFAILED = 70,
};
}

View File

@ -0,0 +1,202 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdio>
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/FeatureMap.h"
#include "inc/TtfUtil.h"
#include "inc/Silf.h"
#include "inc/Error.h"
namespace graphite2 {
class Cmap;
class FileFace;
class GlyphCache;
class NameTable;
class json;
class Font;
using TtfUtil::Tag;
// These are the actual tags, as distinct from the consecutive IDs in TtfUtil.h
class Face
{
// Prevent any kind of copying
Face(const Face&);
Face& operator=(const Face&);
public:
class Table;
static float default_glyph_advance(const void* face_ptr, uint16_t glyphid);
Face(const void* appFaceHandle/*non-NULL*/, const gr_face_ops & ops);
virtual ~Face();
virtual bool runGraphite(Segment &seg, const Silf *silf) const;
public:
bool readGlyphs(uint32_t faceOptions);
bool readGraphite(const Table & silf);
bool readFeatures();
void takeFileFace(FileFace* pFileFace/*takes ownership*/);
const SillMap & theSill() const;
const GlyphCache & glyphs() const;
Cmap & cmap() const;
NameTable * nameTable() const;
void setLogger(FILE *log_file);
json * logger() const throw();
const Silf * chooseSilf(uint32_t script) const;
uint16_t languageForLocale(const char * locale) const;
// Features
uint16_t numFeatures() const;
const FeatureRef * featureById(uint32_t id) const;
const FeatureRef * feature(uint16_t index) const;
// Glyph related
int32_t getGlyphMetric(uint16_t gid, uint8_t metric) const;
uint16_t findPseudo(uint32_t uid) const;
// Errors
unsigned int error() const { return m_error; }
bool error(Error e) { m_error = e.error(); return false; }
unsigned int error_context() const { return m_error; }
void error_context(unsigned int errcntxt) { m_errcntxt = errcntxt; }
CLASS_NEW_DELETE;
private:
SillMap m_Sill;
gr_face_ops m_ops;
const void * m_appFaceHandle; // non-NULL
FileFace * m_pFileFace; //owned
mutable GlyphCache * m_pGlyphFaceCache; // owned - never NULL
mutable Cmap * m_cmap; // cmap cache if available
mutable NameTable * m_pNames;
mutable json * m_logger;
unsigned int m_error;
unsigned int m_errcntxt;
protected:
Silf * m_silfs; // silf subtables.
uint16_t m_numSilf; // num silf subtables in the silf table
private:
uint16_t m_ascent,
m_descent;
#ifdef GRAPHITE2_TELEMETRY
public:
mutable telemetry tele;
#endif
};
inline
const SillMap & Face::theSill() const
{
return m_Sill;
}
inline
uint16_t Face::numFeatures() const
{
return m_Sill.theFeatureMap().numFeats();
}
inline
const FeatureRef * Face::featureById(uint32_t id) const
{
return m_Sill.theFeatureMap().findFeatureRef(id);
}
inline
const FeatureRef *Face::feature(uint16_t index) const
{
return m_Sill.theFeatureMap().feature(index);
}
inline
const GlyphCache & Face::glyphs() const
{
return *m_pGlyphFaceCache;
}
inline
Cmap & Face::cmap() const
{
return *m_cmap;
};
inline
json * Face::logger() const throw()
{
return m_logger;
}
class Face::Table
{
const Face * _f;
mutable const byte * _p;
size_t _sz;
bool _compressed;
Error decompress();
void release();
public:
Table() throw();
Table(const Face & face, const Tag n, uint32_t version=0xffffffff) throw();
~Table() throw();
Table(const Table && rhs) throw();
operator const byte * () const throw();
size_t size() const throw();
Table & operator = (const Table && rhs) throw();
};
inline
Face::Table::Table() throw()
: _f(0), _p(0), _sz(0), _compressed(false)
{
}
inline
Face::Table::Table(const Table && rhs) throw()
: _f(rhs._f), _p(rhs._p), _sz(rhs._sz), _compressed(rhs._compressed)
{
rhs._p = 0;
}
inline
Face::Table::~Table() throw()
{
release();
}
inline
Face::Table::operator const byte * () const throw()
{
return _p;
}
inline
size_t Face::Table::size() const throw()
{
return _sz;
}
} // namespace graphite2
struct gr_face : public graphite2::Face {};

View File

@ -0,0 +1,171 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "inc/Main.h"
#include "inc/FeatureVal.h"
namespace graphite2 {
// Forward declarations for implmentation types
class FeatureMap;
class Face;
class FeatureSetting
{
public:
FeatureSetting(int16_t theValue, uint16_t labelId) : m_label(labelId), m_value(theValue) {};
uint16_t label() const { return m_label; }
int16_t value() const { return m_value; }
CLASS_NEW_DELETE;
private:
FeatureSetting(const FeatureSetting & fs) : m_label(fs.m_label), m_value(fs.m_value) {};
uint16_t m_label;
int16_t m_value;
};
class FeatureRef
{
typedef uint32_t chunk_t;
static const uint8_t SIZEOF_CHUNK = sizeof(chunk_t)*8;
public:
FeatureRef() throw();
FeatureRef(const Face & face, unsigned short & bits_offset, uint32_t max_val,
uint32_t name, uint16_t uiName, uint16_t flags,
FeatureSetting *settings, uint16_t num_set) throw();
~FeatureRef() throw();
bool applyValToFeature(uint32_t val, Features& pDest) const; //defined in GrFaceImp.h
void maskFeature(Features & pDest) const {
if (m_index < pDest.size()) //defensive
pDest[m_index] |= m_mask;
}
uint32_t getFeatureVal(const Features& feats) const; //defined in GrFaceImp.h
uint32_t getId() const { return m_id; }
uint16_t getNameId() const { return m_nameid; }
uint16_t getNumSettings() const { return m_numSet; }
uint16_t getSettingName(uint16_t index) const { return m_nameValues[index].label(); }
int16_t getSettingValue(uint16_t index) const { return m_nameValues[index].value(); }
uint32_t maxVal() const { return m_max; }
const Face & getFace() const { assert(m_face); return *m_face;}
const FeatureMap* getFeatureMap() const;// { return m_pFace;}
CLASS_NEW_DELETE;
private:
FeatureRef(const FeatureRef & rhs);
const Face * m_face;
FeatureSetting * m_nameValues; // array of name table ids for feature values
chunk_t m_mask, // bit mask to get the value from the vector
m_max; // max value the value can take
uint32_t m_id; // feature identifier/name
uint16_t m_nameid, // Name table id for feature name
m_flags, // feature flags (unused at the moment but read from the font)
m_numSet; // number of values (number of entries in m_nameValues)
byte m_bits, // how many bits to shift the value into place
m_index; // index into the array to find the ulong to mask
private: //unimplemented
FeatureRef& operator=(const FeatureRef&);
};
inline
FeatureRef::FeatureRef() throw()
: m_face(0),
m_nameValues(0),
m_mask(0), m_max(0),
m_id(0),
m_nameid(0), m_flags(0), m_numSet(0),
m_bits(0), m_index(0)
{
}
class NameAndFeatureRef
{
public:
NameAndFeatureRef(uint32_t name = 0) : m_name(name) , m_pFRef(NULL){}
NameAndFeatureRef(FeatureRef const & p) : m_name(p.getId()), m_pFRef(&p) {}
bool operator<(const NameAndFeatureRef& rhs) const //orders by m_name
{ return m_name<rhs.m_name; }
CLASS_NEW_DELETE
uint32_t m_name;
const FeatureRef* m_pFRef;
};
class FeatureMap
{
public:
FeatureMap() : m_numFeats(0), m_feats(NULL), m_pNamedFeats(NULL) {}
~FeatureMap() { delete[] m_feats; delete[] m_pNamedFeats; }
bool readFeats(const Face & face);
const FeatureRef *findFeatureRef(uint32_t name) const;
FeatureRef *feature(uint16_t index) const { return m_feats + index; }
//GrFeatureRef *featureRef(byte index) { return index < m_numFeats ? m_feats + index : NULL; }
const FeatureRef *featureRef(byte index) const { return index < m_numFeats ? m_feats + index : NULL; }
FeatureVal* cloneFeatures(uint32_t langname/*0 means default*/) const; //call destroy_Features when done.
uint16_t numFeats() const { return m_numFeats; };
CLASS_NEW_DELETE
private:
friend class SillMap;
uint16_t m_numFeats;
FeatureRef *m_feats;
NameAndFeatureRef* m_pNamedFeats; //owned
FeatureVal m_defaultFeatures; //owned
private: //defensive on m_feats, m_pNamedFeats, and m_defaultFeatures
FeatureMap(const FeatureMap&);
FeatureMap& operator=(const FeatureMap&);
};
class SillMap
{
private:
class LangFeaturePair
{
LangFeaturePair(const LangFeaturePair &);
LangFeaturePair & operator = (const LangFeaturePair &);
public:
LangFeaturePair() : m_lang(0), m_pFeatures(0) {}
~LangFeaturePair() { delete m_pFeatures; }
uint32_t m_lang;
Features* m_pFeatures; //owns
CLASS_NEW_DELETE
};
public:
SillMap() : m_langFeats(NULL), m_numLanguages(0) {}
~SillMap() { delete[] m_langFeats; }
bool readFace(const Face & face);
bool readSill(const Face & face);
FeatureVal* cloneFeatures(uint32_t langname/*0 means default*/) const; //call destroy_Features when done.
uint16_t numLanguages() const { return m_numLanguages; };
uint32_t getLangName(uint16_t index) const { return (index < m_numLanguages)? m_langFeats[index].m_lang : 0; };
const FeatureMap & theFeatureMap() const { return m_FeatureMap; };
private:
FeatureMap m_FeatureMap; //of face
LangFeaturePair * m_langFeats;
uint16_t m_numLanguages;
private: //defensive on m_langFeats
SillMap(const SillMap&);
SillMap& operator=(const SillMap&);
};
} // namespace graphite2
struct gr_feature_ref : public graphite2::FeatureRef {};

View File

@ -0,0 +1,45 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstring>
#include <cassert>
#include "inc/Main.h"
#include "inc/vector.hpp"
namespace graphite2 {
class FeatureRef;
class FeatureMap;
class FeatureVal : public vector<uint32_t>
{
public:
FeatureVal() : m_pMap(0) { }
FeatureVal(size_t num, const FeatureMap & pMap) : vector<uint32_t>(num), m_pMap(&pMap) {}
FeatureVal(const FeatureVal & rhs) : vector<uint32_t>(rhs), m_pMap(rhs.m_pMap) {}
FeatureVal & operator = (const FeatureVal & rhs) { vector<uint32_t>::operator = (rhs); m_pMap = rhs.m_pMap; return *this; }
bool operator ==(const FeatureVal & b) const
{
size_t n = size();
if (n != b.size()) return false;
for(const_iterator l = begin(), r = b.begin(); n && *l == *r; --n, ++l, ++r);
return n == 0;
}
CLASS_NEW_DELETE
private:
friend class FeatureRef; //so that FeatureRefs can manipulate m_vec directly
const FeatureMap* m_pMap;
};
typedef FeatureVal Features;
} // namespace graphite2
struct gr_feature_val : public graphite2::FeatureVal {};

View File

@ -0,0 +1,57 @@
// SPDX-License-Identifier: MIT
// Copyright 2012, SIL International, All rights reserved.
#pragma once
//#include "inc/FeatureMap.h"
//#include "inc/GlyphsCache.h"
//#include "inc/Silf.h"
#ifndef GRAPHITE2_NFILEFACE
#include <cstdio>
#include <cassert>
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/TtfTypes.h"
#include "inc/TtfUtil.h"
namespace graphite2 {
class FileFace
{
static const void * get_table_fn(const void* appFaceHandle, unsigned int name, size_t *len);
static void rel_table_fn(const void* appFaceHandle, const void *table_buffer);
public:
static const gr_face_ops ops;
FileFace(const char *filename);
~FileFace();
operator bool () const throw();
CLASS_NEW_DELETE;
private: //defensive
FILE * _file;
size_t _file_len;
TtfUtil::Sfnt::OffsetSubTable * _header_tbl;
TtfUtil::Sfnt::OffsetSubTable::Entry * _table_dir;
FileFace(const FileFace&);
FileFace& operator=(const FileFace&);
};
inline
FileFace::operator bool() const throw()
{
return _file && _header_tbl && _table_dir;
}
} // namespace graphite2
#endif //!GRAPHITE2_NFILEFACE

View File

@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cassert>
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Face.h"
namespace graphite2 {
#define INVALID_ADVANCE -1e38f // can't be a static const because non-integral
class Font
{
public:
Font(float ppm, const Face & face, const void * appFontHandle=0, const gr_font_ops * ops=0);
virtual ~Font();
float advance(unsigned short glyphid) const;
float scale() const;
bool isHinted() const;
const Face & face() const;
operator bool () const throw() { return m_advances; }
CLASS_NEW_DELETE;
private:
gr_font_ops m_ops;
const void * const m_appFontHandle;
float * m_advances; // One advance per glyph in pixels. Nan if not defined
const Face & m_face;
float m_scale; // scales from design units to ppm
bool m_hinted;
Font(const Font&);
Font& operator=(const Font&);
};
inline
float Font::advance(unsigned short glyphid) const
{
if (m_advances[glyphid] == INVALID_ADVANCE)
m_advances[glyphid] = (*m_ops.glyph_advance_x)(m_appFontHandle, glyphid);
return m_advances[glyphid];
}
inline
float Font::scale() const
{
return m_scale;
}
inline
bool Font::isHinted() const
{
return m_hinted;
}
inline
const Face & Font::face() const
{
return m_face;
}
} // namespace graphite2
struct gr_font : public graphite2::Font {};

View File

@ -0,0 +1,200 @@
// SPDX-License-Identifier: MIT
// Copyright 2012, SIL International, All rights reserved.
#pragma once
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Position.h"
#include "inc/GlyphFace.h"
namespace graphite2 {
class Face;
class FeatureVal;
class Segment;
struct SlantBox
{
static const SlantBox empty;
// SlantBox(float psi = 0., float pdi = 0., float psa = 0., float pda = 0.) : si(psi), di(pdi), sa(psa), da(pda) {};
float width() const { return sa - si; }
float height() const { return da - di; }
float si; // min
float di; // min
float sa; // max
float da; // max
};
struct BBox
{
BBox(float pxi = 0, float pyi = 0., float pxa = 0., float pya = 0.) : xi(pxi), yi(pyi), xa(pxa), ya(pya) {};
float width() const { return xa - xi; }
float height() const { return ya - yi; }
float xi; // min
float yi; // min
float xa; // max
float ya; // max
};
class GlyphBox
{
GlyphBox(const GlyphBox &);
GlyphBox & operator = (const GlyphBox &);
public:
GlyphBox(uint8_t numsubs, unsigned short bitmap, Rect *slanted) : _num(numsubs), _bitmap(bitmap), _slant(*slanted) {};
void addSubBox(int subindex, int boundary, Rect *val) { _subs[subindex * 2 + boundary] = *val; }
Rect &subVal(int subindex, int boundary) { return _subs[subindex * 2 + boundary]; }
const Rect &slant() const { return _slant; }
uint8_t num() const { return _num; }
const Rect *subs() const { return _subs; }
private:
uint8_t _num;
unsigned short _bitmap;
Rect _slant;
Rect _subs[1];
};
class GlyphCache
{
class Loader;
GlyphCache(const GlyphCache&);
GlyphCache& operator=(const GlyphCache&);
public:
GlyphCache(const Face & face, const uint32_t face_options);
~GlyphCache();
unsigned short numGlyphs() const throw();
unsigned short numAttrs() const throw();
unsigned short unitsPerEm() const throw();
const GlyphFace *glyph(unsigned short glyphid) const; //result may be changed by subsequent call with a different glyphid
const GlyphFace *glyphSafe(unsigned short glyphid) const;
float getBoundingMetric(unsigned short glyphid, uint8_t metric) const;
uint8_t numSubBounds(unsigned short glyphid) const;
float getSubBoundingMetric(unsigned short glyphid, uint8_t subindex, uint8_t metric) const;
const Rect & slant(unsigned short glyphid) const { return _boxes[glyphid] ? _boxes[glyphid]->slant() : _empty_slant_box; }
const SlantBox & getBoundingSlantBox(unsigned short glyphid) const;
const BBox & getBoundingBBox(unsigned short glyphid) const;
const SlantBox & getSubBoundingSlantBox(unsigned short glyphid, uint8_t subindex) const;
const BBox & getSubBoundingBBox(unsigned short glyphid, uint8_t subindex) const;
bool check(unsigned short glyphid) const;
bool hasBoxes() const { return _boxes != 0; }
CLASS_NEW_DELETE;
private:
const Rect _empty_slant_box;
const Loader * _glyph_loader;
const GlyphFace * * _glyphs;
GlyphBox * * _boxes;
unsigned short _num_glyphs,
_num_attrs,
_upem;
};
inline
unsigned short GlyphCache::numGlyphs() const throw()
{
return _num_glyphs;
}
inline
unsigned short GlyphCache::numAttrs() const throw()
{
return _num_attrs;
}
inline
unsigned short GlyphCache::unitsPerEm() const throw()
{
return _upem;
}
inline
bool GlyphCache::check(unsigned short glyphid) const
{
return _boxes && glyphid < _num_glyphs;
}
inline
const GlyphFace *GlyphCache::glyphSafe(unsigned short glyphid) const
{
return glyphid < _num_glyphs ? glyph(glyphid) : NULL;
}
inline
float GlyphCache::getBoundingMetric(unsigned short glyphid, uint8_t metric) const
{
if (glyphid >= _num_glyphs) return 0.;
switch (metric) {
case 0: return (float)(glyph(glyphid)->theBBox().bl.x); // x_min
case 1: return (float)(glyph(glyphid)->theBBox().bl.y); // y_min
case 2: return (float)(glyph(glyphid)->theBBox().tr.x); // x_max
case 3: return (float)(glyph(glyphid)->theBBox().tr.y); // y_max
case 4: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.x : 0.f); // sum_min
case 5: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().bl.y : 0.f); // diff_min
case 6: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.x : 0.f); // sum_max
case 7: return (float)(_boxes[glyphid] ? _boxes[glyphid]->slant().tr.y : 0.f); // diff_max
default: return 0.;
}
}
inline const SlantBox &GlyphCache::getBoundingSlantBox(unsigned short glyphid) const
{
return _boxes[glyphid] ? *(SlantBox *)(&(_boxes[glyphid]->slant())) : SlantBox::empty;
}
inline const BBox &GlyphCache::getBoundingBBox(unsigned short glyphid) const
{
return *(BBox *)(&(glyph(glyphid)->theBBox()));
}
inline
float GlyphCache::getSubBoundingMetric(unsigned short glyphid, uint8_t subindex, uint8_t metric) const
{
GlyphBox *b = _boxes[glyphid];
if (b == NULL || subindex >= b->num()) return 0;
switch (metric) {
case 0: return b->subVal(subindex, 0).bl.x;
case 1: return b->subVal(subindex, 0).bl.y;
case 2: return b->subVal(subindex, 0).tr.x;
case 3: return b->subVal(subindex, 0).tr.y;
case 4: return b->subVal(subindex, 1).bl.x;
case 5: return b->subVal(subindex, 1).bl.y;
case 6: return b->subVal(subindex, 1).tr.x;
case 7: return b->subVal(subindex, 1).tr.y;
default: return 0.;
}
}
inline const SlantBox &GlyphCache::getSubBoundingSlantBox(unsigned short glyphid, uint8_t subindex) const
{
GlyphBox *b = _boxes[glyphid];
return *(SlantBox *)(b->subs() + 2 * subindex + 1);
}
inline const BBox &GlyphCache::getSubBoundingBBox(unsigned short glyphid, uint8_t subindex) const
{
GlyphBox *b = _boxes[glyphid];
return *(BBox *)(b->subs() + 2 * subindex);
}
inline
uint8_t GlyphCache::numSubBounds(unsigned short glyphid) const
{
return _boxes[glyphid] ? _boxes[glyphid]->num() : 0;
}
} // namespace graphite2

View File

@ -0,0 +1,60 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "inc/Main.h"
#include "inc/Position.h"
#include "inc/Sparse.h"
namespace graphite2 {
enum metrics: uint8_t {
kgmetLsb = 0, kgmetRsb,
kgmetBbTop, kgmetBbBottom, kgmetBbLeft, kgmetBbRight,
kgmetBbHeight, kgmetBbWidth,
kgmetAdvWidth, kgmetAdvHeight,
kgmetAscent, kgmetDescent
};
class GlyphFace
{
public:
GlyphFace();
template<typename I>
GlyphFace(const Rect & bbox, const Position & adv, I first, const I last);
const Position & theAdvance() const;
const Rect & theBBox() const { return m_bbox; }
const sparse & attrs() const { return m_attrs; }
int32_t getMetric(uint8_t metric) const;
CLASS_NEW_DELETE;
private:
Rect m_bbox; // bounding box metrics in design units
Position m_advance; // Advance width and height in design units
sparse m_attrs;
};
// Inlines: class GlyphFace
//
inline
GlyphFace::GlyphFace()
{}
template<typename I>
GlyphFace::GlyphFace(const Rect & bbox, const Position & adv, I first, const I last)
: m_bbox(bbox),
m_advance(adv),
m_attrs(first, last)
{
}
inline
const Position & GlyphFace::theAdvance() const {
return m_advance;
}
} // namespace graphite2

View File

@ -0,0 +1,212 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <iterator>
#include <utility>
#include "inc/Main.h"
#include "inc/vector.hpp"
#include "inc/json.h"
#include "inc/Position.h"
// An IntervalSet represents the possible movement of a given glyph in a given direction
// (horizontally, vertically, or diagonally).
// A vector is needed to represent disjoint ranges, eg, -300..-150, 20..200, 500..750.
// Each pair represents the min/max of a sub-range.
namespace graphite2 {
class Segment;
enum zones_t {SD, XY};
class Zones
{
struct Exclusion
{
template<zones_t O>
static Exclusion weighted(float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega);
float x, // x position
xm, // xmax position
c, // constant + sum(MiXi^2)
sm, // sum(Mi)
smx; // sum(MiXi)
bool open;
Exclusion(float x, float w, float smi, float smxi, float c);
Exclusion & operator += (Exclusion const & rhs);
uint8_t outcode(float p) const;
Exclusion split_at(float p);
void left_trim(float p);
bool track_cost(float & cost, float & x, float origin) const;
private:
float test_position(float origin) const;
float cost(float x) const;
};
typedef vector<Exclusion> exclusions;
typedef exclusions::iterator iterator;
typedef Exclusion * pointer;
typedef Exclusion & reference;
typedef std::reverse_iterator<iterator> reverse_iterator;
public:
typedef exclusions::const_iterator const_iterator;
typedef Exclusion const * const_pointer;
typedef Exclusion const & const_reference;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
#if !defined GRAPHITE2_NTRACING
struct Debug
{
Exclusion _excl;
bool _isdel;
vector<void *> _env;
Debug(Exclusion *e, bool isdel, json *dbg) : _excl(*e), _isdel(isdel), _env(dbg->getenvs()) { };
};
typedef vector<Debug> debugs;
typedef debugs::const_iterator idebugs;
void addDebug(Exclusion *e);
void removeDebug(float pos, float posm);
void setdebug(json *dbgout) { _dbg = dbgout; }
idebugs dbgs_begin() const { return _dbgs.begin(); }
idebugs dbgs_end() const { return _dbgs.end(); }
void jsonDbgOut(Segment &seg) const;
Position position() const { return Position(_pos, _posm); }
#endif
Zones();
template<zones_t O>
void initialise(float xmin, float xmax, float margin_len, float margin_weight, float ao);
void exclude(float xmin, float xmax);
void exclude_with_margins(float xmin, float xmax, int axis);
template<zones_t O>
void weighted(float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
void weightedAxis(int axis, float xmin, float xmax, float f, float a0, float mi, float xi, float ai, float c, bool nega);
float closest( float origin, float &cost) const;
const_iterator begin() const { return _exclusions.begin(); }
const_iterator end() const { return _exclusions.end(); }
private:
exclusions _exclusions;
#if !defined GRAPHITE2_NTRACING
json * _dbg;
debugs _dbgs;
#endif
float _margin_len,
_margin_weight,
_pos,
_posm;
void insert(Exclusion e);
void remove(float x, float xm);
const_iterator find_exclusion_under(float x) const;
};
inline
Zones::Zones()
: _margin_len(0), _margin_weight(0), _pos(0), _posm(0)
{
#if !defined GRAPHITE2_NTRACING
_dbg = 0;
#endif
_exclusions.reserve(8);
}
inline
Zones::Exclusion::Exclusion(float x_, float xm_, float smi, float smxi, float c_)
: x(x_), xm(xm_), c(c_), sm(smi), smx(smxi), open(false)
{ }
template<zones_t O>
inline
void Zones::initialise(float xmin, float xmax, float margin_len,
float margin_weight, float a0)
{
_margin_len = margin_len;
_margin_weight = margin_weight;
_pos = xmin;
_posm = xmax;
_exclusions.clear();
_exclusions.push_back(Exclusion::weighted<O>(xmin, xmax, 1, a0, 0, 0, 0, 0, false));
_exclusions.front().open = true;
#if !defined GRAPHITE2_NTRACING
_dbgs.clear();
#endif
}
inline
void Zones::exclude(float xmin, float xmax) {
remove(xmin, xmax);
}
template<zones_t O>
inline
void Zones::weighted(float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega) {
insert(Exclusion::weighted<O>(xmin, xmax, f, a0, m, xi, ai, c, nega));
}
inline
void Zones::weightedAxis(int axis, float xmin, float xmax, float f, float a0,
float m, float xi, float ai, float c, bool nega) {
if (axis < 2)
weighted<XY>(xmin, xmax, f, a0, m, xi, ai, c, nega);
else
weighted<SD>(xmin, xmax, f, a0, m, xi, ai, c, nega);
}
#if !defined GRAPHITE2_NTRACING
inline
void Zones::addDebug(Exclusion *e) {
if (_dbg)
_dbgs.push_back(Debug(e, false, _dbg));
}
inline
void Zones::removeDebug(float pos, float posm) {
if (_dbg)
{
Exclusion e(pos, posm, 0, 0, 0);
_dbgs.push_back(Debug(&e, true, _dbg));
}
}
#endif
template<>
inline
Zones::Exclusion Zones::Exclusion::weighted<XY>(float xmin, float xmax, float f, float a0,
float m, float xi, GR_MAYBE_UNUSED float ai, float c, GR_MAYBE_UNUSED bool nega) {
return Exclusion(xmin, xmax,
m + f,
m * xi,
m * xi * xi + f * a0 * a0 + c);
}
template<>
inline
Zones::Exclusion Zones::Exclusion::weighted<SD>(float xmin, float xmax, float f, float a0,
float m, float xi, float ai,float c, bool nega) {
float xia = nega ? xi - ai : xi + ai;
return Exclusion(xmin, xmax,
0.25f * (m + 2.f * f),
0.25f * m * xia,
0.25f * (m * xia * xia + 2.f * f * a0 * a0) + c);
}
} // end of namespace graphite2

View File

@ -0,0 +1,205 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This general interpreter interface.
// Author: Tim Eves
// Build one of direct_machine.cpp or call_machine.cpp to implement this
// interface.
#pragma once
#include <cstring>
#include <limits>
#include <graphite2/Types.h>
#include "inc/Main.h"
#include "inc/ShapingContext.hpp"
#include "inc/SlotBuffer.h"
#if defined(__GNUC__)
#if defined(__clang__) || (__GNUC__ * 100 + __GNUC_MINOR__ * 10) < 430
#define HOT
#if defined(__x86_64)
#define REGPARM(n) __attribute__((regparm(n)))
#else
#define REGPARM(n)
#endif
#else
#define HOT __attribute__((hot))
#if defined(__x86_64)
#define REGPARM(n) __attribute__((hot, regparm(n)))
#else
#define REGPARM(n)
#endif
#endif
#else
#define HOT
#define REGPARM(n)
#endif
#if defined(__MINGW32__)
// MinGW's <limits> at some point includes winnt.h which #define's a
// DELETE macro, which conflicts with enum opcode below, so we undefine
// it here.
#undef DELETE
#endif
namespace graphite2 {
// Forward declarations
class Segment;
class Slot;
class ShapingContext;
namespace vm
{
using instr = void *;
using slotref = SlotBuffer::iterator;
using const_slotref = SlotBuffer::iterator;
enum {VARARGS = 0xff, MAX_NAME_LEN=32};
enum opcode {
NOP = 0,
PUSH_BYTE, PUSH_BYTEU, PUSH_SHORT, PUSH_SHORTU, PUSH_LONG,
ADD, SUB, MUL, DIV,
MIN_, MAX_,
NEG,
TRUNC8, TRUNC16,
COND,
AND, OR, NOT,
EQUAL, NOT_EQ,
LESS, GTR, LESS_EQ, GTR_EQ,
NEXT, NEXT_N, COPY_NEXT,
PUT_GLYPH_8BIT_OBS, PUT_SUBS_8BIT_OBS, PUT_COPY,
INSERT, DELETE,
ASSOC,
CNTXT_ITEM,
ATTR_SET, ATTR_ADD, ATTR_SUB,
ATTR_SET_SLOT,
IATTR_SET_SLOT,
PUSH_SLOT_ATTR, PUSH_GLYPH_ATTR_OBS,
PUSH_GLYPH_METRIC, PUSH_FEAT,
PUSH_ATT_TO_GATTR_OBS, PUSH_ATT_TO_GLYPH_METRIC,
PUSH_ISLOT_ATTR,
PUSH_IGLYPH_ATTR, // not implemented
POP_RET, RET_ZERO, RET_TRUE,
IATTR_SET, IATTR_ADD, IATTR_SUB,
PUSH_PROC_STATE, PUSH_VERSION,
PUT_SUBS, PUT_SUBS2, PUT_SUBS3,
PUT_GLYPH, PUSH_GLYPH_ATTR, PUSH_ATT_TO_GLYPH_ATTR,
BITOR, BITAND, BITNOT,
BITSET, SET_FEAT,
MAX_OPCODE,
// private opcodes for internal use only, comes after all other on disk opcodes
TEMP_COPY = MAX_OPCODE
};
struct opcode_t
{
instr impl[2];
uint8_t param_sz;
char name[MAX_NAME_LEN];
};
class Machine
{
public:
struct regbank;
typedef int32_t stack_t;
static size_t const STACK_ORDER = 10,
STACK_MAX = 1 << STACK_ORDER,
STACK_GUARD = 2;
class Code;
enum status_t {
finished = 0,
stack_underflow,
stack_not_empty,
stack_overflow,
slot_offset_out_bounds,
died_early
};
Machine(ShapingContext &) throw();
static const opcode_t * getOpcodeTable() throw();
CLASS_NEW_DELETE;
ShapingContext & shaping_context() const throw();
status_t status() const throw();
// operator bool () const throw();
private:
void check_final_stack(const stack_t * const sp);
stack_t run(const instr * program, const byte * data,
const_slotref *& slot_in,
slotref & slot_out) HOT;
ShapingContext & _ctxt;
stack_t _stack[STACK_MAX + 2*STACK_GUARD];
status_t _status;
};
struct Machine::regbank {
instr const * ip;
byte const * dp;
Machine::stack_t * sp,
* const sb;
Machine::status_t & status;
ShapingContext & ctxt;
Segment & seg;
slotref * is;
slotref * const isb;
slotref os;
int8_t positioned;
};
inline Machine::Machine(ShapingContext & ctxt) throw()
: _ctxt(ctxt), _status(finished)
{
// Initialise stack guard +1 entries as the stack pointer points to the
// current top of stack, hence the first push will never write entry 0.
// Initialising the guard space like this is unnecessary and is only
// done to keep valgrind happy during fuzz testing. Hopefully loop
// unrolling will flatten this.
for (size_t n = STACK_GUARD + 1; n; --n) _stack[n-1] = 0;
}
inline ShapingContext& Machine::shaping_context() const throw()
{
return _ctxt;
}
inline Machine::status_t Machine::status() const throw()
{
return _status;
}
inline void Machine::check_final_stack(const stack_t * const sp)
{
if (_status != finished) return;
stack_t const * const base = _stack + STACK_GUARD,
* const limit = base + STACK_MAX;
if (sp < base) _status = stack_underflow; // This should be impossible now.
else if (sp >= limit) _status = stack_overflow; // So should this.
else if (sp != base) _status = stack_not_empty;
}
} // namespace vm
} // namespace graphite2

View File

@ -0,0 +1,179 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdlib>
#include "graphite2/Types.h"
#ifdef GRAPHITE2_CUSTOM_HEADER
#include GRAPHITE2_CUSTOM_HEADER
#endif
namespace graphite2 {
typedef uint8_t byte;
#ifdef GRAPHITE2_TELEMETRY
struct telemetry
{
class category;
static size_t * _category;
static void set_category(size_t & t) throw() { _category = &t; }
static void stop() throw() { _category = 0; }
static void count_bytes(size_t n) throw() { if (_category) *_category += n; }
size_t misc,
silf,
glyph,
code,
states,
starts,
transitions;
telemetry() : misc(0), silf(0), glyph(0), code(0), states(0), starts(0), transitions(0) {}
};
class telemetry::category
{
size_t * _prev;
public:
category(size_t & t) : _prev(_category) { _category = &t; }
~category() { _category = _prev; }
};
#else
struct telemetry {};
#endif
// Checked multiplaction to catch overflow or underflow when allocating memory
#if defined(__has_builtin)
#if __has_builtin(__builtin_mul_overflow)
#define HAVE_BUILTIN_OVERFLOW
#endif
#elif defined(__GNUC__) && (__GNUC__ >= 5) && !defined(__INTEL_COMPILER)
#define HAVE_BUILTIN_OVERFLOW
#endif
#if defined(__has_include)
#if __has_include(<intsafe.h>) && !defined(__CYGWIN__)
#define HAVE_INTSAFE_H
#endif
#elif defined(_WIN32)
#define HAVE_INTSAFE_H
#endif
// Need to import intsafe into the top level namespace
#if defined(HAVE_INTSAFE_H)
} // namespace graphite2
#include <intsafe.h>
namespace graphite2 {
#endif
#if defined(HAVE_BUILTIN_OVERFLOW)
inline
bool checked_mul(const size_t a, const size_t b, size_t & t) {
return __builtin_mul_overflow(a, b, &t);
}
#elif defined(HAVE_INTSAFE_H)
inline
bool checked_mul(const size_t a, const size_t b, size_t & t) {
return SizeTMult(a, b, &t) == INTSAFE_E_ARITHMETIC_OVERFLOW;
}
#else
inline
bool checked_mul(const size_t a, const size_t b, size_t & t) {
t = a*b;
return (((a | b) & (~size_t(0) << (sizeof(size_t) << 2))) && (t / a != b));
}
#endif
// typesafe wrapper around malloc for simple types
// use free(pointer) to deallocate
template <typename T> T * gralloc(size_t n)
{
size_t total;
if (checked_mul(n, sizeof(T), total))
return 0;
#ifdef GRAPHITE2_TELEMETRY
telemetry::count_bytes(total);
#endif
return static_cast<T*>(malloc(total));
}
template <typename T> T * grzeroalloc(size_t n)
{
#ifdef GRAPHITE2_TELEMETRY
telemetry::count_bytes(sizeof(T) * n);
#endif
return static_cast<T*>(calloc(n, sizeof(T)));
}
template <typename T>
inline T min(const T a, const T b)
{
return a < b ? a : b;
}
template <typename T>
inline T max(const T a, const T b)
{
return a > b ? a : b;
}
} // namespace graphite2
#define CLASS_NEW_DELETE \
void * operator new (size_t size){ return gralloc<byte>(size);} \
void * operator new (size_t, void * p) throw() { return p; } \
void * operator new[] (size_t size) {return gralloc<byte>(size);} \
void * operator new[] (size_t, void * p) throw() { return p; } \
void operator delete (void * p) throw() { free(p);} \
void operator delete (void *, void *) throw() {} \
void operator delete[] (void * p)throw() { free(p); } \
void operator delete[] (void *, void *) throw() {}
#if defined(__GNUC__) || defined(__clang__)
#define GR_MAYBE_UNUSED __attribute__((unused))
#else
#define GR_MAYBE_UNUSED
#endif
#ifndef __has_cpp_attribute
# define __has_cpp_attribute(x) 0
#endif
#if __has_cpp_attribute(clang::fallthrough)
# define GR_FALLTHROUGH [[clang::fallthrough]]
#elif __has_cpp_attribute(gnu::fallthrough)
# define GR_FALLTHROUGH [[gnu::fallthrough]]
#elif defined(_MSC_VER)
/*
* MSVC's __fallthrough annotations are checked by /analyze (Code Analysis):
* https://msdn.microsoft.com/en-us/library/ms235402%28VS.80%29.aspx
*/
#include <sal.h>
#define GR_FALLTHROUGH __fallthrough
#elif __GNUC__ >= 7
#define GR_FALLTHROUGH __attribute__ ((fallthrough))
#else
#define GR_FALLTHROUGH /* fallthrough */
#endif
// TODO: Remove these specialisations once full refactor of SlotBuffer and iterators is done.
#include <iterator>
struct gr_slot;
template<> gr_slot * std::prev<gr_slot*>(gr_slot *, ptrdiff_t);
template<> gr_slot const * std::prev<gr_slot const*>(gr_slot const *, ptrdiff_t);
template<> gr_slot * std::next<gr_slot*>(gr_slot *, ptrdiff_t);
template<> gr_slot const * std::next<gr_slot const*>(gr_slot const *, ptrdiff_t);
#ifdef _MSC_VER
#pragma warning(disable: 4800)
#pragma warning(disable: 4355)
#endif

View File

@ -0,0 +1,42 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <graphite2/Segment.h>
#include "inc/TtfTypes.h"
#include "inc/locale2lcid.h"
namespace graphite2 {
class NameTable
{
NameTable(const NameTable &);
NameTable & operator = (const NameTable &);
public:
NameTable(const void * data, size_t length, uint16_t platfromId=3, uint16_t encodingID = 1);
~NameTable() { free(const_cast<TtfUtil::Sfnt::FontNames *>(m_table)); }
enum eNameFallback {
eNoFallback = 0,
eEnUSFallbackOnly = 1,
eEnOrAnyFallback = 2
};
uint16_t setPlatformEncoding(uint16_t platfromId=3, uint16_t encodingID = 1);
void * getName(uint16_t & languageId, uint16_t nameId, gr_encform enc, uint32_t & length);
uint16_t getLanguageId(const char * bcp47Locale);
CLASS_NEW_DELETE
private:
uint16_t m_platformId;
uint16_t m_encodingId;
uint16_t m_languageCount;
uint16_t m_platformOffset; // offset of first NameRecord with for platform 3, encoding 1
uint16_t m_platformLastRecord;
uint16_t m_nameDataLength;
const TtfUtil::Sfnt::FontNames * m_table;
const uint8_t * m_nameData;
Locale2Lang m_locale2Lang;
};
} // namespace graphite2

View File

@ -0,0 +1,95 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdlib>
#include "inc/Code.h"
#include "inc/Rule.h"
#include "inc/SlotBuffer.h"
namespace graphite2 {
class Segment;
class Face;
class Silf;
struct State;
class FiniteStateMachine;
class Error;
class ShiftCollider;
class KernCollider;
class json;
enum passtype;
class Pass
{
public:
Pass();
~Pass();
bool readPass(const byte * pPass, size_t pass_length, size_t subtable_base, Face & face,
enum passtype pt, uint32_t version, Error &e);
bool runGraphite(vm::Machine & m, ShapingContext & ctxt, bool reverse) const;
void init(Silf *silf) { m_silf = silf; }
byte collisionLoops() const { return m_numCollRuns; }
bool reverseDir() const { return m_isReverseDir; }
CLASS_NEW_DELETE
private:
void findNDoRule(vm::Machine &, ShapingContext & ctxt, vm::const_slotref &slot) const;
int doAction(const vm::Machine::Code* codeptr, SlotBuffer::iterator & slot_out, vm::Machine &) const;
bool testPassConstraint(vm::Machine & m) const;
bool testConstraint(const Rule & r, vm::Machine &) const;
bool readRules(const byte * rule_map, const size_t num_entries,
const byte *precontext, const uint16_t * sort_key,
const uint16_t * o_constraint, const byte *constraint_data,
const uint16_t * o_action, const byte * action_data,
Face &, enum passtype pt, Error &e);
bool readStates(const byte * starts, const byte * states, const byte * o_rule_map, Face &, Error &e);
bool readRanges(const byte * ranges, size_t num_ranges, Error &e);
uint16_t glyphToCol(const uint16_t gid) const;
bool runFSM(ShapingContext & ctxt, vm::const_slotref slot, Rules &rules) const;
void dumpRuleEventConsidered(ShapingContext const & ctxt, Rules::const_iterator first, Rules::const_iterator const & last) const;
void dumpRuleEventOutput(ShapingContext const & ctxt, Rule const & r, SlotBuffer::const_iterator const, SlotBuffer::const_iterator const) const;
void adjustSlot(int delta, vm::const_slotref & slot, ShapingContext &) const;
bool collisionShift(Segment & seg, int dir, json * const dbgout) const;
bool collisionKern(Segment & seg, int dir, json * const dbgout) const;
bool collisionFinish(Segment & seg, GR_MAYBE_UNUSED json * const dbgout) const;
bool resolveCollisions(Segment & seg, SlotBuffer::iterator const & slotFix, SlotBuffer::iterator start, ShiftCollider &coll, bool isRev,
int dir, bool &moved, bool &hasCol, json * const dbgout) const;
float resolveKern(Segment & seg, SlotBuffer::iterator const slotFix, GR_MAYBE_UNUSED SlotBuffer::iterator start, int dir,
float &ymin, float &ymax, json *const dbgout) const;
const Silf * m_silf;
uint16_t * m_cols;
Rule * m_rules; // rules
Rules::Entry * m_ruleMap;
uint16_t * m_startStates; // prectxt length
uint16_t * m_transitions;
State * m_states;
vm::Machine::Code * m_codes;
byte * m_progs;
byte m_numCollRuns;
byte m_kernColls;
byte m_iMaxLoop;
uint16_t m_numGlyphs;
uint16_t m_numRules;
uint16_t m_numStates;
uint16_t m_numTransition;
uint16_t m_numSuccess;
uint16_t m_successStart;
uint16_t m_numColumns;
byte m_minPreCtxt;
byte m_maxPreCtxt;
byte m_colThreshold;
bool m_isReverseDir;
vm::Machine::Code m_cPConstraint;
private: //defensive
Pass(const Pass&);
Pass& operator=(const Pass&);
};
} // namespace graphite2

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
namespace graphite2 {
class Position
{
public:
constexpr Position() : x(0), y(0) { }
Position(const float inx, const float iny) : x(inx), y(iny) {}
Position operator + (const Position& a) const { return Position(x + a.x, y + a.y); }
Position operator - (const Position& a) const { return Position(x - a.x, y - a.y); }
Position operator * (const float m) const { return Position(x * m, y * m); }
Position &operator += (const Position &a) { x += a.x; y += a.y; return *this; }
Position &operator -= (const Position &a) { x -= a.x; y -= a.y; return *this; }
Position &operator *= (const float m) { x *= m; y *= m; return *this; }
float x;
float y;
};
class Rect
{
public :
Rect() {}
Rect(const Position& botLeft, const Position& topRight): bl(botLeft), tr(topRight) {}
Rect widen(const Rect& other) { return Rect(Position(bl.x > other.bl.x ? other.bl.x : bl.x, bl.y > other.bl.y ? other.bl.y : bl.y), Position(tr.x > other.tr.x ? tr.x : other.tr.x, tr.y > other.tr.y ? tr.y : other.tr.y)); }
Rect operator + (const Position &a) const { return Rect(Position(bl.x + a.x, bl.y + a.y), Position(tr.x + a.x, tr.y + a.y)); }
Rect operator - (const Position &a) const { return Rect(Position(bl.x - a.x, bl.y - a.y), Position(tr.x - a.x, tr.y - a.y)); }
Rect operator * (float m) const { return Rect(Position(bl.x, bl.y) * m, Position(tr.x, tr.y) * m); }
float width() const { return tr.x - bl.x; }
float height() const { return tr.y - bl.y; }
bool hitTest(Rect &other);
// returns Position(overlapx, overlapy) where overlap<0 if overlapping else positive)
Position overlap(Position &offset, Rect &other, Position &otherOffset);
//Position constrainedAvoid(Position &offset, Rect &box, Rect &sdbox, Position &other, Rect &obox, Rect &osdbox);
Position bl;
Position tr;
};
} // namespace graphite2

View File

@ -0,0 +1,135 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
#pragma once
#include "inc/Code.h"
namespace graphite2 {
class json;
struct Rule {
const vm::Machine::Code * constraint,
* action;
unsigned short sort;
byte preContext;
#ifndef NDEBUG
uint16_t rule_idx;
#endif
Rule();
~Rule() {}
CLASS_NEW_DELETE;
private:
Rule(const Rule &);
Rule & operator = (const Rule &);
};
inline
Rule::Rule()
: constraint(0),
action(0),
sort(0),
preContext(0)
{
#ifndef NDEBUG
rule_idx = 0;
#endif
}
class State;
class Rules
{
public:
struct Entry
{
const Rule * rule;
bool operator == (const Entry &r) const;
bool operator < (const Entry &r) const;
};
static constexpr size_t MAX_RULES=128;
using iterator = Entry *;
using const_iterator = Entry const *;
Rules();
void clear() { m_end = m_begin; }
const_iterator begin() const { return m_begin; }
const_iterator end() const { return m_end; }
bool empty() const { return m_begin == m_end; }
size_t size() const { return m_end - m_begin; }
void accumulate_rules(const State &state);
private:
Entry * m_begin,
* m_end,
m_rules[MAX_RULES*2];
};
struct State
{
const Rules::Entry * rules,
* rules_end;
bool empty() const { return rules_end == rules; }
};
inline
bool Rules::Entry::operator == (const Entry &r) const
{
return rule == r.rule;
}
inline
bool Rules::Entry::operator < (const Entry &r) const {
const unsigned short lsort = rule->sort,
rsort = r.rule->sort;
return lsort > rsort || (lsort == rsort && rule < r.rule);
}
inline
Rules::Rules()
: m_begin(m_rules), m_end(m_rules)
{
}
inline
void Rules::accumulate_rules(const State &state)
{
// Only bother if there are rules in the State object.
if (state.empty()) return;
// Merge the new sorted rules list into the current sorted result set.
auto const * lre = begin(), * rre = state.rules;
auto * out = m_rules + (m_begin == m_rules)*MAX_RULES;
auto const * const lrend = out + MAX_RULES,
* const rrend = state.rules_end;
m_begin = out;
while (lre != end() && out != lrend)
{
if (*lre < *rre) *out++ = *lre++;
else if (*rre < *lre) { *out++ = *rre++; }
else { *out++ = *lre++; ++rre; }
if (rre == rrend)
{
while (lre != end() && out != lrend) { *out++ = *lre++; }
m_end = out;
return;
}
}
while (rre != rrend && out != lrend) { *out++ = *rre++; }
m_end = out;
}
} // namespace graphite2

View File

@ -0,0 +1,190 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "inc/Main.h"
#include <cassert>
#include "inc/CharInfo.h"
#include "inc/Face.h"
#include "inc/FeatureVal.h"
#include "inc/GlyphCache.h"
#include "inc/GlyphFace.h"
#include "inc/Slot.h"
#include "inc/SlotBuffer.h"
#include "inc/Position.h"
#include "inc/vector.hpp"
#include "inc/Collider.h"
#define MAX_SEG_GROWTH_FACTOR 64
namespace graphite2 {
typedef vector<Features> FeatureList;
class Font;
class Segment;
class Silf;
enum SpliceParam {
/** sub-Segments longer than this are not cached
* (in Unicode code points) */
eMaxSpliceSize = 96
};
enum justFlags {
gr_justStartInline = 1,
gr_justEndInline = 2
};
class Segment
{
// Prevent copying of any kind.
Segment(const Segment&) = delete;
Segment& operator=(const Segment&) = delete;
public:
enum {
SEG_INITCOLLISIONS = 1,
SEG_HASCOLLISIONS = 2
};
SlotBuffer const & slots() const { return m_srope; }
SlotBuffer & slots() { return m_srope; }
size_t slotCount() const { return m_numGlyphs; } //one slot per glyph
void extendLength(ptrdiff_t num) { m_numGlyphs += num; }
Position advance() const { return m_advance; }
bool runGraphite() { if (m_silf) return m_face->runGraphite(*this, m_silf); else return true;};
void chooseSilf(uint32_t script) { m_silf = m_face->chooseSilf(script); }
const Silf *silf() const { return m_silf; }
size_t charInfoCount() const { return m_numCharinfo; }
const CharInfo *charinfo(unsigned int index) const { return index < m_numCharinfo ? m_charinfo + index : NULL; }
CharInfo *charinfo(unsigned int index) { return index < m_numCharinfo ? m_charinfo + index : NULL; }
Segment(size_t numchars, const Face* face, uint32_t script, int dir);
~Segment();
uint8_t flags() const { return m_flags; }
void flags(uint8_t f) { m_flags = f; }
void appendSlot(int i, int cid, int gid, int fid, size_t coffset);
SlotBuffer::iterator newSlot();
void freeSlot(SlotBuffer::iterator);
Position positionSlots(
Font const * font,
SlotBuffer::iterator first,
SlotBuffer::iterator last,
bool isRtl = false,
bool isFinal = true);
void associateChars(int offset, size_t num);
void linkClusters();
uint16_t getClassGlyph(uint16_t cid, uint16_t offset) const { return m_silf->getClassGlyph(cid, offset); }
uint16_t findClassIndex(uint16_t cid, uint16_t gid) const { return m_silf->findClassIndex(cid, gid); }
int addFeatures(const Features& feats) { m_feats.push_back(feats); return int(m_feats.size()) - 1; }
uint32_t getFeature(int index, uint8_t findex) const { const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex); if (!pFR) return 0; else return pFR->getFeatureVal(m_feats[index]); }
void setFeature(int index, uint8_t findex, uint32_t val) {
const FeatureRef* pFR=m_face->theSill().theFeatureMap().featureRef(findex);
if (pFR)
{
if (val > pFR->maxVal()) val = pFR->maxVal();
pFR->applyValToFeature(val, m_feats[index]);
} }
int8_t dir() const { return m_dir; }
void dir(int8_t val) { m_dir = val; }
bool currdir() const { return ((m_dir >> 6) ^ m_dir) & 1; }
uint8_t passBits() const { return m_passBits; }
void mergePassBits(const uint8_t val) { m_passBits &= val; }
int16_t glyphAttr(uint16_t gid, uint16_t gattr) const { const GlyphFace * p = m_face->glyphs().glyphSafe(gid); return p ? p->attrs()[gattr] : 0; }
int32_t getGlyphMetric(Slot const *iSlot, metrics metric, uint8_t attrLevel, bool rtl) const;
float glyphAdvance(uint16_t gid) const { return m_face->glyphs().glyph(gid)->theAdvance().x; }
const Rect &theGlyphBBoxTemporary(uint16_t gid) const { return m_face->glyphs().glyph(gid)->theBBox(); } //warning value may become invalid when another glyph is accessed
size_t numAttrs() const { return m_silf->numUser(); }
int defaultOriginal() const { return m_defaultOriginal; }
const Face * getFace() const { return m_face; }
const Features & getFeatures(unsigned int /*charIndex*/) { assert(m_feats.size() == 1); return m_feats[0]; }
void bidiPass(int paradir, uint8_t aMirror);
void doMirror(uint16_t aMirror);
SlotBuffer::iterator addLineEnd(SlotBuffer::iterator nSlot);
void delLineEnd(SlotBuffer::iterator s);
void reverseSlots();
bool isWhitespace(const int cid) const;
bool hasCollisionInfo() const { return (m_flags & SEG_HASCOLLISIONS) && m_collisions; }
SlotCollision *collisionInfo(Slot const & s) const { return m_collisions ? m_collisions + s.index() : nullptr; }
CLASS_NEW_DELETE
public: //only used by: GrSegment* makeAndInitialize(const GrFont *font, const GrFace *face, uint32_t script, const FeaturesHandle& pFeats/*must not be IsNull*/, encform enc, const void* pStart, size_t nChars, int dir);
bool read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void*pStart, size_t nChars);
void finalise(const Font *font, bool reverse=false);
float justify(SlotBuffer::iterator pSlot, const Font *font, float width, enum justFlags flags, SlotBuffer::iterator pFirst, SlotBuffer::iterator pLast);
bool initCollisions();
template<typename Callable>
typename std::result_of<Callable()>::type
subsegment(SlotBuffer::const_iterator first, SlotBuffer::const_iterator last, Callable body);
private:
SlotBuffer m_srope;
Position m_advance; // whole segment advance
FeatureList m_feats; // feature settings referenced by charinfos in this segment
CharInfo * m_charinfo; // character info, one per input character
SlotCollision * m_collisions;
const Face * m_face; // GrFace
const Silf * m_silf;
size_t m_bufSize, // how big a buffer to create when need more slots
m_numGlyphs,
m_numCharinfo; // size of the array and number of input characters
int m_defaultOriginal; // number of whitespace chars in the string
int8_t m_dir;
uint8_t m_flags, // General purpose flags
m_passBits; // if bit set then skip pass
};
inline
void Segment::finalise(const Font *font, bool reverse)
{
if (slots().empty()) return;
m_advance = positionSlots(font, slots().begin(), slots().end(), m_silf->dir(), true);
//associateChars(0, m_numCharinfo);
if (reverse && currdir() != (m_dir & 1))
reverseSlots();
linkClusters();
}
inline
int32_t Segment::getGlyphMetric(Slot const * iSlot, metrics metric, uint8_t attrLevel, bool rtl) const {
if (attrLevel > 0)
{
auto is = iSlot->base();
return is->clusterMetric(*this, metric, attrLevel, rtl);
}
else
return m_face->getGlyphMetric(iSlot->gid(), metric);
}
inline
bool Segment::isWhitespace(const int cid) const
{
return ((cid >= 0x0009) * (cid <= 0x000D)
+ (cid == 0x0020)
+ (cid == 0x0085)
+ (cid == 0x00A0)
+ (cid == 0x1680)
+ (cid == 0x180E)
+ (cid >= 0x2000) * (cid <= 0x200A)
+ (cid == 0x2028)
+ (cid == 0x2029)
+ (cid == 0x202F)
+ (cid == 0x205F)
+ (cid == 0x3000)) != 0;
}
} // namespace graphite2
struct gr_segment : public graphite2::Segment {};

View File

@ -0,0 +1,113 @@
/* GRAPHITE2 LICENSING
Copyright 2020, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include <cstddef>
#include "inc/SlotBuffer.h"
#include "inc/vector.hpp"
namespace graphite2 {
class Segment;
class json;
class ShapingContext
{
public:
static constexpr size_t MAX_SLOTS=64;
using slotref = SlotBuffer::iterator;
using const_slotref = SlotBuffer::iterator;
using map_t = vector<const_slotref>;
ShapingContext(Segment & seg, uint8_t direction, size_t maxSize);
size_t context() const;
void reset(SlotBuffer::iterator & slot, unsigned short max_pre_context);
void pushSlot(slotref slot);
void collectGarbage(slotref & aSlot);
const_slotref highwater() const;
void highwater(const_slotref s);
bool highpassed() const;
void highpassed(bool v);
int decMax();
Segment & segment;
json * const dbgout;
map_t map;
SlotBuffer in;
uint8_t const dir;
private:
const_slotref _highwater;
int _max_size;
unsigned short _precontext;
bool _highpassed;
};
inline
size_t ShapingContext::context() const {
return _precontext;
}
inline
auto ShapingContext::highwater() const -> const_slotref {
return _highwater;
}
inline
void ShapingContext::highwater(const_slotref s) {
_highwater = s; _highpassed = false;
}
inline
bool ShapingContext::highpassed() const {
return _highpassed;
}
inline
void ShapingContext::highpassed(bool v) {
_highpassed = v;
}
inline
int ShapingContext::decMax() {
return --_max_size;
}
inline
void ShapingContext::pushSlot(slotref slot)
{
map.push_back(slot);
}
} // namespace graphite2

View File

@ -0,0 +1,105 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include "graphite2/Font.h"
#include "inc/Main.h"
#include "inc/Pass.h"
namespace graphite2 {
class Face;
class Segment;
class FeatureVal;
class VMScratch;
class Error;
class Pseudo
{
public:
uint32_t uid;
uint32_t gid;
CLASS_NEW_DELETE;
};
class Justinfo
{
public:
Justinfo(uint8_t stretch, uint8_t shrink, uint8_t step, uint8_t weight) :
m_astretch(stretch), m_ashrink(shrink), m_astep(step),
m_aweight(weight) {};
uint8_t attrStretch() const { return m_astretch; }
uint8_t attrShrink() const { return m_ashrink; }
uint8_t attrStep() const { return m_astep; }
uint8_t attrWeight() const { return m_aweight; }
private:
uint8_t m_astretch;
uint8_t m_ashrink;
uint8_t m_astep;
uint8_t m_aweight;
};
class Silf
{
// Prevent copying
Silf(const Silf&);
Silf& operator=(const Silf&);
public:
Silf() throw();
~Silf() throw();
bool readGraphite(const byte * const pSilf, size_t lSilf, Face &face, uint32_t version);
bool runGraphite(Segment &seg, uint8_t firstPass=0, uint8_t lastPass=0, int dobidi = 0) const;
uint16_t findClassIndex(uint16_t cid, uint16_t gid) const;
uint16_t getClassGlyph(uint16_t cid, unsigned int index) const;
uint16_t findPseudo(uint32_t uid) const;
size_t numUser() const { return m_aUser; }
uint8_t aPseudo() const { return m_aPseudo; }
uint8_t aBreak() const { return m_aBreak; }
uint8_t aMirror() const {return m_aMirror; }
uint8_t aPassBits() const { return m_aPassBits; }
uint8_t aBidi() const { return m_aBidi; }
uint8_t aCollision() const { return m_aCollision; }
uint8_t substitutionPass() const { return m_sPass; }
uint8_t positionPass() const { return m_pPass; }
uint8_t justificationPass() const { return m_jPass; }
uint8_t bidiPass() const { return m_bPass; }
size_t numPasses() const { return m_numPasses; }
uint8_t maxCompPerLig() const { return m_iMaxComp; }
size_t numClasses() const { return m_nClass; }
byte flags() const { return m_flags; }
byte dir() const { return m_dir; }
size_t numJustLevels() const { return m_numJusts; }
Justinfo *justAttrs() const { return m_justs; }
uint16_t endLineGlyphid() const { return m_gEndLine; }
const gr_faceinfo *silfInfo() const { return &m_silfinfo; }
CLASS_NEW_DELETE;
private:
size_t readClassMap(const byte *p, size_t data_len, uint32_t version, Error &e);
template<typename T> inline uint32_t readClassOffsets(const byte *&p, size_t data_len, Error &e);
Pass * m_passes;
Pseudo * m_pseudos;
uint32_t * m_classOffsets;
uint16_t * m_classData;
Justinfo * m_justs;
uint8_t m_numPasses;
uint8_t m_numJusts;
uint8_t m_sPass, m_pPass, m_jPass, m_bPass,
m_flags, m_dir;
uint8_t m_aPseudo, m_aBreak, m_aUser, m_aBidi, m_aMirror, m_aPassBits,
m_iMaxComp, m_aCollision;
uint16_t m_aLig, m_numPseudo, m_nClass, m_nLinear,
m_gEndLine;
gr_faceinfo m_silfinfo;
void releaseBuffers() throw();
};
} // namespace graphite2

View File

@ -0,0 +1,394 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cassert>
#include <cstring>
#include <limits>
#include <type_traits>
#include "graphite2/Segment.h"
#include "inc/GlyphFace.h"
#include "inc/Main.h"
#include "inc/Position.h"
namespace graphite2 {
class Segment;
// class Slot;
class ShapingContext;
class Font;
struct Slot_data {
int8_t m_parent_offset; // index to parent we are attached to
Position m_position; // absolute position of glyph
Position m_shift; // .shift slot attribute
Position m_advance; // .advance slot attribute
Position m_attach; // attachment point on us
Position m_with; // attachment point position on parent
float m_just; // Justification inserted space
uint32_t m_original; // charinfo that originated this slot (e.g. for feature values)
uint32_t m_before; // charinfo index of before association
uint32_t m_after; // charinfo index of after association
uint32_t m_index; // slot index given to this slot during finalising
uint16_t m_glyphid; // glyph id
uint16_t m_realglyphid;
byte m_attLevel; // attachment level
byte m_bidiLevel; // bidirectional level
int8_t m_bidiCls; // bidirectional class
struct {
bool deleted: 1,
inserted: 1,
copied: 1,
positioned: 1,
clusterhead:1,
last:1,
children:1;
} m_flags; // holds bit flags
};
class Slot : private Slot_data
{
static constexpr int NUMJUSTPARAMS = 5;
union attributes {
private:
struct {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
uint8_t n_attrs;
#endif
int16_t data[sizeof(uintptr_t)/sizeof(int16_t)-1];
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
uint8_t n_attrs;
#endif
} local;
struct {
uint8_t n_attrs, n_justs;
int16_t data[1];
} * external;
bool is_inline() const { return !external || uintptr_t(external) & 0x3;}
public:
constexpr attributes(): external{nullptr} {}
attributes(size_t n_attrs, size_t n_justs = 0): external{nullptr} { reserve(n_attrs, n_justs); }
attributes(attributes const & rhs): external{nullptr} { operator = (rhs); }
attributes(attributes && rhs) noexcept : external{rhs.external} { rhs.external = nullptr; }
~attributes() noexcept { if (!is_inline()) free(external); }
void reserve(size_t target_num_attrs, size_t target_num_justs);
attributes & operator = (attributes const & rhs);
attributes & operator = (attributes && rhs) noexcept;
size_t num_attrs() const { return is_inline() ? local.n_attrs : external->n_attrs; }
size_t num_justs() const { return is_inline() ? 0 : external->n_justs; }
int16_t * user_attributes() { return is_inline() ? local.data : external->data; }
int16_t const * user_attributes() const { return is_inline() ? local.data : external->data; }
int16_t * just_info() { return is_inline() ? nullptr : external->data + external->n_attrs; }
int16_t const * just_info() const { return is_inline() ? nullptr : external->data + external->n_attrs; }
};
bool has_justify() const { return m_attrs.num_justs() != 0; };
void init_just_infos(Segment const & seg);
void attachTo(Slot *ap);
attributes m_attrs;
#if !defined GRAPHITE2_NTRACING
uint8_t m_gen;
#endif
template<class S> class _cluster_iterator;
template<class S> class _child_iterator;
public:
using cluster_iterator = _cluster_iterator<Slot>;
using const_cluster_iterator = _cluster_iterator<Slot const>;
using child_iterator = _child_iterator<Slot>;
using const_child_iterator = _child_iterator<Slot const>;
struct sentinal {};
using attrCode = gr_attrCode;
// static bool is_sentinal(Slot const & s) noexcept {
// return s.m_index == 0xffffffff
// && s.m_glyphid == 0xffff
// && s.m_realglyphid == 0xffff;
// }
// template<class T>
constexpr Slot(sentinal const &)
: Slot_data{0, {}, {}, {}, {}, {}, 0, 0, 0, 0, -1u, uint16_t(-1), uint16_t(-1), 0, 0, 0, {true,false,false,false,true,true,false}},
m_attrs{}
#if !defined GRAPHITE2_NTRACING
, m_gen{0}
#endif
{}
Slot(size_t num_attrs = 0) : Slot_data{}, m_attrs{num_attrs} {}
Slot(Slot const &);
Slot(Slot &&) noexcept;
Slot & operator=(Slot const &);
Slot & operator=(Slot &&) noexcept;
// Glyph
uint16_t gid() const { return m_glyphid; }
uint16_t glyph() const { return m_realglyphid ? m_realglyphid : m_glyphid; }
void glyph(Segment &seg, uint16_t glyphid, const GlyphFace * theGlyph = nullptr);
// void setRealGid(uint16_t realGid) { m_realglyphid = realGid; }
// Positioning
Position const & origin() const { return m_position; }
Position & origin() { return m_position; }
Position const & shift() const { return m_shift; }
// void adjKern(const Position &pos) { m_shift = m_shift + pos; m_advance = m_advance + pos; }
float advance() const { return m_advance.x; }
void advance(Position &val) { m_advance = val; }
Position attachOffset() const { return m_attach - m_with; }
Position const & advancePos() const { return m_advance; }
void position_shift(Position const & p) { m_position += p; }
void floodShift(Position adj, int depth = 0);
void scale_by(float scale) { m_position *= scale; /*m_advance *= scale; m_shift *= scale; m_with *= scale; m_attach *= scale; */}
Position collision_shift(Segment const & seg) const;
// Slot ordering
uint32_t index() const { return m_index; }
void index(uint32_t val) { m_index = val; }
int before() const { return m_before; }
void before(int ind) { m_before = ind; }
int after() const { return m_after; }
void after(int ind) { m_after = ind; }
int original() const { return m_original; }
void original(int ind) { m_original = ind; }
// // Flags
bool deleted() const { return m_flags.deleted; }
void deleted(bool state) { m_flags.deleted = state; }
bool copied() const { return m_flags.copied; }
void copied(bool state) { m_flags.copied = state; }
// bool positioned() const { return m_flags.positioned; }
// void positioned(bool state) { m_flags.positioned = state; }
bool last() const { return m_flags.last; }
void last(bool state) { m_flags.last = state; }
bool insertBefore() const { return !m_flags.inserted; }
void insertBefore(bool state) { m_flags.inserted = !state; }
void clusterhead(bool state) { m_flags.clusterhead = state; }
bool clusterhead() const { return m_flags.clusterhead ;}
// Bidi
uint8_t bidiLevel() const { return m_bidiLevel; }
void bidiLevel(uint8_t level) { m_bidiLevel = level; }
int8_t bidiClass() const { return m_bidiCls; }
void bidiClass(int8_t cls) { m_bidiCls = cls; }
// Operations
Position update_cluster_metric(Segment const & seg, bool const rtl, bool const is_final, float & clsb, float & crsb, unsigned depth=100);
void update(int numSlots, int numCharInfo, Position &relpos);
Position finalise(const Segment & seg, const Font* font, Position & base, Rect & bbox, uint8_t attrLevel, float & clusterMin, bool rtl, bool isFinal, int depth = 0);
int32_t clusterMetric(Segment const & seg, metrics metric, uint8_t attrLevel, bool rtl) const;
// Attributes
void setAttr(Segment & seg, attrCode ind, uint8_t subindex, int16_t val, const ShapingContext & map);
int getAttr(const Segment &seg, attrCode ind, uint8_t subindex) const;
int16_t const *userAttrs() const { return m_attrs.user_attributes(); }
// // Justification
int getJustify(const Segment &seg, uint8_t level, uint8_t subindex) const;
void setJustify(Segment &seg, uint8_t level, uint8_t subindex, int16_t value);
float just() const { return m_just; }
void just(float j) { m_just = j; }
// Clustering
cluster_iterator cluster();
const_cluster_iterator cluster() const;
child_iterator children();
const_child_iterator children() const;
const_cluster_iterator end() const noexcept;
bool isBase() const { return !m_parent_offset; }
bool isParent() const { return m_flags.children; }
Slot const * base() const noexcept;
Slot * base() noexcept { return const_cast<Slot*>(const_cast<Slot const *>(this)->base()); }
Slot const * attachedTo() const;
Slot * attachedTo() { return const_cast<Slot *>(const_cast<Slot const *>(this)->attachedTo());}
bool add_child(Slot *ap);
bool remove_child(Slot *ap);
bool has_base(const Slot *base) const;
#if !defined GRAPHITE2_NTRACING
uint8_t & generation() { return m_gen; }
uint8_t generation() const { return m_gen; }
#else
uint8_t & generation() { static uint8_t _v; return _v; }
#endif
CLASS_NEW_DELETE
};
template<class S>
class Slot::_cluster_iterator {
protected:
S* _s;
public:
using iterator_category = std::bidirectional_iterator_tag;
using value_type = S;
using pointer = value_type*;
using reference = value_type&;
constexpr _cluster_iterator() noexcept: _s{nullptr} {}
_cluster_iterator(pointer s) noexcept : _s{s} {}
bool operator == (_cluster_iterator<S const> const & rhs) const noexcept;
bool operator != (_cluster_iterator<S const> const & rhs) const noexcept;
reference operator*() const noexcept { return *_s; }
pointer operator->() const noexcept { return &operator*(); }
_cluster_iterator<S> & operator++() noexcept { assert(_s); if ((++_s)->clusterhead()) _s = nullptr; return *this; }
_cluster_iterator<S> operator++(int) noexcept { auto tmp(*this); operator++(); return tmp; }
_cluster_iterator<S> & operator--() noexcept { assert(_s); --_s; return *this; }
_cluster_iterator<S> operator--(int) noexcept { auto tmp(*this); operator--(); return tmp; }
operator _cluster_iterator<S const> const &() const noexcept { return *reinterpret_cast<_cluster_iterator<S const> const *>(this); }
};
template<class S>
inline
bool Slot::_cluster_iterator<S>::operator == (_cluster_iterator<S const> const & rhs) const noexcept {
return _s == rhs._s;
}
template<class S>
inline
bool Slot::_cluster_iterator<S>::operator != (_cluster_iterator<S const> const & rhs) const noexcept {
return !rhs.operator==(*this);
}
template<class S>
class Slot::_child_iterator: public _cluster_iterator<S> {
using base_t = Slot::_cluster_iterator<S>;
void _next_child(base_t & (base_t::*advfn)()) noexcept {
assert(base_t::_s);
auto parent = base_t::_s->attachedTo();
do {
(this->*advfn)();
} while (base_t::_s && base_t::_s->attachedTo() != parent);
}
public:
using base_t::iterator_category;
using base_t::value_type;
using base_t::pointer;
using base_t::reference;
constexpr _child_iterator(): _cluster_iterator<S>{} {}
_child_iterator(S * s): base_t{s} {}
_child_iterator<S> & operator++() noexcept { _next_child(&base_t::operator++); return *this; }
_child_iterator<S> operator++(int) noexcept { auto tmp(*this); operator++(); return tmp; }
_child_iterator<S> & operator--() noexcept { _next_child(&base_t::operator--); return *this; }
_child_iterator<S> operator--(int) noexcept { auto tmp(*this); operator--(); return tmp; }
gr_slot const * handle() const noexcept {
return base_t::_s
? base_t::_s->last() && base_t::_s->gid() == 0xffff && base_t::_s->deleted()
? nullptr
: static_cast<gr_slot const *>(base_t::_s)
: nullptr;
}
operator _child_iterator<S const>() const noexcept { return *reinterpret_cast<_child_iterator<S const> const *>(this); }
};
inline
auto Slot::cluster() -> cluster_iterator {
auto r = this;
while (!r->clusterhead()) --r;
return const_cast<Slot *>(r);
}
inline
auto Slot::cluster() const -> const_cluster_iterator {
auto r = this;
while (!r->clusterhead()) --r;
return const_cast<Slot *>(r);
}
inline
auto Slot::children() -> child_iterator {
for (auto ci = cluster(), end = cluster_iterator(); ci != end; ++ci)
if (ci->attachedTo() == this) return child_iterator(&*ci);
return child_iterator();
}
inline
auto Slot::children() const -> const_child_iterator {
for (auto ci = cluster(), end = const_cluster_iterator(); ci != end; ++ci)
if (ci->attachedTo() == this) return const_child_iterator(&*ci);
return const_child_iterator();
}
inline
auto Slot::end() const noexcept -> const_cluster_iterator {
return const_cluster_iterator();
}
inline
Slot::Slot(Slot && rhs) noexcept
: Slot_data(std::move(rhs)),
m_attrs(std::move(rhs.m_attrs))
#if !defined GRAPHITE2_NTRACING
,m_gen(rhs.m_gen)
#endif
{
m_parent_offset = 0;
}
inline
Slot::Slot(Slot const & rhs)
: Slot_data(rhs),
m_attrs(rhs.m_attrs)
#if !defined GRAPHITE2_NTRACING
,m_gen(rhs.m_gen)
#endif
{
m_parent_offset = 0;
}
inline
Slot const * Slot::base() const noexcept {
auto s = this;
while (s->m_parent_offset)
s += s->m_parent_offset;
return s;
}
inline
auto Slot::attachedTo() const -> Slot const * {
return m_parent_offset ? this + m_parent_offset : nullptr;
}
inline
void Slot::attachTo(Slot *ap) {
m_parent_offset = ap ? ap - this : 0;
}
} // namespace graphite2
struct gr_slot : public graphite2::Slot {};

View File

@ -0,0 +1,246 @@
/* GRAPHITE2 LICENSING
Copyright 2019, SIL International
All rights reserved.
This library is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published
by the Free Software Foundation; either version 2.1 of License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should also have received a copy of the GNU Lesser General Public
License along with this library in the file named "LICENSE".
If not, write to the Free Software Foundation, 51 Franklin Street,
Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
internet at http://www.fsf.org/licenses/lgpl.html.
Alternatively, the contents of this file may be used under the terms of the
Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
License, as published by the Free Software Foundation, either version 2
of the License or (at your option) any later version.
*/
#pragma once
#include <iterator>
#include "inc/vector.hpp"
#include "inc/Slot.h"
namespace graphite2 {
class SlotBuffer
{
using _storage = vector<Slot>;
public:
using value_type = _storage::value_type;
using difference_type = _storage::difference_type;
using size_type = _storage::size_type;
using reference = _storage::reference;
using const_reference = _storage::const_reference;
using pointer = _storage::pointer;
private:
_storage _values;
template<typename T> class _iterator;
SlotBuffer(SlotBuffer && rhs);
SlotBuffer & operator=(SlotBuffer && rhs);
CLASS_NEW_DELETE;
public:
using iterator = _iterator<value_type>;
using const_iterator = _iterator<const value_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
// TODO: Retire
iterator newSlot();
void freeSlot(iterator i);
SlotBuffer() : _values{Slot(Slot::sentinal())} {}
~SlotBuffer() { _values.clear(); }
void reserve(size_t size) { _values.reserve(size+1); }
// size_type num_user_attrs() const noexcept { return _attrs_size; }
// size_type num_just_levels() const noexcept { return _justs_size; }
iterator begin() noexcept;
const_iterator begin() const noexcept;
const_iterator cbegin() const noexcept;
iterator end() noexcept;
const_iterator end() const noexcept;
const_iterator cend() const noexcept;
// iterator rbegin() { return reverse_iterator(_head._next); }
// const_iterator rbegin() const { return cbegin(); }
// const_iterator crbegin() const { return const_reverse_iterator(_head._next); }
// iterator rend() { return reverse_iterator(&_head); }
// const_iterator rend() const { return cend(); }
// const_iterator crend() const { return const_reverse_iterator(&_head); }
reference front() { assert(!empty()); return _values.front(); }
const_reference front() const { assert(!empty()); return _values.front(); }
reference back() { assert(!empty()); return _values[_values.size()-2]; }
const_reference back() const { assert(!empty()); return _values[_values.size()-2]; }
bool empty() const noexcept { return size() == 0; }
size_t size() const noexcept { return _values.size()-1; }
iterator insert(const_iterator pos, value_type const &);
iterator insert(const_iterator pos, value_type &&);
template <typename... Args>
iterator emplace(const_iterator pos, Args &&... args);
void push_back(value_type const &v);
void push_back(value_type &&v);
template <typename... Args>
reference emplace_back(Args &&... args) { emplace(end(), std::forward<Args>(args)...); return back(); }
void pop_back();
iterator erase(const_iterator first);
iterator erase(const_iterator first, const_iterator last);
void clear();
void reverse();
void swap(SlotBuffer & rhs);
void splice(const_iterator pos, SlotBuffer & other, const_iterator first, const_iterator last);
void splice(const_iterator pos, SlotBuffer & other);
void splice(const_iterator pos, SlotBuffer & other, const_iterator it);
};
template <typename T>
class SlotBuffer::_iterator
{
SlotBuffer::_storage::iterator _i;
friend SlotBuffer;
constexpr _iterator(std::nullptr_t) noexcept: _i{nullptr} {}
_iterator(_storage::iterator i) : _i{i} {}
// operator _storage::const_iterator() const noexcept { return _i; }
public:
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = value_type*;
using reference = value_type&;
using opaque_type = gr_slot const *;
static _iterator<T> from(T * r) noexcept {
return _iterator<T>(r);
}
void to_base() noexcept {
_i = _i->base();
}
_iterator(): _iterator{nullptr} {}
_iterator(_storage::const_iterator i) : _i{const_cast<_storage::iterator>(i)} {}
_iterator(opaque_type p): _iterator{reinterpret_cast<T *>(const_cast<gr_slot*>(p))} {}
bool operator==(_iterator<T const> const & rhs) const noexcept { return _i == rhs._i; }
bool operator!=(_iterator<T const> const & rhs) const noexcept { return !operator==(rhs); }
reference operator*() const noexcept { return *_i; }
pointer operator->() const noexcept { return &operator*(); }
_iterator<T> & operator++() noexcept { assert(_i); ++_i; return *this;}
_iterator<T> operator++(int) noexcept { _iterator<T> tmp(*this); operator++(); return tmp; }
_iterator<T> & operator--() noexcept { assert(_i); --_i; return *this; }
_iterator<T> operator--(int) noexcept { _iterator<T> tmp(*this); operator--(); return tmp; }
// difference_type operator - (_iterator<T const> rhs) const noexcept { return std::distance(*this._i, rhs._i); }
bool is_valid() const noexcept { return _i != nullptr; }
opaque_type handle() const noexcept {
if (_i == nullptr || (_i->last() && _i->gid() == 0xffff && _i->deleted()))
return nullptr;
return reinterpret_cast<opaque_type>(_i);
}
// operator bool() const noexcept { return _p != nullptr; }
// operator pointer() const noexcept { return operator->(); }
operator _iterator<T const> const &() const noexcept { return *reinterpret_cast<_iterator<T const> const *>(this); }
};
template <typename... Args>
inline
auto SlotBuffer::emplace(const_iterator pos, Args &&... args) -> iterator {
return _values.emplace(pos._i, std::forward<Args>(args)...);
}
inline
void SlotBuffer::swap(SlotBuffer & rhs) {
SlotBuffer tmp{std::move(rhs)};
rhs = std::move(*this);
*this = std::move(tmp);
}
inline
auto SlotBuffer::newSlot() -> iterator {
return iterator::from(new Slot());
}
inline
void SlotBuffer::freeSlot(iterator i) {
delete i._i;
}
inline
auto SlotBuffer::begin() noexcept -> iterator {
return _values.begin();
}
inline auto SlotBuffer::begin() const noexcept -> const_iterator { return cbegin(); }
inline
auto SlotBuffer::cbegin() const noexcept -> const_iterator {
return _values.cbegin();
}
inline
auto SlotBuffer::end() noexcept -> iterator {
return _values.end()-1;
}
inline auto SlotBuffer::end() const noexcept -> const_iterator { return cend(); }
inline
auto SlotBuffer::cend() const noexcept -> const_iterator {
return _values.cend()-1;
}
inline void SlotBuffer::pop_back() { erase(end()); }
inline void SlotBuffer::clear() { erase(begin(), end()); }
inline
void SlotBuffer::splice(const_iterator pos, SlotBuffer & other) {
splice(pos, other, other.begin(), other.end());
}
inline
void SlotBuffer::splice(const_iterator pos, SlotBuffer & other, const_iterator it) {
splice(pos, other, it, std::next(it));
}
} // namespace graphite2

View File

@ -0,0 +1,145 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
#pragma once
#include <iterator>
#include <utility>
#include "inc/Main.h"
namespace graphite2 {
// A read-only packed fast sparse array of uint16_t with uint16_t keys.
// Like most container classes this has capacity and size properties and these
// refer to the number of stored entries and the number of addressable entries
// as normal. However due the sparse nature the capacity is always <= than the
// size.
class sparse
{
public:
typedef uint16_t key_type;
typedef uint16_t mapped_type;
typedef std::pair<const key_type, mapped_type> value_type;
private:
typedef unsigned long mask_t;
static const unsigned char SIZEOF_CHUNK = (sizeof(mask_t) - sizeof(key_type))*8;
struct chunk
{
mask_t mask:SIZEOF_CHUNK;
key_type offset;
};
static const chunk empty_chunk;
sparse(const sparse &);
sparse & operator = (const sparse &);
public:
template<typename I>
sparse(I first, const I last);
sparse() throw();
~sparse() throw();
operator bool () const throw();
mapped_type operator [] (const key_type k) const throw();
size_t capacity() const throw();
size_t size() const throw();
size_t _sizeof() const throw();
CLASS_NEW_DELETE;
private:
union {
chunk * map;
mapped_type * values;
} m_array;
key_type m_nchunks;
};
inline
sparse::sparse() throw() : m_nchunks(0)
{
m_array.map = const_cast<graphite2::sparse::chunk *>(&empty_chunk);
}
template <typename I>
sparse::sparse(I attr, const I last)
: m_nchunks(0)
{
m_array.map = 0;
// Find the maximum extent of the key space.
size_t n_values=0;
long lastkey = -1;
for (I i = attr; i != last; ++i, ++n_values)
{
const typename std::iterator_traits<I>::value_type v = *i;
if (v.second == 0) { --n_values; continue; }
if (v.first <= lastkey) { m_nchunks = 0; return; }
lastkey = v.first;
const key_type k = v.first / SIZEOF_CHUNK;
if (k >= m_nchunks) m_nchunks = k+1;
}
if (m_nchunks == 0)
{
m_array.map=const_cast<graphite2::sparse::chunk *>(&empty_chunk);
return;
}
m_array.values = grzeroalloc<mapped_type>((m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)
/ sizeof(mapped_type)
+ n_values);
if (m_array.values == 0)
return;
// coverity[forward_null : FALSE] Since m_array is union and m_array.values is not NULL
chunk * ci = m_array.map;
ci->offset = (m_nchunks*sizeof(chunk) + sizeof(mapped_type)-1)/sizeof(mapped_type);
mapped_type * vi = m_array.values + ci->offset;
for (; attr != last; ++attr, ++vi)
{
const typename std::iterator_traits<I>::value_type v = *attr;
if (v.second == 0) { --vi; continue; }
chunk * const ci_ = m_array.map + v.first/SIZEOF_CHUNK;
if (ci != ci_)
{
ci = ci_;
ci->offset = key_type(vi - m_array.values);
}
ci->mask |= 1UL << (SIZEOF_CHUNK - 1 - (v.first % SIZEOF_CHUNK));
*vi = v.second;
}
}
inline
sparse::operator bool () const throw()
{
return m_array.map != 0;
}
inline
size_t sparse::size() const throw()
{
return m_nchunks*SIZEOF_CHUNK;
}
inline
size_t sparse::_sizeof() const throw()
{
return sizeof(sparse) + capacity()*sizeof(mapped_type) + m_nchunks*sizeof(chunk);
}
} // namespace graphite2

View File

@ -0,0 +1,390 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstdint>
/*
Responsibility: Tim Eves
Last reviewed: Not yet.
Description:
Provides types required to represent the TTF basic types.
*/
//**********************************************************************************************
// Include files
//**********************************************************************************************
namespace graphite2
{
namespace TtfUtil
{
//**********************************************************************************************
// Forward declarations
//**********************************************************************************************
//**********************************************************************************************
// Type declarations
//**********************************************************************************************
typedef uint8_t byte;
typedef int int32_t;
typedef int16_t short_frac;
typedef int32_t fixed;
typedef int16_t fword;
typedef uint16_t ufword;
typedef int16_t f2dot14;
typedef uint32_t long_date_time[2];
//**********************************************************************************************
// Constants and enum types
//**********************************************************************************************/
enum
{
OneFix = 1<<16
};
//**********************************************************************************************
// Table declarations
//**********************************************************************************************
namespace Sfnt
{
#pragma pack(push,1) // We need this or the structure members aren't aligned
// correctly. Fortunately this form of pragma is supposed
// to be recognised by VS C++ too (at least according to
// MSDN).
struct OffsetSubTable
{
uint32_t scaler_type;
uint16_t num_tables,
search_range,
entry_selector,
range_shift;
struct Entry
{
uint32_t tag,
checksum,
offset,
length;
} table_directory[1];
enum ScalerType
{
TrueTypeMac = 0x74727565U,
TrueTypeWin = 0x00010000U,
Type1 = 0x74797031U
};
};
struct CharacterCodeMap
{
uint16_t version,
num_subtables;
struct
{
uint16_t platform_id,
platform_specific_id;
uint32_t offset;
} encoding[1];
};
struct CmapSubTable
{
uint16_t format,
length,
language;
};
struct CmapSubTableFormat4 : CmapSubTable
{
uint16_t seg_count_x2,
search_range,
entry_selector,
range_shift,
end_code[1];
// There are arrarys after this which need their
// start positions calculated since end_code is
// seg_count uint16s long.
};
struct CmapSubTableFormat12
{
fixed format;
uint32_t length,
language,
num_groups;
struct
{
uint32_t start_char_code,
end_char_code,
start_glyph_id;
} group[1];
};
struct FontHeader
{
fixed version,
font_revision;
uint32_t check_sum_adjustment,
magic_number;
uint16_t flags,
units_per_em;
long_date_time created,
modified;
fword x_min,
y_min,
x_max,
y_max;
uint16_t mac_style,
lowest_rec_ppem;
int16_t font_direction_hint,
index_to_loc_format,
glyph_data_format;
enum
{
MagicNumber = 0x5F0F3CF5,
GlypDataFormat = 0
};
enum {ShortIndexLocFormat, LongIndexLocFormat};
};
struct PostScriptGlyphName
{
fixed format,
italic_angle;
fword underline_position,
underline_thickness;
uint32_t is_fixed_pitch,
min_mem_type42,
max_mem_type42,
min_mem_type1,
max_mem_type1;
enum
{
Format1 = 0x10000,
Format2 = 0x20000,
Format25 = 0x28000,
Format3 = 0x30000,
Format4 = 0x40000
};
};
struct PostScriptGlyphName2 : PostScriptGlyphName
{
uint16_t number_of_glyphs,
glyph_name_index[1];
};
struct PostScriptGlyphName25 : PostScriptGlyphName
{
uint16_t number_of_glyphs;
int8_t offset[1];
};
struct PostScriptGlyphName3 : PostScriptGlyphName {};
struct PostScriptGlyphName4 : PostScriptGlyphName
{
uint16_t glyph_to_char_map[1];
};
struct HorizontalHeader
{
fixed version;
fword ascent,
descent,
line_gap;
ufword advance_width_max;
fword min_left_side_bearing,
max_left_side_bearing,
x_max_element;
int16_t caret_slope_rise,
caret_slope_run;
fword caret_offset;
int16_t reserved[4],
metric_data_format;
uint16_t num_long_hor_metrics;
};
struct MaximumProfile
{
fixed version;
uint16_t num_glyphs,
max_points,
max_contours,
max_component_points,
max_component_contours,
max_zones,
max_twilight_points,
max_storage,
max_function_defs,
max_instruction_defs,
max_stack_elements,
max_size_of_instructions,
max_component_elements,
max_component_depth;
};
typedef byte Panose[10];
struct Compatibility0
{
uint16_t version;
int16_t x_avg_char_width;
uint16_t weight_class,
width_class;
int16_t fs_type,
y_subscript_x_size,
y_subscript_y_size,
y_subscript_x_offset,
y_subscript_y_offset,
y_superscript_x_size,
y_superscript_y_size,
y_superscript_x_offset,
y_superscript_y_offset,
y_strikeout_size,
y_strikeout_position,
family_class;
Panose panose;
uint32_t unicode_range[4];
int8_t ach_vend_id[4];
uint16_t fs_selection,
fs_first_char_index,
fs_last_char_index, // Acording to Apple's spec this is where v0 should end
typo_ascender,
typo_descender,
type_linegap,
win_ascent,
win_descent;
enum
{
Italic =0x01,
Underscore=0x02,
Negative =0x04,
Outlined =0x08,
StrikeOut =0x10,
Bold =0x20
};
};
struct Compatibility1 : Compatibility0
{
uint32_t codepage_range[2];
};
struct Compatibility2 : Compatibility1
{
int16_t x_height,
cap_height;
uint16_t default_char,
break_char,
max_context;
};
struct Compatibility3 : Compatibility2 {};
typedef Compatibility3 Compatibility;
struct NameRecord
{
uint16_t platform_id,
platform_specific_id,
language_id,
name_id,
length,
offset;
enum {Unicode, Mactintosh, Reserved, Microsoft};
enum
{
Copyright, Family, Subfamily, UniqueSubfamily,
Fullname, Version, PostScript
};
};
struct LangTagRecord
{
uint16_t length,
offset;
};
struct FontNames
{
uint16_t format,
count,
string_offset;
NameRecord name_record[1];
};
struct HorizontalMetric
{
uint16_t advance_width;
int16_t left_side_bearing;
};
struct Glyph
{
int16_t number_of_contours;
fword x_min,
y_min,
x_max,
y_max;
};
struct SimpleGlyph : Glyph
{
uint16_t end_pts_of_contours[1];
enum
{
OnCurve = 0x01,
XShort = 0x02,
YShort = 0x04,
Repeat = 0x08,
XIsSame = 0x10,
XIsPos = 0x10,
YIsSame = 0x20,
YIsPos = 0x20
};
};
struct CompoundGlyph : Glyph
{
uint16_t flags,
glyph_index;
enum
{
Arg1Arg2Words = 0x01,
ArgsAreXYValues = 0x02,
RoundXYToGrid = 0x04,
HaveScale = 0x08,
MoreComponents = 0x20,
HaveXAndYScale = 0x40,
HaveTwoByTwo = 0x80,
HaveInstructions = 0x100,
UseMyMetrics = 0x200,
OverlapCompund = 0x400,
ScaledOffset = 0x800,
UnscaledOffset = 0x1000
};
};
#pragma pack(pop)
} // end of namespace Sfnt
} // end of namespace TtfUtil
} // end of namespace graphite2

View File

@ -0,0 +1,182 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
/*
Responsibility: Alan Ward
Last reviewed: Not yet.
Description:
Utility class for handling TrueType font files.
*/
#pragma once
#include <cstddef>
namespace graphite2
{
namespace TtfUtil
{
#define OVERFLOW_OFFSET_CHECK(p, o) (o + reinterpret_cast<size_t>(p) < reinterpret_cast<size_t>(p))
typedef long fontTableId32;
typedef unsigned short gid16;
#define TTF_TAG(a,b,c,d) ((a << 24UL) + (b << 16UL) + (c << 8UL) + (d))
// Enumeration used to specify a table in a TTF file
class Tag
{
unsigned int _v;
public:
Tag(const char n[5]) throw() : _v(TTF_TAG(n[0],n[1],n[2],n[3])) {}
Tag(const unsigned int tag) throw() : _v(tag) {}
operator unsigned int () const throw () { return _v; }
enum
{
Feat = TTF_TAG('F','e','a','t'),
Glat = TTF_TAG('G','l','a','t'),
Gloc = TTF_TAG('G','l','o','c'),
Sile = TTF_TAG('S','i','l','e'),
Silf = TTF_TAG('S','i','l','f'),
Sill = TTF_TAG('S','i','l','l'),
cmap = TTF_TAG('c','m','a','p'),
cvt = TTF_TAG('c','v','t',' '),
cryp = TTF_TAG('c','r','y','p'),
head = TTF_TAG('h','e','a','d'),
fpgm = TTF_TAG('f','p','g','m'),
gdir = TTF_TAG('g','d','i','r'),
glyf = TTF_TAG('g','l','y','f'),
hdmx = TTF_TAG('h','d','m','x'),
hhea = TTF_TAG('h','h','e','a'),
hmtx = TTF_TAG('h','m','t','x'),
loca = TTF_TAG('l','o','c','a'),
kern = TTF_TAG('k','e','r','n'),
LTSH = TTF_TAG('L','T','S','H'),
maxp = TTF_TAG('m','a','x','p'),
name = TTF_TAG('n','a','m','e'),
OS_2 = TTF_TAG('O','S','/','2'),
post = TTF_TAG('p','o','s','t'),
prep = TTF_TAG('p','r','e','p')
};
};
/*----------------------------------------------------------------------------------------------
Class providing utility methods to parse a TrueType font file (TTF).
Callling application handles all file input and memory allocation.
Assumes minimal knowledge of TTF file format.
----------------------------------------------------------------------------------------------*/
////////////////////////////////// tools to find & check TTF tables
bool GetHeaderInfo(size_t & lOffset, size_t & lSize);
bool CheckHeader(const void * pHdr);
bool GetTableDirInfo(const void * pHdr, size_t & lOffset, size_t & lSize);
bool GetTableInfo(const Tag TableTag, const void * pHdr, const void * pTableDir,
size_t & lOffset, size_t & lSize);
bool CheckTable(const Tag TableId, const void * pTable, size_t lTableSize);
////////////////////////////////// simple font wide info
size_t GlyphCount(const void * pMaxp);
#ifdef ALL_TTFUTILS
size_t MaxCompositeComponentCount(const void * pMaxp);
size_t MaxCompositeLevelCount(const void * pMaxp);
size_t LocaGlyphCount(size_t lLocaSize, const void * pHead); // throw (std::domain_error);
#endif
int DesignUnits(const void * pHead);
#ifdef ALL_TTFUTILS
int HeadTableCheckSum(const void * pHead);
void HeadTableCreateTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD);
void HeadTableModifyTime(const void * pHead, unsigned int * pnDateBC, unsigned int * pnDateAD);
bool IsItalic(const void * pHead);
int FontAscent(const void * pOs2);
int FontDescent(const void * pOs2);
bool FontOs2Style(const void *pOs2, bool & fBold, bool & fItalic);
bool Get31EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize);
bool Get31EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize);
bool Get30EngFamilyInfo(const void * pName, size_t & lOffset, size_t & lSize);
bool Get30EngFullFontInfo(const void * pName, size_t & lOffset, size_t & lSize);
int PostLookup(const void * pPost, size_t lPostSize, const void * pMaxp,
const char * pPostName);
#endif
////////////////////////////////// utility methods helpful for name table
bool GetNameInfo(const void * pName, int nPlatformId, int nEncodingId,
int nLangId, int nNameId, size_t & lOffset, size_t & lSize);
//size_t NameTableLength(const byte * pTable);
#ifdef ALL_TTFUTILS
int GetLangsForNames(const void * pName, int nPlatformId, int nEncodingId,
int *nameIdList, int cNameIds, short *langIdList);
void SwapWString(void * pWStr, size_t nSize = 0); // throw (std::invalid_argument);
#endif
////////////////////////////////// cmap lookup tools
const void * FindCmapSubtable(const void * pCmap, int nPlatformId = 3,
int nEncodingId = 1, size_t length = 0);
bool CheckCmapSubtable4(const void * pCmap31, const void * pCmapEnd /*, unsigned int maxgid*/);
gid16 CmapSubtable4Lookup(const void * pCmapSubtabel4, unsigned int nUnicodeId, int rangeKey = 0);
unsigned int CmapSubtable4NextCodepoint(const void *pCmap31, unsigned int nUnicodeId,
int * pRangeKey = 0);
bool CheckCmapSubtable12(const void *pCmap310, const void * pCmapEnd /*, unsigned int maxgid*/);
gid16 CmapSubtable12Lookup(const void * pCmap310, unsigned int uUnicodeId, int rangeKey = 0);
unsigned int CmapSubtable12NextCodepoint(const void *pCmap310, unsigned int nUnicodeId,
int * pRangeKey = 0);
///////////////////////////////// horizontal metric data for a glyph
bool HorMetrics(gid16 nGlyphId, const void * pHmtx, size_t lHmtxSize,
const void * pHhea, int & nLsb, unsigned int & nAdvWid);
////////////////////////////////// primitives for loca and glyf lookup
size_t LocaLookup(gid16 nGlyphId, const void * pLoca, size_t lLocaSize,
const void * pHead); // throw (std::out_of_range);
void * GlyfLookup(const void * pGlyf, size_t lGlyfOffset, size_t lTableLen);
////////////////////////////////// primitves for simple glyph data
bool GlyfBox(const void * pSimpleGlyf, int & xMin, int & yMin,
int & xMax, int & yMax);
#ifdef ALL_TTFUTILS
int GlyfContourCount(const void * pSimpleGlyf);
bool GlyfContourEndPoints(const void * pSimpleGlyf, int * prgnContourEndPoint,
int cnPointsTotal, size_t & cnPoints);
bool GlyfPoints(const void * pSimpleGlyf, int * prgnX, int * prgnY,
char * prgbFlag, int cnPointsTotal, int & cnPoints);
// primitive to find the glyph ids in a composite glyph
bool GetComponentGlyphIds(const void * pSimpleGlyf, int * prgnCompId,
size_t cnCompIdTotal, size_t & cnCompId);
// primitive to find the placement data for a component in a composite glyph
bool GetComponentPlacement(const void * pSimpleGlyf, int nCompId,
bool fOffset, int & a, int & b);
// primitive to find the transform data for a component in a composite glyph
bool GetComponentTransform(const void * pSimpleGlyf, int nCompId,
float & flt11, float & flt12, float & flt21, float & flt22, bool & fTransOffset);
#endif
////////////////////////////////// operate on composite or simple glyph (auto glyf lookup)
void * GlyfLookup(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
size_t lGlyfSize, size_t lLocaSize, const void * pHead); // primitive used by below methods
#ifdef ALL_TTFUTILS
// below are primary user methods for handling glyf data
bool IsSpace(gid16 nGlyphId, const void * pLoca, size_t lLocaSize, const void * pHead);
bool IsDeepComposite(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
size_t lGlyfSize, size_t lLocaSize, const void * pHead);
bool GlyfBox(gid16 nGlyphId, const void * pGlyf, const void * pLoca, size_t lGlyfSize, size_t lLocaSize,
const void * pHead, int & xMin, int & yMin, int & xMax, int & yMax);
bool GlyfContourCount(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
size_t lGlyfSize, size_t lLocaSize, const void *pHead, size_t & cnContours);
bool GlyfContourEndPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
size_t lGlyfSize, size_t lLocaSize, const void * pHead, int * prgnContourEndPoint, size_t cnPoints);
bool GlyfPoints(gid16 nGlyphId, const void * pGlyf, const void * pLoca,
size_t lGlyfSize, size_t lLocaSize, const void * pHead, const int * prgnContourEndPoint, size_t cnEndPoints,
int * prgnX, int * prgnY, bool * prgfOnCurve, size_t cnPoints);
// utitily method used by high-level GlyfPoints
bool SimplifyFlags(char * prgbFlags, int cnPoints);
bool CalcAbsolutePoints(int * prgnX, int * prgnY, int cnPoints);
#endif
} // end of namespace TtfUtil
} // end of namespace graphite2

View File

@ -0,0 +1,228 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
#pragma once
#include <cstdlib>
#include "inc/Main.h"
namespace graphite2 {
typedef uint32_t uchar_t;
template <int N>
struct _utf_codec
{
typedef uchar_t codeunit_t;
static void put(codeunit_t * cp, const uchar_t , int8_t & len) throw();
static uchar_t get(const codeunit_t * cp, int8_t & len) throw();
static bool validate(const codeunit_t * s, const codeunit_t * const e) throw();
};
template <>
struct _utf_codec<32>
{
private:
static const uchar_t limit = 0x110000;
public:
typedef uint32_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
*cp = usv; l = 1;
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
if (cp[0] < limit) { l = 1; return cp[0]; }
else { l = -1; return 0xFFFD; }
}
inline
static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
{
return s <= e;
}
};
template <>
struct _utf_codec<16>
{
private:
static const int32_t lead_offset = 0xD800 - (0x10000 >> 10);
static const int32_t surrogate_offset = 0x10000 - (0xD800 << 10) - 0xDC00;
public:
typedef uint16_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
if (usv < 0x10000) { l = 1; cp[0] = codeunit_t(usv); }
else
{
cp[0] = codeunit_t(lead_offset + (usv >> 10));
cp[1] = codeunit_t(0xDC00 + (usv & 0x3FF));
l = 2;
}
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
const uint32_t uh = cp[0];
l = 1;
if (uh < 0xD800|| uh > 0xDFFF) { return uh; }
if (uh > 0xDBFF) { l = -1; return 0xFFFD; }
const uint32_t ul = cp[1];
if (ul < 0xDC00 || ul > 0xDFFF) { l = -1; return 0xFFFD; }
++l;
return (uh<<10) + ul + surrogate_offset;
}
inline
static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
{
const ptrdiff_t n = e-s;
if (n <= 0) return n == 0;
const uint32_t u = *(e-1); // Get the last codepoint
return (u < 0xD800 || u > 0xDBFF);
}
};
template <>
struct _utf_codec<8>
{
private:
static const int8_t sz_lut[16];
static const byte mask_lut[5];
static const uchar_t limit = 0x110000;
public:
typedef uint8_t codeunit_t;
inline
static void put(codeunit_t * cp, const uchar_t usv, int8_t & l) throw()
{
if (usv < 0x80) {l = 1; cp[0] = usv; return; }
if (usv < 0x0800) {l = 2; cp[0] = 0xC0 + (usv >> 6); cp[1] = 0x80 + (usv & 0x3F); return; }
if (usv < 0x10000) {l = 3; cp[0] = 0xE0 + (usv >> 12); cp[1] = 0x80 + ((usv >> 6) & 0x3F); cp[2] = 0x80 + (usv & 0x3F); return; }
else {l = 4; cp[0] = 0xF0 + (usv >> 18); cp[1] = 0x80 + ((usv >> 12) & 0x3F); cp[2] = 0x80 + ((usv >> 6) & 0x3F); cp[3] = 0x80 + (usv & 0x3F); return; }
}
inline
static uchar_t get(const codeunit_t * cp, int8_t & l) throw()
{
const int8_t seq_sz = sz_lut[*cp >> 4];
uchar_t u = *cp & mask_lut[seq_sz];
l = 1;
bool toolong = false;
switch(seq_sz) {
case 4: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong = (u < 0x10); GR_FALLTHROUGH;
// no break
case 3: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x20); GR_FALLTHROUGH;
// no break
case 2: u <<= 6; u |= *++cp & 0x3F; if (*cp >> 6 != 2) break; ++l; toolong |= (u < 0x80); GR_FALLTHROUGH;
// no break
case 1: break;
case 0: l = -1; return 0xFFFD;
}
if (l != seq_sz || toolong || u >= limit)
{
l = -l;
return 0xFFFD;
}
return u;
}
inline
static bool validate(const codeunit_t * s, const codeunit_t * const e) throw()
{
const ptrdiff_t n = e-s;
if (n <= 0) return n == 0;
s += (n-1);
if (*s < 0x80) return true;
if (*s >= 0xC0) return false;
if (n == 1) return true;
if (*--s < 0x80) return true;
if (*s >= 0xE0) return false;
if (n == 2 || *s >= 0xC0) return true;
if (*--s < 0x80) return true;
if (*s >= 0xF0) return false;
return true;
}
};
template <typename C>
class _utf_iterator
{
typedef _utf_codec<sizeof(C)*8> codec;
C * cp;
mutable int8_t sl;
public:
typedef C codeunit_type;
typedef uchar_t value_type;
typedef uchar_t * pointer;
class reference
{
const _utf_iterator & _i;
reference(const _utf_iterator & i): _i(i) {}
public:
operator value_type () const throw () { return codec::get(_i.cp, _i.sl); }
reference & operator = (const value_type usv) throw() { codec::put(_i.cp, usv, _i.sl); return *this; }
friend class _utf_iterator;
};
_utf_iterator(const void * us=0) : cp(reinterpret_cast<C *>(const_cast<void *>(us))), sl(1) { }
_utf_iterator & operator ++ () { cp += abs(sl); return *this; }
_utf_iterator operator ++ (int) { _utf_iterator tmp(*this); operator++(); return tmp; }
bool operator == (const _utf_iterator & rhs) const throw() { return cp >= rhs.cp; }
bool operator != (const _utf_iterator & rhs) const throw() { return !operator==(rhs); }
reference operator * () const throw() { return *this; }
pointer operator ->() const throw() { return &operator *(); }
operator codeunit_type * () const throw() { return cp; }
bool error() const throw() { return sl < 1; }
bool validate(const _utf_iterator & e) { return codec::validate(cp, e.cp); }
};
template <typename C>
struct utf
{
typedef typename _utf_codec<sizeof(C)*8>::codeunit_t codeunit_t;
typedef _utf_iterator<C> iterator;
typedef _utf_iterator<const C> const_iterator;
inline
static bool validate(codeunit_t * s, codeunit_t * e) throw() {
return _utf_codec<sizeof(C)*8>::validate(s,e);
}
};
typedef utf<uint32_t> utf32;
typedef utf<uint16_t> utf16;
typedef utf<uint8_t> utf8;
} // namespace graphite2

View File

@ -0,0 +1,129 @@
// SPDX-License-Identifier: MIT
// Copyright 2012, SIL International, All rights reserved.
#pragma once
#include <cstddef>
namespace graphite2
{
#if defined GRAPHITE2_BUILTINS && (defined __GNUC__ || defined __clang__)
template<typename T>
inline unsigned int bit_set_count(T v)
{
return __builtin_popcount(v);
}
template<>
inline unsigned int bit_set_count(int16_t v)
{
return __builtin_popcount(static_cast<uint16_t>(v));
}
template<>
inline unsigned int bit_set_count(int8_t v)
{
return __builtin_popcount(static_cast<uint8_t>(v));
}
template<>
inline unsigned int bit_set_count(unsigned long v)
{
return __builtin_popcountl(v);
}
template<>
inline unsigned int bit_set_count(signed long v)
{
return __builtin_popcountl(v);
}
template<>
inline unsigned int bit_set_count(unsigned long long v)
{
return __builtin_popcountll(v);
}
template<>
inline unsigned int bit_set_count(signed long long v)
{
return __builtin_popcountll(v);
}
#else
template<typename T>
inline unsigned int bit_set_count(T v)
{
static size_t const ONES = ~0;
v = v - ((v >> 1) & T(ONES/3)); // temp
v = (v & T(ONES/15*3)) + ((v >> 2) & T(ONES/15*3)); // temp
v = (v + (v >> 4)) & T(ONES/255*15); // temp
return (T)(v * T(ONES/255)) >> (sizeof(T)-1)*8; // count
}
#endif
//TODO: Changed these to uintmax_t when we go to C++11
template<int S>
inline size_t _mask_over_val(size_t v)
{
v = _mask_over_val<S/2>(v);
v |= v >> S*4;
return v;
}
//TODO: Changed these to uintmax_t when we go to C++11
template<>
inline size_t _mask_over_val<1>(size_t v)
{
v |= v >> 1;
v |= v >> 2;
v |= v >> 4;
return v;
}
template<typename T>
inline T mask_over_val(T v)
{
return T(_mask_over_val<sizeof(T)>(v));
}
template<typename T>
inline unsigned long next_highest_power2(T v)
{
return _mask_over_val<sizeof(T)>(v-1)+1;
}
template<typename T>
inline unsigned int log_binary(T v)
{
return bit_set_count(mask_over_val(v))-1;
}
template<typename T>
inline T has_zero(const T x)
{
return (x - T(~T(0)/255)) & ~x & T(~T(0)/255*128);
}
template<typename T>
inline T zero_bytes(const T x, unsigned char n)
{
const T t = T(~T(0)/255*n);
return T((has_zero(x^t) >> 7)*n);
}
#if 0
inline float float_round(float x, uint32_t m)
{
*reinterpret_cast<unsigned int *>(&x) &= m;
return *reinterpret_cast<float *>(&x);
}
#endif
}

View File

@ -0,0 +1,74 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
// debug.h
//
// Created on: 22 Dec 2011
// Author: tim
#pragma once
#if !defined GRAPHITE2_NTRACING
#include <utility>
#include "inc/json.h"
#include "inc/Position.h"
#include "inc/SlotBuffer.h"
namespace graphite2
{
class CharInfo;
class Segment;
class Slot;
typedef std::pair<Segment const * const, Slot const * const> dslot;
struct objectid
{
char name[16];
objectid(SlotBuffer::const_iterator const s) noexcept;
objectid(Segment const & seg) noexcept { set_name(&seg, 0); }
private:
void set_name(void const * addr, uint16_t generation) noexcept;
};
inline
objectid::objectid(SlotBuffer::const_iterator const s) noexcept
{
void const * o = s.handle() ? reinterpret_cast<void const *>(s->original()+1) : nullptr;
set_name(0, o ? s->generation() : 0);
}
json & operator << (json & j, const Position &) throw();
json & operator << (json & j, const Rect &) throw();
json & operator << (json & j, const CharInfo &) throw();
json & operator << (json & j, const dslot &) throw();
json & operator << (json & j, const objectid &) throw();
json & operator << (json & j, const telemetry &) throw();
inline
json & operator << (json & j, const Position & p) throw()
{
return j << json::flat << json::array << p.x << p.y << json::close;
}
inline
json & operator << (json & j, const Rect & p) throw()
{
return j << json::flat << json::array << p.bl.x << p.bl.y << p.tr.x << p.tr.y << json::close;
}
inline
json & operator << (json & j, const objectid & sid) throw()
{
return j << sid.name;
}
} // namespace graphite2
#endif //!defined GRAPHITE2_NTRACING

View File

@ -0,0 +1,155 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
// JSON pretty printer for graphite font debug output logging.
// Created on: 15 Dec 2011
// Author: Tim Eves
#pragma once
#include "inc/Main.h"
#include <cassert>
#include <cstdio>
#include <cstdint>
#include "inc/vector.hpp"
namespace graphite2 {
class json
{
// Prevent copying
json(const json &);
json & operator = (const json &);
typedef void (*_context_t)(json &);
FILE * const _stream;
char _contexts[128], // context stack
* _context, // current context (top of stack)
* _flatten; // if !0 points to context above which
// pretty printed output should occur.
vector<void *> _env;
void context(const char current) throw();
void indent(const int d=0) throw();
void push_context(const char, const char) throw();
void pop_context() throw();
public:
class closer;
using string = const char *;
using number = double;
enum class integer : std::intmax_t {};
enum class integer_u : std::uintmax_t {};
using boolean = bool;
static const std::nullptr_t null;
void setenv(unsigned int index, void *val) { _env.reserve(index + 1); if (index >= _env.size()) _env.insert(_env.end(), _env.size() - index + 1, 0); _env[index] = val; }
void *getenv(unsigned int index) const { return _env[index]; }
const vector<void *> &getenvs() const { return _env; }
static void flat(json &) throw();
static void close(json &) throw();
static void object(json &) throw();
static void array(json &) throw();
static void item(json &) throw();
json(FILE * stream) throw();
~json() throw ();
FILE * stream() const throw();
json & operator << (string) throw();
json & operator << (number) throw();
json & operator << (integer) throw();
json & operator << (integer_u) throw();
json & operator << (boolean) throw();
json & operator << (std::nullptr_t) throw();
json & operator << (_context_t) throw();
operator bool() const throw();
bool good() const throw();
bool eof() const throw();
CLASS_NEW_DELETE;
};
class json::closer
{
// Prevent copying.
closer(const closer &);
closer & operator = (const closer &);
json * const _j;
public:
closer(json * const j) : _j(j) {}
~closer() throw() { if (_j) *_j << close; }
};
inline
json::json(FILE * s) throw()
: _stream(s), _context(_contexts), _flatten(0)
{
if (good())
fflush(s);
}
inline
json::~json() throw ()
{
while (_context > _contexts) pop_context();
}
inline
FILE * json::stream() const throw() { return _stream; }
inline
json & json::operator << (json::_context_t ctxt) throw()
{
ctxt(*this);
return *this;
}
inline
json & operator << (json & j, signed char d) throw() { return j << json::integer(d); }
inline
json & operator << (json & j, unsigned char d) throw() { return j << json::integer_u(d); }
inline
json & operator << (json & j, short int d) throw() { return j << json::integer(d); }
inline
json & operator << (json & j, unsigned short int d) throw() { return j << json::integer_u(d); }
inline
json & operator << (json & j, int d) throw() { return j << json::integer(d); }
inline
json & operator << (json & j, unsigned int d) throw() { return j << json::integer_u(d); }
inline
json & operator << (json & j, long int d) throw() { return j << json::integer(d); }
inline
json & operator << (json & j, unsigned long int d) throw() { return j << json::integer_u(d); }
inline
json & operator << (json & j, long long int d) throw() { return j << json::integer(d); }
inline
json & operator << (json & j, unsigned long long int d) throw() { return j << json::integer_u(d); }
inline
json::operator bool() const throw() { return good(); }
inline
bool json::good() const throw() { return _stream && ferror(_stream) == 0; }
inline
bool json::eof() const throw() { return feof(_stream) != 0; }
} // namespace graphite2

View File

@ -0,0 +1,266 @@
// SPDX-License-Identifier: MIT
// Copyright 2019, SIL International, All rights reserved.
#pragma once
#include <cassert>
#include <memory>
#include <iterator>
#include <type_traits>
#include "inc/Main.h"
namespace graphite2 {
template<typename T>
class list
{
public:
using value_type = T;
using difference_type = ptrdiff_t;
using size_type = size_t;
using reference = value_type&;
using const_reference = const value_type&;
using pointer = value_type*;
using const_pointer = const value_type*;
private:
template <typename> class _iterator;
struct _node_linkage {
_node_linkage * _next;
_node_linkage * _prev;
void link(_node_linkage &);
void unlink();
void splice(_node_linkage & start, _node_linkage & end);
};
struct _node : public _node_linkage {
value_type _value;
};
_node * _allocate_node();
void _release_node(_node * const p) { free(p); }
_node_linkage _head;
size_type _size;
public:
using iterator = _iterator<value_type>;
using const_iterator = _iterator<const value_type>;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
list() : _head{&_head,&_head}, _size{0} {}
list(const list<T> &rhs): list(rhs.begin(), rhs.end()) {}
template <typename I>
list(I first, const I last) { insert(begin(), first, last); }
~list() { clear(); }
reference front() { assert(!empty()); return static_cast<_node*>(_head._next)->_value; }
const_reference front() const { assert(!empty()); return static_cast<_node*>(_head._next)->_value; }
reference back() { assert(!empty()); return static_cast<_node*>(_head._prev)->_value; }
const_reference back() const { assert(!empty()); return static_cast<_node*>(_head._prev)->_value; }
iterator begin() noexcept { return iterator(_head._next); }
const_iterator begin() const noexcept { return cbegin(); }
const_iterator cbegin() const noexcept { return const_iterator(_head._next); }
iterator end() noexcept { return iterator(&_head); }
const_iterator end() const noexcept { return cend(); }
const_iterator cend() const noexcept { return const_iterator(&_head); }
iterator rbegin() noexcept { return reverse_iterator(_head._next); }
const_iterator rbegin() const noexcept { return crbegin(); }
const_iterator crbegin() const noexcept { return const_reverse_iterator(_head._next); }
iterator rend() noexcept { return reverse_iterator(&_head); }
const_iterator rend() const noexcept { return crend(); }
const_iterator crend() const noexcept { return const_reverse_iterator(&_head); }
bool empty() const noexcept { return _head._next == &_head; }
size_t size() const noexcept { return _size; }
void clear() noexcept { erase(begin(),end()); }
iterator insert(const_iterator pos, value_type const &);
iterator insert(const_iterator pos, value_type &&);
// iterator insert(const_iterator pos, size_type, value_type const &);
template <typename I>
iterator insert(const_iterator pos, I first, I last);
void push_back(value_type const & value) { insert(cend(), value); }
void push_front(value_type const & value) { insert(cbegin(), value); }
void pop_front() { erase(iterator(_head._next)); }
void pop_back() { erase(iterator(_head._prev)); }
iterator erase(iterator pos);
iterator erase(iterator first, iterator last);
};
template <typename L>
template <typename T>
class list<L>::_iterator
{
_node_linkage const * _p;
friend list<L>;
// Find the address of the enclosing object of class S from a pointer to
// it's contained member m.
// This is extermely dangerous if performed on a T* that is not an
// agreggate member of class S.
template<class S>
static constexpr S* container_of(void const *p, T S::* m) noexcept {
return reinterpret_cast<S *>(
reinterpret_cast<uint8_t const *>(p)
- reinterpret_cast<uint8_t const *>(&(((S *)0)->*m)));
}
// Cast a _node_linkage to a _node<T>
static _node * node(_iterator<T> const &i) noexcept {
return const_cast<_node *>(static_cast<_node const *>(i._p));
}
public:
using iterator_category = std::bidirectional_iterator_tag;
using difference_type = ptrdiff_t;
using value_type = T;
using pointer = value_type*;
using reference = value_type&;
_iterator(std::nullptr_t): _iterator(nullptr) {}
_iterator(_node_linkage const *p) noexcept: _p{p} {}
bool operator==(_iterator<T> rhs) const noexcept { return _p == rhs._p; }
bool operator!=(_iterator<T> rhs) const noexcept { return !operator==(rhs); }
reference operator*() const noexcept { return node(*this)->_value; }
pointer operator->() const noexcept { return &operator*(); }
_iterator<T> & operator++() noexcept { _p = _p->_next; return *this; }
_iterator<T> operator++(int) noexcept { _iterator<T> tmp(*this); operator++(); return tmp; }
_iterator<T> & operator--() noexcept { _p = _p->_prev; return *this; }
_iterator<T> operator--(int) noexcept { _iterator<T> tmp(*this); operator--(); return tmp; }
operator pointer() const noexcept { return operator->(); }
operator _iterator<T const>() noexcept { return _iterator<T const>(_p); }
};
//
template<typename T>
auto list<T>::_node_linkage::splice(_node_linkage & start, _node_linkage & end) -> void
{
// Unlink from current position
start._prev->_next = end._next;
end._next->_prev = start._prev;
// Link in before this node.
start._prev = this->_prev;
end._next = this;
start._prev->_next = &start;
end._next->_prev = &end;
}
template<typename T>
auto list<T>::_node_linkage::link(_node_linkage & pos) -> void
{
pos.splice(*this, *this);
}
template<typename T>
auto list<T>::_node_linkage::unlink() -> void
{
_prev->_next = _next;
_next->_prev = _prev;
_prev = _next = this;
}
template <typename T>
auto list<T>::_allocate_node() -> _node * {
auto node = grzeroalloc<_node>(1);
node->_next = node->_prev = node;
return node;
}
template<typename T>
auto list<T>::insert(const_iterator pos, value_type const & value) -> iterator
{
assert(pos._p);
auto node = _allocate_node();
if (!node) return end();
node->link(*const_cast<_node_linkage *>(pos._p));
new (&node->_value) T(value);
++_size;
return iterator(node);
}
template<typename T>
auto list<T>::insert(const_iterator pos, value_type && value) -> iterator
{
assert(pos._p);
auto node = _allocate_node();
if (!node) return end();
node->link(*const_cast<_node_linkage *>(pos._p));
new (&node->_value) T(std::move(value));
++_size;
return iterator(node);
}
// template<typename T>
// auto list<T>::insert(const_iterator pos, size_type count, value_type const & value) -> iterator
// {
// assert(pos._p);
// auto r = iterator(--pos._p);
// while (count--) insert(pos, value);
// return ++r;
// }
template <typename T>
template <class I>
auto list<T>::insert(const_iterator pos, I first, I last) -> iterator
{
assert(last._p);
auto r = --iterator(pos._p);
while (first != last) insert(pos, *first++);
return ++r;
}
template<typename T>
auto list<T>::erase(iterator pos) -> iterator
{
assert(pos._p);
auto node = iterator::node(pos++);
node->unlink();
_release_node(node);
--_size;
return pos;
}
template<typename T>
auto list<T>::erase(iterator first, iterator const last) -> iterator
{
assert(last._p);
while (first != last) erase(first++);
return first;
}
} //namespace graphite

View File

@ -0,0 +1,427 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
#include <cstring>
#include <cassert>
#include "inc/Main.h"
namespace graphite2 {
struct IsoLangEntry
{
unsigned short mnLang;
char maLangStr[4];
char maCountry[3];
};
// Windows Language ID, Locale ISO-639 language, country code as used in
// naming table of OpenType fonts
const IsoLangEntry LANG_ENTRIES[] = {
{ 0x0401, "ar","SA" }, // Arabic Saudi Arabia
{ 0x0402, "bg","BG" }, // Bulgarian Bulgaria
{ 0x0403, "ca","ES" }, // Catalan Catalan
{ 0x0404, "zh","TW" }, // Chinese Taiwan
{ 0x0405, "cs","CZ" }, // Czech Czech Republic
{ 0x0406, "da","DK" }, // Danish Denmark
{ 0x0407, "de","DE" }, // German Germany
{ 0x0408, "el","GR" }, // Greek Greece
{ 0x0409, "en","US" }, // English United States
{ 0x040A, "es","ES" }, // Spanish (Traditional Sort) Spain
{ 0x040B, "fi","FI" }, // Finnish Finland
{ 0x040C, "fr","FR" }, // French France
{ 0x040D, "he","IL" }, // Hebrew Israel
{ 0x040E, "hu","HU" }, // Hungarian Hungary
{ 0x040F, "is","IS" }, // Icelandic Iceland
{ 0x0410, "it","IT" }, // Italian Italy
{ 0x0411, "jp","JP" }, // Japanese Japan
{ 0x0412, "ko","KR" }, // Korean Korea
{ 0x0413, "nl","NL" }, // Dutch Netherlands
{ 0x0414, "no","NO" }, // Norwegian (Bokmal) Norway
{ 0x0415, "pl","PL" }, // Polish Poland
{ 0x0416, "pt","BR" }, // Portuguese Brazil
{ 0x0417, "rm","CH" }, // Romansh Switzerland
{ 0x0418, "ro","RO" }, // Romanian Romania
{ 0x0419, "ru","RU" }, // Russian Russia
{ 0x041A, "hr","HR" }, // Croatian Croatia
{ 0x041B, "sk","SK" }, // Slovak Slovakia
{ 0x041C, "sq","AL" }, // Albanian Albania
{ 0x041D, "sv","SE" }, // Swedish Sweden
{ 0x041E, "th","TH" }, // Thai Thailand
{ 0x041F, "tr","TR" }, // Turkish Turkey
{ 0x0420, "ur","PK" }, // Urdu Islamic Republic of Pakistan
{ 0x0421, "id","ID" }, // Indonesian Indonesia
{ 0x0422, "uk","UA" }, // Ukrainian Ukraine
{ 0x0423, "be","BY" }, // Belarusian Belarus
{ 0x0424, "sl","SI" }, // Slovenian Slovenia
{ 0x0425, "et","EE" }, // Estonian Estonia
{ 0x0426, "lv","LV" }, // Latvian Latvia
{ 0x0427, "lt","LT" }, // Lithuanian Lithuania
{ 0x0428, "tg","TJ" }, // Tajik (Cyrillic) Tajikistan
{ 0x042A, "vi","VN" }, // Vietnamese Vietnam
{ 0x042B, "hy","AM" }, // Armenian Armenia
{ 0x042C, "az","AZ" }, // Azeri (Latin) Azerbaijan
{ 0x042D, "eu","" }, // Basque Basque
{ 0x042E, "hsb","DE" }, // Upper Sorbian Germany
{ 0x042F, "mk","MK" }, // Macedonian (FYROM) Former Yugoslav Republic of Macedonia
{ 0x0432, "tn","ZA" }, // Setswana South Africa
{ 0x0434, "xh","ZA" }, // isiXhosa South Africa
{ 0x0435, "zu","ZA" }, // isiZulu South Africa
{ 0x0436, "af","ZA" }, // Afrikaans South Africa
{ 0x0437, "ka","GE" }, // Georgian Georgia
{ 0x0438, "fo","FO" }, // Faroese Faroe Islands
{ 0x0439, "hi","IN" }, // Hindi India
{ 0x043A, "mt","MT" }, // Maltese Malta
{ 0x043B, "se","NO" }, // Sami (Northern) Norway
{ 0x043E, "ms","MY" }, // Malay Malaysia
{ 0x043F, "kk","KZ" }, // Kazakh Kazakhstan
{ 0x0440, "ky","KG" }, // Kyrgyz Kyrgyzstan
{ 0x0441, "sw","KE" }, // Kiswahili Kenya
{ 0x0442, "tk","TM" }, // Turkmen Turkmenistan
{ 0x0443, "uz","UZ" }, // Uzbek (Latin) Uzbekistan
{ 0x0444, "tt","RU" }, // Tatar Russia
{ 0x0445, "bn","IN" }, // Bengali India
{ 0x0446, "pa","IN" }, // Punjabi India
{ 0x0447, "gu","IN" }, // Gujarati India
{ 0x0448, "or","IN" }, // Oriya India
{ 0x0448, "wo","SN" }, // Wolof Senegal
{ 0x0449, "ta","IN" }, // Tamil India
{ 0x044A, "te","IN" }, // Telugu India
{ 0x044B, "kn","IN" }, // Kannada India
{ 0x044C, "ml","IN" }, // Malayalam India
{ 0x044D, "as","IN" }, // Assamese India
{ 0x044E, "mr","IN" }, // Marathi India
{ 0x044F, "sa","IN" }, // Sanskrit India
{ 0x0450, "mn","MN" }, // Mongolian (Cyrillic) Mongolia
{ 0x0451, "bo","CN" }, // Tibetan PRC
{ 0x0452, "cy","GB" }, // Welsh United Kingdom
{ 0x0453, "km","KH" }, // Khmer Cambodia
{ 0x0454, "lo","LA" }, // Lao Lao P.D.R.
{ 0x0455, "my","MM" }, // Burmese Myanmar - not listed in Microsoft docs anymore
{ 0x0456, "gl","ES" }, // Galician Galician
{ 0x0457, "kok","IN" }, // Konkani India
{ 0x045A, "syr","TR" }, // Syriac Syria
{ 0x045B, "si","LK" }, // Sinhala Sri Lanka
{ 0x045D, "iu","CA" }, // Inuktitut Canada
{ 0x045E, "am","ET" }, // Amharic Ethiopia
{ 0x0461, "ne","NP" }, // Nepali Nepal
{ 0x0462, "fy","NL" }, // Frisian Netherlands
{ 0x0463, "ps","AF" }, // Pashto Afghanistan
{ 0x0464, "fil","PH" }, // Filipino Philippines
{ 0x0465, "dv","MV" }, // Divehi Maldives
{ 0x0468, "ha","NG" }, // Hausa (Latin) Nigeria
{ 0x046A, "yo","NG" }, // Yoruba Nigeria
{ 0x046B, "qu","BO" }, // Quechua Bolivia
{ 0x046C, "st","ZA" }, // Sesotho sa Leboa South Africa
{ 0x046D, "ba","RU" }, // Bashkir Russia
{ 0x046E, "lb","LU" }, // Luxembourgish Luxembourg
{ 0x046F, "kl","GL" }, // Greenlandic Greenland
{ 0x0470, "ig","NG" }, // Igbo Nigeria
{ 0x0478, "ii","CN" }, // Yi PRC
{ 0x047A, "arn","CL" }, // Mapudungun Chile
{ 0x047C, "moh","CA" }, // Mohawk Mohawk
{ 0x047E, "br","FR" }, // Breton France
{ 0x0480, "ug","CN" }, // Uighur PRC
{ 0x0481, "mi","NZ" }, // Maori New Zealand
{ 0x0482, "oc","FR" }, // Occitan France
{ 0x0483, "co","FR" }, // Corsican France
{ 0x0484, "gsw","FR" }, // Alsatian France
{ 0x0485, "sah","RU" }, // Yakut Russia
{ 0x0486, "qut","GT" }, // K'iche Guatemala
{ 0x0487, "rw","RW" }, // Kinyarwanda Rwanda
{ 0x048C, "gbz","AF" }, // Dari Afghanistan
{ 0x0801, "ar","IQ" }, // Arabic Iraq
{ 0x0804, "zn","CH" }, // Chinese People's Republic of China
{ 0x0807, "de","CH" }, // German Switzerland
{ 0x0809, "en","GB" }, // English United Kingdom
{ 0x080A, "es","MX" }, // Spanish Mexico
{ 0x080C, "fr","BE" }, // French Belgium
{ 0x0810, "it","CH" }, // Italian Switzerland
{ 0x0813, "nl","BE" }, // Dutch Belgium
{ 0x0814, "nn","NO" }, // Norwegian (Nynorsk) Norway
{ 0x0816, "pt","PT" }, // Portuguese Portugal
{ 0x081A, "sh","RS" }, // Serbian (Latin) Serbia
{ 0x081D, "sv","FI" }, // Sweden Finland
{ 0x082C, "az","AZ" }, // Azeri (Cyrillic) Azerbaijan
{ 0x082E, "dsb","DE" }, // Lower Sorbian Germany
{ 0x083B, "se","SE" }, // Sami (Northern) Sweden
{ 0x083C, "ga","IE" }, // Irish Ireland
{ 0x083E, "ms","BN" }, // Malay Brunei Darussalam
{ 0x0843, "uz","UZ" }, // Uzbek (Cyrillic) Uzbekistan
{ 0x0845, "bn","BD" }, // Bengali Bangladesh
{ 0x0850, "mn","MN" }, // Mongolian (Traditional) People's Republic of China
{ 0x085D, "iu","CA" }, // Inuktitut (Latin) Canada
{ 0x085F, "ber","DZ" }, // Tamazight (Latin) Algeria
{ 0x086B, "es","EC" }, // Quechua Ecuador
{ 0x0C01, "ar","EG" }, // Arabic Egypt
{ 0x0C04, "zh","HK" }, // Chinese Hong Kong S.A.R.
{ 0x0C07, "de","AT" }, // German Austria
{ 0x0C09, "en","AU" }, // English Australia
{ 0x0C0A, "es","ES" }, // Spanish (Modern Sort) Spain
{ 0x0C0C, "fr","CA" }, // French Canada
{ 0x0C1A, "sr","CS" }, // Serbian (Cyrillic) Serbia
{ 0x0C3B, "se","FI" }, // Sami (Northern) Finland
{ 0x0C6B, "qu","PE" }, // Quechua Peru
{ 0x1001, "ar","LY" }, // Arabic Libya
{ 0x1004, "zh","SG" }, // Chinese Singapore
{ 0x1007, "de","LU" }, // German Luxembourg
{ 0x1009, "en","CA" }, // English Canada
{ 0x100A, "es","GT" }, // Spanish Guatemala
{ 0x100C, "fr","CH" }, // French Switzerland
{ 0x101A, "hr","BA" }, // Croatian (Latin) Bosnia and Herzegovina
{ 0x103B, "smj","NO" }, // Sami (Lule) Norway
{ 0x1401, "ar","DZ" }, // Arabic Algeria
{ 0x1404, "zh","MO" }, // Chinese Macao S.A.R.
{ 0x1407, "de","LI" }, // German Liechtenstein
{ 0x1409, "en","NZ" }, // English New Zealand
{ 0x140A, "es","CR" }, // Spanish Costa Rica
{ 0x140C, "fr","LU" }, // French Luxembourg
{ 0x141A, "bs","BA" }, // Bosnian (Latin) Bosnia and Herzegovina
{ 0x143B, "smj","SE" }, // Sami (Lule) Sweden
{ 0x1801, "ar","MA" }, // Arabic Morocco
{ 0x1809, "en","IE" }, // English Ireland
{ 0x180A, "es","PA" }, // Spanish Panama
{ 0x180C, "fr","MC" }, // French Principality of Monoco
{ 0x181A, "sh","BA" }, // Serbian (Latin) Bosnia and Herzegovina
{ 0x183B, "sma","NO" }, // Sami (Southern) Norway
{ 0x1C01, "ar","TN" }, // Arabic Tunisia
{ 0x1C09, "en","ZA" }, // English South Africa
{ 0x1C0A, "es","DO" }, // Spanish Dominican Republic
{ 0x1C1A, "sr","BA" }, // Serbian (Cyrillic) Bosnia and Herzegovina
{ 0x1C3B, "sma","SE" }, // Sami (Southern) Sweden
{ 0x2001, "ar","OM" }, // Arabic Oman
{ 0x2009, "en","JM" }, // English Jamaica
{ 0x200A, "es","VE" }, // Spanish Venezuela
{ 0x201A, "bs","BA" }, // Bosnian (Cyrillic) Bosnia and Herzegovina
{ 0x203B, "sms","FI" }, // Sami (Skolt) Finland
{ 0x2401, "ar","YE" }, // Arabic Yemen
{ 0x2409, "en","BS" }, // English Caribbean
{ 0x240A, "es","CO" }, // Spanish Colombia
{ 0x243B, "smn","FI" }, // Sami (Inari) Finland
{ 0x2801, "ar","SY" }, // Arabic Syria
{ 0x2809, "en","BZ" }, // English Belize
{ 0x280A, "es","PE" }, // Spanish Peru
{ 0x2C01, "ar","JO" }, // Arabic Jordan
{ 0x2C09, "en","TT" }, // English Trinidad and Tobago
{ 0x2C0A, "es","AR" }, // Spanish Argentina
{ 0x3001, "ar","LB" }, // Arabic Lebanon
{ 0x3009, "en","ZW" }, // English Zimbabwe
{ 0x300A, "es","EC" }, // Spanish Ecuador
{ 0x3401, "ar","KW" }, // Arabic Kuwait
{ 0x3409, "en","PH" }, // English Republic of the Philippines
{ 0x340A, "es","CL" }, // Spanish Chile
{ 0x3801, "ar","AE" }, // Arabic U.A.E.
{ 0x380A, "es","UY" }, // Spanish Uruguay
{ 0x3C01, "ar","BH" }, // Arabic Bahrain
{ 0x3C0A, "es","PY" }, // Spanish Paraguay
{ 0x4001, "ar","QA" }, // Arabic Qatar
{ 0x4009, "en","IN" }, // English India
{ 0x400A, "es","BO" }, // Spanish Bolivia
{ 0x4409, "en","MY" }, // English Malaysia
{ 0x440A, "es","SV" }, // Spanish El Salvador
{ 0x4809, "en","SG" }, // English Singapore
{ 0x480A, "es","HN" }, // Spanish Honduras
{ 0x4C0A, "es","NI" }, // Spanish Nicaragua
{ 0x500A, "es","PR" }, // Spanish Puerto Rico
{ 0x540A, "es","US" } // Spanish United States
};
class Locale2Lang
{
Locale2Lang(const Locale2Lang &);
Locale2Lang & operator = (const Locale2Lang &);
public:
Locale2Lang() : mSeedPosition(128)
{
memset((void*)mLangLookup, 0, sizeof(mLangLookup));
// create a tri lookup on first 2 letters of language code
static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry);
for (int i = 0; i < maxIndex; i++)
{
size_t a = LANG_ENTRIES[i].maLangStr[0] - 'a';
size_t b = LANG_ENTRIES[i].maLangStr[1] - 'a';
if (mLangLookup[a][b])
{
const IsoLangEntry ** old = mLangLookup[a][b];
int len = 1;
while (old[len]) len++;
len += 2;
mLangLookup[a][b] = gralloc<const IsoLangEntry *>(len);
if (!mLangLookup[a][b])
{
mLangLookup[a][b] = old;
continue;
}
mLangLookup[a][b][--len] = NULL;
mLangLookup[a][b][--len] = &LANG_ENTRIES[i];
while (--len >= 0)
{
assert(len >= 0);
mLangLookup[a][b][len] = old[len];
}
free(old);
}
else
{
mLangLookup[a][b] = gralloc<const IsoLangEntry *>(2);
if (!mLangLookup[a][b]) continue;
mLangLookup[a][b][1] = NULL;
mLangLookup[a][b][0] = &LANG_ENTRIES[i];
}
}
while (2 * mSeedPosition < maxIndex)
mSeedPosition *= 2;
};
~Locale2Lang()
{
for (int i = 0; i != 26; ++i)
for (int j = 0; j != 26; ++j)
free(mLangLookup[i][j]);
}
unsigned short getMsId(const char * locale) const
{
size_t length = strlen(locale);
size_t langLength = length;
const char * language = locale;
const char * script = NULL;
const char * region = NULL;
size_t regionLength = 0;
const char * dash = strchr(locale, '-');
if (dash && (dash != locale))
{
langLength = (dash - locale);
size_t nextPartLength = length - langLength - 1;
if (nextPartLength >= 2)
{
script = ++dash;
dash = strchr(dash, '-');
if (dash)
{
nextPartLength = (dash - script);
region = ++dash;
}
if (nextPartLength == 2 &&
(locale[langLength+1] > 0x40) && (locale[langLength+1] < 0x5B) &&
(locale[langLength+2] > 0x40) && (locale[langLength+2] < 0x5B))
{
region = script;
regionLength = nextPartLength;
script = NULL;
}
else if (nextPartLength == 4)
{
if (dash)
{
dash = strchr(dash, '-');
if (dash)
{
nextPartLength = (dash - region);
}
else
{
nextPartLength = langLength - (region - locale);
}
regionLength = nextPartLength;
}
}
}
}
size_t a = 'e' - 'a';
size_t b = 'n' - 'a';
unsigned short langId = 0;
int i = 0;
switch (langLength)
{
case 2:
{
a = language[0] - 'a';
b = language[1] - 'a';
if ((a < 26) && (b < 26) && mLangLookup[a][b])
{
while (mLangLookup[a][b][i])
{
if (mLangLookup[a][b][i]->maLangStr[2] != '\0')
{
++i;
continue;
}
if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0))
{
langId = mLangLookup[a][b][i]->mnLang;
break;
}
else if (langId == 0)
{
// possible fallback code
langId = mLangLookup[a][b][i]->mnLang;
}
++i;
}
}
}
break;
case 3:
{
a = language[0] - 'a';
b = language[1] - 'a';
if (mLangLookup[a][b])
{
while (mLangLookup[a][b][i])
{
if (mLangLookup[a][b][i]->maLangStr[2] != language[2])
{
++i;
continue;
}
if (region && (strncmp(mLangLookup[a][b][i]->maCountry, region, regionLength) == 0))
{
langId = mLangLookup[a][b][i]->mnLang;
break;
}
else if (langId == 0)
{
// possible fallback code
langId = mLangLookup[a][b][i]->mnLang;
}
++i;
}
}
}
break;
default:
break;
}
if (langId == 0) langId = 0x409;
return langId;
}
const IsoLangEntry * findEntryById(unsigned short langId) const
{
static const int maxIndex = sizeof(LANG_ENTRIES)/sizeof(IsoLangEntry);
int window = mSeedPosition;
int guess = mSeedPosition - 1;
while (LANG_ENTRIES[guess].mnLang != langId)
{
window /= 2;
if (window == 0) return NULL;
guess += (LANG_ENTRIES[guess].mnLang > langId)? -window : window;
while (guess >= maxIndex)
{
window /= 2;
guess -= window;
assert(window);
}
}
return &LANG_ENTRIES[guess];
}
CLASS_NEW_DELETE;
private:
const IsoLangEntry ** mLangLookup[26][26];
int mSeedPosition;
};
} // namespace graphite2

View File

@ -0,0 +1,101 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// This file will be pulled into and integrated into a machine implmentation
// DO NOT build directly
#pragma once
#define do2(n) do_(n) ,do_(n)
#define NILOP 0U
// types or parameters are: (.. is inclusive)
// number - any byte
// output_class - 0 .. silf.m_nClass
// input_class - 0 .. silf.m_nClass
// sattrnum - 0 .. 29 (gr_slatJWidth) , 55 (gr_slatUserDefn)
// attrid - 0 .. silf.numUser() where sattrnum == 55; 0..silf.m_iMaxComp where sattrnum == 15 otherwise 0
// gattrnum - 0 .. face->getGlyphFaceCache->numAttrs()
// gmetric - 0 .. 11 (kgmetDescent)
// featidx - 0 .. face.numFeatures()
// level - any byte
static const opcode_t opcode_table[] =
{
{{do2(nop)}, 0, "NOP"},
{{do2(push_byte)}, 1, "PUSH_BYTE"}, // number
{{do2(push_byte_u)}, 1, "PUSH_BYTE_U"}, // number
{{do2(push_short)}, 2, "PUSH_SHORT"}, // number number
{{do2(push_short_u)}, 2, "PUSH_SHORT_U"}, // number number
{{do2(push_long)}, 4, "PUSH_LONG"}, // number number number number
{{do2(add)}, 0, "ADD"},
{{do2(sub)}, 0, "SUB"},
{{do2(mul)}, 0, "MUL"},
{{do2(div_)}, 0, "DIV"},
{{do2(min_)}, 0, "MIN"},
{{do2(max_)}, 0, "MAX"},
{{do2(neg)}, 0, "NEG"},
{{do2(trunc8)}, 0, "TRUNC8"},
{{do2(trunc16)}, 0, "TRUNC16"},
{{do2(cond)}, 0, "COND"},
{{do2(and_)}, 0, "AND"}, // 0x10
{{do2(or_)}, 0, "OR"},
{{do2(not_)}, 0, "NOT"},
{{do2(equal)}, 0, "EQUAL"},
{{do2(not_eq_)}, 0, "NOT_EQ"},
{{do2(less)}, 0, "LESS"},
{{do2(gtr)}, 0, "GTR"},
{{do2(less_eq)}, 0, "LESS_EQ"},
{{do2(gtr_eq)}, 0, "GTR_EQ"}, // 0x18
{{do_(next), NILOP}, 0, "NEXT"},
{{NILOP, NILOP}, 1, "NEXT_N"}, // number <= smap.end - map
{{do_(next), NILOP}, 0, "COPY_NEXT"},
{{do_(put_glyph_8bit_obs), NILOP}, 1, "PUT_GLYPH_8BIT_OBS"}, // output_class
{{do_(put_subs_8bit_obs), NILOP}, 3, "PUT_SUBS_8BIT_OBS"}, // slot input_class output_class
{{do_(put_copy), NILOP}, 1, "PUT_COPY"}, // slot
{{do_(insert), NILOP}, 0, "INSERT"},
{{do_(delete_), NILOP}, 0, "DELETE"}, // 0x20
{{do_(assoc), NILOP}, VARARGS, "ASSOC"},
{{NILOP ,do_(cntxt_item)}, 2, "CNTXT_ITEM"}, // slot offset
{{do_(attr_set), NILOP}, 1, "ATTR_SET"}, // sattrnum
{{do_(attr_add), NILOP}, 1, "ATTR_ADD"}, // sattrnum
{{do_(attr_sub), NILOP}, 1, "ATTR_SUB"}, // sattrnum
{{do_(attr_set_slot), NILOP}, 1, "ATTR_SET_SLOT"}, // sattrnum
{{do_(iattr_set_slot), NILOP}, 2, "IATTR_SET_SLOT"}, // sattrnum attrid
{{do2(push_slot_attr)}, 2, "PUSH_SLOT_ATTR"}, // sattrnum slot
{{do2(push_glyph_attr_obs)}, 2, "PUSH_GLYPH_ATTR_OBS"}, // gattrnum slot
{{do2(push_glyph_metric)}, 3, "PUSH_GLYPH_METRIC"}, // gmetric slot level
{{do2(push_feat)}, 2, "PUSH_FEAT"}, // featidx slot
{{do2(push_att_to_gattr_obs)}, 2, "PUSH_ATT_TO_GATTR_OBS"}, // gattrnum slot
{{do2(push_att_to_glyph_metric)}, 3, "PUSH_ATT_TO_GLYPH_METRIC"}, // gmetric slot level
{{do2(push_islot_attr)}, 3, "PUSH_ISLOT_ATTR"}, // sattrnum slot attrid
{{NILOP,NILOP}, 3, "PUSH_IGLYPH_ATTR"},
{{do2(pop_ret)}, 0, "POP_RET"}, // 0x30
{{do2(ret_zero)}, 0, "RET_ZERO"},
{{do2(ret_true)}, 0, "RET_TRUE"},
{{do_(iattr_set), NILOP}, 2, "IATTR_SET"}, // sattrnum attrid
{{do_(iattr_add), NILOP}, 2, "IATTR_ADD"}, // sattrnum attrid
{{do_(iattr_sub), NILOP}, 2, "IATTR_SUB"}, // sattrnum attrid
{{do2(push_proc_state)}, 1, "PUSH_PROC_STATE"}, // dummy
{{do2(push_version)}, 0, "PUSH_VERSION"},
{{do_(put_subs), NILOP}, 5, "PUT_SUBS"}, // slot input_class input_class output_class output_class
{{NILOP,NILOP}, 0, "PUT_SUBS2"},
{{NILOP,NILOP}, 0, "PUT_SUBS3"},
{{do_(put_glyph), NILOP}, 2, "PUT_GLYPH"}, // output_class output_class
{{do2(push_glyph_attr)}, 3, "PUSH_GLYPH_ATTR"}, // gattrnum gattrnum slot
{{do2(push_att_to_glyph_attr)}, 3, "PUSH_ATT_TO_GLYPH_ATTR"}, // gattrnum gattrnum slot
{{do2(bor)}, 0, "BITOR"},
{{do2(band)}, 0, "BITAND"},
{{do2(bnot)}, 0, "BITNOT"}, // 0x40
{{do2(setbits)}, 4, "BITSET"},
{{do_(set_feat), NILOP}, 2, "SET_FEAT"}, // featidx slot
// private opcodes for internal use only, comes after all other on disk opcodes.
{{do_(temp_copy), NILOP}, 0, "TEMP_COPY"}
};

View File

@ -0,0 +1,647 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
#pragma once
// This file will be pulled into and integrated into a machine implmentation
// DO NOT build directly and under no circumstances ever #include headers in
// here or you will break the direct_machine.
//
// Implementers' notes
// ==================
// You have access to a few primitives and the full C++ code:
// declare_params(n) Tells the interpreter how many bytes of parameter
// space to claim for this instruction uses and
// initialises the param pointer. You *must* before the
// first use of param.
// use_params(n) Claim n extra bytes of param space beyond what was
// claimed using delcare_param.
// param A const byte pointer for the parameter space claimed by
// this instruction.
// binop(op) Implement a binary operation on the stack using the
// specified C++ operator.
// NOT_IMPLEMENTED Any instruction body containing this will exit the
// program with an assertion error. Instructions that are
// not implemented should also be marked NILOP in the
// opcodes tables this will cause the code class to spot
// them in a live code stream and throw a runtime_error
// instead.
// push(n) Push the value n onto the stack.
// pop() Pop the top most value and return it.
//
// You have access to the following named fast 'registers':
// sp = The pointer to the current top of stack, the last value
// pushed.
// seg = A reference to the Segment this code is running over.
// is = The current slot index
// isb = The original base slot index at the start of this rule
// isf = The first positioned slot
// isl = The last positioned slot
// ip = The current instruction pointer
// endPos = Position of advance of last cluster
// dir = writing system directionality of the font
// #define NOT_IMPLEMENTED assert(false)
// #define NOT_IMPLEMENTED
#define binop(op) const uint32_t a = pop(); *reg.sp = uint32_t(*reg.sp) op a
#define sbinop(op) const int32_t a = pop(); *reg.sp = int32_t(*reg.sp) op a
#define use_params(n) reg.dp += (n)
#define declare_params(n) const byte * param = reg.dp; \
use_params(n);
#define push(n) { *++reg.sp = n; }
#define pop() (*reg.sp--)
#define slotat(x) (reg.is[(x)])
#define DIE { reg.os=reg.seg.slots().end(); reg.status = Machine::died_early; EXIT(1); }
//#define next_slot(x, op) { op x; while(x -> deleted()) { op x;}; }
// TODO: Find out if Pass::runFSM can be made smarter when finishing building the map to avoid the need for this check.
// TODO: Move more common code into an opcodes_preamble.hxx, to avoid macros as functions.
#define position_context(slat) { \
if (!reg.positioned && (slat == gr_slatPosX || slat == gr_slatPosY)) \
{ \
auto last = reg.ctxt.map.back(); \
if (last != reg.seg.slots().end()) ++last; \
reg.seg.positionSlots(nullptr, reg.ctxt.map.front(), last, reg.seg.currdir()); \
reg.positioned = true; \
} \
}
STARTOP(nop)
do {} while (0);
ENDOP
STARTOP(push_byte)
declare_params(1);
push(int8_t(*param));
ENDOP
STARTOP(push_byte_u)
declare_params(1);
push(uint8_t(*param));
ENDOP
STARTOP(push_short)
declare_params(2);
const int16_t r = int16_t(param[0]) << 8
| uint8_t(param[1]);
push(r);
ENDOP
STARTOP(push_short_u)
declare_params(2);
const uint16_t r = uint16_t(param[0]) << 8
| uint8_t(param[1]);
push(r);
ENDOP
STARTOP(push_long)
declare_params(4);
const int32_t r = int32_t(param[0]) << 24
| uint32_t(param[1]) << 16
| uint32_t(param[2]) << 8
| uint8_t(param[3]);
push(r);
ENDOP
STARTOP(add)
binop(+);
ENDOP
STARTOP(sub)
binop(-);
ENDOP
STARTOP(mul)
binop(*);
ENDOP
STARTOP(div_)
const int32_t b = pop();
const int32_t a = int32_t(*reg.sp);
if (b == 0 || (a == std::numeric_limits<int32_t>::min() && b == -1)) DIE;
*reg.sp = int32_t(*reg.sp) / b;
ENDOP
STARTOP(min_)
const int32_t a = pop(), b = *reg.sp;
if (a < b) *reg.sp = a;
ENDOP
STARTOP(max_)
const int32_t a = pop(), b = *reg.sp;
if (a > b) *reg.sp = a;
ENDOP
STARTOP(neg)
*reg.sp = uint32_t(-int32_t(*reg.sp));
ENDOP
STARTOP(trunc8)
*reg.sp = uint8_t(*reg.sp);
ENDOP
STARTOP(trunc16)
*reg.sp = uint16_t(*reg.sp);
ENDOP
STARTOP(cond)
const uint32_t f = pop(), t = pop(), c = pop();
push(c ? t : f);
ENDOP
STARTOP(and_)
binop(&&);
ENDOP
STARTOP(or_)
binop(||);
ENDOP
STARTOP(not_)
*reg.sp = !*reg.sp;
ENDOP
STARTOP(equal)
binop(==);
ENDOP
STARTOP(not_eq_)
binop(!=);
ENDOP
STARTOP(less)
sbinop(<);
ENDOP
STARTOP(gtr)
sbinop(>);
ENDOP
STARTOP(less_eq)
sbinop(<=);
ENDOP
STARTOP(gtr_eq)
sbinop(>=);
ENDOP
STARTOP(next)
if (reg.is - &reg.ctxt.map[0] >= int(reg.ctxt.map.size())) DIE
if (reg.os != reg.seg.slots().end())
{
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highpassed(true);
++reg.os;
}
++reg.is;
ENDOP
//STARTOP(next_n)
// use_params(1);
// NOT_IMPLEMENTED;
//declare_params(1);
//const size_t num = uint8_t(*param);
//ENDOP
//STARTOP(copy_next)
// if (reg.os) next_slot(reg.os,++);
// next_slot(reg.is, ++);
// ENDOP
STARTOP(put_glyph_8bit_obs)
declare_params(1);
const unsigned int output_class = uint8_t(*param);
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(put_subs_8bit_obs)
declare_params(3);
const int slot_ref = int8_t(param[0]);
const unsigned int input_class = uint8_t(param[1]),
output_class = uint8_t(param[2]);
uint16_t index;
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
index = reg.seg.findClassIndex(input_class, slot->gid());
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, index));
}
ENDOP
STARTOP(put_copy)
declare_params(1);
const int slot_ref = int8_t(*param);
if (reg.os != reg.seg.slots().end() && !reg.os->deleted())
{
auto ref = slotat(slot_ref);
if (ref != reg.seg.slots().end() && ref != reg.os)
{
if (!reg.os->isBase() || reg.os->isParent()) DIE
auto g = reg.os->generation();
*reg.os = *ref;
reg.os->generation() = g;
if (!reg.os->isBase())
reg.os->attachedTo()->add_child(&*reg.os);
}
reg.os->copied(false);
reg.os->deleted(false);
}
ENDOP
STARTOP(insert)
if (reg.ctxt.decMax() <= 0) DIE;
auto iss = reg.os;
while (iss != reg.seg.slots().end() && iss->deleted()) ++iss;
auto slot = reg.seg.slots().emplace(iss++, reg.seg.numAttrs());
if (slot == reg.seg.slots().end()) DIE;
slot->generation() += reg.seg.slots().size();
slot->clusterhead(true);
switch ((slot == reg.seg.slots().begin()) << 1 | (iss == reg.seg.slots().end()))
{
case 0: // Medial insertion
slot->before(std::prev(slot)->after());
slot->original(iss->original());
slot->after(iss->before());
break;
case 1: // Final insertion
slot->before(std::prev(slot)->before());
slot->original(std::prev(slot)->original());
slot->after(std::prev(slot)->after());
break;
case 2: // Initial insertion
slot->before(iss->before());
slot->original(iss->original());
slot->after(iss->before());
break;
default: // Singleton insertion
slot->original(reg.seg.defaultOriginal());
break;
}
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highpassed(false);
reg.os = slot;
reg.seg.extendLength(1);
for (auto i = reg.is; i != reg.ctxt.map.end(); ++i) ++*i;
if (reg.is >= reg.ctxt.map.begin())
--reg.is;
ENDOP
STARTOP(delete_)
if (reg.os == reg.seg.slots().end() || reg.os->deleted()) DIE
reg.seg.extendLength(-1);
reg.os->deleted(true);
if (reg.os == reg.ctxt.highwater())
reg.ctxt.highwater(std::next(reg.os));
if (!(*reg.is)->copied()) {
*reg.is = slotref::from(new Slot(std::move(*reg.os)));
(*reg.is)->copied(true);
}
else { **reg.is = std::move(*reg.os); }
reg.os = reg.seg.slots().erase(reg.os);
// if (reg.os != reg.seg.slots().begin())
--reg.os;
for (auto i = reg.is+1; i != reg.ctxt.map.end(); ++i) --*i;
ENDOP
STARTOP(assoc)
declare_params(1);
unsigned int num = uint8_t(*param);
const int8_t * assocs = reinterpret_cast<const int8_t *>(param+1);
use_params(num);
int max = -1;
int min = -1;
while (num-- > 0)
{
int sr = *assocs++;
slotref ts = slotat(sr);
if (ts != reg.seg.slots().end() && (min == -1 || ts->before() < min)) min = ts->before();
if (ts != reg.seg.slots().end() && ts->after() > max) max = ts->after();
}
if (min > -1) // implies max > -1
{
reg.os->before(min);
reg.os->after(max);
}
ENDOP
STARTOP(cntxt_item)
// It turns out this is a cunningly disguised condition forward jump.
declare_params(3);
const int is_arg = int8_t(param[0]);
const size_t iskip = uint8_t(param[1]),
dskip = uint8_t(param[2]);
if (reg.isb + is_arg != reg.is)
{
reg.ip += iskip;
reg.dp += dskip;
push(true);
}
ENDOP
STARTOP(attr_set)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
int const val = pop();
reg.os->setAttr(reg.seg, slat, 0, val, reg.ctxt);
ENDOP
STARTOP(attr_add)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, 0));
reg.os->setAttr(reg.seg, slat, 0, int32_t(val + res), reg.ctxt);
ENDOP
STARTOP(attr_sub)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, 0));
reg.os->setAttr(reg.seg, slat, 0, int32_t(res - val), reg.ctxt);
ENDOP
STARTOP(attr_set_slot)
declare_params(1);
auto const slat = Slot::attrCode(uint8_t(*param));
int const offset = int(reg.is - reg.ctxt.map.begin())*int(slat == gr_slatAttTo);
int const val = pop() + offset;
reg.os->setAttr(reg.seg, slat, offset, val, reg.ctxt);
ENDOP
STARTOP(iattr_set_slot)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
int const val = int(pop() + (reg.is - reg.ctxt.map.begin())*int(slat == gr_slatAttTo));
reg.os->setAttr(reg.seg, slat, idx, val, reg.ctxt);
ENDOP
STARTOP(push_slot_attr)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
int const slot_ref = int8_t(param[1]);
position_context(slat)
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int res = slot->getAttr(reg.seg, slat, 0);
push(res);
}
ENDOP
STARTOP(push_glyph_attr_obs)
declare_params(2);
unsigned int const glyph_attr = uint8_t(param[0]);
int const slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(int32_t(reg.seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_glyph_metric)
declare_params(3);
const auto glyph_attr = metrics(param[0]);
const int slot_ref = int8_t(param[1]);
const signed int attr_level = uint8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(reg.seg.getGlyphMetric(&*slot, glyph_attr, attr_level, reg.ctxt.dir));
ENDOP
STARTOP(push_feat)
declare_params(2);
const unsigned int feat = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
uint8_t fid = reg.seg.charinfo(slot->original())->fid();
push(reg.seg.getFeature(fid, feat));
}
ENDOP
STARTOP(push_att_to_gattr_obs)
declare_params(2);
const unsigned int glyph_attr = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto att = slot->attachedTo();
auto & ref = att ? *att : *slot;
push(int32_t(reg.seg.glyphAttr(ref.gid(), glyph_attr)));
}
ENDOP
STARTOP(push_att_to_glyph_metric)
declare_params(3);
const auto glyph_attr = metrics(param[0]);
const int slot_ref = int8_t(param[1]);
const signed int attr_level = uint8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto parent = slot->attachedTo();
if (!parent) parent = &*slot;
push(int32_t(reg.seg.getGlyphMetric(parent, glyph_attr, attr_level, reg.ctxt.dir)));
}
ENDOP
STARTOP(push_islot_attr)
declare_params(3);
auto const slat = Slot::attrCode(uint8_t(param[0]));
int const slot_ref = int8_t(param[1]),
idx = uint8_t(param[2]);
position_context(slat)
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int res = slot->getAttr(reg.seg, slat, idx);
push(res);
}
ENDOP
#if 0
STARTOP(push_iglyph_attr) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(pop_ret)
const uint32_t ret = pop();
EXIT(ret);
ENDOP
STARTOP(ret_zero)
EXIT(0);
ENDOP
STARTOP(ret_true)
EXIT(1);
ENDOP
STARTOP(iattr_set)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
int const val = pop();
reg.os->setAttr(reg.seg, slat, idx, val, reg.ctxt);
ENDOP
STARTOP(iattr_add)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, idx));
reg.os->setAttr(reg.seg, slat, idx, int32_t(val + res), reg.ctxt);
ENDOP
STARTOP(iattr_sub)
declare_params(2);
auto const slat = Slot::attrCode(uint8_t(param[0]));
uint8_t const idx = uint8_t(param[1]);
uint32_t const val = pop();
position_context(slat)
uint32_t res = uint32_t(reg.os->getAttr(reg.seg, slat, idx));
reg.os->setAttr(reg.seg, slat, idx, int32_t(res - val), reg.ctxt);
ENDOP
STARTOP(push_proc_state)
use_params(1);
push(1);
ENDOP
STARTOP(push_version)
push(0x00030000);
ENDOP
STARTOP(put_subs)
declare_params(5);
const int slot_ref = int8_t(param[0]);
const unsigned int input_class = uint8_t(param[1]) << 8
| uint8_t(param[2]);
const unsigned int output_class = uint8_t(param[3]) << 8
| uint8_t(param[4]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
int index = reg.seg.findClassIndex(input_class, slot->gid());
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, index));
}
ENDOP
#if 0
STARTOP(put_subs2) // not implemented
NOT_IMPLEMENTED;
ENDOP
STARTOP(put_subs3) // not implemented
NOT_IMPLEMENTED;
ENDOP
#endif
STARTOP(put_glyph)
declare_params(2);
const unsigned int output_class = uint8_t(param[0]) << 8
| uint8_t(param[1]);
reg.os->glyph(reg.seg, reg.seg.getClassGlyph(output_class, 0));
ENDOP
STARTOP(push_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8_t(param[0]) << 8
| uint8_t(param[1]);
const int slot_ref = int8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
push(int32_t(reg.seg.glyphAttr(slot->gid(), glyph_attr)));
ENDOP
STARTOP(push_att_to_glyph_attr)
declare_params(3);
const unsigned int glyph_attr = uint8_t(param[0]) << 8
| uint8_t(param[1]);
const int slot_ref = int8_t(param[2]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
auto att = slot->attachedTo();
auto & ref = att ? *att : *slot;
push(int32_t(reg.seg.glyphAttr(ref.gid(), glyph_attr)));
}
ENDOP
STARTOP(temp_copy)
#if 0
reg.seg.slots().push_back(Slot());
auto slot = --reg.seg.slots().end();
int16_t *tempUserAttrs = slot->userAttrs();
memcpy(&reg.seg.slots().back(), &*reg.os, sizeof(Slot));
memcpy(tempUserAttrs, reg.os->userAttrs(), reg.seg.numAttrs() * sizeof(uint16_t));
slot->userAttrs(tempUserAttrs);
slot->copied(true);
reg.seg.slots().erase(slot);
*reg.is = slot;
#else
auto slot = reg.seg.newSlot();
if (slot == reg.seg.slots().end() || reg.os == reg.seg.slots().end()) DIE;
// copy slot reg.os into new slot
*slot = *reg.os;
slot->copied(true);
// TODO: remove this once we're using gr::list methods. This is the
// hack that, that enables the hack, that enables debug output.
// slot.prev(std::prev(reg.os));
// slot.next(std::next(reg.os));
*reg.is = slot;
#endif
ENDOP
STARTOP(band)
binop(&);
ENDOP
STARTOP(bor)
binop(|);
ENDOP
STARTOP(bnot)
*reg.sp = ~*reg.sp;
ENDOP
STARTOP(setbits)
declare_params(4);
const uint16_t m = uint16_t(param[0]) << 8
| uint8_t(param[1]);
const uint16_t v = uint16_t(param[2]) << 8
| uint8_t(param[3]);
*reg.sp = ((*reg.sp) & ~m) | v;
ENDOP
STARTOP(set_feat)
declare_params(2);
const unsigned int feat = uint8_t(param[0]);
const int slot_ref = int8_t(param[1]);
slotref slot = slotat(slot_ref);
if (slot != reg.seg.slots().end())
{
uint8_t fid = reg.seg.charinfo(slot->original())->fid();
reg.seg.setFeature(fid, feat, pop());
}
ENDOP

View File

@ -0,0 +1,192 @@
// SPDX-License-Identifier: MIT
// Copyright 2010, SIL International, All rights reserved.
// designed to have a limited subset of the std::vector api
#pragma once
#include <cstddef>
#include <cassert>
#include <cstring>
#include <cstdlib>
#include <iterator>
#include <new>
#include "Main.h"
namespace graphite2 {
// template <typename T>
// inline
// ptrdiff_t distance(T* first, T* last) { return last-first; }
template <typename T>
class vector
{
T * m_first, *m_last, *m_end;
public:
using value_type = T;
using size_type = size_t;
using difference_type = ptrdiff_t;
using reference = T&;
using const_reference = T const &;
using pointer = T*;
using const_pointer = T const *;
using iterator = T*;
using const_iterator = T const *;
using reverse_iterator = std::reverse_iterator<iterator>;
using const_reverse_iterator = std::reverse_iterator<const_iterator>;
constexpr vector() : m_first{nullptr}, m_last{nullptr}, m_end{nullptr} {}
vector(size_type n, const value_type& value) : vector<T>() { insert(begin(), n, value); }
explicit vector(size_type n) : vector<T>(n, T()) {}
template<class It>
vector(It first, const It last) : vector<T>() { insert(begin(), first, last); }
vector(const vector<T> &rhs) : vector<T>(rhs.begin(), rhs.end()) { }
vector(vector<T> &&rhs) : m_first{rhs.m_first}, m_last{rhs.m_last}, m_end{rhs.m_end} { rhs.m_first = rhs.m_last = nullptr; }
vector(std::initializer_list<T> ilist) : vector{ilist.begin(), ilist.end()} {}
~vector() { clear(); free(m_first); }
iterator begin() { return m_first; }
const_iterator cbegin() const { return m_first; }
const_iterator begin() const { return m_first; }
iterator end() { return m_last; }
const_iterator cend() const { return m_last; }
const_iterator end() const { return m_last; }
bool empty() const { return m_first == m_last; }
size_type size() const { return m_last - m_first; }
size_type capacity() const{ return m_end - m_first; }
void reserve(size_type n);
void resize(size_type n, value_type const & v = value_type());
reference front() { assert(size() > 0); return *begin(); }
const_reference front() const { assert(size() > 0); return *begin(); }
reference back() { assert(size() > 0); return *(end()-1); }
const_reference back() const { assert(size() > 0); return *(end()-1); }
vector<T> & operator = (const vector<T> & rhs) { if (&rhs != this) assign(rhs.begin(), rhs.end()); return *this; }
vector<T> & operator = (vector<T> && rhs);
reference operator [] (size_type n) { assert(size() > n); return m_first[n]; }
const_reference operator [] (size_type n) const { assert(size() > n); return m_first[n]; }
void assign(size_type n, const value_type& u) { clear(); insert(begin(), n, u); }
template<class It>
void assign(It first, It last) { clear(); insert(begin(), first, last); }
void assign(std::initializer_list<T> ilist) { assign(ilist.begin(), ilist.end()); }
iterator insert(const_iterator p, const value_type & x) { auto n = _insert_default(p, 1); new (n) value_type(x); return n; }
iterator insert(const_iterator p, value_type && x) { auto n = _insert_default(p, 1); new (n) value_type(std::move(x)); return n; }
iterator insert(const_iterator p, size_type n, const T & x);
template<class It>
iterator insert(const_iterator p, It first, It last);
void pop_back() { assert(size() > 0); --m_last; }
void push_back(const value_type &v) { if (m_last == m_end) reserve(size()+1); new (m_last++) value_type(v); }
void push_back(value_type && v) { if (m_last == m_end) reserve(size()+1); new (m_last++) value_type(std::move(v)); }
template<typename... Args>
iterator emplace(const_iterator p, Args &&... args) { auto n = _insert_default(p, 1); new (n) value_type(std::forward<Args>(args)...); return n; }
template<typename... Args>
reference emplace_back(Args &&... args) { if (m_last == m_end) reserve(size()+1); return *new (m_last++) value_type(std::forward<Args>(args)...); }
void clear() { erase(begin(), end()); }
iterator erase(const_iterator p) { return erase(p, std::next(p)); }
iterator erase(const_iterator first, const_iterator last);
private:
iterator _insert_default(const_iterator p, size_type n);
};
template <typename T>
inline
void vector<T>::reserve(size_type n)
{
if (n > capacity())
{
const ptrdiff_t sz = size();
size_t requested;
if (checked_mul(n, sizeof(value_type), requested)) std::abort();
m_first = static_cast<value_type*>(realloc(m_first, requested));
if (!m_first) std::abort();
m_last = m_first + sz;
m_end = m_first + n;
}
}
template <typename T>
inline
void vector<T>::resize(size_type n, const value_type & v) {
const ptrdiff_t d = n-size();
if (d < 0) erase(end()+d, end());
else if (d > 0) insert(end(), d, v);
}
template<typename T>
inline
vector<T> & vector<T>::operator = (vector<T> && rhs) {
if (&rhs != this) {
clear();
m_first = rhs.m_first;
m_last = rhs.m_last;
m_end = rhs.m_end;
rhs.m_first = rhs.m_last = nullptr;
}
return *this;
}
template<typename T>
inline
auto vector<T>::_insert_default(const_iterator p, size_type n) -> iterator
{
assert(begin() <= p && p <= end());
const ptrdiff_t d = p - begin();
reserve(((size() + n + 7) >> 3) << 3);
auto i = begin() + d;
// Move tail if there is one
if (i != end()) memmove(i + n, i, std::distance(i, end())*sizeof(value_type));
m_last += n;
return i;
}
template<typename T>
inline
auto vector<T>::insert(const_iterator p, size_type n, const T & x) -> iterator
{
auto const i = _insert_default(p, n);
// Copy in elements
for (auto u = i; n; --n, ++u) { new (u) value_type(x); }
return i;
}
template<typename T>
template<class It>
inline
auto vector<T>::insert(const_iterator p, It first, It last) -> iterator
{
auto const i = _insert_default(p, std::distance(first, last));
// Copy in elements
for (auto u = i;first != last; ++first, ++u) { new (u) value_type(*first); }
return i;
}
template<typename T>
inline
auto vector<T>::erase(const_iterator first, const_iterator last) -> iterator
{
if (first != last)
{
for (iterator e = const_cast<iterator>(first); e != last; ++e)
e->~value_type();
auto const sz = std::distance(first, last);
if (m_last != last) memmove(const_cast<iterator>(first),
last,
std::distance(last, cend())*sizeof(value_type));
m_last -= sz;
}
return const_cast<iterator>(first);
}
} // namespace graphite2

124
src/graphite2/src/json.cpp Normal file
View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: MIT
// Copyright 2011, SIL International, All rights reserved.
// JSON debug logging
// Author: Tim Eves
#if !defined GRAPHITE2_NTRACING
#include <cstdio>
#include <limits>
#include "inc/json.h"
#if defined(_MSC_VER)
#define FORMAT_INTMAX "%lli"
#define FORMAT_UINTMAX "%llu"
#else
#define FORMAT_INTMAX "%ji"
#define FORMAT_UINTMAX "%ju"
#endif
using namespace graphite2;
namespace
{
enum
{
seq = ',',
obj='}', member=':', empty_obj='{',
arr=']', empty_arr='['
};
}
const std::nullptr_t json::null = nullptr;
inline
void json::context(const char current) throw()
{
fprintf(_stream, "%c", *_context);
indent();
*_context = current;
}
void json::indent(const int d) throw()
{
if (*_context == member || (_flatten && _flatten < _context))
fputc(' ', _stream);
else
fprintf(_stream, "\n%*s", 4*int(_context - _contexts + d), "");
}
inline
void json::push_context(const char prefix, const char suffix) throw()
{
assert(_context - _contexts < ptrdiff_t(sizeof _contexts));
if (_context == _contexts)
*_context = suffix;
else
context(suffix);
*++_context = prefix;
}
void json::pop_context() throw()
{
assert(_context > _contexts);
if (*_context == seq) indent(-1);
else fputc(*_context, _stream);
fputc(*--_context, _stream);
if (_context == _contexts) fputc('\n', _stream);
fflush(_stream);
if (_flatten >= _context) _flatten = 0;
*_context = seq;
}
// These four functions cannot be inlined as pointers to these
// functions are needed for operator << (_context_t) to work.
void json::flat(json & j) throw() { if (!j._flatten) j._flatten = j._context; }
void json::close(json & j) throw() { j.pop_context(); }
void json::object(json & j) throw() { j.push_context('{', '}'); }
void json::array(json & j) throw() { j.push_context('[', ']'); }
void json::item(json & j) throw()
{
while (j._context > j._contexts+1 && j._context[-1] != arr)
j.pop_context();
}
json & json::operator << (json::string s) throw()
{
const char ctxt = _context[-1] == obj ? *_context == member ? seq : member : seq;
context(ctxt);
fprintf(_stream, "\"%s\"", s);
if (ctxt == member) fputc(' ', _stream);
return *this;
}
json & json::operator << (json::number f) throw()
{
context(seq);
if (std::numeric_limits<json::number>::infinity() == f)
fputs("Infinity", _stream);
else if (-std::numeric_limits<json::number>::infinity() == f)
fputs("-Infinity", _stream);
else if (std::numeric_limits<json::number>::quiet_NaN() == f ||
std::numeric_limits<json::number>::signaling_NaN() == f)
fputs("NaN", _stream);
else
fprintf(_stream, "%g", f);
return *this;
}
json & json::operator << (json::integer d) throw() { context(seq); fprintf(_stream, FORMAT_INTMAX, intmax_t(d)); return *this; }
json & json::operator << (json::integer_u d) throw() { context(seq); fprintf(_stream, FORMAT_UINTMAX, uintmax_t(d)); return *this; }
json & json::operator << (json::boolean b) throw() { context(seq); fputs(b ? "true" : "false", _stream); return *this; }
json & json::operator << (std::nullptr_t) throw() { context(seq); fputs("null",_stream); return *this; }
#endif

View File

@ -0,0 +1,126 @@
# SPDX-License-Identifier: MIT
# Copyright 2010, SIL International, All rights reserved.
project(testing)
include(Graphite)
include(CMakeDependentOption)
CMAKE_DEPENDENT_OPTION(GRAPHITE2_COMPARE_RENDERER "Build tool to compare output of different renderers" OFF
"NOT GRAPHITE2_NFILEFACE" OFF)
include_directories(${graphite2_core_SOURCE_DIR})
set(S ${graphite2_core_SOURCE_DIR})
add_library(graphite2-base STATIC
${S}/FeatureMap.cpp
${S}/Intervals.cpp
${S}/NameTable.cpp
${S}/Sparse.cpp
${S}/TtfUtil.cpp
${S}/UtfCodec.cpp)
add_library(graphite2-file STATIC
${S}/call_machine.cpp
${S}/Code.cpp
${S}/Collider.cpp
${S}/CmapCache.cpp
${S}/Decompressor.cpp
${S}/Face.cpp
${S}/FileFace.cpp
${S}/GlyphCache.cpp
${S}/GlyphFace.cpp
${S}/gr_logging.cpp
${S}/Pass.cpp
${S}/Segment.cpp
${S}/ShapingContext.cpp
${S}/Silf.cpp
${S}/Slot.cpp
${S}/SlotBuffer.cpp
)
set(TELEMETRY)
if (GRAPHITE2_TELEMETRY)
set(TELEMETRY ";GRAPHITE2_TELEMETRY")
endif()
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
set_target_properties(graphite2-base PROPERTIES
COMPILE_DEFINITIONS "GRAPHITE2_STATIC;GRAPHITE2_NTRACING;_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;UNICODE")
set_target_properties(graphite2-file PROPERTIES
COMPILE_DEFINITIONS "GRAPHITE2_STATIC;GRAPHITE2_NTRACING${TELEMETRY};_SCL_SECURE_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;UNICODE")
else (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
# don't -D GRAPHITE2_STATIC so that tests can see the API symbols in the static library
set_target_properties(graphite2-base PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wno-class-memaccess -fno-rtti -fno-exceptions"
COMPILE_DEFINITIONS "GRAPHITE2_NTRACING"
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
LINKER_LANGUAGE C)
set_target_properties(graphite2-file PROPERTIES
COMPILE_FLAGS "-Wall -Wextra -Wno-class-memaccess -fno-rtti -fno-exceptions"
COMPILE_DEFINITIONS "GRAPHITE2_NTRACING${TELEMETRY}"
LINK_FLAGS "-nodefaultlibs ${GRAPHITE_LINK_FLAGS}"
LINKER_LANGUAGE C)
endif()
if (GRAPHITE2_COMPARE_RENDERER)
add_subdirectory(comparerenderer)
endif()
add_subdirectory(endian)
add_subdirectory(bittwiddling)
if (NOT GRAPHITE2_NFILEFACE)
add_subdirectory(examples)
endif()
add_subdirectory(featuremap)
add_subdirectory(grcollections)
add_subdirectory(json)
add_subdirectory(nametabletest)
add_subdirectory(sparsetest)
add_subdirectory(utftest)
if (NOT GRAPHITE2_NFILEFACE)
add_subdirectory(vm)
endif()
add_subdirectory(fuzz-tests)
enable_testing()
fonttest(padauk1 Padauk.ttf 1015 102F 100F 1039 100F 1031 1038)
fonttest(padauk2 Padauk.ttf 1000 103C 102D 102F)
fonttest(padauk3 Padauk.ttf 101e 1004 103a 1039 1001 103b 102d 102f 1004 103a 1038)
fonttest(padauk4 Padauk.ttf 1005 1000 1039 1000 1030)
fonttest(padauk5 Padauk.ttf 1000 103c 1031 102c 1004 1037 103a)
fonttest(padauk6 Padauk.ttf 1000 102D 1005 1039 1006 102C)
# padauk7 can cause an infinite loop, though the text is miss-spelt
fonttest(padauk7 Padauk.ttf 1017 1014 103c 103d 102f)
fonttest(padauk8 Padauk.ttf 1004 103A 1039 1005)
fonttest(padauk9 Padauk.ttf 1004 103A 1039)
fonttest(padauk10 Padauk.ttf 1004 103D 1000 103A -feat kdot=1,wtri=1)
fonttest(padauk11 Padauk.ttf 100B 1039 100C 1031 102C)
fonttest(padauk12 Padauk.ttf 0048 0065 006C 006C 006F 0020 004D 0075 006D -j 107)
fonttest(scher1 Scheherazadegr.ttf 0628 0628 064E 0644 064E 0654 0627 064E -rtl)
fonttest(scher2 Scheherazadegr.ttf 0627 0644 0625 0639 0644 0627 0646 -rtl)
fonttest(scher3 Scheherazadegr.ttf 0627 0031 0032 002D 0034 0035 0627 -rtl)
fonttest(scher4 Scheherazadegr.ttf 0627 0653 06AF -rtl)
fonttest(scher5 Scheherazadegr_noglyfs.ttf 0627 0653 06AF -rtl)
fonttest(charis1 charis_r_gr.ttf 0069 02E6 02E8 02E5)
fonttest(charis2 charis_r_gr.ttf 1D510 0041 1D513)
fonttest(charis3 charis_r_gr.ttf 0054 0069 1ec3 0075 -feat lang=vie)
fonttest(charis4 charis_r_gr.ttf 006b 0361 070)
fonttest(charis5 charis_r_gr.ttf 0020 006C 0325 0065)
fonttest(charis6 charis_r_gr.ttf 0048 0065 006C 006C 006F 0020 004D 0075 006D -j 107)
fonttest(charis7 charis_fast.ttf 0049 0065 006C 006C 006F)
fonttest(charis8 charis_r_gr.ttf 0054 0069 1ec3 0075 -feat "lang=vi ")
fonttest(magyar1 MagyarLinLibertineG.ttf 0031 0035 -feat 210=36)
fonttest(magyar2 MagyarLinLibertineG.ttf 0031 0030 -feat 210=200)
fonttest(magyar3 MagyarLinLibertineG.ttf 0066 0069 0066 0074 0079 002d 0066 0069 0076 0065 -feat 209=3)
fonttest(grtest1 grtest1gr.ttf 0062 0061 0061 0061 0061 0061 0061 0062 0061)
fonttest(general1 general.ttf 0E01 0062)
fonttest(piglatin1 PigLatinBenchmark_v3.ttf 0068 0065 006C 006C 006F)
feattest(padauk_feat Padauk.ttf)
feattest(charis_feat charis_r_gr.ttf)
feattest(scher_feat Scheherazadegr.ttf)
cmptest(padaukcmp1 Padauk.ttf my_HeadwordSyllables.txt)
cmptest(chariscmp1 charis_r_gr.ttf udhr_eng.txt)
cmptest(chariscmp2 charis_r_gr.ttf udhr_yor.txt)
cmptest(annacmp1 Annapurnarc2.ttf udhr_nep.txt)
cmptest(schercmp1 Scheherazadegr.ttf udhr_arb.txt -r)
cmptest(awamicmp1 AwamiNastaliq-Regular.ttf awami_tests.txt -r -e 1)

View File

@ -0,0 +1,19 @@
# SPDX-License-Identifier: MIT
# Copyright 2014, SIL International, All rights reserved.
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.12 FATAL_ERROR)
project(bits)
include(Graphite)
include_directories(${graphite2_core_SOURCE_DIR})
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
add_definitions(-D_SCL_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_WARNINGS -DUNICODE)
add_custom_target(${PROJECT_NAME}_copy_dll ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${graphite2_core_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}graphite2${CMAKE_SHARED_LIBRARY_SUFFIX} ${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
add_dependencies(${PROJECT_NAME}_copy_dll graphite2 bits)
endif()
add_executable(bits bits.cpp)
target_link_libraries(bits graphite2-base)
add_test(NAME bits COMMAND $<TARGET_FILE:bits>)

View File

@ -0,0 +1,117 @@
// SPDX-License-Identifier: MIT
// Copyright (C) 2014 SIL International
/*
Responsibility: Tim Eves
Description:
The test harness for the Sparse class. This validates the
sparse classe is working correctly.
*/
#include <cassert>
#include <iomanip>
#include <iostream>
#include <limits>
#include <string>
#include "inc/Main.h"
#include "inc/bits.h"
using namespace graphite2;
#if (defined(__x86_64__) && !defined(__ILP32__)) || defined(_WIN64)
#define HAS_64BIT
#endif
#define maskoff(n) (size_t(-1L) >> (8*sizeof(size_t) - n))
#define pat(b) 0x01##b, 0x03##b, 0x07##b, 0x0f##b
#define pat8(b) pat(b), pat(f##b)
#define pat16(b) pat8(b), pat8(ff##b)
#define pat32(b) pat16(b), pat16(ffff##b)
#define pat64(b) pat32(b), pat32(ffffffff##b)
#define patterns(bw) \
uint##bw##_t const u##bw##_pat[] = {0, pat##bw(UL) }; \
int##bw##_t const * s##bw##_pat = reinterpret_cast<int##bw##_t const *>(u##bw##_pat)
//#define BENCHMARK 40000000
#if defined BENCHMARK
#define benchmark() for (int n = BENCHMARK; n; --n)
#else
#define benchmark()
#endif
typedef unsigned long long uint64;
typedef signed long long int64;
namespace
{
patterns(8);
patterns(16);
patterns(32);
#if defined(HAS_64BIT)
patterns(64);
#endif
int ret = 0;
template<typename T>
struct type_name {};
// template<typename T>
// inline
// std::ostream & operator << (std::ostream & o, type_name<T>)
// {
// if (!std::numeric_limits<T>::is_signed) o.put('u');
// o << "int" << std::dec << sizeof(T)*8;
// }
template<typename T>
inline
void test_bit_set_count(const T pat[])
{
for (unsigned int p = 0; p <= sizeof(T)*8; ++p)
{
#if !defined BENCHMARK
std::cout << "bit_set_count("
<< (!std::numeric_limits<T>::is_signed ? "uint" : "int")
<< std::dec << sizeof(T)*8 << "(0x"
<< std::hex
<< std::setw(sizeof(T)*2)
<< std::setfill('0')
<< (pat[p] & maskoff(8*sizeof(T)))
<< ")) -> "
<< std::dec
<< bit_set_count(pat[p]) << std::endl;
#endif
if (bit_set_count(pat[p]) != p)
{
std::cerr << " != " << std::dec << p << std::endl;
ret = sizeof(T);
}
}
}
}
int main(int argc , char *argv[])
{
assert(sizeof(uint64) == 8);
benchmark()
{
// Test bit_set_count
test_bit_set_count(u8_pat);
test_bit_set_count(s8_pat);
test_bit_set_count(u16_pat);
test_bit_set_count(s16_pat);
test_bit_set_count(u32_pat);
test_bit_set_count(s32_pat);
#if defined(HAS_64BIT)
test_bit_set_count(u64_pat);
test_bit_set_count(s64_pat);
#endif
}
return ret;
}

View File

@ -0,0 +1,54 @@
# SPDX-License-Identifier: MIT
# Copyright 2010, SIL International, All rights reserved.
project(comparerenderer)
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(RT_LDFLAGS "rt")
endif()
include_directories(../common ${PROJECT_SOURCE_DIR}/../../src)
file(GLOB PRIVATE_HEADERS *.h)
set_source_files_properties(${PRIVATE_HEADERS} PROPERTIES HEADER_FILE_ONLY true)
add_executable(comparerenderer
CompareRenderer.cpp
${PRIVATE_HEADERS})
if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set_target_properties(comparerenderer PROPERTIES
COMPILE_FLAGS "-std=c++11")
endif()
# rt is for timing
target_link_libraries(comparerenderer graphite2-base graphite2 ${FREETYPE_LIBRARIES} ${GLIB_LD_FLAGS} ${HB_LDFLAGS} ${HB1_LDFLAGS} ${GR_LDFLAGS} ${ICU_LDFLAGS} ${RT_LDFLAGS})
if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows")
add_definitions(-D_CRT_SECURE_NO_WARNINGS -D_SCL_SECURE_NO_WARNINGS)
add_custom_target(${PROJECT_NAME}_copy_dll ALL
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${graphite2_core_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${CMAKE_SHARED_LIBRARY_PREFIX}graphite2${CMAKE_SHARED_LIBRARY_SUFFIX} ${PROJECT_BINARY_DIR}/${CMAKE_CFG_INTDIR})
add_dependencies(${PROJECT_NAME}_copy_dll graphite2 iconv comparerenderer)
endif()
macro(comparetest TESTNAME FONTFILE TEXTFILE)
if (${GR_FOUND})
# comparision tests between old and new graphite
add_test(NAME ${TESTNAME}_comparison COMMAND $<TARGET_FILE:comparerenderer>
-t ${testing_SOURCE_DIR}/texts/${TEXTFILE} -f ${testing_SOURCE_DIR}/fonts/${FONTFILE} -s 12 -v -l ${PROJECT_BINARY_DIR}/${TESTNAME}.log -n --tolerance 0.0001 ${ARGN})
set_tests_properties(${TESTNAME}_comparison PROPERTIES TIMEOUT 3)
add_test(NAME ${TESTNAME}cmpOutput COMMAND ${CMAKE_COMMAND} -E compare_files ${PROJECT_BINARY_DIR}/${TESTNAME}.log ${PROJECT_SOURCE_DIR}/../standards/${TESTNAME}${PLATFORM_TEST_SUFFIX}.log)
set_tests_properties(${TESTNAME}cmpOutput PROPERTIES DEPENDS ${TESTNAME})
endif()
endmacro()
comparetest(charis charis_r_gr.ttf udhr_eng.txt)
comparetest(padauk Padauk.ttf my_HeadwordSyllables.txt)
comparetest(padauk_ulon Padauk.ttf my_HeadwordSyllables.txt --features ulon=1)
comparetest(charis_yor charis_r_gr.ttf udhr_yor.txt)
comparetest(piglatin PigLatinBenchmark_v3.ttf udhr_eng.txt)
# silgraphite and gr2 bidi algorithms don't quite agree enough
# comparetest(scheherazade_arb Scheherazadegr.ttf udhr_arb.txt --rtl)
comparetest(annapurna_nep Annapurnarc2.ttf udhr_nep.txt)
# comparerenderer can also be used for font testing
# install(TARGETS comparerenderer EXPORT comparerenderer RUNTIME DESTINATION bin)

View File

@ -0,0 +1,388 @@
// SPDX-License-Identifier: MIT OR MPL-2.0 OR GPL-2.0-or-later-2.1-or-later
// Copyright 2010, SIL International, All rights reserved.
#include <cassert>
#include <cstddef>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <sys/types.h>
#include <sys/stat.h>
#ifdef __GNUC__
#include <unistd.h>
#ifndef __linux__
#include <sys/time.h>
#endif
#endif
#ifdef WIN32
#include <windows.h>
#endif
#include "RendererOptions.h"
#include "Renderer.h"
#include "RenderedLine.h"
#include "FeatureParser.h"
#include "Gr2Renderer.h"
#include "graphite2/Log.h"
const size_t NUM_RENDERERS = 6;
class CompareRenderer
{
public:
CompareRenderer(const char * testFile, Renderer** renderers, bool verbose)
: m_fileBuffer(NULL), m_fileLength(0), m_numLines(0), m_lineOffsets(NULL),
m_renderers(renderers), m_verbose(verbose), m_cfMask(ALL_DIFFERENCE_TYPES)
{
// read the file into memory for fast access
struct stat fileStat;
if (stat(testFile, &fileStat) == 0)
{
FILE * file = fopen(testFile, "rb");
if (file)
{
m_fileBuffer = new char[fileStat.st_size];
if (m_fileBuffer)
{
m_fileLength = fread(m_fileBuffer, 1, fileStat.st_size, file);
assert(m_fileLength == fileStat.st_size);
countLines();
findLines();
for (size_t r = 0; r < NUM_RENDERERS; r++)
{
if (m_renderers[r])
{
m_lineResults[r] = new RenderedLine[m_numLines];
}
else
{
m_lineResults[r] = NULL;
}
m_elapsedTime[r] = 0.0f;
m_glyphCount[r] = 0;
}
}
fclose(file);
}
else
{
fprintf(stderr, "Error opening file %s\n", testFile);
}
}
else
{
fprintf(stderr, "Error stating file %s\n", testFile);
for (size_t r = 0; r < NUM_RENDERERS; r++)
{
m_lineResults[r] = NULL;
m_elapsedTime[r] = 0.0f;
}
}
}
~CompareRenderer()
{
delete [] m_fileBuffer;
m_fileBuffer = NULL;
for (size_t i = 0; i < NUM_RENDERERS; i++)
{
if (m_lineResults[i]) delete [] m_lineResults[i];
m_lineResults[i] = NULL;
}
if (m_lineOffsets) delete [] m_lineOffsets;
m_lineOffsets = NULL;
}
void runTests(FILE * log, int repeat = 1)
{
for (size_t r = 0; r < NUM_RENDERERS; r++)
{
if (m_renderers[r])
{
for (int i = 0; i < repeat; i++)
m_elapsedTime[r] += runRenderer(*m_renderers[r], m_lineResults[r], m_glyphCount[r], log);
fprintf(stdout, "Ran %s in %fs (%lu glyphs)\n", m_renderers[r]->name(), m_elapsedTime[r], m_glyphCount[r]);
}
}
}
int compare(float tolerance, float fractionalTolerance, FILE * log)
{
int status = IDENTICAL;
for (size_t i = 0; i < NUM_RENDERERS; i++)
{
for (size_t j = i + 1; j < NUM_RENDERERS; j++)
{
if (m_renderers[i] == NULL || m_renderers[j] == NULL) continue;
if (m_lineResults[i] == NULL || m_lineResults[j] == NULL) continue;
fprintf(log, "Comparing %s with %s\n", m_renderers[i]->name(), m_renderers[j]->name());
for (size_t line = 0; line < m_numLines; line++)
{
LineDifference ld = m_lineResults[i][line].compare(m_lineResults[j][line], tolerance, fractionalTolerance);
ld = (LineDifference)(m_cfMask & ld);
if (ld)
{
fprintf(log, "Line %u %s\n", (unsigned int)line, DIFFERENCE_DESC[ld]);
for (size_t c = m_lineOffsets[line]; c < m_lineOffsets[line+1]; c++)
{
fprintf(log, "%c", m_fileBuffer[c]);
}
fprintf(log, "\n");
m_lineResults[i][line].dump(log);
fprintf(log, "%s\n", m_renderers[i]->name());
m_lineResults[j][line].dump(log);
fprintf(log, "%s\n", m_renderers[j]->name());
status |= ld;
}
}
}
}
return status;
}
void setDifferenceMask(LineDifference m) { m_cfMask = m; }
protected:
float runRenderer(Renderer & renderer, RenderedLine * pLineResult, unsigned long & glyphCount, FILE *log)
{
glyphCount = 0;
unsigned int i = 0;
const char * pLine = m_fileBuffer;
#ifdef __linux__
struct timespec startTime;
struct timespec endTime;
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &startTime);
#else
#ifdef WIN32
LARGE_INTEGER counterFreq;
LARGE_INTEGER startCounter;
LARGE_INTEGER endCounter;
if (!QueryPerformanceFrequency(&counterFreq))
fprintf(stderr, "Warning no high performance counter available\n");
QueryPerformanceCounter(&startCounter);
#else
struct timeval startTime;
struct timeval endTime;
gettimeofday(&startTime,0);
#endif
#endif
// check for CRLF
int lfLength = 1;
if ((m_numLines > 1) && (m_lineOffsets[1] > 2) && (m_fileBuffer[m_lineOffsets[1]-2] == '\r'))
lfLength = 2;
if (m_verbose)
{
fprintf(log, "[\n");
while (i < m_numLines)
{
size_t lineLength = m_lineOffsets[i+1] - m_lineOffsets[i] - lfLength;
pLine = m_fileBuffer + m_lineOffsets[i];
renderer.renderText(pLine, lineLength, pLineResult + i, log);
pLineResult[i].dump(log);
glyphCount += pLineResult[i].numGlyphs();
++i;
}
fprintf(log, "]\n");
}
else
{
while (i < m_numLines)
{
size_t lineLength = m_lineOffsets[i+1] - m_lineOffsets[i] - lfLength;
pLine = m_fileBuffer + m_lineOffsets[i];
renderer.renderText(pLine, lineLength, pLineResult + i, log);
glyphCount += pLineResult[i].numGlyphs();
++i;
}
}
float elapsed = 0.;
#ifdef __linux__
clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &endTime);
long deltaSeconds = endTime.tv_sec - startTime.tv_sec;
long deltaNs = endTime.tv_nsec - startTime.tv_nsec;
if (deltaNs < 0)
{
deltaSeconds -= 1;
deltaNs += 1000000000;
}
elapsed = deltaSeconds + deltaNs / 1000000000.0f;
#else
#ifdef WIN32
QueryPerformanceCounter(&endCounter);
elapsed = (endCounter.QuadPart - startCounter.QuadPart) / static_cast<float>(counterFreq.QuadPart);
#else
gettimeofday(&endTime,0);
long deltaSeconds = endTime.tv_sec - startTime.tv_sec;
long deltaUs = endTime.tv_usec - startTime.tv_usec;
if (deltaUs < 0)
{
deltaSeconds -= 1;
deltaUs += 1000000;
}
elapsed = deltaSeconds + deltaUs / 1000000.0f;
#endif
#endif
return elapsed;
}
size_t countLines()
{
for (size_t i = 0; i < m_fileLength; i++)
{
if (m_fileBuffer[i] == '\n')
{
++m_numLines;
}
}
return m_numLines;
}
void findLines()
{
m_lineOffsets = new size_t[m_numLines+1];
m_lineOffsets[0] = 0;
int line = 0;
for (size_t i = 0; i < m_fileLength; i++)
{
if (m_fileBuffer[i] == '\n')
{
m_lineOffsets[++line] = i + 1;
}
if (m_fileBuffer[i] > 0 && m_fileBuffer[i] < 32)
m_fileBuffer[i] = 32;
}
m_lineOffsets[m_numLines] = m_fileLength;
}
private:
char * m_fileBuffer;
size_t m_fileLength;
size_t m_numLines;
size_t * m_lineOffsets;
Renderer** m_renderers;
RenderedLine * m_lineResults[NUM_RENDERERS];
float m_elapsedTime[NUM_RENDERERS];
unsigned long m_glyphCount[NUM_RENDERERS];
bool m_verbose;
LineDifference m_cfMask;
};
int main(int argc, char ** argv)
{
if (!parseOptions(argc, argv) ||
!rendererOptions[OptFontFile].exists() ||
!rendererOptions[OptTextFile].exists() ||
!rendererOptions[OptSize].exists())
{
fprintf(stderr, "Usage:\n%s [options] -t utf8.txt -f font.ttf -s 12\n", argv[0]);
fprintf(stderr, "Options:\n");
showOptions();
return -1;
}
const char * textFile = rendererOptions[OptTextFile].get(argv);
const char * fontFile = rendererOptions[OptFontFile].get(argv);
int fontSize = rendererOptions[OptSize].getInt(argv);
FILE * log = stdout;
if (rendererOptions[OptLogFile].exists())
{
log = fopen(rendererOptions[OptLogFile].get(argv), "wb");
if (!log)
{
fprintf(stderr, "Failed to open log file %s\n",
rendererOptions[OptLogFile].get(argv));
return -2;
}
}
Renderer* renderers[NUM_RENDERERS] = {NULL, NULL, NULL, NULL, NULL};
FeatureParser * featureSettings = NULL;
FeatureParser * altFeatureSettings = NULL;
bool direction = rendererOptions[OptRtl].exists();
const std::string traceLogPath = rendererOptions[OptTrace].exists() ? rendererOptions[OptTrace].get(argv) : std::string();
Gr2Face face(fontFile, traceLogPath, rendererOptions[OptDemand].get(argv));
if (rendererOptions[OptFeatures].exists())
{
featureSettings = new FeatureParser(rendererOptions[OptFeatures].get(argv));
}
if (rendererOptions[OptQuiet].exists())
{
fclose(stderr);
}
if (rendererOptions[OptAlternativeFont].exists())
{
if (rendererOptions[OptAltFeatures].exists())
{
altFeatureSettings = new FeatureParser(rendererOptions[OptAltFeatures].get(argv));
}
else
{
altFeatureSettings = featureSettings;
}
const char * altFontFile = rendererOptions[OptAlternativeFont].get(argv);
if (rendererOptions[OptGraphite2].exists())
{
std::string altTraceLogPath = traceLogPath;
altTraceLogPath.insert(traceLogPath.find_last_of('.'), ".alt");
Gr2Face altFace(altFontFile, altTraceLogPath, rendererOptions[OptDemand].get(argv));
renderers[0] = new Gr2Renderer(face, fontSize, direction, featureSettings);
renderers[1] = new Gr2Renderer(altFace, fontSize, direction, altFeatureSettings);
}
}
else
{
if (rendererOptions[OptGraphite2].exists())
renderers[0] = new Gr2Renderer(face, fontSize, direction, featureSettings);
if (rendererOptions[OptGraphite2s].exists())
{
Gr2Face uncached(fontFile,
std::string(traceLogPath).insert(traceLogPath.find_last_of('.'), ".uncached"), rendererOptions[OptDemand].get(argv));
renderers[1] = new Gr2Renderer(uncached, fontSize, direction, featureSettings);
}
}
if (renderers[0] == NULL && renderers[1] == NULL)
{
fprintf(stderr, "Please specify at least 1 renderer\n");
showOptions();
if (rendererOptions[OptLogFile].exists()) fclose(log);
return -3;
}
CompareRenderer compareRenderers(textFile, renderers, rendererOptions[OptVerbose].exists());
if (rendererOptions[OptRepeat].exists())
compareRenderers.runTests(log, rendererOptions[OptRepeat].getInt(argv));
else
compareRenderers.runTests(log);
// set compare options
if (rendererOptions[OptIgnoreGlyphIdDifferences].exists())
{
compareRenderers.setDifferenceMask((LineDifference)(ALL_DIFFERENCE_TYPES ^ DIFFERENT_GLYPHS));
}
int status = 0;
if (rendererOptions[OptCompare].exists())
status = compareRenderers.compare(rendererOptions[OptTolerance].getFloat(argv),
rendererOptions[OptFractionalTolerance].getFloat(argv), log);
for (size_t i = 0; i < NUM_RENDERERS; i++)
{
if (renderers[i])
{
delete renderers[i];
renderers[i] = NULL;
}
}
if (altFeatureSettings != featureSettings)
delete altFeatureSettings;
delete featureSettings;
if (rendererOptions[OptLogFile].exists()) fclose(log);
return status;
}

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