├── .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 |