├── .github └── workflows │ ├── ci.yml │ └── run_ctest.sh ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── CMakeLists.txt ├── INSTALL.md ├── LICENSE ├── README.md ├── doc ├── CMakeLists.txt └── Doxyfile.in.txt ├── example ├── common │ ├── macros.h │ └── sample_text.h ├── js_example │ ├── README.md │ ├── js_example.html │ └── js_example.js ├── ppm_example │ ├── CMakeLists.txt │ ├── README.md │ ├── ppm_example.c │ └── ppm_example.h └── sdl_example │ ├── CMakeLists.txt │ ├── README.md │ ├── emscripten_shell.html │ ├── sdl_example.cpp │ └── sdl_example.hpp ├── include └── cobbletext │ ├── AdvanceInfo.hpp │ ├── Encoding.hpp │ ├── Engine.hpp │ ├── Exception.hpp │ ├── FontInfo.hpp │ ├── GlyphInfo.hpp │ ├── Library.hpp │ ├── Math.hpp │ ├── OutputInfo.hpp │ ├── ScriptDirection.hpp │ ├── TextAlignment.hpp │ ├── TileInfo.hpp │ ├── advance_info.h │ ├── cobbletext.h │ ├── cobbletext.hpp │ ├── common.h │ ├── common.hpp │ ├── encoding.h │ ├── engine.h │ ├── engine_properties.h │ ├── font_info.h │ ├── glyph_info.h │ ├── library.h │ ├── macros.h │ ├── math_.h │ ├── output_info.h │ ├── script_direction.h │ ├── text_alignment.h │ ├── text_properties.h │ ├── tile_info.h │ └── uchar_helper.h ├── misc ├── cmake_modules │ └── FindICUCobbletext.cmake ├── icu │ └── icu_data_filter.json ├── vcpkg_custom_ports │ ├── icu-cobbletext │ │ ├── CONTROL │ │ ├── disable-escapestr-tool.patch │ │ ├── fix-extra.patch │ │ ├── fix_parallel_build_on_windows.patch │ │ ├── portfile.cmake │ │ └── remove-MD-from-configure.patch │ └── notes.txt └── vcpkg_custom_triplets │ ├── arm64-osx-dynamic-release.cmake │ ├── arm64-osx-dynamic.cmake │ ├── x64-linux-dynamic-release.cmake │ ├── x64-linux-dynamic.cmake │ ├── x64-osx-dynamic-release.cmake │ ├── x64-osx-dynamic.cmake │ ├── x64-windows-dynamic-release.cmake │ └── x86-windows-dynamic-release.cmake ├── script ├── build_icu_emscripten_prefixed.sh ├── embed_resource.py ├── get_precompiled_icu_tartanllama.cmake ├── package_github_artifacts.py └── package_overlay │ └── notice │ ├── adobe_notdef.txt │ ├── boost.txt │ ├── bzip2.txt │ ├── emscripten.txt │ ├── gsl.txt │ ├── harfbuzz.txt │ ├── icu.txt │ ├── libpng.txt │ ├── stb.txt │ └── zlib.txt ├── src ├── AdvanceInfo.cpp ├── CMakeLists.txt ├── Engine.cpp ├── EngineImpl.cpp ├── EngineImpl.hpp ├── Exception.cpp ├── FontInfo.cpp ├── GlyphInfo.cpp ├── Library.cpp ├── LibraryImpl.cpp ├── LibraryImpl.hpp ├── Math.cpp ├── OutputInfo.cpp ├── Resource.hpp ├── TileInfo.cpp ├── Version.h.in ├── c │ ├── engine_c.cpp │ ├── engine_c.hpp │ ├── library_c.cpp │ ├── library_c.hpp │ ├── math_c.cpp │ ├── util_c.cpp │ ├── util_c.hpp │ └── version_c.cpp ├── em │ ├── AdvanceInfo_em.cpp │ ├── CMakeLists.txt │ ├── Encoding_em.cpp │ ├── Engine_em.cpp │ ├── Exception_em.cpp │ ├── FontInfo_em.cpp │ ├── GlyphInfo_em.cpp │ ├── Library_em.cpp │ ├── Math_em.cpp │ ├── Output_em.cpp │ ├── ScriptDirection_em.cpp │ ├── TextAlignment_em.cpp │ ├── TileInfo_em.cpp │ └── js │ │ └── cobbletext_js.cpp └── internal │ ├── Codec.cpp │ ├── Codec.hpp │ ├── ColorUtil.hpp │ ├── Context.hpp │ ├── Debug.hpp │ ├── ExceptionUtil.hpp │ ├── FreeType.cpp │ ├── FreeType.hpp │ ├── ICUError.cpp │ ├── ICUError.hpp │ ├── RandomUtil.cpp │ ├── RandomUtil.hpp │ ├── font │ ├── Font.hpp │ ├── FontTable.cpp │ ├── FontTable.hpp │ ├── Glyph.hpp │ ├── GlyphKey.cpp │ ├── GlyphKey.hpp │ ├── GlyphTable.cpp │ └── GlyphTable.hpp │ ├── image │ ├── AtlasPacker.cpp │ ├── AtlasPacker.hpp │ └── ImageResize.hpp │ ├── input │ ├── InlineObject.hpp │ ├── StringIndexer.cpp │ ├── StringIndexer.hpp │ ├── TextFormat.hpp │ ├── TextRun.cpp │ ├── TextRun.hpp │ ├── TextSource.cpp │ └── TextSource.hpp │ ├── layout │ ├── InternalTextRun.cpp │ ├── InternalTextRun.hpp │ ├── LayoutEngine.cpp │ ├── LayoutEngine.hpp │ ├── LineBreaker.cpp │ ├── LineBreaker.hpp │ ├── LineRun.cpp │ ├── LineRun.hpp │ ├── ShapeResult.cpp │ ├── ShapeResult.hpp │ ├── Shaper.cpp │ └── Shaper.hpp │ └── table │ ├── BidiTable.cpp │ ├── BidiTable.hpp │ ├── ScriptTable.cpp │ └── ScriptTable.hpp └── test ├── CMakeLists.txt ├── main.cpp ├── test_engine.cpp ├── test_internal_input_StringIndexer.cpp ├── test_library.cpp └── test_version.cpp /.github/workflows/run_ctest.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ctest copies the libraries to the executable directory automatically 4 | # on Windows. Otherwise, will use the ld/dyld environment variables. 5 | # On macOS, DYLD_FALLBACK_LIBRARY_PATH is stripped on child processes 6 | # which is why this wrapper script exists.. 7 | set -x 8 | set -e 9 | 10 | TRIPLET="$1" 11 | 12 | LD_LIBRARY_PATH="$GITHUB_WORKSPACE/vcpkg/installed/$TRIPLET/lib/" \ 13 | DYLD_FALLBACK_LIBRARY_PATH="$GITHUB_WORKSPACE/vcpkg/installed/$TRIPLET/lib/:$GITHUB_WORKSPACE/build/bin/:/usr/local/lib:/lib:/usr/lib" \ 14 | ctest --verbose -C Debug 15 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | ## Cmake output directory 3 | build/ 4 | build_em/ 5 | 6 | 7 | ## Visual Studio Code 8 | .vscode/ 9 | 10 | 11 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "lib/adobe-notdef"] 2 | path = lib/adobe-notdef 3 | url = https://github.com/adobe-fonts/adobe-notdef 4 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 0.2.1 (2025-05-05) 4 | 5 | * Fixed a memory leak [(#2)](https://github.com/chfoo/cobbletext/issues/2) 6 | * Fixed compatibility with latest version of stb 7 | 8 | ## 0.2.0 (2020-05-08) 9 | 10 | * Removed library `clear_glyphs()` and added engine `clear_tiles()`. 11 | * Changed `add_inline_object()` to accept width and height instead of size. 12 | * Fixed Engine OutputInfo containing uninitialized values if `lay_out()` was not called. 13 | * Fixed dynamic library symbol exports on Windows. 14 | * Added CMake variable `COBBLETEXT_CPP_API` to enable C++ API on a dynamic library build for Windows. 15 | 16 | ## 0.1.0 (2020-04-30) 17 | 18 | * First release 19 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | include(CheckIncludeFile) 4 | 5 | project(cobbletext VERSION 0.2.1 DESCRIPTION "Complex text layout and rendering engine") 6 | 7 | set(COBBLETEXT_BUILD_EXAMPLES true CACHE BOOL "Whether to include example programs into the build config") 8 | set(COBBLETEXT_BUILD_TESTS true CACHE BOOL "Whether to include tests into the build config") 9 | set(COBBLETEXT_BUILD_DOCS true CACHE BOOL "Whether to include documentation to the build config") 10 | set(COBBLETEXT_STATIC false CACHE BOOL "Enable to build the library as a static library. Default is dynamic library.") 11 | set(COBBLETEXT_CPP_API false CACHE BOOL "Enable the C++ API for dynamic library build on Windows.") 12 | set(COBBLETEXT_CUSTOM_ICU false CACHE BOOL "Whether to use link with ICU libraries compiled with --with-library-suffix=cobbletext") 13 | set(COBBLETEXT_EMSCRIPTEN false CACHE BOOL "Enables config to build to Emscripten with emcmake and emmake.") 14 | 15 | if(COBBLETEXT_EMSCRIPTEN) 16 | set(COBBLETEXT_EMSCRIPTEN_ICU_STRATEGY "manual" CACHE STRING "Sets how to get ICU" 17 | STRINGS "download_from_tartanllama" "manual") 18 | endif() 19 | 20 | set(CMAKE_CXX_STANDARD 17) 21 | set(CMAKE_CXX_STANDARD_REQUIRED True) 22 | set(C_STANDARD 11) 23 | 24 | CHECK_INCLUDE_FILE("uchar.h" HAS_UCHAR_H) 25 | 26 | if(NOT HAS_UCHAR_H) 27 | add_compile_definitions(COBBLETEXT_NO_UCHAR_H=1) 28 | endif() 29 | 30 | if(COBBLETEXT_STATIC) 31 | add_compile_definitions(COBBLETEXT_STATIC=1) 32 | endif() 33 | 34 | if(COBBLETEXT_CPP_API) 35 | add_compile_definitions(COBBLETEXT_ENABLE_CPP_API=1) 36 | endif() 37 | 38 | if(COBBLETEXT_EMSCRIPTEN) 39 | add_compile_options("SHELL:-s USE_BOOST_HEADERS=1") 40 | # add_compile_options("SHELL:-s USE_ICU=1") 41 | add_compile_options("SHELL:-s USE_SDL=2") 42 | add_compile_options("SHELL:-s USE_FREETYPE=1") 43 | add_compile_options("SHELL:-s USE_HARFBUZZ=1") 44 | 45 | add_link_options("SHELL:-s USE_BOOST_HEADERS=1") 46 | # add_link_options("SHELL:-s USE_ICU=1") 47 | add_link_options("SHELL:-s USE_SDL=2") 48 | add_link_options("SHELL:-s USE_FREETYPE=1") 49 | add_link_options("SHELL:-s USE_HARFBUZZ=1") 50 | 51 | add_link_options("SHELL:-s TOTAL_MEMORY=41943040") # actually INTITAL_MEMORY 52 | add_link_options("SHELL:-s ALLOW_MEMORY_GROWTH=1") 53 | 54 | if(COBBLETEXT_EMSCRIPTEN_ICU_STRATEGY STREQUAL "download_from_tartanllama") 55 | include(script/get_precompiled_icu_tartanllama.cmake) 56 | endif() 57 | else() 58 | find_package(Boost REQUIRED) 59 | find_package(Freetype REQUIRED) 60 | find_path(HARFBUZZ_INCLUDE_PATH "harfbuzz") 61 | find_library(HARFBUZZ_LIB_PATH "harfbuzz") 62 | endif() 63 | 64 | # dt = data 65 | # uc = common 66 | # in = i18n: locale i18n functions 67 | # lx = layout extensions, optional engine 68 | # io = I/O, optional support for I/O with Unicode 69 | # tu = tool utility library, internal API for optional tools 70 | if(COBBLETEXT_CUSTOM_ICU) 71 | list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/misc/cmake_modules/") 72 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) 73 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) 74 | find_package(ICUCobbletext REQUIRED COMPONENTS uc data) # data needs to be linked last 75 | add_compile_definitions(U_HAVE_LIB_SUFFIX=1) 76 | add_compile_definitions(U_LIB_SUFFIX_C_NAME=_cobbletext) 77 | else() 78 | find_package(ICU REQUIRED COMPONENTS uc data) 79 | endif() 80 | 81 | find_path(MS_GSL_INCLUDE_PATH "gsl") 82 | find_path(STB_INCLUDE_PATH "stb_image_resize2.h" PATH_SUFFIXES "stb") 83 | 84 | set(COBBLETEXT_DEPENDENCY_INCLUDES 85 | ${Boost_INCLUDE_DIRS} 86 | ${FREETYPE_INCLUDE_DIRS} 87 | "${HARFBUZZ_INCLUDE_PATH}" 88 | "${ICU_INCLUDE_DIR}" 89 | "${MS_GSL_INCLUDE_PATH}" 90 | "${STB_INCLUDE_PATH}" 91 | ) 92 | set(COBBLETEXT_DEPENDENCY_LIBS 93 | ${FREETYPE_LIBRARIES} 94 | ${ICU_LIBRARIES} 95 | ${HARFBUZZ_LIB_PATH} 96 | ) 97 | 98 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 99 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin") 100 | 101 | enable_testing() 102 | 103 | add_subdirectory(src cobbletext) 104 | 105 | if(COBBLETEXT_BUILD_EXAMPLES) 106 | add_subdirectory(example/ppm_example) 107 | add_subdirectory(example/sdl_example) 108 | endif() 109 | 110 | if(COBBLETEXT_BUILD_TESTS) 111 | add_subdirectory(test) 112 | endif() 113 | 114 | if(COBBLETEXT_BUILD_DOCS) 115 | add_subdirectory("doc") 116 | endif() 117 | 118 | if(COBBLETEXT_EMSCRIPTEN) 119 | add_subdirectory(src/em cobbletext_em) 120 | endif() 121 | 122 | install(TARGETS cobbletext DESTINATION lib) 123 | install(DIRECTORY "${CMAKE_SOURCE_DIR}/include/cobbletext/" 124 | DESTINATION "include/cobbletext" 125 | ) 126 | 127 | if(COBBLETEXT_EMSCRIPTEN) 128 | install(TARGETS cobbletext_js DESTINATION bin) 129 | install(FILES "${CMAKE_BINARY_DIR}/bin/cobbletext.wasm" DESTINATION bin) 130 | endif() 131 | -------------------------------------------------------------------------------- /doc/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # File adapted from https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/ 2 | 3 | find_package(Doxygen REQUIRED) 4 | 5 | file(GLOB_RECURSE COBBLETEXT_PUBLIC_HEADERS 6 | "${CMAKE_SOURCE_DIR}/include/cobbletext/*.h" 7 | "${CMAKE_SOURCE_DIR}/include/cobbletext/*.hpp") 8 | 9 | set(DOXYGEN_INPUT_DIR "${CMAKE_SOURCE_DIR}/include/") 10 | set(DOXYGEN_OUTPUT_DIR "${CMAKE_CURRENT_BINARY_DIR}/doxygen") 11 | set(DOXYGEN_INDEX_FILE "${DOXYGEN_OUTPUT_DIR}/html/index.html") 12 | set(DOXYFILE_IN "${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in.txt") 13 | set(DOXYFILE_OUT "${CMAKE_CURRENT_BINARY_DIR}/Doxyfile") 14 | 15 | configure_file("${DOXYFILE_IN}" "${DOXYFILE_OUT}" @ONLY) 16 | 17 | file(MAKE_DIRECTORY "${DOXYGEN_OUTPUT_DIR}") 18 | 19 | add_custom_command( 20 | OUTPUT "${DOXYGEN_INDEX_FILE}" 21 | DEPENDS ${COBBLETEXT_PUBLIC_HEADERS} 22 | COMMAND "${DOXYGEN_EXECUTABLE}" ${DOXYFILE_OUT} 23 | MAIN_DEPENDENCY "${DOXYFILE_OUT}" "${DOXYFILE_IN}" 24 | VERBATIM 25 | ) 26 | 27 | add_custom_target("doc" ALL DEPENDS "${DOXYGEN_INDEX_FILE}" VERBATIM) 28 | -------------------------------------------------------------------------------- /example/common/macros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #define ABORT_WITH_MESSAGE(message) { printf((message)); abort(); } 5 | #define ABORT_IF_NULL(value, message) { if ((value) == NULL) { printf((message)); abort(); } } 6 | -------------------------------------------------------------------------------- /example/common/sample_text.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // "I can eat glass" text from http://kermitproject.org/utf8.html 4 | 5 | const char EAT_GLASS_TEXT[] = 6 | "I can eat glass and it doesn't hurt me. " 7 | "Je peux manger du verre, ça ne me fait pas mal. " 8 | "Puedo comer vidrio, no me hace daño. " 9 | "Я могу есть стекло, оно мне не вредит. " 10 | "मैं काँच खा सकता हूँ और मुझे उससे कोई चोट नहीं पहुंचती. " 11 | "أنا قادر على أكل الزجاج و هذا لا يؤلمني. " 12 | "אני יכול לאכול זכוכית וזה לא מזיק לי. " 13 | "Tôi có thể ăn thủy tinh mà không hại gì. " 14 | "ฉันกินกระจกได้ แต่มันไม่ทำให้ฉันเจ็บ " 15 | "我能吞下玻璃而不伤身体。" 16 | "我能吞下玻璃而不傷身體。" 17 | "私はガラスを食べられます。それは私を傷つけません。" 18 | "나는 유리를 먹을 수 있어요. 그래도 아프지 않아요 "; 19 | 20 | const char EMOJI_TEXT[] = 21 | "I🙂can😋eat🍽️glass👓🔍🪞🥂🪟and☝️it🙅doesn’t🤕hurt👍me." 22 | // Invisible code points warning! 23 | "🐕" // dog 24 | "🦮" // service dog 25 | "🐕‍🦺" // guide dog (3 code points) 26 | "🇦🇶" // Antarctica (2 code points) 27 | "🏴‍☠️" // pirate flag (4 code points) 28 | ""; 29 | -------------------------------------------------------------------------------- /example/js_example/README.md: -------------------------------------------------------------------------------- 1 | # js_example 2 | 3 | This HTML app shows drawing glyphs using the HTML5 canvas and cobbletext.js. 4 | 5 | Note that the HTML5 2D graphics context doesn't work entirely in our favor, so the example only supports colors on the entire text. As well, gamma correction for improved readability has been omitted. 6 | 7 | To run the example, it requires the files: 8 | 9 | * cobbletext.js 10 | * cobbletext.wasm 11 | * NotoSans-Regular.ttf 12 | * NotoSansCJK-Regular.ttc 13 | * NotoSansArabic-Regular.ttf 14 | * NotoSansHebrew-Regular.ttf 15 | * Symbola.otf 16 | 17 | Some of the API returns handles to C++ vectors. These need to be deleted manually from the JavaScript side. In a future release, this may be improved to use regular JavaScript arrays. 18 | -------------------------------------------------------------------------------- /example/js_example/js_example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Cobbletext Javascript Demo 7 | 8 | 51 | 52 | 53 | 54 |
55 | Loading... 56 | 57 |
58 |
59 |
60 |
61 | 62 | 63 | 64 |
65 | 66 |
67 | 68 | 69 |
70 |
71 | 72 | 73 |
74 |
75 | 76 | 87 |
88 |
89 | 90 |
91 |
92 |
Render context:
93 | 94 |
95 |
96 |
Coverage texture atlas:
97 | 98 |
99 |
100 |
HTML5 colored atlas:
101 | 102 |
103 | 104 |
105 | 106 |
107 |

