diff --git a/.github/actions/build-and-test/action.yml b/.github/actions/build-and-test/action.yml index 4e189ec73..ad8a25c2a 100644 --- a/.github/actions/build-and-test/action.yml +++ b/.github/actions/build-and-test/action.yml @@ -52,11 +52,11 @@ runs: - name: "cargo build for ${{ inputs.target }}" shell: bash run: | - ${{ inputs.executable }} build --all --target ${{ inputs.target }} --release ${{ steps.set-feature-flags.outputs.cargo-features }} $CARGO_VERBOSE + ${{ inputs.executable }} build --workspace --target ${{ inputs.target }} --release ${{ steps.set-feature-flags.outputs.cargo-features }} $CARGO_VERBOSE - name: "cargo test" shell: bash run: | - ${{ inputs.executable }} test --all --target ${{ inputs.target }} ${{ inputs.test-flags }} --release ${{ steps.set-feature-flags.outputs.cargo-features }} $CARGO_VERBOSE + ${{ inputs.executable }} test --workspace --target ${{ inputs.target }} ${{ inputs.test-flags }} --release ${{ steps.set-feature-flags.outputs.cargo-features }} $CARGO_VERBOSE - name: "Package test failure files" id: package-tests if: ${{ failure() }} diff --git a/.github/workflows/build_and_test.yml b/.github/workflows/build_and_test.yml index 33b77acc8..122f5608d 100644 --- a/.github/workflows/build_and_test.yml +++ b/.github/workflows/build_and_test.yml @@ -1,6 +1,6 @@ name: "Build and Test" -on: [workflow_call] +on: [ workflow_call ] jobs: clippy: @@ -68,12 +68,12 @@ jobs: run: | artifact_dir="appimage" mkdir -p "$artifact_dir" - + if [[ $SOURCE_BRANCH == master ]] ; then export TECTONIC_APPIMAGE_TAG=continuous export UPDATE_INFORMATION="gh-releases-zsync|tectonic-typesetting|tectonic|continuous|tectonic-*.AppImage.zsync" fi - + ./dist/appimage/build.sh cp dist/appimage/tectonic-*.AppImage* "$artifact_dir" env: @@ -141,7 +141,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - toolchain: ["beta", "nightly"] + toolchain: [ "beta", "nightly" ] fail-fast: false steps: - name: Checkout repository @@ -166,7 +166,7 @@ jobs: linux-feature-tests: strategy: matrix: - features: ["_all_", "_none_", "geturl-curl serialization"] + features: [ "_all_", "_none_", "geturl-curl serialization" ] fail-fast: false runs-on: ubuntu-latest steps: @@ -193,8 +193,8 @@ jobs: pkg-config: strategy: matrix: - image: [ubuntu-latest, windows-latest, macos-latest] - install-all-deps: [true, false] + image: [ ubuntu-latest, windows-latest, macos-latest ] + install-all-deps: [ true, false ] include: # By default, all items have toolchain: stable, and don't publish - toolchain: stable @@ -327,12 +327,13 @@ jobs: echo "CROSS_ROOTLESS_CONTAINER_ENGINE=1" >> "$GITHUB_ENV" echo "DOCKER_OPTS=--privileged -e HOST_UID=${HOST_UID} -e HOST_GID=${HOST_GID}" >> "$GITHUB_ENV" # TODO: Add font files to the cross images so we can include fontconfig_bridge in tests + # TODO: Figure out why doctests are broken so we can drop all-targets - name: "Build and Test" uses: ./.github/actions/build-and-test with: target: ${{ matrix.target }} publish: 'true' executable: 'cross' - test-flags: '--exclude tectonic_bridge_fontconfig' + test-flags: '--all-targets --exclude tectonic_bridge_fontconfig' package-flags: '--command-name=cross --reroot=.' diff --git a/Cargo.lock b/Cargo.lock index 4154fe197..f8fb8454b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -296,6 +296,26 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytemuck" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "441473f2b4b0459a68628c744bc61d23e730fb00128b841d30fa4bb3972257e4" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.103", +] + [[package]] name = "byteorder" version = "1.5.0" @@ -601,6 +621,18 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c34f04666d835ff5d62e058c3995147c06f42fe86ff053337632bca83e42702d" +[[package]] +name = "enrede" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42685d561e9e3f84c7cf5c9ec917e6112052b82b741ffe5f9dd373bfc19504cd" +dependencies = [ + "arrayvec", + "bytemuck", + "phf", + "serde", +] + [[package]] name = "env_home" version = "0.1.0" @@ -2126,6 +2158,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ + "phf_macros", "phf_shared", ] @@ -2149,6 +2182,19 @@ dependencies = [ "rand", ] +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.103", +] + [[package]] name = "phf_shared" version = "0.11.3" @@ -3108,8 +3154,10 @@ dependencies = [ "libc", "tectonic_bridge_core", "tectonic_bridge_flate", + "tectonic_bridge_freetype2", "tectonic_bridge_graphite2", "tectonic_bridge_harfbuzz", + "tectonic_bridge_icu", "tectonic_cfg_support", "tectonic_errors", "tectonic_pdf_io", @@ -3196,6 +3244,8 @@ name = "tectonic_xetex_layout" version = "0.0.0-dev.0" dependencies = [ "cc", + "enrede", + "libc", "tectonic_bridge_core", "tectonic_bridge_fontconfig", "tectonic_bridge_freetype2", @@ -3203,6 +3253,8 @@ dependencies = [ "tectonic_bridge_harfbuzz", "tectonic_bridge_icu", "tectonic_cfg_support", + "tectonic_io_base", + "tectonic_mac_core", ] [[package]] diff --git a/crates/bridge_core/src/lib.rs b/crates/bridge_core/src/lib.rs index bf622d2ed..124815303 100644 --- a/crates/bridge_core/src/lib.rs +++ b/crates/bridge_core/src/lib.rs @@ -194,6 +194,8 @@ impl DriverHooks for MinimalDriver { // Function defined in the C support code: extern "C" { fn _ttbc_get_error_message() -> *const libc::c_char; + #[allow(improper_ctypes)] + fn _ttbc_get_core_state() -> *mut CoreBridgeState<'static>; } lazy_static::lazy_static! { @@ -377,6 +379,18 @@ impl<'a> CoreBridgeState<'a> { } } + /// Get the current global bridge state. Uses a mutex to ensure unique access, and panics + /// if no global state is set. + pub fn with_global_state FnOnce(&mut CoreBridgeState<'b>) -> T>(f: F) -> T { + /// Ensures we only enter the state once at a time, globally + static GLOBAL: Mutex<()> = Mutex::new(()); + let _lock = GLOBAL.lock().unwrap(); + // SAFETY: Pointer is either null or valid, set by the engine on entrance + let state = unsafe { _ttbc_get_core_state().as_mut() } + .expect("Currently within an engine context in C"); + f(state) + } + fn input_open_name_format( &mut self, name: &str, @@ -637,7 +651,8 @@ impl<'a> CoreBridgeState<'a> { InputId::new(self.input_handles.len()) } - fn input_get_size(&mut self, handle: InputId) -> usize { + /// Get the size of an input. Prints a warning and returns 0 on error + pub fn input_get_size(&mut self, handle: InputId) -> usize { let rhandle: &mut InputHandle = self.get_input(handle); match rhandle.get_size() { @@ -671,7 +686,8 @@ impl<'a> CoreBridgeState<'a> { rhandle.try_seek(pos) } - fn input_read(&mut self, handle: InputId, buf: &mut [u8]) -> Result<()> { + /// Read from an input + pub fn input_read(&mut self, handle: InputId, buf: &mut [u8]) -> Result<()> { let rhandle: &mut InputHandle = self.get_input(handle); rhandle.read_exact(buf).map_err(Error::from) } diff --git a/crates/bridge_core/support/support.c b/crates/bridge_core/support/support.c index e6d3e5365..21d05e172 100644 --- a/crates/bridge_core/support/support.c +++ b/crates/bridge_core/support/support.c @@ -113,6 +113,13 @@ _ttbc_get_error_message(void) } +ttbc_state_t * +_ttbc_get_core_state(void) +{ + return tectonic_global_bridge_core; +} + + jmp_buf * ttbc_global_engine_enter(ttbc_state_t *api) { diff --git a/crates/engine_xetex/Cargo.toml b/crates/engine_xetex/Cargo.toml index 7c661f0c6..9a45d80bd 100644 --- a/crates/engine_xetex/Cargo.toml +++ b/crates/engine_xetex/Cargo.toml @@ -23,9 +23,11 @@ links = "tectonic_engine_xetex" [dependencies] libc = "^0.2" tectonic_bridge_core = { path = "../bridge_core", version = "0.0.0-dev.0" } +tectonic_bridge_freetype2 = { path = "../bridge_freetype2", version = "0.0.0-dev.0" } tectonic_bridge_flate = { path = "../bridge_flate", version = "0.0.0-dev.0" } tectonic_bridge_graphite2 = { path = "../bridge_graphite2", version = "0.0.0-dev.0" } tectonic_bridge_harfbuzz = { path = "../bridge_harfbuzz", version = "0.0.0-dev.0" } +tectonic_bridge_icu = { path = "../bridge_icu", version = "0.0.0-dev.0" } tectonic_errors = { path = "../errors", version = "0.0.0-dev.0" } tectonic_pdf_io = { path = "../pdf_io", version = "0.0.0-dev.0" } tectonic_xetex_layout = { path = "../xetex_layout", version = "0.0.0-dev.0" } @@ -42,9 +44,11 @@ external-harfbuzz = [ [package.metadata.internal_dep_versions] tectonic_bridge_core = "4e16bf963700aae59772a6fb223981ceaa9b5f57" +tectonic_bridge_freetype2 = "fe112797308ca8aa0478cd31851ab75bc5afd43c" tectonic_bridge_flate = "5933308152efb6ba206b4dc01ab6814063b835c0" tectonic_bridge_graphite2 = "2c1ffcd702a662c003bd3d7d0ca4d169784cb6ad" tectonic_bridge_harfbuzz = "2c1ffcd702a662c003bd3d7d0ca4d169784cb6ad" +tectonic_bridge_icu = "thiscommit:2023-09-17:6uIZ4lA" tectonic_cfg_support = "9d5feb40c7ac6958ee3c50604af9271eb2db2b20" tectonic_errors = "317ae79ceaa2593fb56090e37bf1f5cc24213dd9" tectonic_pdf_io = "9a8b975e76c7a27f140d0974ec3442f2347e18ad" diff --git a/crates/engine_xetex/build.rs b/crates/engine_xetex/build.rs index ceea4f67f..9243a4480 100644 --- a/crates/engine_xetex/build.rs +++ b/crates/engine_xetex/build.rs @@ -18,7 +18,10 @@ fn main() { let flate_include_dir = env::var("DEP_TECTONIC_BRIDGE_FLATE_INCLUDE").unwrap(); let graphite2_include_path = env::var("DEP_GRAPHITE2_INCLUDE_PATH").unwrap(); let graphite2_static = !env::var("DEP_GRAPHITE2_DEFINE_STATIC").unwrap().is_empty(); + let freetype_include_path = env::var("DEP_FREETYPE2_INCLUDE_PATH").unwrap(); let harfbuzz_include_path = env::var("DEP_HARFBUZZ_INCLUDE_PATH").unwrap(); + let fontconfig_include_path = env::var("DEP_FONTCONFIG_INCLUDE_PATH"); + let icu_include_path = env::var("DEP_ICUUC_INCLUDE_PATH").unwrap(); // If we want to profile, the default assumption is that we must force the // compiler to include frame pointers. We whitelist platforms that are @@ -76,11 +79,28 @@ fn main() { cxx_cfg.include(item); } + for item in freetype_include_path.split(';') { + c_cfg.include(item); + cxx_cfg.include(item); + } + for item in graphite2_include_path.split(';') { c_cfg.include(item); cxx_cfg.include(item); } + for item in icu_include_path.split(';') { + c_cfg.include(item); + cxx_cfg.include(item); + } + + if let Ok(fc_includes) = fontconfig_include_path { + for item in fc_includes.split(';') { + c_cfg.include(item); + cxx_cfg.include(item); + } + } + if graphite2_static { c_cfg.define("GRAPHITE2_STATIC", "1"); cxx_cfg.define("GRAPHITE2_STATIC", "1"); diff --git a/crates/engine_xetex/src/lib.rs b/crates/engine_xetex/src/lib.rs index 738d67d40..b1ca2ebb9 100644 --- a/crates/engine_xetex/src/lib.rs +++ b/crates/engine_xetex/src/lib.rs @@ -256,6 +256,9 @@ mod linkage { #[allow(unused_imports)] use tectonic_xetex_layout as clipyrenamehack2; + + #[allow(unused_imports)] + use tectonic_bridge_icu as clipyrenamehack3; } /// Does our resulting executable link correctly? diff --git a/crates/engine_xetex/xetex/xetex-ext.c b/crates/engine_xetex/xetex/xetex-ext.c index 11d8ade63..3a64bc05e 100644 --- a/crates/engine_xetex/xetex/xetex-ext.c +++ b/crates/engine_xetex/xetex/xetex-ext.c @@ -113,12 +113,14 @@ linebreak_start(int f, int32_t localeStrNum, uint16_t* text, int32_t textLength) } int -linebreak_next(void) +linebreak_next(int f) { if (brkIter != NULL) return ubrk_next((UBreakIterator*)brkIter); - else - return findNextGraphiteBreak(); + else { + XeTeXLayoutEngine engine = (XeTeXLayoutEngine) font_layout_engine[f]; + return findNextGraphiteBreak(engine); + } } int @@ -469,7 +471,7 @@ readFeatureNumber(const char* s, const char* e, hb_tag_t* f, int* v) } static void* -loadOTfont(PlatformFontRef fontRef, XeTeXFont font, Fixed scaled_size, char* cp1) +loadOTfont(RawPlatformFontRef fontRef, XeTeXFont font, Fixed scaled_size, char* cp1) { XeTeXLayoutEngine engine = NULL; hb_tag_t script = HB_TAG_NONE; @@ -508,11 +510,11 @@ loadOTfont(PlatformFontRef fontRef, XeTeXFont font, Fixed scaled_size, char* cp1 } if (reqEngine == 'G') { - char* tmpShapers[] = {shapers[0]}; + static const char* graphiteShaper[] = {"graphite2", NULL}; /* create a default engine so we can query the font for Graphite features; * because of font caching, it's cheap to discard this and create the real one later */ - engine = createLayoutEngine(fontRef, font, script, language, - features, nFeatures, tmpShapers, rgbValue, extend, slant, embolden); + engine = createLayoutEngineBorrowed(font, script, NULL, + features, nFeatures, graphiteShaper, rgbValue, extend, slant, embolden); if (engine == NULL) return NULL; @@ -640,6 +642,10 @@ loadOTfont(PlatformFontRef fontRef, XeTeXFont font, Fixed scaled_size, char* cp1 shapers[nShapers] = NULL; } + if (engine) { + deleteLayoutEngine(engine); + } + if (embolden != 0.0) embolden = embolden * Fix2D(scaled_size) / 100.0; @@ -652,14 +658,21 @@ loadOTfont(PlatformFontRef fontRef, XeTeXFont font, Fixed scaled_size, char* cp1 if ((loaded_font_flags & FONT_FLAGS_VERTICAL) != 0) setFontLayoutDir(font, 1); - engine = createLayoutEngine(fontRef, font, script, language, - features, nFeatures, shapers, rgbValue, extend, slant, embolden); + engine = createLayoutEngine(font, script, language, + features, nFeatures, (const char**) shapers, rgbValue, extend, slant, embolden); - if (!engine) { - // only free these if creation failed, otherwise the engine now owns them + // The layout engine clones all these - this is temporary to avoid using + // different alloc/dealloc + if (features) { free(features); + } + if (shapers) { free(shapers); - } else { + } + if (language) { + free(language); + } + if (engine) { native_font_type_flag = OTGR_FONT_FLAG; } @@ -721,7 +734,7 @@ find_native_font(char* uname, int32_t scaled_size) char* name = (char*)uname; char* varString = NULL; char* featString = NULL; - PlatformFontRef fontRef; + RawPlatformFontRef fontRef; XeTeXFont font = NULL; int index = 0; @@ -761,7 +774,7 @@ find_native_font(char* uname, int32_t scaled_size) } font = createFontFromFile(nameString + 1, index, scaled_size); if (font != NULL) { - loaded_font_design_size = D2Fix(getDesignSize(font)); + set_loaded_font_design_size(D2Fix(getDesignSize(font))); /* This is duplicated in XeTeXFontMgr::findFont! */ setReqEngine(0); @@ -834,7 +847,7 @@ find_native_font(char* uname, int32_t scaled_size) deleteFont(font); } else { if (getReqEngine() == 'O' || getReqEngine() == 'G' || - getFontTablePtr(font, kGSUB) != NULL || getFontTablePtr(font, kGPOS) != NULL) + hasFontTable(font, kGSUB) || hasFontTable(font, kGPOS)) rval = loadOTfont(fontRef, font, scaled_size, featString); /* loadOTfont failed or the above check was false */ @@ -1155,7 +1168,7 @@ make_font_def(int32_t f) uint8_t filenameLen; int fontDefLength; char* cp; - /* PlatformFontRef fontRef = 0; */ + /* RawPlatformFontRef fontRef = 0; */ float extend = 1.0; float slant = 0.0; float embolden = 0.0; @@ -1199,7 +1212,7 @@ make_font_def(int32_t f) XeTeXLayoutEngine engine; engine = (XeTeXLayoutEngine)font_layout_engine[f]; - /* fontRef = */ getFontRef(engine); + /* fontRef = getFontRef(engine); */ filename = getFontFilename(engine, &index); assert(filename); @@ -1293,7 +1306,7 @@ make_font_def(int32_t f) cp += 4; } - free((char*) filename); + freeFontFilename((char*) filename); return fontDefLength; } @@ -2112,7 +2125,7 @@ aat_print_font_name(int what, CFDictionaryRef attributes, int param1, int param2 void print_glyph_name(int32_t font, int32_t gid) { - const char* s; + const char* s = NULL; int len = 0; #ifdef XETEX_MAC if (font_area[font] == AAT_FONT_FLAG) { @@ -2127,6 +2140,8 @@ print_glyph_name(int32_t font, int32_t gid) } while (len-- > 0) print_char(*s++); + if (s) + freeGlyphName(s); } int32_t real_get_native_word_cp(void* pNode, int side) diff --git a/crates/engine_xetex/xetex/xetex-ext.h b/crates/engine_xetex/xetex/xetex-ext.h index 78f15bb1c..f8bda3ea9 100644 --- a/crates/engine_xetex/xetex/xetex-ext.h +++ b/crates/engine_xetex/xetex/xetex-ext.h @@ -107,7 +107,7 @@ typedef struct { BEGIN_EXTERN_C void linebreak_start(int f, int32_t localeStrNum, uint16_t* text, int32_t textLength); -int linebreak_next(void); +int linebreak_next(int f); int get_encoding_mode_and_info(int32_t* info); void print_utf8_str(const unsigned char* str, int len); void print_chars(const unsigned short* str, int len); diff --git a/crates/engine_xetex/xetex/xetex-xetex0.c b/crates/engine_xetex/xetex/xetex-xetex0.c index 8b3098c4f..b9cade3dc 100644 --- a/crates/engine_xetex/xetex/xetex-xetex0.c +++ b/crates/engine_xetex/xetex/xetex-xetex0.c @@ -11350,9 +11350,9 @@ load_native_font(int32_t u, str_number nom, str_number aire, scaled_t s) if (s >= 0) actual_size = s; else if (s != -1000) - actual_size = xn_over_d(loaded_font_design_size, -(int32_t) s, 1000); + actual_size = xn_over_d(get_loaded_font_design_size(), -(int32_t) s, 1000); else - actual_size = loaded_font_design_size; + actual_size = get_loaded_font_design_size(); if (pool_ptr + name_length > pool_size) overflow("pool size", pool_size - init_pool_ptr); @@ -11416,7 +11416,7 @@ load_native_font(int32_t u, str_number nom, str_number aire, scaled_t s) font_check[font_ptr].s1 = 0; font_check[font_ptr].s0 = 0; font_glue[font_ptr] = TEX_NULL; - font_dsize[font_ptr] = loaded_font_design_size; + font_dsize[font_ptr] = get_loaded_font_design_size(); font_size[font_ptr] = actual_size; if (native_font_type_flag == AAT_FONT_FLAG) @@ -11494,7 +11494,7 @@ void do_locale_linebreaks(int32_t s, int32_t len) offs = 0; do { prevOffs = offs; - offs = linebreak_next(); + offs = linebreak_next(main_f); if (offs > 0) { if (prevOffs != 0) { if (use_penalty) { @@ -18420,8 +18420,6 @@ close_files_and_terminate(void) { int32_t k; - terminate_font_manager(); - for (k = 0; k <= 15; k++) { if (write_open[k]) ttstub_output_close(write_file[k]); diff --git a/crates/xetex_layout/Cargo.toml b/crates/xetex_layout/Cargo.toml index 81cfc91b4..daf31ce35 100644 --- a/crates/xetex_layout/Cargo.toml +++ b/crates/xetex_layout/Cargo.toml @@ -26,18 +26,25 @@ tectonic_bridge_freetype2 = { path = "../bridge_freetype2", version = "0.0.0-dev tectonic_bridge_graphite2 = { path = "../bridge_graphite2", version = "0.0.0-dev.0" } tectonic_bridge_harfbuzz = { path = "../bridge_harfbuzz", version = "0.0.0-dev.0" } tectonic_bridge_icu = { path = "../bridge_icu", version = "0.0.0-dev.0" } - -[target.'cfg(not(target_os = "macos"))'.dependencies] -tectonic_bridge_fontconfig = { path = "../bridge_fontconfig", version = "0.0.0-dev.0" } +tectonic_io_base = { path = "../io_base", version = "0.0.0-dev.0" } +libc = "0.2" +enrede = { version = "0.2", features = ["alloc"] } [build-dependencies] cc = "^1.0.66" tectonic_cfg_support = { path = "../cfg_support", version = "0.0.0-dev.0" } +[target.'cfg(not(target_os = "macos"))'.dependencies] +tectonic_bridge_fontconfig = { path = "../bridge_fontconfig", version = "0.0.0-dev.0" } + +[target.'cfg(target_os = "macos")'.dependencies] +tectonic_mac_core = { path = "../mac_core", version = "0.0.0-dev.0" } + [features] external-harfbuzz = ["tectonic_bridge_harfbuzz/external-harfbuzz"] [package.metadata.internal_dep_versions] +tectonic_mac_core = "6932d5f15fec0fb5c219b887bdd11b72641af07a" tectonic_bridge_core = "thiscommit:2021-01-16:wie2Ejoh" tectonic_bridge_fontconfig = "89268e8317eee0e23f9c0d572aa764d4504d85b5" tectonic_bridge_graphite2 = "2722731f9e32c6963fe8c8566a201b33672c5c5a" @@ -45,3 +52,4 @@ tectonic_bridge_freetype2 = "2c1ffcd702a662c003bd3d7d0ca4d169784cb6ad" tectonic_bridge_harfbuzz = "2c1ffcd702a662c003bd3d7d0ca4d169784cb6ad" tectonic_bridge_icu = "thiscommit:2023-09-17:6uIZ4lA" tectonic_cfg_support = "thiscommit:aeRoo7oa" +tectonic_io_base = "41168a9fa2902401afc79e60bbefa638bde6df2d" diff --git a/crates/xetex_layout/build.rs b/crates/xetex_layout/build.rs index 2d5e3bc62..288e77296 100644 --- a/crates/xetex_layout/build.rs +++ b/crates/xetex_layout/build.rs @@ -5,137 +5,18 @@ //! use different font-finding and layout frameworks depending on the target //! platform. //! -//! Specifically, on macOS we use CoreText. On all other platforms, including +//! Specifically, on macOS we use `CoreText`. On all other platforms, including //! Windows, we use Fontconfig to discover fonts. use std::{env, path::PathBuf}; -use tectonic_cfg_support::target_cfg; fn main() { - let target = env::var("TARGET").unwrap(); let out_dir = env::var("OUT_DIR").unwrap(); let manifest_dir: PathBuf = env::var("CARGO_MANIFEST_DIR").unwrap().into(); - let is_mac_os = target_cfg!(target_os = "macos"); - // Include paths and settings exported by our internal dependencies. + // Copy the generated header file for exported functions. - let core_include_dir = env::var("DEP_TECTONIC_BRIDGE_CORE_INCLUDE").unwrap(); - let fontconfig_include_path = - env::var("DEP_FONTCONFIG_INCLUDE_PATH").unwrap_or_else(|_| String::new()); - let freetype2_include_path = env::var("DEP_FREETYPE2_INCLUDE_PATH").unwrap(); - let graphite2_include_path = env::var("DEP_GRAPHITE2_INCLUDE_PATH").unwrap(); - let graphite2_static = !env::var("DEP_GRAPHITE2_DEFINE_STATIC").unwrap().is_empty(); - let harfbuzz_include_path = env::var("DEP_HARFBUZZ_INCLUDE_PATH").unwrap(); - let icu_include_path = env::var("DEP_ICUUC_INCLUDE_PATH").unwrap(); - - // Define the C++ support library. - - let mut cppcfg = cc::Build::new(); - - let cppflags = [ - "-std=c++17", - "-Wall", - "-Wdate-time", - "-Wendif-labels", - "-Wextra", - "-Wformat=2", - "-Wlogical-op", - "-Wmissing-declarations", - "-Wmissing-include-dirs", - "-Wpointer-arith", - "-Wredundant-decls", - "-Wsuggest-attribute=noreturn", - "-Wsuggest-attribute=format", - "-Wshadow", - "-Wswitch-bool", - "-Wundef", - // TODO: Fix existing warnings before enabling these: - // "-Wdouble-promotion", - // "-Wcast-align", - // "-Wconversion", - // "-Wmissing-variable-declarations", - "-Wextra-semi", - // "-Wsuggest-attribute=const", - // "-Wsuggest-attribute=pure", - // "-Wunreachable-code-aggresive", - "-Wno-unused-parameter", - "-Wno-implicit-fallthrough", - "-fno-exceptions", - "-fno-rtti", - ]; - - for flag in &cppflags { - cppcfg.flag_if_supported(flag); - } - - fn compile(cfg: &mut cc::Build, s: &str) { - cfg.file(s); - println!("cargo:rerun-if-changed={s}"); - } - - cppcfg - .cpp(true) - .flag("-Wall") - .include("layout") - .include(&core_include_dir); - - for item in fontconfig_include_path.split(';') { - cppcfg.include(item); - } - - for item in harfbuzz_include_path.split(';') { - cppcfg.include(item); - } - - for item in freetype2_include_path.split(';') { - cppcfg.include(item); - } - - for item in graphite2_include_path.split(';') { - cppcfg.include(item); - } - - for item in icu_include_path.split(';') { - cppcfg.include(item); - } - - compile(&mut cppcfg, "layout/xetex-XeTeXFontInst.cpp"); - compile(&mut cppcfg, "layout/xetex-XeTeXFontMgr.cpp"); - compile(&mut cppcfg, "layout/xetex-XeTeXLayoutInterface.cpp"); - - if graphite2_static { - cppcfg.define("GRAPHITE2_STATIC", "1"); - } - - // Platform-specific adjustments: - - if is_mac_os { - cppcfg.define("XETEX_MAC", Some("1")); - compile(&mut cppcfg, "layout/xetex-XeTeXFontInst_Mac.cpp"); - compile(&mut cppcfg, "layout/xetex-XeTeXFontMgr_Mac.mm"); - println!("cargo:rustc-link-lib=framework=Foundation"); - println!("cargo:rustc-link-lib=framework=CoreFoundation"); - println!("cargo:rustc-link-lib=framework=CoreGraphics"); - println!("cargo:rustc-link-lib=framework=CoreText"); - println!("cargo:rustc-link-lib=framework=AppKit"); - } - - if !is_mac_os { - // At the moment we use Fontconfig on both Linux and Windows. - compile(&mut cppcfg, "layout/xetex-XeTeXFontMgr_FC.cpp"); - } - - if target.contains("-msvc") { - cppcfg.flag("/EHsc"); - } - - // OK, back to generic build rules. - - cppcfg.compile("libtectonic_xetex_layout.a"); - - // Copy the static header file for C preprocessing convenience. - - let mut main_header_src = manifest_dir; + let mut main_header_src = manifest_dir.clone(); main_header_src.push("layout"); main_header_src.push("tectonic_xetex_layout.h"); @@ -144,28 +25,6 @@ fn main() { std::fs::copy(&main_header_src, &main_header_dest).expect("failed to copy main header"); - // Cargo exposes this as the environment variable DEP_XXX_INCLUDE_PATH, - // where XXX is the "links" setting in Cargo.toml. This is the key element - // that allows us to have a network of crates containing both C/C++ and Rust - // code that all interlink. - - print!("cargo:include-path={out_dir}"); - - for item in harfbuzz_include_path.split(';') { - print!(";{item}"); - } - - for item in freetype2_include_path.split(';') { - print!(";{item}"); - } - - for item in graphite2_include_path.split(';') { - print!(";{item}"); - } - - for item in icu_include_path.split(';') { - print!(";{item}"); - } - - println!(); + println!("cargo:rerun-if-changed=layout/tectonic_xetex_layout.h"); + println!("cargo:include-path={out_dir}"); } diff --git a/crates/xetex_layout/cbindgen.toml b/crates/xetex_layout/cbindgen.toml new file mode 100644 index 000000000..8337ea6ac --- /dev/null +++ b/crates/xetex_layout/cbindgen.toml @@ -0,0 +1,25 @@ +language = "C" +cpp_compat = true +style = "type" +include_guard = "XETEX_LAYOUT_BINDINGS_H" +includes = ["harfbuzz/hb.h", "harfbuzz/hb-ft.h"] +after_includes = """ +#ifdef XETEX_MAC +#include +#else +#include +#endif + +typedef struct XeTeXFont_rec* XeTeXFont; +typedef struct XeTeXLayoutEngine_rec* XeTeXLayoutEngine;""" + +[export.rename] +"Tag" = "hb_tag_t" +"Feature" = "hb_feature_t" +"Font" = "hb_font_t" + +[defines] +"target_os = macos" = "XETEX_MAC" + +[enum] +prefix_with_name = true diff --git a/crates/xetex_layout/layout/tectonic_xetex_layout.h b/crates/xetex_layout/layout/tectonic_xetex_layout.h index 1c94229da..c60df9740 100644 --- a/crates/xetex_layout/layout/tectonic_xetex_layout.h +++ b/crates/xetex_layout/layout/tectonic_xetex_layout.h @@ -1,208 +1,263 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012-2015 by Khaled Hosny +#ifndef XETEX_LAYOUT_BINDINGS_H +#define XETEX_LAYOUT_BINDINGS_H + +#include +#include +#include +#include +#include "harfbuzz/hb.h" +#include "harfbuzz/hb-ft.h" +#ifdef XETEX_MAC +#include +#else +#include +#endif - SIL Author(s): Jonathan Kew +typedef struct XeTeXFont_rec* XeTeXFont; +typedef struct XeTeXLayoutEngine_rec* XeTeXLayoutEngine; -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: +#if !defined(XETEX_MAC) +#define FONT_FAMILY_NAME 1 +#endif -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. +#if !defined(XETEX_MAC) +#define FONT_STYLE_NAME 2 +#endif -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +#if !defined(XETEX_MAC) +#define FONT_FULL_NAME 4 +#endif -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ +#if !defined(XETEX_MAC) +#define PREFERRED_FAMILY_NAME 16 +#endif -/* Formerly known as [xetex-]XeTeXLayoutInterface.h */ +#if !defined(XETEX_MAC) +#define PREFERRED_SUBFAMILY_NAME 17 +#endif -#ifndef XETEX_LAYOUT_INTERFACE_H -#define XETEX_LAYOUT_INTERFACE_H 1 +#define LEFT_SIDE 0 -#include "tectonic_bridge_core.h" +#define RIGHT_SIDE 1 -/* harfbuzz: hb_tag_t and hb_font_t used below */ -#include +typedef struct { + float xMin; + float yMin; + float xMax; + float yMax; +} GlyphBBox; +typedef struct { + float x; + float y; +} FloatPoint; -/* Set up our types */ +typedef uint32_t OTTag; -#ifdef XETEX_MAC +#if !defined(XETEX_MAC) +typedef int32_t Fixed; +#endif -#include -typedef CTFontDescriptorRef PlatformFontRef; +#if !defined(XETEX_MAC) +typedef FcPattern *RawPlatformFontRef; +#endif -#else /* XETEX_MAC */ +#if defined(XETEX_MAC) +typedef CTFontDescriptorRef RawPlatformFontRef; +#endif -#include -typedef FcPattern* PlatformFontRef; -typedef int32_t Fixed; /* macOS defines Fixed in system headers */ +#ifdef __cplusplus +extern "C" { +#endif // __cplusplus -#endif /* XETEX_MAC */ +int32_t getCachedGlyphBBox(uint16_t font_id, uint16_t glyph_id, GlyphBBox *bbox); -typedef struct { - float x; - float y; -} FloatPoint; +void cacheGlyphBBox(uint16_t font_id, uint16_t glyph_id, const GlyphBBox *bbox); -typedef struct { - float xMin; - float yMin; - float xMax; - float yMax; -} GlyphBBox; +void set_cp_code(int32_t font_num, uint32_t code, int32_t side, int32_t value); -typedef uint32_t OTTag; -typedef uint16_t GlyphID; +int32_t get_cp_code(int32_t font_num, uint32_t code, int32_t side); -typedef struct XeTeXFont_rec* XeTeXFont; -typedef struct XeTeXLayoutEngine_rec* XeTeXLayoutEngine; +XeTeXLayoutEngine createLayoutEngine(XeTeXFont font, + hb_tag_t script, + char *language, + hb_feature_t *features, + int n_features, + const char **shapers, + uint32_t rgb_value, + float extend, + float slant, + float embolden); -/* Now we can defined our C APIs */ +XeTeXLayoutEngine createLayoutEngineBorrowed(XeTeXFont font, + hb_tag_t script, + char *language, + hb_feature_t *features, + int n_features, + const char **shapers, + uint32_t rgb_value, + float extend, + float slant, + float embolden); -BEGIN_EXTERN_C +void deleteLayoutEngine(XeTeXLayoutEngine this_); -extern Fixed loaded_font_design_size; +XeTeXFont getFont(XeTeXLayoutEngine engine); -#define LEFT_SIDE 0 -#define RIGHT_SIDE 1 +float getExtendFactor(XeTeXLayoutEngine engine); -void set_cp_code(int fontNum, unsigned int code, int side, int value); -int get_cp_code(int fontNum, unsigned int code, int side); +float getSlantFactor(XeTeXLayoutEngine engine); -int getCachedGlyphBBox(uint16_t fontID, uint16_t glyphID, GlyphBBox* bbox); -void cacheGlyphBBox(uint16_t fontID, uint16_t glyphID, const GlyphBBox* bbox); +float getEmboldenFactor(XeTeXLayoutEngine engine); -void terminate_font_manager(void); -void destroy_font_manager(void); +float getPointSize(XeTeXLayoutEngine engine); -XeTeXFont createFont(PlatformFontRef fontRef, Fixed pointSize); -XeTeXFont createFontFromFile(const char* filename, int index, Fixed pointSize); +void getAscentAndDescent(XeTeXLayoutEngine engine, float *ascent, float *descent); -void setFontLayoutDir(XeTeXFont font, int vertical); +void getCapAndXHeight(XeTeXLayoutEngine engine, float *capheight, float *xheight); -PlatformFontRef findFontByName(const char* name, char* var, double size); +int getDefaultDirection(XeTeXLayoutEngine engine); -char getReqEngine(void); -void setReqEngine(char reqEngine); -const char* getFullName(PlatformFontRef fontRef); +uint32_t getRgbValue(XeTeXLayoutEngine engine); -char* getFontFilename(XeTeXLayoutEngine engine, uint32_t* index); +void getGlyphBounds(XeTeXLayoutEngine engine, uint32_t glyph_id, GlyphBBox *bbox); -double getDesignSize(XeTeXFont font); +float getGlyphWidthFromEngine(XeTeXLayoutEngine engine, uint32_t glyph_id); -void deleteFont(XeTeXFont font); +void getGlyphHeightDepth(XeTeXLayoutEngine engine, uint32_t glyph_id, float *height, float *depth); -void* getFontTablePtr(XeTeXFont font, uint32_t tableTag); +void getGlyphSidebearings(XeTeXLayoutEngine engine, uint32_t glyph_id, float *lsb, float *rsb); -Fixed getSlant(XeTeXFont font); +float getGlyphItalCorr(XeTeXLayoutEngine engine, uint32_t glyph_id); -unsigned int countScripts(XeTeXFont font); -unsigned int countLanguages(XeTeXFont font, hb_tag_t script); -unsigned int countFeatures(XeTeXFont font, hb_tag_t script, hb_tag_t language); -unsigned int countGlyphs(XeTeXFont font); +uint32_t mapCharToGlyph(XeTeXLayoutEngine engine, uint32_t char_code); -hb_tag_t getIndScript(XeTeXFont font, unsigned int index); -hb_tag_t getIndLanguage(XeTeXFont font, hb_tag_t script, unsigned int index); -hb_tag_t getIndFeature(XeTeXFont font, hb_tag_t script, hb_tag_t language, unsigned int index); +int getFontCharRange(XeTeXLayoutEngine engine, int req_first); -float getGlyphWidth(XeTeXFont font, uint32_t gid); +int mapGlyphToIndex(XeTeXLayoutEngine engine, const char *glyph_name); -XeTeXLayoutEngine createLayoutEngine(PlatformFontRef fontRef, XeTeXFont font, hb_tag_t script, char *language, - hb_feature_t* features, int nFeatures, char **shapers, uint32_t rgbValue, - float extend, float slant, float embolden); +bool usingGraphite(XeTeXLayoutEngine engine); -void deleteLayoutEngine(XeTeXLayoutEngine engine); +bool usingOpenType(XeTeXLayoutEngine engine); -XeTeXFont getFont(XeTeXLayoutEngine engine); -PlatformFontRef getFontRef(XeTeXLayoutEngine engine); +bool isOpenTypeMathFont(XeTeXLayoutEngine engine); -float getExtendFactor(XeTeXLayoutEngine engine); -float getSlantFactor(XeTeXLayoutEngine engine); -float getEmboldenFactor(XeTeXLayoutEngine engine); +hb_font_t *ttxl_get_hb_font(XeTeXLayoutEngine engine); -int layoutChars(XeTeXLayoutEngine engine, uint16_t* chars, int32_t offset, int32_t count, int32_t max, - bool rightToLeft); +int layoutChars(XeTeXLayoutEngine engine, + uint16_t *chars, + int32_t offset, + int32_t count, + int32_t max, + bool rtl); + +const char *getFontFilename(XeTeXLayoutEngine engine, uint32_t *index); + +void freeFontFilename(const char *filename); + +void getGlyphs(XeTeXLayoutEngine engine, uint32_t *glyphs); -void getGlyphs(XeTeXLayoutEngine engine, uint32_t* glyphs); void getGlyphAdvances(XeTeXLayoutEngine engine, float *advances); -void getGlyphPositions(XeTeXLayoutEngine engine, FloatPoint* positions); -float getPointSize(XeTeXLayoutEngine engine); +void getGlyphPositions(XeTeXLayoutEngine engine, FloatPoint *positions); -void getAscentAndDescent(XeTeXLayoutEngine engine, float* ascent, float* descent); -void getCapAndXHeight(XeTeXLayoutEngine engine, float* capheight, float* xheight); +uint32_t countGraphiteFeatures(XeTeXLayoutEngine engine); -int getDefaultDirection(XeTeXLayoutEngine engine); +uint32_t getGraphiteFeatureCode(XeTeXLayoutEngine engine, uint32_t index); -uint32_t getRgbValue(XeTeXLayoutEngine engine); +uint32_t countGraphiteFeatureSettings(XeTeXLayoutEngine engine, uint32_t feature_id); -void getGlyphBounds(XeTeXLayoutEngine engine, uint32_t glyphID, GlyphBBox* bbox); +uint32_t getGraphiteFeatureSettingCode(XeTeXLayoutEngine engine, + uint32_t feature_id, + uint32_t index); -float getGlyphWidthFromEngine(XeTeXLayoutEngine engine, uint32_t glyphID); +uint32_t getGraphiteFeatureDefaultSetting(XeTeXLayoutEngine engine, uint32_t feature_id); -void getGlyphHeightDepth(XeTeXLayoutEngine engine, uint32_t glyphID, float* height, float* depth); +const char *getGraphiteFeatureLabel(XeTeXLayoutEngine engine, uint32_t feature_id); -void getGlyphSidebearings(XeTeXLayoutEngine engine, uint32_t glyphID, float* lsb, float* rsb); +const char *getGraphiteFeatureSettingLabel(XeTeXLayoutEngine engine, + uint32_t feature_id, + uint32_t setting_id); -float getGlyphItalCorr(XeTeXLayoutEngine engine, uint32_t glyphID); +bool findGraphiteFeature(XeTeXLayoutEngine engine, + const char *s, + const char *e, + hb_tag_t *f, + int *v); -uint32_t mapCharToGlyph(XeTeXLayoutEngine engine, uint32_t charCode); +long findGraphiteFeatureNamed(XeTeXLayoutEngine engine, const char *name, int namelength); -int mapGlyphToIndex(XeTeXLayoutEngine engine, const char* glyphName); +long findGraphiteFeatureSettingNamed(XeTeXLayoutEngine engine, + uint32_t id, + const char *name, + int namelength); -const char* getGlyphName(XeTeXFont font, uint16_t gid, int* len); +bool initGraphiteBreaking(XeTeXLayoutEngine engine, const uint16_t *txt_ptr, unsigned int txt_len); -int getFontCharRange(XeTeXLayoutEngine engine, int reqFirst); +int findNextGraphiteBreak(XeTeXLayoutEngine engine); -/* graphite interface functions... */ -bool initGraphiteBreaking(XeTeXLayoutEngine engine, const uint16_t* txtPtr, int txtLen); -int findNextGraphiteBreak(void); +bool hasFontTable(XeTeXFont font, OTTag table_tag); -bool usingOpenType(XeTeXLayoutEngine engine); -bool usingGraphite(XeTeXLayoutEngine engine); -bool isOpenTypeMathFont(XeTeXLayoutEngine engine); +Fixed getSlant(XeTeXFont font); -bool findGraphiteFeature(XeTeXLayoutEngine engine, const char* s, const char* e, hb_tag_t* f, int* v); +unsigned int countGlyphs(XeTeXFont font); + +float getGlyphWidth(XeTeXFont font, uint32_t gid); + +void setFontLayoutDir(XeTeXFont font, int vertical); + +hb_tag_t getIndScript(XeTeXFont font, unsigned int index); + +hb_tag_t getIndLanguage(XeTeXFont font, hb_tag_t script, unsigned int index); + +hb_tag_t getIndFeature(XeTeXFont font, hb_tag_t script, hb_tag_t language, unsigned int index); + +const char *getGlyphName(XeTeXFont font, uint16_t gid, int *len); + +void freeGlyphName(char *name); -uint32_t countGraphiteFeatures(XeTeXLayoutEngine engine); -uint32_t getGraphiteFeatureCode(XeTeXLayoutEngine engine, uint32_t index); -uint32_t countGraphiteFeatureSettings(XeTeXLayoutEngine engine, uint32_t feature); -uint32_t getGraphiteFeatureSettingCode(XeTeXLayoutEngine engine, uint32_t feature, uint32_t index); -uint32_t getGraphiteFeatureDefaultSetting(XeTeXLayoutEngine engine, uint32_t feature); -char* getGraphiteFeatureLabel(XeTeXLayoutEngine engine, uint32_t feature); -char* getGraphiteFeatureSettingLabel(XeTeXLayoutEngine engine, uint32_t feature, uint32_t setting); -long findGraphiteFeatureNamed(XeTeXLayoutEngine engine, const char* name, int namelength); -long findGraphiteFeatureSettingNamed(XeTeXLayoutEngine engine, uint32_t feature, const char* name, int namelength); - -/* Extra APIs needed to encapsulate across the crate boundaries */ -hb_font_t *ttxl_get_hb_font(XeTeXLayoutEngine engine); float ttxl_font_units_to_points(XeTeXFont font, float units); + float ttxl_font_points_to_units(XeTeXFont font, float points); + float ttxl_font_get_point_size(XeTeXFont font); -const char *ttxl_platfont_get_desc(PlatformFontRef fontRef); -#ifdef XETEX_MAC -char* getFileNameFromCTFont(CTFontRef ctFontRef, uint32_t *index); +XeTeXFont createFont(RawPlatformFontRef font_ref, Fixed point_size); + +XeTeXFont createFontFromFile(const char *filename, int index, Fixed point_size); + +void deleteFont(XeTeXFont font); + +unsigned int countScripts(XeTeXFont font); + +unsigned int countLanguages(XeTeXFont font, hb_tag_t script); + +unsigned int countFeatures(XeTeXFont font, hb_tag_t script, hb_tag_t language); + +#if defined(XETEX_MAC) +const char *getFileNameFromCTFont(CTFontRef ct_font, uint32_t *index); #endif -END_EXTERN_C +Fixed get_loaded_font_design_size(void); + +void set_loaded_font_design_size(Fixed val); + +void destroy_font_manager(void); + +RawPlatformFontRef findFontByName(const char *name, char *var, double size); + +char getReqEngine(void); + +void setReqEngine(char engine); + +const char *getFullName(RawPlatformFontRef font); + +double getDesignSize(XeTeXFont font); + +const char *ttxl_platfont_get_desc(RawPlatformFontRef font); + +#ifdef __cplusplus +} // extern "C" +#endif // __cplusplus -#endif /* XETEX_LAYOUT_INTERFACE_H */ +#endif /* XETEX_LAYOUT_BINDINGS_H */ diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontInst.cpp b/crates/xetex_layout/layout/xetex-XeTeXFontInst.cpp deleted file mode 100644 index a93fbe2fc..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontInst.cpp +++ /dev/null @@ -1,561 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -/* - * file name: XeTeXFontInst.cpp - * - * created on: 2005-10-22 - * created by: Jonathan Kew - * - * originally based on PortableFontInstance.cpp from ICU - */ - -#include "tectonic_xetex_layout.h" -#include "xetex-XeTeXFontInst.h" - -#include - -/* Return NAME with any leading path stripped off. This returns a - pointer into NAME. For example, `basename ("/foo/bar.baz")' - returns "bar.baz". */ - -static const char * -xbasename (const char *name) -{ - const char *base = name; - const char *p; - - for (p = base; *p; p++) { - if (IS_DIR_SEP(*p)) - base = p + 1; - } - - return base; -} - - -FT_Library gFreeTypeLibrary = 0; - -static hb_font_funcs_t* hbFontFuncs = NULL; - -XeTeXFontInst::XeTeXFontInst(const char* pathname, int index, float pointSize, int &status) - : m_unitsPerEM(0) - , m_pointSize(pointSize) - , m_ascent(0) - , m_descent(0) - , m_capHeight(0) - , m_xHeight(0) - , m_italicAngle(0) - , m_vertical(false) - , m_filename(NULL) - , m_index(0) - , m_ftFace(0) - , m_backingData(NULL) - , m_backingData2(NULL) - , m_hbFont(NULL) -{ - if (pathname != NULL) - initialize(pathname, index, status); -} - -XeTeXFontInst::~XeTeXFontInst() -{ - if (m_ftFace != 0) { - FT_Done_Face(m_ftFace); - m_ftFace = 0; - } - hb_font_destroy(m_hbFont); - free(m_backingData); - free(m_backingData2); - free(m_filename); -} - -/* HarfBuzz font functions */ -static hb_bool_t -_get_nominal_glyph(hb_font_t*, void *font_data, hb_codepoint_t ch, hb_codepoint_t *gid, void*) -{ - FT_Face face = (FT_Face) font_data; - *gid = FT_Get_Char_Index (face, ch); - return *gid != 0; -} - -static hb_bool_t -_get_variation_glyph(hb_font_t*, void *font_data, hb_codepoint_t ch, hb_codepoint_t vs, hb_codepoint_t *gid, void*) -{ - FT_Face face = (FT_Face) font_data; - *gid = FT_Face_GetCharVariantIndex (face, ch, vs); - return *gid != 0; -} - -static FT_Fixed -_get_glyph_advance(FT_Face face, FT_UInt gid, bool vertical) -{ - FT_Error error; - FT_Fixed advance; - int flags = FT_LOAD_NO_SCALE; - - if (vertical) - flags |= FT_LOAD_VERTICAL_LAYOUT; - - error = FT_Get_Advance(face, gid, flags, &advance); - if (error) - advance = 0; - - /* FreeType's vertical metrics grows downward */ - if (vertical) - advance = -advance; - - return advance; -} - -static hb_position_t -_get_glyph_h_advance(hb_font_t*, void *font_data, hb_codepoint_t gid, void*) -{ - return _get_glyph_advance((FT_Face) font_data, gid, false); -} - -static hb_position_t -_get_glyph_v_advance(hb_font_t*, void *font_data, hb_codepoint_t gid, void*) -{ - return _get_glyph_advance((FT_Face) font_data, gid, true); -} - -static hb_bool_t -_get_glyph_h_origin(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_position_t *x, hb_position_t *y, void*) -{ - // horizontal origin is (0, 0) - return true; -} - -static hb_bool_t -_get_glyph_v_origin(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_position_t *x, hb_position_t *y, void*) -{ - // vertical origin is (0, 0) for now - return true; - - // TODO - // Keep the code below for reference, for now we want to keep vertical - // origin at (0, 0) for compatibility with pre-0.9999. - // Reconsider this (e.g. using BASE table) when we get around overhauling - // the text directionality model and implementing real vertical typesetting. - - FT_Face face = (FT_Face) font_data; - FT_Error error; - - error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE); - if (!error) { - *x = face->glyph->metrics.horiBearingX - face->glyph->metrics.vertBearingX; - *y = face->glyph->metrics.horiBearingY - (-face->glyph->metrics.vertBearingY); - } - - return !error; -} - -static hb_position_t -_get_glyph_h_kerning(hb_font_t*, void *font_data, hb_codepoint_t gid1, hb_codepoint_t gid2, void*) -{ - FT_Face face = (FT_Face) font_data; - FT_Error error; - FT_Vector kerning; - hb_position_t ret; - - error = FT_Get_Kerning (face, gid1, gid2, FT_KERNING_UNSCALED, &kerning); - if (error) - ret = 0; - else - ret = kerning.x; - return ret; -} - -static hb_position_t -_get_glyph_v_kerning(hb_font_t*, void *font_data, hb_codepoint_t gid1, hb_codepoint_t gid2, void*) -{ - /* FreeType does not support vertical kerning */ - return 0; -} - -static hb_bool_t -_get_glyph_extents(hb_font_t*, void *font_data, hb_codepoint_t gid, hb_glyph_extents_t *extents, void*) -{ - FT_Face face = (FT_Face) font_data; - FT_Error error; - - error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE); - if (!error) { - extents->x_bearing = face->glyph->metrics.horiBearingX; - extents->y_bearing = face->glyph->metrics.horiBearingY; - extents->width = face->glyph->metrics.width; - extents->height = -face->glyph->metrics.height; - } - - return !error; -} - -static hb_bool_t -_get_glyph_contour_point(hb_font_t*, void *font_data, hb_codepoint_t gid, unsigned int point_index, hb_position_t *x, hb_position_t *y, void*) -{ - FT_Face face = (FT_Face) font_data; - FT_Error error; - bool ret = false; - - error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE); - if (!error) { - if (face->glyph->format == FT_GLYPH_FORMAT_OUTLINE) { - if (point_index < (unsigned int) face->glyph->outline.n_points) { - *x = face->glyph->outline.points[point_index].x; - *y = face->glyph->outline.points[point_index].y; - ret = true; - } - } - } - - return ret; -} - -static hb_bool_t -_get_glyph_name(hb_font_t *, void *font_data, hb_codepoint_t gid, char *name, unsigned int size, void *) -{ - FT_Face face = (FT_Face) font_data; - bool ret = false; - - ret = !FT_Get_Glyph_Name (face, gid, name, size); - if (ret && (size && !*name)) - ret = false; - - return ret; -} - -static hb_font_funcs_t * -_get_font_funcs(void) -{ - static hb_font_funcs_t* funcs = hb_font_funcs_create(); - - hb_font_funcs_set_nominal_glyph_func (funcs, _get_nominal_glyph, NULL, NULL); - hb_font_funcs_set_variation_glyph_func (funcs, _get_variation_glyph, NULL, NULL); - hb_font_funcs_set_glyph_h_advance_func (funcs, _get_glyph_h_advance, NULL, NULL); - hb_font_funcs_set_glyph_v_advance_func (funcs, _get_glyph_v_advance, NULL, NULL); - hb_font_funcs_set_glyph_h_origin_func (funcs, _get_glyph_h_origin, NULL, NULL); - hb_font_funcs_set_glyph_v_origin_func (funcs, _get_glyph_v_origin, NULL, NULL); - hb_font_funcs_set_glyph_h_kerning_func (funcs, _get_glyph_h_kerning, NULL, NULL); - hb_font_funcs_set_glyph_v_kerning_func (funcs, _get_glyph_v_kerning, NULL, NULL); - hb_font_funcs_set_glyph_extents_func (funcs, _get_glyph_extents, NULL, NULL); - hb_font_funcs_set_glyph_contour_point_func (funcs, _get_glyph_contour_point, NULL, NULL); - hb_font_funcs_set_glyph_name_func (funcs, _get_glyph_name, NULL, NULL); - - return funcs; -} - -static hb_blob_t * -_get_table(hb_face_t *, hb_tag_t tag, void *user_data) -{ - FT_Face face = (FT_Face) user_data; - FT_ULong length = 0; - FT_Byte *table; - FT_Error error; - hb_blob_t* blob = NULL; - - error = FT_Load_Sfnt_Table(face, tag, 0, NULL, &length); - if (!error) { - table = (FT_Byte *) xmalloc(length * sizeof(char)); - if (table != NULL) { - error = FT_Load_Sfnt_Table(face, tag, 0, (FT_Byte*)table, &length); - if (!error) { - blob = hb_blob_create((const char*) table, length, HB_MEMORY_MODE_WRITABLE, table, free); - } else { - free(table); - } - } - } - - return blob; -} - -void -XeTeXFontInst::initialize(const char* pathname, int index, int &status) -{ - TT_Postscript *postTable; - TT_OS2* os2Table; - FT_Error error; - hb_face_t *hbFace; - - if (!gFreeTypeLibrary) { - error = FT_Init_FreeType(&gFreeTypeLibrary); - if (error) - _tt_abort("FreeType initialization failed, error %d", error); - } - - // Here we emulate some logic that was originally in find_native_font(); - rust_input_handle_t handle = ttstub_input_open (pathname, TTBC_FILE_FORMAT_OPEN_TYPE, 0); - if (handle == INVALID_HANDLE) - handle = ttstub_input_open (pathname, TTBC_FILE_FORMAT_TRUE_TYPE, 0); - if (handle == INVALID_HANDLE) - handle = ttstub_input_open (pathname, TTBC_FILE_FORMAT_TYPE1, 0); - if (handle == INVALID_HANDLE) { - status = 1; - return; - } - - size_t sz = ttstub_input_get_size (handle); - m_backingData = (FT_Byte *) xmalloc (sz); - ssize_t r = ttstub_input_read (handle, (char *) m_backingData, sz); - if (r < 0 || (size_t) r != sz) - _tt_abort("failed to read font file"); - ttstub_input_close(handle); - - error = FT_New_Memory_Face(gFreeTypeLibrary, m_backingData, sz, index, &m_ftFace); - - if (error || !FT_IS_SCALABLE(m_ftFace)) { - status = 1; - return; - } - - /* for non-sfnt-packaged fonts (presumably Type 1), see if there is an AFM file we can attach */ - if (index == 0 && !FT_IS_SFNT(m_ftFace)) { - // Tectonic: this code used to use kpse_find_file and FT_Attach_File - // to try to find metrics for this font. Thanks to the existence of - // FT_Attach_Stream we can emulate this behavior while going through - // the Rust I/O layer. - - char *afm = xstrdup (xbasename (pathname)); - char *p = strrchr (afm, '.'); - if (p != NULL && strlen(p) == 4 && tolower(*(p+1)) == 'p' && tolower(*(p+2)) == 'f') - strcpy(p, ".afm"); - - rust_input_handle_t afm_handle = ttstub_input_open (afm, TTBC_FILE_FORMAT_AFM, 0); - free (afm); - - if (afm_handle != INVALID_HANDLE) { - sz = ttstub_input_get_size (afm_handle); - m_backingData2 = (FT_Byte *) xmalloc (sz); - r = ttstub_input_read (afm_handle, (char *) m_backingData2, sz); - if (r < 0 || (size_t) r != sz) - _tt_abort("failed to read AFM file"); - ttstub_input_close(afm_handle); - - FT_Open_Args open_args; - open_args.flags = FT_OPEN_MEMORY; - open_args.memory_base = m_backingData2; - open_args.memory_size = sz; - - FT_Attach_Stream(m_ftFace, &open_args); - } - } - - m_filename = xstrdup(pathname); - m_index = index; - m_unitsPerEM = m_ftFace->units_per_EM; - m_ascent = unitsToPoints(m_ftFace->ascender); - m_descent = unitsToPoints(m_ftFace->descender); - - postTable = (TT_Postscript *) getFontTable(ft_sfnt_post); - if (postTable != NULL) { - m_italicAngle = Fix2D(postTable->italicAngle); - } - - os2Table = (TT_OS2*) getFontTable(ft_sfnt_os2); - if (os2Table) { - m_capHeight = unitsToPoints(os2Table->sCapHeight); - m_xHeight = unitsToPoints(os2Table->sxHeight); - } - - // Set up HarfBuzz font - hbFace = hb_face_create_for_tables(_get_table, m_ftFace, NULL); - hb_face_set_index(hbFace, index); - hb_face_set_upem(hbFace, m_unitsPerEM); - m_hbFont = hb_font_create(hbFace); - hb_face_destroy(hbFace); - - if (hbFontFuncs == NULL) - hbFontFuncs = _get_font_funcs(); - - hb_font_set_funcs(m_hbFont, hbFontFuncs, m_ftFace, NULL); - hb_font_set_scale(m_hbFont, m_unitsPerEM, m_unitsPerEM); - // We don’t want device tables adjustments - hb_font_set_ppem(m_hbFont, 0, 0); - - return; -} - -void -XeTeXFontInst::setLayoutDirVertical(bool vertical) -{ - m_vertical = vertical; -} - -void * -XeTeXFontInst::getFontTable(OTTag tag) const -{ - FT_ULong tmpLength = 0; - FT_Error error = FT_Load_Sfnt_Table(m_ftFace, tag, 0, NULL, &tmpLength); - if (error) - return NULL; - - void* table = xmalloc(tmpLength * sizeof(char)); - if (table != NULL) { - error = FT_Load_Sfnt_Table(m_ftFace, tag, 0, (FT_Byte*)table, &tmpLength); - if (error) { - free((void *) table); - return NULL; - } - } - - return table; -} - -void * -XeTeXFontInst::getFontTable(FT_Sfnt_Tag tag) const -{ - return FT_Get_Sfnt_Table(m_ftFace, tag); -} - -void -XeTeXFontInst::getGlyphBounds(GlyphID gid, GlyphBBox* bbox) -{ - bbox->xMin = bbox->yMin = bbox->xMax = bbox->yMax = 0.0; - - FT_Error error = FT_Load_Glyph(m_ftFace, gid, FT_LOAD_NO_SCALE); - if (error) - return; - - FT_Glyph glyph; - error = FT_Get_Glyph(m_ftFace->glyph, &glyph); - if (error == 0) { - FT_BBox ft_bbox; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_UNSCALED, &ft_bbox); - bbox->xMin = unitsToPoints(ft_bbox.xMin); - bbox->yMin = unitsToPoints(ft_bbox.yMin); - bbox->xMax = unitsToPoints(ft_bbox.xMax); - bbox->yMax = unitsToPoints(ft_bbox.yMax); - FT_Done_Glyph(glyph); - } -} - -GlyphID -XeTeXFontInst::mapCharToGlyph(UChar32 ch) const -{ - return FT_Get_Char_Index(m_ftFace, ch); -} - -uint16_t -XeTeXFontInst::getNumGlyphs() const -{ - return m_ftFace->num_glyphs; -} - -float -XeTeXFontInst::getGlyphWidth(GlyphID gid) -{ - return unitsToPoints(_get_glyph_advance(m_ftFace, gid, false)); -} - -void -XeTeXFontInst::getGlyphHeightDepth(GlyphID gid, float* ht, float* dp) -{ - GlyphBBox bbox; - getGlyphBounds(gid, &bbox); - - if (ht) - *ht = bbox.yMax; - if (dp) - *dp = -bbox.yMin; -} - -void -XeTeXFontInst::getGlyphSidebearings(GlyphID gid, float* lsb, float* rsb) -{ - float width = getGlyphWidth(gid); - - GlyphBBox bbox; - getGlyphBounds(gid, &bbox); - - if (lsb) - *lsb = bbox.xMin; - if (rsb) - *rsb = width - bbox.xMax; -} - -float -XeTeXFontInst::getGlyphItalCorr(GlyphID gid) -{ - float rval = 0.0; - - float width = getGlyphWidth(gid); - - GlyphBBox bbox; - getGlyphBounds(gid, &bbox); - - if (bbox.xMax > width) - rval = bbox.xMax - width; - - return rval; -} - -GlyphID -XeTeXFontInst::mapGlyphToIndex(const char* glyphName) const -{ - return FT_Get_Name_Index(m_ftFace, const_cast(glyphName)); -} - -const char* -XeTeXFontInst::getGlyphName(GlyphID gid, int& nameLen) -{ - if (FT_HAS_GLYPH_NAMES(m_ftFace)) { - static char buffer[256]; - FT_Get_Glyph_Name(m_ftFace, gid, buffer, 256); - nameLen = strlen(buffer); - return &buffer[0]; - } - else { - nameLen = 0; - return NULL; - } -} - -UChar32 -XeTeXFontInst::getFirstCharCode() -{ - FT_UInt gindex; - return FT_Get_First_Char(m_ftFace, &gindex); -} - -UChar32 -XeTeXFontInst::getLastCharCode() -{ - FT_UInt gindex; - UChar32 ch = FT_Get_First_Char(m_ftFace, &gindex); - UChar32 prev = ch; - while (gindex != 0) { - prev = ch; - ch = FT_Get_Next_Char(m_ftFace, ch, &gindex); - } - return prev; -} diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontInst.h b/crates/xetex_layout/layout/xetex-XeTeXFontInst.h deleted file mode 100644 index e026bfe57..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontInst.h +++ /dev/null @@ -1,148 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009, 2011 by Jonathan Kew - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -/* - * file name: XeTeXFontInst.h - * - * created on: 2005-10-22 - * created by: Jonathan Kew - * - * originally based on PortableFontInstance.h from ICU - */ - -#ifndef __XeTeXFontInst_H -#define __XeTeXFontInst_H - -#include "tectonic_bridge_core.h" -#include "xetex-XeTeXFontMgr.h" - -#include -#include FT_GLYPH_H -#include FT_ADVANCES_H -#include FT_TRUETYPE_TABLES_H - -#include - -static inline double -Fix2D(Fixed f) -{ - return f / 65536.0; -} - -static inline Fixed -D2Fix(double d) -{ - return (int) (d * 65536.0 + 0.5); -} - - -// create specific subclasses for each supported platform - -class XeTeXFontInst -{ -protected: - unsigned short m_unitsPerEM; - float m_pointSize; - float m_ascent; - float m_descent; - float m_capHeight; - float m_xHeight; - float m_italicAngle; - - bool m_vertical; // false = horizontal, true = vertical - - char *m_filename; // font filename - uint32_t m_index; // face index - - FT_Face m_ftFace; - FT_Byte *m_backingData, *m_backingData2; - hb_font_t* m_hbFont; - -public: - XeTeXFontInst(float pointSize, int &status); - XeTeXFontInst(const char* filename, int index, float pointSize, int &status); - - virtual ~XeTeXFontInst(); - - void initialize(const char* pathname, int index, int &status); - - void *getFontTable(OTTag tableTag) const; - void *getFontTable(FT_Sfnt_Tag tableTag) const; - - const char *getFilename(uint32_t* index) const - { - *index = m_index; - return m_filename; - } - hb_font_t *getHbFont() const { return m_hbFont; } - void setLayoutDirVertical(bool vertical); - bool getLayoutDirVertical() const { return m_vertical; } - - float getPointSize() const { return m_pointSize; } - float getAscent() const { return m_ascent; } - float getDescent() const { return m_descent; } - float getCapHeight() const { return m_capHeight; } - float getXHeight() const { return m_xHeight; } - float getItalicAngle() const { return m_italicAngle; } - - GlyphID mapCharToGlyph(UChar32 ch) const; - GlyphID mapGlyphToIndex(const char* glyphName) const; - - uint16_t getNumGlyphs() const; - - void getGlyphBounds(GlyphID glyph, GlyphBBox* bbox); - - float getGlyphWidth(GlyphID glyph); - void getGlyphHeightDepth(GlyphID glyph, float *ht, float* dp); - void getGlyphSidebearings(GlyphID glyph, float* lsb, float* rsb); - float getGlyphItalCorr(GlyphID glyph); - - const char* getGlyphName(GlyphID gid, int& nameLen); - - UChar32 getFirstCharCode(); - UChar32 getLastCharCode(); - - /* Tectonic: these are modified from the base XeTeX code to use doubles; - * otherwise roundoff errors can accumulate leading to differences in the - * XDV outputs. */ - float unitsToPoints(double units) const - { - return (units * m_pointSize) / m_unitsPerEM; - } - - float pointsToUnits(double points) const - { - return (points * m_unitsPerEM) / m_pointSize; - } -}; - -#endif diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.cpp b/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.cpp deleted file mode 100644 index 7a9780602..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.cpp +++ /dev/null @@ -1,174 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012, 2013 by Jiang Jiang - Copyright (c) 2012-2015 by Khaled Hosny - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -/* - * file name: XeTeXFontInst_Mac.cpp - * - * created on: 2005-10-22 - * created by: Jonathan Kew - */ - -#include "tectonic_bridge_core.h" -#include "xetex-XeTeXFontInst_Mac.h" - -extern FT_Library gFreeTypeLibrary; - -char* -getNameFromCTFont(CTFontRef ctFontRef, CFStringRef nameKey) -{ - char *buf; - CFStringRef name = CTFontCopyName(ctFontRef, nameKey); - CFIndex len = CFStringGetLength(name); - len = len * 6 + 1; - buf = (char *) xmalloc(len); - if (CFStringGetCString(name, buf, len, kCFStringEncodingUTF8)) - return buf; - free(buf); - return NULL; -} - -char* -getFileNameFromCTFont(CTFontRef ctFontRef, uint32_t *index) -{ - char *ret = NULL; - CFURLRef url = NULL; - -#if !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 - /* kCTFontURLAttribute was not avialable before 10.6 */ - ATSFontRef atsFont; - FSRef fsref; - OSStatus status; - atsFont = CTFontGetPlatformFont(ctFontRef, NULL); - status = ATSFontGetFileReference(atsFont, &fsref); - if (status == noErr) - url = CFURLCreateFromFSRef(NULL, &fsref); -#else - url = (CFURLRef) CTFontCopyAttribute(ctFontRef, kCTFontURLAttribute); -#endif - if (url) { - UInt8 pathname[PATH_MAX]; - if (CFURLGetFileSystemRepresentation(url, true, pathname, PATH_MAX)) { - FT_Error error; - FT_Face face; - - *index = 0; - - if (!gFreeTypeLibrary) { - error = FT_Init_FreeType(&gFreeTypeLibrary); - if (error) - _tt_abort("FreeType initialization failed; error %d", error); - } - - error = FT_New_Face(gFreeTypeLibrary, (char *) pathname, 0, &face); - if (!error) { - if (face->num_faces > 1) { - int num_faces = face->num_faces; - char *ps_name1 = getNameFromCTFont(ctFontRef, kCTFontPostScriptNameKey); - int i; - *index = 0xFFFFFFFF; - FT_Done_Face (face); - for (i = 0; i < num_faces; i++) { - error = FT_New_Face (gFreeTypeLibrary, (char *) pathname, i, &face); - if (!error) { - const char *ps_name2 = FT_Get_Postscript_Name(face); - if (streq_ptr(ps_name1, ps_name2)) { - *index = i; - break; - } - FT_Done_Face (face); - } - } - free(ps_name1); - } - } - - if (*index != 0xFFFFFFFF) - ret = strdup((char *) pathname); - } - CFRelease(url); - } - - return ret; -} - -XeTeXFontInst_Mac::XeTeXFontInst_Mac(CTFontDescriptorRef descriptor, float pointSize, int &status) - : XeTeXFontInst(NULL, 0, pointSize, status) - , m_descriptor(descriptor) - , m_fontRef(0) -{ - initialize(status); -} - -XeTeXFontInst_Mac::~XeTeXFontInst_Mac() -{ - if (m_descriptor != 0) - CFRelease(m_descriptor); - if (m_fontRef != 0) - CFRelease(m_fontRef); -} - -void -XeTeXFontInst_Mac::initialize(int &status) -{ - if (m_descriptor == 0) { - status = 1; - return; - } - - if (status != 0) - m_descriptor = 0; - - // Create a copy of original font descriptor with font cascading (fallback) disabled - CFArrayRef emptyCascadeList = CFArrayCreate(NULL, NULL, 0, &kCFTypeArrayCallBacks); - const void* values[] = { emptyCascadeList }; - static const void* attributeKeys[] = { kCTFontCascadeListAttribute }; - CFDictionaryRef attributes = CFDictionaryCreate(NULL, attributeKeys, values, 1, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CFRelease(emptyCascadeList); - - m_descriptor = CTFontDescriptorCreateCopyWithAttributes(m_descriptor, attributes); - CFRelease(attributes); - m_fontRef = CTFontCreateWithFontDescriptor(m_descriptor, m_pointSize * 72.0 / 72.27, NULL); - if (m_fontRef) { - char *pathname; - uint32_t index; - pathname = getFileNameFromCTFont(m_fontRef, &index); - - XeTeXFontInst::initialize(pathname, index, status); - } else { - status = 1; - CFRelease(m_descriptor); - m_descriptor = 0; - } -} diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.h b/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.h deleted file mode 100644 index 370e98d56..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontInst_Mac.h +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012, 2013 by Jiang Jiang - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -/* - * file name: XeTeXFontInst_Mac.h - * - * created on: 2005-10-22 - * created by: Jonathan Kew - */ - - -#ifndef __XeTeXFontInst_Mac_H -#define __XeTeXFontInst_Mac_H - -#include "tectonic_bridge_core.h" -#include "xetex-XeTeXFontInst.h" - -#include - -class XeTeXFontInst_Mac : public XeTeXFontInst -{ -protected: - CTFontDescriptorRef m_descriptor; - CTFontRef m_fontRef; - -public: - XeTeXFontInst_Mac(CTFontDescriptorRef descriptor, float pointSize, int &status); - - virtual ~XeTeXFontInst_Mac(); - - virtual void initialize(int &status); -}; - -#endif diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr.cpp b/crates/xetex_layout/layout/xetex-XeTeXFontMgr.cpp deleted file mode 100644 index 038ab2608..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr.cpp +++ /dev/null @@ -1,671 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009-2014 by Jonathan Kew - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#include "tectonic_xetex_layout.h" - -#ifdef XETEX_MAC -#include "xetex-XeTeXFontMgr_Mac.h" -#else -#include "xetex-XeTeXFontMgr_FC.h" -#endif -#include "xetex-XeTeXFontInst.h" - -#include - -// see cpascal.h -#define printcstring(STR) \ - do { \ - const char* ch_ptr = (STR); \ - while (*ch_ptr) \ - print_char(*(ch_ptr++)); \ - } while (0) - -XeTeXFontMgr* XeTeXFontMgr::sFontManager = NULL; -char XeTeXFontMgr::sReqEngine = 0; -Fixed loaded_font_design_size = 0; /* explicitly *not* static */ - -/* use our own fmax function because it seems to be missing on certain platforms - (solaris2.9, at least) */ -static inline double -my_fmax(double x, double y) -{ - return (x > y) ? x : y; -} - -XeTeXFontMgr* -XeTeXFontMgr::GetFontManager() -{ - if (sFontManager == NULL) { -#ifdef XETEX_MAC - sFontManager = new XeTeXFontMgr_Mac; -#else - sFontManager = new XeTeXFontMgr_FC; -#endif - sFontManager->initialize(); - } - - return sFontManager; -} - -void -XeTeXFontMgr::Terminate() -{ - if (sFontManager != NULL) { - sFontManager->terminate(); - // we don't actually deallocate the manager, just ask it to clean up - // any auxiliary data such as the cocoa pool or freetype/fontconfig stuff - // as we still need to access font names after this is called - } -} - -void -XeTeXFontMgr::Destroy() -{ - // Here we actually fully destroy the font manager. - - if (sFontManager != NULL) { - delete sFontManager; - sFontManager = NULL; - } -} - -PlatformFontRef -XeTeXFontMgr::findFont(const char* name, char* variant, double ptSize) - // ptSize is in TeX points, or negative for 'scaled' factor - // "variant" string will be shortened (in-place) by removal of /B and /I if present -{ - std::string nameStr(name); - Font* font = NULL; - double dsize = 10.0; - loaded_font_design_size = 655360L; - - for (int pass = 0; pass < 2; ++pass) { - // try full name as given - std::map::iterator i = m_nameToFont.find(nameStr); - if (i != m_nameToFont.end()) { - font = i->second; - if (font->opSizeInfo.designSize != 0.0) - dsize = font->opSizeInfo.designSize; - break; - } - - // if there's a hyphen, split there and try Family-Style - int hyph = nameStr.find('-'); - if (hyph > 0 && hyph < (int) (nameStr.length() - 1)) { - std::string family(nameStr.begin(), nameStr.begin() + hyph); - std::map::iterator f = m_nameToFamily.find(family); - if (f != m_nameToFamily.end()) { - std::string style(nameStr.begin() + hyph + 1, nameStr.end()); - i = f->second->styles->find(style); - if (i != f->second->styles->end()) { - font = i->second; - if (font->opSizeInfo.designSize != 0.0) - dsize = font->opSizeInfo.designSize; - break; - } - } - } - - // try as PostScript name - i = m_psNameToFont.find(nameStr); - if (i != m_psNameToFont.end()) { - font = i->second; - if (font->opSizeInfo.designSize != 0.0) - dsize = font->opSizeInfo.designSize; - break; - } - - // try for the name as a family name - std::map::iterator f = m_nameToFamily.find(nameStr); - - if (f != m_nameToFamily.end()) { - // look for a family member with the "regular" bit set in OS/2 - int regFonts = 0; - for (i = f->second->styles->begin(); i != f->second->styles->end(); ++i) - if (i->second->isReg) { - if (regFonts == 0) - font = i->second; - ++regFonts; - } - - // families with Ornament or similar fonts may flag those as Regular, - // which confuses the search above... so try some known names - if (font == NULL || regFonts > 1) { - // try for style "Regular", "Plain", "Normal", "Roman" - i = f->second->styles->find("Regular"); - if (i != f->second->styles->end()) - font = i->second; - else { - i = f->second->styles->find("Plain"); - if (i != f->second->styles->end()) - font = i->second; - else { - i = f->second->styles->find("Normal"); - if (i != f->second->styles->end()) - font = i->second; - else { - i = f->second->styles->find("Roman"); - if (i != f->second->styles->end()) - font = i->second; - } - } - } - } - - if (font == NULL) { - // look through the family for the (weight, width, slant) nearest to (80, 100, 0) - font = bestMatchFromFamily(f->second, 80, 100, 0); - } - - if (font != NULL) - break; - } - - if (pass == 0) { - // didn't find it in our caches, so do a platform search (may be relatively expensive); - // this will update the caches with any fonts that seem to match the name given, - // so that the second pass might find it - searchForHostPlatformFonts(nameStr); - } - } - - if (font == NULL) - return 0; - - Family* parent = font->parent; - - // if there are variant requests, try to apply them - // and delete B, I, and S=... codes from the string, just retain /engine option - sReqEngine = 0; - bool reqBold = false; - bool reqItal = false; - if (variant != NULL) { - std::string varString; - char* cp = variant; - while (*cp) { - if (strncmp(cp, "AAT", 3) == 0) { - sReqEngine = 'A'; - cp += 3; - if (varString.length() > 0 && *(varString.end() - 1) != '/') - varString.append("/"); - varString.append("AAT"); - goto skip_to_slash; - } - if (strncmp(cp, "ICU", 3) == 0) { // for backword compatability - sReqEngine = 'O'; - cp += 3; - if (varString.length() > 0 && *(varString.end() - 1) != '/') - varString.append("/"); - varString.append("OT"); - goto skip_to_slash; - } - if (strncmp(cp, "OT", 2) == 0) { - sReqEngine = 'O'; - cp += 2; - if (varString.length() > 0 && *(varString.end() - 1) != '/') - varString.append("/"); - varString.append("OT"); - goto skip_to_slash; - } - if (strncmp(cp, "GR", 2) == 0) { - sReqEngine = 'G'; - cp += 2; - if (varString.length() > 0 && *(varString.end() - 1) != '/') - varString.append("/"); - varString.append("GR"); - goto skip_to_slash; - } - if (*cp == 'S') { - ++cp; - if (*cp == '=') - ++cp; - ptSize = 0.0; - while (*cp >= '0' && *cp <= '9') { - ptSize = ptSize * 10 + *cp - '0'; - ++cp; - } - if (*cp == '.') { - double dec = 1.0; - ++cp; - while (*cp >= '0' && *cp <= '9') { - dec = dec * 10.0; - ptSize = ptSize + (*cp - '0') / dec; - ++cp; - } - } - goto skip_to_slash; - } - - /* if the code is "B" or "I", we skip putting it in varString */ - while (1) { - if (*cp == 'B') { - reqBold = true; - ++cp; - continue; - } - if (*cp == 'I') { - reqItal = true; - ++cp; - continue; - } - break; - } - - skip_to_slash: - while (*cp && *cp != '/') - ++cp; - if (*cp == '/') - ++cp; - } - strcpy(variant, varString.c_str()); - - std::map::iterator i; - if (reqItal) { - Font* bestMatch = font; - if (font->slant < parent->maxSlant) - // try for a face with more slant - bestMatch = bestMatchFromFamily(parent, font->weight, font->width, parent->maxSlant); - - if (bestMatch == font && font->slant > parent->minSlant) - // maybe the slant is negated, or maybe this was something like "Times-Italic/I" - bestMatch = bestMatchFromFamily(parent, font->weight, font->width, parent->minSlant); - - if (parent->minWeight == parent->maxWeight && bestMatch->isBold != font->isBold) { - // try again using the bold flag, as we can't trust weight values - Font* newBest = NULL; - for (i = parent->styles->begin(); i != parent->styles->end(); ++i) { - if (i->second->isBold == font->isBold) { - if (newBest == NULL && i->second->isItalic != font->isItalic) { - newBest = i->second; - break; - } - } - } - if (newBest != NULL) - bestMatch = newBest; - } - - if (bestMatch == font) { - // maybe slant values weren't present; try the style bits as a fallback - bestMatch = NULL; - for (i = parent->styles->begin(); i != parent->styles->end(); ++i) { - if (i->second->isItalic == !font->isItalic) { - if (parent->minWeight != parent->maxWeight) { - // weight info was available, so try to match that - if (bestMatch == NULL || weightAndWidthDiff(i->second, font) < weightAndWidthDiff(bestMatch, font)) - bestMatch = i->second; - } else { - // no weight info, so try matching style bits - if (bestMatch == NULL && i->second->isBold == font->isBold) { - bestMatch = i->second; - break; // found a match, no need to look further as we can't distinguish! - } - } - } - } - } - if (bestMatch != NULL) - font = bestMatch; - } - - if (reqBold) { - // try for more boldness, with the same width and slant - Font* bestMatch = font; - if (font->weight < parent->maxWeight) { - // try to increase weight by 1/2 x (max - min), rounding up - bestMatch = bestMatchFromFamily(parent, - font->weight + (parent->maxWeight - parent->minWeight) / 2 + 1, - font->width, font->slant); - if (parent->minSlant == parent->maxSlant) { - // double-check the italic flag, as we can't trust slant values - Font* newBest = NULL; - for (i = parent->styles->begin(); i != parent->styles->end(); ++i) { - if (i->second->isItalic == font->isItalic) { - if (newBest == NULL || weightAndWidthDiff(i->second, bestMatch) < weightAndWidthDiff(newBest, bestMatch)) - newBest = i->second; - } - } - if (newBest != NULL) - bestMatch = newBest; - } - } - if (bestMatch == font && !font->isBold) { - for (i = parent->styles->begin(); i != parent->styles->end(); ++i) { - if (i->second->isItalic == font->isItalic && i->second->isBold) { - bestMatch = i->second; - break; - } - } - } - font = bestMatch; - } - } - - // if there's optical size info, try to apply it - if (ptSize < 0.0) - ptSize = dsize; - if (font != NULL && font->opSizeInfo.subFamilyID != 0 && ptSize > 0.0) { - double bestMismatch = my_fmax(font->opSizeInfo.minSize - ptSize, ptSize - font->opSizeInfo.maxSize); - if (bestMismatch > 0.0) { - Font* bestMatch = font; - for (std::map::iterator i = parent->styles->begin(); i != parent->styles->end(); ++i) { - if (i->second->opSizeInfo.subFamilyID != font->opSizeInfo.subFamilyID) - continue; - double mismatch = my_fmax(i->second->opSizeInfo.minSize - ptSize, ptSize - i->second->opSizeInfo.maxSize); - if (mismatch < bestMismatch) { - bestMatch = i->second; - bestMismatch = mismatch; - } - if (bestMismatch <= 0.0) - break; - } - font = bestMatch; - } - } - - if (font != NULL && font->opSizeInfo.designSize != 0.0) - loaded_font_design_size = unsigned(font->opSizeInfo.designSize * 65536.0 + 0.5); - - /* Tectonic: there used to be a bit of tracing code here, but we neede to - * move it to find_native_font() to preserve encapsulation. */ - - return font->fontRef; -} - -const char* -XeTeXFontMgr::getFullName(PlatformFontRef font) const -{ - std::map::const_iterator i = m_platformRefToFont.find(font); - if (i == m_platformRefToFont.end()) - _tt_abort("internal error %d in XeTeXFontMgr", 2); - if (i->second->m_fullName != NULL) - return i->second->m_fullName->c_str(); - else - return i->second->m_psName->c_str(); -} - -int -XeTeXFontMgr::weightAndWidthDiff(const Font* a, const Font* b) const -{ - if (a->weight == 0 && a->width == 0) { - // assume there was no OS/2 info - if (a->isBold == b->isBold) - return 0; - else - return 10000; - } - - int widDiff = labs(a->width - b->width); - if (widDiff < 10) - widDiff *= 50; - - return labs(a->weight - b->weight) + widDiff; -} - -int -XeTeXFontMgr::styleDiff(const Font* a, int wt, int wd, int slant) const -{ - int widDiff = labs(a->width - wd); - if (widDiff < 10) - widDiff *= 200; - - return labs(labs(a->slant) - labs(slant)) * 2 + labs(a->weight - wt) + widDiff; -} - -XeTeXFontMgr::Font* -XeTeXFontMgr::bestMatchFromFamily(const Family* fam, int wt, int wd, int slant) const -{ - Font* bestMatch = NULL; - for (std::map::iterator s = fam->styles->begin(); s != fam->styles->end(); ++s) - if (bestMatch == NULL || styleDiff(s->second, wt, wd, slant) < styleDiff(bestMatch, wt, wd, slant)) - bestMatch = s->second; - return bestMatch; -} - - -XeTeXFontMgr::OpSizeRec* -XeTeXFontMgr::getOpSize(XeTeXFont font) -{ - hb_font_t *hbFont = ((XeTeXFontInst *) font)->getHbFont(); - - if (hbFont == NULL) - return NULL; - - hb_face_t *face = hb_font_get_face(hbFont); - OpSizeRec *pSizeRec = (OpSizeRec*) xmalloc(sizeof(OpSizeRec)); - - unsigned int designSize, minSize, maxSize; - bool ok = hb_ot_layout_get_size_params(face, - &designSize, - &pSizeRec->subFamilyID, - &pSizeRec->nameCode, - &minSize, - &maxSize); - - if (ok) { - // Convert sizes from PostScript deci-points to TeX points - pSizeRec->designSize = designSize * 72.27 / 72.0 / 10.0; - pSizeRec->minSize = minSize * 72.27 / 72.0 / 10.0; - pSizeRec->maxSize = maxSize * 72.27 / 72.0 / 10.0; - return pSizeRec; - } - - free(pSizeRec); - return NULL; -} - - -double -XeTeXFontMgr::getDesignSize(XeTeXFont font) -{ - OpSizeRec* pSizeRec = getOpSize(font); - - if (pSizeRec == NULL) - return 10.0; - - /* Tectonic: make sure not to leak pSizeRec */ - double result = pSizeRec->designSize; - free(pSizeRec); - return result; -} - - -void -XeTeXFontMgr::getOpSizeRecAndStyleFlags(Font* theFont) -{ - XeTeXFont font = createFont(theFont->fontRef, 655360); - XeTeXFontInst* fontInst = (XeTeXFontInst*) font; - if (font != 0) { - OpSizeRec* pSizeRec = getOpSize(font); - - if (pSizeRec != NULL) { - theFont->opSizeInfo.designSize = pSizeRec->designSize; - if (pSizeRec->subFamilyID == 0 - && pSizeRec->nameCode == 0 - && pSizeRec->minSize == 0.0 - && pSizeRec->maxSize == 0.0) { - /* Tectonic: make sure not to leak pSizeRec */ - free(pSizeRec); - goto done_size; // feature is valid, but no 'size' range - } - - theFont->opSizeInfo.subFamilyID = pSizeRec->subFamilyID; - theFont->opSizeInfo.nameCode = pSizeRec->nameCode; - theFont->opSizeInfo.minSize = pSizeRec->minSize; - theFont->opSizeInfo.maxSize = pSizeRec->maxSize; - free(pSizeRec); - } - - done_size: - - const TT_OS2* os2Table = (TT_OS2*) fontInst->getFontTable(ft_sfnt_os2); - if (os2Table != NULL) { - theFont->weight = os2Table->usWeightClass; - theFont->width = os2Table->usWidthClass; - uint16_t sel = os2Table->fsSelection; - theFont->isReg = (sel & (1 << 6)) != 0; - theFont->isBold = (sel & (1 << 5)) != 0; - theFont->isItalic = (sel & (1 << 0)) != 0; - } - - const TT_Header* headTable = (TT_Header*) fontInst->getFontTable(ft_sfnt_head); - if (headTable != NULL) { - uint16_t ms = headTable->Mac_Style; - if ((ms & (1 << 0)) != 0) - theFont->isBold = true; - if ((ms & (1 << 1)) != 0) - theFont->isItalic = true; - } - - const TT_Postscript* postTable = (const TT_Postscript*) fontInst->getFontTable(ft_sfnt_post); - if (postTable != NULL) { - theFont->slant = (int)(1000 * (tan(Fix2D(-postTable->italicAngle) * M_PI / 180.0))); - } - deleteFont(font); - } -} - -// append a name but only if it's not already in the list -void -XeTeXFontMgr::appendToList(std::list* list, const char* str) -{ - for (std::list::const_iterator i = list->begin(); i != list->end(); ++i) - if (*i == str) - return; - list->push_back(str); -} - -// prepend a name, removing it from later in the list if present -void -XeTeXFontMgr::prependToList(std::list* list, const char* str) -{ - for (std::list::iterator i = list->begin(); i != list->end(); ++i) - if (*i == str) { - list->erase(i); - break; - } - list->push_front(str); -} - -void -XeTeXFontMgr::addToMaps(PlatformFontRef platformFont, const NameCollection* names) -{ - if (m_platformRefToFont.find(platformFont) != m_platformRefToFont.end()) - return; // this font has already been cached - - if (names->m_psName.length() == 0) - return; // can't use a font that lacks a PostScript name - - if (m_psNameToFont.find(names->m_psName) != m_psNameToFont.end()) - return; // duplicates an earlier PS name, so skip - - Font* thisFont = new Font(platformFont); - thisFont->m_psName = new std::string(names->m_psName); - getOpSizeRecAndStyleFlags(thisFont); - - m_psNameToFont[names->m_psName] = thisFont; - m_platformRefToFont[platformFont] = thisFont; - - if (names->m_fullNames.size() > 0) - thisFont->m_fullName = new std::string(*(names->m_fullNames.begin())); - - if (names->m_familyNames.size() > 0) - thisFont->m_familyName = new std::string(*(names->m_familyNames.begin())); - else - thisFont->m_familyName = new std::string(names->m_psName); - - if (names->m_styleNames.size() > 0) - thisFont->m_styleName = new std::string(*(names->m_styleNames.begin())); - else - thisFont->m_styleName = new std::string; - - std::list::const_iterator i; - for (i = names->m_familyNames.begin(); i != names->m_familyNames.end(); ++i) { - std::map::iterator iFam = m_nameToFamily.find(*i); - Family* family; - if (iFam == m_nameToFamily.end()) { - family = new Family; - m_nameToFamily[*i] = family; - family->minWeight = thisFont->weight; - family->maxWeight = thisFont->weight; - family->minWidth = thisFont->width; - family->maxWidth = thisFont->width; - family->minSlant = thisFont->slant; - family->maxSlant = thisFont->slant; - } else { - family = iFam->second; - if (thisFont->weight < family->minWeight) - family->minWeight = thisFont->weight; - if (thisFont->weight > family->maxWeight) - family->maxWeight = thisFont->weight; - if (thisFont->width < family->minWidth) - family->minWidth = thisFont->width; - if (thisFont->width > family->maxWidth) - family->maxWidth = thisFont->width; - if (thisFont->slant < family->minSlant) - family->minSlant = thisFont->slant; - if (thisFont->slant > family->maxSlant) - family->maxSlant = thisFont->slant; - } - - if (thisFont->parent == NULL) - thisFont->parent = family; - - // ensure all style names in the family point to thisFont - for (std::list::const_iterator j = names->m_styleNames.begin(); j != names->m_styleNames.end(); ++j) { - std::map::iterator iFont = family->styles->find(*j); - if (iFont == family->styles->end()) - (*family->styles)[*j] = thisFont; -/* - else if (iFont->second != thisFont) - fprintf(stderr, "# Font name warning: ambiguous Style \"%s\" in Family \"%s\" (PSNames \"%s\" and \"%s\")\n", - j->c_str(), i->c_str(), iFont->second->m_psName->c_str(), thisFont->m_psName->c_str()); -*/ - } - } - - for (i = names->m_fullNames.begin(); i != names->m_fullNames.end(); ++i) { - std::map::iterator iFont = m_nameToFont.find(*i); - if (iFont == m_nameToFont.end()) - m_nameToFont[*i] = thisFont; -/* - else if (iFont->second != thisFont) - fprintf(stderr, "# Font name warning: ambiguous FullName \"%s\" (PSNames \"%s\" and \"%s\")\n", - i->c_str(), iFont->second->m_psName->c_str(), thisFont->m_psName->c_str()); -*/ - } -} - -void -XeTeXFontMgr::terminate() -{ -} diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr.h b/crates/xetex_layout/layout/xetex-XeTeXFontMgr.h deleted file mode 100644 index 85bac818d..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr.h +++ /dev/null @@ -1,187 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012, 2013 by Jiang Jiang - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#ifndef __XETEX_FONT_MANAGER_H -#define __XETEX_FONT_MANAGER_H - -#include "tectonic_xetex_layout.h" - -#include -#include -#include -#include - -class XeTeXFontMgr -{ -public: - static XeTeXFontMgr* GetFontManager(); - // returns the global fontmanager (creating it if necessary) - static void Terminate(); - // clean up (may be required if using the cocoa implementation) - static void Destroy(); - - PlatformFontRef findFont(const char* name, char* variant, double ptSize); - // 1st arg is name as specified by user (C string, UTF-8) - // 2nd is /B/I/AAT/OT/ICU/GR/S=## qualifiers - // 1. try name given as "full name" - // 2. if there's a hyphen, split and try "family-style" - // 3. try as PostScript name - // 4. try name as family with "Regular/Plain/Normal" style - // apply style qualifiers and optical sizing if present - - // SIDE EFFECT: sets sReqEngine to 'A' or 'O' or 'G' if appropriate, - // else clears it to 0 - - // SIDE EFFECT: updates TeX variables /nameoffile/ and /namelength/, - // to match the actual font found - - // SIDE EFFECT: edits /variant/ string in-place removing /B or /I - - const char* getFullName(PlatformFontRef font) const; - // return the full name of the font, suitable for use in XeTeX source - // without requiring style qualifiers - - double getDesignSize(XeTeXFont font); - - char getReqEngine() const { return sReqEngine; } - // return the requested rendering technology for the most recent findFont - // or 0 if no specific technology was requested - - void setReqEngine(char reqEngine) const { sReqEngine = reqEngine; } - - // made public for Tectonic to enable encapsulation of some tracing code - virtual std::string getPlatformFontDesc(PlatformFontRef font) const = 0; - -protected: - static XeTeXFontMgr* sFontManager; - static char sReqEngine; - - XeTeXFontMgr() - { } - virtual ~XeTeXFontMgr() - { } - - virtual void initialize() = 0; - virtual void terminate(); - - class Font; - class Family; - - struct OpSizeRec { - double designSize; - double minSize; - double maxSize; - unsigned int subFamilyID; - unsigned int nameCode; - }; - - class Font { - public: - Font(PlatformFontRef ref) - : m_fullName(NULL), m_psName(NULL), m_familyName(NULL), m_styleName(NULL) - , parent(NULL) - , fontRef(ref), weight(0), width(0), slant(0) - , isReg(false), isBold(false), isItalic(false) - { opSizeInfo.subFamilyID = 0; - opSizeInfo.designSize = 10.0; } /* default to 10.0pt */ - ~Font() - { delete m_fullName; delete m_psName; } - - std::string* m_fullName; - std::string* m_psName; - std::string* m_familyName; // default family and style names that should locate this font - std::string* m_styleName; - Family* parent; - PlatformFontRef fontRef; - OpSizeRec opSizeInfo; - uint16_t weight; - uint16_t width; - int16_t slant; - bool isReg; - bool isBold; - bool isItalic; - }; - - class Family { - public: - Family() - : minWeight(0), maxWeight(0) - , minWidth(0), maxWidth(0) - , minSlant(0), maxSlant(0) - { - styles = new std::map; - } - ~Family() - { - delete styles; - } - - std::map* styles; - uint16_t minWeight; - uint16_t maxWeight; - uint16_t minWidth; - uint16_t maxWidth; - int16_t minSlant; - int16_t maxSlant; - }; - - class NameCollection { - public: - std::list m_familyNames; - std::list m_styleNames; - std::list m_fullNames; - std::string m_psName; - std::string m_subFamily; - }; - - std::map m_nameToFont; // maps full name (as used in TeX source) to font record - std::map m_nameToFamily; - std::map m_platformRefToFont; - std::map m_psNameToFont; // maps PS name (as used in .xdv) to font record - - int weightAndWidthDiff(const Font* a, const Font* b) const; - int styleDiff(const Font* a, int wt, int wd, int slant) const; - Font* bestMatchFromFamily(const Family* fam, int wt, int wd, int slant) const; - void appendToList(std::list* list, const char* str); - void prependToList(std::list* list, const char* str); - void addToMaps(PlatformFontRef platformFont, const NameCollection* names); - - OpSizeRec *getOpSize(XeTeXFont font); - - virtual void getOpSizeRecAndStyleFlags(Font* theFont); - virtual void searchForHostPlatformFonts(const std::string& name) = 0; - - virtual NameCollection* readNames(PlatformFontRef fontRef) = 0; -}; - -#endif /* __XETEX_FONT_MANAGER_H */ diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.cpp b/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.cpp deleted file mode 100644 index b3f1f2d8e..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.cpp +++ /dev/null @@ -1,382 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009, 2011 by Jonathan Kew - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#include "tectonic_bridge_core.h" - -#include "xetex-XeTeXFontMgr_FC.h" - -/* allow compilation with old Fontconfig header */ -#ifndef FC_FULLNAME -#define FC_FULLNAME "fullname" -#endif - -#include -#include FT_SFNT_NAMES_H -#include FT_TRUETYPE_IDS_H - -#include - -#define kFontFamilyName 1 -#define kFontStyleName 2 -#define kFontFullName 4 -#define kPreferredFamilyName 16 -#define kPreferredSubfamilyName 17 - -extern FT_Library gFreeTypeLibrary; - -static UConverter* macRomanConv = NULL; -static UConverter* utf16beConv = NULL; -static UConverter* utf8Conv = NULL; - -static char* -convertToUtf8(UConverter* conv, const unsigned char* name, int len) -{ - char* buffer1 = NULL; - char* buffer2 = NULL; - int bufSize = -1; - - if (2 * (len + 1) > bufSize) { - if (buffer1 != NULL) { - delete[] buffer1; - delete[] buffer2; - } - bufSize = 2 * len + 100; - buffer1 = new char[bufSize]; - buffer2 = new char[bufSize]; - } - - UErrorCode status = U_ZERO_ERROR; - len = ucnv_toUChars(conv, (UChar*)buffer1, bufSize, (const char*)name, len, &status); - len = ucnv_fromUChars(utf8Conv, buffer2, bufSize, (UChar*)buffer1, len, &status); - buffer2[len] = 0; - - delete[] buffer1; - return buffer2; -} - -XeTeXFontMgr::NameCollection* -XeTeXFontMgr_FC::readNames(FcPattern* pat) -{ - NameCollection* names = new NameCollection; - - char* pathname; - if (FcPatternGetString(pat, FC_FILE, 0, (FcChar8**)&pathname) != FcResultMatch) - return names; - int index; - if (FcPatternGetInteger(pat, FC_INDEX, 0, &index) != FcResultMatch) - return names; - - FT_Face face; - if (FT_New_Face(gFreeTypeLibrary, pathname, index, &face) != 0) - return names; - - const char* name = FT_Get_Postscript_Name(face); - if (name == NULL) - return names; - names->m_psName = name; - - // for sfnt containers, we'll read the name table ourselves, not rely on Fontconfig - if (FT_IS_SFNT(face)) { - unsigned int i; - std::list familyNames; - std::list subFamilyNames; - FT_SfntName nameRec; - for (i = 0; i < FT_Get_Sfnt_Name_Count(face); ++i) { - char* utf8name = NULL; - if (FT_Get_Sfnt_Name(face, i, &nameRec) != 0) - continue; - switch (nameRec.name_id) { - case kFontFullName: - case kFontFamilyName: - case kFontStyleName: - case kPreferredFamilyName: - case kPreferredSubfamilyName: - { - bool preferredName = false; - /* Tectonic: macRomanConv may not be available; see comment below */ - if (macRomanConv != NULL && nameRec.platform_id == TT_PLATFORM_MACINTOSH - && nameRec.encoding_id == TT_MAC_ID_ROMAN && nameRec.language_id == 0) { - utf8name = convertToUtf8(macRomanConv, nameRec.string, nameRec.string_len); - preferredName = true; - } - else if ((nameRec.platform_id == TT_PLATFORM_APPLE_UNICODE) - || (nameRec.platform_id == TT_PLATFORM_MICROSOFT)) - utf8name = convertToUtf8(utf16beConv, nameRec.string, nameRec.string_len); - - if (utf8name != NULL) { - std::list* nameList = NULL; - switch (nameRec.name_id) { - case kFontFullName: - nameList = &names->m_fullNames; - break; - case kFontFamilyName: - nameList = &names->m_familyNames; - break; - case kFontStyleName: - nameList = &names->m_styleNames; - break; - case kPreferredFamilyName: - nameList = &familyNames; - break; - case kPreferredSubfamilyName: - nameList = &subFamilyNames; - break; - } - if (preferredName) - prependToList(nameList, utf8name); - else - appendToList(nameList, utf8name); - delete[] utf8name; - } - } - break; - } - } - if (familyNames.size() > 0) - names->m_familyNames = familyNames; - if (subFamilyNames.size() > 0) - names->m_styleNames = subFamilyNames; - } else { - index = 0; - while (FcPatternGetString(pat, FC_FULLNAME, index++, (FcChar8**)&name) == FcResultMatch) - appendToList(&names->m_fullNames, name); - index = 0; - while (FcPatternGetString(pat, FC_FAMILY, index++, (FcChar8**)&name) == FcResultMatch) - appendToList(&names->m_familyNames, name); - index = 0; - while (FcPatternGetString(pat, FC_STYLE, index++, (FcChar8**)&name) == FcResultMatch) - appendToList(&names->m_styleNames, name); - - if (names->m_fullNames.size() == 0) { - std::string fullName(names->m_familyNames.front()); - if (names->m_styleNames.size() > 0) { - fullName += " "; - fullName += names->m_styleNames.front(); - } - names->m_fullNames.push_back(fullName); - } - } - - FT_Done_Face(face); - - return names; -} - -void -XeTeXFontMgr_FC::getOpSizeRecAndStyleFlags(Font* theFont) -{ - XeTeXFontMgr::getOpSizeRecAndStyleFlags(theFont); - - if (theFont->weight == 0 && theFont->width == 0) { - // try to get values from FontConfig, as it apparently wasn't an sfnt - FcPattern* pat = theFont->fontRef; - int value; - if (FcPatternGetInteger(pat, FC_WEIGHT, 0, &value) == FcResultMatch) - theFont->weight = value; - if (FcPatternGetInteger(pat, FC_WIDTH, 0, &value) == FcResultMatch) - theFont->width = value; - if (FcPatternGetInteger(pat, FC_SLANT, 0, &value) == FcResultMatch) - theFont->slant = value; - } -} - -void -XeTeXFontMgr_FC::cacheFamilyMembers(const std::list& familyNames) -{ - if (familyNames.size() == 0) - return; - for (int f = 0; f < allFonts->nfont; ++f) { - FcPattern* pat = allFonts->fonts[f]; - if (m_platformRefToFont.find(pat) != m_platformRefToFont.end()) - continue; - char* s; - for (int i = 0; FcPatternGetString(pat, FC_FAMILY, i, (FcChar8**)&s) == FcResultMatch; ++i) { - for (std::list::const_iterator j = familyNames.begin(); j != familyNames.end(); ++j) { - if (*j == s) { - NameCollection* names = readNames(pat); - addToMaps(pat, names); - delete names; - goto cached; - } - } - } - cached: - ; - } -} - -void -XeTeXFontMgr_FC::searchForHostPlatformFonts(const std::string& name) -{ - if (cachedAll) // we've already loaded everything on an earlier search - return; - - std::string famName; - int hyph = name.find('-'); - if (hyph > 0 && hyph < (int) (name.length() - 1)) - famName.assign(name.begin(), name.begin() + hyph); - else - hyph = 0; - - bool found = false; - while (1) { - for (int f = 0; f < allFonts->nfont; ++f) { - FcPattern* pat = allFonts->fonts[f]; - if (m_platformRefToFont.find(pat) != m_platformRefToFont.end()) - continue; - - if (cachedAll) { - // failed to find it via FC; add everything to our maps (potentially slow) as a last resort - NameCollection* names = readNames(pat); - addToMaps(pat, names); - delete names; - continue; - } - - char* s; - int i; - for (i = 0; FcPatternGetString(pat, FC_FULLNAME, i, (FcChar8**)&s) == FcResultMatch; ++i) { - if (name == s) { - NameCollection* names = readNames(pat); - addToMaps(pat, names); - cacheFamilyMembers(names->m_familyNames); - delete names; - found = true; - goto next_font; - } - } - - for (i = 0; FcPatternGetString(pat, FC_FAMILY, i, (FcChar8**)&s) == FcResultMatch; ++i) { - if (name == s || (hyph && famName == s)) { - NameCollection* names = readNames(pat); - addToMaps(pat, names); - cacheFamilyMembers(names->m_familyNames); - delete names; - found = true; - goto next_font; - } - char* t; - for (int j = 0; FcPatternGetString(pat, FC_STYLE, j, (FcChar8**)&t) == FcResultMatch; ++j) { - std::string full(s); - full += " "; - full += t; - if (name == full) { - NameCollection* names = readNames(pat); - addToMaps(pat, names); - cacheFamilyMembers(names->m_familyNames); - delete names; - found = true; - goto next_font; - } - } - } - - next_font: - ; - } - - if (found || cachedAll) - break; - cachedAll = true; - } -} - -void -XeTeXFontMgr_FC::initialize() -{ - if (FcInit() == FcFalse) - _tt_abort("fontconfig initialization failed"); - - if (gFreeTypeLibrary == 0 && FT_Init_FreeType(&gFreeTypeLibrary) != 0) - _tt_abort("FreeType initialization failed"); - - UErrorCode err = U_ZERO_ERROR; - - /* Tectonic: Alpine >=3.16 splits the ICU data in a way that seems to make - * the "macintosh" converter unavailable in our MUSL static builds. I don't - * see a workaround: installing `icu-data-full` doesn't help, I think - * because the static libraries don't access the data file that it provides. - */ - macRomanConv = ucnv_open("macintosh", &err); - if (!U_SUCCESS(err)) { - err = U_ZERO_ERROR; - macRomanConv = NULL; - } - - utf16beConv = ucnv_open("UTF16BE", &err); - utf8Conv = ucnv_open("UTF8", &err); - if (!U_SUCCESS(err)) - _tt_abort("cannot read font names"); - - FcPattern* pat = FcNameParse((const FcChar8*)":outline=true"); - FcObjectSet* os = FcObjectSetBuild(FC_FAMILY, FC_STYLE, FC_FILE, FC_INDEX, - FC_FULLNAME, FC_WEIGHT, FC_WIDTH, FC_SLANT, FC_FONTFORMAT, NULL); - allFonts = FcFontList(FcConfigGetCurrent(), pat, os); - FcObjectSetDestroy(os); - FcPatternDestroy(pat); - - cachedAll = false; -} - -void -XeTeXFontMgr_FC::terminate() -{ - if (allFonts != NULL) { - FcFontSetDestroy(allFonts); - allFonts = NULL; - } - - if (macRomanConv != NULL) { - ucnv_close(macRomanConv); - macRomanConv = NULL; - } - if (utf16beConv != NULL) { - ucnv_close(utf16beConv); - utf16beConv = NULL; - } - if (utf8Conv != NULL) { - ucnv_close(utf8Conv); - utf8Conv = NULL; - } -} - -std::string -XeTeXFontMgr_FC::getPlatformFontDesc(PlatformFontRef font) const -{ - std::string path; - FcChar8* s; - if (FcPatternGetString(font, FC_FILE, 0, (FcChar8**)&s) == FcResultMatch) - path = (char*)s; - else - path = "[unknown]"; - return path; -} diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.h b/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.h deleted file mode 100644 index e0303694b..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_FC.h +++ /dev/null @@ -1,66 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#ifndef __XETEX_FONT_MGR_FC_H -#define __XETEX_FONT_MGR_FC_H - -#include "tectonic_bridge_core.h" -#include "xetex-XeTeXFontMgr.h" - -class XeTeXFontMgr_FC - : public XeTeXFontMgr -{ -public: - XeTeXFontMgr_FC() - { } - virtual ~XeTeXFontMgr_FC() - { } - -protected: - - virtual void initialize(); - virtual void terminate(); - - virtual void getOpSizeRecAndStyleFlags(Font* theFont); - virtual void searchForHostPlatformFonts(const std::string& name); - - virtual NameCollection* readNames(FcPattern* pat); - - std::string getPlatformFontDesc(PlatformFontRef font) const; - - void cacheFamilyMembers(const std::list& familyNames); - - FcFontSet* allFonts; - bool cachedAll; -}; - -#endif /* __XETEX_FONT_MGR_FC_H */ diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.h b/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.h deleted file mode 100644 index 63c4ded6a..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.h +++ /dev/null @@ -1,75 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012, 2013 by Jiang Jiang - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#ifndef __XETEX_FONT_MGR_MAC_H -#define __XETEX_FONT_MGR_MAC_H - -#include "tectonic_bridge_core.h" - -#include "xetex-XeTeXFontMgr.h" - -#include - -class XeTeXFontMgr_Mac - : public XeTeXFontMgr -{ -public: - XeTeXFontMgr_Mac() - { } - virtual ~XeTeXFontMgr_Mac() - { } - -protected: - - virtual void initialize(); - virtual void terminate(); - - virtual void searchForHostPlatformFonts(const std::string& name); - - virtual NameCollection* readNames(CTFontDescriptorRef fontRef); - - virtual std::string getPlatformFontDesc(PlatformFontRef font) const; - -private: - void addFontsToCaches(CFArrayRef fonts); - - void addFamilyToCaches(CTFontDescriptorRef familyRef); - - void addFontAndSiblingsToCaches(CTFontDescriptorRef fontRef); - - void appendNameToList(CTFontRef font, - std::list* nameList, - CFStringRef nameKey); -}; - -#endif /* __XETEX_FONT_MGR_MAC_H */ diff --git a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.mm b/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.mm deleted file mode 100644 index d7d5bfed0..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXFontMgr_Mac.mm +++ /dev/null @@ -1,250 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009 by Jonathan Kew - Copyright (c) 2012, 2013 by Jiang Jiang - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#include "tectonic_bridge_core.h" -#include "xetex-XeTeXFontMgr_Mac.h" - -#include - -CTFontDescriptorRef findFontWithName(CFStringRef name, CFStringRef key) -{ - CFStringRef keys[] = { key }; - CFTypeRef values[] = { name }; - CFDictionaryRef attributes = CFDictionaryCreate(NULL, (const void **) &keys, (const void **) &values, 1, - &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - CTFontDescriptorRef descriptor = CTFontDescriptorCreateWithAttributes(attributes); - CFRelease(attributes); - - CFSetRef mandatoryAttributes = CFSetCreate(NULL, (const void **) &keys, 1, &kCFTypeSetCallBacks); - CFArrayRef matches = CTFontDescriptorCreateMatchingFontDescriptors(descriptor, mandatoryAttributes); - CFRelease(mandatoryAttributes); - CFRelease(descriptor); - - CTFontDescriptorRef matched = NULL; - if (matches) { - if (CFArrayGetCount(matches)) { - matched = (CTFontDescriptorRef) CFArrayGetValueAtIndex(matches, 0); - CFRetain(matched); - } - CFRelease(matches); - } - return matched; -} - -void -XeTeXFontMgr_Mac::appendNameToList(CTFontRef font, - std::list* nameList, - CFStringRef nameKey) -{ - CFStringRef name = CTFontCopyName(font, nameKey); - if (name) { - appendToList(nameList, [(NSString *) name UTF8String]); - CFRelease(name); - } - CFStringRef language; - name = CTFontCopyLocalizedName(font, nameKey, &language); - if (name) { - appendToList(nameList, [(NSString *) name UTF8String]); - CFRelease(name); - } -} - -XeTeXFontMgr::NameCollection* -XeTeXFontMgr_Mac::readNames(CTFontDescriptorRef fontRef) -{ - NameCollection* names = new NameCollection; - - CFStringRef psName = (CFStringRef) CTFontDescriptorCopyAttribute(fontRef, kCTFontNameAttribute); - if (!psName) - return names; - - NSAutoreleasePool *pool = [NSAutoreleasePool new]; - - names->m_psName = [(NSString *) psName UTF8String]; - CFRelease(psName); - - CTFontRef font = CTFontCreateWithFontDescriptor(fontRef, 0.0, 0); - appendNameToList(font, &names->m_fullNames, kCTFontFullNameKey); - appendNameToList(font, &names->m_familyNames, kCTFontFamilyNameKey); - appendNameToList(font, &names->m_styleNames, kCTFontStyleNameKey); - CFRelease(font); - - [pool release]; - - return names; -} - -void -XeTeXFontMgr_Mac::addFontsToCaches(CFArrayRef fonts) -{ - NSEnumerator* enumerator = [(NSArray*)fonts objectEnumerator]; - while (id aFont = [enumerator nextObject]) { - CTFontDescriptorRef fontRef = findFontWithName((CFStringRef)[aFont objectAtIndex: 0], kCTFontNameAttribute); - NameCollection* names = readNames(fontRef); - addToMaps(fontRef, names); - delete names; - } -} - -void -XeTeXFontMgr_Mac::addFamilyToCaches(CTFontDescriptorRef familyRef) -{ - CFStringRef nameStr = (CFStringRef) CTFontDescriptorCopyAttribute(familyRef, kCTFontFamilyNameAttribute); - if (nameStr) { - NSArray* members = [[NSFontManager sharedFontManager] - availableMembersOfFontFamily: (NSString*)nameStr]; - CFRelease(nameStr); - addFontsToCaches((CFArrayRef)members); - } -} - -void -XeTeXFontMgr_Mac::addFontAndSiblingsToCaches(CTFontDescriptorRef fontRef) -{ - CFStringRef name = (CFStringRef) CTFontDescriptorCopyAttribute(fontRef, kCTFontNameAttribute); - if (name) { - NSFont* font = [NSFont fontWithName:(NSString*)name size:10.0]; - CFRelease(name); - NSArray* members = [[NSFontManager sharedFontManager] - availableMembersOfFontFamily: [font familyName]]; - addFontsToCaches((CFArrayRef)members); - } -} - -void -XeTeXFontMgr_Mac::searchForHostPlatformFonts(const std::string& name) -{ - // the name might be: - // FullName - // Family-Style (if there's a hyphen) - // PSName - // Family - // ...so we need to try it as each of these - - CFStringRef nameStr = CFStringCreateWithCString(kCFAllocatorDefault, name.c_str(), kCFStringEncodingUTF8); - CTFontDescriptorRef matched = findFontWithName(nameStr, kCTFontDisplayNameAttribute); - if (matched) { - // found it, so locate the family, and add all members to the caches - addFontAndSiblingsToCaches(matched); - CFRelease(matched); - return; - } - - int hyph = name.find('-'); - if (hyph > 0 && hyph < name.length() - 1) { - std::string family(name.begin(), name.begin() + hyph); - CFStringRef familyStr = CFStringCreateWithCString(kCFAllocatorDefault, family.c_str(), kCFStringEncodingUTF8); - - NSArray* familyMembers = [[NSFontManager sharedFontManager] - availableMembersOfFontFamily: (NSString*)familyStr]; - if ([familyMembers count] > 0) { - addFontsToCaches((CFArrayRef)familyMembers); - return; - } - - matched = findFontWithName(familyStr, kCTFontFamilyNameAttribute); - if (matched) { - addFamilyToCaches(matched); - CFRelease(matched); - return; - } - } - - matched = findFontWithName(nameStr, kCTFontNameAttribute); - if (matched) { - addFontAndSiblingsToCaches(matched); - CFRelease(matched); - return; - } - - NSArray* familyMembers = [[NSFontManager sharedFontManager] - availableMembersOfFontFamily: (NSString*)nameStr]; - if ([familyMembers count] > 0) { - addFontsToCaches((CFArrayRef)familyMembers); - return; - } - - matched = findFontWithName(nameStr, kCTFontFamilyNameAttribute); - if (matched) { - addFamilyToCaches(matched); - CFRelease(matched); - return; - } -} - -NSAutoreleasePool* pool = NULL; - -void -XeTeXFontMgr_Mac::initialize() -{ - pool = [[NSAutoreleasePool alloc] init]; -} - -void -XeTeXFontMgr_Mac::terminate() -{ - if (pool != NULL) { - [pool release]; - } -} - -std::string -XeTeXFontMgr_Mac::getPlatformFontDesc(PlatformFontRef descriptor) const -{ - std::string path; - CTFontRef ctFont = CTFontCreateWithFontDescriptor(descriptor, 0.0, 0); - if (ctFont) { - CFURLRef url = NULL; -#if !defined(MAC_OS_X_VERSION_10_6) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_6 - /* kCTFontURLAttribute was not avialable before 10.6 */ - FSRef fsref; - ATSFontRef atsFont = CTFontGetPlatformFont(ctFont, NULL); - OSStatus status = ATSFontGetFileReference(atsFont, &fsref); - if (status == noErr) - url = CFURLCreateFromFSRef(NULL, &fsref); -#else - url = (CFURLRef) CTFontCopyAttribute(ctFont, kCTFontURLAttribute); -#endif - if (url) { - UInt8 posixPath[PATH_MAX]; - if (CFURLGetFileSystemRepresentation(url, true, posixPath, PATH_MAX)) { - path = (char*)posixPath; - } - CFRelease(url); - } - CFRelease(ctFont); - } - if (path.length() == 0) - path = "[unknown]"; - return path; -} diff --git a/crates/xetex_layout/layout/xetex-XeTeXLayoutInterface.cpp b/crates/xetex_layout/layout/xetex-XeTeXLayoutInterface.cpp deleted file mode 100644 index e73af3471..000000000 --- a/crates/xetex_layout/layout/xetex-XeTeXLayoutInterface.cpp +++ /dev/null @@ -1,1139 +0,0 @@ -/****************************************************************************\ - Part of the XeTeX typesetting system - Copyright (c) 1994-2008 by SIL International - Copyright (c) 2009-2012 by Jonathan Kew - Copyright (c) 2012-2015 by Khaled Hosny - - SIL Author(s): Jonathan Kew - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE -FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Except as contained in this notice, the name of the copyright holders -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in this Software without prior written -authorization from the copyright holders. -\****************************************************************************/ - -#include "tectonic_bridge_core.h" - -#include /* Barely needed in this file. */ - -#include -#include -#include -#include -#if !HB_VERSION_ATLEAST(2,5,0) -/* Note: this configuration is no longer actively tested */ -#include -#endif -#include - -#include "tectonic_xetex_layout.h" - -#include "xetex-XeTeXFontInst.h" -#ifdef XETEX_MAC -#include "xetex-XeTeXFontInst_Mac.h" -#endif -#include "xetex-XeTeXFontMgr.h" - -struct XeTeXLayoutEngine_rec -{ - XeTeXFontInst* font; - PlatformFontRef fontRef; - hb_tag_t script; - hb_language_t language; - hb_feature_t* features; - char** ShaperList; // the requested shapers - bool shaperListToFree; - char* shaper; // the actually used shaper - int nFeatures; - uint32_t rgbValue; - float extend; - float slant; - float embolden; - hb_buffer_t* hbBuffer; -}; - -/*******************************************************************/ -/* Glyph bounding box cache to speed up \XeTeXuseglyphmetrics mode */ -/*******************************************************************/ -#include - -// key is combined value representing (font_id << 16) + glyph -// value is glyph bounding box in TeX points -static std::map sGlyphBoxes; - -int -getCachedGlyphBBox(uint16_t fontID, uint16_t glyphID, GlyphBBox* bbox) -{ - uint32_t key = ((uint32_t)fontID << 16) + glyphID; - std::map::const_iterator i = sGlyphBoxes.find(key); - if (i == sGlyphBoxes.end()) { - return 0; - } - *bbox = i->second; - return 1; -} - -void -cacheGlyphBBox(uint16_t fontID, uint16_t glyphID, const GlyphBBox* bbox) -{ - uint32_t key = ((uint32_t)fontID << 16) + glyphID; - sGlyphBoxes[key] = *bbox; -} - -/* The following code used to be in a file called "hz.cpp" and there's no - * particular reason for it to be here, but it was a tiny file with a weird - * name so I wanted to get rid of it. The functions are invoked from the C - * code. */ - -typedef std::pair GlyphId; -typedef std::map ProtrusionFactor; -ProtrusionFactor leftProt, rightProt; - -void -set_cp_code(int fontNum, unsigned int code, int side, int value) -{ - GlyphId id(fontNum, code); - - switch (side) { - case LEFT_SIDE: - leftProt[id] = value; - break; - case RIGHT_SIDE: - rightProt[id] = value; - break; - default: - assert(0); // we should not reach here - } -} - - -int -get_cp_code(int fontNum, unsigned int code, int side) -{ - GlyphId id(fontNum, code); - ProtrusionFactor *container; - - switch (side) { - case LEFT_SIDE: - container = &leftProt; - break; - case RIGHT_SIDE: - container = &rightProt; - break; - default: - assert(0); // we should not reach here - } - - ProtrusionFactor::iterator it = container->find(id); - if (it == container->end()) - return 0; - - return it->second; -} - - - -/*******************************************************************/ - -void -terminate_font_manager() -{ - XeTeXFontMgr::Terminate(); -} - -void -destroy_font_manager() -{ - XeTeXFontMgr::Destroy(); -} - -XeTeXFont -createFont(PlatformFontRef fontRef, Fixed pointSize) -{ - int status = 0; -#ifdef XETEX_MAC - XeTeXFontInst* font = new XeTeXFontInst_Mac(fontRef, Fix2D(pointSize), status); -#else - FcChar8* pathname = 0; - FcPatternGetString(fontRef, FC_FILE, 0, &pathname); - int index; - FcPatternGetInteger(fontRef, FC_INDEX, 0, &index); - XeTeXFontInst* font = new XeTeXFontInst((const char*)pathname, index, Fix2D(pointSize), status); -#endif - if (status != 0) { - delete font; - return NULL; - } - return (XeTeXFont)font; -} - -XeTeXFont -createFontFromFile(const char* filename, int index, Fixed pointSize) -{ - int status = 0; - XeTeXFontInst* font = new XeTeXFontInst(filename, index, Fix2D(pointSize), status); - if (status != 0) { - delete font; - return NULL; - } - return (XeTeXFont)font; -} - -void -setFontLayoutDir(XeTeXFont font, int vertical) -{ - ((XeTeXFontInst*)font)->setLayoutDirVertical(vertical != 0); -} - -PlatformFontRef -findFontByName(const char* name, char* var, double size) -{ - return XeTeXFontMgr::GetFontManager()->findFont(name, var, size); -} - -char -getReqEngine() -{ - return XeTeXFontMgr::GetFontManager()->getReqEngine(); -} - -void -setReqEngine(char reqEngine) -{ - XeTeXFontMgr::GetFontManager()->setReqEngine(reqEngine); -} - -const char* -getFullName(PlatformFontRef fontRef) -{ - return XeTeXFontMgr::GetFontManager()->getFullName(fontRef); -} - -double -getDesignSize(XeTeXFont font) -{ - return XeTeXFontMgr::GetFontManager()->getDesignSize(font); -} - -char* -getFontFilename(XeTeXLayoutEngine engine, uint32_t* index) -{ - return xstrdup(engine->font->getFilename(index)); -} - -PlatformFontRef -getFontRef(XeTeXLayoutEngine engine) -{ - return engine->fontRef; -} - -void -deleteFont(XeTeXFont font) -{ - delete (XeTeXFontInst*)font; -} - -void* -getFontTablePtr(XeTeXFont font, uint32_t tableTag) -{ - return const_cast(((XeTeXFontInst*)font)->getFontTable(tableTag)); -} - -Fixed -getSlant(XeTeXFont font) -{ - float italAngle = ((XeTeXFontInst*)font)->getItalicAngle(); - return D2Fix(tan(-italAngle * M_PI / 180.0)); -} - -static unsigned int -getLargerScriptListTable(XeTeXFont font, hb_tag_t** scriptList) -{ - unsigned int rval = 0; - - hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); - - hb_tag_t* scriptListSub = NULL; - hb_tag_t* scriptListPos = NULL; - - unsigned int scriptCountSub = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL); - scriptListSub = (hb_tag_t*) xcalloc(scriptCountSub, sizeof(hb_tag_t*)); - hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountSub, scriptListSub); - - unsigned int scriptCountPos = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GPOS, 0, NULL, NULL); - scriptListPos = (hb_tag_t*) xcalloc(scriptCountPos, sizeof(hb_tag_t*)); - hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &scriptCountPos, scriptListPos); - - if (scriptCountSub > scriptCountPos) { - if (scriptList != NULL) - *scriptList = scriptListSub; - rval = scriptCountSub; - } else { - if (scriptList != NULL) - *scriptList = scriptListPos; - rval = scriptCountPos; - } - - return rval; -} - -unsigned int -countScripts(XeTeXFont font) -{ - return getLargerScriptListTable(font, NULL); -} - -hb_tag_t -getIndScript(XeTeXFont font, unsigned int index) -{ - hb_tag_t rval = 0; - - hb_tag_t* scriptList; - - unsigned int scriptCount = getLargerScriptListTable(font, &scriptList); - if (scriptList != NULL) { - if (index < scriptCount) - rval = scriptList[index]; - } - - return rval; -} - -unsigned int -countLanguages(XeTeXFont font, hb_tag_t script) -{ - unsigned int rval = 0; - - hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); - hb_tag_t* scriptList; - - unsigned int scriptCount = getLargerScriptListTable(font, &scriptList); - if (scriptList != NULL) { - for (unsigned int i = 0; i < scriptCount; i++) { - if (scriptList[i] == script) { - rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); - rval += hb_ot_layout_script_get_language_tags (face, HB_OT_TAG_GPOS, i, 0, NULL, NULL); - break; - } - } - } - - return rval; -} - -hb_tag_t -getIndLanguage(XeTeXFont font, hb_tag_t script, unsigned int index) -{ - hb_tag_t rval = 0; - - hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); - hb_tag_t* scriptList; - - unsigned int scriptCount = getLargerScriptListTable(font, &scriptList); - if (scriptList != NULL) { - for (unsigned int i = 0; i < scriptCount; i++) { - if (scriptList[i] == script) { - unsigned int langCount; - hb_tag_t* langList; - - langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, NULL, NULL); - langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*)); - hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GSUB, i, 0, &langCount, langList); - - if (index < langCount) { - rval = langList[index]; - break; - } - - free(langList); - - langCount = hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, NULL, NULL); - langList = (hb_tag_t*) xcalloc(langCount, sizeof(hb_tag_t*)); - hb_ot_layout_script_get_language_tags(face, HB_OT_TAG_GPOS, i, 0, &langCount, langList); - - if (index < langCount) { - rval = langList[index]; - break; - } - - free(langList); - } - } - } - - return rval; -} - -unsigned int -countFeatures(XeTeXFont font, hb_tag_t script, hb_tag_t language) -{ - unsigned int rval = 0; - - hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); - - for (int i = 0; i < 2; ++i) { - unsigned int scriptIndex, langIndex = 0; - hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS; - if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) { - if (hb_ot_layout_script_select_language(face, tableTag, scriptIndex, 1, &language, &langIndex) || language == 0) { - rval += hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL); - } - } - } - - return rval; -} - -hb_tag_t -getIndFeature(XeTeXFont font, hb_tag_t script, hb_tag_t language, unsigned int index) -{ - hb_tag_t rval = 0; - - hb_face_t* face = hb_font_get_face(((XeTeXFontInst*)font)->getHbFont()); - - for (int i = 0; i < 2; ++i) { - unsigned int scriptIndex, langIndex = 0; - hb_tag_t tableTag = i == 0 ? HB_OT_TAG_GSUB : HB_OT_TAG_GPOS; - if (hb_ot_layout_table_find_script(face, tableTag, script, &scriptIndex)) { - if (hb_ot_layout_script_select_language(face, tableTag, scriptIndex, 1, &language, &langIndex) || language == 0) { - unsigned int featCount = hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, NULL, NULL); - hb_tag_t* featList = (hb_tag_t*) xcalloc(featCount, sizeof(hb_tag_t*)); - hb_ot_layout_language_get_feature_tags(face, tableTag, scriptIndex, langIndex, 0, &featCount, featList); - - if (index < featCount) { - rval = featList[index]; - break; - } - - index -= featCount; - } - } - } - - return rval; -} - -uint32_t -countGraphiteFeatures(XeTeXLayoutEngine engine) -{ - uint32_t rval = 0; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) - rval = gr_face_n_fref(grFace); - - return rval; -} - -uint32_t -getGraphiteFeatureCode(XeTeXLayoutEngine engine, uint32_t index) -{ - uint32_t rval = 0; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_fref(grFace, index); - rval = gr_fref_id(feature); - } - - return rval; -} - -uint32_t -countGraphiteFeatureSettings(XeTeXLayoutEngine engine, uint32_t featureID) -{ - uint32_t rval = 0; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); - rval = gr_fref_n_values(feature); - } - - return rval; -} - -uint32_t -getGraphiteFeatureSettingCode(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t index) -{ - uint32_t rval = 0; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); - rval = gr_fref_value(feature, index); - } - - return rval; -} - -hb_tag_t tag_from_lang(hb_language_t lang) { - const char* str = hb_language_to_string(lang); - if (str) { - return hb_tag_from_string(str, strlen(str)); - } else { - return 0; - } -} - -uint32_t -getGraphiteFeatureDefaultSetting(XeTeXLayoutEngine engine, uint32_t featureID) -{ - uint32_t rval = 0; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); - gr_feature_val *featureValues = gr_face_featureval_for_lang (grFace, tag_from_lang(engine->language)); - - rval = gr_fref_feature_value(feature, featureValues); - } - - return rval; -} - -char * -getGraphiteFeatureLabel(XeTeXLayoutEngine engine, uint32_t featureID) -{ - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); - uint32_t len = 0; - uint16_t langID = 0x409; - - return (char *) gr_fref_label(feature, &langID, gr_utf8, &len); - } - - return NULL; -} - -char * -getGraphiteFeatureSettingLabel(XeTeXLayoutEngine engine, uint32_t featureID, uint32_t settingID) -{ - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - const gr_feature_ref* feature = gr_face_find_fref(grFace, featureID); - for (int i = 0; i < gr_fref_n_values(feature); i++) { - if ((int) settingID == gr_fref_value(feature, i)) { - uint32_t len = 0; - uint16_t langID = 0x409; - - return (char *) gr_fref_value_label(feature, i, &langID, gr_utf8, &len); - } - } - } - - return NULL; -} - -bool -findGraphiteFeature(XeTeXLayoutEngine engine, const char* s, const char* e, hb_tag_t* f, int* v) - /* s...e is a "feature=setting" string; look for this in the font */ -{ - long tmp; - - *f = 0; - *v = 0; - while (*s == ' ' || *s == '\t') - ++s; - const char* cp = s; - while (cp < e && *cp != '=') - ++cp; - - tmp = findGraphiteFeatureNamed(engine, s, cp - s); - *f = tmp; - if (tmp == -1) - return false; - - ++cp; - while (cp < e && (*cp == ' ' || *cp == '\t')) - ++cp; - - if (cp == e) - /* no setting was specified */ - return false; - - *v = findGraphiteFeatureSettingNamed(engine, *f, cp, e - cp); - if (*v == -1) - return false; - - return true; -} - -long -findGraphiteFeatureNamed(XeTeXLayoutEngine engine, const char* name, int namelength) -{ - long rval = -1; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - hb_tag_t tag = hb_tag_from_string(name, namelength); - for (int i = 0; i < gr_face_n_fref(grFace); i++) { - const gr_feature_ref* feature = gr_face_fref(grFace, i); - uint32_t len = 0; - uint16_t langID = 0x409; - - char* label = (char*)gr_fref_label(feature, &langID, gr_utf8, &len); - - if (gr_fref_id(feature) == tag || strncmp(label, name, namelength) == 0) { - rval = gr_fref_id(feature); - gr_label_destroy(label); - break; - } - - gr_label_destroy(label); - } - } - - return rval; -} - -long -findGraphiteFeatureSettingNamed(XeTeXLayoutEngine engine, uint32_t id, const char* name, int namelength) -{ - long rval = -1; - - hb_face_t* hbFace = hb_font_get_face(engine->font->getHbFont()); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - - if (grFace != NULL) { - hb_tag_t tag = hb_tag_from_string(name, namelength); - const gr_feature_ref* feature = gr_face_find_fref(grFace, id); - for (int i = 0; i < gr_fref_n_values(feature); i++) { - uint32_t len = 0; - uint16_t langID = 0x409; - - char* label = (char*)gr_fref_value_label(feature, i, &langID, gr_utf8, &len); - - if (gr_fref_id(feature) == tag || strncmp(label, name, namelength) == 0) { - rval = gr_fref_value(feature, i); - gr_label_destroy(label); - break; - } - - gr_label_destroy(label); - } - } - - return rval; -} - -float -getGlyphWidth(XeTeXFont font, uint32_t gid) -{ - return ((XeTeXFontInst*)font)->getGlyphWidth(gid); -} - -unsigned int -countGlyphs(XeTeXFont font) -{ - return ((XeTeXFontInst*)font)->getNumGlyphs(); -} - -XeTeXFont -getFont(XeTeXLayoutEngine engine) -{ - return (XeTeXFont)(engine->font); -} - -float -getExtendFactor(XeTeXLayoutEngine engine) -{ - return engine->extend; -} - -float -getSlantFactor(XeTeXLayoutEngine engine) -{ - return engine->slant; -} - -float -getEmboldenFactor(XeTeXLayoutEngine engine) -{ - return engine->embolden; -} - -XeTeXLayoutEngine -createLayoutEngine(PlatformFontRef fontRef, XeTeXFont font, hb_tag_t script, char *language, - hb_feature_t* features, int nFeatures, char **shapers, uint32_t rgbValue, - float extend, float slant, float embolden) -{ - XeTeXLayoutEngine result = new XeTeXLayoutEngine_rec; - result->fontRef = fontRef; - result->font = (XeTeXFontInst*)font; - result->script = script; - result->features = features; - result->ShaperList = shapers; - result->shaperListToFree = false; - result->shaper = NULL; - result->nFeatures = nFeatures; - result->rgbValue = rgbValue; - result->extend = extend; - result->slant = slant; - result->embolden = embolden; - result->hbBuffer = hb_buffer_create(); - - // For Graphite fonts treat the language as BCP 47 tag, for OpenType we - // treat it as a OT language tag for backward compatibility with pre-0.9999 - // XeTeX. - if (getReqEngine() == 'G') - result->language = hb_language_from_string(language, -1); - else - result->language = hb_ot_tag_to_language(hb_tag_from_string(language, -1)); - - free(language); - - return result; -} - -void -deleteLayoutEngine(XeTeXLayoutEngine engine) -{ - hb_buffer_destroy(engine->hbBuffer); - delete engine->font; - free(engine->shaper); - if(engine->shaperListToFree) { - free(engine->ShaperList); - engine->shaperListToFree = false; - engine->ShaperList = NULL; - } - delete engine; -} - -#if !HB_VERSION_ATLEAST(2,5,0) -static unsigned int -_decompose_compat(hb_unicode_funcs_t* ufuncs, - hb_codepoint_t u, - hb_codepoint_t* decomposed, - void* user_data) -{ - return 0; -} - -static hb_unicode_funcs_t* -_get_unicode_funcs(void) -{ - static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs()); - hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, _decompose_compat, NULL, NULL); - return ufuncs; -} -#endif - -int -layoutChars(XeTeXLayoutEngine engine, uint16_t chars[], int32_t offset, int32_t count, int32_t max, - bool rightToLeft) -{ - bool res; - hb_script_t script = HB_SCRIPT_INVALID; - hb_direction_t direction = HB_DIRECTION_LTR; - hb_segment_properties_t segment_props; - hb_shape_plan_t *shape_plan; - hb_font_t* hbFont = engine->font->getHbFont(); - hb_face_t* hbFace = hb_font_get_face(hbFont); - - if (engine->font->getLayoutDirVertical()) - direction = HB_DIRECTION_TTB; - else if (rightToLeft) - direction = HB_DIRECTION_RTL; - - script = hb_ot_tag_to_script (engine->script); - - hb_buffer_reset(engine->hbBuffer); - -#if !HB_VERSION_ATLEAST(2,5,0) - static hb_unicode_funcs_t* hbUnicodeFuncs = NULL; - if (hbUnicodeFuncs == NULL) - hbUnicodeFuncs = _get_unicode_funcs(); - hb_buffer_set_unicode_funcs(engine->hbBuffer, hbUnicodeFuncs); -#endif - - hb_buffer_add_utf16(engine->hbBuffer, chars, max, offset, count); - hb_buffer_set_direction(engine->hbBuffer, direction); - hb_buffer_set_script(engine->hbBuffer, script); - hb_buffer_set_language(engine->hbBuffer, engine->language); - - hb_buffer_guess_segment_properties(engine->hbBuffer); - hb_buffer_get_segment_properties(engine->hbBuffer, &segment_props); - - if (engine->ShaperList == NULL) { - // HarfBuzz gives graphite2 shaper a priority, so that for hybrid - // Graphite/OpenType fonts, Graphite will be used. However, pre-0.9999 - // XeTeX preferred OpenType over Graphite, so we are doing the same - // here for sake of backward compatibility. Since "ot" shaper never - // fails, we set the shaper list to just include it. - engine->ShaperList = (char**) xcalloc(2, sizeof(char*)); - engine->ShaperList[0] = (char*) "ot"; - engine->ShaperList[1] = NULL; - engine->shaperListToFree = true; - } - - shape_plan = hb_shape_plan_create_cached(hbFace, &segment_props, engine->features, engine->nFeatures, engine->ShaperList); - res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures); - - if (engine->shaper != NULL) { - free(engine->shaper); - engine->shaper = NULL; - } - - if (res) { - engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan)); - hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); - } else { - // all selected shapers failed, retrying with default - // we don't use _cached here as the cached plain will always fail. - hb_shape_plan_destroy(shape_plan); - shape_plan = hb_shape_plan_create(hbFace, &segment_props, engine->features, engine->nFeatures, NULL); - res = hb_shape_plan_execute(shape_plan, hbFont, engine->hbBuffer, engine->features, engine->nFeatures); - - if (res) { - engine->shaper = strdup(hb_shape_plan_get_shaper(shape_plan)); - hb_buffer_set_content_type(engine->hbBuffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); - } else { - _tt_abort("all shapers failed"); - } - } - - hb_shape_plan_destroy(shape_plan); - - int glyphCount = hb_buffer_get_length(engine->hbBuffer); - -#ifdef DEBUG - char buf[1024]; - unsigned int consumed; - - printf ("shaper: %s\n", engine->shaper); - - hb_buffer_serialize_flags_t flags = HB_BUFFER_SERIALIZE_FLAGS_DEFAULT; - hb_buffer_serialize_format_t format = HB_BUFFER_SERIALIZE_FORMAT_JSON; - - hb_buffer_serialize_glyphs (engine->hbBuffer, 0, glyphCount, buf, sizeof(buf), &consumed, hbFont, format, flags); - if (consumed) - printf ("buffer glyphs: %s\n", buf); -#endif - - return glyphCount; -} - -void -getGlyphs(XeTeXLayoutEngine engine, uint32_t glyphs[]) -{ - int glyphCount = hb_buffer_get_length(engine->hbBuffer); - hb_glyph_info_t *hbGlyphs = hb_buffer_get_glyph_infos(engine->hbBuffer, NULL); - - for (int i = 0; i < glyphCount; i++) - glyphs[i] = hbGlyphs[i].codepoint; -} - -void -getGlyphAdvances(XeTeXLayoutEngine engine, float advances[]) -{ - int glyphCount = hb_buffer_get_length(engine->hbBuffer); - hb_glyph_position_t *hbPositions = hb_buffer_get_glyph_positions(engine->hbBuffer, NULL); - - for (int i = 0; i < glyphCount; i++) { - if (engine->font->getLayoutDirVertical()) - advances[i] = engine->font->unitsToPoints(hbPositions[i].y_advance); - else - advances[i] = engine->font->unitsToPoints(hbPositions[i].x_advance); - } -} - -void -getGlyphPositions(XeTeXLayoutEngine engine, FloatPoint positions[]) -{ - int glyphCount = hb_buffer_get_length(engine->hbBuffer); - hb_glyph_position_t *hbPositions = hb_buffer_get_glyph_positions(engine->hbBuffer, NULL); - - float x = 0, y = 0; - - if (engine->font->getLayoutDirVertical()) { - for (int i = 0; i < glyphCount; i++) { - positions[i].x = -engine->font->unitsToPoints(x + hbPositions[i].y_offset); /* negative is forwards */ - positions[i].y = engine->font->unitsToPoints(y - hbPositions[i].x_offset); - x += hbPositions[i].y_advance; - y += hbPositions[i].x_advance; - } - positions[glyphCount].x = -engine->font->unitsToPoints(x); - positions[glyphCount].y = engine->font->unitsToPoints(y); - } else { - for (int i = 0; i < glyphCount; i++) { - positions[i].x = engine->font->unitsToPoints(x + hbPositions[i].x_offset); - positions[i].y = -engine->font->unitsToPoints(y + hbPositions[i].y_offset); /* negative is upwards */ - x += hbPositions[i].x_advance; - y += hbPositions[i].y_advance; - } - positions[glyphCount].x = engine->font->unitsToPoints(x); - positions[glyphCount].y = -engine->font->unitsToPoints(y); - } - - if (engine->extend != 1.0 || engine->slant != 0.0) - for (int i = 0; i <= glyphCount; ++i) - positions[i].x = positions[i].x * engine->extend - positions[i].y * engine->slant; -} - -float -getPointSize(XeTeXLayoutEngine engine) -{ - return engine->font->getPointSize(); -} - -void -getAscentAndDescent(XeTeXLayoutEngine engine, float* ascent, float* descent) -{ - *ascent = engine->font->getAscent(); - *descent = engine->font->getDescent(); -} - -void -getCapAndXHeight(XeTeXLayoutEngine engine, float* capheight, float* xheight) -{ - *capheight = engine->font->getCapHeight(); - *xheight = engine->font->getXHeight(); -} - -int -getDefaultDirection(XeTeXLayoutEngine engine) -{ - hb_script_t script = hb_buffer_get_script(engine->hbBuffer); - if (hb_script_get_horizontal_direction (script) == HB_DIRECTION_RTL) - return UBIDI_DEFAULT_RTL; - else - return UBIDI_DEFAULT_LTR; -} - -uint32_t -getRgbValue(XeTeXLayoutEngine engine) -{ - return engine->rgbValue; -} - -void -getGlyphBounds(XeTeXLayoutEngine engine, uint32_t glyphID, GlyphBBox* bbox) -{ - engine->font->getGlyphBounds(glyphID, bbox); - if (engine->extend != 0.0) { - bbox->xMin *= engine->extend; - bbox->xMax *= engine->extend; - } -} - -float -getGlyphWidthFromEngine(XeTeXLayoutEngine engine, uint32_t glyphID) -{ - return engine->extend * engine->font->getGlyphWidth(glyphID); -} - -void -getGlyphHeightDepth(XeTeXLayoutEngine engine, uint32_t glyphID, float* height, float* depth) -{ - engine->font->getGlyphHeightDepth(glyphID, height, depth); -} - -void -getGlyphSidebearings(XeTeXLayoutEngine engine, uint32_t glyphID, float* lsb, float* rsb) -{ - engine->font->getGlyphSidebearings(glyphID, lsb, rsb); - if (engine->extend != 0.0) { - *lsb *= engine->extend; - *rsb *= engine->extend; - } -} - -float -getGlyphItalCorr(XeTeXLayoutEngine engine, uint32_t glyphID) -{ - return engine->extend * engine->font->getGlyphItalCorr(glyphID); -} - -uint32_t -mapCharToGlyph(XeTeXLayoutEngine engine, uint32_t charCode) -{ - return engine->font->mapCharToGlyph(charCode); -} - -int -getFontCharRange(XeTeXLayoutEngine engine, int reqFirst) -{ - if (reqFirst) - return engine->font->getFirstCharCode(); - else - return engine->font->getLastCharCode(); -} - -const char* -getGlyphName(XeTeXFont font, uint16_t gid, int* len) -{ - return ((XeTeXFontInst*)font)->getGlyphName(gid, *len); -} - -int -mapGlyphToIndex(XeTeXLayoutEngine engine, const char* glyphName) -{ - return engine->font->mapGlyphToIndex(glyphName); -} - -static gr_segment* grSegment = NULL; -static const gr_slot* grPrevSlot = NULL; -static int grTextLen; - -bool -initGraphiteBreaking(XeTeXLayoutEngine engine, const uint16_t* txtPtr, int txtLen) -{ - hb_font_t* hbFont = engine->font->getHbFont(); - hb_face_t* hbFace = hb_font_get_face(hbFont); - gr_face* grFace = hb_graphite2_face_get_gr_face(hbFace); - gr_font* grFont = gr_make_font(hb_font_get_ptem(hbFont), grFace); - if (grFace != NULL && grFont != NULL) { - if (grSegment != NULL) { - gr_seg_destroy(grSegment); - grSegment = NULL; - grPrevSlot = NULL; - } - - gr_feature_val *grFeatureValues = gr_face_featureval_for_lang (grFace, tag_from_lang(engine->language)); - - int nFeatures = engine->nFeatures; - hb_feature_t *features = engine->features; - while (nFeatures--) { - const gr_feature_ref *fref = gr_face_find_fref (grFace, features->tag); - if (fref) - gr_fref_set_feature_value (fref, features->value, grFeatureValues); - features++; - } - - grSegment = gr_make_seg(grFont, grFace, engine->script, grFeatureValues, gr_utf16, txtPtr, txtLen, 0); - grPrevSlot = gr_seg_first_slot(grSegment); - grTextLen = txtLen; - - return true; - } - - return false; -} - -int -findNextGraphiteBreak(void) -{ - int ret = -1; - - if (grSegment != NULL) { - if (grPrevSlot && grPrevSlot != gr_seg_last_slot(grSegment)) { - for (const gr_slot* s = gr_slot_next_in_segment(grPrevSlot); s != NULL; s = gr_slot_next_in_segment(s)) { - const gr_char_info* ci = NULL; - int bw; - - ci = gr_seg_cinfo(grSegment, gr_slot_index(s)); - bw = gr_cinfo_break_weight(ci); - if (bw < gr_breakNone && bw >= gr_breakBeforeWord) { - grPrevSlot = s; - ret = gr_cinfo_base(ci); - } else if (bw > gr_breakNone && bw <= gr_breakWord) { - grPrevSlot = gr_slot_next_in_segment(s); - ret = gr_cinfo_base(ci) + 1; - } - - if (ret != -1) - break; - } - - if (ret == -1) { - grPrevSlot = gr_seg_last_slot(grSegment); - ret = grTextLen; - } - } - } - - return ret; -} - -bool -usingGraphite(XeTeXLayoutEngine engine) -{ - if (engine->shaper != NULL && (strcmp("graphite2", engine->shaper) == 0)) - return true; - else - return false; -} - -bool -usingOpenType(XeTeXLayoutEngine engine) -{ - if (engine->shaper == NULL || (strcmp("ot", engine->shaper) == 0)) - return true; - else - return false; -} - -bool -isOpenTypeMathFont(XeTeXLayoutEngine engine) -{ - return hb_ot_math_has_data(hb_font_get_face(engine->font->getHbFont())); -} - -/* New Tectonic APIs for crate encapsulation */ - -hb_font_t * -ttxl_get_hb_font(XeTeXLayoutEngine engine) -{ - return engine->font->getHbFont(); -} - -float -ttxl_font_units_to_points(XeTeXFont font, float units) -{ - return ((XeTeXFontInst *) font)->unitsToPoints(units); -} - -float -ttxl_font_points_to_units(XeTeXFont font, float points) -{ - return ((XeTeXFontInst *) font)->pointsToUnits(points); -} - -float -ttxl_font_get_point_size(XeTeXFont font) -{ - return ((XeTeXFontInst *) font)->getPointSize(); -} - -const char * -ttxl_platfont_get_desc(PlatformFontRef fontRef) -{ - return XeTeXFontMgr::GetFontManager()->getPlatformFontDesc(fontRef).c_str(); -} diff --git a/crates/xetex_layout/src/c_api.rs b/crates/xetex_layout/src/c_api.rs new file mode 100644 index 000000000..1e15ed4d8 --- /dev/null +++ b/crates/xetex_layout/src/c_api.rs @@ -0,0 +1,106 @@ +use crate::engine::LayoutEngine; +use crate::font::Font; +use std::collections::BTreeMap; +use std::sync::Mutex; +#[cfg(not(target_os = "macos"))] +use tectonic_bridge_fontconfig as fc; + +mod engine; +mod font; +mod manager; + +#[derive(Copy, Clone, PartialEq, Debug)] +#[repr(C)] +pub struct FloatPoint { + pub x: f32, + pub y: f32, +} + +/// cbindgen:rename-all=camelCase +#[derive(Copy, Clone, Default, PartialEq, Debug)] +#[repr(C)] +pub struct GlyphBBox { + pub x_min: f32, + pub y_min: f32, + pub x_max: f32, + pub y_max: f32, +} + +#[cfg(not(target_os = "macos"))] +pub type Fixed = i32; +/// cbindgen:ignore +#[cfg(target_os = "macos")] +pub type Fixed = u32; +pub type OTTag = u32; +pub type GlyphID = u16; +/// cbindgen:ignore +pub type XeTeXFont = *mut Font; +/// cbindgen:ignore +pub type XeTeXLayoutEngine = *mut LayoutEngine; +#[cfg(not(target_os = "macos"))] +pub(crate) type RawPlatformFontRef = *mut fc::sys::FcPattern; +#[cfg(target_os = "macos")] +pub(crate) type RawPlatformFontRef = tectonic_mac_core::sys::CTFontDescriptorRef; +/// cbindgen:ignore +#[cfg(not(target_os = "macos"))] +pub(crate) type PlatformFontRef = fc::Pattern; +/// cbindgen:ignore +#[cfg(target_os = "macos")] +pub(crate) type PlatformFontRef = tectonic_mac_core::CTFontDescriptor; + +/// key is combined value representing `(font_id, glyph)` +/// value is glyph bounding box in TeX points +static GLYPH_BOXES: Mutex> = Mutex::new(BTreeMap::new()); + +#[no_mangle] +pub unsafe extern "C" fn getCachedGlyphBBox( + font_id: u16, + glyph_id: u16, + bbox: *mut GlyphBBox, +) -> i32 { + match GLYPH_BOXES.lock().unwrap().get(&(font_id, glyph_id)) { + Some(val) => { + *bbox = *val; + 1 + } + None => 0, + } +} + +#[no_mangle] +pub unsafe extern "C" fn cacheGlyphBBox(font_id: u16, glyph_id: u16, bbox: *const GlyphBBox) { + GLYPH_BOXES + .lock() + .unwrap() + .insert((font_id, glyph_id), *bbox); +} + +/* The following code used to be in a file called "hz.cpp" and there's no + * particular reason for it to be here, but it was a tiny file with a weird + * name so I wanted to get rid of it. The functions are invoked from the C + * code. */ + +pub const LEFT_SIDE: i32 = 0; +pub const RIGHT_SIDE: i32 = 1; + +static LEFT_PROT: Mutex> = Mutex::new(BTreeMap::new()); +static RIGHT_PROT: Mutex> = Mutex::new(BTreeMap::new()); + +#[no_mangle] +pub extern "C" fn set_cp_code(font_num: i32, code: u32, side: i32, value: i32) { + match side { + LEFT_SIDE => LEFT_PROT.lock().unwrap().insert((font_num, code), value), + RIGHT_SIDE => RIGHT_PROT.lock().unwrap().insert((font_num, code), value), + _ => unreachable!(), + }; +} + +#[no_mangle] +pub extern "C" fn get_cp_code(font_num: i32, code: u32, side: i32) -> i32 { + match side { + LEFT_SIDE => LEFT_PROT.lock().unwrap().get(&(font_num, code)).copied(), + RIGHT_SIDE => RIGHT_PROT.lock().unwrap().get(&(font_num, code)).copied(), + _ => unreachable!(), + } + .unwrap_or(0) +} diff --git a/crates/xetex_layout/src/c_api/engine.rs b/crates/xetex_layout/src/c_api/engine.rs new file mode 100644 index 000000000..8b0116395 --- /dev/null +++ b/crates/xetex_layout/src/c_api/engine.rs @@ -0,0 +1,565 @@ +use crate::c_api::{FloatPoint, GlyphBBox, XeTeXFont, XeTeXLayoutEngine}; +use crate::engine::{find_graphite_feature_named, GrBreak, LayoutEngine, MaybeBorrow}; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; +use std::{ptr, slice}; +use tectonic_bridge_graphite2 as gr; +use tectonic_bridge_harfbuzz as hb; + +#[no_mangle] +pub unsafe extern "C" fn createLayoutEngine( + font: XeTeXFont, + script: hb::Tag, + language: *mut libc::c_char, + features: *mut hb::Feature, + n_features: libc::c_int, + shapers: *mut *const libc::c_char, + rgb_value: u32, + extend: f32, + slant: f32, + embolden: f32, +) -> XeTeXLayoutEngine { + let font = Box::from_raw(font); + + let language = if !language.is_null() { + Some(Cow::Owned(CStr::from_ptr(language).to_owned())) + } else { + None + }; + let features: Box<[_]> = if !features.is_null() { + let len = n_features as usize; + Box::from(slice::from_raw_parts(features, len)) + } else { + Box::new([]) + }; + let shaper_list = if !shapers.is_null() { + let mut len = 0; + while !(*shapers.add(len)).is_null() { + len += 1; + } + len += 1; + slice::from_raw_parts(shapers, len).to_vec() + } else { + Vec::new() + }; + + let this = Box::new(LayoutEngine::new( + MaybeBorrow::Owned(font), + script, + language, + features, + shaper_list, + rgb_value, + extend, + slant, + embolden, + )); + + Box::into_raw(this) +} + +#[no_mangle] +pub unsafe extern "C" fn createLayoutEngineBorrowed( + font: XeTeXFont, + script: hb::Tag, + language: *mut libc::c_char, + features: *mut hb::Feature, + n_features: libc::c_int, + shapers: *mut *const libc::c_char, + rgb_value: u32, + extend: f32, + slant: f32, + embolden: f32, +) -> XeTeXLayoutEngine { + let font = &mut *font; + + let language = if !language.is_null() { + Some(Cow::Borrowed(CStr::from_ptr(language))) + } else { + None + }; + let features: Box<[_]> = if !features.is_null() { + let len = n_features as usize; + // Clone the slice - we don't own it and it may be realloced at any time. + Box::from(slice::from_raw_parts(features, len)) + } else { + Box::new([]) + }; + let shaper_list = if !shapers.is_null() { + let mut len = 0; + while !(*shapers.add(len)).is_null() { + len += 1; + } + len += 1; + slice::from_raw_parts(shapers, len).to_vec() + } else { + Vec::new() + }; + + let this = Box::new(LayoutEngine::new( + MaybeBorrow::Borrowed(font), + script, + language, + features, + shaper_list, + rgb_value, + extend, + slant, + embolden, + )); + + Box::into_raw(this) +} + +#[no_mangle] +pub unsafe extern "C" fn deleteLayoutEngine(this: XeTeXLayoutEngine) { + let _ = Box::from_raw(this); +} + +#[no_mangle] +pub unsafe extern "C" fn getFont(engine: XeTeXLayoutEngine) -> XeTeXFont { + (*engine).font_mut() +} + +#[no_mangle] +pub unsafe extern "C" fn getExtendFactor(engine: XeTeXLayoutEngine) -> f32 { + (*engine).extend() +} + +#[no_mangle] +pub unsafe extern "C" fn getSlantFactor(engine: XeTeXLayoutEngine) -> f32 { + (*engine).slant() +} + +#[no_mangle] +pub unsafe extern "C" fn getEmboldenFactor(engine: XeTeXLayoutEngine) -> f32 { + (*engine).embolden() +} + +#[no_mangle] +pub unsafe extern "C" fn getPointSize(engine: XeTeXLayoutEngine) -> f32 { + (*engine).font().point_size() +} + +#[no_mangle] +pub unsafe extern "C" fn getAscentAndDescent( + engine: XeTeXLayoutEngine, + ascent: *mut f32, + descent: *mut f32, +) { + *ascent = (*engine).font().ascent(); + *descent = (*engine).font().descent(); +} + +#[no_mangle] +pub unsafe extern "C" fn getCapAndXHeight( + engine: XeTeXLayoutEngine, + capheight: *mut f32, + xheight: *mut f32, +) { + *capheight = (*engine).font().cap_height(); + *xheight = (*engine).font().x_height(); +} + +#[no_mangle] +pub unsafe extern "C" fn getDefaultDirection(engine: XeTeXLayoutEngine) -> libc::c_int { + (*engine).default_dir() as libc::c_int +} + +#[no_mangle] +pub unsafe extern "C" fn getRgbValue(engine: XeTeXLayoutEngine) -> u32 { + (*engine).rgb() +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphBounds( + engine: XeTeXLayoutEngine, + glyph_id: u32, + bbox: *mut GlyphBBox, +) { + *bbox = (*engine).font_mut().get_glyph_bounds(glyph_id as u16); + if (*engine).extend() != 0.0 { + (*bbox).x_min *= (*engine).extend(); + (*bbox).x_max *= (*engine).extend(); + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphWidthFromEngine(engine: XeTeXLayoutEngine, glyph_id: u32) -> f32 { + (*engine).extend() * (*engine).font().get_glyph_width(glyph_id) +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphHeightDepth( + engine: XeTeXLayoutEngine, + glyph_id: u32, + height: *mut f32, + depth: *mut f32, +) { + (*engine) + .font_mut() + .get_glyph_height_depth(glyph_id as u16, height.as_mut(), depth.as_mut()); +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphSidebearings( + engine: XeTeXLayoutEngine, + glyph_id: u32, + lsb: *mut f32, + rsb: *mut f32, +) { + (*engine) + .font_mut() + .get_glyph_sidebearings(glyph_id as u16, lsb.as_mut(), rsb.as_mut()); + if (*engine).extend() != 0.0 { + *lsb *= (*engine).extend(); + *rsb *= (*engine).extend(); + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphItalCorr(engine: XeTeXLayoutEngine, glyph_id: u32) -> f32 { + (*engine).extend() * (*engine).font_mut().get_glyph_ital_corr(glyph_id as u16) +} + +#[no_mangle] +pub unsafe extern "C" fn mapCharToGlyph(engine: XeTeXLayoutEngine, char_code: u32) -> u32 { + (*engine).font().map_char_to_glyph(char_code) as u32 +} + +#[no_mangle] +pub unsafe extern "C" fn getFontCharRange( + engine: XeTeXLayoutEngine, + req_first: libc::c_int, +) -> libc::c_int { + if req_first != 0 { + (*engine).font().first_char_code() as libc::c_int + } else { + (*engine).font().last_char_code() as libc::c_int + } +} + +#[no_mangle] +pub unsafe extern "C" fn mapGlyphToIndex( + engine: XeTeXLayoutEngine, + glyph_name: *const libc::c_char, +) -> libc::c_int { + (*engine) + .font() + .map_glyph_to_index(CStr::from_ptr(glyph_name)) as libc::c_int +} + +#[no_mangle] +pub unsafe extern "C" fn usingGraphite(engine: XeTeXLayoutEngine) -> bool { + (*engine).used_graphite() +} + +#[no_mangle] +pub unsafe extern "C" fn usingOpenType(engine: XeTeXLayoutEngine) -> bool { + (*engine).used_ot() +} + +#[no_mangle] +pub unsafe extern "C" fn isOpenTypeMathFont(engine: XeTeXLayoutEngine) -> bool { + (*engine).font().hb_font().face().has_ot_math_data() +} + +#[no_mangle] +pub unsafe extern "C" fn ttxl_get_hb_font(engine: XeTeXLayoutEngine) -> *mut hb::sys::hb_font_t { + (*engine).font().hb_font().as_ptr() +} + +#[no_mangle] +pub unsafe extern "C" fn layoutChars( + engine: XeTeXLayoutEngine, + chars: *mut u16, + offset: i32, + count: i32, + max: i32, + rtl: bool, +) -> libc::c_int { + let chars = slice::from_raw_parts(chars, max as usize); + let engine = &mut *engine; + engine.layout_chars(&chars[offset as usize..(offset + count) as usize], rtl) as libc::c_int +} + +#[no_mangle] +pub unsafe extern "C" fn getFontFilename( + engine: XeTeXLayoutEngine, + index: *mut u32, +) -> *const libc::c_char { + (*engine).font().filename(&mut *index).to_owned().into_raw() +} + +#[no_mangle] +pub unsafe extern "C" fn freeFontFilename(filename: *const libc::c_char) { + let _ = CString::from_raw(filename.cast_mut()); +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphs(engine: XeTeXLayoutEngine, glyphs: *mut u32) { + let hb_glyphs = (*engine).hb_buffer().glyph_info().unwrap(); + + for (idx, glyph) in hb_glyphs.iter().enumerate() { + *glyphs.add(idx) = glyph.codepoint; + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphAdvances(engine: XeTeXLayoutEngine, advances: *mut f32) { + let engine = &*engine; + let hb_positions = engine.hb_buffer().glyph_positions().unwrap(); + + for (i, pos) in hb_positions.iter().enumerate() { + let advance = if engine.font().layout_dir_vertical() { + pos.y_advance + } else { + pos.x_advance + }; + + *advances.add(i) = engine.font().units_to_points(advance as f64) as f32; + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphPositions(engine: XeTeXLayoutEngine, positions: *mut FloatPoint) { + let engine = &mut *engine; + let hb_positions = engine.hb_buffer().glyph_positions().unwrap(); + + let mut x: f32 = 0.0; + let mut y: f32 = 0.0; + let font = engine.font(); + + if font.layout_dir_vertical() { + for (i, pos) in hb_positions.iter().enumerate() { + (*positions.add(i)).x = -font.units_to_points((x + pos.y_offset as f32) as f64) as f32; + (*positions.add(i)).y = font.units_to_points((y - pos.x_offset as f32) as f64) as f32; + x += pos.y_advance as f32; + y += pos.x_advance as f32; + } + + (*positions.add(hb_positions.len())).x = -font.units_to_points(x as f64) as f32; + (*positions.add(hb_positions.len())).y = font.units_to_points(y as f64) as f32; + } else { + for (i, pos) in hb_positions.iter().enumerate() { + (*positions.add(i)).x = font.units_to_points((x + pos.x_offset as f32) as f64) as f32; + (*positions.add(i)).y = -font.units_to_points((y + pos.y_offset as f32) as f64) as f32; /* negative is upwards */ + x += pos.x_advance as f32; + y += pos.y_advance as f32; + } + (*positions.add(hb_positions.len())).x = font.units_to_points(x as f64) as f32; + (*positions.add(hb_positions.len())).y = -font.units_to_points(y as f64) as f32; + } + + if engine.extend() != 1.0 || engine.slant() != 0.0 { + for i in 0..=hb_positions.len() { + let pos = &mut *positions.add(i); + pos.x = pos.x * engine.extend() - pos.y * engine.slant(); + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn countGraphiteFeatures(engine: XeTeXLayoutEngine) -> u32 { + let hb_face = (*engine).font().hb_font().face(); + match hb_face.gr_face() { + Some(face) => face.num_feature_refs() as u32, + None => 0, + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGraphiteFeatureCode(engine: XeTeXLayoutEngine, index: u32) -> u32 { + crate::engine::get_graphite_feature_code(&*engine, index).unwrap_or(0) +} + +#[no_mangle] +pub unsafe extern "C" fn countGraphiteFeatureSettings( + engine: XeTeXLayoutEngine, + feature_id: u32, +) -> u32 { + crate::engine::count_graphite_feature_settings(&*engine, feature_id).unwrap_or(0) +} + +#[no_mangle] +pub unsafe extern "C" fn getGraphiteFeatureSettingCode( + engine: XeTeXLayoutEngine, + feature_id: u32, + index: u32, +) -> u32 { + crate::engine::get_graphite_feature_setting_code(&*engine, feature_id, index).unwrap_or(0) +} + +#[no_mangle] +pub unsafe extern "C" fn getGraphiteFeatureDefaultSetting( + engine: XeTeXLayoutEngine, + feature_id: u32, +) -> u32 { + crate::engine::get_graphite_feature_default_setting(&*engine, feature_id).unwrap_or(0) +} + +#[no_mangle] +pub unsafe extern "C" fn getGraphiteFeatureLabel( + engine: XeTeXLayoutEngine, + feature_id: u32, +) -> *const libc::c_char { + match crate::engine::get_graphite_feature_label(&*engine, feature_id) { + Some(label) => label.into_raw().cast(), + None => ptr::null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn getGraphiteFeatureSettingLabel( + engine: XeTeXLayoutEngine, + feature_id: u32, + setting_id: u32, +) -> *const libc::c_char { + match crate::engine::get_graphite_feature_setting_label(&*engine, feature_id, setting_id) { + Some(label) => label.into_raw().cast(), + None => ptr::null(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn findGraphiteFeature( + engine: XeTeXLayoutEngine, + s: *const libc::c_char, + e: *const libc::c_char, + f: *mut hb::Tag, + v: *mut libc::c_int, +) -> bool { + let len = e.byte_offset_from(s).unsigned_abs(); + let str = slice::from_raw_parts(s.cast(), len); + crate::engine::find_graphite_feature(&*engine, str, &mut *f, &mut *v) +} + +#[no_mangle] +pub unsafe extern "C" fn findGraphiteFeatureNamed( + engine: XeTeXLayoutEngine, + name: *const libc::c_char, + namelength: libc::c_int, +) -> libc::c_long { + let name = if !name.is_null() && namelength > 0 { + slice::from_raw_parts(name.cast::(), namelength as usize) + } else { + return -1; + }; + find_graphite_feature_named(&*engine, name) + .map(|i| i as libc::c_long) + .unwrap_or(-1) +} + +#[no_mangle] +pub unsafe extern "C" fn findGraphiteFeatureSettingNamed( + engine: XeTeXLayoutEngine, + id: u32, + name: *const libc::c_char, + namelength: libc::c_int, +) -> libc::c_long { + let name = if !name.is_null() && namelength > 0 { + slice::from_raw_parts(name.cast(), namelength as usize) + } else { + return -1; + }; + + crate::engine::find_graphite_feature_setting_named(&*engine, id, name) + .map(|i| i as libc::c_long) + .unwrap_or(-1) +} + +#[no_mangle] +pub unsafe extern "C" fn initGraphiteBreaking( + engine: XeTeXLayoutEngine, + txt_ptr: *const u16, + txt_len: libc::c_uint, +) -> bool { + let engine = &mut *engine; + + engine.gr_breaking = None; + + let hb_font = engine.font().hb_font(); + let hb_face = hb_font.face(); + let Some(gr_face) = hb_face.gr_face() else { + return false; + }; + let Some(gr_font) = gr::Font::new(hb_font.ptem(), gr_face) else { + return false; + }; + + let lang = engine + .language + .to_string() + .map(hb::Tag::from_cstr) + .map(hb::Tag::to_raw) + .unwrap_or(0); + let mut gr_feature_values = gr_face.feature_val_for_lang(lang); + + let features = &engine.features; + for i in (0..engine.features.len()).rev() { + let fref = gr_face.find_feature_ref(features[i].tag); + if let Some(fref) = fref { + let _ = fref.set_feat_value(gr_feature_values.as_mut(), features[i].value as u16); + } + } + + // SAFETY: Required pointer must be to an array of at least len + let text = unsafe { slice::from_raw_parts(txt_ptr, txt_len as usize) }; + let gr_seg = gr::Segment::new( + gr_font.as_ref(), + gr_face, + engine.script().to_raw(), + gr_feature_values.as_ref(), + text, + ) + .unwrap(); + + engine.gr_breaking = Some(GrBreak { + slot: gr_seg.as_ref().first_slot(), + segment: gr_seg, + text_len: txt_len, + }); + + true +} + +#[no_mangle] +pub unsafe extern "C" fn findNextGraphiteBreak(engine: XeTeXLayoutEngine) -> libc::c_int { + let engine = &mut *engine; + let Some(breaking) = &mut engine.gr_breaking else { + return -1; + }; + + let segment = breaking.segment.as_ref(); + if breaking.slot != segment.last_slot() { + let mut s = segment.next(&breaking.slot); + let mut ret = -1; + + while let Some(slot) = s { + let ci = segment.cinfo(segment.index(&slot)); + let bw = ci.break_weight(); + if (gr::BREAK_BEFORE_WORD..gr::BREAK_NONE).contains(&bw) { + breaking.slot = slot.clone(); + ret = ci.base() as libc::c_int; + } else if (gr::BREAK_NONE + 1..=gr::BREAK_WORD).contains(&bw) { + breaking.slot = segment.next(&slot).unwrap(); + ret = (ci.base() + 1) as libc::c_int; + } + + if ret != -1 { + break; + } + + s = segment.next(&slot); + } + + if ret == -1 { + breaking.slot = segment.last_slot(); + breaking.text_len as libc::c_int + } else { + ret + } + } else { + -1 + } +} diff --git a/crates/xetex_layout/src/c_api/font.rs b/crates/xetex_layout/src/c_api/font.rs new file mode 100644 index 000000000..ae4fb65b5 --- /dev/null +++ b/crates/xetex_layout/src/c_api/font.rs @@ -0,0 +1,229 @@ +use crate::c_api::{Fixed, OTTag, RawPlatformFontRef, XeTeXFont}; +use crate::font::{get_larger_script_list_table_ot, Font}; +use crate::utils::{d_to_fix, fix_to_d, raw_to_rs}; +use std::ffi::{CStr, CString}; +use std::ptr; +use tectonic_bridge_freetype2 as ft; +use tectonic_bridge_harfbuzz as hb; +#[cfg(target_os = "macos")] +use tectonic_mac_core::{sys::CTFontRef, CTFont, CoreType}; + +#[no_mangle] +pub unsafe extern "C" fn hasFontTable(font: XeTeXFont, table_tag: OTTag) -> bool { + // TODO: has_font_table for efficiency + (*font) + .load_font_table(ft::TableTag::Other(table_tag)) + .is_some() +} + +#[no_mangle] +pub unsafe extern "C" fn getSlant(font: XeTeXFont) -> Fixed { + let angle = (*font).italic_angle() as f64; + d_to_fix(f64::tan(-angle * std::f64::consts::PI / 180.0)) +} + +#[no_mangle] +pub unsafe extern "C" fn countGlyphs(font: XeTeXFont) -> libc::c_uint { + (*font).num_glyphs() as libc::c_uint +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphWidth(font: XeTeXFont, gid: u32) -> f32 { + (*font).get_glyph_width(gid) +} + +#[no_mangle] +pub unsafe extern "C" fn setFontLayoutDir(font: XeTeXFont, vertical: libc::c_int) { + (*font).set_layout_dir_vertical(vertical != 0) +} + +#[no_mangle] +pub unsafe extern "C" fn getIndScript(font: XeTeXFont, index: libc::c_uint) -> hb::Tag { + get_larger_script_list_table_ot(&*font) + .script(index as usize) + .map_or(hb::Tag::new(0), |s| s.tag()) +} + +#[no_mangle] +pub unsafe extern "C" fn getIndLanguage( + font: XeTeXFont, + script: hb::Tag, + index: libc::c_uint, +) -> hb::Tag { + let index = index as usize; + let script = get_larger_script_list_table_ot(&*font).find_script(script); + + if let Some(script) = script { + if let Some(lang) = script.lang(index) { + return lang.tag(); + } + let lang = script.swap_table().and_then(|s| s.lang(index)); + if let Some(lang) = lang { + return lang.tag(); + } + } + + hb::Tag::new(0) +} + +#[no_mangle] +pub unsafe extern "C" fn getIndFeature( + font: XeTeXFont, + script: hb::Tag, + language: hb::Tag, + index: libc::c_uint, +) -> hb::Tag { + let mut index = index as usize; + let layout = (*font).hb_font().face().ot_layout(); + + for table_tag in [hb::GTag::GSub, hb::GTag::GPos] { + let table = layout.table(table_tag); + if let Some(script) = table.find_script(script) { + let lang = script.select_lang(&[language]); + if lang.is_ok() || language == hb::Tag::new(0) { + let lang = lang.unwrap_or_else(|l| l); + let feat = lang.feature(index); + + if let Some(feat) = feat { + return feat; + } + + index -= lang.feature_tags_len(); + } + } + } + + hb::Tag::new(0) +} + +#[no_mangle] +pub unsafe extern "C" fn getGlyphName( + font: XeTeXFont, + gid: u16, + len: *mut libc::c_int, +) -> *const libc::c_char { + match (*font).get_glyph_name(gid) { + Some(out) => { + *len = out.as_bytes().len() as libc::c_int; + CString::into_raw(out) + } + None => { + *len = 0; + ptr::null() + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn freeGlyphName(name: *mut libc::c_char) { + let _ = CString::from_raw(name); +} + +#[no_mangle] +pub unsafe extern "C" fn ttxl_font_units_to_points(font: XeTeXFont, units: f32) -> f32 { + (*font).units_to_points(units as f64) as f32 +} + +#[no_mangle] +pub unsafe extern "C" fn ttxl_font_points_to_units(font: XeTeXFont, points: f32) -> f32 { + (*font).points_to_units(points as f64) as f32 +} + +#[no_mangle] +pub unsafe extern "C" fn ttxl_font_get_point_size(font: XeTeXFont) -> f32 { + (*font).point_size() +} + +#[no_mangle] +pub unsafe extern "C" fn createFont(font_ref: RawPlatformFontRef, point_size: Fixed) -> XeTeXFont { + let font_ref = match raw_to_rs(font_ref) { + Some(fr) => fr, + None => return ptr::null_mut(), + }; + + match Font::new(font_ref, fix_to_d(point_size) as f32) { + Err(_) => ptr::null_mut(), + Ok(out) => Box::into_raw(Box::new(out)), + } +} + +#[no_mangle] +pub unsafe extern "C" fn createFontFromFile( + filename: *const libc::c_char, + index: libc::c_int, + point_size: Fixed, +) -> XeTeXFont { + let filename = if filename.is_null() { + None + } else { + Some(CStr::from_ptr(filename).to_str().unwrap()) + }; + + match Font::new_path_index(filename, index as usize, fix_to_d(point_size) as f32) { + Err(_) => ptr::null_mut(), + Ok(out) => Box::into_raw(Box::new(out)), + } +} + +#[no_mangle] +pub unsafe extern "C" fn deleteFont(font: XeTeXFont) { + let _ = Box::from_raw(font); +} + +#[no_mangle] +pub unsafe extern "C" fn countScripts(font: XeTeXFont) -> libc::c_uint { + get_larger_script_list_table_ot(&*font).script_tags_len() as libc::c_uint +} + +#[no_mangle] +pub unsafe extern "C" fn countLanguages(font: XeTeXFont, script: hb::Tag) -> libc::c_uint { + let table = get_larger_script_list_table_ot(&*font); + let script = table.find_script(script); + let out = match script { + Some(script) => { + script.language_tags_len() + + script + .swap_table() + .map(|s| s.language_tags_len()) + .unwrap_or(0) + } + None => 0, + }; + out as libc::c_uint +} + +#[no_mangle] +pub unsafe extern "C" fn countFeatures( + font: XeTeXFont, + script: hb::Tag, + language: hb::Tag, +) -> libc::c_uint { + let layout = (*font).hb_font().face().ot_layout(); + + let mut rval = 0; + for table_tag in [hb::GTag::GSub, hb::GTag::GPos] { + let table = layout.table(table_tag); + if let Some(script) = table.find_script(script) { + let lang = script.select_lang(&[language]); + if lang.is_ok() || language == hb::Tag::new(0) { + rval += lang.unwrap_or_else(|l| l).feature_tags_len(); + } + } + } + rval as libc::c_uint +} + +#[cfg(target_os = "macos")] +#[no_mangle] +pub unsafe extern "C" fn getFileNameFromCTFont( + ct_font: CTFontRef, + index: *mut u32, +) -> *const libc::c_char { + use std::ptr::NonNull; + crate::font::get_file_name_from_ct_font( + &CTFont::new_borrowed(NonNull::new(ct_font.cast_mut()).unwrap()), + &mut *index, + ) + .map(CString::into_raw) + .unwrap_or(ptr::null_mut()) +} diff --git a/crates/xetex_layout/src/c_api/manager.rs b/crates/xetex_layout/src/c_api/manager.rs new file mode 100644 index 000000000..b66a408a9 --- /dev/null +++ b/crates/xetex_layout/src/c_api/manager.rs @@ -0,0 +1,87 @@ +use crate::c_api::{Fixed, RawPlatformFontRef, XeTeXFont}; +use crate::manager::{Engine, FontManager}; +use crate::utils::raw_to_rs; +use std::convert::TryFrom; +use std::ffi::CStr; +use std::{ptr, slice}; + +#[no_mangle] +pub extern "C" fn get_loaded_font_design_size() -> Fixed { + FontManager::with_font_manager(|mgr| mgr.font_design_size()) +} + +#[no_mangle] +pub extern "C" fn set_loaded_font_design_size(val: Fixed) { + FontManager::with_font_manager(|mgr| { + mgr.set_font_design_size(val); + }); +} + +#[no_mangle] +pub unsafe extern "C" fn destroy_font_manager() { + FontManager::destroy(); +} + +#[no_mangle] +pub unsafe extern "C" fn findFontByName( + name: *const libc::c_char, + var: *mut libc::c_char, + size: f64, +) -> RawPlatformFontRef { + let name = CStr::from_ptr(name); + let var = if var.is_null() { + None + } else { + let len = CStr::from_ptr(var).to_bytes().len(); + Some(slice::from_raw_parts_mut(var.cast(), len)) + }; + + #[cfg(target_os = "macos")] + return FontManager::with_font_manager(|mgr| { + use tectonic_mac_core::CoreType; + mgr.find_font(name, var, size) + .map(tectonic_mac_core::CTFontDescriptor::into_type_ref) + .unwrap_or(ptr::null_mut()) + }); + #[cfg(not(target_os = "macos"))] + FontManager::with_font_manager(|mgr| { + mgr.find_font(name, var, size) + .map(|pat| pat.as_ref().as_ptr()) + .unwrap_or(ptr::null_mut()) + }) +} + +#[no_mangle] +pub extern "C" fn getReqEngine() -> libc::c_char { + FontManager::with_font_manager(|mgr| mgr.get_req_engine() as libc::c_char) +} + +#[no_mangle] +pub extern "C" fn setReqEngine(engine: libc::c_char) { + FontManager::with_font_manager(|mgr| { + mgr.set_req_engine(Engine::try_from(engine as u8).unwrap()) + }) +} + +#[no_mangle] +pub unsafe extern "C" fn getFullName(font: RawPlatformFontRef) -> *const libc::c_char { + match raw_to_rs(font) { + Some(font) => FontManager::with_font_manager(|mgr| mgr.get_full_name(font).as_ptr()), + None => ptr::null_mut(), + } +} + +#[no_mangle] +pub unsafe extern "C" fn getDesignSize(font: XeTeXFont) -> f64 { + FontManager::with_font_manager(|mgr| mgr.get_design_size(&*font)) +} + +#[no_mangle] +pub unsafe extern "C" fn ttxl_platfont_get_desc(font: RawPlatformFontRef) -> *const libc::c_char { + match raw_to_rs(font) { + Some(font) => { + FontManager::with_font_manager(|mgr| mgr.get_platform_font_desc(&font).as_ptr()) + } + None => ptr::null_mut(), + } +} diff --git a/crates/xetex_layout/src/engine.rs b/crates/xetex_layout/src/engine.rs new file mode 100644 index 000000000..d1ca8bd71 --- /dev/null +++ b/crates/xetex_layout/src/engine.rs @@ -0,0 +1,434 @@ +//! Base engine for laying out fonts. + +use crate::font::Font; +use crate::manager::{Engine, FontManager}; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; +use std::ops::{Deref, DerefMut}; +use std::ptr; +use tectonic_bridge_graphite2 as gr; +use tectonic_bridge_harfbuzz as hb; + +/// Item that may be borrowed or owned. Similar to `Cow`, but with mutable references. +pub enum MaybeBorrow<'a, T> { + /// Owned item + Owned(Box), + /// Borrowed item + Borrowed(&'a mut T), +} + +impl Deref for MaybeBorrow<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match self { + MaybeBorrow::Owned(val) => val, + MaybeBorrow::Borrowed(val) => val, + } + } +} + +impl DerefMut for MaybeBorrow<'_, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match self { + MaybeBorrow::Owned(val) => val, + MaybeBorrow::Borrowed(val) => val, + } + } +} + +pub(crate) struct GrBreak { + pub(crate) segment: gr::Segment, + pub(crate) slot: gr::Slot, + pub(crate) text_len: libc::c_uint, +} + +/// Base layout engine. Combines all the information needed to shape a font for TeX. +#[repr(C)] +pub struct LayoutEngine { + font: MaybeBorrow<'static, Font>, + script: hb::Tag, + pub(crate) language: hb::Language, + pub(crate) features: Box<[hb::Feature]>, + /// the requested shapers + shaper_list: Vec<*const libc::c_char>, + /// the actually used shaper + shaper: Option, + rgb_value: u32, + extend: f32, + slant: f32, + embolden: f32, + hb_buffer: hb::Buffer, + pub(crate) gr_breaking: Option, +} + +impl LayoutEngine { + /// Create a new layout engine from the needed information + #[allow(clippy::too_many_arguments)] + pub fn new( + font: MaybeBorrow<'static, Font>, + script: hb::Tag, + language: Option>, + features: Box<[hb::Feature]>, + shaper_list: Vec<*const libc::c_char>, + rgb_value: u32, + extend: f32, + slant: f32, + embolden: f32, + ) -> LayoutEngine { + let req_engine = FontManager::with_font_manager(|mgr| mgr.get_req_engine()); + LayoutEngine { + font, + script, + // For Graphite fonts treat the language as BCP 47 tag, for OpenType we + // treat it as a OT language tag for backward compatibility with pre-0.9999 + // XeTeX. + language: if req_engine == Engine::Graphite { + language + .map(|lang| hb::Language::from_cstr(&lang)) + .unwrap_or_default() + } else { + language + .map(|lang| hb::Tag::from_cstr(&lang).to_language()) + .unwrap_or_default() + }, + features, + shaper_list, + shaper: None, + rgb_value, + extend, + slant, + embolden, + hb_buffer: hb::Buffer::new(), + gr_breaking: None, + } + } + + /// Get the font script used + pub fn script(&self) -> hb::Tag { + self.script + } + + /// Get the extend + pub fn extend(&self) -> f32 { + self.extend + } + + /// Get the slant + pub fn slant(&self) -> f32 { + self.slant + } + + /// Get the embolden + pub fn embolden(&self) -> f32 { + self.embolden + } + + /// Get the RGB + pub fn rgb(&self) -> u32 { + self.rgb_value + } + + /// Get the font + pub fn font(&self) -> &Font { + &self.font + } + + /// Get a mutable reference to the font + pub fn font_mut(&mut self) -> &mut Font { + &mut self.font + } + + /// Get the default direction + pub fn default_dir(&self) -> u8 { + pub const UBIDI_DEFAULT_LTR: u8 = 0xFE; + pub const UBIDI_DEFAULT_RTL: u8 = 0xFF; + + let script = self.hb_buffer.as_ref().get_script(); + if script.get_horizontal_direction() == hb::Direction::Rtl { + UBIDI_DEFAULT_RTL + } else { + UBIDI_DEFAULT_LTR + } + } + + /// Check whether we're using the graphite shaper + pub fn used_graphite(&self) -> bool { + self.shaper + .as_ref() + .is_some_and(|s| s.to_bytes() == b"graphite2") + } + + /// Check whether we're using the OpenType shaper + pub fn used_ot(&self) -> bool { + self.shaper.as_ref().is_some_and(|s| s.to_bytes() == b"ot") + } + + /// Get the harfbuzz buffer + pub fn hb_buffer(&self) -> hb::BufferRef<'_> { + self.hb_buffer.as_ref() + } + + /// Get the number of characters in the given slice + pub fn layout_chars(&mut self, chars: &[u16], rtl: bool) -> usize { + let hb_font = self.font.hb_font(); + let hb_face = hb_font.face(); + + let direction = if self.font.layout_dir_vertical() { + hb::Direction::Ttb + } else if rtl { + hb::Direction::Rtl + } else { + hb::Direction::Ltr + }; + + let script = self.script.to_script(); + self.hb_buffer.as_mut().reset(); + // TODO: figure out cfg for harfbuzz versions below 2.5 + // if hb_version_atleast(2, 5, 0) == 0 { + // #[derive(Copy, Clone)] + // struct SendSync(*mut hb_unicode_funcs_t); + // + // unsafe impl Send for SendSync {} + // unsafe impl Sync for SendSync {} + // + // unsafe extern "C" fn _decompose_compat( + // _: *mut hb_unicode_funcs_t, + // _: hb_codepoint_t, + // _: *mut hb_codepoint_t, + // _: *mut libc::c_void, + // ) -> libc::c_uint { + // 0 + // } + // + // unsafe extern "C" fn _get_unicode_funcs() -> *mut hb_unicode_funcs_t { + // static UFUNCS: OnceLock = OnceLock::new(); + // let ufuncs = *UFUNCS + // .get_or_init(|| SendSync(hb_unicode_funcs_create(hb_icu_get_unicode_funcs()))); + // + // hb_unicode_funcs_set_decompose_compatibility_func( + // ufuncs.0, + // _decompose_compat, + // ptr::null_mut(), + // None, + // ); + // + // ufuncs.0 + // } + // + // static HB_UNICODE_FUNCS: OnceLock = OnceLock::new(); + // let funcs = *HB_UNICODE_FUNCS.get_or_init(|| SendSync(_get_unicode_funcs())); + // hb_buffer_set_unicode_funcs(engine.hb_buffer, funcs.0); + // } + + self.hb_buffer.as_mut().add_utf16(chars); + self.hb_buffer.as_mut().set_direction(direction); + self.hb_buffer.as_mut().set_script(script); + self.hb_buffer.as_mut().set_language(self.language); + + self.hb_buffer.as_mut().guess_segment_properties(); + let segment_props = self.hb_buffer.as_mut().get_segment_properties(); + + if self.shaper_list.is_empty() { + // HarfBuzz gives graphite2 shaper a priority, so that for hybrid + // Graphite/OpenType fonts, Graphite will be used. However, pre-0.9999 + // XeTeX preferred OpenType over Graphite, so we are doing the same + // here for sake of backward compatibility. Since "ot" shaper never + // fails, we set the shaper list to just include it. + self.shaper_list = vec![c"ot".as_ptr(), ptr::null()]; + } + + let mut shape_plan = hb::ShapePlan::new_cached( + hb_face, + &segment_props, + &self.features, + Some(&self.shaper_list), + ); + let res = shape_plan + .as_mut() + .execute(hb_font, self.hb_buffer.as_mut(), &self.features); + + self.shaper = None; + + if res { + self.shaper = shape_plan.as_ref().get_shaper().map(CStr::to_owned); + } else { + // all selected shapers failed, retrying with default + // we don't use _cached here as the cached plan will always fail. + shape_plan = hb::ShapePlan::new(hb_face, &segment_props, &self.features, None); + let res = shape_plan + .as_mut() + .execute(hb_font, self.hb_buffer.as_mut(), &self.features); + + if res { + self.shaper = shape_plan.as_ref().get_shaper().map(CStr::to_owned); + } else { + panic!("all shapers failed"); + } + } + + self.hb_buffer.as_ref().len() + } +} + +pub(crate) fn get_graphite_feature_code(engine: &LayoutEngine, index: u32) -> Option { + let id = engine + .font() + .hb_font() + .face() + .gr_face()? + .feature_ref(index as usize)? + .id(); + Some(id) +} + +pub(crate) fn count_graphite_feature_settings( + engine: &LayoutEngine, + feature_id: u32, +) -> Option { + let out = engine + .font() + .hb_font() + .face() + .gr_face()? + .find_feature_ref(feature_id)? + .num_values() as u32; + Some(out) +} + +pub(crate) fn get_graphite_feature_setting_code( + engine: &LayoutEngine, + feature_id: u32, + index: u32, +) -> Option { + let out = engine + .font() + .hb_font() + .face() + .gr_face()? + .find_feature_ref(feature_id)? + .value(index as usize) as u32; + Some(out) +} + +pub(crate) fn get_graphite_feature_default_setting( + engine: &LayoutEngine, + feature_id: u32, +) -> Option { + let face = engine.font().hb_font().face().gr_face()?; + let feat = face.find_feature_ref(feature_id)?; + let lang = engine + .language + .to_string() + .map(hb::Tag::from_cstr) + .map(hb::Tag::to_raw) + .unwrap_or(0); + let feature_values = face.feature_val_for_lang(lang); + let out = feat.feat_value(feature_values.as_ref()) as u32; + Some(out) +} + +pub(crate) fn get_graphite_feature_label( + engine: &LayoutEngine, + feature_id: u32, +) -> Option { + let face = engine.font().hb_font().face().gr_face()?; + let feature = face.find_feature_ref(feature_id)?; + let lang_id = 0x409; + let label = feature.label(lang_id)?; + Some(label) +} + +pub(crate) fn get_graphite_feature_setting_label( + engine: &LayoutEngine, + feature_id: u32, + setting_id: u32, +) -> Option { + let face = engine.font().hb_font().face().gr_face()?; + + let feature = face.find_feature_ref(feature_id)?; + for i in 0..feature.num_values() { + if setting_id == feature.value(i) as u32 { + let lang_id = 0x409; + return feature.value_label(i, lang_id); + } + } + + None +} + +pub(crate) fn find_graphite_feature( + engine: &LayoutEngine, + str: &[u8], + tag: &mut hb::Tag, + v: &mut libc::c_int, +) -> bool { + *tag = hb::Tag::new(0); + *v = 0; + + let mut idx = 0; + while str[idx] == b' ' || str[idx] == b'\t' { + idx += 1; + } + while str.get(idx).is_some_and(|c| *c != b'=') { + idx += 1; + } + + match find_graphite_feature_named(engine, &str[..idx]) { + Some(val) => *tag = hb::Tag::new(val), + None => return false, + } + + idx += 1; + while idx < str.len() && (str[idx] == b' ' || str[idx] == b'\t') { + idx += 1; + } + + if idx >= str.len() { + return false; + } + + *v = find_graphite_feature_setting_named(engine, tag.to_raw(), &str[idx..]) + .map(|i| i as libc::c_int) + .unwrap_or(-1); + + *v != -1 +} + +pub(crate) fn find_graphite_feature_named(engine: &LayoutEngine, name: &[u8]) -> Option { + let gr_face = engine.font().hb_font().face().gr_face()?; + + let tag = hb::Tag::from_str(std::str::from_utf8(name).unwrap()).to_raw(); + + for i in 0..gr_face.num_feature_refs() { + let feature = gr_face.feature_ref(i)?; + let lang_id = 0x409; + let label = feature.label(lang_id)?; + + if &label.as_bytes()[..name.len()] == name || feature.id() == tag { + return Some(feature.id()); + } + } + + None +} + +pub(crate) fn find_graphite_feature_setting_named( + engine: &LayoutEngine, + id: u32, + name: &[u8], +) -> Option { + let face = engine.font().hb_font().face().gr_face()?; + + let tag = hb::Tag::from_str(std::str::from_utf8(name).unwrap()).to_raw(); + + let feature = face.find_feature_ref(id)?; + for i in 0..feature.num_values() { + let lang_id = 0x409; + let label = feature.value_label(i, lang_id)?; + if &label.as_bytes()[..name.len()] == name || feature.id() == tag { + return Some(feature.value(i)); + } + } + None +} diff --git a/crates/xetex_layout/src/font.rs b/crates/xetex_layout/src/font.rs new file mode 100644 index 000000000..caf234ad9 --- /dev/null +++ b/crates/xetex_layout/src/font.rs @@ -0,0 +1,600 @@ +//! Font handling - specific fonts used by an engine while shaping text. + +use crate::c_api::{Fixed, GlyphBBox, GlyphID, PlatformFontRef}; +use crate::utils::fix_to_d; +use std::ffi::{CStr, CString}; +use std::path::Path; +use std::str::FromStr; +use std::sync::{Arc, Mutex, OnceLock}; +use tectonic_bridge_core::{CoreBridgeState, FileFormat}; +#[cfg(not(target_os = "macos"))] +use tectonic_bridge_fontconfig as fc; +use tectonic_bridge_freetype2 as ft; +use tectonic_bridge_harfbuzz as hb; +#[cfg(target_os = "macos")] +use tectonic_mac_core::{ + CFArray, CFDictionary, CFType, CFUrl, CTFont, CTFontDescriptor, FontAttribute, FontNameKey, +}; + +fn get_glyph_advance(face: &ft::Face, gid: libc::c_uint, vertical: bool) -> ft::Fixed { + let flags = if vertical { + ft::LoadFlags::NO_SCALE | ft::LoadFlags::VERTICAL_LAYOUT + } else { + ft::LoadFlags::NO_SCALE + }; + let out = match face.get_advance(gid, flags) { + Ok(advance) => { + if vertical { + -advance + } else { + advance + } + } + Err(_) => 0, + }; + out as ft::Fixed +} + +fn get_font_funcs() -> hb::FontFuncsRef<'static, Arc>> { + static FONTS: OnceLock>>> = OnceLock::new(); + + FONTS + .get_or_init(|| { + let mut funcs = hb::FontFuncs::>>::new(); + + let mut f = funcs.as_mut(); + f.nominal_glyph_func(|_, face, ch| { + face.lock().unwrap().get_char_index(ch).map(|cc| cc.get()) + }); + f.variation_glyph_func(|_, face, ch, vs| { + face.lock() + .unwrap() + .get_char_variant_index(ch, vs) + .map(|cc| cc.get()) + }); + f.glyph_h_advance(|_, face, gid| { + get_glyph_advance(&face.lock().unwrap(), gid, false) as hb::Position + }); + f.glyph_v_advance(|_, face, gid| { + get_glyph_advance(&face.lock().unwrap(), gid, true) as hb::Position + }); + f.glyph_h_origin(|_, _, _| Some((0, 0))); + f.glyph_v_origin(|_, _, _| { + Some((0, 0)) + + // TODO + // Keep the code below for reference, for now we want to keep vertical + // origin at (0, 0) for compatibility with pre-0.9999. + // Reconsider this (e.g. using BASE table) when we get around overhauling + // the text directionality model and implementing real vertical typesetting. + + /* + FT_Face face = (FT_Face) font_data; + FT_Error error; + + error = FT_Load_Glyph (face, gid, FT_LOAD_NO_SCALE); + if (!error) { + *x = face->glyph->metrics.horiBearingX - face->glyph->metrics.vertBearingX; + *y = face->glyph->metrics.horiBearingY - (-face->glyph->metrics.vertBearingY); + } + + return !error; + */ + }); + f.glyph_h_kerning(|_, face, gid1, gid2| { + match face + .lock() + .unwrap() + .get_kerning(gid1, gid2, ft::KerningMode::Unscaled) + { + Ok(vec) => vec.x as hb::Position, + Err(_) => 0, + } + }); + f.glyph_v_kerning(|_, _, _, _| 0); + f.glyph_extents(|_, face, gid| { + let mut face = face.lock().unwrap(); + if let Ok(glyph) = face.load_glyph(gid, ft::LoadFlags::NO_SCALE) { + Some(hb::GlyphExtents { + x_bearing: glyph.metrics().horiBearingX as hb::Position, + y_bearing: glyph.metrics().horiBearingY as hb::Position, + width: glyph.metrics().width as hb::Position, + height: -glyph.metrics().height as hb::Position, + }) + } else { + None + } + }); + f.glyph_contour_point(|_, face, gid, point_index| { + let mut face = face.lock().unwrap(); + + if let Ok(glyph) = face.load_glyph(gid, ft::LoadFlags::NO_SCALE) { + if let Some(outline) = glyph.outline() { + if point_index < (outline.n_points as u32) { + let x = outline.points()[point_index as usize].x as hb::Position; + let y = outline.points()[point_index as usize].y as hb::Position; + return Some((x, y)); + } + } + } + None + }); + f.glyph_name( + |_, face, gid, buf| match face.lock().unwrap().get_glyph_name(gid, buf) { + Ok(str) if !str.to_bytes().is_empty() && str.to_bytes()[0] == 0 => 0, + Err(_) => 0, + Ok(str) => str.to_bytes().len(), + }, + ); + + funcs.make_immutable() + }) + .as_ref() +} + +pub(crate) fn get_larger_script_list_table_ot(font: &Font) -> hb::ot::Table<'_> { + let layout = font.hb_font().face().ot_layout(); + + let sl_sub = layout.table(hb::GTag::GSub); + let sl_pos = layout.table(hb::GTag::GPos); + + if sl_sub.script_tags_len() > sl_pos.script_tags_len() { + sl_sub + } else { + sl_pos + } +} + +enum FontKind { + FtFont, + #[cfg(target_os = "macos")] + Mac(CTFontDescriptor, Option), +} + +/// A font, with all the information needed by TeX to shape it for typesetting. +pub struct Font { + units_per_em: libc::c_ushort, + point_size: f32, + ascent: f32, + descent: f32, + cap_height: f32, + x_height: f32, + italic_angle: f32, + + vertical: bool, + + filename: CString, + index: u32, + + ft_face: Option>>, + hb_font: Option, + + // Currently only used on MacOS + #[allow(dead_code)] + kind: FontKind, +} + +impl Font { + #[cfg(not(target_os = "macos"))] + pub(crate) fn new(font: PlatformFontRef, point_size: f32) -> Result { + let path = font + .as_ref() + .get::(0) + .and_then(|s| s.to_str().map_err(|_| fc::FcErr::NoMatch)) + .ok(); + let index = font.as_ref().get::(0).unwrap_or(0); + + Font::new_path_index(path, index as usize, point_size) + } + + #[cfg(target_os = "macos")] + pub(crate) fn new(descriptor: PlatformFontRef, point_size: f32) -> Result { + let mut out = Font { + units_per_em: 0, + point_size, + ascent: 0.0, + descent: 0.0, + cap_height: 0.0, + x_height: 0.0, + italic_angle: 0.0, + vertical: false, + filename: CString::new("").unwrap(), + index: 0, + ft_face: None, + hb_font: None, + kind: FontKind::Mac(descriptor, None), + }; + out.initialize_mac()?; + Ok(out) + } + + pub(crate) fn new_path_index( + path: Option<&str>, + index: usize, + point_size: f32, + ) -> Result { + let mut out = Font { + units_per_em: 0, + point_size, + ascent: 0.0, + descent: 0.0, + cap_height: 0.0, + x_height: 0.0, + italic_angle: 0.0, + vertical: false, + filename: CString::new("").unwrap(), + index: 0, + ft_face: None, + hb_font: None, + kind: FontKind::FtFont, + }; + if let Some(path) = path { + out.initialize_ft(path, index)?; + } + Ok(out) + } + + fn initialize_ft(&mut self, pathname: &str, index: usize) -> Result<(), ()> { + CoreBridgeState::with_global_state(|engine| { + let handle = engine + .input_open(pathname, FileFormat::OpenType, false) + .or_else(|| engine.input_open(pathname, FileFormat::TrueType, false)) + .or_else(|| engine.input_open(pathname, FileFormat::Type1, false)); + let Some(handle) = handle else { + return Err(()); + }; + + let sz = engine.input_get_size(handle); + let mut backing_data = vec![0; sz]; + engine + .input_read(handle, &mut backing_data) + .expect("failed to read font file"); + + engine.input_close(handle); + + self.ft_face = match ft::Face::new_memory(backing_data, index) { + Ok(face) => Some(Arc::new(Mutex::new(face))), + Err(_) => return Err(()), + }; + + if !self.ft_face().is_scalable() { + return Err(()); + } + + if index == 0 && !self.ft_face().is_sfnt() { + let afm = Path::new(pathname) + .file_name() + .map(Path::new) + .unwrap_or(Path::new(pathname)); + let afm = afm.with_extension("afm"); + + let afm_handle = engine.input_open(afm.to_str().unwrap(), FileFormat::Afm, false); + + if let Some(afm_handle) = afm_handle { + let sz = engine.input_get_size(afm_handle); + let mut backing_data2 = vec![0; sz]; + engine + .input_read(handle, &mut backing_data2) + .expect("failed to read AFM file"); + + self.ft_face().attach_stream_mem(backing_data2).unwrap(); + engine.input_close(afm_handle); + } + } + Ok(()) + })?; + + self.filename = CString::from_str(pathname).unwrap(); + self.index = index as u32; + let upe = { self.ft_face().units_per_em() }; + self.units_per_em = upe; + let a = { self.ft_face().ascender() } as f64; + self.ascent = self.units_to_points(a) as f32; + let d = { self.ft_face().descender() } as f64; + self.descent = self.units_to_points(d) as f32; + + let ft_face = self.ft_face(); + let post_table = ft_face.get_sfnt_table::(); + let ia = if let Some(table) = post_table { + fix_to_d(table.italic_angle as Fixed) as f32 + } else { + self.italic_angle + }; + drop(ft_face); + self.italic_angle = ia; + + let ft_face = self.ft_face(); + let os2_table = ft_face.get_sfnt_table::(); + let (ch, xh) = if let Some(table) = os2_table { + let ch = self.units_to_points(table.sCapHeight as f64) as f32; + let xh = self.units_to_points(table.sxHeight as f64) as f32; + (ch, xh) + } else { + (self.cap_height, self.x_height) + }; + drop(ft_face); + self.cap_height = ch; + self.x_height = xh; + + let ft_face = Arc::clone(self.ft_face.as_ref().unwrap()); + let mut hb_face = hb::Face::new_tables(move |_, tag| { + if let Ok(table) = ft_face + .lock() + .unwrap() + .load_sfnt_table(ft::TableTag::Other(tag.to_raw())) + { + Some(hb::Blob::new(table)) + } else { + None + } + }); + + hb_face.as_mut().set_index(self.index); + hb_face.as_mut().set_upem(self.units_per_em as u32); + + let mut hb_font = hb::Font::new(hb_face.as_ref()); + + hb_font + .as_mut() + .set_funcs(get_font_funcs(), Arc::clone(self.ft_face.as_ref().unwrap())); + hb_font + .as_mut() + .set_scale(self.units_per_em as i32, self.units_per_em as i32); + hb_font.as_mut().set_ppem(0, 0); + + self.hb_font = Some(hb_font); + + Ok(()) + } + + #[cfg(target_os = "macos")] + fn initialize_mac(&mut self) -> Result<(), ()> { + let FontKind::Mac(descriptor, font_ref) = &mut self.kind else { + return Err(()); + }; + + let empty_cascade_list = CFArray::::empty(); + let attributes = + CFDictionary::new([(FontAttribute::CascadeList.to_str(), empty_cascade_list)]); + + *descriptor = descriptor.copy_with_attrs(&attributes); + *font_ref = Some(CTFont::new_descriptor( + descriptor, + self.point_size as f64 * 72.0 / 72.27, + )); + let mut index = 0; + let pathname = get_file_name_from_ct_font(font_ref.as_ref().unwrap(), &mut index).unwrap(); + self.initialize_ft(pathname.to_str().unwrap(), index as usize) + } + + pub(crate) fn ft_face(&self) -> std::sync::MutexGuard<'_, ft::Face> { + self.ft_face.as_ref().unwrap().lock().unwrap() + } + + pub(crate) fn get_glyph_name(&self, gid: u16) -> Option { + if self.ft_face().has_glyph_names() { + let mut buf = vec![0u8; 256]; + self.ft_face().get_glyph_name(gid as u32, &mut buf).unwrap(); + + CStr::from_bytes_until_nul(&buf).map(CStr::to_owned).ok() + } else { + None + } + } + + pub(crate) fn get_glyph_sidebearings( + &mut self, + gid: GlyphID, + lsb: Option<&mut f32>, + rsb: Option<&mut f32>, + ) { + let width = self.get_glyph_width(gid as u32); + + let bbox = self.get_glyph_bounds(gid); + + if let Some(lsb) = lsb { + *lsb = bbox.x_min; + } + if let Some(rsb) = rsb { + *rsb = width - bbox.x_max; + } + } + + pub(crate) fn get_glyph_ital_corr(&mut self, gid: GlyphID) -> f32 { + let width = self.get_glyph_width(gid as u32); + let bbox = self.get_glyph_bounds(gid); + + if bbox.x_max > width { + bbox.x_max - width + } else { + 0.0 + } + } + + pub(crate) fn map_char_to_glyph(&self, ch: u32) -> GlyphID { + match self.ft_face().get_char_index(ch) { + Some(val) => val.get() as GlyphID, + None => 0, + } + } + + pub(crate) fn first_char_code(&self) -> u32 { + self.ft_face().get_first_char().0 + } + + pub(crate) fn last_char_code(&self) -> u32 { + let ft_face = self.ft_face(); + + let (mut ch, mut index) = ft_face.get_first_char(); + let mut prev = ch; + while index != 0 { + prev = ch; + (ch, index) = ft_face.get_next_char(ch); + } + prev + } + + pub(crate) fn map_glyph_to_index(&self, glyph_name: &CStr) -> GlyphID { + match self.ft_face().get_name_index(glyph_name) { + Some(index) => index.get() as u16, + None => 0, + } + } + + pub(crate) fn load_font_table(&self, tag: ft::TableTag) -> Option> { + self.ft_face().load_sfnt_table(tag).ok() + } + + pub(crate) fn get_glyph_bounds(&mut self, gid: GlyphID) -> GlyphBBox { + let mut ft_face = self.ft_face(); + + let glyph = ft_face + .load_glyph(gid as u32, ft::LoadFlags::NO_SCALE) + .and_then(|slot| slot.get_glyph()); + + match glyph { + Ok(glyph) => { + let ft::BBox { + x_min, + y_min, + x_max, + y_max, + } = glyph.get_cbox(ft::BBoxMode::Unscaled); + drop(ft_face); + GlyphBBox { + x_min: self.units_to_points(x_min as f64) as f32, + y_min: self.units_to_points(y_min as f64) as f32, + x_max: self.units_to_points(x_max as f64) as f32, + y_max: self.units_to_points(y_max as f64) as f32, + } + } + Err(_) => GlyphBBox::default(), + } + } + + pub(crate) fn get_glyph_height_depth( + &mut self, + gid: GlyphID, + height: Option<&mut f32>, + depth: Option<&mut f32>, + ) { + let bbox = self.get_glyph_bounds(gid); + if let Some(height) = height { + *height = bbox.y_max; + } + if let Some(depth) = depth { + *depth = -bbox.y_min; + } + } + + pub(crate) fn filename(&self, index: &mut u32) -> &CStr { + *index = self.index; + &self.filename + } + + // pub(crate) fn get_font_table(&self) -> Option<&T::Table> { + // self.ft_face().get_sfnt_table::() + // } + + pub(crate) fn italic_angle(&self) -> f32 { + self.italic_angle + } + + pub(crate) fn num_glyphs(&self) -> usize { + self.ft_face().num_glyphs() + } + + pub(crate) fn get_glyph_width(&self, gid: u32) -> f32 { + self.units_to_points(get_glyph_advance(&self.ft_face(), gid, false) as f64) as f32 + } + + pub(crate) fn layout_dir_vertical(&self) -> bool { + self.vertical + } + + pub(crate) fn set_layout_dir_vertical(&mut self, vertical: bool) { + self.vertical = vertical; + } + + pub(crate) fn point_size(&self) -> f32 { + self.point_size + } + + pub(crate) fn ascent(&self) -> f32 { + self.ascent + } + + pub(crate) fn descent(&self) -> f32 { + self.descent + } + + pub(crate) fn cap_height(&self) -> f32 { + self.cap_height + } + + pub(crate) fn x_height(&self) -> f32 { + self.x_height + } + + pub(crate) fn hb_font(&self) -> hb::FontRef<'_> { + self.hb_font.as_ref().unwrap().as_ref() + } + + pub(crate) fn try_hb_font(&self) -> Option> { + self.hb_font.as_ref().map(|f| f.as_ref()) + } + + /* Tectonic: these are modified from the base XeTeX code to use doubles; + * otherwise roundoff errors can accumulate leading to differences in the + * XDV outputs. */ + pub(crate) fn units_to_points(&self, units: f64) -> f64 { + (units * self.point_size as f64) / (self.units_per_em as f64) + } + + pub(crate) fn points_to_units(&self, points: f64) -> f64 { + (points * (self.units_per_em as f64)) / self.point_size as f64 + } +} + +#[cfg(target_os = "macos")] +pub(crate) fn get_file_name_from_ct_font(ct_font: &CTFont, index: &mut u32) -> Option { + let url = ct_font + .attr(FontAttribute::URL) + // SAFETY: CFUrl has no generic parameters + .and_then(|t| unsafe { t.downcast::() }.ok())?; + + let pathname = url.fs_representation()?; + *index = 0; + + let face = ft::Face::new(&pathname, 0); + if let Ok(face) = face { + if face.num_faces() > 1 { + let num_faces = face.num_faces(); + let ps_name1 = ct_font.name(FontNameKey::PostScript); + *index = u32::MAX; + for i in 0..num_faces { + let face = ft::Face::new(&pathname, i); + if let Ok(face) = face { + let ps_name2 = face.get_postscript_name(); + match (&ps_name1, ps_name2) { + (None, None) => { + *index = i as u32; + break; + } + (Some(name1), Some(name2)) if &*name1.as_cstr() == name2 => { + *index = i as u32; + break; + } + _ => (), + } + } + } + } + } + + if *index != u32::MAX { + Some(pathname) + } else { + None + } +} diff --git a/crates/xetex_layout/src/lib.rs b/crates/xetex_layout/src/lib.rs index 2257e25d4..185e67eed 100644 --- a/crates/xetex_layout/src/lib.rs +++ b/crates/xetex_layout/src/lib.rs @@ -1,35 +1,16 @@ // Copyright 2020-2021 the Tectonic Project // Licensed under the MIT License. -//! This crate contains no Rust code. It exists to export a *C* API to C++ font -//! loading and layout code in the Cargo build framework used by [Tectonic]. -//! Ideally, it will migrate to become a cbindgen C API to a Rust -//! implementation. +//! This crate provides font loading and layout code, as well as C bindings to it. //! //! [Tectonic]: https://tectonic-typesetting.github.io/ -/// Import things from our bridge crates to ensure that we actually link with -/// them. -mod linkage { - #[allow(unused_imports)] - use tectonic_bridge_core as clipyrenamehack1; +pub mod engine; +pub mod font; +pub mod manager; +mod utils; - #[allow(unused_imports)] - use tectonic_bridge_freetype2 as clipyrenamehack2; - - #[allow(unused_imports)] - use tectonic_bridge_graphite2 as clipyrenamehack3; - - #[allow(unused_imports)] - use tectonic_bridge_harfbuzz as clipyrenamehack4; - - #[allow(unused_imports)] - use tectonic_bridge_icu as clipyrenamehack5; - - #[cfg(not(target_os = "macos"))] - #[allow(unused_imports)] - use tectonic_bridge_fontconfig as clipyrenamehack6; -} +mod c_api; /// Does our resulting executable link correctly? #[test] diff --git a/crates/xetex_layout/src/manager.rs b/crates/xetex_layout/src/manager.rs new file mode 100644 index 000000000..5f89dfeca --- /dev/null +++ b/crates/xetex_layout/src/manager.rs @@ -0,0 +1,835 @@ +//! Font management - backend API for resolving fonts and caching them for future use. + +use crate::c_api::{Fixed, PlatformFontRef}; +use crate::font::Font; +use crate::utils::{d_to_fix, fix_to_d}; +use std::borrow::Cow; +use std::cell::RefCell; +use std::collections::HashMap; +use std::convert::TryFrom; +use std::ffi::{CStr, CString}; +use tectonic_bridge_freetype2 as ft; + +#[cfg(not(target_os = "macos"))] +mod fc; +#[cfg(target_os = "macos")] +mod mac; + +thread_local! { + static FONT_MGR: RefCell> = const { RefCell::new(None) }; +} + +#[derive(Default)] +pub(crate) struct OpSizeRec { + design_size: f64, + min_size: f64, + max_size: f64, + sub_family_id: libc::c_uint, + name_code: libc::c_uint, +} + +#[allow(dead_code)] +struct FontInfo { + full_name: Option, + ps_name: CString, + family_name: CString, + style_name: CString, + parent: usize, + font_ref: PlatformFontRef, + op_size_info: OpSizeRec, + weight: u16, + width: u16, + slant: i16, + is_reg: bool, + is_bold: bool, + is_italic: bool, +} + +impl FontInfo { + fn new( + font_ref: PlatformFontRef, + ps_name: CString, + family_name: CString, + style_name: CString, + ) -> FontInfo { + let mut out = FontInfo { + full_name: None, + ps_name, + family_name, + style_name, + parent: usize::MAX, + font_ref, + op_size_info: OpSizeRec::default(), + weight: 0, + width: 0, + slant: 0, + is_reg: false, + is_bold: false, + is_italic: false, + }; + out.op_size_info.sub_family_id = 0; + out.op_size_info.design_size = 10.0; /* default to 10.0pt */ + out + } +} + +#[derive(Default)] +struct FamilyInfo { + styles: HashMap, + min_weight: u16, + max_weight: u16, + min_width: u16, + max_width: u16, + min_slant: i16, + max_slant: i16, +} + +impl FamilyInfo { + fn new() -> FamilyInfo { + FamilyInfo::default() + } +} + +#[derive(Default)] +struct NameCollection { + family_names: Vec, + style_names: Vec, + full_names: Vec, + ps_name: Option, +} + +trait FontManagerBackend { + fn get_platform_font_desc<'a>(&'a self, font: &'a PlatformFontRef) -> Cow<'a, CStr>; + fn get_op_size_rec_and_style_flags(&self, font: &mut FontInfo); + fn search_for_host_platform_fonts(&mut self, maps: &mut FontMaps, name: &CStr); + fn read_names(&self, font: PlatformFontRef) -> NameCollection; +} + +fn base_get_op_size_rec_and_style_flags(font: &mut FontInfo) { + let xfont = match Font::new(font.font_ref.clone(), 10.0) { + Ok(xfont) => xfont, + Err(_) => return, + }; + + let size_rec = FontManager::get_op_size(&xfont); + if let Some(size_rec) = size_rec { + font.op_size_info.design_size = size_rec.design_size; + if size_rec.sub_family_id != 0 + || size_rec.name_code != 0 + || size_rec.min_size != 0.0 + || size_rec.max_size != 0.0 + { + font.op_size_info.sub_family_id = size_rec.sub_family_id; + font.op_size_info.name_code = size_rec.name_code; + font.op_size_info.min_size = size_rec.min_size; + font.op_size_info.max_size = size_rec.max_size; + } + } + + let ft_face = xfont.ft_face(); + let os2_table = ft_face.get_sfnt_table::(); + if let Some(table) = os2_table { + font.weight = table.usWeightClass; + font.width = table.usWidthClass; + let sel = table.fsSelection; + font.is_reg = (sel & (1 << 6)) != 0; + font.is_bold = (sel & (1 << 5)) != 0; + font.is_italic = (sel & (1 << 0)) != 0; + } + + let head_table = ft_face.get_sfnt_table::(); + if let Some(table) = head_table { + let ms = table.Mac_Style; + if (ms & (1 << 0)) != 0 { + font.is_bold = true; + } + if (ms & (1 << 1)) != 0 { + font.is_italic = true; + } + } + + let post_table = ft_face.get_sfnt_table::(); + if let Some(table) = post_table { + font.slant = (1000.0 + * (f64::tan(fix_to_d((-table.italic_angle) as Fixed) * std::f64::consts::PI / 180.0))) + as _; + } +} + +#[derive(Default)] +struct FontMaps { + fonts: Vec, + families: Vec, + + name_to_font: HashMap, + name_to_family: HashMap, + platform_ref_to_font: HashMap, + ps_name_to_font: HashMap, +} + +impl FontMaps { + fn add_to_maps( + &mut self, + backend: &dyn FontManagerBackend, + pfont: PlatformFontRef, + names: &NameCollection, + ) { + if self.platform_ref_to_font.contains_key(&pfont) { + // this font has already been cached + return; + } + let ps_name = match &names.ps_name { + Some(name) => name, + // can't use a font that lacks a PostScript name + None => return, + }; + if self.ps_name_to_font.contains_key(ps_name) { + // duplicates an earlier PS name, so skip + return; + } + + let family_name = if !names.family_names.is_empty() { + names.family_names[0].clone() + } else { + ps_name.to_owned() + }; + + let style_name = if !names.style_names.is_empty() { + names.style_names[0].clone() + } else { + CString::default() + }; + + let mut font = FontInfo::new(pfont.clone(), ps_name.to_owned(), family_name, style_name); + backend.get_op_size_rec_and_style_flags(&mut font); + self.fonts.push(font); + let font_pos = self.fonts.len() - 1; + let font = &mut self.fonts[font_pos]; + self.ps_name_to_font.insert(font.ps_name.clone(), font_pos); + self.platform_ref_to_font.insert(pfont, font_pos); + + if !names.full_names.is_empty() { + font.full_name = Some(names.full_names[0].clone()); + } + + for i in &names.family_names { + let fam = self.name_to_family.get(i).copied(); + let (family, fam_pos) = match fam { + None => { + let mut family = FamilyInfo::new(); + family.min_weight = font.weight; + family.max_weight = font.weight; + family.min_width = font.width; + family.max_width = font.width; + family.min_slant = font.slant; + family.max_slant = font.slant; + self.families.push(family); + let fam_pos = self.families.len() - 1; + let family = &mut self.families[fam_pos]; + self.name_to_family.insert(i.clone(), fam_pos); + (family, fam_pos) + } + Some(fam_pos) => { + let family = &mut self.families[fam_pos]; + family.min_weight = u16::min(family.min_weight, font.weight); + family.max_weight = u16::max(family.max_weight, font.weight); + family.min_width = u16::min(family.min_width, font.width); + family.max_width = u16::max(family.max_width, font.width); + family.min_slant = i16::min(family.min_slant, font.slant); + family.max_slant = i16::max(family.max_slant, font.slant); + (family, fam_pos) + } + }; + + if font.parent == usize::MAX { + font.parent = fam_pos; + } + + for style in &names.style_names { + let f = family.styles.get(style); + if f.is_none() { + family.styles.insert(style.clone(), font_pos); + } + /* + else if (iFont->second != thisFont) + fprintf(stderr, "# Font name warning: ambiguous Style \"%s\" in Family \"%s\" (PSNames \"%s\" and \"%s\")\n", + j->c_str(), i->c_str(), iFont->second->m_psName->c_str(), thisFont->m_psName->c_str()); + */ + } + } + + for i in &names.full_names { + let f = self.name_to_font.get(i); + if f.is_none() { + self.name_to_font.insert(i.clone(), font_pos); + } + /* + else if (iFont->second != thisFont) + fprintf(stderr, "# Font name warning: ambiguous FullName \"%s\" (PSNames \"%s\" and \"%s\")\n", + i->c_str(), iFont->second->m_psName->c_str(), thisFont->m_psName->c_str()); + */ + } + } +} + +/// The font engine to use +#[derive(Copy, Clone, Debug, PartialEq)] +#[repr(u8)] +pub enum Engine { + /// Use the platform default + Default = 0, + /// Use the Apple font engine + Apple = b'A', + /// Use OpenType + OpenType = b'O', + /// Use Graphite2 + Graphite = b'G', +} + +impl TryFrom for Engine { + type Error = (); + + fn try_from(value: u8) -> Result { + Ok(match value { + 0 => Engine::Default, + b'A' => Engine::Apple, + b'O' => Engine::OpenType, + b'G' => Engine::Graphite, + _ => return Err(()), + }) + } +} + +/// Font manager - combines a font management backend such as FreeType or CoreText with a cache of +/// loaded fonts and the engine to use. +pub struct FontManager { + backend: Box, + maps: FontMaps, + req_engine: Engine, + loaded_font_design_size: Fixed, +} + +impl FontManager { + fn init_font_manager() { + let backend: Box; + + #[cfg(target_os = "macos")] + { + backend = Box::new(mac::MacBackend::new()); + } + #[cfg(not(target_os = "macos"))] + { + backend = Box::new(fc::FcBackend::new()); + } + + FONT_MGR.with_borrow_mut(|mgr| { + *mgr = Some(FontManager { + backend, + maps: Default::default(), + req_engine: Engine::Default, + loaded_font_design_size: 0, + }) + }); + } + + /// Access the global font manager. Initialize it if necessary. + pub fn with_font_manager(f: impl FnOnce(&mut FontManager) -> T) -> T { + let init = FONT_MGR.with_borrow(|mgr| mgr.is_none()); + if init { + Self::init_font_manager(); + } + FONT_MGR.with_borrow_mut(|mgr| f(mgr.as_mut().unwrap())) + } + + /// Destroy the global font manager + pub fn destroy() { + FONT_MGR.with_borrow_mut(|mgr| { + *mgr = None; + }) + } + + /// Get the font for a given name, variant, and point size + pub fn find_font( + &mut self, + name: &CStr, + variant: Option<&mut [u8]>, + mut pt_size: f64, + ) -> Option { + let mut font = None; + let mut dsize = 10.0; + self.loaded_font_design_size = 655360; + + for pass in 0..2 { + // try full name as given + if let Some(&font_pos) = self.maps.name_to_font.get(name) { + let temp_font = &self.maps.fonts[font_pos]; + font = Some(font_pos); + if temp_font.op_size_info.design_size != 0.0 { + dsize = temp_font.op_size_info.design_size + } + break; + } + + let bytes = name.to_bytes(); + let split = bytes + .iter() + .position(|c| *c == b'-') + .map(|index| (&bytes[..index], &bytes[index + 1..])); + + // if there's a hyphen, split there and try Family-Style + if let Some((family, style)) = split { + let family = CString::new(family).unwrap(); + if let Some(&fam_pos) = self.maps.name_to_family.get(&family) { + let style = CString::new(style).unwrap(); + let temp_fam = &self.maps.families[fam_pos]; + if let Some(&font_pos) = temp_fam.styles.get(&style) { + let temp_font = &self.maps.fonts[font_pos]; + font = Some(font_pos); + if temp_font.op_size_info.design_size != 0.0 { + dsize = temp_font.op_size_info.design_size; + } + break; + } + } + } + + // try as PostScript name + if let Some(&font_pos) = self.maps.ps_name_to_font.get(name) { + let temp_font = &self.maps.fonts[font_pos]; + font = Some(font_pos); + if temp_font.op_size_info.design_size != 0.0 { + dsize = temp_font.op_size_info.design_size; + } + break; + } + + // try for the name as a family name + if let Some(&fam_pos) = self.maps.name_to_family.get(name) { + let family = &self.maps.families[fam_pos]; + // look for a family member with the "regular" bit set in OS/2 + let mut reg_fonts = 0; + for &font_pos in family.styles.values() { + let temp_font = &self.maps.fonts[font_pos]; + if temp_font.is_reg { + if reg_fonts == 0 { + font = Some(font_pos); + } + reg_fonts += 1; + } + } + + // families with Ornament or similar fonts may flag those as Regular, + // which confuses the search above... so try some known names + if font.is_none() || reg_fonts > 1 { + // try for style "Regular", "Plain", "Normal", "Roman" + for name in [c"Regular", c"Plain", c"Normal", c"Roman"] { + if let Some(&font_pos) = family.styles.get(name) { + font = Some(font_pos); + break; + } + } + } + + if font.is_none() { + // look through the family for the (weight, width, slant) nearest to (80, 100, 0) + font = self.best_match_from_family(family, 80, 100, 0); + } + + if font.is_some() { + break; + } + } + + if pass == 0 { + self.search_for_host_platform_fonts(name); + } + } + + let mut font_pos = font?; + + let parent_pos = self.maps.fonts[font_pos].parent; + + // if there are variant requests, try to apply them + // and delete B, I, and S=... codes from the string, just retain /engine option + self.req_engine = Engine::Default; + let mut req_bold = false; + let mut req_ital = false; + if let Some(variant) = variant { + let mut var_str = String::new(); + let mut cp = &*variant; + while !cp.is_empty() { + const VARIANTS: &[(&[u8], Engine, &str)] = &[ + (b"AAT", Engine::Apple, "AAT"), + (b"ICU", Engine::OpenType, "OT"), + (b"OT", Engine::OpenType, "OT"), + (b"GR", Engine::Graphite, "GR"), + ]; + + let any_var = VARIANTS.iter().any(|&(cmp, engine, var)| { + if cp.starts_with(cmp) { + self.req_engine = engine; + cp = &cp[cmp.len()..]; + if var_str.chars().last().is_some_and(|c| c != '/') { + var_str.push_str(var); + } + true + } else { + false + } + }); + + if any_var { + } else if cp.starts_with(b"S") { + cp = &cp[1..]; + if cp.first() == Some(&b'=') { + cp = &cp[1..]; + } + pt_size = 0.0; + while cp.first().is_some_and(|c| c.is_ascii_digit()) { + pt_size = pt_size * 10.0 + (cp[0] - b'0') as f64; + cp = &cp[1..]; + } + if cp.first() == Some(&b'.') { + let mut dec = 1.0; + cp = &cp[1..]; + while cp.first().is_some_and(|c| c.is_ascii_digit()) { + dec *= 10.0; + pt_size += (cp[0] - b'0') as f64 / dec; + cp = &cp[1..]; + } + } + } else { + // if the code is "B" or "I", we skip putting it in varString + loop { + if cp[0] == b'B' { + req_bold = true; + cp = &cp[1..]; + } else if cp[0] == b'I' { + req_ital = true; + cp = &cp[1..]; + } else { + break; + } + } + } + + while cp.first().is_some_and(|&c| c != b'/') { + cp = &cp[1..]; + } + if cp.first().is_some_and(|&c| c == b'/') { + cp = &cp[1..]; + } + } + + variant[..var_str.len()].copy_from_slice(var_str.as_bytes()); + variant[var_str.len()] = 0; + + if req_ital { + let font = &self.maps.fonts[font_pos]; + let parent = &self.maps.families[parent_pos]; + let mut best_match_pos = font_pos; + if font.slant < parent.max_slant { + // try for a face with more slant + best_match_pos = self + .best_match_from_family( + parent, + font.weight as libc::c_int, + font.width as libc::c_int, + parent.max_slant as libc::c_int, + ) + .unwrap_or(best_match_pos); + } + + if best_match_pos == font_pos && font.slant > parent.min_slant { + // maybe the slant is negated, or maybe this was something like "Times-Italic/I" + best_match_pos = self + .best_match_from_family( + parent, + font.weight as libc::c_int, + font.width as libc::c_int, + parent.min_slant as libc::c_int, + ) + .unwrap_or(best_match_pos); + } + + let best_match = &self.maps.fonts[best_match_pos]; + if parent.min_weight == parent.max_weight && best_match.is_bold != font.is_bold { + // try again using the bold flag, as we can't trust weight values + let mut new_best = None; + for &style_pos in parent.styles.values() { + let style = &self.maps.fonts[style_pos]; + if style.is_bold == font.is_bold + && new_best.is_none() + && style.is_italic != font.is_italic + { + new_best = Some(style_pos); + break; + } + } + if let Some(new_best) = new_best { + best_match_pos = new_best; + } + } + + if best_match_pos == font_pos { + let mut new_best = None; + // maybe slant values weren't present; try the style bits as a fallback + for &style_pos in parent.styles.values() { + let style = &self.maps.fonts[style_pos]; + if style.is_italic != font.is_italic { + if parent.min_weight != parent.max_weight { + // weight info was available, so try to match that + if new_best.is_none() + || Self::weight_and_width_diff(style, font) + < Self::weight_and_width_diff( + &self.maps.fonts[new_best.unwrap()], + font, + ) + { + new_best = Some(style_pos); + } + } else { + // no weight info, so try matching style bits + if new_best.is_none() && style.is_bold == font.is_bold { + new_best = Some(style_pos); + break; // found a match, no need to look further as we can't distinguish! + } + } + } + } + + if let Some(new_best) = new_best { + best_match_pos = new_best; + } + } + + font_pos = best_match_pos; + } + + if req_bold { + let font = &self.maps.fonts[font_pos]; + let parent = &self.maps.families[parent_pos]; + let mut best_match = font_pos; + if font.weight < parent.max_weight { + best_match = self + .best_match_from_family( + parent, + (font.weight + (parent.max_weight - (parent.min_weight)) / 2 + 1) + as libc::c_int, + font.width as libc::c_int, + font.slant as libc::c_int, + ) + .unwrap_or(best_match); + if parent.min_slant == parent.max_slant { + let mut new_best = None; + for &style_pos in parent.styles.values() { + let style = &self.maps.fonts[style_pos]; + if style.is_italic == font.is_italic + && (new_best.is_none() + || Self::weight_and_width_diff( + style, + &self.maps.fonts[best_match], + ) < Self::weight_and_width_diff( + &self.maps.fonts[new_best.unwrap()], + &self.maps.fonts[best_match], + )) + { + new_best = Some(style_pos); + } + } + if let Some(new_best) = new_best { + best_match = new_best; + } + } + } + if best_match == font_pos && font.is_bold { + for &style_pos in parent.styles.values() { + let style = &self.maps.fonts[style_pos]; + if style.is_italic == font.is_italic && style.is_bold { + best_match = style_pos; + break; + } + } + } + font_pos = best_match; + } + } + + if pt_size < 0.0 { + pt_size = dsize; + } + + let font = &self.maps.fonts[font_pos]; + let parent = &self.maps.families[parent_pos]; + if font.op_size_info.sub_family_id != 0 && pt_size > 0.0 { + let mut best_mismatch = f64::max( + font.op_size_info.min_size - pt_size, + pt_size - font.op_size_info.max_size, + ); + if best_mismatch > 0.0 { + let mut best_match = font_pos; + for &style_pos in parent.styles.values() { + let style = &self.maps.fonts[style_pos]; + if style.op_size_info.sub_family_id != font.op_size_info.sub_family_id { + continue; + } + let mismatch = f64::max( + style.op_size_info.min_size - pt_size, + pt_size - style.op_size_info.max_size, + ); + if mismatch < best_mismatch { + best_match = style_pos; + best_mismatch = mismatch; + } + if best_mismatch <= 0.0 { + break; + } + } + font_pos = best_match; + } + } + + let font = &self.maps.fonts[font_pos]; + if font.op_size_info.design_size != 0.0 { + self.loaded_font_design_size = d_to_fix(font.op_size_info.design_size); + } + + Some(font.font_ref.clone()) + } + + /// Get the full name of a font as a C-string + pub fn get_full_name(&self, font: PlatformFontRef) -> &CStr { + let font_pos = *self + .maps + .platform_ref_to_font + .get(&font) + .unwrap_or_else(|| panic!("internal error {} in FontManager", 2)); + let font = &self.maps.fonts[font_pos]; + font.full_name.as_ref().unwrap_or(&font.ps_name) + } + + /// Get the design size of a font + pub fn get_design_size(&self, font: &Font) -> f64 { + let size_rec = Self::get_op_size(font); + match size_rec { + None => 10.0, + Some(size_rec) => size_rec.design_size, + } + } + + fn weight_and_width_diff(a: &FontInfo, b: &FontInfo) -> libc::c_int { + if a.weight == 0 && a.width == 0 { + return if a.is_bold == b.is_bold { 0 } else { 10000 }; + } + + let mut wid_diff = u16::abs_diff(a.width, b.width); + if wid_diff < 10 { + wid_diff *= 50; + } + + (u16::abs_diff(a.weight, b.weight) + wid_diff) as libc::c_int + } + + fn style_diff( + a: &FontInfo, + wt: libc::c_int, + wd: libc::c_int, + slant: libc::c_int, + ) -> libc::c_int { + let mut wid_diff = u16::abs_diff(a.width, wd as u16); + if wid_diff < 10 { + wid_diff *= 200; + } + + (u32::abs_diff(a.slant.unsigned_abs() as u32, slant.unsigned_abs()) * 2 + + u32::abs_diff(a.weight as u32, wt.unsigned_abs()) + + wid_diff as u32) as libc::c_int + } + + fn best_match_from_family( + &self, + family: &FamilyInfo, + wt: libc::c_int, + wd: libc::c_int, + slant: libc::c_int, + ) -> Option { + let mut best_match = None; + for &font_pos in family.styles.values() { + let font = &self.maps.fonts[font_pos]; + best_match = match best_match { + None => Some(font_pos), + Some(best_pos) => { + let best = &self.maps.fonts[best_pos]; + if Self::style_diff(font, wt, wd, slant) < Self::style_diff(best, wt, wd, slant) + { + Some(font_pos) + } else { + Some(best_pos) + } + } + }; + } + best_match + } + + fn append_to_list + AsRef>(list: &mut Vec, str: T) { + if !list.iter().any(|s| **s == *str.as_ref()) { + list.push(str.into()) + } + } + + #[cfg_attr(target_os = "macos", allow(unused))] + fn prepend_to_list + AsRef>(list: &mut Vec, str: T) { + *list = list.drain(..).filter(|s| **s != *str.as_ref()).collect(); + list.insert(0, str.into()); + } + + fn get_op_size(font: &Font) -> Option { + let hb_font = font.try_hb_font()?; + + hb_font + .face() + .ot_layout() + .size_params() + .map(|params| OpSizeRec { + sub_family_id: params.subfamily_id, + name_code: params.subfamily_name_id, + design_size: params.design_size as f64 * 72.27 / 72.0 / 10.0, + min_size: params.start as f64 * 72.27 / 72.0 / 10.0, + + max_size: params.end as f64 * 72.27 / 72.0 / 10.0, + }) + } + + fn search_for_host_platform_fonts(&mut self, name: &CStr) { + self.backend + .search_for_host_platform_fonts(&mut self.maps, name) + } + + /// Get the platform-specific font file location + pub fn get_platform_font_desc<'a>(&'a self, font: &'a PlatformFontRef) -> Cow<'a, CStr> { + self.backend.get_platform_font_desc(font) + } + + // TODO: Design size shouldn't really be a global property, as it's specific to last-loaded font + /// Get the last loaded font's design size + pub fn font_design_size(&self) -> Fixed { + self.loaded_font_design_size + } + + /// Set the design size to return for the last loaded font + pub fn set_font_design_size(&mut self, val: Fixed) { + self.loaded_font_design_size = val; + } + + /// Get the requested engine to use + pub fn get_req_engine(&self) -> Engine { + self.req_engine + } + + /// Set the engine to request - this engine will be used if possible, falling back to a + /// platform-specific list of other options. + pub fn set_req_engine(&mut self, engine: Engine) { + self.req_engine = engine; + } +} diff --git a/crates/xetex_layout/src/manager/fc.rs b/crates/xetex_layout/src/manager/fc.rs new file mode 100644 index 000000000..9ba747aec --- /dev/null +++ b/crates/xetex_layout/src/manager/fc.rs @@ -0,0 +1,285 @@ +use super::{ + base_get_op_size_rec_and_style_flags, FontInfo, FontManager, FontManagerBackend, FontMaps, + NameCollection, +}; +use crate::c_api::PlatformFontRef; +use enrede::encoding::{MacRoman, Utf16BE, Utf8}; +use enrede::Str; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; +use tectonic_bridge_fontconfig as fc; +use tectonic_bridge_freetype2 as ft; + +pub const FONT_FAMILY_NAME: libc::c_ushort = 1; +pub const FONT_STYLE_NAME: libc::c_ushort = 2; +pub const FONT_FULL_NAME: libc::c_ushort = 4; +pub const PREFERRED_FAMILY_NAME: libc::c_ushort = 16; +pub const PREFERRED_SUBFAMILY_NAME: libc::c_ushort = 17; + +pub struct FcBackend { + all_fonts: fc::FontSet, + cached_all: bool, +} + +impl FcBackend { + pub fn new() -> FcBackend { + if !fc::init() { + panic!("fontconfig initialization failed"); + } + ft::init(); + + let pat = fc::Pattern::from_name(c":outline=true").unwrap(); + let os = fc::ObjectSet::new(&[ + fc::Property::Family, + fc::Property::Style, + fc::Property::File, + fc::Property::Index, + fc::Property::FullName, + fc::Property::Weight, + fc::Property::Width, + fc::Property::Slant, + fc::Property::FontFormat, + ]); + let all_fonts = fc::FontSet::new(pat.as_ref(), os.as_ref()); + + FcBackend { + all_fonts, + cached_all: false, + } + } + + fn cache_family_members(&mut self, maps: &mut FontMaps, names: &[CString]) { + if names.is_empty() { + return; + } + + let font_set = self.all_fonts.as_ref(); + 'outer: for &pat in font_set.fonts() { + let pat = pat.upgrade(); + if maps.platform_ref_to_font.contains_key(&pat) { + continue; + } + + let mut i = 0; + while let Ok(str) = pat.as_ref().get::(i) { + for name in names { + if **name == *str { + let names = self.read_names(pat.clone()); + maps.add_to_maps(self, pat, &names); + continue 'outer; + } + } + + i += 1; + } + } + } +} + +impl FontManagerBackend for FcBackend { + fn get_platform_font_desc<'a>(&'a self, font: &'a PlatformFontRef) -> Cow<'a, CStr> { + if let Ok(str) = font.as_ref().get::(0) { + Cow::Borrowed(str) + } else { + Cow::Borrowed(c"[unknown]") + } + } + + fn get_op_size_rec_and_style_flags(&self, font: &mut FontInfo) { + base_get_op_size_rec_and_style_flags(font); + + if font.weight == 0 && font.width == 0 { + let pat = &font.font_ref; + if let Ok(weight) = pat.as_ref().get::(0) { + font.weight = weight as u16; + } + if let Ok(width) = pat.as_ref().get::(0) { + font.width = width as u16; + } + if let Ok(slant) = pat.as_ref().get::(0) { + font.slant = slant as i16; + } + } + } + + fn search_for_host_platform_fonts(&mut self, maps: &mut FontMaps, name: &CStr) { + if self.cached_all { + return; + } + + let bytes = name.to_bytes(); + let split = bytes + .iter() + .position(|c| *c == b'-') + .map(|index| (&bytes[..index], &bytes[index + 1..])); + + let (fam_name, hyph) = match split { + Some((fam, _)) => (fam, fam.len()), + None => (&[] as &[_], 0), + }; + + let mut found = false; + loop { + 'outer: for pos in 0..self.all_fonts.as_ref().fonts().len() { + let font_set = self.all_fonts.as_ref(); + let pat = font_set.fonts()[pos].upgrade(); + if maps.platform_ref_to_font.contains_key(&pat) { + continue; + } + + if self.cached_all { + let names = self.read_names(pat.clone()); + maps.add_to_maps(self, pat.clone(), &names); + continue; + } + + let mut i = 0; + while let Ok(str) = pat.as_ref().get::(i) { + if name == str { + let names = self.read_names(pat.clone()); + maps.add_to_maps(self, pat.clone(), &names); + self.cache_family_members(maps, &names.family_names); + found = true; + continue 'outer; + } + i += 1; + } + + let mut i = 0; + while let Ok(str) = pat.as_ref().get::(i) { + if name == str || (hyph != 0 && fam_name == str.to_bytes()) { + let names = self.read_names(pat.clone()); + maps.add_to_maps(self, pat.clone(), &names); + self.cache_family_members(maps, &names.family_names); + found = true; + continue 'outer; + } + let mut j = 0; + while let Ok(style) = pat.as_ref().get::(j) { + let mut full = str.to_bytes().to_owned(); + full.push(b' '); + full.extend(style.to_bytes()); + + if name.to_bytes() == full { + let names = self.read_names(pat.clone()); + maps.add_to_maps(self, pat.clone(), &names); + self.cache_family_members(maps, &names.family_names); + found = true; + continue 'outer; + } + + j += 1; + } + i += 1; + } + } + + if found || self.cached_all { + break; + } + self.cached_all = true; + } + } + + fn read_names(&self, pat: PlatformFontRef) -> NameCollection { + let mut names = NameCollection::default(); + + let pathname = match pat.as_ref().get::(0) { + Ok(name) => name, + Err(_) => return names, + }; + + let index = match pat.as_ref().get::(0) { + Ok(index) => index, + Err(_) => return names, + }; + + let face = match ft::Face::new(pathname, index as usize) { + Ok(face) => face, + Err(_) => return names, + }; + + let name = match face.get_postscript_name() { + Some(name) => name, + None => return names, + }; + + names.ps_name = Some(name.to_owned()); + + if face.is_sfnt() { + let mut family_names = Vec::new(); + let mut sub_family_names = Vec::new(); + + for i in 0..face.get_sfnt_name_count() { + let mut utf8_name = None; + let name_rec = match face.get_sfnt_name(i) { + Ok(name) => name, + Err(_) => continue, + }; + + match name_rec.name_id { + FONT_FULL_NAME + | FONT_FAMILY_NAME + | FONT_STYLE_NAME + | PREFERRED_FAMILY_NAME + | PREFERRED_SUBFAMILY_NAME => { + let mut preferred_name = false; + if name_rec.platform_id == ft::PlatformId::MACINTOSH + && name_rec.encoding_id == ft::EncodingId::MAC_ROMAN + && name_rec.language_id == ft::LanguageId::MAC_ENGLISH + { + let str = Str::::from_bytes_infallible(name_rec.string); + utf8_name = Some( + enrede::CString::try_from(str.recode::().unwrap()).unwrap(), + ); + preferred_name = true; + } else if name_rec.platform_id == ft::PlatformId::APPLE_UNICODE + || name_rec.platform_id == ft::PlatformId::MICROSOFT + { + let str = Str::::from_bytes(name_rec.string).unwrap(); + utf8_name = Some( + enrede::CString::try_from(str.recode::().unwrap()).unwrap(), + ); + } + + if let Some(name) = utf8_name { + let name_list = match name_rec.name_id { + FONT_FULL_NAME => &mut names.full_names, + FONT_FAMILY_NAME => &mut names.family_names, + FONT_STYLE_NAME => &mut names.style_names, + PREFERRED_FAMILY_NAME => &mut family_names, + PREFERRED_SUBFAMILY_NAME => &mut sub_family_names, + _ => unreachable!(), + }; + + if preferred_name { + FontManager::prepend_to_list(name_list, name.into_std()); + } else { + FontManager::append_to_list(name_list, name.into_std()); + } + } + } + _ => (), + } + } + } else { + let mut index = 0; + while let Ok(name) = pat.as_ref().get::(index) { + index += 1; + FontManager::append_to_list(&mut names.full_names, name); + } + index = 0; + while let Ok(fam) = pat.as_ref().get::(index) { + index += 1; + FontManager::append_to_list(&mut names.family_names, fam); + } + index = 0; + while let Ok(name) = pat.as_ref().get::(index) { + index += 1; + FontManager::append_to_list(&mut names.style_names, name); + } + } + + names + } +} diff --git a/crates/xetex_layout/src/manager/mac.rs b/crates/xetex_layout/src/manager/mac.rs new file mode 100644 index 000000000..3ea0b4aa6 --- /dev/null +++ b/crates/xetex_layout/src/manager/mac.rs @@ -0,0 +1,164 @@ +use crate::c_api::PlatformFontRef; +use crate::manager::{ + base_get_op_size_rec_and_style_flags, FontInfo, FontManager, FontManagerBackend, FontMaps, + NameCollection, +}; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; +use tectonic_mac_core::{ + CFArray, CFDictionary, CFSet, CFString, CFUrl, CTFont, CTFontDescriptor, CoreType, + FontAttribute, FontNameKey, +}; + +fn find_fonts_with_name(name: CFString, key: FontAttribute) -> CFArray { + let attributes = CFDictionary::new([(key.to_str(), name.into_ty())]); + let descriptor = CTFontDescriptor::new_with_attrs(&attributes); + + let mandatory_attributes = CFSet::new(&[key.to_str()]); + descriptor.matching_font_descriptors(&mandatory_attributes) +} + +fn find_font_with_name(name: CFString, key: FontAttribute) -> Option { + let matches = find_fonts_with_name(name, key); + + let mut matched = None; + if !matches.is_empty() { + matched = Some(matches[0].clone()); + } + matched +} + +fn append_name_to_list(font: &CTFont, name_list: &mut Vec, name_key: FontNameKey) { + let name = font.name(name_key); + if let Some(name) = name { + FontManager::append_to_list(name_list, name.as_cstr()); + } + let name = font.localized_name(name_key); + if let Some(name) = name { + FontManager::append_to_list(name_list, name.as_cstr()); + } +} + +pub struct MacBackend {} + +impl MacBackend { + pub fn new() -> MacBackend { + MacBackend {} + } + + fn add_fonts_to_caches(&self, maps: &mut FontMaps, members: CFArray) { + for i in 0..members.len() { + let font = &members[i]; + let names = self.read_names(font.clone()); + maps.add_to_maps(self, font.clone(), &names) + } + } + + fn add_font_and_siblings_to_caches(&self, maps: &mut FontMaps, font: &CTFontDescriptor) { + let font = CTFont::new_descriptor(font, 10.0); + let attr = font.attr(FontAttribute::FamilyName).unwrap(); + // SAFETY: CFString has no generic parameters + let family = unsafe { attr.downcast::() }.unwrap(); + let matched = find_fonts_with_name(family, FontAttribute::FamilyName); + self.add_fonts_to_caches(maps, matched); + } + + fn add_family_to_caches(&self, maps: &mut FontMaps, family: CTFontDescriptor) { + let name_str = family + .attr(FontAttribute::FamilyName) + // SAFETY: CFString has no generic parameters + .and_then(|ty| unsafe { ty.downcast::() }.ok()); + if let Some(name_str) = name_str { + let members = find_fonts_with_name(name_str, FontAttribute::FamilyName); + self.add_fonts_to_caches(maps, members); + } + } +} + +impl FontManagerBackend for MacBackend { + fn get_platform_font_desc<'a>(&'a self, font: &'a PlatformFontRef) -> Cow<'a, CStr> { + let mut path = Cow::Borrowed(c"[unknown]"); + + let ct_font = CTFont::new_descriptor(font, 0.0); + let url = ct_font + .attr(FontAttribute::URL) + // SAFETY: CFUrl has no generic parameters + .and_then(|ty| unsafe { ty.downcast::() }.ok()); + + if let Some(url) = url { + if let Some(fs_path) = url.fs_representation() { + path = Cow::Owned(fs_path); + } + } + + path + } + + fn get_op_size_rec_and_style_flags(&self, font: &mut FontInfo) { + base_get_op_size_rec_and_style_flags(font); + } + + fn search_for_host_platform_fonts(&mut self, maps: &mut FontMaps, name: &CStr) { + let name_str = CFString::new(name); + let matched = find_font_with_name(name_str.clone(), FontAttribute::DisplayName); + if let Some(matched) = matched { + self.add_font_and_siblings_to_caches(maps, &matched); + return; + } + + let hyph = name.to_bytes().iter().copied().position(|c| c == b'-'); + if let Some(hyph) = hyph { + let family = CString::new(&name.to_bytes()[..hyph]).unwrap(); + let family_str = CFString::new(&*family); + let family_members = + find_fonts_with_name(family_str.clone(), FontAttribute::FamilyName); + if !family_members.is_empty() { + self.add_fonts_to_caches(maps, family_members); + return; + } + + let matched = find_font_with_name(family_str, FontAttribute::FamilyName); + if let Some(matched) = matched { + self.add_family_to_caches(maps, matched); + return; + } + } + + let matched = find_font_with_name(name_str.clone(), FontAttribute::Name); + if let Some(matched) = matched { + self.add_font_and_siblings_to_caches(maps, &matched); + return; + } + + let family_members = find_fonts_with_name(name_str.clone(), FontAttribute::FamilyName); + if !family_members.is_empty() { + self.add_fonts_to_caches(maps, family_members); + return; + } + + let matched = find_font_with_name(name_str, FontAttribute::FamilyName); + if let Some(matched) = matched { + self.add_family_to_caches(maps, matched); + } + } + + fn read_names(&self, font: PlatformFontRef) -> NameCollection { + let mut names = NameCollection::default(); + + let ps_name = match font.attr(FontAttribute::Name) { + Some(ps_name) => ps_name, + None => return names, + }; + // SAFETY: CFString has no generic parameters + let ps_name = unsafe { ps_name.downcast::() }.unwrap(); + + names.ps_name = Some(ps_name.get_cstring()); + + let font = CTFont::new_descriptor(&font, 0.0); + append_name_to_list(&font, &mut names.full_names, FontNameKey::Full); + append_name_to_list(&font, &mut names.family_names, FontNameKey::Family); + append_name_to_list(&font, &mut names.style_names, FontNameKey::Style); + + names + } +} diff --git a/crates/xetex_layout/src/utils.rs b/crates/xetex_layout/src/utils.rs new file mode 100644 index 000000000..ee7efc817 --- /dev/null +++ b/crates/xetex_layout/src/utils.rs @@ -0,0 +1,23 @@ +use crate::c_api::{Fixed, PlatformFontRef, RawPlatformFontRef}; +use std::ptr::NonNull; + +pub fn fix_to_d(f: Fixed) -> f64 { + f as f64 / 65536.0 +} + +pub fn d_to_fix(d: f64) -> Fixed { + (d * 65536.0 + 0.5) as Fixed +} + +pub fn raw_to_rs(font: RawPlatformFontRef) -> Option { + #[cfg(target_os = "macos")] + let out = { + use tectonic_mac_core::CoreType; + // SAFETY: Pointer must be from us, and is thus a borrowed ref + NonNull::new(font.cast_mut()).map(|ptr| unsafe { PlatformFontRef::new_borrowed(ptr) }) + }; + #[cfg(not(target_os = "macos"))] + // SAFETY: Pointer must be from us, and is thus a borrowed ref + let out = { unsafe { NonNull::new(font).map(|p| PlatformFontRef::from_raw_borrowed(p)) } }; + out +}