openscad-cpp-evaluator 1.13.0__tar.gz → 1.15.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/CLAUDE.md +34 -2
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/CMakeLists.txt +20 -3
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/PKG-INFO +1 -1
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/bindings/module.cpp +35 -2
- openscad_cpp_evaluator-1.15.0/cmake/embed_font.cmake +71 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/export.hpp +8 -1
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/freetype_font_provider.hpp +13 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/pyproject.toml +1 -1
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/python/openscad_cpp_evaluator/__init__.py +26 -2
- openscad_cpp_evaluator-1.15.0/resources/fonts/LiberationSans-Bold.ttf +0 -0
- openscad_cpp_evaluator-1.15.0/resources/fonts/LiberationSans-BoldItalic.ttf +0 -0
- openscad_cpp_evaluator-1.15.0/resources/fonts/LiberationSans-Italic.ttf +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/function_builtins.cpp +17 -8
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/text.cpp +12 -1
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/export.cpp +16 -4
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/text/freetype_font_provider.cpp +57 -14
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_export.cpp +60 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_python_bindings.py +52 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_text.cpp +137 -0
- openscad_cpp_evaluator-1.13.0/cmake/embed_font.cmake +0 -28
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/.github/workflows/wheels.yml +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/.gitignore +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/.gitmodules +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/LICENSE +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/README.md +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/bindings/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/bindings/ast_to_py.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/examples/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/examples/eval_perf_benchmark.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/examples/manifold_cache_reuse.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/examples/minimal_debugger.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/.github/workflows/ci.yml +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/.gitignore +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/CLAUDE.md +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/README.md +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/api.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/ast_node.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/declarations.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/expression.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/module_instantiation.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/scope_builder.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast/vector_element.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/ast.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/comments.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/position.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/pretty_print.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/scope_table.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/serialization.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/include/openscad_cpp_parser/source_map.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/api.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/ast_node.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/declarations.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/expression.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/format_utils.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/module_instantiation.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/scope_builder.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/ast/vector_element.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/comment_attach_internal.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/comments.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/driver.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/driver.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/lexer.l +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/lexer_api.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/oscad_location.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/grammar/parser.y +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/inline_comment_attach.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/position.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/scope.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/serialization/json_io.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/serialization/yaml_io.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/src/source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_assignments.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_ast_generation.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_comments_and_files.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_const_borrow.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_control.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_expressions.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_functions.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_inline_comments.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_lexical.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_modules.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_node_str.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_pretty_print.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_render_expression.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_scope.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_serialization.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_smoke.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_source_map.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_strict_commas.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_use_include.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tests/test_vectors.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/external/openscad_cpp_parser/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/bound_args.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/bytecode.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/bytecode_compiler.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/bytecode_vm.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/call_args.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/colored_body.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/csg_node.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/css_colors.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/debug_hooks.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/debug_repl.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/dispatch.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/dxf_svg_import.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/eval_context.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/eval_error.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/eval_use.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/evaluator.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/font_match.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/font_provider.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/format_closure.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/function_builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/import_builtin.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/manifold_cache.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/mesh_check.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/mesh_import.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/osc_range.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/profile.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/scope_trail.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/segments.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/surface_load.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/text_metrics.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/utf8.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/value.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/include/openscad_cpp_evaluator/zip_stored.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/resources/fonts/LICENSE +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/resources/fonts/LiberationSans-Regular.ttf +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/builtins.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/call_args.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/color.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/control.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/dxf_dim.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/extrude.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/import.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/modifiers.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/primitives_2d.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/primitives_3d.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/registry.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/roof.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/segments.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/surface.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/topology.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/bytecode_vm.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/colored_body.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/csg_generate.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/csg_resolve.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/css_colors.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/debug/debug_repl.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/debug_profile.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/eval_error.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/eval_use.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/format_closure.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/import/dxf_import.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/import/mesh_import.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/import/surface_load.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/import/svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/measure_geometry.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/stmt_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/text/font_match.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/text/text_metrics.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/user_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/utf8.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/value.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_booleans.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_bytecode_compiler.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_children_separate.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_cli.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_control_flow.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_csg_tree.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_debug_hooks.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_dxf_svg_import.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_eval_context.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_expr_eval.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_extrude_roof.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_feature_detection.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_function_builtins.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_helpers.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_import_export.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_include_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_manifold_cache.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_mesh_check.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_multi_color_merge.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_primitives.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_profiling.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_render_expr.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_scope_trail.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_scoping.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_surface.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_tail_calls.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_transforms.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_unexpected_args.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_use.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_value.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_viewport_params.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tests/test_zip_stored.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tools/cli/CMakeLists.txt +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tools/cli/cli_lib.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tools/cli/cli_lib.hpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/tools/cli/main.cpp +0 -0
- {openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/uv.lock +0 -0
|
@@ -276,7 +276,16 @@ OpenSCAD's `detect_properties()`/`detect_script()` does by hand.
|
|
|
276
276
|
`resolveFont()` never fails. An unmatched family falls back to the bundled Liberation Sans
|
|
277
277
|
(`resources/fonts/`, embedded into the binary by `cmake/embed_font.cmake`, byte-identical to the
|
|
278
278
|
Python reference's own bundled font), which is both the default and the last-resort fallback — so
|
|
279
|
-
`text()` draws something even on a machine with no fonts installed at all.
|
|
279
|
+
`text()` draws something even on a machine with no fonts installed at all.
|
|
280
|
+
**All four styles are bundled** (Regular, Bold, Italic, Bold Italic), and they are seeded into the
|
|
281
|
+
face index ahead of anything installed, so `style=` resolves the same way on every machine.
|
|
282
|
+
Bundling only Regular meant a `style=Bold` nothing installed could satisfy fell silently back to
|
|
283
|
+
Regular — `fontmetrics(10, "Liberation Sans:style=bold")` then reported Regular's numbers under
|
|
284
|
+
the name the script asked for (BelfrySCAD #381). A bundled entry's `path` is the sentinel
|
|
285
|
+
`"<bundled>"` and its `faceIndex` is the already-open handle, so `resolveFont` opens no file for it.
|
|
286
|
+
`FreetypeFontProvider::listFonts()` returns every resolvable face (family, style, path), sorted and
|
|
287
|
+
deduplicated by family+style — reached from Python as `list_fonts()`, which adds the exact `font=`
|
|
288
|
+
spec for each. It is the only call here that forces the system scan the rest of the class defers. That mirrors
|
|
280
289
|
fontconfig's own never-fails matching, and so real OpenSCAD's: a missing font silently becomes the
|
|
281
290
|
nearest available one rather than an error.
|
|
282
291
|
|
|
@@ -289,6 +298,21 @@ variable real OpenSCAD reads). Family match is case-insensitive and required; st
|
|
|
289
298
|
dropping to Regular beats falling through to an unrelated family, which is what fontconfig does
|
|
290
299
|
too.
|
|
291
300
|
|
|
301
|
+
**`text()` takes `font` positionally too — and NOTHING after it.** `text(text, size, font)` is the
|
|
302
|
+
positional set; `halign`/`valign`/`spacing`/`direction`/`language`/`script` are name-only, because
|
|
303
|
+
that is what the 2026.02.01 binary does: `text("Hi", 10, "F", "center")` is not centred there while
|
|
304
|
+
`halign="center"` is, measured by bounding box. So **`text()` and `textmetrics()` genuinely differ**
|
|
305
|
+
— the latter takes all nine positionally — and neither can be assumed from the other. `font` being
|
|
306
|
+
name-only made `text("Hi", 10, "Liberation Sans:style=Bold")` silently draw the default face.
|
|
307
|
+
|
|
308
|
+
**`font` is a POSITIONAL argument in both metrics functions**, and so is everything after it:
|
|
309
|
+
`fontmetrics(size, font)` and `textmetrics(text, size, font, direction, language, script, halign,
|
|
310
|
+
valign, spacing)`. Verified against the 2026.02.01 binary rather than read off a doc page. Having
|
|
311
|
+
`font` name-only meant `fontmetrics(10, "Liberation Sans:style=Bold")` — how the bug report was
|
|
312
|
+
written, and how real OpenSCAD reads it — was taken as *no font at all*, so the default face was
|
|
313
|
+
measured and reported under the requested name. That was the visible half of BelfrySCAD #381; the
|
|
314
|
+
bundled-styles gap above was the other.
|
|
315
|
+
|
|
292
316
|
`text_metrics.hpp`/`.cpp` holds the shared layout math (`measureText`/`textAlignOffset`) used by
|
|
293
317
|
both `text()` (`builtins/text.cpp`) and `textmetrics()`/`fontmetrics()` (`function_builtins.cpp`).
|
|
294
318
|
Sharing it is the point: the numbers a script positions against cannot disagree with what it
|
|
@@ -953,7 +977,15 @@ grep for `ponytail:`.
|
|
|
953
977
|
restating the list, because three hand-written copies had already drifted far enough that `.off`
|
|
954
978
|
could be written by neither the GUI nor the CLI while every test passed.
|
|
955
979
|
Colour is carried by 3MF, AMF, OBJ (companion `.mtl`), PLY, VRML and X3D; STL and OFF compose
|
|
956
|
-
every body into one solid and store geometry only.
|
|
980
|
+
every body into one solid and store geometry only.
|
|
981
|
+
**`ExportOptions::splitColors` picks what the file is FOR**, and is the axis worth asking a user
|
|
982
|
+
about rather than "should parts be separate": on (the default) writes one object per colour, which
|
|
983
|
+
is what a **multi-material** print wants — each colour is something the slicer assigns to a
|
|
984
|
+
filament. Off is a **single-material** print: everything unions into one welded solid, so touching
|
|
985
|
+
colour regions lose the coincident faces that stacking per-colour objects would leave, and the
|
|
986
|
+
per-triangle colour is dropped because such a print has no use for it. Implemented by forcing
|
|
987
|
+
`splitBodiesForExport`'s `allSame` branch, which already unions rather than concatenates —
|
|
988
|
+
concatenating the per-colour objects instead would have left the seam. **AMF** puts colour on the `<volume>` rather
|
|
957
989
|
than the face, so an object whose triangles are not all one colour is written as one volume per
|
|
958
990
|
colour — per-triangle `<color>` is legal in the spec but poorly supported by the slicers that are
|
|
959
991
|
the audience for it.
|
|
@@ -218,13 +218,30 @@ FetchContent_MakeAvailable(cpp_linenoise)
|
|
|
218
218
|
# a real installed/packaged build (e.g. the PyPI wheel) has no guarantee the
|
|
219
219
|
# source tree's resources/fonts/ is present or at any predictable path
|
|
220
220
|
# alongside the installed shared library.
|
|
221
|
-
|
|
221
|
+
# The whole Liberation Sans family, not just Regular: a `style=` that
|
|
222
|
+
# cannot be satisfied silently falls back to the regular face, which is
|
|
223
|
+
# what made fontmetrics(10, "Liberation Sans:style=bold") report Regular's
|
|
224
|
+
# numbers (BelfrySCAD #381). Regular stays FIRST -- it is the default face
|
|
225
|
+
# and the last-resort fallback.
|
|
226
|
+
set(BUNDLED_FONT_SRCS
|
|
227
|
+
${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts/LiberationSans-Regular.ttf
|
|
228
|
+
${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts/LiberationSans-Bold.ttf
|
|
229
|
+
${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts/LiberationSans-Italic.ttf
|
|
230
|
+
${CMAKE_CURRENT_SOURCE_DIR}/resources/fonts/LiberationSans-BoldItalic.ttf
|
|
231
|
+
)
|
|
222
232
|
set(BUNDLED_FONT_HEADER ${CMAKE_CURRENT_BINARY_DIR}/generated/openscad_cpp_evaluator/bundled_font_data.hpp)
|
|
233
|
+
# Joined into ONE argument, and quoted. Left as a ";" list,
|
|
234
|
+
# add_custom_command expands it into separate arguments and the font paths
|
|
235
|
+
# land where -P expects the script; joined with "|" but unquoted, make
|
|
236
|
+
# emits a bare pipe and the shell splits the command in two. The separator
|
|
237
|
+
# is "@" for the same reason -- no shell meaning, and no filesystem here
|
|
238
|
+
# puts one in a path.
|
|
239
|
+
string(REPLACE ";" "@" BUNDLED_FONT_SRCS_ARG "${BUNDLED_FONT_SRCS}")
|
|
223
240
|
add_custom_command(
|
|
224
241
|
OUTPUT ${BUNDLED_FONT_HEADER}
|
|
225
|
-
COMMAND ${CMAKE_COMMAND} -
|
|
242
|
+
COMMAND ${CMAKE_COMMAND} "-DIN_FILES=${BUNDLED_FONT_SRCS_ARG}" -DOUT_FILE=${BUNDLED_FONT_HEADER}
|
|
226
243
|
-P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_font.cmake
|
|
227
|
-
DEPENDS ${
|
|
244
|
+
DEPENDS ${BUNDLED_FONT_SRCS} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/embed_font.cmake
|
|
228
245
|
COMMENT "Embedding bundled font bytes into bundled_font_data.hpp"
|
|
229
246
|
)
|
|
230
247
|
add_custom_target(generate_bundled_font DEPENDS ${BUNDLED_FONT_HEADER})
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
#include "openscad_cpp_evaluator/eval_use.hpp"
|
|
24
24
|
#include "openscad_cpp_evaluator/evaluator.hpp"
|
|
25
25
|
#include "openscad_cpp_evaluator/export.hpp"
|
|
26
|
+
#include "openscad_cpp_evaluator/freetype_font_provider.hpp"
|
|
26
27
|
#include "openscad_cpp_evaluator/mesh_check.hpp"
|
|
27
28
|
#include "openscad_cpp_evaluator/manifold_cache.hpp"
|
|
28
29
|
#include "openscad_cpp_evaluator/profile.hpp"
|
|
@@ -403,7 +404,8 @@ void applyPdfOptions(const nb::dict& d, oscadeval::ExportPdfOptions& pdf) {
|
|
|
403
404
|
}
|
|
404
405
|
|
|
405
406
|
nb::list exportModelPy(const std::string& path, const Geometry& geom, const std::string& format, bool asciiStl,
|
|
406
|
-
bool stripSlivers, bool splitComponents, bool
|
|
407
|
+
bool stripSlivers, bool splitComponents, bool splitColors, bool svgFill,
|
|
408
|
+
const std::string& svgFillColor,
|
|
407
409
|
bool svgStroke, const std::string& svgStrokeColor, double svgStrokeWidth,
|
|
408
410
|
nb::object pdfOptions) {
|
|
409
411
|
oscadeval::ExportPdfOptions pdf;
|
|
@@ -417,6 +419,7 @@ nb::list exportModelPy(const std::string& path, const Geometry& geom, const std:
|
|
|
417
419
|
opts.asciiStl = asciiStl;
|
|
418
420
|
opts.stripSlivers = stripSlivers;
|
|
419
421
|
opts.splitComponents = splitComponents;
|
|
422
|
+
opts.splitColors = splitColors;
|
|
420
423
|
opts.svg.fill = svgFill;
|
|
421
424
|
opts.svg.fillColor = svgFillColor;
|
|
422
425
|
opts.svg.stroke = svgStroke;
|
|
@@ -849,7 +852,8 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
849
852
|
|
|
850
853
|
m.def("export_model", &exportModelPy, nb::arg("path"), nb::arg("geometry"), nb::arg("format") = std::string(),
|
|
851
854
|
nb::arg("ascii_stl") = false, nb::arg("strip_slivers") = true,
|
|
852
|
-
nb::arg("split_components") = false, nb::arg("
|
|
855
|
+
nb::arg("split_components") = false, nb::arg("split_colors") = true,
|
|
856
|
+
nb::arg("svg_fill") = false,
|
|
853
857
|
nb::arg("svg_fill_color") = std::string("white"), nb::arg("svg_stroke") = true,
|
|
854
858
|
nb::arg("svg_stroke_color") = std::string("black"), nb::arg("svg_stroke_width") = 0.35,
|
|
855
859
|
nb::arg("pdf_options") = nb::none(),
|
|
@@ -860,6 +864,10 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
860
864
|
"`split_components` gives every disconnected piece its own object in the multi-object formats "
|
|
861
865
|
"(3MF, AMF, OBJ, PLY, VRML, X3D). Off by default, matching OpenSCAD, which writes one object "
|
|
862
866
|
"per colour however many pieces it is in.\n\n"
|
|
867
|
+
"`split_colors` (on by default) writes one object per colour -- what a MULTI-MATERIAL print "
|
|
868
|
+
"wants, each colour being something the slicer assigns to a filament. Turn it off for a "
|
|
869
|
+
"single-material print: everything welds into one solid, with no seams between colour "
|
|
870
|
+
"regions and no per-triangle colour it has no use for.\n\n"
|
|
863
871
|
"The svg_* arguments are OpenSCAD's -O export-svg/... set and apply to .svg only. "
|
|
864
872
|
"svg_stroke/svg_stroke_width also pad the page, so they are not purely cosmetic. An .svg "
|
|
865
873
|
"export needs an all-2D model and raises otherwise, as OpenSCAD does.\n\n"
|
|
@@ -869,6 +877,31 @@ NB_MODULE(_openscad_cpp_evaluator, m) {
|
|
|
869
877
|
"add-meta-data, title, author, subject, keywords). An unknown key raises rather than "
|
|
870
878
|
"being ignored. .pdf is 2D-only too, and centres the drawing on a fixed paper size.");
|
|
871
879
|
|
|
880
|
+
m.def("list_fonts", []() {
|
|
881
|
+
nb::list out;
|
|
882
|
+
oscadeval::FreetypeFontProvider provider;
|
|
883
|
+
for (const oscadeval::FontFace& f : provider.listFonts()) {
|
|
884
|
+
nb::dict d;
|
|
885
|
+
d["family"] = f.family;
|
|
886
|
+
d["style"] = f.style;
|
|
887
|
+
// The exact string to put in font=. Style included only when
|
|
888
|
+
// it is not the plain face: "Liberation Sans" is what you
|
|
889
|
+
// write for Regular, and printing the longer form for it would
|
|
890
|
+
// teach the wrong habit.
|
|
891
|
+
const bool plain = f.style.empty() || f.style == "Regular";
|
|
892
|
+
d["spec"] = plain ? f.family : f.family + ":style=" + f.style;
|
|
893
|
+
d["path"] = f.path;
|
|
894
|
+
out.append(d);
|
|
895
|
+
}
|
|
896
|
+
return out;
|
|
897
|
+
},
|
|
898
|
+
"Every font text()/textmetrics()/fontmetrics() can resolve: family, style, the exact "
|
|
899
|
+
"`font=` spec that selects it, and the file it comes from (\"<bundled>\" for the "
|
|
900
|
+
"faces shipped with this package). Read from the same FreeType index those functions "
|
|
901
|
+
"match against, NOT from the windowing toolkit -- a system font database names faces "
|
|
902
|
+
"differently, which is the whole reason a list is worth showing. Scans the system font "
|
|
903
|
+
"directories, so it costs real time on a machine with many fonts.");
|
|
904
|
+
|
|
872
905
|
m.def("export_extensions", []() { return oscadeval::exportExtensions(); },
|
|
873
906
|
"The file extensions export_model understands, dot-prefixed. Read from the C++ writer table "
|
|
874
907
|
"rather than restated here, so the Python side cannot drift from what can actually be written.");
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# Reads IN_FILES (a ";"-separated list of binaries) and writes OUT_FILE: a
|
|
2
|
+
# C++ header exposing their bytes as static arrays, plus a kBundledFonts
|
|
3
|
+
# table over them. Run via
|
|
4
|
+
# `cmake -DIN_FILES="a.ttf;b.ttf" -DOUT_FILE=... -P embed_font.cmake` as a
|
|
5
|
+
# build-time add_custom_command, not configure_file -- fonts baked in as
|
|
6
|
+
# bytes have no runtime file path to go stale once the shared library is
|
|
7
|
+
# installed somewhere other than its own build tree (see this script's own
|
|
8
|
+
# call site in the top-level CMakeLists.txt for the packaging bug this
|
|
9
|
+
# replaces).
|
|
10
|
+
#
|
|
11
|
+
# More than one font, because a style= that cannot be satisfied is worse
|
|
12
|
+
# than useless: it silently returns the regular face, which is what made
|
|
13
|
+
# `fontmetrics(10, "Liberation Sans:style=bold")` report Regular's numbers
|
|
14
|
+
# (BelfrySCAD #381). The whole family is embedded so the styles a script
|
|
15
|
+
# asks for exist even on a machine with no fonts of its own.
|
|
16
|
+
if(NOT DEFINED IN_FILES OR NOT DEFINED OUT_FILE)
|
|
17
|
+
message(FATAL_ERROR "embed_font.cmake requires -DIN_FILES=... -DOUT_FILE=...")
|
|
18
|
+
endif()
|
|
19
|
+
|
|
20
|
+
# "@"-joined by the caller into a single argument; see the call site for
|
|
21
|
+
# why neither a ";" list nor a "|" join survives the trip.
|
|
22
|
+
string(REPLACE "@" ";" IN_FILES "${IN_FILES}")
|
|
23
|
+
|
|
24
|
+
set(_decls "")
|
|
25
|
+
set(_table "")
|
|
26
|
+
set(_index 0)
|
|
27
|
+
foreach(_file IN LISTS IN_FILES)
|
|
28
|
+
file(READ "${_file}" _hex HEX)
|
|
29
|
+
string(LENGTH "${_hex}" _hexLen)
|
|
30
|
+
math(EXPR _byteCount "${_hexLen} / 2")
|
|
31
|
+
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," _bytesCsv "${_hex}")
|
|
32
|
+
get_filename_component(_name "${_file}" NAME)
|
|
33
|
+
string(APPEND _decls
|
|
34
|
+
"// ${_name}
|
|
35
|
+
inline constexpr unsigned char kBundledFont${_index}[] = {
|
|
36
|
+
${_bytesCsv}
|
|
37
|
+
};
|
|
38
|
+
")
|
|
39
|
+
string(APPEND _table " {kBundledFont${_index}, ${_byteCount}},\n")
|
|
40
|
+
if(_index EQUAL 0)
|
|
41
|
+
set(_firstSize "${_byteCount}")
|
|
42
|
+
endif()
|
|
43
|
+
math(EXPR _index "${_index} + 1")
|
|
44
|
+
endforeach()
|
|
45
|
+
|
|
46
|
+
file(WRITE "${OUT_FILE}"
|
|
47
|
+
"// Auto-generated by cmake/embed_font.cmake -- do not edit.
|
|
48
|
+
#pragma once
|
|
49
|
+
#include <cstddef>
|
|
50
|
+
|
|
51
|
+
namespace oscadeval {
|
|
52
|
+
|
|
53
|
+
${_decls}
|
|
54
|
+
struct BundledFont {
|
|
55
|
+
const unsigned char* data;
|
|
56
|
+
std::size_t size;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
// Index 0 is the default face and the last-resort fallback; the rest are
|
|
60
|
+
// the other styles of the same family, so a `style=` request can be met
|
|
61
|
+
// without anything installed on the machine.
|
|
62
|
+
inline constexpr BundledFont kBundledFonts[] = {
|
|
63
|
+
${_table}};
|
|
64
|
+
inline constexpr std::size_t kBundledFontCount = ${_index};
|
|
65
|
+
|
|
66
|
+
// The default face, named for the callers that only ever wanted that one.
|
|
67
|
+
inline constexpr const unsigned char* kBundledFontData = kBundledFont0;
|
|
68
|
+
inline constexpr std::size_t kBundledFontDataSize = ${_firstSize};
|
|
69
|
+
|
|
70
|
+
} // namespace oscadeval
|
|
71
|
+
")
|
|
@@ -53,7 +53,8 @@ struct ExportObject {
|
|
|
53
53
|
// 1-based index is appended to `openParts` for the caller to warn about.
|
|
54
54
|
std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& bodies,
|
|
55
55
|
std::vector<int>* openParts = nullptr,
|
|
56
|
-
bool splitComponents = false
|
|
56
|
+
bool splitComponents = false,
|
|
57
|
+
bool splitColors = true);
|
|
57
58
|
|
|
58
59
|
// Writes a binary STL: composes every body's mesh into one solid (bodies
|
|
59
60
|
// with no `.body` -- 2D-only sections -- or an empty Manifold are
|
|
@@ -236,6 +237,12 @@ struct ExportOptions {
|
|
|
236
237
|
// pieces it is in; see that function's own comment for why the default
|
|
237
238
|
// moved here.
|
|
238
239
|
bool splitComponents = false;
|
|
240
|
+
// Rule 2 of splitBodiesForExport: one object per colour. That is what
|
|
241
|
+
// a MULTI-MATERIAL print wants -- each colour becomes something the
|
|
242
|
+
// slicer assigns to a filament. Turn it off for a single-material
|
|
243
|
+
// print, which wants one welded solid instead: no seams between
|
|
244
|
+
// colour regions, and no per-triangle colour it has no use for.
|
|
245
|
+
bool splitColors = true;
|
|
239
246
|
// .svg only.
|
|
240
247
|
ExportSvgOptions svg;
|
|
241
248
|
// .pdf only.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#pragma once
|
|
2
2
|
|
|
3
|
+
#include "openscad_cpp_evaluator/font_match.hpp"
|
|
3
4
|
#include "openscad_cpp_evaluator/font_provider.hpp"
|
|
4
5
|
|
|
5
6
|
#include <memory>
|
|
@@ -30,6 +31,18 @@ public:
|
|
|
30
31
|
std::optional<std::array<double, 4>> glyphInkBounds(FontHandle handle, GlyphId glyph) override;
|
|
31
32
|
GlyphContours glyphOutline(FontHandle handle, GlyphId glyph, int segsPerCurve) override;
|
|
32
33
|
|
|
34
|
+
// Every face this provider can resolve: the bundled family first, then
|
|
35
|
+
// everything installed, sorted by family then style. Forces the system
|
|
36
|
+
// scan that the rest of the class defers, so it is the one call here
|
|
37
|
+
// that costs real time on a machine with a few hundred fonts.
|
|
38
|
+
//
|
|
39
|
+
// These are the names `font=` actually takes -- the point of showing
|
|
40
|
+
// them at all (BelfrySCAD #379: "the displayed font name can be
|
|
41
|
+
// different from the font name needed by OpenSCAD"). A list read from
|
|
42
|
+
// the windowing toolkit's font database would reproduce exactly that
|
|
43
|
+
// mismatch.
|
|
44
|
+
std::vector<FontFace> listFonts();
|
|
45
|
+
|
|
33
46
|
private:
|
|
34
47
|
struct Impl;
|
|
35
48
|
std::unique_ptr<Impl> impl_;
|
|
@@ -20,7 +20,7 @@ __all__ = [
|
|
|
20
20
|
"ManifoldCache", "CallSiteProfile", "ProfileResult", "format_csg_tree", "bodies_from_dicts",
|
|
21
21
|
"FastContinueSignal", "parse_ast", "parse_ast_string", "format_source",
|
|
22
22
|
"check_mesh", "strip_slivers",
|
|
23
|
-
"export_model", "export_extensions",
|
|
23
|
+
"export_model", "export_extensions", "list_fonts",
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
|
|
@@ -293,6 +293,7 @@ def strip_slivers(verts, tris):
|
|
|
293
293
|
|
|
294
294
|
def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
295
295
|
strip_slivers: bool = True, split_components: bool = False,
|
|
296
|
+
split_colors: bool = True,
|
|
296
297
|
svg_fill: bool = False, svg_fill_color: str = "white",
|
|
297
298
|
svg_stroke: bool = True, svg_stroke_color: str = "black",
|
|
298
299
|
svg_stroke_width: float = 0.35, pdf_options: dict | None = None) -> list:
|
|
@@ -316,6 +317,12 @@ def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
|
316
317
|
`split_components` gives every disconnected piece its own object in the
|
|
317
318
|
multi-object formats. Off by default, which is what OpenSCAD writes.
|
|
318
319
|
|
|
320
|
+
`split_colors` (on by default) writes one object per colour -- what a
|
|
321
|
+
MULTI-MATERIAL print wants, each colour being something the slicer
|
|
322
|
+
assigns to a filament. Turn it off for a single-material print:
|
|
323
|
+
everything welds into one solid, with no seams between colour regions
|
|
324
|
+
and no per-triangle colour it has no use for.
|
|
325
|
+
|
|
319
326
|
The `svg_*` arguments are OpenSCAD's `-O export-svg/...` set and apply
|
|
320
327
|
to `.svg` only, which writes a 2D model at 1:1 in millimetres.
|
|
321
328
|
`svg_stroke`/`svg_stroke_width` also pad the page, so they are not
|
|
@@ -335,13 +342,30 @@ def export_model(path: str, geometry, format: str = "", ascii_stl: bool = False,
|
|
|
335
342
|
"""
|
|
336
343
|
try:
|
|
337
344
|
return list(_ext.export_model(path, geometry, format, ascii_stl, strip_slivers,
|
|
338
|
-
split_components, svg_fill, svg_fill_color,
|
|
345
|
+
split_components, split_colors, svg_fill, svg_fill_color,
|
|
339
346
|
svg_stroke, svg_stroke_color, svg_stroke_width,
|
|
340
347
|
pdf_options))
|
|
341
348
|
except Exception as e:
|
|
342
349
|
raise EvalError(str(e)) from e
|
|
343
350
|
|
|
344
351
|
|
|
352
|
+
def list_fonts() -> list:
|
|
353
|
+
"""Every font `text()`/`textmetrics()`/`fontmetrics()` can resolve.
|
|
354
|
+
|
|
355
|
+
A list of dicts with `family`, `style`, `spec` and `path`. `spec` is
|
|
356
|
+
the exact string to put in `font=`, which is the point of asking:
|
|
357
|
+
the name a system font dialog shows is often not the name OpenSCAD
|
|
358
|
+
takes, and this list comes from the same FreeType index the text
|
|
359
|
+
functions match against. `path` is `"<bundled>"` for the faces
|
|
360
|
+
shipped with this package.
|
|
361
|
+
|
|
362
|
+
Scans the system font directories, so it costs real time on a machine
|
|
363
|
+
with many fonts -- call it when someone asks to see the list, not on
|
|
364
|
+
a hot path.
|
|
365
|
+
"""
|
|
366
|
+
return list(_ext.list_fonts())
|
|
367
|
+
|
|
368
|
+
|
|
345
369
|
def export_extensions() -> list:
|
|
346
370
|
"""The file extensions export_model understands, dot-prefixed.
|
|
347
371
|
|
|
Binary file
|
|
Binary file
|
{openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/builtins/function_builtins.cpp
RENAMED
|
@@ -785,14 +785,21 @@ Value objectOf(std::vector<std::pair<std::string, Value>> items) {
|
|
|
785
785
|
Value builtinTextmetrics(Evaluator& ev, const CallArgs& args) {
|
|
786
786
|
const std::string text = asStringOr(getArg(args, 0, "text", Value{std::string("")}), "");
|
|
787
787
|
const double size = toDoubleLenient(getArg(args, 1, "size", Value{10.0}));
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
788
|
+
// Positional indices, all of them: OpenSCAD's own signature is
|
|
789
|
+
// textmetrics(text, size, font, direction, language, script, halign,
|
|
790
|
+
// valign, spacing) and it honours every one of those positionally --
|
|
791
|
+
// verified against the 2026.02.01 binary, not read off a doc page.
|
|
792
|
+
// `font` being name-only here is what made
|
|
793
|
+
// `textmetrics("Hi", 10, "Liberation Sans:style=Bold")` silently
|
|
794
|
+
// measure the default face (BelfrySCAD #381).
|
|
795
|
+
const std::string halign = asStringOr(getArg(args, 6, "halign", Value{std::string("left")}), "left");
|
|
796
|
+
const std::string valign = asStringOr(getArg(args, 7, "valign", Value{std::string("baseline")}), "baseline");
|
|
797
|
+
const double spacing = toDoubleLenient(getArg(args, 8, "spacing", Value{1.0}));
|
|
798
|
+
const std::string fontSpec = asStringOr(getArg(args, 2, "font", Value{std::string("")}), "");
|
|
792
799
|
ShapeOptions shape;
|
|
793
|
-
shape.direction = asStringOr(getArg(args,
|
|
794
|
-
shape.language = asStringOr(getArg(args,
|
|
795
|
-
shape.script = asStringOr(getArg(args,
|
|
800
|
+
shape.direction = asStringOr(getArg(args, 3, "direction", Value{std::string("")}), "");
|
|
801
|
+
shape.language = asStringOr(getArg(args, 4, "language", Value{std::string("")}), "");
|
|
802
|
+
shape.script = asStringOr(getArg(args, 5, "script", Value{std::string("")}), "");
|
|
796
803
|
|
|
797
804
|
FontProvider& fp = ev.fontProvider();
|
|
798
805
|
const FontHandle handle = fp.resolveFont(fontSpec);
|
|
@@ -813,7 +820,9 @@ Value builtinTextmetrics(Evaluator& ev, const CallArgs& args) {
|
|
|
813
820
|
// resolved font, scaled for `size`. Mirrors _builtin_fontmetrics.
|
|
814
821
|
Value builtinFontmetrics(Evaluator& ev, const CallArgs& args) {
|
|
815
822
|
const double size = toDoubleLenient(getArg(args, 0, "size", Value{10.0}));
|
|
816
|
-
|
|
823
|
+
// Positional too: fontmetrics(10, "Liberation Sans:style=Bold") is
|
|
824
|
+
// how the bug report was written, and how the real binary reads it.
|
|
825
|
+
const std::string fontSpec = asStringOr(getArg(args, 1, "font", Value{std::string("")}), "");
|
|
817
826
|
|
|
818
827
|
FontProvider& fp = ev.fontProvider();
|
|
819
828
|
const FontHandle handle = fp.resolveFont(fontSpec);
|
|
@@ -34,7 +34,18 @@ CSGParams resolveText(Evaluator& ev, const oscad::ModularCall& node, EvalContext
|
|
|
34
34
|
auto [args, effCtx] = resolveCallArgs(ev, node.arguments, ctx);
|
|
35
35
|
const std::string text = asStringOr(getArg(args, 0, "text", Value{std::string("")}), "");
|
|
36
36
|
const double size = toDoubleLenient(getArg(args, 1, "size", Value{10.0}));
|
|
37
|
-
|
|
37
|
+
// font is POSITIONAL, at index 2: `text("Hi", 10, "Liberation Sans")`
|
|
38
|
+
// is how people write it and how the reference reads it -- measured
|
|
39
|
+
// against the 2026.02.01 binary, whose bounding box for the
|
|
40
|
+
// positional form matches the named one exactly.
|
|
41
|
+
//
|
|
42
|
+
// Only font. halign/valign/spacing/direction/language/script stay
|
|
43
|
+
// name-only, because the same measurement shows the reference
|
|
44
|
+
// ignoring them positionally: `text("Hi", 10, "F", "center")` is NOT
|
|
45
|
+
// centred there, while halign="center" is. text() and textmetrics()
|
|
46
|
+
// genuinely differ here -- textmetrics takes all nine positionally --
|
|
47
|
+
// so neither can be assumed from the other.
|
|
48
|
+
const std::string fontSpec = asStringOr(getArg(args, 2, "font", Value{std::string("")}), "");
|
|
38
49
|
const std::string halign = asStringOr(getArg(args, std::nullopt, "halign", Value{std::string("left")}), "left");
|
|
39
50
|
const std::string valign = asStringOr(getArg(args, std::nullopt, "valign", Value{std::string("baseline")}), "baseline");
|
|
40
51
|
const double spacing = toDoubleLenient(getArg(args, std::nullopt, "spacing", Value{1.0}));
|
|
@@ -421,7 +421,7 @@ std::vector<std::array<float, 4>> carryTriColors(const std::vector<std::array<fl
|
|
|
421
421
|
} // namespace
|
|
422
422
|
|
|
423
423
|
std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& bodies, std::vector<int>* openParts,
|
|
424
|
-
bool splitComponents) {
|
|
424
|
+
bool splitComponents, bool splitColors) {
|
|
425
425
|
struct Solid {
|
|
426
426
|
manifold::Manifold man;
|
|
427
427
|
std::optional<std::array<float, 4>> color;
|
|
@@ -484,7 +484,15 @@ std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& b
|
|
|
484
484
|
if (k.hasColor) k.color = *solids[i].color;
|
|
485
485
|
keys.push_back(k);
|
|
486
486
|
}
|
|
487
|
-
|
|
487
|
+
// Splitting by colour is for a MULTI-MATERIAL print, where each
|
|
488
|
+
// colour becomes an object the slicer assigns to a filament. A
|
|
489
|
+
// single-material print wants the opposite: one welded solid, with
|
|
490
|
+
// no seams between colour regions for the slicer to treat as
|
|
491
|
+
// separate walls. Unioning everything here (rather than
|
|
492
|
+
// concatenating the per-colour objects afterwards) is what makes
|
|
493
|
+
// touching regions weld instead of leaving coincident faces.
|
|
494
|
+
const bool allSame = !splitColors ||
|
|
495
|
+
std::all_of(keys.begin(), keys.end(), [&](const ColorKey& k) { return k == keys[0]; });
|
|
488
496
|
|
|
489
497
|
if (allSame) {
|
|
490
498
|
// The common case by far, and it needs no per-body subtraction
|
|
@@ -496,7 +504,10 @@ std::vector<ExportObject> splitBodiesForExport(const std::vector<ColoredBody>& b
|
|
|
496
504
|
Claimed c;
|
|
497
505
|
c.man = addAll(parts);
|
|
498
506
|
c.color = solids[0].color;
|
|
499
|
-
|
|
507
|
+
// Per-triangle colour cannot survive a weld across colours --
|
|
508
|
+
// the union rebuilds the surface -- and a single-material
|
|
509
|
+
// print has no use for it anyway.
|
|
510
|
+
c.triColors = splitColors ? solids[0].triColors : std::vector<std::array<float, 4>>{};
|
|
500
511
|
c.sourceTris = solids[0].tris;
|
|
501
512
|
c.key = keys[0];
|
|
502
513
|
claimedGroups.push_back(std::move(c));
|
|
@@ -1507,7 +1518,8 @@ std::vector<std::string> exportModel(const std::string& path, const std::vector<
|
|
|
1507
1518
|
// its own -- that is what the file contains.
|
|
1508
1519
|
for (std::string& w : checkExportBodies(bodies)) warnings.push_back(std::move(w));
|
|
1509
1520
|
std::vector<int> openParts;
|
|
1510
|
-
const std::vector<ExportObject> objects =
|
|
1521
|
+
const std::vector<ExportObject> objects =
|
|
1522
|
+
splitBodiesForExport(bodies, &openParts, opts.splitComponents, opts.splitColors);
|
|
1511
1523
|
reportOpen(openParts);
|
|
1512
1524
|
if (objects.empty()) throw std::runtime_error("No geometry to export");
|
|
1513
1525
|
if (ext == ".3mf") {
|
{openscad_cpp_evaluator-1.13.0 → openscad_cpp_evaluator-1.15.0}/src/text/freetype_font_provider.cpp
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
#include "openscad_cpp_evaluator/freetype_font_provider.hpp"
|
|
2
2
|
|
|
3
|
+
#include <algorithm>
|
|
4
|
+
|
|
3
5
|
#include "openscad_cpp_evaluator/bundled_font_data.hpp"
|
|
4
6
|
#include "openscad_cpp_evaluator/font_match.hpp"
|
|
5
7
|
|
|
@@ -104,6 +106,11 @@ struct FreetypeFontProvider::Impl {
|
|
|
104
106
|
FontMetrics metrics;
|
|
105
107
|
};
|
|
106
108
|
|
|
109
|
+
//: A FontFace whose `path` is this is already open: its faceIndex IS
|
|
110
|
+
//: the handle. Real paths cannot collide with it -- no filesystem
|
|
111
|
+
//: yields a name in angle brackets from findFontFiles().
|
|
112
|
+
static constexpr const char* kBundledPath = "<bundled>";
|
|
113
|
+
|
|
107
114
|
std::vector<Face> faces; // handle == index
|
|
108
115
|
std::unordered_map<std::string, FontHandle> bySpec; // resolved spec -> handle
|
|
109
116
|
std::vector<FontFace> installed; // system font index
|
|
@@ -160,21 +167,30 @@ struct FreetypeFontProvider::Impl {
|
|
|
160
167
|
return faces.size() - 1;
|
|
161
168
|
}
|
|
162
169
|
|
|
170
|
+
// Every bundled face, in order -- handle 0 is the regular one, the
|
|
171
|
+
// default and the last-resort fallback. The rest are the other styles
|
|
172
|
+
// of the same family: a `style=` request that nothing installed can
|
|
173
|
+
// satisfy used to fall silently back to regular, which is what made
|
|
174
|
+
// fontmetrics("Liberation Sans:style=bold") report Regular's numbers
|
|
175
|
+
// (BelfrySCAD #381).
|
|
163
176
|
void openBundled() {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
177
|
+
for (std::size_t i = 0; i < kBundledFontCount; ++i) {
|
|
178
|
+
const BundledFont& src = kBundledFonts[i];
|
|
179
|
+
Face f;
|
|
180
|
+
if (FT_New_Memory_Face(lib, reinterpret_cast<const FT_Byte*>(src.data),
|
|
181
|
+
static_cast<FT_Long>(src.size), 0, &f.ft) != 0) {
|
|
182
|
+
if (i == 0) throw std::runtime_error("FreetypeFontProvider: bundled font failed to load");
|
|
183
|
+
continue; // a spare style is not worth failing every render for
|
|
184
|
+
}
|
|
185
|
+
f.blob = hb_blob_create(reinterpret_cast<const char*>(src.data),
|
|
186
|
+
static_cast<unsigned>(src.size), HB_MEMORY_MODE_READONLY, nullptr, nullptr);
|
|
187
|
+
f.hbFace = hb_face_create(f.blob, 0);
|
|
188
|
+
f.hbFont = hb_font_create(f.hbFace);
|
|
189
|
+
hb_ot_font_set_funcs(f.hbFont);
|
|
190
|
+
f.metrics = readMetrics(f.ft);
|
|
191
|
+
scaleHbFontToEm(f.hbFont, static_cast<unsigned>(f.metrics.unitsPerEm));
|
|
192
|
+
faces.push_back(f);
|
|
168
193
|
}
|
|
169
|
-
f.blob = hb_blob_create(reinterpret_cast<const char*>(kBundledFontData),
|
|
170
|
-
static_cast<unsigned>(kBundledFontDataSize), HB_MEMORY_MODE_READONLY, nullptr,
|
|
171
|
-
nullptr);
|
|
172
|
-
f.hbFace = hb_face_create(f.blob, 0);
|
|
173
|
-
f.hbFont = hb_font_create(f.hbFace);
|
|
174
|
-
hb_ot_font_set_funcs(f.hbFont);
|
|
175
|
-
f.metrics = readMetrics(f.ft);
|
|
176
|
-
scaleHbFontToEm(f.hbFont, static_cast<unsigned>(f.metrics.unitsPerEm));
|
|
177
|
-
faces.push_back(f);
|
|
178
194
|
}
|
|
179
195
|
|
|
180
196
|
// Reads family/style out of every face of every installed font file.
|
|
@@ -185,6 +201,15 @@ struct FreetypeFontProvider::Impl {
|
|
|
185
201
|
void scanInstalled() {
|
|
186
202
|
if (scanned) return;
|
|
187
203
|
scanned = true;
|
|
204
|
+
// The bundled faces go in FIRST, so what a given family+style
|
|
205
|
+
// resolves to does not depend on what the machine happens to have
|
|
206
|
+
// installed -- the same script draws the same shapes everywhere.
|
|
207
|
+
// Their "path" is the sentinel below and their faceIndex is the
|
|
208
|
+
// already-open handle, so resolveFont skips opening a file at all.
|
|
209
|
+
for (std::size_t i = 0; i < faces.size() && i < kBundledFontCount; ++i) {
|
|
210
|
+
installed.push_back(FontFace{std::string(kBundledPath), static_cast<int>(i),
|
|
211
|
+
faces[i].metrics.family, faces[i].metrics.style});
|
|
212
|
+
}
|
|
188
213
|
for (const std::string& path : findFontFiles()) {
|
|
189
214
|
FT_Face probe = nullptr;
|
|
190
215
|
if (FT_New_Face(lib, path.c_str(), -1, &probe) != 0) continue;
|
|
@@ -222,12 +247,30 @@ FontHandle FreetypeFontProvider::resolveFont(const std::string& spec) {
|
|
|
222
247
|
impl_->scanInstalled();
|
|
223
248
|
FontHandle handle = 0;
|
|
224
249
|
if (const std::optional<FontFace> hit = matchFace(parsed, impl_->installed)) {
|
|
225
|
-
handle =
|
|
250
|
+
handle = hit->path == Impl::kBundledPath ? static_cast<FontHandle>(hit->faceIndex)
|
|
251
|
+
: impl_->openFile(hit->path, hit->faceIndex).value_or(0);
|
|
226
252
|
}
|
|
227
253
|
impl_->bySpec[spec] = handle;
|
|
228
254
|
return handle;
|
|
229
255
|
}
|
|
230
256
|
|
|
257
|
+
std::vector<FontFace> FreetypeFontProvider::listFonts() {
|
|
258
|
+
impl_->scanInstalled();
|
|
259
|
+
std::vector<FontFace> out = impl_->installed;
|
|
260
|
+
// A family installed in several files, or listed both bundled and
|
|
261
|
+
// installed, should appear once per STYLE, not once per file.
|
|
262
|
+
std::sort(out.begin(), out.end(), [](const FontFace& a, const FontFace& b) {
|
|
263
|
+
if (a.family != b.family) return a.family < b.family;
|
|
264
|
+
return a.style < b.style;
|
|
265
|
+
});
|
|
266
|
+
out.erase(std::unique(out.begin(), out.end(),
|
|
267
|
+
[](const FontFace& a, const FontFace& b) {
|
|
268
|
+
return a.family == b.family && a.style == b.style;
|
|
269
|
+
}),
|
|
270
|
+
out.end());
|
|
271
|
+
return out;
|
|
272
|
+
}
|
|
273
|
+
|
|
231
274
|
FontMetrics FreetypeFontProvider::metrics(FontHandle handle) {
|
|
232
275
|
if (handle >= impl_->faces.size()) return impl_->faces[0].metrics;
|
|
233
276
|
return impl_->faces[handle].metrics;
|