108 |
109 |
110 | 111 | 112 | -------------------------------------------------------------------------------- /example/ppm_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(ppm_example ppm_example.c) 3 | 4 | find_path(UTHASH_INCLUDE_PATH "uthash.h") 5 | target_include_directories(ppm_example PRIVATE 6 | "${CMAKE_SOURCE_DIR}/include/" 7 | "${CMAKE_SOURCE_DIR}/src/" 8 | "${CMAKE_CURRENT_BINARY_DIR}" 9 | ${UTHASH_INCLUDE_PATH} 10 | ) 11 | 12 | if(WIN32) 13 | find_path(GETOPT_INCLUDE_PATH "getopt.h") 14 | find_library(GETOPT_LIBRARY getopt) 15 | target_include_directories(ppm_example PRIVATE "${GETOPT_INCLUDE_PATH}") 16 | target_link_libraries(ppm_example PRIVATE "${GETOPT_LIBRARY}") 17 | endif() 18 | 19 | add_compile_definitions(HASH_DEBUG=1) 20 | 21 | target_link_libraries(ppm_example PRIVATE cobbletext ${COBBLETEXT_DEPENDENCY_LIBS}) 22 | 23 | if(NOT WIN32) 24 | target_link_libraries(ppm_example PRIVATE dl) 25 | endif() 26 | 27 | add_test(NAME ppm_example COMMAND ppm_example WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 28 | -------------------------------------------------------------------------------- /example/ppm_example/README.md: -------------------------------------------------------------------------------- 1 | # ppm_example 2 | 3 | This C program writes a PPM image file output along with a PGM image of each glyph. 4 | 5 | Example usage: 6 | 7 | ./ppm_example -w 500 -f /usr/share/fonts/truetype/noto/NotoSans-Regular.ttf \ 8 | -f /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc \ 9 | -f /usr/share/fonts/truetype/unifont/unifont.ttf 10 | 11 | Use `./ppm_example -h` to see all options. 12 | -------------------------------------------------------------------------------- /example/ppm_example/ppm_example.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | enum Property { 11 | PROPERTY_NONE = 0, 12 | PROPERTY_RED_TEXT = 1 13 | }; 14 | 15 | #define INLINE_OBJECT_1 1 16 | 17 | struct AtlasEntry { 18 | CobbletextGlyphID glyph_id; 19 | uint32_t x; 20 | uint32_t y; 21 | uint32_t width; 22 | uint32_t height; 23 | int32_t offset_x; 24 | int32_t offset_y; 25 | UT_hash_handle hh; 26 | }; 27 | 28 | typedef struct App { 29 | CobbletextLibrary * library; 30 | CobbletextEngine * engine; 31 | struct AtlasEntry * atlas_table; 32 | CobbletextFontID fonts[16]; 33 | uint32_t font_count; 34 | uint32_t line_length; 35 | uint32_t text_alignment; 36 | char * locale; 37 | uint8_t * atlas; 38 | uint32_t atlas_size; 39 | uint32_t * image; 40 | uint32_t image_width; 41 | uint32_t image_height; 42 | int32_t pen_x; 43 | int32_t pen_y; 44 | } App; 45 | 46 | void app_init(App * app, int argc, char * argv[]); 47 | 48 | void app_print_help(App * app); 49 | 50 | void app_run(App * app); 51 | 52 | void app_destroy(App * app); 53 | 54 | void app_check_error(App * app); 55 | 56 | void app_load_font(App * app, char * path); 57 | 58 | void app_set_up_font_fallback(App * app); 59 | 60 | void app_set_text(App * app); 61 | 62 | void app_render_text(App * app); 63 | 64 | void app_create_atlas(App * app); 65 | 66 | void app_pack_tiles_for_atlas(App * app); 67 | 68 | void app_draw_atlas_tile(App * app, 69 | const struct CobbletextTileInfo * tile, 70 | const struct CobbletextGlyphInfo * glyph); 71 | 72 | void app_draw_image(App * app); 73 | 74 | void app_draw_glyph(App * app, 75 | const struct CobbletextAdvanceInfo * advance); 76 | 77 | void app_save_file(App * app); 78 | -------------------------------------------------------------------------------- /example/sdl_example/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_executable(sdl_example sdl_example.cpp) 3 | 4 | target_include_directories(sdl_example PRIVATE 5 | "${CMAKE_SOURCE_DIR}/include/" 6 | "${CMAKE_SOURCE_DIR}/src/" 7 | "${CMAKE_CURRENT_BINARY_DIR}" 8 | ) 9 | 10 | if(NOT COBBLETEXT_EMSCRIPTEN) 11 | find_path(SDL2_INCLUDE_PATH SDL.h PATH_SUFFIXES SDL2) 12 | find_library(SDL2_LIBRARY NAMES SDL2) 13 | 14 | message("SDL2 headers located at ${SDL2_INCLUDE_PATH}") 15 | message("SDL2 library is ${SDL2_LIBRARY}") 16 | 17 | target_include_directories(sdl_example PRIVATE ${SDL2_INCLUDE_PATH}) 18 | 19 | if(WIN32) 20 | find_path(GETOPT_INCLUDE_PATH "getopt.h") 21 | find_library(GETOPT_LIBRARY getopt) 22 | target_include_directories(sdl_example PRIVATE "${GETOPT_INCLUDE_PATH}") 23 | target_link_libraries(sdl_example PRIVATE "${GETOPT_LIBRARY}") 24 | else() 25 | target_link_libraries(sdl_example PRIVATE dl) 26 | endif() 27 | 28 | elseif(COBBLETEXT_EMSCRIPTEN) 29 | set(CMAKE_EXECUTABLE_SUFFIX ".html") 30 | target_link_options(sdl_example PRIVATE "SHELL:-s EXIT_RUNTIME=1") 31 | target_link_options(sdl_example PRIVATE --shell-file "${CMAKE_CURRENT_SOURCE_DIR}/emscripten_shell.html") 32 | endif() 33 | 34 | target_link_libraries(sdl_example PRIVATE cobbletext ${COBBLETEXT_DEPENDENCY_LIBS} ${SDL2_LIBRARY}) 35 | -------------------------------------------------------------------------------- /example/sdl_example/README.md: -------------------------------------------------------------------------------- 1 | # sdl_example 2 | 3 | This C++ program uses SDL to display the text. It draws all the glyphs to a texture atlas (with data on the alpha channel) and renders sections of the texture to the renderer. 4 | 5 | While the glyphs are rendered with alpha blending in this example, gamma correction is not performed to improve readability due to limitations in the SDL API. 6 | 7 | For high performance rendering in 3D applications, an array of tile information can be passed to a shader program that draws the texture atlas with alpha blending and gamma correction. This is out of scope for this example. 8 | 9 | Example usage: 10 | 11 | ./sdl_example -f /usr/share/fonts/truetype/noto/NotoSans-Regular.ttf \ 12 | -f /usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc \ 13 | -f /usr/share/fonts/truetype/unifont/unifont.ttf 14 | 15 | Use `./sdl_example -h` to see all options. 16 | -------------------------------------------------------------------------------- /example/sdl_example/sdl_example.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __EMSCRIPTEN__ 8 | #include 9 | #include 10 | #endif 11 | 12 | #define SDL_MAIN_HANDLED 13 | 14 | #ifdef EXAMPLE_INCLUDE_SDL_STYLE_1 15 | #include 16 | #elif defined(EXAMPLE_INCLUDE_SDL_STYLE_2) 17 | #include 18 | #else 19 | #include 20 | #endif 21 | 22 | #include 23 | 24 | #ifdef __EMSCRIPTEN__ 25 | extern "C" { 26 | void EMSCRIPTEN_KEEPALIVE setRendererSize(double width, double height); 27 | } 28 | #endif 29 | 30 | namespace example { 31 | 32 | class WindowDeleter { 33 | public: 34 | void operator()(SDL_Window * pointer) { 35 | SDL_DestroyWindow(pointer); 36 | } 37 | }; 38 | 39 | class RendererDeleter { 40 | public: 41 | void operator()(SDL_Renderer * pointer) { 42 | SDL_DestroyRenderer(pointer); 43 | } 44 | }; 45 | 46 | class SurfaceDeleter { 47 | public: 48 | void operator()(SDL_Surface * pointer) { 49 | SDL_FreeSurface(pointer); 50 | } 51 | }; 52 | 53 | class TextureDeleter { 54 | public: 55 | void operator()(SDL_Texture * pointer) { 56 | SDL_DestroyTexture(pointer); 57 | } 58 | }; 59 | 60 | enum class CustomProperty : int { 61 | None = 0, 62 | RedText = 1 63 | }; 64 | 65 | class AtlasEntry { 66 | public: 67 | uint32_t atlasX; 68 | uint32_t atlasY; 69 | uint32_t imageWidth; 70 | uint32_t imageHeight; 71 | int32_t imageOffsetX; 72 | int32_t imageOffsetY; 73 | }; 74 | 75 | class App { 76 | std::unique_ptr window; 77 | std::unique_ptr renderer; 78 | bool running = false; 79 | bool windowDirty = true; 80 | 81 | std::shared_ptr library; 82 | std::shared_ptr engine; 83 | 84 | std::vector fonts; 85 | 86 | static constexpr int InlineObject1 = 1; 87 | 88 | uint32_t atlasSize = 128; 89 | std::unique_ptr atlas; 90 | std::unordered_map atlasTable; 91 | 92 | int32_t penX; 93 | int32_t penY; 94 | 95 | int rendererWidth; 96 | int rendererHeight; 97 | 98 | public: 99 | App(); 100 | void parseArgs(int argc, char * argv[]); 101 | void run(); 102 | 103 | private: 104 | #ifdef __EMSCRIPTEN__ 105 | static void emCallback(void* userData); 106 | #endif 107 | 108 | void checkSDLError(int errorCode); 109 | [[noreturn]] void reportSDLError(); 110 | 111 | void runOnce(); 112 | 113 | void printHelp(); 114 | void processEvent(SDL_Event & event); 115 | void loadFont(char * path); 116 | void setUpFontFallback(); 117 | void setUpText(); 118 | void layOutText(); 119 | void createAtlas(); 120 | void packTilesForAtlas(); 121 | void drawAtlasTile(uint8_t * textureData, 122 | const cobbletext::TileInfo & tile, 123 | const cobbletext::GlyphInfo & glyph); 124 | void drawText(); 125 | void drawGlyph(const cobbletext::AdvanceInfo & advance); 126 | }; 127 | 128 | } 129 | 130 | -------------------------------------------------------------------------------- /include/cobbletext/AdvanceInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.hpp" 6 | #include "macros.h" 7 | 8 | namespace cobbletext { 9 | 10 | 11 | enum class AdvanceType { 12 | Invalid = 0, 13 | Glyph = 1, 14 | InlineObject = 2, 15 | LineBreak = 3, 16 | Bidi = 4, 17 | Layout = 5 18 | }; 19 | 20 | class COBBLETEXT_CPP_API AdvanceInfo { 21 | public: 22 | AdvanceType type = AdvanceType::Invalid; 23 | uint32_t textIndex = 0; 24 | 25 | int32_t advanceX = 0; 26 | int32_t advanceY = 0; 27 | 28 | GlyphID glyphID = 0; 29 | int32_t glyphOffsetX = 0; 30 | int32_t glyphOffsetY = 0; 31 | 32 | InlineObjectID inlineObject = 0; 33 | 34 | CustomPropertyID customProperty = 0; 35 | 36 | private: 37 | friend std::ostream & operator<<(std::ostream & stream, 38 | const AdvanceInfo & advanceInfo); 39 | }; 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /include/cobbletext/Encoding.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cobbletext { 4 | 5 | enum class Encoding { 6 | UTF8 = 1, 7 | UTF16LE = 2 8 | }; 9 | 10 | 11 | } 12 | -------------------------------------------------------------------------------- /include/cobbletext/Engine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "AdvanceInfo.hpp" 7 | #include "common.hpp" 8 | #include "macros.h" 9 | #include "Encoding.hpp" 10 | #include "FontInfo.hpp" 11 | #include "GlyphInfo.hpp" 12 | #include "Library.hpp" 13 | #include "OutputInfo.hpp" 14 | #include "ScriptDirection.hpp" 15 | #include "TileInfo.hpp" 16 | #include "TextAlignment.hpp" 17 | 18 | namespace cobbletext { 19 | 20 | 21 | class COBBLETEXT_CPP_API Engine { 22 | public: 23 | uint32_t lineLength = 0; 24 | std::string locale; 25 | TextAlignment textAlignment = TextAlignment::NotSpecified; 26 | std::string language; 27 | std::string script; 28 | ScriptDirection scriptDirection = ScriptDirection::NotSpecified; 29 | FontID font = 0; 30 | double fontSize = 12; 31 | CustomPropertyID customProperty = 0; 32 | OutputInfo outputInfo; 33 | 34 | std::vector tiles(); 35 | std::vector advances(); 36 | 37 | explicit Engine(std::shared_ptr library); 38 | ~Engine(); 39 | 40 | void addText(const uint8_t * data, uint32_t length, Encoding encoding); 41 | void addTextUTF8(std::string_view text); 42 | void addTextUTF8(const char * text, int32_t length); 43 | void addTextUTF16(std::u16string_view text); 44 | void addTextUTF16(const char16_t * text, int32_t length); 45 | void addTextUTF32(std::u32string_view text); 46 | void addTextUTF32(const char32_t * text, int32_t length); 47 | 48 | void addInlineObject(InlineObjectID id, uint32_t width, uint32_t height); 49 | 50 | void clear(); 51 | void layOut(); 52 | 53 | bool tilesValid(); 54 | 55 | void rasterize(); 56 | 57 | bool packTiles(uint32_t width, uint32_t height); 58 | void clearTiles(); 59 | 60 | private: 61 | class Impl; 62 | std::unique_ptr impl; 63 | }; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /include/cobbletext/Exception.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "macros.h" 8 | 9 | namespace cobbletext { 10 | 11 | class COBBLETEXT_CPP_API RuntimeError : public std::runtime_error { 12 | public: 13 | explicit RuntimeError(const std::string & what_arg); 14 | explicit RuntimeError(const char* what_arg); 15 | }; 16 | 17 | class COBBLETEXT_CPP_API LogicError : public std::logic_error { 18 | public: 19 | explicit LogicError(const std::string & what_arg); 20 | explicit LogicError(const char* what_arg); 21 | }; 22 | 23 | class COBBLETEXT_CPP_API LibraryError : public RuntimeError { 24 | public: 25 | long long int code; 26 | std::string message; 27 | 28 | explicit LibraryError(std::string message); 29 | explicit LibraryError(std::string message, long long int code); 30 | }; 31 | 32 | } 33 | -------------------------------------------------------------------------------- /include/cobbletext/FontInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "macros.h" 8 | 9 | namespace cobbletext { 10 | 11 | class COBBLETEXT_CPP_API FontInfo { 12 | public: 13 | FontID id = 0; 14 | std::string familyName; 15 | std::string styleName; 16 | uint16_t unitsPerEM = 0; 17 | int16_t ascender = 0; 18 | int16_t descender = 0; 19 | int16_t height = 0; 20 | int16_t underlinePosition = 0; 21 | int16_t underlineThickness = 0; 22 | 23 | private: 24 | friend std::ostream & operator<<(std::ostream & stream, 25 | const FontInfo & fontInfo); 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /include/cobbletext/GlyphInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "common.hpp" 7 | #include "macros.h" 8 | 9 | namespace cobbletext { 10 | 11 | class COBBLETEXT_CPP_API GlyphInfo { 12 | public: 13 | GlyphID id = 0; 14 | std::vector image; 15 | uint32_t imageWidth = 0; 16 | uint32_t imageHeight = 0; 17 | int32_t imageOffsetX = 0; 18 | int32_t imageOffsetY = 0; 19 | 20 | private: 21 | friend std::ostream & operator<<(std::ostream & stream, 22 | const GlyphInfo & glyphInfo); 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /include/cobbletext/Library.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "common.hpp" 8 | #include "macros.h" 9 | #include "FontInfo.hpp" 10 | #include "GlyphInfo.hpp" 11 | 12 | namespace cobbletext { 13 | 14 | 15 | class COBBLETEXT_CPP_API Library { 16 | static std::string version_; 17 | 18 | public: 19 | static int32_t versionMajor(); 20 | static int32_t versionMinor(); 21 | static int32_t versionPatch(); 22 | static std::string version(); 23 | 24 | friend class Engine; 25 | 26 | Library(); 27 | ~Library(); 28 | 29 | FontID fallbackFont(); 30 | 31 | FontID loadFont(const std::string path); 32 | 33 | FontID loadFontBytes(const uint8_t * bytes, size_t length, 34 | int32_t faceIndex = 0); 35 | 36 | FontInfo getFontInfo(FontID id); 37 | 38 | GlyphInfo getGlyphInfo(GlyphID id); 39 | 40 | void setFontAlternative(FontID id, FontID fallbackID); 41 | 42 | FontID getFontAlternative(FontID id); 43 | 44 | private: 45 | class Impl; 46 | std::unique_ptr impl; 47 | }; 48 | 49 | 50 | } 51 | -------------------------------------------------------------------------------- /include/cobbletext/Math.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "macros.h" 6 | 7 | namespace cobbletext { 8 | 9 | class COBBLETEXT_CPP_API Math { 10 | public: 11 | static uint32_t alpha_blend_over_argb(uint32_t background, 12 | uint32_t foreground); 13 | 14 | static uint32_t gamma_argb(uint32_t color, double gamma); 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /include/cobbletext/OutputInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.hpp" 6 | #include "macros.h" 7 | 8 | namespace cobbletext { 9 | 10 | class COBBLETEXT_CPP_API OutputInfo { 11 | public: 12 | uint32_t textWidth = 0; 13 | uint32_t textHeight = 0; 14 | 15 | private: 16 | friend std::ostream & operator<<(std::ostream & stream, 17 | const OutputInfo & outputInfo); 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /include/cobbletext/ScriptDirection.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | namespace cobbletext { 5 | 6 | 7 | enum class ScriptDirection { 8 | NotSpecified = 0, 9 | LTR = 1, 10 | RTL = 2 11 | }; 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /include/cobbletext/TextAlignment.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | namespace cobbletext { 4 | 5 | enum class TextAlignment { 6 | NotSpecified = 0, 7 | Start = 1, 8 | End = 2, 9 | Left = 3, 10 | Right = 4, 11 | Center = 5 12 | }; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /include/cobbletext/TileInfo.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.hpp" 6 | #include "macros.h" 7 | 8 | namespace cobbletext { 9 | 10 | class TileInfo { 11 | public: 12 | GlyphID glyphID = 0; 13 | uint32_t atlasX = 0; 14 | uint32_t atlasY = 0; 15 | 16 | private: 17 | friend std::ostream & operator<<(std::ostream & stream, 18 | const TileInfo & tileInfo); 19 | }; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /include/cobbletext/advance_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * Type of information contained in `#CobbletextAdvanceInfo`. 13 | * 14 | * One of: 15 | * - `#COBBLETEXT_ADVANCE_TYPE_INVALID` 16 | * - `#COBBLETEXT_ADVANCE_TYPE_GLYPH` 17 | * - `#COBBLETEXT_ADVANCE_TYPE_INLINE_OBJECT` 18 | * - `#COBBLETEXT_ADVANCE_TYPE_LINE_BREAK` 19 | * - `#COBBLETEXT_ADVANCE_TYPE_BIDI` 20 | * - `#COBBLETEXT_ADVANCE_TYPE_LAYOUT` 21 | */ 22 | typedef uint8_t CobbletextAdvanceType; 23 | 24 | /** 25 | * This value shouldn't happen. 26 | */ 27 | #define COBBLETEXT_ADVANCE_TYPE_INVALID 0 28 | 29 | /** 30 | * Draw a glyph. 31 | */ 32 | #define COBBLETEXT_ADVANCE_TYPE_GLYPH 1 33 | 34 | /** 35 | * Draw an inline object. 36 | */ 37 | #define COBBLETEXT_ADVANCE_TYPE_INLINE_OBJECT 2 38 | 39 | /** 40 | * Pen movement to position a new line. 41 | */ 42 | #define COBBLETEXT_ADVANCE_TYPE_LINE_BREAK 3 43 | 44 | /** 45 | * Pen movement to position to draw bidirectional text. 46 | */ 47 | #define COBBLETEXT_ADVANCE_TYPE_BIDI 4 48 | 49 | /** 50 | * General purpose pen movement. 51 | */ 52 | #define COBBLETEXT_ADVANCE_TYPE_LAYOUT 5 53 | 54 | /** 55 | * Representation of a pen drawing instruction. 56 | * 57 | * The origin of the X-Y coordinate system is defined to be the top-left 58 | * of the drawing area where 59 | * 60 | * - Positive X values move to the right 61 | * - Positive Y values move to the bottom 62 | * 63 | * The advance values are to be added to the current pen position after 64 | * drawing a glyph or object (if any). 65 | */ 66 | struct CobbletextAdvanceInfo { 67 | /** 68 | * The type of movement. 69 | */ 70 | CobbletextAdvanceType type; 71 | 72 | /** 73 | * Position of the text in code points. 74 | */ 75 | uint32_t text_index; 76 | 77 | /** 78 | * Horizontal movement of the pen. 79 | */ 80 | int32_t advance_x; 81 | 82 | /** 83 | * Vertical movement of the pen. 84 | */ 85 | int32_t advance_y; 86 | 87 | /** 88 | * Glyph to be drawn. 89 | * 90 | * - Only valid when `#type` is a glyph. 91 | */ 92 | CobbletextGlyphID glyph_id; 93 | 94 | /** 95 | * Horizontal offset to the current pen position when drawing a glyph. 96 | */ 97 | int32_t glyph_offset_x; 98 | 99 | /** 100 | * Vertical offset to the current pen position when drawing a glyph. 101 | */ 102 | int32_t glyph_offset_y; 103 | 104 | /** 105 | * User-provided inline object ID. 106 | * 107 | * - Only valid when `#type` is a inline object. 108 | */ 109 | CobbletextInlineObjectID inline_object; 110 | 111 | /** 112 | * User-provided custom property. 113 | * 114 | * - Default is 0. 115 | */ 116 | CobbletextCustomPropertyID custom_property; 117 | }; 118 | 119 | #ifdef __cplusplus 120 | } 121 | #endif 122 | -------------------------------------------------------------------------------- /include/cobbletext/cobbletext.h: -------------------------------------------------------------------------------- 1 | #ifndef COBBLETEXT_COBBLETEXT_H_ 2 | #define COBBLETEXT_COBBLETEXT_H_ 3 | 4 | // 5 | // Copyright 2020 Christopher Foo 6 | // License: Mozilla Public License Version 2.0 7 | // 8 | 9 | /** 10 | * @mainpage Cobbletext: complex text layout and rendering engine library. 11 | * 12 | * This is the reference API documentation for Cobbletext.
13 | * 14 | * For the C API, see `cobbletext/cobbletext.h`.
15 | * 16 | * For the C++ api, see `cobbletext/cobbletext.hpp`.
17 | * 18 | * Consult the project README file for details. 19 | */ 20 | 21 | /** 22 | * @file cobbletext.h 23 | * 24 | * Cobbletext C API 25 | * 26 | * Start at `cobbletext_library_new()`, then `cobbletext_engine_new()`. 27 | * 28 | * Whenever a function is described as "can error", check the value of 29 | * `cobbletext_get_error_code()` before continuing. If there is an error, 30 | * the returned value or the internal state may be invalid. 31 | * 32 | * Pointer parameters are copied by the function unless noted. Pointer 33 | * return types should not be freed and become invalid on the next function 34 | * call unless noted. 35 | * 36 | * Whenever a pointer is described "free only using library", use 37 | * Cobbletext's corresponding delete function for that handle. 38 | * Do not call `free()` on it. 39 | */ 40 | 41 | 42 | #ifdef __cplusplus 43 | extern "C" { 44 | #endif 45 | 46 | #include "advance_info.h" 47 | #include "common.h" 48 | #include "encoding.h" 49 | #include "engine_properties.h" 50 | #include "engine.h" 51 | #include "font_info.h" 52 | #include "glyph_info.h" 53 | #include "library.h" 54 | #include "macros.h" 55 | #include "math_.h" 56 | #include "output_info.h" 57 | #include "script_direction.h" 58 | #include "text_properties.h" 59 | #include "tile_info.h" 60 | #include "uchar_helper.h" 61 | 62 | #ifdef COBBLETEXT_TYPEDEF_STRUCTS 63 | typedef struct CobbletextFontInfo CobbletextFontInfo; 64 | typedef struct CobbletextGlyphInfo CobbletextGlyphInfo; 65 | typedef struct CobbletextTextProperties CobbletextTextProperties; 66 | typedef struct CobbletextEngineProperties CobbletextEngineProperties; 67 | typedef struct CobbletextTileInfo CobbletextTileInfo; 68 | typedef struct CobbletextAdvanceInfo CobbletextAdvanceInfo; 69 | typedef struct CobbletextOutputInfo CobbletextOutputInfo; 70 | #endif 71 | 72 | #ifdef __cplusplus 73 | } 74 | #endif 75 | 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /include/cobbletext/cobbletext.hpp: -------------------------------------------------------------------------------- 1 | #ifndef COBBLETEXT_COBBLETEXT_HPP_ 2 | #define COBBLETEXT_COBBLETEXT_HPP_ 3 | 4 | /** 5 | * @file cobbletext.hpp 6 | * 7 | * Cobbletext C++ API 8 | * 9 | * Start at `cobbletext::Library()`, then `cobbletext::Engine()`. 10 | * 11 | * The C API wraps this C++ API. 12 | * 13 | */ 14 | 15 | #include "AdvanceInfo.hpp" 16 | #include "common.hpp" 17 | #include "Encoding.hpp" 18 | #include "Engine.hpp" 19 | #include "Exception.hpp" 20 | #include "FontInfo.hpp" 21 | #include "GlyphInfo.hpp" 22 | #include "Library.hpp" 23 | #include "Math.hpp" 24 | #include "OutputInfo.hpp" 25 | #include "ScriptDirection.hpp" 26 | #include "TileInfo.hpp" 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /include/cobbletext/common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "macros.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * Unique font face ID. 13 | * 14 | * A plain integer that can be used to identify a font face. 15 | * 16 | * It will always be unique per library context. 17 | */ 18 | typedef uint32_t CobbletextFontID; 19 | 20 | /** 21 | * Unique glyph ID. 22 | * 23 | * A plain integer that can be used to identify a glyph for a font face, 24 | * at a specific font size. 25 | * 26 | * It will always be unique per library context. 27 | * 28 | * IDs are recycled to different glyphs if no engine holds references to the 29 | * ID. 30 | */ 31 | typedef uint32_t CobbletextGlyphID; 32 | 33 | /** 34 | * A user-provided identifer for inline objects. 35 | * 36 | * The user decides how to interpret this identifer for custom objects. 37 | */ 38 | typedef uint32_t CobbletextInlineObjectID; 39 | 40 | /** 41 | * A user-provided integer for custom properties. 42 | * 43 | * The user decides on how to interpret this integer for implementing custom 44 | * properties to runs of text. 45 | * 46 | * The value could be a ID to a mapping or a direct color value. 47 | */ 48 | typedef uint64_t CobbletextCustomPropertyID; 49 | 50 | 51 | /** 52 | * Returns X from the X.Y.Z version. 53 | */ 54 | COBBLETEXT_API 55 | int32_t cobbletext_get_version_major(); 56 | 57 | /** 58 | * Returns Y from the X.Y.Z version. 59 | */ 60 | COBBLETEXT_API 61 | int32_t cobbletext_get_version_minor(); 62 | 63 | /** 64 | * Returns Z from the X.Y.Z version. 65 | */ 66 | COBBLETEXT_API 67 | int32_t cobbletext_get_version_patch(); 68 | 69 | /** 70 | * Returns the version string formatted as X.Y.Z 71 | * 72 | * - Do not free. 73 | */ 74 | COBBLETEXT_API 75 | const char * cobbletext_get_version(); 76 | 77 | #ifdef __cplusplus 78 | } 79 | #endif 80 | -------------------------------------------------------------------------------- /include/cobbletext/common.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | typedef uint32_t FontID; 8 | typedef uint32_t GlyphID; 9 | typedef uint32_t InlineObjectID; 10 | typedef uint32_t CustomPropertyID; 11 | 12 | } 13 | -------------------------------------------------------------------------------- /include/cobbletext/encoding.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Specifies the encoding of bytes. 11 | * 12 | * One of: 13 | * 14 | * - `#COBBLETEXT_ENCODING_UTF8` 15 | * - `#COBBLETEXT_ENCODING_UTF16LE` 16 | */ 17 | typedef uint8_t CobbletextEncoding; 18 | 19 | /** 20 | * UTF-8 encoding 21 | */ 22 | #define COBBLETEXT_ENCODING_UTF8 1 23 | 24 | /** 25 | * UTF-16 little endian encoding 26 | */ 27 | #define COBBLETEXT_ENCODING_UTF16LE 2 28 | 29 | #ifdef __cplusplus 30 | } 31 | #endif 32 | -------------------------------------------------------------------------------- /include/cobbletext/engine_properties.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "text_alignment.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * Properties for controlling output parameters on a 13 | * `struct CobbletextEngine` instance. 14 | */ 15 | struct CobbletextEngineProperties { 16 | /** 17 | * Line length in pixels. 18 | * 19 | * If the value is 0, no word wrapping is performed. Otherwise, lines will 20 | * be word wrapped at the given pixel width. 21 | */ 22 | uint32_t line_length; 23 | 24 | /** 25 | * Locale as a BCP 47 language tag. 26 | * 27 | * This value is used for assisting lower-level functions to tailor the 28 | * presentation of the text to your application's user. It is typically 29 | * the GUI's locale or a document's language. 30 | * 31 | * The default is an empty string which indicates automatic detection of 32 | * the user's locale if possible. 33 | * 34 | * A tag is 2 characters or longer. 35 | * 36 | * - Getter: Never null. 37 | * - Setter: May be null. (Null interpreted as empty string). 38 | */ 39 | const char * locale; 40 | 41 | /** 42 | * Controls the alignment of text in each line. 43 | * 44 | * Default is not specified. 45 | */ 46 | CobbletextTextAlignment text_alignment; 47 | }; 48 | 49 | #ifdef __cplusplus 50 | } 51 | #endif 52 | -------------------------------------------------------------------------------- /include/cobbletext/font_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Font face properties. 11 | */ 12 | struct CobbletextFontInfo { 13 | /** 14 | * ID for this face. 15 | */ 16 | CobbletextFontID id; 17 | 18 | /** 19 | * Font family name. 20 | * 21 | * - Never null pointer. 22 | */ 23 | const char * family_name; 24 | 25 | /** 26 | * Font style name. 27 | * 28 | * - Never null pointer. 29 | */ 30 | const char * style_name; 31 | 32 | /** 33 | * Number of font units per EM square. 34 | * 35 | * If value is 0, the font is a bitmap font and not a vector font. 36 | * Additionally, properties, such as ascender or height, 37 | * related to this EM size is not valid. 38 | */ 39 | uint16_t units_per_em; 40 | 41 | /** 42 | * Ascender in font units. 43 | * 44 | * Positive is above baseline. 45 | */ 46 | int16_t ascender; 47 | 48 | /** 49 | * Descencder in font units. 50 | * 51 | * Negative is below baseline. 52 | */ 53 | int16_t descender; 54 | 55 | /** 56 | * Distance between two baselines in font units. 57 | * 58 | * If 0, it is not a vector, but a bitmap font. 59 | */ 60 | int16_t height; 61 | 62 | /** 63 | * Position of underline in font units. 64 | */ 65 | int16_t underline_position; 66 | 67 | /** 68 | * Thickness of underline in font units. 69 | */ 70 | int16_t underline_thickness; 71 | }; 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | -------------------------------------------------------------------------------- /include/cobbletext/glyph_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Glyph properties 11 | */ 12 | struct CobbletextGlyphInfo { 13 | /** 14 | * ID for this glyph. 15 | */ 16 | CobbletextGlyphID id; 17 | 18 | /** 19 | * Coverage map of the glyph. 20 | * 21 | * 0 is transparent, 255 is opaque. 22 | * 23 | * The value is null if the glyph has not yet been rasterized. 24 | * 25 | * - May be null 26 | */ 27 | const uint8_t * image; 28 | 29 | /** 30 | * Width of the coverage map. 31 | * 32 | * - Value is 0 if no `#image` 33 | */ 34 | uint32_t image_width; 35 | 36 | /** 37 | * Height of the coverage map. 38 | * 39 | * - Value is 0 if no `#image` 40 | */ 41 | uint32_t image_height; 42 | 43 | /** 44 | * Horizontal offset when drawing the image. 45 | * 46 | * Additive to your pen position. 47 | */ 48 | int32_t image_offset_x; 49 | 50 | /** 51 | * Vertical offset when drawing the image. 52 | * 53 | * Additive to your pen position. 54 | */ 55 | int32_t image_offset_y; 56 | }; 57 | 58 | #ifdef __cplusplus 59 | } 60 | #endif 61 | -------------------------------------------------------------------------------- /include/cobbletext/library.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "macros.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Opaque handle to the library's context. 11 | * 12 | * Use `cobbletext_library_new()` to create one. 13 | * 14 | * Once you obtain a library context, you can use it to open a layout and 15 | * render engine with `cobbletext_engine_new()`. 16 | * 17 | * Cobbletext and its dependencies is not thread safe. A library context 18 | * can be created for each thread. Otherwise, a simple application will only 19 | * need to use a single library context. 20 | * 21 | * Cached glyphs are automatically freed from memory if no engine references 22 | * them. Fonts cannot be closed at this time. 23 | */ 24 | typedef struct CobbletextLibrary CobbletextLibrary; 25 | 26 | 27 | /** 28 | * Returns a new library context. 29 | * 30 | * - Can error. 31 | * - Free only using library. 32 | * - Null on handle allocation error. Otherwise, not null. 33 | */ 34 | COBBLETEXT_API 35 | CobbletextLibrary * cobbletext_library_new(); 36 | 37 | /** 38 | * Frees a library context. 39 | */ 40 | COBBLETEXT_API 41 | void cobbletext_library_delete(CobbletextLibrary * library); 42 | 43 | /** 44 | * Returns error code. 45 | * 46 | * The return code is usually passed through from the libraries used 47 | * by Cobbletext. 48 | * 49 | * @return 0 if there is no error, a non-zero value otherwise. 50 | */ 51 | COBBLETEXT_API 52 | int32_t cobbletext_get_error_code(CobbletextLibrary * library); 53 | 54 | /** 55 | * Returns a textual version of the error code. 56 | * 57 | * - Value is never null. 58 | * - Do not free. 59 | */ 60 | COBBLETEXT_API 61 | const char * cobbletext_get_error_message(CobbletextLibrary * library); 62 | 63 | /** 64 | * Clears any error code or error message. 65 | */ 66 | COBBLETEXT_API 67 | void cobbletext_clear_error(CobbletextLibrary * library); 68 | 69 | /** 70 | * Returns the ID for the built-in fallback font when there is no font loaded. 71 | */ 72 | COBBLETEXT_API 73 | CobbletextFontID cobbletext_library_get_fallback_font( 74 | CobbletextLibrary * library); 75 | 76 | /** 77 | * Loads a font face from a file. 78 | * 79 | * @param path Filename of the font file. If the file has multiple faces, 80 | * use the fragment notation by adding `#` and the face index such as 81 | * `myfont.ttc#0`. 82 | * 83 | * - Can error. 84 | */ 85 | COBBLETEXT_API 86 | CobbletextFontID cobbletext_library_load_font(CobbletextLibrary * library, 87 | const char * path); 88 | 89 | /** 90 | * Loads a font face from the given bytes of a font file. 91 | * 92 | * @param data Binary contents of a font file. Tbe value is copied. 93 | * @param length Size in bytes. 94 | * @param face_index Face index if the file has multiple faces. Use 0 if this 95 | * parameter is irrelevant. 96 | * 97 | * - Can error. 98 | */ 99 | COBBLETEXT_API 100 | CobbletextFontID cobbletext_library_load_font_bytes( 101 | CobbletextLibrary * library, const uint8_t * data, uint32_t length, 102 | int32_t face_index); 103 | 104 | /** 105 | * Sets an alternative font to be used if glyphs are not in the font. 106 | * 107 | * By default, the engine does not try another font if a font does not 108 | * have the required glyphs. By specifying an alternative font, the engine 109 | * will try to use another font. This is also known as font fallback. 110 | * 111 | * This function can be used to chain multiple fonts so the engine can try 112 | * them in order. You can also add the built-in fallback font at the of 113 | * the chain to guarantee something will be drawn. 114 | * 115 | * @param font_id The font in interest. 116 | * @param fallback_font_id The alternative font to select when the font in 117 | * interest does not have the required glyphs. 118 | */ 119 | COBBLETEXT_API 120 | void cobbletext_library_set_font_alternative(CobbletextLibrary * library, 121 | CobbletextFontID font_id, CobbletextFontID fallback_font_id); 122 | 123 | /** 124 | * Returns the alternative font for the given font. 125 | * 126 | * @return A font ID if there is an alternative set, otherwise 0. 127 | */ 128 | COBBLETEXT_API 129 | CobbletextFontID cobbletext_library_get_font_alternative( 130 | CobbletextLibrary * library, CobbletextFontID font_id); 131 | 132 | /** 133 | * Returns information about a loaded font. 134 | * 135 | * - Can error. 136 | * - Do not free. 137 | * - Valid until the next function call. 138 | */ 139 | COBBLETEXT_API 140 | const struct CobbletextFontInfo * cobbletext_library_get_font_info( 141 | CobbletextLibrary * library, CobbletextFontID font); 142 | 143 | /** 144 | * Returns information about a glyph from a loaded font. 145 | * 146 | * - Can error. 147 | * - Do not free. 148 | * - Valid until the next function call. 149 | */ 150 | COBBLETEXT_API 151 | const struct CobbletextGlyphInfo * cobbletext_library_get_glyph_info( 152 | CobbletextLibrary * library, CobbletextFontID glyph); 153 | 154 | #ifdef __cplusplus 155 | } 156 | #endif 157 | -------------------------------------------------------------------------------- /include/cobbletext/macros.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * Macro for Windows C symbol import and export, Emscripten attribute 5 | * 6 | * @private 7 | */ 8 | #if defined(_WIN32) && !defined(COBBLETEXT_STATIC) 9 | #ifdef cobbletext_EXPORTS 10 | #define COBBLETEXT_API __declspec(dllexport) 11 | #else 12 | #define COBBLETEXT_API __declspec(dllimport) 13 | #endif 14 | #elif defined(__EMSCRIPTEN__) && defined(COBBLETEXT_ENABLE_EM_API) 15 | #include 16 | #define COBBLETEXT_API EMSCRIPTEN_KEEPALIVE 17 | #else 18 | #define COBBLETEXT_API 19 | #endif 20 | 21 | /** 22 | * Macro for Windows C++ symbol import and export 23 | * 24 | * @private 25 | */ 26 | #if defined(_WIN32) && defined(COBBLETEXT_ENABLE_CPP_API) && !defined(COBBLETEXT_STATIC) 27 | #ifdef cobbletext_EXPORTS 28 | #define COBBLETEXT_CPP_API __declspec(dllexport) 29 | #else 30 | #define COBBLETEXT_CPP_API __declspec(dllimport) 31 | #endif 32 | #else 33 | #define COBBLETEXT_CPP_API 34 | #endif 35 | -------------------------------------------------------------------------------- /include/cobbletext/math_.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "macros.h" 6 | 7 | #ifdef __cplusplus 8 | extern "C" { 9 | #endif 10 | 11 | /** 12 | * Composite two ARGB color values using alpha blending and the 13 | * "over" operator. 14 | */ 15 | 16 | COBBLETEXT_API 17 | uint32_t cobbletext_math_alpha_blend_over_argb(uint32_t background, 18 | uint32_t foreground); 19 | 20 | /** 21 | * Apply gamma function to the ARGB color value. 22 | * 23 | * To apply gamma correction function, use `1 / gamma`. 24 | */ 25 | COBBLETEXT_API 26 | uint32_t cobbletext_math_gamma_argb(uint32_t color, double gamma); 27 | 28 | #ifdef __cplusplus 29 | } 30 | #endif 31 | -------------------------------------------------------------------------------- /include/cobbletext/output_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Sizes for the rendered text. 11 | * 12 | * There are two types of bounding boxes: 13 | * 14 | * - Text bounding box 15 | * - Raster bounding box 16 | * 17 | * The text bounding box (or text box) only includes sizes computed by pen 18 | * movements. 19 | * 20 | * The raster bounding box (or image size) includes what is actually drawn 21 | * to the image. TODO 22 | */ 23 | struct CobbletextOutputInfo { 24 | /** 25 | * Width of the text box in pixels. 26 | */ 27 | uint32_t text_width; 28 | 29 | /** 30 | * Height of the text box in pixels. 31 | */ 32 | uint32_t text_height; 33 | }; 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | -------------------------------------------------------------------------------- /include/cobbletext/script_direction.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef __cplusplus 4 | extern "C" { 5 | #endif 6 | 7 | /** 8 | * Script direction of a run of text. 9 | * 10 | * One of: 11 | * 12 | * - `#COBBLETEXT_SCRIPT_DIRECTION_NOT_SPECIFIED` 13 | * - `#COBBLETEXT_SCRIPT_DIRECTION_LTR` 14 | * - `#COBBLETEXT_SCRIPT_DIRECTION_RTL` 15 | */ 16 | typedef uint8_t CobbletextScriptDirection; 17 | 18 | /** 19 | * Automatically detect script direction. 20 | */ 21 | #define COBBLETEXT_SCRIPT_DIRECTION_NOT_SPECIFIED 0 22 | 23 | /** 24 | * Force script to be left-to-right. 25 | */ 26 | #define COBBLETEXT_SCRIPT_DIRECTION_LTR 1 27 | 28 | /** 29 | * Force script to be left-to-right. 30 | */ 31 | #define COBBLETEXT_SCRIPT_DIRECTION_RTL 2 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /include/cobbletext/text_alignment.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Alignment of text within a line. 11 | * 12 | * One of: 13 | * 14 | * - `#COBBLETEXT_TEXT_ALIGNMENT_NOT_SPECIFIED` 15 | * - `#COBBLETEXT_TEXT_ALIGNMENT_START` 16 | * - `#COBBLETEXT_TEXT_ALIGNMENT_END` 17 | * - `#COBBLETEXT_TEXT_ALIGNMENT_LEFT` 18 | * - `#COBBLETEXT_TEXT_ALIGNMENT_RIGHT` 19 | * - `#COBBLETEXT_TEXT_ALIGNMENT_CENTER` 20 | */ 21 | typedef uint8_t CobbletextTextAlignment; 22 | 23 | /** 24 | * Default alias for `#COBBLETEXT_TEXT_ALIGNMENT_START` 25 | */ 26 | #define COBBLETEXT_TEXT_ALIGNMENT_NOT_SPECIFIED 0 27 | 28 | /** 29 | * Left-align text if LTR and right-align text if RTL. 30 | */ 31 | #define COBBLETEXT_TEXT_ALIGNMENT_START 1 32 | 33 | /** 34 | * Right-align text if LTR and left-align text if RTL. 35 | */ 36 | #define COBBLETEXT_TEXT_ALIGNMENT_END 2 37 | 38 | /** 39 | * Force lines to be left-aligned. 40 | */ 41 | #define COBBLETEXT_TEXT_ALIGNMENT_LEFT 3 42 | 43 | /** 44 | * Force lines to be right-aligned. 45 | */ 46 | #define COBBLETEXT_TEXT_ALIGNMENT_RIGHT 4 47 | 48 | /** 49 | * Centers each line. 50 | */ 51 | #define COBBLETEXT_TEXT_ALIGNMENT_CENTER 5 52 | 53 | #ifdef __cplusplus 54 | } 55 | #endif 56 | -------------------------------------------------------------------------------- /include/cobbletext/text_properties.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | #include "script_direction.h" 5 | 6 | #ifdef __cplusplus 7 | extern "C" { 8 | #endif 9 | 10 | /** 11 | * Properties for text runs on a `struct CobbletextEngine` instance. 12 | */ 13 | struct CobbletextTextProperties { 14 | /** 15 | * The current language as a BCP 47 language tag. 16 | * 17 | * This low-level value typically controls the glyph variant selection to 18 | * use from a font file. 19 | * 20 | * Default value is an empty string which indicates automatic detection. 21 | * 22 | * A tag is 2 characters or longer. 23 | * 24 | * - Getter: Never null. 25 | * - Setter: May be null. (Null interpreted as empty string). 26 | */ 27 | const char * language; 28 | 29 | /** 30 | * The current script as a ISO 15924 string. 31 | * 32 | * This low-level value typically controls the rules for shaping glyphs. 33 | * 34 | * A ISO 15924 string is a 4 character string such as "Latn". 35 | * 36 | * Default value is an empty string which indicates automatic detection. 37 | * 38 | * - Getter: Never null. 39 | * - Setter: May be null. (Null interpreted as empty string). 40 | */ 41 | const char * script; 42 | 43 | /** 44 | * The current script direction. 45 | * 46 | * Default value "not specified" indicates automatic detection. 47 | */ 48 | CobbletextScriptDirection script_direction; 49 | 50 | /** 51 | * The current font face. 52 | * 53 | * Default is 0 (not corresponding to a valid font face). 54 | */ 55 | CobbletextFontID font; 56 | 57 | /** 58 | * The current font size in points. 59 | * 60 | * Default is 12. 61 | * 62 | * If the font file contains bitmaps and not vectors, the value 63 | * represented is in pixels. 64 | */ 65 | double font_size; 66 | 67 | /** 68 | * The current user-provided custom property. 69 | * 70 | * Default value is 0. 71 | */ 72 | CobbletextCustomPropertyID custom_property; 73 | }; 74 | 75 | #ifdef __cplusplus 76 | } 77 | #endif 78 | -------------------------------------------------------------------------------- /include/cobbletext/tile_info.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "common.h" 4 | 5 | #ifdef __cplusplus 6 | extern "C" { 7 | #endif 8 | 9 | /** 10 | * Representation of a rendered glyph for an `#CobbletextEngine`. 11 | */ 12 | struct CobbletextTileInfo { 13 | /** 14 | * The glyph ID represented. 15 | */ 16 | CobbletextGlyphID glyph_id; 17 | 18 | /** 19 | * Horizontal offset within a texture atlas. 20 | * 21 | * - Value is 0 if tile packing is not performed. 22 | */ 23 | uint32_t atlas_x; 24 | 25 | /** 26 | * Vertical offset within a texture atlas. 27 | * 28 | * - Value is 0 if tile packing is not performed. 29 | */ 30 | uint32_t atlas_y; 31 | }; 32 | 33 | #ifdef __cplusplus 34 | } 35 | #endif 36 | -------------------------------------------------------------------------------- /include/cobbletext/uchar_helper.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #ifndef COBBLETEXT_NO_UCHAR_H 5 | #include 6 | typedef char16_t CobbletextChar16; 7 | typedef char32_t CobbletextChar32; 8 | #else 9 | #include 10 | typedef uint_least16_t CobbletextChar16; 11 | typedef uint_least32_t CobbletextChar32; 12 | #endif 13 | -------------------------------------------------------------------------------- /misc/icu/icu_data_filter.json: -------------------------------------------------------------------------------- 1 | { 2 | "strategy": "additive", 3 | "featureFilters": { 4 | "brkitr_rules": "include", 5 | "brkitr_dictionaries": "include", 6 | "brkitr_tree": "include", 7 | "conversion_mappings": "exclude", 8 | "coll_ucadata": "exclude", 9 | "coll_tree": "exclude", 10 | "confusables": "exclude", 11 | "misc": "include", 12 | "curr_supplemental": "exclude", 13 | "curr_tree": "exclude", 14 | "lang_tree": "exclude", 15 | "normalization": "exclude", 16 | "region_tree": "exclude", 17 | "rbnf_tree": "exclude", 18 | "stringprep": "exclude", 19 | "zone_tree": "exclude", 20 | "zone_supplemental": "exclude", 21 | "translit": "exclude", 22 | "unames": "exclude", 23 | "ulayout": "include", 24 | "unit_tree": "exclude", 25 | "cnvalias": "include", 26 | "locales_tree": "exclude" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/icu-cobbletext/CONTROL: -------------------------------------------------------------------------------- 1 | Source: icu-cobbletext 2 | Version: 65.1-4 3 | Homepage: http://icu-project.org/apiref/icu4c/ 4 | Description: Mature and widely used Unicode and localization library. 5 | Supports: !(arm|uwp) 6 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/icu-cobbletext/disable-escapestr-tool.patch: -------------------------------------------------------------------------------- 1 | diff --git a/source/tools/Makefile.in b/source/tools/Makefile.in 2 | index c3f81d6..dc41af3 100644 3 | --- a/source/tools/Makefile.in 4 | +++ b/source/tools/Makefile.in 5 | @@ -19,9 +19,9 @@ SUBDIRS = toolutil ctestfw makeconv genrb genbrk \ 6 | gencnval gensprep icuinfo genccode gencmn icupkg pkgdata \ 7 | gentest gennorm2 gencfu gendict 8 | 9 | -ifneq (@platform_make_fragment_name@,mh-cygwin-msvc) 10 | -SUBDIRS += escapesrc 11 | -endif 12 | +#ifneq (@platform_make_fragment_name@,mh-cygwin-msvc) 13 | +#SUBDIRS += escapesrc 14 | +#endif 15 | 16 | ## List of phony targets 17 | .PHONY : all all-local all-recursive install install-local \ 18 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/icu-cobbletext/fix-extra.patch: -------------------------------------------------------------------------------- 1 | diff --urN a/source/extra/Makefile.in b/source/extra/Makefile.in 2 | --- a/source/extra/Makefile.in 3 | +++ b/source/extra/Makefile.in 4 | @@ -23,7 +23,7 @@ 5 | ## Files to remove for 'make clean' 6 | CLEANFILES = *~ 7 | 8 | -SUBDIRS = scrptrun uconv 9 | +SUBDIRS = uconv 10 | 11 | ## List of phony targets 12 | .PHONY : all all-local all-recursive install install-local \ 13 | 14 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/icu-cobbletext/fix_parallel_build_on_windows.patch: -------------------------------------------------------------------------------- 1 | diff --urN a/source/data/Makefile.in b/source/data/Makefile.in 2 | --- a/source/data/Makefile.in 3 | +++ b/source/data/Makefile.in 4 | @@ -221,11 +221,12 @@ 5 | ## Include the main build rules for data files 6 | include $(top_builddir)/$(subdir)/rules.mk 7 | 8 | +PKGDATA_LIST = $(TMP_DIR)/icudata.lst 9 | 10 | ifeq ($(ENABLE_SO_VERSION_DATA),1) 11 | ifeq ($(PKGDATA_MODE),dll) 12 | SO_VERSION_DATA = $(OUTTMPDIR)/icudata.res 13 | -$(SO_VERSION_DATA) : $(MISCSRCDIR)/icudata.rc 14 | +$(SO_VERSION_DATA) : $(MISCSRCDIR)/icudata.rc $(PKGDATA_LIST) 15 | ifeq ($(MSYS_RC_MODE),1) 16 | rc.exe -i$(srcdir)/../common -i$(top_builddir)/common -fo$@ $(CPPFLAGS) $< 17 | else 18 | @@ -234,7 +235,6 @@ 19 | endif 20 | endif 21 | 22 | -PKGDATA_LIST = $(TMP_DIR)/icudata.lst 23 | 24 | 25 | ##################################################### 26 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/icu-cobbletext/remove-MD-from-configure.patch: -------------------------------------------------------------------------------- 1 | diff -urN a/source/runConfigureICU b/source/runConfigureICU 2 | --- a/source/runConfigureICU 2018-03-26 21:38:44.000000000 +0800 3 | +++ b/source/runConfigureICU 2018-08-26 09:04:53.197454400 +0800 4 | @@ -322,10 +322,10 @@ 5 | THE_COMP="Microsoft Visual C++" 6 | CC=cl; export CC 7 | CXX=cl; export CXX 8 | - RELEASE_CFLAGS='-Gy -MD' 9 | - RELEASE_CXXFLAGS='-Gy -MD' 10 | - DEBUG_CFLAGS='-FS -Zi -MDd' 11 | - DEBUG_CXXFLAGS='-FS -Zi -MDd' 12 | + RELEASE_CFLAGS='-Gy' 13 | + RELEASE_CXXFLAGS='-Gy' 14 | + DEBUG_CFLAGS='-FS -Zi' 15 | + DEBUG_CXXFLAGS='-FS -Zi' 16 | DEBUG_LDFLAGS='-DEBUG' 17 | ;; 18 | *BSD) 19 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_ports/notes.txt: -------------------------------------------------------------------------------- 1 | vcpkg source git commits 2 | 3 | icu 89d112b839a61f81fb5f341c908d6412e3855228 4 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/arm64-osx-dynamic-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE arm64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | set(VCPKG_BUILD_TYPE release) 5 | 6 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 7 | 8 | set(VCPKG_OSX_ARCHITECTURES arm64) 9 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/arm64-osx-dynamic.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE arm64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 6 | 7 | set(VCPKG_OSX_ARCHITECTURES arm64) 8 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x64-linux-dynamic-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | set(VCPKG_BUILD_TYPE release) 5 | 6 | set(VCPKG_CMAKE_SYSTEM_NAME Linux) 7 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x64-linux-dynamic.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Linux) 6 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x64-osx-dynamic-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | set(VCPKG_BUILD_TYPE release) 5 | 6 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 7 | 8 | set(VCPKG_OSX_ARCHITECTURES x86_64) 9 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x64-osx-dynamic.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | 5 | set(VCPKG_CMAKE_SYSTEM_NAME Darwin) 6 | 7 | set(VCPKG_OSX_ARCHITECTURES x86_64) 8 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x64-windows-dynamic-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x64) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | set(VCPKG_BUILD_TYPE release) 5 | -------------------------------------------------------------------------------- /misc/vcpkg_custom_triplets/x86-windows-dynamic-release.cmake: -------------------------------------------------------------------------------- 1 | set(VCPKG_TARGET_ARCHITECTURE x86) 2 | set(VCPKG_CRT_LINKAGE dynamic) 3 | set(VCPKG_LIBRARY_LINKAGE dynamic) 4 | set(VCPKG_BUILD_TYPE release) 5 | -------------------------------------------------------------------------------- /script/build_icu_emscripten_prefixed.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Builds WASM version of ICU using an unmerged pull request 3 | # The build only works with Emscripten SDK version 1.38.48 4 | 5 | set -e 6 | set -x 7 | 8 | SCRIPT_PATH=$(readlink -f "$BASH_SOURCE") 9 | SCRIPT_DIR=$(dirname "$SCRIPT_PATH") 10 | 11 | mkdir -p icu_emscripten_prefixed/ 12 | cd icu_emscripten_prefixed/ 13 | 14 | if [ ! -d "icu" ]; then 15 | git clone https://github.com/mabels/icu/ --depth 10 --branch wasm32 16 | fi 17 | mkdir -p icu_build icu_installed 18 | cd icu_build 19 | ICU_DATA_FILTER_FILE=$SCRIPT_DIR/../misc/icu/icu_data_filter.json \ 20 | emconfigure ../icu/icu4c/source/runConfigureICU wasm32 \ 21 | --with-library-suffix=cobbletext \ 22 | --prefix=`pwd`/../icu_installed 23 | cp ../icu/icu4c/source/tools/toolutil/nodejs-system.js tools/toolutil/ 24 | emmake make 25 | emmake make install 26 | 27 | echo "Done" 28 | -------------------------------------------------------------------------------- /script/embed_resource.py: -------------------------------------------------------------------------------- 1 | #!/bin/env python3 2 | 3 | import argparse 4 | import sys 5 | import textwrap 6 | 7 | def main(): 8 | arg_parser = argparse.ArgumentParser() 9 | arg_parser.add_argument("name") 10 | arg_parser.add_argument("input", type=argparse.FileType("rb")) 11 | arg_parser.add_argument("output", type=argparse.FileType("wt")) 12 | 13 | args = arg_parser.parse_args() 14 | 15 | file = args.output 16 | file.write(textwrap.dedent("""\ 17 | // This file was automatically generated by embed_resource.py 18 | // Do not edit! 19 | 20 | #include "Resource.hpp" 21 | 22 | namespace cobbletext { 23 | 24 | """)) 25 | 26 | file.write("const uint8_t Resource::{name}[] {{\n".format(name=args.name)) 27 | 28 | while True: 29 | data = args.input.read(20) 30 | 31 | if len(data) == 0: 32 | break 33 | 34 | file.write(" ") 35 | 36 | for byte in data: 37 | file.write(str(byte)) 38 | file.write(", ") 39 | 40 | file.write("\n") 41 | 42 | file.write("};\n") 43 | 44 | file.write("const size_t Resource::{name}_SIZE = {size};\n\n".format( 45 | name=args.name, size=args.input.tell())) 46 | 47 | file.write(textwrap.dedent("""\ 48 | 49 | } 50 | """)) 51 | 52 | if __name__ == "__main__": 53 | main() 54 | -------------------------------------------------------------------------------- /script/get_precompiled_icu_tartanllama.cmake: -------------------------------------------------------------------------------- 1 | include(FetchContent) 2 | FetchContent_Declare( 3 | icu-emscripten 4 | GIT_REPOSITORY https://github.com/TartanLlama/icu-emscripten.git 5 | GIT_TAG master 6 | ) 7 | # FetchContent_MakeAvailable(icu-emscripten) 8 | 9 | # Backwards compatible MakeAvailable 10 | FetchContent_GetProperties(icu-emscripten) 11 | if(NOT icu-emscripten_POPULATED) 12 | FetchContent_Populate(icu-emscripten) 13 | # add_subdirectory(${icu-emscripten_SOURCE_DIR} ${icu-emscripten_BINARY_DIR}) 14 | endif() 15 | 16 | # Make sure CMake doesn't try to pull in system installations of ICU 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY NEVER) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE NEVER) 19 | set(ICU_ROOT ${icu-emscripten_SOURCE_DIR}) 20 | -------------------------------------------------------------------------------- /script/package_overlay/notice/adobe_notdef.txt: -------------------------------------------------------------------------------- 1 | Copyright 2016-2019 Adobe (http://www.adobe.com/). 2 | 3 | This Font Software is licensed under the SIL Open Font License, 4 | Version 1.1. 5 | 6 | This license is copied below, and is also available with a FAQ at: 7 | http://scripts.sil.org/OFL 8 | 9 | ----------------------------------------------------------- 10 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 11 | ----------------------------------------------------------- 12 | 13 | PREAMBLE 14 | The goals of the Open Font License (OFL) are to stimulate worldwide 15 | development of collaborative font projects, to support the font 16 | creation efforts of academic and linguistic communities, and to 17 | provide a free and open framework in which fonts may be shared and 18 | improved in partnership with others. 19 | 20 | The OFL allows the licensed fonts to be used, studied, modified and 21 | redistributed freely as long as they are not sold by themselves. The 22 | fonts, including any derivative works, can be bundled, embedded, 23 | redistributed and/or sold with any software provided that any reserved 24 | names are not used by derivative works. The fonts and derivatives, 25 | however, cannot be released under any other type of license. The 26 | requirement for fonts to remain under this license does not apply to 27 | any document created using the fonts or their derivatives. 28 | 29 | DEFINITIONS 30 | "Font Software" refers to the set of files released by the Copyright 31 | Holder(s) under this license and clearly marked as such. This may 32 | include source files, build scripts and documentation. 33 | 34 | "Reserved Font Name" refers to any names specified as such after the 35 | copyright statement(s). 36 | 37 | "Original Version" refers to the collection of Font Software 38 | components as distributed by the Copyright Holder(s). 39 | 40 | "Modified Version" refers to any derivative made by adding to, 41 | deleting, or substituting -- in part or in whole -- any of the 42 | components of the Original Version, by changing formats or by porting 43 | the Font Software to a new environment. 44 | 45 | "Author" refers to any designer, engineer, programmer, technical 46 | writer or other person who contributed to the Font Software. 47 | 48 | PERMISSION & CONDITIONS 49 | Permission is hereby granted, free of charge, to any person obtaining 50 | a copy of the Font Software, to use, study, copy, merge, embed, 51 | modify, redistribute, and sell modified and unmodified copies of the 52 | Font Software, subject to the following conditions: 53 | 54 | 1) Neither the Font Software nor any of its individual components, in 55 | Original or Modified Versions, may be sold by itself. 56 | 57 | 2) Original or Modified Versions of the Font Software may be bundled, 58 | redistributed and/or sold with any software, provided that each copy 59 | contains the above copyright notice and this license. These can be 60 | included either as stand-alone text files, human-readable headers or 61 | in the appropriate machine-readable metadata fields within text or 62 | binary files as long as those fields can be easily viewed by the user. 63 | 64 | 3) No Modified Version of the Font Software may use the Reserved Font 65 | Name(s) unless explicit written permission is granted by the 66 | corresponding Copyright Holder. This restriction only applies to the 67 | primary font name as presented to the users. 68 | 69 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 70 | Software shall not be used to promote, endorse or advertise any 71 | Modified Version, except to acknowledge the contribution(s) of the 72 | Copyright Holder(s) and the Author(s) or with their explicit written 73 | permission. 74 | 75 | 5) The Font Software, modified or unmodified, in part or in whole, 76 | must be distributed entirely under this license, and must not be 77 | distributed under any other license. The requirement for fonts to 78 | remain under this license does not apply to any document created using 79 | the Font Software. 80 | 81 | TERMINATION 82 | This license becomes null and void if any of the above conditions are 83 | not met. 84 | 85 | DISCLAIMER 86 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 87 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 88 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 89 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 90 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 91 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 92 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 93 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 94 | OTHER DEALINGS IN THE FONT SOFTWARE. 95 | -------------------------------------------------------------------------------- /script/package_overlay/notice/boost.txt: -------------------------------------------------------------------------------- 1 | Boost Software License - Version 1.0 - August 17th, 2003 2 | 3 | Permission is hereby granted, free of charge, to any person or organization 4 | obtaining a copy of the software and accompanying documentation covered by 5 | this license (the "Software") to use, reproduce, display, distribute, 6 | execute, and transmit the Software, and to prepare derivative works of the 7 | Software, and to permit third-parties to whom the Software is furnished to 8 | do so, all subject to the following: 9 | 10 | The copyright notices in the Software and this entire statement, including 11 | the above license grant, this restriction and the following disclaimer, 12 | must be included in all copies of the Software, in whole or in part, and 13 | all derivative works of the Software, unless such copies or derivative 14 | works are solely in the form of machine-executable object code generated by 15 | a source language processor. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT 20 | SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE 21 | FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, 22 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /script/package_overlay/notice/bzip2.txt: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------- 3 | 4 | This program, "bzip2", the associated library "libbzip2", and all 5 | documentation, are copyright (C) 1996-2019 Julian R Seward. All 6 | rights reserved. 7 | 8 | Redistribution and use in source and binary forms, with or without 9 | modification, are permitted provided that the following conditions 10 | are met: 11 | 12 | 1. Redistributions of source code must retain the above copyright 13 | notice, this list of conditions and the following disclaimer. 14 | 15 | 2. The origin of this software must not be misrepresented; you must 16 | not claim that you wrote the original software. If you use this 17 | software in a product, an acknowledgment in the product 18 | documentation would be appreciated but is not required. 19 | 20 | 3. Altered source versions must be plainly marked as such, and must 21 | not be misrepresented as being the original software. 22 | 23 | 4. The name of the author may not be used to endorse or promote 24 | products derived from this software without specific prior written 25 | permission. 26 | 27 | THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS 28 | OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 29 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 31 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 33 | GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 35 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 36 | NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 37 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 38 | 39 | Julian Seward, jseward@acm.org 40 | bzip2/libbzip2 version 1.0.8 of 13 July 2019 41 | 42 | -------------------------------------------------------------------------- 43 | -------------------------------------------------------------------------------- /script/package_overlay/notice/emscripten.txt: -------------------------------------------------------------------------------- 1 | Emscripten is available under 2 licenses, the MIT license and the 2 | University of Illinois/NCSA Open Source License. 3 | 4 | Both are permissive open source licenses, with little if any 5 | practical difference between them. 6 | 7 | The reason for offering both is that (1) the MIT license is 8 | well-known, while (2) the University of Illinois/NCSA Open Source 9 | License allows Emscripten's code to be integrated upstream into 10 | LLVM, which uses that license, should the opportunity arise. 11 | 12 | The full text of both licenses follows. 13 | 14 | ============================================================================== 15 | 16 | Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. 17 | 18 | Permission is hereby granted, free of charge, to any person obtaining a copy 19 | of this software and associated documentation files (the "Software"), to deal 20 | in the Software without restriction, including without limitation the rights 21 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 22 | copies of the Software, and to permit persons to whom the Software is 23 | furnished to do so, subject to the following conditions: 24 | 25 | The above copyright notice and this permission notice shall be included in 26 | all copies or substantial portions of the Software. 27 | 28 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 29 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 30 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 33 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 34 | THE SOFTWARE. 35 | 36 | ============================================================================== 37 | 38 | Copyright (c) 2010-2014 Emscripten authors, see AUTHORS file. 39 | All rights reserved. 40 | 41 | Permission is hereby granted, free of charge, to any person obtaining a 42 | copy of this software and associated documentation files (the 43 | "Software"), to deal with the Software without restriction, including 44 | without limitation the rights to use, copy, modify, merge, publish, 45 | distribute, sublicense, and/or sell copies of the Software, and to 46 | permit persons to whom the Software is furnished to do so, subject to 47 | the following conditions: 48 | 49 | Redistributions of source code must retain the above copyright 50 | notice, this list of conditions and the following disclaimers. 51 | 52 | Redistributions in binary form must reproduce the above 53 | copyright notice, this list of conditions and the following disclaimers 54 | in the documentation and/or other materials provided with the 55 | distribution. 56 | 57 | Neither the names of Mozilla, 58 | nor the names of its contributors may be used to endorse 59 | or promote products derived from this Software without specific prior 60 | written permission. 61 | 62 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 63 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 64 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 65 | IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR 66 | ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 67 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 68 | SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. 69 | 70 | ============================================================================== 71 | 72 | This program uses portions of Node.js source code located in src/library_path.js, 73 | in accordance with the terms of the MIT license. Node's license follows: 74 | 75 | """ 76 | Copyright Joyent, Inc. and other Node contributors. All rights reserved. 77 | Permission is hereby granted, free of charge, to any person obtaining a copy 78 | of this software and associated documentation files (the "Software"), to 79 | deal in the Software without restriction, including without limitation the 80 | rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 81 | sell copies of the Software, and to permit persons to whom the Software is 82 | furnished to do so, subject to the following conditions: 83 | 84 | The above copyright notice and this permission notice shall be included in 85 | all copies or substantial portions of the Software. 86 | 87 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 88 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 89 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 90 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 91 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 93 | IN THE SOFTWARE. 94 | """ 95 | 96 | The musl libc project is bundled in this repo, and it has the MIT license, see 97 | system/lib/libc/musl/COPYRIGHT 98 | 99 | The third_party/ subdirectory contains code with other licenses. None of it is 100 | used by default, but certain options use it (e.g., the optional closure compiler 101 | flag will run closure compiler from third_party/). 102 | 103 | -------------------------------------------------------------------------------- /script/package_overlay/notice/gsl.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Microsoft Corporation. All rights reserved. 2 | 3 | This code is licensed under the MIT License (MIT). 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /script/package_overlay/notice/harfbuzz.txt: -------------------------------------------------------------------------------- 1 | HarfBuzz is licensed under the so-called "Old MIT" license. Details follow. 2 | For parts of HarfBuzz that are licensed under different licenses see individual 3 | files names COPYING in subdirectories where applicable. 4 | 5 | Copyright © 2010,2011,2012,2013,2014,2015,2016,2017,2018,2019,2020 Google, Inc. 6 | Copyright © 2018,2019,2020 Ebrahim Byagowi 7 | Copyright © 2019,2020 Facebook, Inc. 8 | Copyright © 2012 Mozilla Foundation 9 | Copyright © 2011 Codethink Limited 10 | Copyright © 2008,2010 Nokia Corporation and/or its subsidiary(-ies) 11 | Copyright © 2009 Keith Stribley 12 | Copyright © 2009 Martin Hosken and SIL International 13 | Copyright © 2007 Chris Wilson 14 | Copyright © 2006 Behdad Esfahbod 15 | Copyright © 2005 David Turner 16 | Copyright © 2004,2007,2008,2009,2010 Red Hat, Inc. 17 | Copyright © 1998-2004 David Turner and Werner Lemberg 18 | 19 | For full copyright notices consult the individual files in the package. 20 | 21 | 22 | Permission is hereby granted, without written agreement and without 23 | license or royalty fees, to use, copy, modify, and distribute this 24 | software and its documentation for any purpose, provided that the 25 | above copyright notice and the following two paragraphs appear in 26 | all copies of this software. 27 | 28 | IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 29 | DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 30 | ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 31 | IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 32 | DAMAGE. 33 | 34 | THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 35 | BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 36 | FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 37 | ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 38 | PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 39 | -------------------------------------------------------------------------------- /script/package_overlay/notice/libpng.txt: -------------------------------------------------------------------------------- 1 | COPYRIGHT NOTICE, DISCLAIMER, and LICENSE 2 | ========================================= 3 | 4 | PNG Reference Library License version 2 5 | --------------------------------------- 6 | 7 | * Copyright (c) 1995-2019 The PNG Reference Library Authors. 8 | * Copyright (c) 2018-2019 Cosmin Truta. 9 | * Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson. 10 | * Copyright (c) 1996-1997 Andreas Dilger. 11 | * Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 12 | 13 | The software is supplied "as is", without warranty of any kind, 14 | express or implied, including, without limitation, the warranties 15 | of merchantability, fitness for a particular purpose, title, and 16 | non-infringement. In no event shall the Copyright owners, or 17 | anyone distributing the software, be liable for any damages or 18 | other liability, whether in contract, tort or otherwise, arising 19 | from, out of, or in connection with the software, or the use or 20 | other dealings in the software, even if advised of the possibility 21 | of such damage. 22 | 23 | Permission is hereby granted to use, copy, modify, and distribute 24 | this software, or portions hereof, for any purpose, without fee, 25 | subject to the following restrictions: 26 | 27 | 1. The origin of this software must not be misrepresented; you 28 | must not claim that you wrote the original software. If you 29 | use this software in a product, an acknowledgment in the product 30 | documentation would be appreciated, but is not required. 31 | 32 | 2. Altered source versions must be plainly marked as such, and must 33 | not be misrepresented as being the original software. 34 | 35 | 3. This Copyright notice may not be removed or altered from any 36 | source or altered source distribution. 37 | 38 | 39 | PNG Reference Library License version 1 (for libpng 0.5 through 1.6.35) 40 | ----------------------------------------------------------------------- 41 | 42 | libpng versions 1.0.7, July 1, 2000, through 1.6.35, July 15, 2018 are 43 | Copyright (c) 2000-2002, 2004, 2006-2018 Glenn Randers-Pehrson, are 44 | derived from libpng-1.0.6, and are distributed according to the same 45 | disclaimer and license as libpng-1.0.6 with the following individuals 46 | added to the list of Contributing Authors: 47 | 48 | Simon-Pierre Cadieux 49 | Eric S. Raymond 50 | Mans Rullgard 51 | Cosmin Truta 52 | Gilles Vollant 53 | James Yu 54 | Mandar Sahastrabuddhe 55 | Google Inc. 56 | Vadim Barkov 57 | 58 | and with the following additions to the disclaimer: 59 | 60 | There is no warranty against interference with your enjoyment of 61 | the library or against infringement. There is no warranty that our 62 | efforts or the library will fulfill any of your particular purposes 63 | or needs. This library is provided with all faults, and the entire 64 | risk of satisfactory quality, performance, accuracy, and effort is 65 | with the user. 66 | 67 | Some files in the "contrib" directory and some configure-generated 68 | files that are distributed with libpng have other copyright owners, and 69 | are released under other open source licenses. 70 | 71 | libpng versions 0.97, January 1998, through 1.0.6, March 20, 2000, are 72 | Copyright (c) 1998-2000 Glenn Randers-Pehrson, are derived from 73 | libpng-0.96, and are distributed according to the same disclaimer and 74 | license as libpng-0.96, with the following individuals added to the 75 | list of Contributing Authors: 76 | 77 | Tom Lane 78 | Glenn Randers-Pehrson 79 | Willem van Schaik 80 | 81 | libpng versions 0.89, June 1996, through 0.96, May 1997, are 82 | Copyright (c) 1996-1997 Andreas Dilger, are derived from libpng-0.88, 83 | and are distributed according to the same disclaimer and license as 84 | libpng-0.88, with the following individuals added to the list of 85 | Contributing Authors: 86 | 87 | John Bowler 88 | Kevin Bracey 89 | Sam Bushell 90 | Magnus Holmgren 91 | Greg Roelofs 92 | Tom Tanner 93 | 94 | Some files in the "scripts" directory have other copyright owners, 95 | but are released under this license. 96 | 97 | libpng versions 0.5, May 1995, through 0.88, January 1996, are 98 | Copyright (c) 1995-1996 Guy Eric Schalnat, Group 42, Inc. 99 | 100 | For the purposes of this copyright and license, "Contributing Authors" 101 | is defined as the following set of individuals: 102 | 103 | Andreas Dilger 104 | Dave Martindale 105 | Guy Eric Schalnat 106 | Paul Schmidt 107 | Tim Wegner 108 | 109 | The PNG Reference Library is supplied "AS IS". The Contributing 110 | Authors and Group 42, Inc. disclaim all warranties, expressed or 111 | implied, including, without limitation, the warranties of 112 | merchantability and of fitness for any purpose. The Contributing 113 | Authors and Group 42, Inc. assume no liability for direct, indirect, 114 | incidental, special, exemplary, or consequential damages, which may 115 | result from the use of the PNG Reference Library, even if advised of 116 | the possibility of such damage. 117 | 118 | Permission is hereby granted to use, copy, modify, and distribute this 119 | source code, or portions hereof, for any purpose, without fee, subject 120 | to the following restrictions: 121 | 122 | 1. The origin of this source code must not be misrepresented. 123 | 124 | 2. Altered versions must be plainly marked as such and must not 125 | be misrepresented as being the original source. 126 | 127 | 3. This Copyright notice may not be removed or altered from any 128 | source or altered source distribution. 129 | 130 | The Contributing Authors and Group 42, Inc. specifically permit, 131 | without fee, and encourage the use of this source code as a component 132 | to supporting the PNG file format in commercial products. If you use 133 | this source code in a product, acknowledgment is not required but would 134 | be appreciated. 135 | -------------------------------------------------------------------------------- /script/package_overlay/notice/stb.txt: -------------------------------------------------------------------------------- 1 | This software is available under 2 licenses -- choose whichever you prefer. 2 | ------------------------------------------------------------------------------ 3 | ALTERNATIVE A - MIT License 4 | Copyright (c) 2017 Sean Barrett 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 9 | of the Software, and to permit persons to whom the Software is furnished to do 10 | so, subject to the following conditions: 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | ------------------------------------------------------------------------------ 21 | ALTERNATIVE B - Public Domain (www.unlicense.org) 22 | This is free and unencumbered software released into the public domain. 23 | Anyone is free to copy, modify, publish, use, compile, sell, or distribute this 24 | software, either in source code form or as a compiled binary, for any purpose, 25 | commercial or non-commercial, and by any means. 26 | In jurisdictions that recognize copyright laws, the author or authors of this 27 | software dedicate any and all copyright interest in the software to the public 28 | domain. We make this dedication for the benefit of the public at large and to 29 | the detriment of our heirs and successors. We intend this dedication to be an 30 | overt act of relinquishment in perpetuity of all present and future rights to 31 | this software under copyright law. 32 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 33 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 34 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 35 | AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 36 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 37 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 38 | -------------------------------------------------------------------------------- /script/package_overlay/notice/zlib.txt: -------------------------------------------------------------------------------- 1 | (C) 1995-2017 Jean-loup Gailly and Mark Adler 2 | 3 | This software is provided 'as-is', without any express or implied 4 | warranty. In no event will the authors be held liable for any damages 5 | arising from the use of this software. 6 | 7 | Permission is granted to anyone to use this software for any purpose, 8 | including commercial applications, and to alter it and redistribute it 9 | freely, subject to the following restrictions: 10 | 11 | 1. The origin of this software must not be misrepresented; you must not 12 | claim that you wrote the original software. If you use this software 13 | in a product, an acknowledgment in the product documentation would be 14 | appreciated but is not required. 15 | 2. Altered source versions must be plainly marked as such, and must not be 16 | misrepresented as being the original software. 17 | 3. This notice may not be removed or altered from any source distribution. 18 | 19 | Jean-loup Gailly Mark Adler 20 | jloup@gzip.org madler@alumni.caltech.edu 21 | 22 | If you use the zlib library in a product, we would appreciate *not* receiving 23 | lengthy legal documents to sign. The sources are provided for free but without 24 | warranty of any kind. The library has been entirely written by Jean-loup 25 | Gailly and Mark Adler; it does not include third-party code. 26 | 27 | If you redistribute modified sources, we would appreciate that you include in 28 | the file ChangeLog history information documenting your changes. Please read 29 | the FAQ for more information on the distribution of modified source versions. 30 | -------------------------------------------------------------------------------- /src/AdvanceInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "AdvanceInfo.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | std::ostream & operator<<(std::ostream & stream, const AdvanceInfo & advance) { 8 | stream 9 | << "[AdvanceInfo " 10 | << "type=" << static_cast(advance.type) << " " 11 | << "textIndex=" << advance.textIndex << " " 12 | << "advance=" << advance.advanceX << "," << advance.advanceY << " " 13 | << "customProperty=" << advance.customProperty << " " 14 | << "glyphID=" << advance.glyphID << " " 15 | << "glyph offset=" << advance.glyphOffsetX << "," << advance.glyphOffsetY << " "; 16 | 17 | if (advance.type == AdvanceType::InlineObject) { 18 | stream << "inlineObject=" << advance.inlineObject << " "; 19 | } 20 | 21 | stream << "]"; 22 | 23 | return stream; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | set(COBBLETEXT_DEBUG false CACHE BOOL "Enable dev features such as debug printing") 2 | set(COBBLETEXT_ADDRESS_SANITIZER false CACHE BOOL "Add address sanitizer flags (gcc/clang)") 3 | set(COBBLETEXT_USE_STACK_TRACE false CACHE BOOL "Add stack traces to the library exceptions (requires boost-stacktrace, boost-exception)") 4 | 5 | file(GLOB COBBLETEXT_LIBRARY_SOURCES "*.cpp" "c/*.cpp" "internal/*.cpp" "internal/*/*.cpp") 6 | 7 | if(COBBLETEXT_STATIC) 8 | add_library(cobbletext STATIC ${COBBLETEXT_LIBRARY_SOURCES} "Resource.cpp" ) 9 | else() 10 | add_library(cobbletext SHARED ${COBBLETEXT_LIBRARY_SOURCES} "Resource.cpp" ) 11 | endif() 12 | 13 | configure_file(Version.h.in "${CMAKE_CURRENT_BINARY_DIR}/Version.h") 14 | 15 | find_package(Python3 COMPONENTS Interpreter) 16 | add_custom_command(OUTPUT Resource.cpp 17 | COMMAND "${Python3_EXECUTABLE}" 18 | "${CMAKE_SOURCE_DIR}/script/embed_resource.py" 19 | "ADOBE_NOTDEF" 20 | "${CMAKE_SOURCE_DIR}/lib/adobe-notdef/AND-Regular.otf" 21 | "${CMAKE_CURRENT_BINARY_DIR}/Resource.cpp" 22 | VERBATIM 23 | ) 24 | 25 | set(COBBLETEXT_LIBRARY_ALL_INCLUDES 26 | "${CMAKE_SOURCE_DIR}/include/" 27 | "${CMAKE_SOURCE_DIR}/include/cobbletext/" 28 | "${CMAKE_CURRENT_SOURCE_DIR}" 29 | "${CMAKE_CURRENT_BINARY_DIR}" 30 | ${COBBLETEXT_DEPENDENCY_INCLUDES} 31 | ) 32 | 33 | target_include_directories(cobbletext PRIVATE ${COBBLETEXT_LIBRARY_ALL_INCLUDES}) 34 | set_target_properties(cobbletext PROPERTIES VERSION ${cobbletext_VERSION}) 35 | set_target_properties(cobbletext PROPERTIES SOVERSION ${cobbletext_VERSION_MAJOR}) 36 | 37 | if(MSVC) 38 | target_compile_options(cobbletext PRIVATE /W4) 39 | else() 40 | target_compile_options(cobbletext PRIVATE -Wall -Wextra -Wpedantic) 41 | endif() 42 | 43 | if(COBBLETEXT_USE_STACK_TRACE) 44 | add_compile_definitions(COBBLETEXT_USE_STACK_TRACE=1) 45 | endif() 46 | 47 | if(COBBLETEXT_DEBUG) 48 | add_compile_definitions(COBBLETEXT_DEBUG=1) 49 | endif() 50 | 51 | if(COBBLETEXT_ADDRESS_SANITIZER) 52 | set(SANITIZE_FLAGS -fno-omit-frame-pointer -fsanitize=address) 53 | target_compile_options(cobbletext PRIVATE ${SANITIZE_FLAGS} ) 54 | target_link_options(cobbletext PRIVATE ${SANITIZE_FLAGS} ) 55 | endif() 56 | 57 | target_link_libraries(cobbletext PRIVATE ${COBBLETEXT_DEPENDENCY_LIBS}) 58 | -------------------------------------------------------------------------------- /src/Engine.cpp: -------------------------------------------------------------------------------- 1 | #include "Engine.hpp" 2 | #include "EngineImpl.hpp" 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "LibraryImpl.hpp" 9 | 10 | namespace cobbletext { 11 | 12 | Engine::Engine(std::shared_ptr library) : 13 | impl(std::make_unique(*this, library->impl->context)) {} 14 | 15 | Engine::~Engine() = default; // for pimpl 16 | 17 | std::vector Engine::tiles() { 18 | return impl->layoutEngine->tiles(); 19 | } 20 | 21 | std::vector Engine::advances() { 22 | return impl->layoutEngine->advances(); 23 | } 24 | 25 | void Engine::addText(const uint8_t * data, uint32_t length, Encoding encoding) { 26 | switch (encoding) { 27 | case Encoding::UTF8: { 28 | auto u16text = impl->codec.utf8BytesToUTF16CodeUnits( 29 | gsl::span(data, length) 30 | ); 31 | addTextUTF16(u16text); 32 | delete [] u16text.data(); 33 | break; 34 | } 35 | case Encoding::UTF16LE: { 36 | auto u16text = impl->codec.utf16LEBytesToUTF16CodeUnits( 37 | gsl::span(data, length) 38 | ); 39 | addTextUTF16(u16text); 40 | delete [] u16text.data(); 41 | break; 42 | } 43 | } 44 | } 45 | 46 | void Engine::addTextUTF8(std::string_view text) { 47 | auto stringPiece = icu::StringPiece(text.data(), text.size()); 48 | auto uString = icu::UnicodeString::fromUTF8(stringPiece); 49 | impl->addUnicodeString(uString); 50 | } 51 | 52 | void Engine::addTextUTF8(const char * text, int32_t length) { 53 | icu::StringPiece stringPiece; 54 | 55 | if (length >= 0) { 56 | stringPiece = icu::StringPiece(text, length); 57 | } else { 58 | stringPiece = icu::StringPiece(text); 59 | } 60 | 61 | auto uString = icu::UnicodeString::fromUTF8(stringPiece); 62 | impl->addUnicodeString(uString); 63 | } 64 | 65 | void Engine::addTextUTF16(std::u16string_view text) { 66 | impl->addUnicodeString(icu::UnicodeString(text.data(), text.size())); 67 | } 68 | 69 | void Engine::addTextUTF16(const char16_t * text, int32_t length) { 70 | if (length >= 0) { 71 | impl->addUnicodeString(icu::UnicodeString(text, length)); 72 | } else { 73 | impl->addUnicodeString(icu::UnicodeString(text)); 74 | } 75 | 76 | } 77 | 78 | void Engine::addTextUTF32(std::u32string_view text) { 79 | auto uString = icu::UnicodeString::fromUTF32( 80 | reinterpret_cast(text.data()), text.size()); 81 | impl->addUnicodeString(uString); 82 | } 83 | 84 | void Engine::addTextUTF32(const char32_t * text, int32_t length) { 85 | icu::UnicodeString uString = icu::UnicodeString::fromUTF32( 86 | reinterpret_cast(text), length); 87 | 88 | impl->addUnicodeString(uString); 89 | } 90 | 91 | void Engine::addInlineObject(InlineObjectID id, uint32_t width, uint32_t height) { 92 | impl->addInlineObject(id, width, height); 93 | } 94 | 95 | void Engine::clear() { 96 | impl->clear(); 97 | } 98 | 99 | void Engine::layOut() { 100 | impl->layOut(); 101 | } 102 | 103 | 104 | 105 | bool Engine::tilesValid() { 106 | return impl->tilesValid(); 107 | } 108 | 109 | void Engine::rasterize() { 110 | impl->rasterize(); 111 | } 112 | 113 | bool Engine::packTiles(uint32_t width, uint32_t height) { 114 | return impl->packTiles(width, height); 115 | } 116 | 117 | void Engine::clearTiles() { 118 | impl->clearTiles(); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/EngineImpl.cpp: -------------------------------------------------------------------------------- 1 | #include "EngineImpl.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | Engine::Impl::Impl(Engine & parent, 8 | std::shared_ptr context) : 9 | parent(parent), 10 | context(context), 11 | textSource(std::make_shared()), 12 | layoutEngine(std::make_shared( 13 | context, textSource)) {} 14 | 15 | internal::TextFormat Engine::Impl::makeCurrentTextFormat() { 16 | internal::TextFormat textFormat; 17 | 18 | textFormat.customProperty = parent.customProperty; 19 | textFormat.fontFace = parent.font; 20 | textFormat.fontSize = parent.fontSize; 21 | textFormat.language = parent.language; 22 | textFormat.script = parent.script; 23 | textFormat.scriptDirection = parent.scriptDirection; 24 | 25 | return textFormat; 26 | } 27 | 28 | void Engine::Impl::addUnicodeString(icu::UnicodeString string) { 29 | textSource->addText(string, makeCurrentTextFormat()); 30 | } 31 | 32 | void Engine::Impl::addInlineObject(InlineObjectID id, uint32_t width, 33 | uint32_t height) { 34 | textSource->addInlineObject(id, width, height, makeCurrentTextFormat()); 35 | } 36 | 37 | void Engine::Impl::clear() { 38 | textSource->clear(); 39 | layoutEngine->clearText(); 40 | parent.outputInfo.textWidth = parent.outputInfo.textHeight = 0; 41 | } 42 | 43 | void Engine::Impl::layOut() { 44 | if (parent.locale != "") { 45 | textSource->locale = parent.locale.c_str(); 46 | } else { 47 | textSource->locale = icu::Locale::getDefault(); 48 | } 49 | layoutEngine->lineLength = parent.lineLength; 50 | layoutEngine->textAlignment = parent.textAlignment; 51 | layoutEngine->layOut(); 52 | 53 | parent.outputInfo.textWidth = layoutEngine->textWidth(); 54 | parent.outputInfo.textHeight = layoutEngine->textHeight(); 55 | } 56 | 57 | bool Engine::Impl::tilesValid() { 58 | return layoutEngine->tilesValid(); 59 | } 60 | 61 | void Engine::Impl::rasterize() { 62 | for (auto & tile : layoutEngine->tiles()) { 63 | const auto & glyphKey = context->glyphTable->idToKey(tile.glyphID); 64 | context->glyphTable->rasterize(glyphKey); 65 | } 66 | } 67 | 68 | bool Engine::Impl::packTiles(uint32_t width, uint32_t height) { 69 | return context->atlasPacker->pack(layoutEngine->tiles(), width, height); 70 | } 71 | 72 | void Engine::Impl::clearTiles() { 73 | return layoutEngine->clearTiles(); 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/EngineImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "Engine.hpp" 8 | #include "Library.hpp" 9 | #include "internal/input/TextSource.hpp" 10 | #include "internal/input/TextFormat.hpp" 11 | #include "internal/Context.hpp" 12 | #include "internal/Codec.hpp" 13 | #include "internal/layout/LayoutEngine.hpp" 14 | 15 | namespace cobbletext { 16 | 17 | class Engine::Impl { 18 | public: 19 | Engine & parent; 20 | std::shared_ptr context; 21 | std::shared_ptr textSource; 22 | std::shared_ptr layoutEngine; 23 | internal::Codec codec; 24 | 25 | 26 | Impl(Engine & parent, std::shared_ptr context); 27 | 28 | internal::TextFormat makeCurrentTextFormat(); 29 | 30 | void addUnicodeString(icu::UnicodeString string); 31 | 32 | void addInlineObject(InlineObjectID id, uint32_t width, uint32_t height); 33 | 34 | void clear(); 35 | 36 | void layOut(); 37 | 38 | bool tilesValid(); 39 | 40 | void rasterize(); 41 | 42 | bool packTiles(uint32_t width, uint32_t height); 43 | 44 | void clearTiles(); 45 | }; 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/Exception.cpp: -------------------------------------------------------------------------------- 1 | #include "Exception.hpp" 2 | 3 | namespace cobbletext { 4 | 5 | RuntimeError::RuntimeError(const std::string & what_arg) : 6 | std::runtime_error(what_arg) {} 7 | 8 | RuntimeError::RuntimeError(const char * what_arg) : 9 | std::runtime_error(what_arg) {} 10 | 11 | LogicError::LogicError(const std::string & what_arg) : 12 | std::logic_error(what_arg) {} 13 | 14 | LogicError::LogicError(const char * what_arg) : 15 | std::logic_error(what_arg) {} 16 | 17 | LibraryError::LibraryError(std::string message) : 18 | RuntimeError(message), 19 | code(-1), 20 | message(message) {} 21 | 22 | LibraryError::LibraryError(std::string message, long long int code) : 23 | RuntimeError(std::to_string(code) + " : " + message), 24 | code(code), 25 | message(message) {} 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/FontInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "FontInfo.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | std::ostream & operator<<(std::ostream & stream, const FontInfo & font) { 8 | stream 9 | << "[FontInfo " 10 | << "id=" << font.id << " " 11 | << "familyName=" << font.familyName << " " 12 | << "styleName=" << font.styleName << " " 13 | << "unitsPerEM=" << font.unitsPerEM << " " 14 | << "ascender=" << font.ascender << " " 15 | << "descender=" << font.descender << " " 16 | << "underlinePosition=" << font.underlinePosition << " " 17 | << "underlineThickness=" << font.underlineThickness << " " 18 | << "]"; 19 | 20 | return stream; 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/GlyphInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "GlyphInfo.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | std::ostream & operator<<(std::ostream & stream, const GlyphInfo & glyph) { 8 | stream 9 | << "[GlyphInfo " 10 | << "id=" << glyph.id << " " 11 | << "image size=" << glyph.imageWidth << "," << glyph.imageHeight << " " 12 | << "image offset=" << glyph.imageOffsetX << "," << glyph.imageOffsetY << " " 13 | << "image=" << glyph.image.size() << " " 14 | << "]"; 15 | 16 | return stream; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/Library.cpp: -------------------------------------------------------------------------------- 1 | #include "Library.hpp" 2 | #include "LibraryImpl.hpp" 3 | 4 | #ifndef __INTELLISENSE__ 5 | #include "Version.h" 6 | #else 7 | #define COBBLETEXT_VERSION_MAJOR 0 8 | #define COBBLETEXT_VERSION_MINOR 0 9 | #define COBBLETEXT_VERSION_PATCH 0 10 | #endif 11 | 12 | #include 13 | 14 | namespace cobbletext { 15 | 16 | int32_t Library::versionMajor() { 17 | return COBBLETEXT_VERSION_MAJOR; 18 | } 19 | 20 | int32_t Library::versionMinor() { 21 | return COBBLETEXT_VERSION_MINOR; 22 | } 23 | 24 | int32_t Library::versionPatch() { 25 | return COBBLETEXT_VERSION_PATCH; 26 | } 27 | 28 | std::string Library::version_ = 29 | std::to_string(COBBLETEXT_VERSION_MAJOR) + "." + 30 | std::to_string(COBBLETEXT_VERSION_MINOR) + "." + 31 | std::to_string(COBBLETEXT_VERSION_PATCH); 32 | 33 | std::string Library::version() { 34 | return version_; 35 | } 36 | 37 | Library::Library() : 38 | impl(std::make_unique()) {} 39 | 40 | Library::~Library() = default; // for pimpl 41 | 42 | FontID Library::fallbackFont() { 43 | return impl->context->fontTable->fallbackFont; 44 | } 45 | 46 | FontID Library::loadFont(const std::string path) { 47 | return impl->loadFont(path); 48 | } 49 | 50 | FontID Library::loadFontBytes(const uint8_t * bytes, size_t length, 51 | int32_t faceIndex) { 52 | return impl->context->fontTable->loadBytes(gsl::span(bytes, length), 53 | faceIndex); 54 | } 55 | 56 | FontInfo Library::getFontInfo(FontID id) { 57 | return impl->context->fontTable->getFontInfo(id); 58 | } 59 | 60 | GlyphInfo Library::getGlyphInfo(GlyphID id) { 61 | return impl->context->glyphTable->getGlyphInfo(id); 62 | } 63 | 64 | void Library::setFontAlternative(FontID id, FontID fallbackID) { 65 | impl->context->fontTable->setFontAlternative(id, fallbackID); 66 | } 67 | 68 | FontID Library::getFontAlternative(FontID id) { 69 | return impl->context->fontTable->getFontAlternative(id); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /src/LibraryImpl.cpp: -------------------------------------------------------------------------------- 1 | #include "LibraryImpl.hpp" 2 | 3 | namespace cobbletext { 4 | 5 | Library::Impl::Impl() : 6 | context(std::make_shared()) 7 | {} 8 | 9 | 10 | FontID Library::Impl::loadFont(const std::string & path) { 11 | std::smatch fragmentMatch; 12 | 13 | if (std::regex_match(path, fragmentMatch, fragmentPattern)) { 14 | auto fragment = fragmentMatch.str(2); 15 | size_t numProcessed = 0; 16 | int32_t faceIndex = 0; 17 | 18 | try { 19 | faceIndex = std::stoi(fragment, &numProcessed); 20 | } catch (std::exception & error) { 21 | } 22 | 23 | if (numProcessed == fragment.size()) { 24 | auto processedPath = fragmentMatch.str(1); 25 | return context->fontTable->load(processedPath.c_str(), faceIndex); 26 | } 27 | } 28 | 29 | return context->fontTable->load(path.c_str()); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/LibraryImpl.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Library.hpp" 4 | 5 | #include 6 | #include 7 | 8 | #include "EngineImpl.hpp" 9 | #include "internal/Context.hpp" 10 | 11 | namespace cobbletext { 12 | 13 | class Library::Impl { 14 | const std::regex fragmentPattern = std::regex("(.+)#([^/\\]+)", 15 | std::regex::extended); 16 | 17 | public: 18 | 19 | std::shared_ptr context; 20 | 21 | Impl(); 22 | 23 | FontID loadFont(const std::string & path); 24 | 25 | }; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/Math.cpp: -------------------------------------------------------------------------------- 1 | #include "Math.hpp" 2 | 3 | #include 4 | 5 | #include "internal/ColorUtil.hpp" 6 | 7 | namespace cobbletext { 8 | 9 | uint32_t Math::alpha_blend_over_argb(uint32_t background, uint32_t foreground) { 10 | using C = cobbletext::internal::ColorUtil; 11 | 12 | double alphaBG = C::alphaARGB(background) / 255.0; 13 | double redBG = C::redARGB(background) / 255.0; 14 | double greenBG = C::greenARGB(background) / 255.0; 15 | double blueBG = C::blueARGB(background) / 255.0; 16 | 17 | double alphaFG = C::alphaARGB(foreground) / 255.0; 18 | double redFG = C::redARGB(foreground) / 255.0; 19 | double greenFG = C::greenARGB(foreground) / 255.0; 20 | double blueFG = C::blueARGB(foreground) / 255.0; 21 | 22 | double divisor = alphaFG + alphaBG * (1 - alphaFG); 23 | 24 | double newAlpha = divisor; 25 | double newRed = redFG * alphaFG + redBG * alphaBG * (1 - alphaFG); 26 | double newGreen = greenFG * alphaFG + greenBG * alphaBG * (1 - alphaFG); 27 | double newBlue = blueFG * alphaFG + blueBG * alphaBG * (1 - alphaFG); 28 | 29 | return C::makeARGB(newRed * 255, newGreen * 255, newBlue * 255, 30 | newAlpha * 255); 31 | } 32 | 33 | uint32_t Math::gamma_argb(uint32_t color, double gamma) { 34 | using C = cobbletext::internal::ColorUtil; 35 | 36 | uint8_t alpha = C::alphaARGB(color); 37 | uint8_t red = C::redARGB(color); 38 | uint8_t green = C::greenARGB(color); 39 | uint8_t blue = C::blueARGB(color); 40 | 41 | uint8_t newAlpha = alpha; 42 | uint8_t newRed = 255.0 * pow(red / 255.0, gamma); 43 | uint8_t newGreen = 255.0 * pow(green / 255.0, gamma); 44 | uint8_t newBlue = 255.0 * pow(blue / 255.0, gamma); 45 | 46 | return C::makeARGB(newRed, newGreen, newBlue, newAlpha); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/OutputInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "OutputInfo.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | std::ostream & operator<<(std::ostream & stream, const OutputInfo & info) { 8 | stream 9 | << "[OutputInfo " 10 | << "text size=" << info.textWidth << "," << info.textHeight << " " 11 | << "]"; 12 | 13 | return stream; 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/Resource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace cobbletext { 7 | 8 | // Note: class definition for this declaration is generated using 9 | // embed_resource.py by the cmake build system 10 | class Resource { 11 | 12 | public: 13 | 14 | static const uint8_t ADOBE_NOTDEF[]; 15 | static const size_t ADOBE_NOTDEF_SIZE; 16 | 17 | }; 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/TileInfo.cpp: -------------------------------------------------------------------------------- 1 | #include "TileInfo.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext { 6 | 7 | std::ostream & operator<<(std::ostream & stream, const TileInfo & tile) { 8 | stream 9 | << "[TileInfo " 10 | << "glyphID=" << tile.glyphID << " " 11 | << "atlas pos=" << tile.atlasX << "," << tile.atlasY << " " 12 | << "]"; 13 | 14 | return stream; 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/Version.h.in: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define COBBLETEXT_VERSION_MAJOR @cobbletext_VERSION_MAJOR@ 4 | #define COBBLETEXT_VERSION_MINOR @cobbletext_VERSION_MINOR@ 5 | #define COBBLETEXT_VERSION_PATCH @cobbletext_VERSION_PATCH@ 6 | -------------------------------------------------------------------------------- /src/c/engine_c.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | 7 | #include "Engine.hpp" 8 | #include "EngineImpl.hpp" 9 | #include "cobbletext.h" 10 | 11 | 12 | struct CobbletextEngine { 13 | CobbletextLibrary * library; 14 | std::unique_ptr obj; 15 | std::unique_ptr properties; 16 | std::unique_ptr textProperties; 17 | std::vector> tiles; 18 | std::vector> advances; 19 | std::unique_ptr tilesPointer; 20 | std::unique_ptr advancesPointer; 21 | std::unique_ptr outputInfo; 22 | bool tilesPrepared; 23 | bool advancesPrepared; 24 | }; 25 | 26 | 27 | namespace cobbletext::c { 28 | 29 | void prepareEngineTiles(struct CobbletextEngine * engine); 30 | void prepareEngineAdvances(struct CobbletextEngine * engine); 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/c/library_c.cpp: -------------------------------------------------------------------------------- 1 | #include "c/library_c.hpp" 2 | 3 | #include "c/util_c.hpp" 4 | #include "LibraryImpl.hpp" 5 | #include "Exception.hpp" 6 | 7 | CobbletextLibrary * cobbletext_library_new() { 8 | CobbletextLibrary * handle = nullptr; 9 | 10 | try { 11 | handle = new CobbletextLibrary(); 12 | handle->obj = std::make_shared(); 13 | cobbletext::c::handleSuccess(handle); 14 | } catch (std::exception & exception) { 15 | if (handle) { 16 | cobbletext::c::handleException(handle, &exception); 17 | } 18 | } 19 | 20 | return handle; 21 | } 22 | 23 | void cobbletext_library_delete(CobbletextLibrary * library) { 24 | delete library; 25 | } 26 | 27 | int32_t cobbletext_get_error_code(CobbletextLibrary * library) { 28 | return library->errorCode; 29 | } 30 | 31 | const char * cobbletext_get_error_message(CobbletextLibrary * library) { 32 | return library->errorMessage.c_str(); 33 | } 34 | 35 | void cobbletext_clear_error(CobbletextLibrary * library) { 36 | library->errorCode = 0; 37 | library->errorMessage = ""; 38 | } 39 | 40 | CobbletextFontID cobbletext_library_get_fallback_font( 41 | CobbletextLibrary * library) { 42 | return library->obj->fallbackFont(); 43 | } 44 | 45 | CobbletextFontID cobbletext_library_load_font(CobbletextLibrary * library, 46 | const char * path) { 47 | try { 48 | auto fontID = library->obj->loadFont(path); 49 | cobbletext::c::handleSuccess(library); 50 | return fontID; 51 | } catch (std::exception & exception) { 52 | cobbletext::c::handleException(library, &exception); 53 | return 0; 54 | } 55 | } 56 | 57 | CobbletextFontID cobbletext_library_load_font_bytes(CobbletextLibrary * library, 58 | const uint8_t * data, uint32_t length, int32_t face_index) { 59 | try { 60 | auto fontID = library->obj->loadFontBytes(data, length, face_index); 61 | cobbletext::c::handleSuccess(library); 62 | return fontID; 63 | } catch (std::exception & exception) { 64 | cobbletext::c::handleException(library, &exception); 65 | return 0; 66 | } 67 | } 68 | 69 | const struct CobbletextFontInfo * cobbletext_library_get_font_info( 70 | CobbletextLibrary * library, CobbletextFontID font) { 71 | 72 | cobbletext::FontInfo fontInfo; 73 | 74 | try { 75 | fontInfo = library->obj->getFontInfo(font); 76 | cobbletext::c::handleSuccess(library); 77 | 78 | } catch (std::exception & exception) { 79 | cobbletext::c::handleException(library, &exception); 80 | return nullptr; 81 | } 82 | 83 | library->fontInfo = std::make_unique(); 84 | library->fontInfo->ascender = fontInfo.ascender; 85 | library->fontInfo->descender = fontInfo.descender; 86 | library->fontInfo->family_name = fontInfo.familyName.c_str(); 87 | library->fontInfo->height = fontInfo.height; 88 | library->fontInfo->id = fontInfo.id; 89 | library->fontInfo->style_name = fontInfo.styleName.c_str(); 90 | library->fontInfo->underline_position = fontInfo.underlinePosition; 91 | library->fontInfo->underline_thickness = fontInfo.underlineThickness; 92 | library->fontInfo->units_per_em = fontInfo.unitsPerEM; 93 | 94 | return library->fontInfo.get(); 95 | } 96 | 97 | const struct CobbletextGlyphInfo * cobbletext_library_get_glyph_info( 98 | CobbletextLibrary * library, CobbletextFontID glyph) { 99 | cobbletext::GlyphInfo glyphInfo; 100 | 101 | try { 102 | glyphInfo = library->obj->getGlyphInfo(glyph); 103 | cobbletext::c::handleSuccess(library); 104 | } catch (std::exception & exception) { 105 | cobbletext::c::handleException(library, &exception); 106 | return nullptr; 107 | } 108 | 109 | library->glyphInfo = std::make_unique(); 110 | library->glyphInfo->id = glyphInfo.id; 111 | 112 | uint8_t * image = new uint8_t[glyphInfo.image.size()]; 113 | library->glyphImage = std::unique_ptr(image); 114 | std::copy(glyphInfo.image.begin(), glyphInfo.image.end(), image); 115 | library->glyphInfo->image = image; 116 | 117 | library->glyphInfo->image_width = glyphInfo.imageWidth; 118 | library->glyphInfo->image_height = glyphInfo.imageHeight; 119 | library->glyphInfo->image_offset_x = glyphInfo.imageOffsetX; 120 | library->glyphInfo->image_offset_y = glyphInfo.imageOffsetY; 121 | 122 | return library->glyphInfo.get(); 123 | } 124 | 125 | void cobbletext_library_set_font_alternative(CobbletextLibrary * library, 126 | CobbletextFontID font_id, CobbletextFontID fallback_font_id) { 127 | library->obj->setFontAlternative(font_id, fallback_font_id); 128 | } 129 | 130 | CobbletextFontID cobbletext_library_get_font_alternative( 131 | CobbletextLibrary * library, CobbletextFontID font_id) { 132 | return library->obj->getFontAlternative(font_id); 133 | } 134 | 135 | namespace cobbletext::c { 136 | 137 | } 138 | -------------------------------------------------------------------------------- /src/c/library_c.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "cobbletext.h" 9 | #include "Library.hpp" 10 | 11 | struct CobbletextLibrary { 12 | std::shared_ptr obj; 13 | int32_t errorCode; 14 | std::string errorMessage; 15 | std::unique_ptr fontInfo; 16 | std::unique_ptr glyphInfo; 17 | std::unique_ptr glyphImage; 18 | }; 19 | 20 | namespace cobbletext::c { 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/c/math_c.cpp: -------------------------------------------------------------------------------- 1 | #include "cobbletext.h" 2 | 3 | #include "Math.hpp" 4 | 5 | uint32_t cobbletext_math_alpha_blend_over_argb(uint32_t background, 6 | uint32_t foreground) { 7 | return cobbletext::Math::alpha_blend_over_argb(background, foreground); 8 | } 9 | 10 | 11 | uint32_t cobbletext_math_gamma_argb(uint32_t color, double gamma) { 12 | return cobbletext::Math::gamma_argb(color, gamma); 13 | } 14 | 15 | -------------------------------------------------------------------------------- /src/c/util_c.cpp: -------------------------------------------------------------------------------- 1 | #include "c/util_c.hpp" 2 | 3 | #include "library_c.hpp" 4 | #include "Exception.hpp" 5 | #include "internal/Debug.hpp" 6 | #include "internal/ExceptionUtil.hpp" 7 | 8 | namespace cobbletext::c { 9 | 10 | void handleSuccess(struct CobbletextLibrary * library) { 11 | library->errorCode = 0; 12 | library->errorMessage = ""; 13 | } 14 | 15 | void handleException(struct CobbletextLibrary * library, 16 | std::exception * exception) { 17 | auto * libraryError = dynamic_cast(exception); 18 | 19 | if (libraryError) { 20 | library->errorCode = libraryError->code; 21 | library->errorMessage = libraryError->message; 22 | } else { 23 | library->errorCode = -1; 24 | library->errorMessage = exception->what(); 25 | } 26 | 27 | #if defined(COBBLETEXT_DEBUG) && defined(COBBLETEXT_USE_STACK_TRACE) 28 | auto & exceptionRef = *exception; 29 | const boost::stacktrace::stacktrace * stackTrace = 30 | cobbletext::internal::get_error_info(exceptionRef); 31 | 32 | if (stackTrace) { 33 | COBBLETEXT_DEBUG_PRINT(*stackTrace) 34 | } else { 35 | COBBLETEXT_DEBUG_PRINT("no stack trace available"); 36 | } 37 | #endif 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/c/util_c.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "cobbletext.h" 6 | 7 | namespace cobbletext::c { 8 | 9 | void handleSuccess(struct CobbletextLibrary * library); 10 | 11 | void handleException(struct CobbletextLibrary * library, 12 | std::exception * exception); 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/c/version_c.cpp: -------------------------------------------------------------------------------- 1 | #include "cobbletext.h" 2 | 3 | #include "Library.hpp" 4 | 5 | int32_t cobbletext_get_version_major() { 6 | return cobbletext::Library::versionMajor(); 7 | } 8 | 9 | int32_t cobbletext_get_version_minor() { 10 | return cobbletext::Library::versionMinor(); 11 | } 12 | 13 | int32_t cobbletext_get_version_patch() { 14 | return cobbletext::Library::versionPatch(); 15 | } 16 | 17 | const char * cobbletext_get_version() { 18 | return cobbletext::Library::version().c_str(); 19 | } 20 | -------------------------------------------------------------------------------- /src/em/AdvanceInfo_em.cpp: -------------------------------------------------------------------------------- 1 | #include "AdvanceInfo.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_AdvanceInfo) { 8 | enum_("AdvanceType") 9 | .value("Invalid", cobbletext::AdvanceType::Invalid) 10 | .value("Glyph", cobbletext::AdvanceType::Glyph) 11 | .value("InlineObject", cobbletext::AdvanceType::InlineObject) 12 | .value("LineBreak", cobbletext::AdvanceType::LineBreak) 13 | .value("Bidi", cobbletext::AdvanceType::Bidi) 14 | .value("Layout", cobbletext::AdvanceType::Layout) 15 | ; 16 | 17 | value_object("AdvanceInfo") 18 | .field("type", &cobbletext::AdvanceInfo::type) 19 | .field("textIndex", &cobbletext::AdvanceInfo::textIndex) 20 | .field("advanceX", &cobbletext::AdvanceInfo::advanceX) 21 | .field("advanceY", &cobbletext::AdvanceInfo::advanceY) 22 | .field("glyphID", &cobbletext::AdvanceInfo::glyphID) 23 | .field("glyphOffsetX", &cobbletext::AdvanceInfo::glyphOffsetX) 24 | .field("glyphOffsetY", &cobbletext::AdvanceInfo::glyphOffsetY) 25 | .field("inlineObject", &cobbletext::AdvanceInfo::inlineObject) 26 | .field("customProperty", &cobbletext::AdvanceInfo::customProperty) 27 | ; 28 | } 29 | -------------------------------------------------------------------------------- /src/em/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | file(GLOB COBBLETEXT_LIBRARY_SOURCES "../*.cpp" "../c/*.cpp" "../internal/*.cpp" "../internal/*/*.cpp") 2 | file(GLOB COBBLETEXT_EM_SOURCES "*.cpp") 3 | 4 | add_executable(cobbletext_js 5 | js/cobbletext_js.cpp 6 | ${COBBLETEXT_LIBRARY_SOURCES} 7 | ${COBBLETEXT_EM_SOURCES} 8 | "${CMAKE_CURRENT_BINARY_DIR}/Resource.cpp" 9 | ) 10 | 11 | configure_file(../Version.h.in "${CMAKE_CURRENT_BINARY_DIR}/Version.h") 12 | 13 | find_package(Python3 COMPONENTS Interpreter) 14 | add_custom_command(OUTPUT Resource.cpp 15 | COMMAND "${Python3_EXECUTABLE}" 16 | "${CMAKE_SOURCE_DIR}/script/embed_resource.py" 17 | "ADOBE_NOTDEF" 18 | "${CMAKE_SOURCE_DIR}/lib/adobe-notdef/AND-Regular.otf" 19 | "${CMAKE_CURRENT_BINARY_DIR}/Resource.cpp" 20 | VERBATIM 21 | ) 22 | 23 | set(COBBLETEXT_LIBRARY_ALL_INCLUDES 24 | "${CMAKE_SOURCE_DIR}/include/" 25 | "${CMAKE_SOURCE_DIR}/include/cobbletext/" 26 | "${CMAKE_SOURCE_DIR}/src/" 27 | "${CMAKE_CURRENT_BINARY_DIR}" 28 | ${COBBLETEXT_DEPENDENCY_INCLUDES} 29 | ) 30 | 31 | target_compile_definitions(cobbletext_js PRIVATE COBBLETEXT_ENABLE_EM_API=1) 32 | target_include_directories(cobbletext_js PRIVATE ${COBBLETEXT_LIBRARY_ALL_INCLUDES}) 33 | 34 | set(CMAKE_EXECUTABLE_SUFFIX ".js") 35 | set_target_properties(cobbletext_js PROPERTIES OUTPUT_NAME "cobbletext") 36 | 37 | target_link_options(cobbletext_js PRIVATE "SHELL:-s MODULARIZE=1") 38 | target_link_options(cobbletext_js PRIVATE "SHELL:-s EXPORT_NAME=CobbletextModule") 39 | target_link_options(cobbletext_js PRIVATE "--bind") 40 | 41 | # If LINKABLE is disabled, EMSCRIPTEN_GENERATE_BITCODE_STATIC_LIBRARIES=true 42 | # should be set to generate .bc files intead of .a files 43 | # https://github.com/emscripten-core/emscripten/issues/5465 44 | 45 | # target_link_options(cobbletext_js PRIVATE "SHELL:-s LINKABLE=1") 46 | 47 | target_link_libraries(cobbletext_js PRIVATE ${COBBLETEXT_DEPENDENCY_LIBS}) 48 | -------------------------------------------------------------------------------- /src/em/Encoding_em.cpp: -------------------------------------------------------------------------------- 1 | #include "Encoding.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_Encoding) { 8 | enum_("Encoding") 9 | .value("UTF8", cobbletext::Encoding::UTF8) 10 | .value("UTF16LE", cobbletext::Encoding::UTF16LE) 11 | ; 12 | } 13 | -------------------------------------------------------------------------------- /src/em/Engine_em.cpp: -------------------------------------------------------------------------------- 1 | #include "Engine.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | namespace cobbletext::em { 8 | 9 | void addTextWrapper(Engine & engine, std::string text, Encoding encoding) { 10 | engine.addText(reinterpret_cast(text.c_str()), text.size(), encoding); 11 | } 12 | 13 | void addTextUTF8Wrapper(Engine & engine, std::string text) { 14 | engine.addTextUTF8(text); 15 | } 16 | 17 | void addTextUTF16Wrapper(Engine & engine, std::wstring text) { 18 | engine.addTextUTF16(std::u16string(text.begin(), text.end())); 19 | } 20 | 21 | void addTextUTF32Wrapper(Engine & engine, std::vector text) { 22 | engine.addTextUTF32(std::u32string(text.begin(), text.end())); 23 | } 24 | 25 | } 26 | 27 | 28 | EMSCRIPTEN_BINDINGS(cobbletext_Engine) { 29 | class_("Engine") 30 | .property("lineLength", &cobbletext::Engine::lineLength) 31 | .property("locale", &cobbletext::Engine::locale) 32 | .property("textAlignment", &cobbletext::Engine::textAlignment) 33 | .property("language", &cobbletext::Engine::language) 34 | .property("script", &cobbletext::Engine::script) 35 | .property("scriptDirection", &cobbletext::Engine::scriptDirection) 36 | .property("font", &cobbletext::Engine::font) 37 | .property("fontSize", &cobbletext::Engine::fontSize) 38 | .property("customProperty", &cobbletext::Engine::customProperty) 39 | .property("outputInfo", &cobbletext::Engine::outputInfo) 40 | 41 | .function("tiles", &cobbletext::Engine::tiles) 42 | .function("advances", &cobbletext::Engine::advances) 43 | 44 | // .constructor>() 45 | // .smart_ptr>("Engine") 46 | .smart_ptr_constructor("Engine", &std::make_shared>) 47 | 48 | .function("addText", &cobbletext::em::addTextWrapper) 49 | .function("addTextUTF8", &cobbletext::em::addTextUTF8Wrapper) 50 | .function("addTextUTF16", &cobbletext::em::addTextUTF16Wrapper) 51 | .function("addTextUTF32", &cobbletext::em::addTextUTF32Wrapper) 52 | .function("addInlineObject", &cobbletext::Engine::addInlineObject) 53 | .function("clear", &cobbletext::Engine::clear) 54 | .function("layOut", &cobbletext::Engine::layOut) 55 | .function("tilesValid", &cobbletext::Engine::tilesValid) 56 | .function("rasterize", &cobbletext::Engine::rasterize) 57 | .function("packTiles", &cobbletext::Engine::packTiles) 58 | .function("clearTiles", &cobbletext::Engine::clearTiles) 59 | 60 | ; 61 | 62 | register_vector("vector_TileInfo"); 63 | register_vector("vector_AdvanceInfo"); 64 | register_vector("vector_char32_t"); 65 | } 66 | -------------------------------------------------------------------------------- /src/em/Exception_em.cpp: -------------------------------------------------------------------------------- 1 | #include "Exception.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | 8 | namespace cobbletext::em { 9 | 10 | std::string getExceptionMessage(intptr_t pointer) { 11 | auto error = reinterpret_cast(pointer); 12 | return std::string(error->what()); 13 | } 14 | 15 | } 16 | 17 | EMSCRIPTEN_BINDINGS(cobbletext_Exception) { 18 | class_("RuntimeError") 19 | // .function("what", &cobbletext::RuntimeError::what) 20 | ; 21 | 22 | class_("LogicError") 23 | // .function("what", &cobbletext::LogicError::what) 24 | ; 25 | 26 | class_("LibraryError") 27 | .property("code", &cobbletext::LibraryError::code) 28 | .property("message", &cobbletext::LibraryError::message) 29 | // .function("what", &cobbletext::LibraryError::what) 30 | ; 31 | 32 | function("getExceptionMessage", &cobbletext::em::getExceptionMessage, 33 | allow_raw_pointers()); 34 | } 35 | -------------------------------------------------------------------------------- /src/em/FontInfo_em.cpp: -------------------------------------------------------------------------------- 1 | #include "FontInfo.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_FontInfo) { 8 | value_object("FontInfo") 9 | .field("id", &cobbletext::FontInfo::id) 10 | .field("familyName", &cobbletext::FontInfo::familyName) 11 | .field("styleName", &cobbletext::FontInfo::styleName) 12 | .field("unitsPerEM", &cobbletext::FontInfo::unitsPerEM) 13 | .field("ascender", &cobbletext::FontInfo::ascender) 14 | .field("descender", &cobbletext::FontInfo::descender) 15 | .field("height", &cobbletext::FontInfo::height) 16 | .field("underlinePosition", &cobbletext::FontInfo::underlinePosition) 17 | .field("underlineThickness", &cobbletext::FontInfo::underlineThickness) 18 | ; 19 | } 20 | -------------------------------------------------------------------------------- /src/em/GlyphInfo_em.cpp: -------------------------------------------------------------------------------- 1 | #include "GlyphInfo.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_GlyphInfo) { 8 | value_object("GlyphInfo") 9 | .field("id", &cobbletext::GlyphInfo::id) 10 | .field("image", &cobbletext::GlyphInfo::image) 11 | .field("imageWidth", &cobbletext::GlyphInfo::imageWidth) 12 | .field("imageHeight", &cobbletext::GlyphInfo::imageHeight) 13 | .field("imageOffsetX", &cobbletext::GlyphInfo::imageOffsetX) 14 | .field("imageOffsetY", &cobbletext::GlyphInfo::imageOffsetY) 15 | ; 16 | register_vector("vector_uint8_t"); 17 | } 18 | -------------------------------------------------------------------------------- /src/em/Library_em.cpp: -------------------------------------------------------------------------------- 1 | #include "Library.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | namespace cobbletext::em { 8 | 9 | FontID loadFontBytesWrapper(Library & library, const std::string bytes, 10 | int32_t faceIndex) { 11 | return library.loadFontBytes( 12 | reinterpret_cast(bytes.data()), bytes.size(), 13 | faceIndex); 14 | } 15 | 16 | } 17 | 18 | EMSCRIPTEN_BINDINGS(cobbletext_Library) { 19 | class_("Library") 20 | .class_function("versionMajor", &cobbletext::Library::versionMajor) 21 | .class_function("versionMinor", &cobbletext::Library::versionMinor) 22 | .class_function("versionPatch", &cobbletext::Library::versionPatch) 23 | .class_function("version", &cobbletext::Library::version) 24 | 25 | // .constructor<>() 26 | // .smart_ptr>("Library") 27 | .smart_ptr_constructor("Library", &std::make_shared) 28 | 29 | .function("fallbackFont", &cobbletext::Library::fallbackFont) 30 | .function("loadFont", &cobbletext::Library::loadFont) 31 | .function("loadFontBytes", &cobbletext::em::loadFontBytesWrapper) 32 | .function("getFontInfo", &cobbletext::Library::getFontInfo) 33 | .function("getGlyphInfo", &cobbletext::Library::getGlyphInfo) 34 | .function("setFontAlternative", &cobbletext::Library::setFontAlternative) 35 | .function("getFontAlternative", &cobbletext::Library::getFontAlternative) 36 | ; 37 | } 38 | -------------------------------------------------------------------------------- /src/em/Math_em.cpp: -------------------------------------------------------------------------------- 1 | #include "Math.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_Math) { 8 | class_("Math") 9 | 10 | .class_function("alpha_blend_over_argb", &cobbletext::Math::alpha_blend_over_argb) 11 | .class_function("gamma_correction_argb", &cobbletext::Math::gamma_argb) 12 | ; 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/em/Output_em.cpp: -------------------------------------------------------------------------------- 1 | #include "OutputInfo.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_OutputInfo) { 8 | 9 | value_object("OutputInfo") 10 | .field("textWidth", &cobbletext::OutputInfo::textWidth) 11 | .field("textHeight", &cobbletext::OutputInfo::textHeight) 12 | ; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/em/ScriptDirection_em.cpp: -------------------------------------------------------------------------------- 1 | #include "ScriptDirection.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_ScriptDirection) { 8 | enum_("ScriptDirection") 9 | .value("NotSpecified", cobbletext::ScriptDirection::NotSpecified) 10 | .value("LTR", cobbletext::ScriptDirection::LTR) 11 | .value("RTL", cobbletext::ScriptDirection::RTL) 12 | ; 13 | } 14 | -------------------------------------------------------------------------------- /src/em/TextAlignment_em.cpp: -------------------------------------------------------------------------------- 1 | #include "TextAlignment.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_TextAlignment) { 8 | enum_("TextAlignment") 9 | .value("NotSpecified", cobbletext::TextAlignment::NotSpecified) 10 | .value("Start", cobbletext::TextAlignment::Start) 11 | .value("End", cobbletext::TextAlignment::End) 12 | .value("Left", cobbletext::TextAlignment::Left) 13 | .value("Right", cobbletext::TextAlignment::Right) 14 | .value("Center", cobbletext::TextAlignment::Center) 15 | ; 16 | } 17 | -------------------------------------------------------------------------------- /src/em/TileInfo_em.cpp: -------------------------------------------------------------------------------- 1 | #include "TileInfo.hpp" 2 | 3 | #include 4 | 5 | using namespace emscripten; 6 | 7 | EMSCRIPTEN_BINDINGS(cobbletext_TileInfo) { 8 | value_object("TileInfo") 9 | .field("glyphID", &cobbletext::TileInfo::glyphID) 10 | .field("atlasX", &cobbletext::TileInfo::atlasX) 11 | .field("atlasY", &cobbletext::TileInfo::atlasY) 12 | ; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/em/js/cobbletext_js.cpp: -------------------------------------------------------------------------------- 1 | #include "cobbletext.h" 2 | #include "cobbletext.hpp" 3 | 4 | #include 5 | 6 | EMSCRIPTEN_KEEPALIVE 7 | int main(int argc, char const *argv[]) 8 | { 9 | 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /src/internal/Codec.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/Codec.hpp" 2 | 3 | #include 4 | 5 | #include "internal/ICUError.hpp" 6 | 7 | namespace cobbletext::internal { 8 | 9 | Codec::Codec() { 10 | ICUError errorCode; 11 | 12 | utf8Converter.reset(ucnv_open("UTF-8", errorCode)); 13 | utf16LEConverter.reset(ucnv_open("UTF-16LE", errorCode)); 14 | } 15 | 16 | std::u16string_view Codec::utf8BytesToUTF16CodeUnits( 17 | const gsl::span & bytes) { 18 | return decodeWithConverter(utf8Converter.get(), bytes); 19 | } 20 | 21 | std::u16string_view Codec::utf16LEBytesToUTF16CodeUnits( 22 | const gsl::span & bytes) { 23 | return decodeWithConverter(utf16LEConverter.get(), bytes); 24 | } 25 | 26 | std::u16string_view Codec::decodeWithConverter(UConverter * converter, 27 | const gsl::span & bytes) { 28 | 29 | ICUError errorCode; 30 | auto bufferSize = bytes.size() * 2; 31 | auto buffer = new char16_t[bufferSize]; 32 | auto size = ucnv_toUChars(converter, buffer, bufferSize, 33 | (const char *) bytes.data(), bytes.size(), errorCode); 34 | 35 | return std::u16string_view(buffer, size); 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /src/internal/Codec.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | namespace cobbletext::internal { 12 | 13 | class UConverterDeleter { 14 | public: 15 | void operator()(UConverter * converter) const { 16 | ucnv_close(converter); 17 | } 18 | }; 19 | 20 | class Codec { 21 | std::unique_ptr utf8Converter; 22 | std::unique_ptr utf16LEConverter; 23 | 24 | public: 25 | Codec(); 26 | 27 | std::u16string_view utf8BytesToUTF16CodeUnits( 28 | const gsl::span & bytes); 29 | std::u16string_view utf16LEBytesToUTF16CodeUnits( 30 | const gsl::span & bytes); 31 | 32 | private: 33 | std::u16string_view decodeWithConverter(UConverter * converter, 34 | const gsl::span & bytes); 35 | }; 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/internal/ColorUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | class ColorUtil { 8 | public: 9 | 10 | static uint32_t makeARGB(uint8_t red, uint8_t green, uint8_t blue, 11 | uint8_t alpha); 12 | static uint8_t alphaARGB(uint32_t color); 13 | static uint8_t redARGB(uint32_t color); 14 | static uint8_t greenARGB(uint32_t color); 15 | static uint8_t blueARGB(uint32_t color); 16 | 17 | }; 18 | 19 | inline 20 | uint32_t ColorUtil::makeARGB(uint8_t red, uint8_t green, uint8_t blue, 21 | uint8_t alpha) { 22 | return (alpha << 24) | (red << 16) | (green << 8) | blue; 23 | } 24 | 25 | inline 26 | uint8_t ColorUtil::alphaARGB(uint32_t color) { 27 | return (color >> 24) & 0xff; 28 | } 29 | 30 | inline 31 | uint8_t ColorUtil::redARGB(uint32_t color) { 32 | return (color >> 16) & 0xff; 33 | } 34 | 35 | inline 36 | uint8_t ColorUtil::greenARGB(uint32_t color) { 37 | return (color >> 8) & 0xff; 38 | } 39 | 40 | inline 41 | uint8_t ColorUtil::blueARGB(uint32_t color) { 42 | return (color >> 0) & 0xff; 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/internal/Context.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "internal/FreeType.hpp" 6 | #include "internal/font/FontTable.hpp" 7 | #include "internal/font/GlyphTable.hpp" 8 | #include "internal/image/AtlasPacker.hpp" 9 | 10 | namespace cobbletext::internal { 11 | 12 | class Context { 13 | public: 14 | std::shared_ptr freeType; 15 | std::shared_ptr fontTable; 16 | std::shared_ptr glyphTable; 17 | std::shared_ptr atlasPacker; 18 | 19 | Context() : 20 | freeType(std::make_shared()), 21 | fontTable(std::make_shared(freeType)), 22 | glyphTable(std::make_shared(freeType, fontTable)), 23 | atlasPacker(std::make_shared(glyphTable)) {} 24 | }; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/internal/Debug.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef COBBLETEXT_DEBUG 8 | #include 9 | #define COBBLETEXT_DEBUG_PRINT(code) { std::cerr << code << std::endl; } 10 | #else 11 | #define COBBLETEXT_DEBUG_PRINT(code) 12 | #endif 13 | 14 | namespace cobbletext::internal { 15 | 16 | class Debug { 17 | public: 18 | 19 | [[noreturn]] static void abort(std::string & message) { 20 | std::cerr << message << std::endl; 21 | ::abort(); 22 | } 23 | 24 | [[noreturn]] static void abort(const char * message) { 25 | std::cerr << message << std::endl; 26 | ::abort(); 27 | } 28 | 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/internal/ExceptionUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifdef COBBLETEXT_USE_STACK_TRACE 4 | #include 5 | #include 6 | #endif 7 | 8 | namespace cobbletext::internal { 9 | 10 | #ifdef COBBLETEXT_USE_STACK_TRACE 11 | 12 | 13 | typedef boost::error_info traced; 14 | 15 | template 16 | [[noreturn]] void throw_with_trace(const E& e) { 17 | throw boost::enable_error_info(e) 18 | << traced(boost::stacktrace::stacktrace()); 19 | } 20 | 21 | using boost::get_error_info; 22 | 23 | 24 | #else 25 | 26 | 27 | #define throw_with_trace(x) { throw (x); } 28 | 29 | 30 | #endif 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/internal/FreeType.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/FreeType.hpp" 2 | 3 | #include "Exception.hpp" 4 | #include "internal/ExceptionUtil.hpp" 5 | #include "internal/Debug.hpp" 6 | 7 | namespace cobbletext::internal { 8 | 9 | void FreeTypeLibraryDeleter::operator()(FT_Library library) const { 10 | if (library != nullptr) { 11 | FT_Done_FreeType(library); 12 | } 13 | } 14 | 15 | FreeType::FreeType() { 16 | FT_Library pointer; 17 | auto errorCode = FT_Init_FreeType(&pointer); 18 | 19 | throwIfError(errorCode); 20 | 21 | library.reset(pointer); 22 | 23 | COBBLETEXT_DEBUG_PRINT("freetype initialized"); 24 | } 25 | 26 | LibraryError FreeType::makeError(FT_Error errorCode) { 27 | #if FREETYPE_MAJOR * 100 + FREETYPE_MINOR < 210 28 | return LibraryError("freetype error " + std::to_string(errorCode), errorCode); 29 | #else 30 | auto message = FT_Error_String(errorCode); 31 | 32 | if (message) { 33 | return LibraryError(FT_Error_String(errorCode), errorCode); 34 | } else { 35 | return LibraryError("freetype error " + std::to_string(errorCode), errorCode); 36 | } 37 | #endif 38 | } 39 | 40 | void FreeType::throwIfError(FT_Error errorCode) { 41 | if (errorCode) { 42 | throw_with_trace(FreeType::makeError(errorCode)); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/internal/FreeType.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include FT_FREETYPE_H 7 | 8 | #include "Exception.hpp" 9 | 10 | namespace cobbletext::internal { 11 | 12 | class FreeTypeLibraryDeleter { 13 | public: 14 | void operator()(FT_Library library) const; 15 | }; 16 | 17 | class FreeType { 18 | public: 19 | 20 | // FreeType has typedef `struct FT_LibraryRec_ *` as `FT_Library` 21 | // which is annoying 22 | std::unique_ptr library; 23 | 24 | FreeType(); 25 | 26 | static LibraryError makeError(FT_Error errorCode); 27 | static void throwIfError(FT_Error errorCode); 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/internal/ICUError.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/ICUError.hpp" 2 | 3 | #include "Exception.hpp" 4 | #include "internal/ExceptionUtil.hpp" 5 | 6 | namespace cobbletext::internal { 7 | 8 | void ICUError::handleFailure() const { 9 | throw_with_trace(LibraryError(u_errorName(errorCode), errorCode)); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/internal/ICUError.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | class ICUError : public icu::ErrorCode { 8 | void handleFailure() const; 9 | }; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/internal/RandomUtil.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/RandomUtil.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | std::size_t RandomUtil::seed = 0; 8 | 9 | 10 | std::size_t RandomUtil::getSeed() { 11 | while (seed == 0) { 12 | std::random_device random; 13 | seed = (random() << 16ull) | random(); 14 | } 15 | 16 | return seed; 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/internal/RandomUtil.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | class RandomUtil { 8 | public: 9 | 10 | private: 11 | static std::size_t seed; 12 | 13 | public: 14 | static std::size_t getSeed(); 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/internal/font/Font.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __EMSCRIPTEN__ 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | #include "internal/FreeType.hpp" 13 | 14 | namespace cobbletext::internal { 15 | 16 | class Font { 17 | public: 18 | FT_Face const freeTypeFace; 19 | hb_font_t * const harfBuzzFont; 20 | double fontSize = 0; // in points 21 | double bitmapScale = 0; // if non-zero, scale bitmap font to fit font size 22 | std::shared_ptr> fontData; 23 | 24 | Font(FT_Face freeTypeFace, hb_font_t * harfBuzzFont) : 25 | freeTypeFace(freeTypeFace), 26 | harfBuzzFont(harfBuzzFont) {}; 27 | 28 | }; 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/internal/font/FontTable.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/font/FontTable.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "Exception.hpp" 8 | #include "Resource.hpp" 9 | #include "internal/ExceptionUtil.hpp" 10 | 11 | namespace cobbletext::internal { 12 | 13 | FontTable::FontTable(std::shared_ptr freeType) : 14 | freeType(freeType), 15 | fallbackFont( 16 | loadBytes( 17 | gsl::span(Resource::ADOBE_NOTDEF, Resource::ADOBE_NOTDEF_SIZE)) 18 | ) {} 19 | 20 | FontTable::~FontTable() { 21 | for (auto const & item : fonts) { 22 | FT_Done_Face(item.second.freeTypeFace); 23 | hb_font_destroy(item.second.harfBuzzFont); 24 | } 25 | } 26 | 27 | FontID FontTable::load(const char * path, int32_t faceIndex) { 28 | auto library = freeType->library.get(); 29 | FT_Face fontFace; 30 | 31 | auto errorCode = FT_New_Face(library, path, faceIndex, &fontFace); 32 | 33 | FreeType::throwIfError(errorCode); 34 | 35 | auto hbFont = hb_ft_font_create_referenced(fontFace); 36 | auto id = getFreeID(); 37 | 38 | fonts.emplace(id, Font(fontFace, hbFont)); 39 | 40 | return id; 41 | } 42 | 43 | FontID FontTable::loadBytes(const gsl::span & data, 44 | int32_t faceIndex) { 45 | auto library = freeType->library.get(); 46 | FT_Face fontFace; 47 | 48 | auto dataVector = std::make_shared>( 49 | data.data(), data.data() + data.size()); 50 | 51 | auto errorCode = FT_New_Memory_Face(library, 52 | reinterpret_cast(dataVector->data()), 53 | dataVector->size(), faceIndex, &fontFace); 54 | 55 | FreeType::throwIfError(errorCode); 56 | 57 | auto hbFont = hb_ft_font_create_referenced(fontFace); 58 | auto id = getFreeID(); 59 | 60 | fonts.emplace(id, Font(fontFace, hbFont)); 61 | fonts.at(id).fontData = dataVector; 62 | 63 | return id; 64 | } 65 | 66 | FontID FontTable::getFreeID() { 67 | auto id = idCounter; 68 | 69 | if (id == std::numeric_limits::max()) { 70 | throw_with_trace(RuntimeError("max font id")); 71 | } 72 | 73 | idCounter++; 74 | return id; 75 | } 76 | 77 | bool FontTable::hasFont(FontID fontID) { 78 | return fonts.find(fontID) != fonts.end(); 79 | } 80 | 81 | Font & FontTable::getFont(FontID fontID) { 82 | try { 83 | return fonts.at(fontID); 84 | } catch (const std::out_of_range & exception) { 85 | throw_with_trace( 86 | LogicError("font not in table: " + std::to_string(fontID))); 87 | } 88 | } 89 | 90 | Font & FontTable::getFontWithFallback(FontID fontID) { 91 | if (fonts.find(fontID) != fonts.end()) { 92 | return fonts.at(fontID); 93 | } 94 | 95 | return getFont(fallbackFont); 96 | } 97 | 98 | FontInfo FontTable::getFontInfo(FontID fontID) { 99 | auto & font = getFont(fontID); 100 | 101 | FontInfo fontInfo; 102 | 103 | fontInfo.id = fontID; 104 | 105 | if (font.freeTypeFace->family_name) { 106 | fontInfo.familyName = font.freeTypeFace->family_name; 107 | } 108 | 109 | if (font.freeTypeFace->style_name) { 110 | fontInfo.styleName = font.freeTypeFace->style_name; 111 | } 112 | 113 | fontInfo.unitsPerEM = font.freeTypeFace->units_per_EM; 114 | fontInfo.ascender = font.freeTypeFace->ascender; 115 | fontInfo.descender = font.freeTypeFace->descender; 116 | fontInfo.height = font.freeTypeFace->height; 117 | fontInfo.underlinePosition = font.freeTypeFace->underline_position; 118 | fontInfo.underlineThickness = font.freeTypeFace->underline_thickness; 119 | 120 | return fontInfo; 121 | } 122 | 123 | bool FontTable::setFontSize(FontID fontID, double fontSize) { 124 | auto & font = getFont(fontID); 125 | 126 | if (font.fontSize == fontSize) { 127 | return false; 128 | } 129 | 130 | if (FT_IS_SCALABLE(font.freeTypeFace)) { 131 | auto errorCode = FT_Set_Char_Size(font.freeTypeFace, 132 | 0, fontSize * 64, 0, 0); 133 | 134 | FreeType::throwIfError(errorCode); 135 | } else { 136 | FT_Int sizeCount = font.freeTypeFace->num_fixed_sizes; 137 | FT_Int selectedIndex = 0; 138 | int16_t selectedSize = 0; 139 | 140 | for (FT_Int index = 0; index < sizeCount; index++) { 141 | auto strikeInfo = &font.freeTypeFace->available_sizes[index]; 142 | int16_t candidateSize = strikeInfo->height; 143 | 144 | if (fontSize > candidateSize && candidateSize > selectedSize) { 145 | selectedIndex = index; 146 | selectedSize = candidateSize; 147 | } 148 | } 149 | 150 | auto errorCode = FT_Select_Size(font.freeTypeFace, selectedIndex); 151 | 152 | FreeType::throwIfError(errorCode); 153 | 154 | auto strikeInfo = &font.freeTypeFace->available_sizes[selectedIndex]; 155 | 156 | if (strikeInfo->height) { 157 | font.bitmapScale = fontSize / strikeInfo->height; 158 | } 159 | } 160 | 161 | font.fontSize = fontSize; 162 | 163 | return true; 164 | } 165 | 166 | int32_t FontTable::fontUnitsToPixels(FontID fontID, int32_t value) { 167 | auto & font = getFont(fontID); 168 | 169 | return FT_MulFix(value, font.freeTypeFace->size->metrics.y_scale) / 64; 170 | } 171 | 172 | 173 | void FontTable::setFontAlternative(FontID fontID, FontID fallbackFontID) { 174 | alternativeMap[fontID] = fallbackFontID; 175 | } 176 | 177 | FontID FontTable::getFontAlternative(FontID fontID) { 178 | const auto & iter = alternativeMap.find(fontID); 179 | 180 | if (iter != alternativeMap.end()) { 181 | return iter->second; 182 | } else { 183 | return 0; 184 | } 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/internal/font/FontTable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #ifdef __EMSCRIPTEN__ 10 | #include 11 | #else 12 | #include 13 | #endif 14 | 15 | #include "FontInfo.hpp" 16 | #include "internal/font/Font.hpp" 17 | #include "internal/FreeType.hpp" 18 | 19 | namespace cobbletext::internal { 20 | 21 | class FontTable { 22 | private: 23 | unsigned long int idCounter = 1; 24 | std::unordered_map fonts; 25 | std::unordered_map alternativeMap; 26 | std::shared_ptr freeType; 27 | 28 | public: 29 | const FontID fallbackFont; 30 | 31 | explicit FontTable(std::shared_ptr freeType); 32 | ~FontTable(); 33 | 34 | FontID load(const char * path, int32_t faceIndex = 0); 35 | FontID loadBytes(const gsl::span & data, 36 | int32_t faceIndex = 0); 37 | 38 | bool hasFont(FontID fontID); 39 | 40 | Font & getFont(FontID fontID); 41 | Font & getFontWithFallback(FontID fontID); 42 | FontInfo getFontInfo(FontID fontID); 43 | 44 | bool setFontSize(FontID fontID, double fontSize); 45 | 46 | int32_t fontUnitsToPixels(FontID fontID, int32_t value); 47 | 48 | void setFontAlternative(FontID fontID, FontID fallbackFontID); 49 | 50 | FontID getFontAlternative(FontID fontID); 51 | 52 | private: 53 | FontID getFreeID(); 54 | }; 55 | 56 | } 57 | -------------------------------------------------------------------------------- /src/internal/font/Glyph.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.hpp" 6 | #include "internal/font/GlyphKey.hpp" 7 | 8 | namespace cobbletext::internal { 9 | 10 | class Glyph { 11 | public: 12 | GlyphID id; 13 | bool hasImage = false; 14 | std::vector image; 15 | uint32_t imageWidth = 0; 16 | uint32_t imageHeight = 0; 17 | int32_t imageOffsetX = 0; 18 | int32_t imageOffsetY = 0; 19 | 20 | unsigned int referenceCount = 0; 21 | 22 | explicit Glyph(GlyphID id) : id(id) {} 23 | }; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/internal/font/GlyphKey.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/font/GlyphKey.hpp" 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include "internal/RandomUtil.hpp" 8 | 9 | namespace cobbletext::internal { 10 | 11 | bool GlyphKey::operator==(const GlyphKey & other) const { 12 | return fontFace == other.fontFace 13 | && fontSize == other.fontSize 14 | && index == other.index; 15 | } 16 | 17 | std::size_t GlyphKeyHasher::operator()(GlyphKey const & glyphKey) const { 18 | std::size_t hash = RandomUtil::getSeed(); 19 | boost::hash_combine(hash, std::hash{}(glyphKey.fontFace)); 20 | boost::hash_combine(hash, std::hash{}(glyphKey.fontSize)); 21 | boost::hash_combine(hash, std::hash{}(glyphKey.index)); 22 | 23 | return hash; 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /src/internal/font/GlyphKey.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include "stdint.h" 6 | 7 | #include "common.hpp" 8 | 9 | namespace cobbletext::internal { 10 | 11 | class GlyphKey { 12 | public: 13 | const FontID fontFace; 14 | const double fontSize; 15 | const uint32_t index; 16 | 17 | GlyphKey(FontID fontFace, double fontSize, uint32_t index) : 18 | fontFace(fontFace), fontSize(fontSize), index(index) {} 19 | 20 | bool operator==(const GlyphKey & other) const; 21 | }; 22 | 23 | class GlyphKeyHasher { 24 | public: 25 | std::size_t operator()(const GlyphKey & glyphKey) const; 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/internal/font/GlyphTable.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/font/GlyphTable.hpp" 2 | 3 | #include 4 | 5 | #include "internal/FreeType.hpp" 6 | #include "internal/Debug.hpp" 7 | #include "Exception.hpp" 8 | #include "internal/ExceptionUtil.hpp" 9 | #include "internal/image/ImageResize.hpp" 10 | 11 | namespace cobbletext::internal { 12 | 13 | GlyphTable::GlyphTable(std::shared_ptr freeType, 14 | std::shared_ptr fontTable) : 15 | freeType(freeType), 16 | fontTable(fontTable) { 17 | 18 | FT_Bitmap_Init(&tempBitmap); 19 | } 20 | 21 | GlyphTable::~GlyphTable() { 22 | FT_Bitmap_Done(freeType->library.get(), &tempBitmap); 23 | } 24 | 25 | bool GlyphTable::registerGlyph(const GlyphKey & glyphKey) { 26 | const auto & result = glyphs.find(glyphKey); 27 | 28 | if (result != glyphs.end()) { 29 | return false; 30 | } 31 | 32 | GlyphID id = getFreeID(); 33 | idMap.emplace(id, glyphKey); 34 | 35 | Glyph glyph(id); 36 | glyphs.emplace(glyphKey, glyph); 37 | 38 | return true; 39 | } 40 | 41 | bool GlyphTable::removeGlyph(const GlyphKey & glyphKey) { 42 | const auto & result = glyphs.find(glyphKey); 43 | 44 | if (result == glyphs.end()) { 45 | return false; 46 | } 47 | 48 | freeIDList.push_back(result->second.id); 49 | 50 | idMap.erase(result->second.id); 51 | glyphs.erase(result); 52 | 53 | return true; 54 | } 55 | 56 | bool GlyphTable::isRegistered(const GlyphKey & glyphKey) { 57 | const auto & result = glyphs.find(glyphKey); 58 | 59 | return result != glyphs.end(); 60 | } 61 | 62 | void GlyphTable::incrementReference(const GlyphKey & glyphKey) { 63 | auto & result = glyphs.at(glyphKey); 64 | result.referenceCount += 1; 65 | } 66 | 67 | void GlyphTable::decrementReference(const GlyphKey & glyphKey) { 68 | auto & result = glyphs.at(glyphKey); 69 | result.referenceCount -= 1; 70 | 71 | if (result.referenceCount == 0) { 72 | removeGlyph(glyphKey); 73 | } 74 | } 75 | 76 | const GlyphKey & GlyphTable::idToKey(GlyphID glyphID) { 77 | return idMap.at(glyphID); 78 | } 79 | 80 | GlyphID GlyphTable::keyToID(const GlyphKey & glyphKey) { 81 | return glyphs.at(glyphKey).id; 82 | } 83 | 84 | Glyph & GlyphTable::getGlyph(const GlyphKey & glyphKey) { 85 | auto & glyph = glyphs.at(glyphKey); 86 | 87 | return glyph; 88 | } 89 | 90 | void GlyphTable::rasterize(const GlyphKey & glyphKey) { 91 | auto & glyph = glyphs.at(glyphKey); 92 | 93 | if (glyph.hasImage) { 94 | return; 95 | } 96 | 97 | auto font = fontTable->getFont(glyphKey.fontFace); 98 | 99 | fontTable->setFontSize(glyphKey.fontFace, 100 | glyphKey.fontSize); 101 | 102 | auto errorCode = FT_Load_Glyph(font.freeTypeFace, 103 | glyphKey.index, FT_LOAD_DEFAULT); 104 | 105 | FreeType::throwIfError(errorCode); 106 | 107 | if (font.freeTypeFace->glyph->format != FT_GLYPH_FORMAT_BITMAP) { 108 | errorCode = FT_Render_Glyph(font.freeTypeFace->glyph, 109 | FT_RENDER_MODE_NORMAL); 110 | 111 | FreeType::throwIfError(errorCode); 112 | } 113 | 114 | auto & bitmap = font.freeTypeFace->glyph->bitmap; 115 | 116 | if (!font.bitmapScale) { 117 | // vector font 118 | glyph.imageWidth = bitmap.width; 119 | glyph.imageHeight = bitmap.rows; 120 | 121 | auto buffer = static_cast(bitmap.buffer); 122 | 123 | glyph.image = std::vector(buffer, 124 | buffer + bitmap.width * bitmap.rows); 125 | 126 | glyph.imageOffsetX = font.freeTypeFace->glyph->bitmap_left; 127 | glyph.imageOffsetY = -font.freeTypeFace->glyph->bitmap_top; 128 | } else { 129 | scaleBitmapGlyph(bitmap, glyph, font); 130 | } 131 | 132 | glyph.hasImage = true; 133 | } 134 | 135 | void GlyphTable::scaleBitmapGlyph(FT_Bitmap & bitmap, Glyph & glyph, Font & font) { 136 | switch (bitmap.pixel_mode) { 137 | case FT_PIXEL_MODE_MONO: 138 | scaleBitmapMono(bitmap, glyph, font); 139 | break; 140 | case FT_PIXEL_MODE_GRAY: 141 | scaleBitmapGrayscale(bitmap, glyph, font); 142 | break; 143 | default: 144 | COBBLETEXT_DEBUG_PRINT( 145 | "unknown bitmap pixel_mode=" << bitmap.pixel_mode); 146 | } 147 | } 148 | 149 | void GlyphTable::scaleBitmapMono(FT_Bitmap & bitmap, Glyph & glyph, 150 | Font & font) { 151 | auto errorCode = FT_Bitmap_Convert(freeType->library.get(), 152 | &bitmap, &tempBitmap, 1); 153 | 154 | FreeType::throwIfError(errorCode); 155 | 156 | if (tempBitmap.num_grays != 256) { 157 | // FT_Bitmap_Convert doesn't ramp the values 158 | size_t size = bitmap.width * bitmap.rows; 159 | 160 | for (size_t index = 0; index < size; index++) { 161 | tempBitmap.buffer[index] = tempBitmap.buffer[index] ? 255 : 0; 162 | } 163 | } 164 | 165 | scaleBitmapGrayscale(tempBitmap, glyph, font); 166 | } 167 | 168 | void GlyphTable::scaleBitmapGrayscale(FT_Bitmap & bitmap, Glyph & glyph, 169 | Font & font) { 170 | uint32_t scaledWidth = bitmap.width * font.bitmapScale; 171 | uint32_t scaledHeight = bitmap.rows * font.bitmapScale; 172 | 173 | glyph.imageWidth = scaledWidth; 174 | glyph.imageHeight = scaledHeight; 175 | 176 | glyph.image = std::vector(scaledWidth * scaledHeight, 0); 177 | 178 | auto buffer = static_cast(bitmap.buffer); 179 | 180 | stbir_resize_uint8_linear(buffer, bitmap.width, bitmap.rows, 0, 181 | reinterpret_cast(glyph.image.data()), 182 | scaledWidth, scaledHeight, 0, static_cast(1)); 183 | 184 | glyph.imageOffsetX = font.freeTypeFace->glyph->bitmap_left 185 | * font.bitmapScale; 186 | glyph.imageOffsetY = -font.freeTypeFace->glyph->bitmap_top 187 | * font.bitmapScale; 188 | } 189 | 190 | GlyphInfo GlyphTable::getGlyphInfo(GlyphID glyphID) { 191 | if (idMap.find(glyphID) == idMap.end()) { 192 | throw_with_trace( 193 | LogicError("glyph not in table: " + std::to_string(glyphID))); 194 | } 195 | 196 | auto & glyphKey = idToKey(glyphID); 197 | auto & glyph = glyphs.at(glyphKey); 198 | GlyphInfo glyphInfo; 199 | 200 | rasterize(glyphKey); 201 | 202 | glyphInfo.id = glyph.id; 203 | glyphInfo.image = glyph.image; 204 | glyphInfo.imageHeight = glyph.imageHeight; 205 | glyphInfo.imageWidth = glyph.imageWidth; 206 | glyphInfo.imageOffsetX = glyph.imageOffsetX; 207 | glyphInfo.imageOffsetY = glyph.imageOffsetY; 208 | 209 | return glyphInfo; 210 | } 211 | 212 | GlyphID GlyphTable::getFreeID() { 213 | if (!freeIDList.empty()) { 214 | GlyphID id = freeIDList.back(); 215 | freeIDList.pop_back(); 216 | return id; 217 | } 218 | 219 | GlyphID id = idCounter; 220 | 221 | if (id == std::numeric_limits::max()) { 222 | throw_with_trace(RuntimeError("max glyph id")); 223 | } 224 | 225 | idCounter++; 226 | return id; 227 | } 228 | 229 | } 230 | -------------------------------------------------------------------------------- /src/internal/font/GlyphTable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "internal/FreeType.hpp" 8 | #include FT_BITMAP_H 9 | 10 | #include "internal/font/GlyphKey.hpp" 11 | #include "internal/font/Glyph.hpp" 12 | #include "internal/font/FontTable.hpp" 13 | #include "GlyphInfo.hpp" 14 | 15 | namespace cobbletext::internal { 16 | 17 | class GlyphTable { 18 | private: 19 | std::shared_ptr freeType; 20 | std::shared_ptr fontTable; 21 | unsigned long int idCounter = 1; 22 | 23 | std::unordered_map idMap; 24 | std::unordered_map glyphs; 25 | 26 | FT_Bitmap tempBitmap; 27 | 28 | std::vector freeIDList; 29 | 30 | public: 31 | GlyphTable(std::shared_ptr freeType, std::shared_ptr fontTable); 32 | ~GlyphTable(); 33 | 34 | bool registerGlyph(const GlyphKey & glyphKey); 35 | bool removeGlyph(const GlyphKey & glyphKey); 36 | bool isRegistered(const GlyphKey & glyphKey); 37 | 38 | void incrementReference(const GlyphKey & glyphKey); 39 | void decrementReference(const GlyphKey & glyphKey); 40 | 41 | const GlyphKey & idToKey(GlyphID glyphID); 42 | 43 | GlyphID keyToID(const GlyphKey & glyphKey); 44 | 45 | Glyph & getGlyph(const GlyphKey & glyphKey); 46 | 47 | void rasterize(const GlyphKey & glyphKey); 48 | 49 | GlyphInfo getGlyphInfo(GlyphID glyphID); 50 | 51 | private: 52 | GlyphID getFreeID(); 53 | 54 | void scaleBitmapGlyph(FT_Bitmap & bitmap, Glyph & glyph, Font & font); 55 | void scaleBitmapMono(FT_Bitmap & bitmap, Glyph & glyph, Font & font); 56 | void scaleBitmapGrayscale(FT_Bitmap & bitmap, Glyph & glyph, Font & font); 57 | }; 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/internal/image/AtlasPacker.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/image/AtlasPacker.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "internal/Debug.hpp" 7 | 8 | namespace cobbletext::internal { 9 | 10 | AtlasPacker::AtlasPacker(std::shared_ptr glyphTable) : 11 | glyphTable(glyphTable) {} 12 | 13 | bool AtlasPacker::glyphSortComparator( 14 | const std::unique_ptr & slot1, 15 | const std::unique_ptr & slot2) { 16 | return slot1->glyph.imageHeight > slot2->glyph.imageHeight; 17 | } 18 | 19 | bool AtlasPacker::pack(std::vector & tiles, uint32_t width, uint32_t height) { 20 | // This packs glyphs into rows starting with largest height to smallest. 21 | 22 | std::vector> slots; 23 | 24 | for (auto & tile : tiles) { 25 | const auto & key = glyphTable->idToKey(tile.glyphID); 26 | const auto & glyph = glyphTable->getGlyph(key); 27 | slots.push_back(std::make_unique(tile, glyph)); 28 | } 29 | 30 | std::sort(slots.begin(), slots.end(), glyphSortComparator); 31 | 32 | uint32_t currentX = 0; 33 | uint32_t currentY = 0; 34 | uint32_t currentRowHeight = 0; 35 | 36 | for (auto & slot : slots) { 37 | if (currentX + slot->glyph.imageWidth > width) { 38 | if (currentRowHeight == 0) { 39 | // The glyph is too wide. 40 | return true; 41 | } 42 | 43 | // Move to a new row. 44 | currentX = 0; 45 | currentY += currentRowHeight; 46 | currentRowHeight = 0; 47 | } 48 | 49 | slot->tileInfo.atlasX = currentX; 50 | slot->tileInfo.atlasY = currentY; 51 | currentRowHeight = std::max(currentRowHeight, slot->glyph.imageHeight); 52 | currentX += slot->glyph.imageWidth; 53 | 54 | if (currentY + currentRowHeight > height) { 55 | return true; 56 | } 57 | } 58 | 59 | COBBLETEXT_DEBUG_PRINT("Packed atlas. Pen=" 60 | << currentX << " " << currentY); 61 | 62 | return false; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/internal/image/AtlasPacker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "TileInfo.hpp" 8 | #include "internal/font/Glyph.hpp" 9 | #include "internal/font/GlyphTable.hpp" 10 | 11 | namespace cobbletext::internal { 12 | 13 | class AtlasPackerSlot { 14 | public: 15 | TileInfo & tileInfo; 16 | const Glyph & glyph; 17 | 18 | AtlasPackerSlot(TileInfo & tileInfo, const Glyph & glyph) : 19 | tileInfo(tileInfo), glyph(glyph) {} 20 | }; 21 | 22 | class AtlasPacker { 23 | std::shared_ptr glyphTable; 24 | 25 | public: 26 | explicit AtlasPacker(std::shared_ptr glyphTable); 27 | 28 | static bool glyphSortComparator( 29 | const std::unique_ptr & slot1, 30 | const std::unique_ptr & slot2); 31 | 32 | bool pack(std::vector & tiles, uint32_t width, uint32_t height); 33 | 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/internal/image/ImageResize.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #define STB_IMAGE_RESIZE_IMPLEMENTATION 5 | #define STBIR_DEFAULT_FILTER_UPSAMPLE STBIR_FILTER_BOX 6 | #define STBIR_DEFAULT_FILTER_DOWNSAMPLE STBIR_FILTER_TRIANGLE 7 | #include 8 | -------------------------------------------------------------------------------- /src/internal/input/InlineObject.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | class InlineObject { 8 | public: 9 | InlineObject(int id, uint32_t pixelWidth, uint32_t pixelHeight) 10 | : id(id), pixelWidth(pixelWidth), pixelHeight(pixelHeight) {} 11 | 12 | int id; 13 | uint32_t pixelWidth; 14 | uint32_t pixelHeight; 15 | }; 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/internal/input/StringIndexer.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/input/StringIndexer.hpp" 2 | 3 | #include 4 | 5 | namespace cobbletext::internal { 6 | 7 | StringIndexer::StringIndexer() : 8 | iterator(icu::UnicodeString()) {} 9 | 10 | StringIndexer::StringIndexer(std::shared_ptr textBuffer) : 11 | textBuffer(textBuffer), 12 | iterator(*textBuffer), 13 | currentIndex32(0) { 14 | 15 | iterator.first32(); 16 | } 17 | 18 | void StringIndexer::setTextBuffer( 19 | std::shared_ptr textBuffer) { 20 | this->textBuffer = textBuffer; 21 | } 22 | 23 | void StringIndexer::reset() { 24 | assert(textBuffer); 25 | iterator.setText(*textBuffer); 26 | currentIndex32 = 0; 27 | } 28 | 29 | int32_t StringIndexer::codePointToCodeUnit(int32_t index32) { 30 | while (currentIndex32 < index32) { 31 | auto codeUnit = iterator.next32PostInc(); 32 | 33 | if (codeUnit == icu::StringCharacterIterator::DONE) { 34 | break; 35 | } 36 | 37 | currentIndex32 += 1; 38 | } 39 | 40 | while (currentIndex32 > index32) { 41 | auto codeUnit = iterator.previous32(); 42 | 43 | if (codeUnit == icu::StringCharacterIterator::DONE) { 44 | break; 45 | } 46 | 47 | currentIndex32 -= 1; 48 | } 49 | 50 | return iterator.getIndex(); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /src/internal/input/StringIndexer.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "cobbletext/macros.h" 11 | 12 | namespace cobbletext::internal { 13 | 14 | class COBBLETEXT_CPP_API StringIndexer { 15 | std::shared_ptr textBuffer; 16 | icu::StringCharacterIterator iterator; 17 | int32_t currentIndex32; 18 | 19 | public: 20 | StringIndexer(); 21 | explicit StringIndexer(std::shared_ptr textBuffer); 22 | 23 | void setTextBuffer(std::shared_ptr textBuffer); 24 | void reset(); 25 | int32_t codePointToCodeUnit(int32_t index32); 26 | }; 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/internal/input/TextFormat.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "common.hpp" 6 | #include "ScriptDirection.hpp" 7 | 8 | namespace cobbletext::internal { 9 | 10 | class TextFormat { 11 | public: 12 | FontID fontFace = 0; 13 | double fontSize = 12; 14 | std::string language; 15 | std::string script; 16 | ScriptDirection scriptDirection = ScriptDirection::NotSpecified; 17 | CustomPropertyID customProperty = 0; 18 | }; 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/internal/input/TextRun.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/input/TextRun.hpp" 2 | 3 | namespace cobbletext::internal { 4 | 5 | std::ostream & operator<<(std::ostream & stream, const TextRun & textRun) { 6 | #ifdef COBBLETEXT_DEBUG 7 | std::string text; 8 | textRun.text.toUTF8String(text); 9 | #endif 10 | 11 | stream 12 | << "[TextRun " 13 | << textRun.textIndex << ":" << textRun.textLength << " " 14 | #ifdef COBBLETEXT_DEBUG 15 | << "text='" << text << "' " 16 | #endif 17 | << "lang=" << textRun.textFormat.language << " " 18 | << "script=" << textRun.textFormat.script << " " 19 | << "direction=" << 20 | static_cast(textRun.textFormat.scriptDirection) << " " 21 | << "font=" << textRun.textFormat.fontFace << " " 22 | << "fontSize=" << textRun.textFormat.fontSize << " " 23 | << "customProperty=" << textRun.textFormat.customProperty << " "; 24 | 25 | if (textRun.inlineObject) { 26 | stream << "inline object"; 27 | } 28 | 29 | stream << "]"; 30 | 31 | return stream; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/internal/input/TextRun.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef COBBLETEXT_DEBUG 7 | #include 8 | #include 9 | #endif 10 | 11 | #include "internal/input/TextFormat.hpp" 12 | #include "internal/input/InlineObject.hpp" 13 | 14 | namespace cobbletext::internal { 15 | 16 | class TextRun { 17 | public: 18 | // These values are in code units to the text buffer, not code points! 19 | int32_t textIndex; 20 | int32_t textLength; 21 | 22 | TextFormat textFormat; 23 | 24 | std::optional inlineObject; 25 | 26 | #ifdef COBBLETEXT_DEBUG 27 | icu::UnicodeString text; 28 | #endif 29 | 30 | private: 31 | friend std::ostream & operator<<(std::ostream & stream, 32 | const TextRun & textRun); 33 | }; 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/internal/input/TextSource.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "internal/input/TextSource.hpp" 3 | 4 | #include "internal/ICUError.hpp" 5 | 6 | namespace cobbletext::internal { 7 | 8 | TextSource::TextSource() : 9 | textBuffer(std::make_shared()) { 10 | } 11 | 12 | void TextSource::clear() { 13 | runs.clear(); 14 | textBuffer->remove(); 15 | } 16 | 17 | void TextSource::addText(icu::UnicodeString text, TextFormat textFormat) { 18 | TextRun run; 19 | 20 | run.textFormat = textFormat; 21 | run.textIndex = textBuffer->length(); 22 | textBuffer->append(text); 23 | run.textLength = textBuffer->length() - run.textIndex; 24 | 25 | #ifdef COBBLETEXT_DEBUG 26 | run.text = text; 27 | #endif 28 | 29 | runs.push_back(run); 30 | } 31 | 32 | void TextSource::addInlineObject(int id, uint32_t pixelWidth, 33 | uint32_t pixelHeight, TextFormat textFormat) { 34 | TextRun run; 35 | 36 | run.textFormat = textFormat; 37 | 38 | run.inlineObject.emplace(id, pixelWidth, pixelHeight); 39 | 40 | run.textIndex = textBuffer->length(); 41 | textBuffer->append(static_cast(OBJECT_REPLACEMENT_CODE_POINT)); 42 | run.textLength = textBuffer->length() - run.textIndex; 43 | 44 | runs.push_back(run); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/internal/input/TextSource.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | #include "internal/input/TextFormat.hpp" 10 | #include "internal/input/InlineObject.hpp" 11 | #include "ScriptDirection.hpp" 12 | #include "internal/input/TextRun.hpp" 13 | 14 | namespace cobbletext::internal { 15 | 16 | class TextSource { 17 | static constexpr char32_t OBJECT_REPLACEMENT_CODE_POINT = 0xFFFC; 18 | 19 | public: 20 | icu::Locale locale; 21 | std::vector runs; 22 | std::shared_ptr textBuffer; 23 | 24 | public: 25 | TextSource(); 26 | 27 | void clear(); 28 | void addText(icu::UnicodeString text, TextFormat textFormat); 29 | void addInlineObject(int id, uint32_t pixelWidth, uint32_t pixelHeight, 30 | TextFormat textFormat); 31 | }; 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/internal/layout/InternalTextRun.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/layout/InternalTextRun.hpp" 2 | 3 | namespace cobbletext::internal { 4 | 5 | InternalTextRun::InternalTextRun(const TextRun & source) : 6 | InternalTextRun(source, source.textIndex, source.textLength) { 7 | } 8 | 9 | InternalTextRun::InternalTextRun(const TextRun & source, int32_t textIndex, 10 | int32_t textLength) : 11 | source(source), 12 | textIndex(textIndex), 13 | textLength(textLength) { 14 | } 15 | 16 | std::ostream & operator<<(std::ostream & stream, const InternalTextRun & run) { 17 | char tagBuffer[5] = {0}; 18 | hb_tag_to_string(hb_script_to_iso15924_tag(run.script), tagBuffer); 19 | const char * language = hb_language_to_string(run.language); 20 | 21 | #ifdef COBBLETEXT_DEBUG 22 | std::string text; 23 | auto subString = run.source.text.tempSubString( 24 | run.textIndex - run.source.textIndex, run.textLength); 25 | subString.toUTF8String(text); 26 | #endif 27 | 28 | stream 29 | << "[InternalTextRun " 30 | << run.textIndex << ":" << run.textLength << " " 31 | #ifdef COBBLETEXT_DEBUG 32 | << "text='" << text << "' " 33 | #endif 34 | << "lang=" << (language != nullptr ? language : "(no lang)") << " " 35 | << "script=" << tagBuffer << " " 36 | << "direction=" << hb_direction_to_string(run.direction) 37 | << "]"; 38 | 39 | return stream; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/internal/layout/InternalTextRun.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #ifdef __EMSCRIPTEN__ 7 | #include 8 | #else 9 | #include 10 | #endif 11 | 12 | 13 | #include "internal/input/TextRun.hpp" 14 | 15 | namespace cobbletext::internal { 16 | 17 | class InternalTextRun { 18 | public: 19 | const TextRun & source; 20 | 21 | // These values are in code units to the text buffer, not code points! 22 | int32_t textIndex; 23 | int32_t textLength; 24 | 25 | hb_language_t language = HB_LANGUAGE_INVALID; 26 | hb_script_t script = HB_SCRIPT_UNKNOWN; 27 | hb_direction_t direction = HB_DIRECTION_INVALID; 28 | 29 | explicit InternalTextRun(const TextRun & source); 30 | explicit InternalTextRun(const TextRun & source, int32_t textIndex, int32_t textLength); 31 | 32 | private: 33 | friend std::ostream & operator<<(std::ostream & stream, const InternalTextRun & run); 34 | }; 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/internal/layout/LayoutEngine.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #ifdef __EMSCRIPTEN__ 9 | #include 10 | #else 11 | #include 12 | #endif 13 | 14 | 15 | #include "AdvanceInfo.hpp" 16 | #include "internal/Context.hpp" 17 | #include "internal/input/TextSource.hpp" 18 | #include "internal/layout/InternalTextRun.hpp" 19 | #include "internal/layout/LineBreaker.hpp" 20 | #include "internal/layout/Shaper.hpp" 21 | #include "internal/layout/ShapeResult.hpp" 22 | #include "internal/table/BidiTable.hpp" 23 | #include "internal/table/ScriptTable.hpp" 24 | #include "TextAlignment.hpp" 25 | #include "TileInfo.hpp" 26 | 27 | namespace cobbletext::internal { 28 | 29 | class LayoutEngine { 30 | BidiTable bidiTable; 31 | ScriptTable scriptTable; 32 | Shaper shaper; 33 | LineBreaker lineBreaker; 34 | 35 | std::shared_ptr context; 36 | std::shared_ptr textSource; 37 | 38 | hb_language_t defaultLanguageHB = HB_LANGUAGE_INVALID; 39 | ScriptDirection defaultDirection = ScriptDirection::NotSpecified; 40 | hb_direction_t defaultDirectionHB = HB_DIRECTION_INVALID; 41 | 42 | std::vector internalRuns; 43 | std::shared_ptr> shapeResults; 44 | 45 | std::unordered_set glyphs; 46 | std::vector tiles_; 47 | std::vector advances_; 48 | 49 | uint32_t textWidth_; 50 | uint32_t textHeight_; 51 | 52 | bool tilesValid_ = true; 53 | 54 | uint32_t previousTextIndex; 55 | 56 | public: 57 | uint32_t lineLength = 0; 58 | TextAlignment textAlignment = TextAlignment::NotSpecified; 59 | 60 | std::vector & tiles(); 61 | std::vector & advances(); 62 | 63 | uint32_t textWidth(); 64 | uint32_t textHeight(); 65 | 66 | bool tilesValid(); 67 | 68 | LayoutEngine(std::shared_ptr context, 69 | std::shared_ptr textSource); 70 | ~LayoutEngine(); 71 | 72 | void clearText(); 73 | void clearTiles(); 74 | 75 | void layOut(); 76 | 77 | private: 78 | void createInternalRuns(); 79 | void processInlineObject(const TextRun & textRun); 80 | void processTextRun(const TextRun & textRun); 81 | void fastPathTextRun(const TextRun & textRun); 82 | 83 | void registerGlyphsAndMakeTiles(); 84 | void makeAdvances(std::vector & lineRuns); 85 | void processLineRun(const LineRun & lineRun); 86 | int32_t getTextAlignmentOffset(const LineRun & lineRun); 87 | }; 88 | 89 | } 90 | -------------------------------------------------------------------------------- /src/internal/layout/LineBreaker.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include "internal/layout/ShapeResult.hpp" 12 | #include "internal/layout/LineRun.hpp" 13 | #include "internal/input/StringIndexer.hpp" 14 | #include "internal/Context.hpp" 15 | 16 | namespace cobbletext::internal { 17 | 18 | class LineBreaker { 19 | std::shared_ptr context; 20 | icu::Locale breakerLocale; 21 | std::unique_ptr lineBreaker; 22 | std::unique_ptr characterBreaker; 23 | std::shared_ptr text; 24 | StringIndexer stringIndexer; 25 | 26 | std::shared_ptr> shapeResults; 27 | std::optional::iterator> shapeResultIterator; 28 | 29 | std::vector lines; 30 | 31 | LineRun currentLine; 32 | 33 | public: 34 | uint32_t lineLength = 0; 35 | 36 | explicit LineBreaker(std::shared_ptr context); 37 | 38 | void locale(const icu::Locale & locale); 39 | 40 | void setTextBuffer(std::shared_ptr text); 41 | 42 | std::vector applyBreaks( 43 | std::shared_ptr> shapeResults); 44 | 45 | private: 46 | void remakeBreakers(); 47 | 48 | void pushCurrentLine(); 49 | 50 | bool isMandatoryLineBreakAfter(int32_t codePointIndex); 51 | 52 | bool isCharacterBreakableBefore(int32_t codePointIndex); 53 | 54 | void fillLine(); 55 | 56 | void rewindLine(); 57 | 58 | void emergencyFillLine(); 59 | 60 | void analyzeLineHeight(); 61 | 62 | uint32_t getShapeLength(const ShapeResult & shapeResult); 63 | }; 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/internal/layout/LineRun.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/layout/LineRun.hpp" 2 | 3 | namespace cobbletext::internal { 4 | 5 | void LineRun::clear() { 6 | shapeResults.clear(); 7 | lineBreakShapeResult.reset(); 8 | totalAdvance = 0; 9 | lineHeight = 0; 10 | ascent = 0; 11 | descent = 0; 12 | } 13 | 14 | std::ostream & operator<<(std::ostream & stream, const LineRun & line) { 15 | stream 16 | << "[LineRun " 17 | << "totalAdvance=" << line.totalAdvance << " " 18 | << "lineHeight=" << line.lineHeight << " " 19 | << "ascent=" << line.ascent << " " 20 | << "descent=" << line.descent << " " 21 | << "shapeResults=" << line.shapeResults.size() << " "; 22 | 23 | if (!line.shapeResults.empty()) { 24 | stream << line.shapeResults.front() << " " 25 | << line.shapeResults.back() << " "; 26 | } 27 | 28 | stream << "]"; 29 | 30 | return stream; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/internal/layout/LineRun.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "internal/layout/ShapeResult.hpp" 9 | 10 | namespace cobbletext::internal { 11 | 12 | class LineRun { 13 | public: 14 | // Don't forget clear() if adding new members 15 | std::vector> shapeResults; 16 | std::optional lineBreakShapeResult; 17 | uint32_t totalAdvance = 0; 18 | uint32_t lineHeight = 0; 19 | uint32_t ascent = 0; 20 | uint32_t descent = 0; 21 | 22 | void clear(); 23 | 24 | private: 25 | friend std::ostream & operator<<(std::ostream & stream, const LineRun & lineRun); 26 | }; 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/internal/layout/ShapeResult.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/layout/ShapeResult.hpp" 2 | 3 | #ifdef COBBLETEXT_DEBUG 4 | #include 5 | #include 6 | #endif 7 | 8 | namespace cobbletext::internal { 9 | 10 | std::ostream & operator<<(std::ostream & stream, const ShapeResult & shapeResult) { 11 | #ifdef COBBLETEXT_DEBUG 12 | std::string text; 13 | icu::UnicodeString uString; 14 | uString.append(static_cast(shapeResult.codePoint)); 15 | uString.toUTF8String(text); 16 | #endif 17 | 18 | stream 19 | << "[ShapeResult " 20 | #ifdef COBBLETEXT_DEBUG 21 | << "codePoint=" << shapeResult.codePoint << " " 22 | << "text='" << text << "' " 23 | #endif 24 | << "glyphIndex=" << shapeResult.glyphIndex << " " 25 | << "cluster=" << shapeResult.cluster << " " 26 | << "advance=" << shapeResult.xAdvance << "," << shapeResult.yAdvance << " " 27 | << "offset=" << shapeResult.xOffset << "," << shapeResult.yOffset << " " 28 | << "fontID=" << shapeResult.fontID << " " 29 | << "]"; 30 | 31 | return stream; 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/internal/layout/ShapeResult.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "internal/layout/InternalTextRun.hpp" 7 | 8 | namespace cobbletext::internal { 9 | 10 | class ShapeResult { 11 | public: 12 | const InternalTextRun & run; 13 | int32_t xAdvance; 14 | int32_t yAdvance; 15 | int32_t xOffset; 16 | int32_t yOffset; 17 | uint32_t cluster; 18 | uint32_t glyphIndex; 19 | FontID fontID; 20 | 21 | #ifdef COBBLETEXT_DEBUG 22 | uint32_t codePoint; 23 | #endif 24 | 25 | explicit ShapeResult(const InternalTextRun & run) : run(run) {} 26 | 27 | private: 28 | friend std::ostream & operator<<(std::ostream & stream, const ShapeResult & shapeResult); 29 | }; 30 | 31 | } 32 | -------------------------------------------------------------------------------- /src/internal/layout/Shaper.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/layout/Shaper.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "internal/Debug.hpp" 7 | 8 | namespace cobbletext::internal { 9 | 10 | Shaper::Shaper(std::shared_ptr fontTable) : 11 | fontTable(fontTable), 12 | harfBuzzBuffer(hb_buffer_create()) { 13 | 14 | // hb_buffer_reference(harfBuzzBuffer.get()); 15 | hb_buffer_set_cluster_level(harfBuzzBuffer.get(), 16 | HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS); 17 | } 18 | 19 | void Shaper::setTextBuffer(std::shared_ptr text) { 20 | this->text = text; 21 | } 22 | 23 | std::vector Shaper::shapeRuns( 24 | const std::vector & runs) { 25 | std::vector results; 26 | 27 | for ( auto const & run : runs) { 28 | shapeRun(run, results); 29 | } 30 | 31 | return results; 32 | } 33 | 34 | void Shaper::shapeRun(const InternalTextRun & run, 35 | std::vector & results) { 36 | FontID fontID = run.source.textFormat.fontFace; 37 | 38 | if (!fontTable->hasFont(fontID)) { 39 | fontID = fontTable->fallbackFont; 40 | shapeRunWithFont(run, fontID, results, INFINITY); 41 | return; 42 | } 43 | 44 | bool hasAlternative = fontTable->getFontAlternative(fontID); 45 | 46 | if (!hasAlternative) { 47 | shapeRunWithFont(run, fontID, results, INFINITY); 48 | return; 49 | } 50 | 51 | auto currentID = fontID; 52 | 53 | while (true) { 54 | bool success = shapeRunWithFont(run, currentID, results, 55 | FONT_ALTERNATIVE_THRESHOLD); 56 | 57 | if (success) { 58 | return; 59 | } 60 | 61 | currentID = fontTable->getFontAlternative(currentID); 62 | 63 | if (!currentID) { 64 | break; 65 | } 66 | 67 | if (!fontTable->hasFont(currentID)) { 68 | currentID = fontTable->fallbackFont; 69 | } 70 | } 71 | 72 | shapeRunWithFont(run, fontID, results, INFINITY); 73 | } 74 | 75 | 76 | bool Shaper::shapeRunWithFont(const InternalTextRun & run, FontID fontID, 77 | std::vector & results, double notDefThreshold) { 78 | auto & font = fontTable->getFont(fontID); 79 | 80 | hb_buffer_clear_contents(harfBuzzBuffer.get()); 81 | 82 | hb_buffer_set_direction(harfBuzzBuffer.get(), run.direction); 83 | hb_buffer_set_script(harfBuzzBuffer.get(), run.script); 84 | hb_buffer_set_language(harfBuzzBuffer.get(), run.language); 85 | 86 | hb_buffer_add_utf16(harfBuzzBuffer.get(), 87 | reinterpret_cast(text->getBuffer()), 88 | text->length(), 89 | run.textIndex, run.textLength 90 | ); 91 | 92 | auto hasChanged = fontTable->setFontSize(fontID, 93 | run.source.textFormat.fontSize); 94 | 95 | if (hasChanged) { 96 | hb_ft_font_changed(font.harfBuzzFont); 97 | } 98 | 99 | hb_shape(font.harfBuzzFont, harfBuzzBuffer.get(), nullptr, 0); 100 | 101 | unsigned int glyphCount; 102 | 103 | hb_glyph_info_t * glyphInfos = hb_buffer_get_glyph_infos( 104 | harfBuzzBuffer.get(), &glyphCount); 105 | hb_glyph_position_t * glyphPositions = hb_buffer_get_glyph_positions( 106 | harfBuzzBuffer.get(), &glyphCount); 107 | 108 | unsigned int notDefCount = 0; 109 | 110 | for (size_t index = 0; index < glyphCount; index++) { 111 | auto glyphInfo = glyphInfos[index]; 112 | 113 | if (glyphInfo.codepoint == 0) { 114 | notDefCount++; 115 | } 116 | } 117 | 118 | double notDefScore; 119 | 120 | if (glyphCount > 0) { 121 | notDefScore = notDefCount / (double) glyphCount; 122 | } else { 123 | notDefScore = 0; 124 | } 125 | 126 | COBBLETEXT_DEBUG_PRINT("notDefScore=" << notDefScore 127 | << " notDefCount=" << notDefCount 128 | << " glyphCount=" << glyphCount); 129 | 130 | if (notDefScore < notDefThreshold) { 131 | pushBufferResults(run, glyphInfos, glyphPositions, glyphCount, fontID, 132 | font, results); 133 | return true; 134 | } else { 135 | return false; 136 | } 137 | 138 | } 139 | 140 | void Shaper::pushBufferResults(const InternalTextRun & run, 141 | hb_glyph_info_t * glyphInfos, 142 | hb_glyph_position_t * glyphPositions, 143 | unsigned int glyphCount, FontID fontID, Font & font, 144 | std::vector & results) { 145 | if (!glyphCount) { 146 | return; 147 | } 148 | 149 | for (size_t index_ = 0; index_ < glyphCount; index_++) { 150 | size_t index; 151 | 152 | if (run.direction == HB_DIRECTION_RTL) { 153 | index = glyphCount - index_ - 1; 154 | } else { 155 | index = index_; 156 | } 157 | 158 | auto glyphInfo = glyphInfos[index]; 159 | auto glyphPosition = glyphPositions[index]; 160 | 161 | ShapeResult result = ShapeResult(run); 162 | 163 | double scaleFactor = 1 / 64.0; // FreeType pixel units 164 | 165 | if (font.bitmapScale) { 166 | // Not a vector font, but a bitmap font. HarfBuzz uses the raw 167 | // selected bitmap size. 168 | scaleFactor *= font.bitmapScale; 169 | } 170 | 171 | result.xAdvance = glyphPosition.x_advance * scaleFactor; 172 | result.yAdvance = glyphPosition.y_advance * scaleFactor; 173 | result.xOffset = glyphPosition.x_offset * scaleFactor; 174 | result.yOffset = glyphPosition.y_offset * scaleFactor; 175 | result.glyphIndex = glyphInfo.codepoint; 176 | result.cluster = glyphInfo.cluster; 177 | result.fontID = fontID; 178 | 179 | #ifdef COBBLETEXT_DEBUG 180 | result.codePoint = text->char32At(glyphInfo.cluster); 181 | #endif 182 | 183 | results.push_back(result); 184 | } 185 | } 186 | 187 | } 188 | -------------------------------------------------------------------------------- /src/internal/layout/Shaper.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #ifdef __EMSCRIPTEN__ 11 | #include 12 | #else 13 | #include 14 | #endif 15 | 16 | 17 | #include "internal/layout/InternalTextRun.hpp" 18 | #include "internal/font/FontTable.hpp" 19 | #include "internal/layout/ShapeResult.hpp" 20 | 21 | namespace cobbletext::internal { 22 | 23 | class HarfBuzzBufferDeleter { 24 | public: 25 | void operator()(hb_buffer_t * buffer) const { 26 | hb_buffer_destroy(buffer); 27 | } 28 | }; 29 | 30 | class Shaper { 31 | 32 | private: 33 | std::shared_ptr fontTable; 34 | std::unique_ptr harfBuzzBuffer; 35 | std::shared_ptr text; 36 | static constexpr double FONT_ALTERNATIVE_THRESHOLD = 0.2; 37 | 38 | public: 39 | explicit Shaper(std::shared_ptr fontTable); 40 | 41 | void setTextBuffer(std::shared_ptr text); 42 | 43 | std::vector shapeRuns(const std::vector & runs); 44 | 45 | private: 46 | void shapeRun(const InternalTextRun & run, 47 | std::vector & results); 48 | 49 | bool shapeRunWithFont(const InternalTextRun & run, FontID fontID, 50 | std::vector & results, double notDefThreshold); 51 | 52 | void pushBufferResults(const InternalTextRun & run, 53 | hb_glyph_info_t * glyphInfos, 54 | hb_glyph_position_t * glyphPositions, 55 | unsigned int glyphCount, FontID fontID, Font & font, 56 | std::vector & results); 57 | 58 | }; 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/internal/table/BidiTable.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/table/BidiTable.hpp" 2 | 3 | #include 4 | #include 5 | 6 | #include "internal/ICUError.hpp" 7 | #include "internal/Debug.hpp" 8 | 9 | namespace cobbletext::internal { 10 | 11 | BidiTable::BidiTable() 12 | : bidiStruct(ubidi_open()) { 13 | } 14 | 15 | hb_direction_t BidiTable::directionToHarfBuzz(ScriptDirection direction) { 16 | switch (direction) { 17 | case ScriptDirection::LTR: 18 | return HB_DIRECTION_LTR; 19 | case ScriptDirection::RTL: 20 | return HB_DIRECTION_RTL; 21 | default: 22 | return HB_DIRECTION_INVALID; 23 | } 24 | } 25 | 26 | 27 | void BidiTable::setTextBuffer(std::shared_ptr text) { 28 | this->text = text; 29 | } 30 | 31 | void BidiTable::analyze(ScriptDirection direction) { 32 | assert(text); 33 | 34 | UBiDiLevel paragraphLevel; 35 | 36 | switch (direction) { 37 | case ScriptDirection::LTR: 38 | case ScriptDirection::NotSpecified: 39 | paragraphLevel = UBIDI_DEFAULT_LTR; 40 | break; 41 | case ScriptDirection::RTL: 42 | paragraphLevel = UBIDI_DEFAULT_RTL; 43 | break; 44 | default: 45 | Debug::abort("unknown script direction"); 46 | } 47 | 48 | ICUError errorCode; 49 | 50 | ubidi_setPara(bidiStruct.get(), text->getBuffer(), text->length(), 51 | paragraphLevel, nullptr, errorCode); 52 | } 53 | 54 | bool BidiTable::isMixed() { 55 | UBiDiDirection direction = ubidi_getDirection(bidiStruct.get()); 56 | 57 | return direction == UBIDI_MIXED; 58 | } 59 | 60 | ScriptDirection BidiTable::getDirection(int32_t codeUnitIndex) { 61 | UBiDiLevel level = ubidi_getLevelAt(bidiStruct.get(), codeUnitIndex); 62 | 63 | if (level % 2 == 0) { 64 | return ScriptDirection::LTR; 65 | } else { 66 | return ScriptDirection::RTL; 67 | } 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /src/internal/table/BidiTable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #ifdef __EMSCRIPTEN__ 8 | #include 9 | #else 10 | #include 11 | #endif 12 | 13 | #include 14 | #include 15 | 16 | 17 | #include "ScriptDirection.hpp" 18 | 19 | namespace cobbletext::internal { 20 | 21 | class UbidiDeleter { 22 | public: 23 | void operator()(UBiDi * bidiStruct) const { 24 | ubidi_close(bidiStruct); 25 | } 26 | }; 27 | 28 | class BidiTable { 29 | std::unique_ptr bidiStruct; 30 | std::shared_ptr text; 31 | 32 | public: 33 | BidiTable(); 34 | 35 | static hb_direction_t directionToHarfBuzz(ScriptDirection direction); 36 | 37 | void setTextBuffer(std::shared_ptr text); 38 | 39 | void analyze(ScriptDirection direction); 40 | 41 | bool isMixed(); 42 | 43 | ScriptDirection getDirection(int32_t codeUnitIndex); 44 | }; 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/internal/table/ScriptTable.cpp: -------------------------------------------------------------------------------- 1 | #include "internal/table/ScriptTable.hpp" 2 | 3 | #include 4 | 5 | #include "internal/ICUError.hpp" 6 | 7 | namespace cobbletext::internal { 8 | 9 | hb_script_t ScriptTable::scriptToHarfBuzz(std::string script) { 10 | return hb_script_from_string(script.c_str(), script.size()); 11 | } 12 | 13 | hb_language_t ScriptTable::languageToHarfBuzz(std::string language) { 14 | return hb_language_from_string(language.c_str(), language.size()); 15 | } 16 | 17 | void ScriptTable::setTextBuffer(std::shared_ptr text) { 18 | this->text = text; 19 | } 20 | 21 | hb_script_t ScriptTable::getHarfBuzzScript(int32_t codeUnitIndex) { 22 | assert(text); 23 | ICUError errorCode; 24 | auto codePoint = text->char32At(codeUnitIndex); 25 | 26 | auto scriptCode = uscript_getScript(codePoint, errorCode); 27 | auto usage = uscript_getUsage(scriptCode); 28 | 29 | if (usage == USCRIPT_USAGE_RECOMMENDED) { 30 | auto name4Letter = uscript_getShortName(scriptCode); 31 | 32 | if (name4Letter == nullptr) { 33 | return HB_SCRIPT_COMMON; 34 | } else { 35 | auto hbScript = hb_script_from_string(name4Letter, 4); 36 | return hbScript; 37 | } 38 | 39 | } else { 40 | return HB_SCRIPT_UNKNOWN; 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/internal/table/ScriptTable.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | 11 | #ifdef __EMSCRIPTEN__ 12 | #include 13 | #else 14 | #include 15 | #endif 16 | 17 | 18 | namespace cobbletext::internal { 19 | 20 | class ScriptTable { 21 | std::shared_ptr text; 22 | 23 | public: 24 | static hb_script_t scriptToHarfBuzz(std::string script); 25 | static hb_language_t languageToHarfBuzz(std::string language); 26 | 27 | void setTextBuffer(std::shared_ptr text); 28 | 29 | hb_script_t getHarfBuzzScript(int32_t codeUnitIndex); 30 | 31 | }; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | file(GLOB_RECURSE COBBLETEXT_TEST_SOURCES *.cpp) 3 | 4 | find_package(Catch2 3 REQUIRED) 5 | 6 | add_executable(test_main ${COBBLETEXT_TEST_SOURCES}) 7 | 8 | target_include_directories(test_main PRIVATE 9 | "${CMAKE_SOURCE_DIR}/include/" 10 | "${CMAKE_SOURCE_DIR}/src/" 11 | "${CMAKE_CURRENT_BINARY_DIR}" 12 | "${ICU_INCLUDE_DIR}" 13 | "${CATCH2_INCLUDE_PATH}" 14 | ) 15 | 16 | target_link_libraries(test_main PRIVATE cobbletext 17 | ${ICU_LIBRARIES} Catch2::Catch2WithMain) 18 | 19 | if(NOT COBBLETEXT_STATIC) 20 | if(NOT WIN32) 21 | target_link_libraries(test_main PRIVATE dl) 22 | endif() 23 | endif() 24 | 25 | add_test(NAME main COMMAND test_main WORKING_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 26 | set_property(TEST main APPEND PROPERTY ENVIRONMENT 27 | "TEST_FONT_PATH=${CMAKE_SOURCE_DIR}/lib/adobe-notdef/AND-Regular.otf") 28 | -------------------------------------------------------------------------------- /test/main.cpp: -------------------------------------------------------------------------------- 1 | #define CATCH_CONFIG_MAIN 2 | #include 3 | -------------------------------------------------------------------------------- /test/test_internal_input_StringIndexer.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | #include 5 | 6 | #include "internal/input/StringIndexer.hpp" 7 | 8 | TEST_CASE("StringIndexer") { 9 | auto text = std::make_shared( 10 | icu::UnicodeString::fromUTF8("h🐲🐲h")); 11 | 12 | cobbletext::internal::StringIndexer indexer(text); 13 | 14 | SECTION("individual -1 range") { 15 | CHECK( indexer.codePointToCodeUnit(-1) == 0 ); 16 | } 17 | 18 | SECTION("individual") { 19 | CHECK( indexer.codePointToCodeUnit(0) == 0 ); 20 | } 21 | 22 | SECTION("individual") { 23 | CHECK( indexer.codePointToCodeUnit(1) == 1 ); 24 | } 25 | 26 | SECTION("individual") { 27 | CHECK( indexer.codePointToCodeUnit(2) == 3 ); 28 | } 29 | 30 | SECTION("individual") { 31 | CHECK( indexer.codePointToCodeUnit(3) == 5 ); 32 | } 33 | 34 | SECTION("individual end") { 35 | CHECK( indexer.codePointToCodeUnit(4) == 6 ); 36 | } 37 | 38 | SECTION("individual +1 range") { 39 | CHECK( indexer.codePointToCodeUnit(5) == 6 ); 40 | } 41 | 42 | SECTION("consecutive") { 43 | CHECK( indexer.codePointToCodeUnit(0) == 0 ); 44 | CHECK( indexer.codePointToCodeUnit(1) == 1 ); 45 | CHECK( indexer.codePointToCodeUnit(2) == 3 ); 46 | CHECK( indexer.codePointToCodeUnit(3) == 5 ); 47 | } 48 | 49 | SECTION("random") { 50 | CHECK( indexer.codePointToCodeUnit(2) == 3 ); 51 | CHECK( indexer.codePointToCodeUnit(-1) == 0 ); 52 | CHECK( indexer.codePointToCodeUnit(4) == 6 ); 53 | CHECK( indexer.codePointToCodeUnit(3) == 5 ); 54 | CHECK( indexer.codePointToCodeUnit(1) == 1 ); 55 | CHECK( indexer.codePointToCodeUnit(6) == 6 ); 56 | CHECK( indexer.codePointToCodeUnit(0) == 0 ); 57 | CHECK( indexer.codePointToCodeUnit(5) == 6 ); 58 | } 59 | 60 | SECTION("out of range") { 61 | CHECK( indexer.codePointToCodeUnit(0) == 0 ); 62 | CHECK( indexer.codePointToCodeUnit(-1) == 0 ); 63 | CHECK( indexer.codePointToCodeUnit(-2) == 0 ); 64 | CHECK( indexer.codePointToCodeUnit(-1) == 0 ); 65 | 66 | CHECK( indexer.codePointToCodeUnit(3) == 5 ); 67 | CHECK( indexer.codePointToCodeUnit(4) == 6 ); 68 | CHECK( indexer.codePointToCodeUnit(5) == 6 ); 69 | CHECK( indexer.codePointToCodeUnit(4) == 6 ); 70 | CHECK( indexer.codePointToCodeUnit(3) == 5 ); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /test/test_library.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | 6 | #include "cobbletext/cobbletext.h" 7 | 8 | TEST_CASE("library construction and error clearing") { 9 | CobbletextLibrary * library = cobbletext_library_new(); 10 | 11 | REQUIRE( library ); 12 | CHECK( cobbletext_get_error_code(library) == 0 ); 13 | CHECK_THAT( cobbletext_get_error_message(library), Catch::Matchers::Equals("") ); 14 | 15 | const struct CobbletextFontInfo * font_info = 16 | cobbletext_library_get_font_info(library, 123123); 17 | 18 | CHECK( cobbletext_get_error_code(library) ); 19 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 20 | 21 | cobbletext_clear_error(library); 22 | CHECK( cobbletext_get_error_code(library) == 0 ); 23 | CHECK_THAT( cobbletext_get_error_message(library), Catch::Matchers::Equals("") ); 24 | 25 | REQUIRE_NOTHROW( cobbletext_library_delete(library) ); 26 | } 27 | 28 | TEST_CASE("library heavy methods") { 29 | CobbletextLibrary * library = cobbletext_library_new(); 30 | 31 | SECTION("fallback font") { 32 | CobbletextFontID fallback_font = 33 | cobbletext_library_get_fallback_font(library); 34 | 35 | REQUIRE( fallback_font ); 36 | 37 | const struct CobbletextFontInfo * font_info = 38 | cobbletext_library_get_font_info(library, fallback_font); 39 | 40 | REQUIRE( font_info ); 41 | CHECK( font_info->id == fallback_font ); 42 | CHECK( font_info->family_name ); 43 | CHECK( font_info->style_name ); 44 | CHECK( font_info->units_per_em ); 45 | CHECK( font_info->ascender ); 46 | CHECK( font_info->descender ); 47 | CHECK( font_info->height ); 48 | CHECK( font_info->underline_position ); 49 | CHECK( font_info->underline_thickness ); 50 | } 51 | 52 | SECTION("check nonexistent font") { 53 | const struct CobbletextFontInfo * font_info = 54 | cobbletext_library_get_font_info(library, 123123); 55 | 56 | CHECK( cobbletext_get_error_code(library) ); 57 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 58 | } 59 | 60 | SECTION("check nonexistent glyph") { 61 | const struct CobbletextGlyphInfo * glyph_info = 62 | cobbletext_library_get_glyph_info(library, 123123); 63 | 64 | CHECK( cobbletext_get_error_code(library) ); 65 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 66 | } 67 | 68 | SECTION("load nonexistant file") { 69 | cobbletext_library_load_font(library, "/nonexistant/font.ttf"); 70 | 71 | CHECK( cobbletext_get_error_code(library) ); 72 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 73 | } 74 | 75 | SECTION("load nonexistant file with face index") { 76 | cobbletext_library_load_font(library, "/nonexistant/font.ttf#123"); 77 | 78 | CHECK( cobbletext_get_error_code(library) ); 79 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 80 | 81 | cobbletext_library_load_font(library, "/nonexistant/font.ttf#123invalid"); 82 | 83 | CHECK( cobbletext_get_error_code(library) ); 84 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 85 | } 86 | 87 | SECTION("load nonsense bytes") { 88 | uint8_t * bytes = new uint8_t[100]; 89 | cobbletext_library_load_font_bytes(library, bytes, 100, 0); 90 | delete [] bytes; 91 | 92 | CHECK( cobbletext_get_error_code(library) ); 93 | CHECK_THAT( cobbletext_get_error_message(library), !Catch::Matchers::Equals("") ); 94 | } 95 | 96 | SECTION("check load file") { 97 | std::string fontPath; 98 | 99 | if (getenv("TEST_FONT_PATH")) { 100 | fontPath.append(getenv("TEST_FONT_PATH")); 101 | } else { 102 | fprintf(stderr, "TEST_FONT_PATH not set"); 103 | fontPath.append("lib/adobe-notdef/AND-Regular.otf"); 104 | } 105 | 106 | cobbletext_library_load_font(library, fontPath.c_str()); 107 | 108 | CHECK_FALSE( cobbletext_get_error_code(library) ); 109 | CHECK_THAT( cobbletext_get_error_message(library), Catch::Matchers::Equals("") ); 110 | } 111 | 112 | cobbletext_library_delete(library); 113 | } 114 | -------------------------------------------------------------------------------- /test/test_version.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "cobbletext/cobbletext.h" 4 | 5 | TEST_CASE("version getters") { 6 | 7 | CHECK_NOTHROW( cobbletext_get_version_major() ); 8 | CHECK_NOTHROW( cobbletext_get_version_minor() ); 9 | CHECK_NOTHROW( cobbletext_get_version_patch() ); 10 | CHECK( cobbletext_get_version() ); 11 | 12 | } 13 | --------------------------------------------------------------------------------