├── .appveyor.yml ├── .circleci ├── config.yml ├── inner-build.sh └── outer-build.sh ├── .gitattributes ├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .gitmodules ├── .rustfmt.toml ├── .travis.yml.disabled ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── RELEASE_PROCESS.md ├── bibtex ├── Cargo.toml └── src │ └── lib.rs ├── bridge ├── Cargo.toml ├── build.rs ├── src │ ├── lib.rs │ ├── macro_stub.rs │ └── stub_errno.rs └── stub │ └── stub_errno.c ├── cfg_support ├── Cargo.toml ├── LICENSE ├── README.md └── src │ └── lib.rs ├── dist ├── appimage │ ├── .gitignore │ ├── build.sh │ ├── tectonic.desktop │ └── tectonic.svg ├── arch │ ├── PKGBUILD │ └── deploy.sh ├── build-mdbook.sh ├── deploy_key.enc ├── deploy_key.pub ├── docker │ └── x86_64-alpine-linux-musl │ │ ├── Dockerfile │ │ ├── README.md │ │ ├── cargo-config.toml │ │ ├── linkwrapper.sh │ │ ├── setup_priv.sh │ │ ├── setup_unpriv.sh │ │ ├── sudoers │ │ └── toolwrapper.sh ├── force-push-tree.sh ├── prepare-win-ci-deps.bat └── travis.sh ├── docs ├── book.toml └── src │ ├── SUMMARY.md │ ├── cli │ └── index.md │ ├── cookbook │ ├── index.md │ └── vcpkg.md │ ├── getting-started │ └── index.md │ ├── index.md │ ├── introduction │ └── index.md │ └── lang-impl │ └── index.md ├── dpx ├── Cargo.toml ├── LICENSE ├── build.rs ├── shims │ └── dpx_shims.c └── src │ ├── dpx_agl.rs │ ├── dpx_bmpimage.rs │ ├── dpx_cff.rs │ ├── dpx_cff_dict.rs │ ├── dpx_cid.rs │ ├── dpx_cidtype0.rs │ ├── dpx_cidtype2.rs │ ├── dpx_cmap.rs │ ├── dpx_cmap_read.rs │ ├── dpx_cmap_write.rs │ ├── dpx_cs_type2.rs │ ├── dpx_dpxconf.rs │ ├── dpx_dpxcrypt.rs │ ├── dpx_dpxfile.rs │ ├── dpx_dpxutil.rs │ ├── dpx_dvi.rs │ ├── dpx_dvicodes.rs │ ├── dpx_dvipdfmx.rs │ ├── dpx_epdf.rs │ ├── dpx_error.rs │ ├── dpx_fontmap.rs │ ├── dpx_jp2image.rs │ ├── dpx_jpegimage.rs │ ├── dpx_mfileio.rs │ ├── dpx_mpost.rs │ ├── dpx_numbers.rs │ ├── dpx_otl_opt.rs │ ├── dpx_pdfcolor.rs │ ├── dpx_pdfdev.rs │ ├── dpx_pdfdoc.rs │ ├── dpx_pdfdraw.rs │ ├── dpx_pdfencoding.rs │ ├── dpx_pdfencrypt.rs │ ├── dpx_pdffont.rs │ ├── dpx_pdfnames.rs │ ├── dpx_pdfobj.rs │ ├── dpx_pdfparse.rs │ ├── dpx_pdfresource.rs │ ├── dpx_pdfximage.rs │ ├── dpx_pkfont.rs │ ├── dpx_pngimage.rs │ ├── dpx_pst.rs │ ├── dpx_sfnt.rs │ ├── dpx_subfont.rs │ ├── dpx_t1_char.rs │ ├── dpx_t1_load.rs │ ├── dpx_tfm.rs │ ├── dpx_truetype.rs │ ├── dpx_tt_aux.rs │ ├── dpx_tt_cmap.rs │ ├── dpx_tt_glyf.rs │ ├── dpx_tt_gsub.rs │ ├── dpx_tt_post.rs │ ├── dpx_tt_table.rs │ ├── dpx_type0.rs │ ├── dpx_type1.rs │ ├── dpx_type1c.rs │ ├── dpx_unicode.rs │ ├── dpx_vf.rs │ ├── lib.rs │ ├── shims.rs │ ├── specials.rs │ └── specials │ ├── color.rs │ ├── dvipdfmx.rs │ ├── dvips.rs │ ├── html.rs │ ├── misc.rs │ ├── pdfm.rs │ ├── tpic.rs │ ├── util.rs │ └── xtx.rs ├── engine ├── Cargo.toml ├── build.rs ├── src │ ├── cmd.rs │ ├── fmt_file.rs │ ├── lib.rs │ ├── node.rs │ ├── stub_icu.rs │ ├── stub_teckit.rs │ ├── text_layout_engine.rs │ ├── tfm.rs │ ├── trie.rs │ ├── xetex_aatfont.rs │ ├── xetex_consts.rs │ ├── xetex_engine_interface.rs │ ├── xetex_errors.rs │ ├── xetex_ext.rs │ ├── xetex_font_info.rs │ ├── xetex_font_info_coretext.rs │ ├── xetex_font_manager.rs │ ├── xetex_font_manager_coretext.rs │ ├── xetex_font_manager_fontconfig.rs │ ├── xetex_ini.rs │ ├── xetex_io.rs │ ├── xetex_layout_interface.rs │ ├── xetex_linebreak.rs │ ├── xetex_math.rs │ ├── xetex_opentype_math.rs │ ├── xetex_output.rs │ ├── xetex_pagebuilder.rs │ ├── xetex_pic.rs │ ├── xetex_scaledmath.rs │ ├── xetex_shipout.rs │ ├── xetex_stringpool.rs │ ├── xetex_synctex.rs │ ├── xetex_texmfmp.rs │ ├── xetex_xetex0.rs │ └── xetex_xetexd.rs └── tectonic │ ├── core-foundation.h │ ├── stub_icu.c │ ├── stub_stdio.c │ ├── teckit-Common.h │ ├── teckit-Engine.cpp │ ├── teckit-Format.h │ ├── teckit-NormalizationData.c │ ├── teckit-c-Engine.h │ └── teckit-cxx-Engine.h ├── fuzz ├── .gitignore ├── Cargo.toml ├── README.md ├── fuzz_targets │ └── compile.rs ├── run-fuzzer.sh └── seeds │ └── hello.tex ├── src ├── app_dirs.rs ├── bin │ └── tectonic.rs ├── config.rs ├── digest.rs ├── driver.rs ├── engines │ ├── bibtex.rs │ ├── mod.rs │ ├── spx2html.rs │ ├── tex.rs │ └── xdvipdfmx.rs ├── errors.rs ├── io │ ├── cached_itarbundle.rs │ ├── dirbundle.rs │ ├── filesystem.rs │ ├── format_cache.rs │ ├── memory.rs │ ├── mod.rs │ ├── setup.rs │ ├── stack.rs │ ├── stdstreams.rs │ └── zipbundle.rs ├── lib.rs ├── status │ ├── mod.rs │ ├── plain.rs │ └── termcolor.rs ├── test_util.rs └── unstable_opts.rs ├── tests ├── assets │ ├── ckx.map │ ├── cmbsy10.tfm │ ├── cmbx10.tfm │ ├── cmbx5.tfm │ ├── cmbx6.tfm │ ├── cmbx7.tfm │ ├── cmbx8.tfm │ ├── cmbx9.tfm │ ├── cmcsc10.tfm │ ├── cmdunh10.tfm │ ├── cmex10.tfm │ ├── cmmi10.tfm │ ├── cmmi5.tfm │ ├── cmmi6.tfm │ ├── cmmi7.tfm │ ├── cmmi8.tfm │ ├── cmmi9.tfm │ ├── cmmib10.tfm │ ├── cmr10.pfb │ ├── cmr10.tfm │ ├── cmr5.tfm │ ├── cmr6.tfm │ ├── cmr7.tfm │ ├── cmr8.tfm │ ├── cmr9.tfm │ ├── cmsl10.tfm │ ├── cmsl8.tfm │ ├── cmsl9.tfm │ ├── cmsltt10.tfm │ ├── cmss10.tfm │ ├── cmssbx10.tfm │ ├── cmssi10.tfm │ ├── cmssq8.tfm │ ├── cmssqi8.tfm │ ├── cmsy10.tfm │ ├── cmsy5.tfm │ ├── cmsy6.tfm │ ├── cmsy7.tfm │ ├── cmsy8.tfm │ ├── cmsy9.tfm │ ├── cmti10.tfm │ ├── cmti7.tfm │ ├── cmti8.tfm │ ├── cmti9.tfm │ ├── cmtt10.tfm │ ├── cmtt8.tfm │ ├── cmtt9.tfm │ ├── cmu10.tfm │ ├── glyphlist.txt │ ├── hyphen.tex │ ├── issue393_ungetc_trigger.pdf │ ├── kanjix.map │ ├── knuth-plain.tex │ ├── lmroman12-regular.otf │ ├── manfnt.tfm │ ├── pdfglyphlist.txt │ ├── pdftex.map │ ├── plain.tex │ ├── png_gray_4bit.png │ ├── png_graya.png │ ├── png_palette_4bit.png │ ├── png_palette_alpha.png │ ├── png_rgba.png │ ├── png_rgba_16_bit.png │ ├── redbox.png │ ├── tectonic-format-latex.tex │ └── tectonic-format-plain.tex ├── bibtex.rs ├── bibtex │ ├── plain.bst │ ├── single_entry.aux │ ├── single_entry.bbl │ ├── single_entry.bib │ └── single_entry.blg ├── cached_itarbundle.rs ├── driver.rs ├── executable.rs ├── executable │ ├── subdirectory │ │ ├── content │ │ │ └── 1.tex │ │ └── relative_include.tex │ └── test space.tex ├── formats.rs ├── tex-outputs.rs ├── tex-outputs │ ├── a4paper.log │ ├── a4paper.pdf │ ├── a4paper.tex │ ├── a4paper.xdv │ ├── file_encoding.log │ ├── file_encoding.tex │ ├── file_encoding.xdv │ ├── file_encoding_utf16be.txt │ ├── file_encoding_utf16le.txt │ ├── file_encoding_utf8.txt │ ├── hallöchen 🐨 welt 🌍.log │ ├── hallöchen 🐨 welt 🌍.tex │ ├── hallöchen 🐨 welt 🌍.xdv │ ├── issue393_ungetc.log │ ├── issue393_ungetc.tex │ ├── issue393_ungetc.xdv │ ├── md5_of_hello.log │ ├── md5_of_hello.pdf │ ├── md5_of_hello.tex │ ├── md5_of_hello.xdv │ ├── negative_roman_numeral.log │ ├── negative_roman_numeral.tex │ ├── negative_roman_numeral.xdv │ ├── otf_basic.log │ ├── otf_basic.tex │ ├── otf_basic.xdv │ ├── pdfoutput.log │ ├── pdfoutput.tex │ ├── pdfoutput.xdv │ ├── png_formats.log │ ├── png_formats.pdf │ ├── png_formats.tex │ ├── png_formats.xdv │ ├── redbox_png.log │ ├── redbox_png.pdf │ ├── redbox_png.tex │ ├── redbox_png.xdv │ ├── synctex.log │ ├── synctex.synctex.gz │ ├── synctex.tex │ ├── synctex.xdv │ ├── tectoniccodatokens_errinside.log │ ├── tectoniccodatokens_errinside.tex │ ├── tectoniccodatokens_noend.log │ ├── tectoniccodatokens_noend.tex │ ├── tectoniccodatokens_ok.log │ ├── tectoniccodatokens_ok.tex │ ├── tectoniccodatokens_ok.xdv │ ├── tex_logo.log │ ├── tex_logo.pdf │ ├── tex_logo.tex │ ├── tex_logo.xdv │ ├── the_letter_a.log │ ├── the_letter_a.pdf │ ├── the_letter_a.tex │ └── the_letter_a.xdv ├── trip.rs ├── trip │ ├── etrip.fot │ ├── etrip.log │ ├── etrip.out │ ├── etrip.tex │ ├── etrip.tfm │ ├── etrip.xdv │ ├── trip.fot │ ├── trip.log │ ├── trip.tex │ ├── trip.tfm │ ├── trip.xdv │ └── tripos.tex ├── util │ └── mod.rs └── xenia │ ├── .version │ ├── aastex6.cls │ ├── gemini.pdf │ ├── mode.tex │ ├── paper.bib │ ├── paper.tex │ ├── pdm.pdf │ ├── phased.pdf │ ├── schematic.pdf │ ├── setup.tex │ ├── vla.pdf │ └── yahapj.bst └── xdv ├── Cargo.toml ├── README.md ├── examples └── xdvdump.rs └── src └── lib.rs /.appveyor.yml: -------------------------------------------------------------------------------- 1 | # Appveyor configuration template for Rust using rustup for Rust installation 2 | # https://github.com/starkat99/appveyor-rust 3 | 4 | ## Operating System (VM environment) ## 5 | 6 | # Rust needs at least Visual Studio 2013 Appveyor OS for MSVC targets. 7 | os: Visual Studio 2017 8 | 9 | ## Build Matrix ## 10 | environment: 11 | matrix: 12 | ### MSVC Targets ### 13 | # Stable 64-bit MSVC 14 | - target: x86_64-pc-windows-msvc 15 | channel: stable 16 | toolchain: stable-x86_64-pc-windows-msvc 17 | target_env: msvc 18 | # Stable 32-bit MSVC 19 | # - channel: stable 20 | # toolchain: stable-i686-pc-windows-msvc 21 | # target: i686-pc-windows-msvc 22 | # target_env: msvc 23 | # mingw_subdir: mingw32 24 | # mingw_target: i686-w64-mingw32 25 | # mingw_package_prefix: mingw-w64-i686 26 | 27 | ### GNU Targets ### 28 | # Stable 64-bit GNU 29 | - target: x86_64-pc-windows-gnu 30 | channel: stable 31 | toolchain: stable-x86_64-pc-windows-msvc 32 | target_env: gnu 33 | mingw_subdir: mingw64 34 | mingw_target: x86_64-w64-mingw32 35 | mingw_package_prefix: mingw-w64-x86_64 36 | # Stable 32-bit GNU 37 | # - channel: stable 38 | # toolchain: stable-i686-pc-windows-msvc 39 | # target: i686-pc-windows-gnu 40 | # target_env: gnu 41 | # mingw_subdir: mingw32 42 | # mingw_target: i686-w64-mingw32 43 | # mingw_package_prefix: mingw-w64-i686 44 | 45 | # Don't CI branches besides master. Pull requests still get CI'd -- but 46 | # when a PR comes in on a branch in the same repo, this prevents it from 47 | # being CI'd twice. 48 | branches: 49 | only: 50 | - master 51 | 52 | cache: 53 | - '%USERPROFILE%\.cargo\bin' 54 | - '%USERPROFILE%\.cargo\config' 55 | - '%USERPROFILE%\.cargo\env' 56 | - '%USERPROFILE%\.cargo\.crates.toml' 57 | - target 58 | 59 | ## Install Script ## 60 | 61 | # This is the most important part of the Appveyor configuration. This installs the version of Rust 62 | # specified by the 'channel' and 'target' environment variables from the build matrix. This uses 63 | # rustup to install Rust. 64 | # 65 | # For simple configurations, instead of using the build matrix, you can simply set the 66 | # default-toolchain and default-host manually here. 67 | install: 68 | - appveyor-retry appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe 69 | - rustup-init -yv --default-toolchain %channel%-msvc --default-host %target% 70 | - set PATH=%PATH%;%USERPROFILE%\.cargo\bin 71 | - if "%target_env%" == "gnu" set PATH=C:\msys64\%mingw_subdir%\bin;C:\msys64\usr\bin;%PATH% 72 | - rustup target add %target% 73 | - rustup component add rust-src 74 | - if "%target_env%" == "gnu" pacman -S --noconfirm "%mingw_package_prefix%-fontconfig" "%mingw_package_prefix%-freetype" "%mingw_package_prefix%-icu" 75 | - if "%target_env%" == "msvc" git clone https://github.com/microsoft/vcpkg %USERPROFILE%\vcpkg 76 | - if "%target_env%" == "msvc" call %USERPROFILE%\vcpkg\bootstrap-vcpkg.bat 77 | - if "%target_env%" == "msvc" %USERPROFILE%\vcpkg\vcpkg install --triplet x64-windows-static fontconfig freetype harfbuzz[icu,graphite2] 78 | - if "%target_env%" == "msvc" set VCPKG_ROOT=%USERPROFILE%\vcpkg 79 | - if "%target_env%" == "msvc" set TECTONIC_DEP_BACKEND=vcpkg 80 | - if "%target_env%" == "msvc" set RUSTFLAGS=-Ctarget-feature=+crt-static 81 | - rustc -vV 82 | - cargo -vV 83 | 84 | ## Build Script ## 85 | 86 | # 'cargo test' takes care of building for us, so disable Appveyor's build stage. This prevents 87 | # the "directory does not contain a project or solution file" error. 88 | build: false 89 | 90 | before_test: 91 | - set RUST_BACKTRACE=1 92 | # Building on msvc toolchain is seen as cross compilation. 93 | - set PKG_CONFIG_ALLOW_CROSS=1 94 | # Workaround rust#53454 95 | - if "%target_env%" == "gnu" copy /y "C:\msys64\%mingw_subdir%\%mingw_target%\lib\crt2.o" %USERPROFILE%\.rustup\toolchains\%toolchain%\lib\rustlib\%target%\lib\crt2.o" 96 | - if "%target_env%" == "gnu" copy /y "C:\msys64\%mingw_subdir%\%mingw_target%\lib\dllcrt2.o" %USERPROFILE%\.rustup\toolchains\%toolchain%\lib\rustlib\%target%\lib\dllcrt2.o" 97 | # Dodge format file locking issue in the test suite 98 | - set RUST_TEST_THREADS=1 99 | 100 | # Uses 'cargo test' to run tests and build. Alternatively, the project may call compiled programs 101 | # directly or perform other testing commands. Rust will automatically be placed in the PATH 102 | # environment variable. 103 | test_script: 104 | - cargo test --target %target% 105 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 the Tectonic Project 2 | # Licensed under the MIT License 3 | 4 | # This CI workflow builds and tests Tectonic on a synthetic PowerPC CPU to 5 | # verify that we work on big-endian systems. It's not included in the Travis 6 | # suite because the workflow is so different from our standard one, and Circle 7 | # has a longer build time limit, which is important in this laughably 8 | # inefficient approach that we're taking. 9 | 10 | version: 2 11 | 12 | jobs: 13 | build: 14 | machine: 15 | image: circleci/classic:201711-01 16 | steps: 17 | # Print a notice aimed at confused pull-requesters: 18 | - run: echo "Testing Tectonic on a synthetic big-endian CPU; this will take a while ..." 19 | - checkout 20 | - run: 21 | command: sudo bash .circleci/outer-build.sh $CIRCLE_WORKING_DIRECTORY 22 | no_output_timeout: 1800 23 | -------------------------------------------------------------------------------- /.circleci/inner-build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2018-2019 the Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | set -e -x 6 | 7 | work="$1" 8 | cd "$work" 9 | 10 | # Validate that we're actually running in big-endian land! I spent a while 11 | # working on ARM emulation before I noticed that Ubuntu ARM builds are 12 | # little-endian ... 13 | 14 | cat <<'EOF' >check-bigendian.rs 15 | #[cfg(not(target_endian = "big"))] 16 | fn error_if_not_big() { this_platform_should_be_bigendian_but_apparently_is_not(); } 17 | fn main() {} 18 | EOF 19 | 20 | rustc --crate-type=bin -o check-bigendian check-bigendian.rs 21 | 22 | # I've read that QEMU has trouble with multithreading, so we try hard to 23 | # serialize everything we do. 24 | 25 | export RUSTFLAGS="-C codegen-units=1" 26 | cargo build -j=1 27 | RUST_TEST_THREADS=1 cargo test -j=1 28 | -------------------------------------------------------------------------------- /.circleci/outer-build.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2018-2019 the Tectonic Project 3 | # Licensed under the MIT License 4 | 5 | set -e -x 6 | 7 | buildroot=$HOME 8 | 9 | # This link points to a tarball of a pre-assembled Ubuntu PowerPC chroot 10 | # that can be run via QEMU. This tarball was generated according to the 11 | # procedure describe here: 12 | # https://github.com/tectonic-typesetting/tectonic-ci-support/blob/master/bigendian/README.md . 13 | # The current version embeds Rust 1.36.0. 14 | 15 | tarball='https://github.com/tectonic-typesetting/tectonic-ci-support/releases/download/production/tectonic-buildenv-ppc.tar.gz' 16 | 17 | # Validate that our little scheme is going to work. Annoyingly 18 | # $CIRCLE_WORKING_DIRECTORY (passed in as $1 since we're sudo -- it's stripped 19 | # out of the environment) is defined with a literal `~` in its value. 20 | 21 | work=$(eval echo $1) 22 | 23 | case $work in 24 | $buildroot/*) ;; 25 | *) echo >&2 "error: working directory \"$work\" does not reside within \"$buildroot\"" 26 | exit 1 ;; 27 | esac 28 | 29 | chroot_work=${work#$buildroot} 30 | 31 | # Install the stuff we need to run in the QEMU chroot. 32 | 33 | apt-get -qq update 34 | apt-get install -qy binfmt-support qemu-user-static 35 | 36 | # Xenial's QEMU has a bug in its PPC "binfmt_misc" definition that means that 37 | # it fails to identify certain valid PPC binaries (namely, our 38 | # /usr/lib/gcc/powerpc-linux-gnu/5/cc1). See 39 | # https://aur.archlinux.org/packages/qemu-user-static/?comments=all . Fix it: 40 | 41 | update-binfmts --disable qemu-ppc 42 | sed -e 's/\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff/\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfc/' \ 43 | binfmt-bugfix 44 | tee /var/lib/binfmts/qemu-ppc "] 9 | description = """ 10 | A modernized, complete, embeddable TeX/LaTeX engine. Tectonic is forked from the XeTeX 11 | extension to the classic “Web2C” implementation of TeX and uses the TeXLive distribution 12 | of support files. 13 | """ 14 | homepage = "https://tectonic-typesetting.github.io/" 15 | documentation = "https://docs.rs/tectonic" 16 | repository = "https://github.com/tectonic-typesetting/tectonic/" 17 | readme = "README.md" 18 | keywords = ["tex", "latex", "typesetting", "font"] 19 | categories = ["command-line-interface", "parser-implementations", "rendering", "science", "text-processing"] 20 | license = "MIT" 21 | edition = "2018" 22 | exclude = ["/dist/", "/reference_sources/"] 23 | 24 | [badges] 25 | travis-ci = { repository = "tectonic-typesetting/tectonic" } 26 | codecov = { repository = "tectonic-typesetting/tectonic", service = "github" } 27 | 28 | [workspace] 29 | members = ["engine", "dpx", "bridge", "xdv"] 30 | 31 | [lib] 32 | name = "tectonic" 33 | crate-type = ["rlib"] 34 | 35 | [dependencies] 36 | app_dirs2 = "2" 37 | atty = "0.2" 38 | byte-unit = "^4.0" 39 | cfg-if = "0.1" 40 | structopt = "0.3" 41 | error-chain = "^0.12" 42 | flate2 = { version = "^1.0", default-features = false, features = ["zlib"] } 43 | fs2 = "^0.4" 44 | headers = "^0.2" 45 | lazy_static = "^1.4" 46 | libc = "^0.2" 47 | tempfile = "^3.1" 48 | md-5 = "^0.9" 49 | reqwest = "^0.9" 50 | sha2 = "^0.8" 51 | serde = { version = "^1.0", features = ["derive"], optional = true } 52 | tectonic_engine = { path = "engine", version = "0.0.1-dev" } 53 | tectonic_xdv = { path = "xdv", version = "0.1.9-dev" } 54 | termcolor = "^1.0" 55 | toml = { version = "^0.5", optional = true } 56 | zip = { version = "^0.5", default-features = false, features = ["deflate"] } 57 | 58 | [features] 59 | default = ["serialization"] 60 | # Note: we used to have this to couple "serde" and "serde-derive", but we've 61 | # adopted the newer scheme to avoid having to depend on both -- should maybe 62 | # just get rid of this feature: 63 | serialization = ["serde", "toml"] 64 | 65 | # developer feature to compile with the necessary flags for profiling tectonic. 66 | profile = ["tectonic_engine/profile"] 67 | 68 | # freetype-sys = "^0.4" 69 | # harfbuzz-sys = "^0.1" 70 | # libz-sys = "^1.0" 71 | 72 | [dev-dependencies] 73 | futures = "0.1" 74 | headers = "0.2" 75 | hyper = "0.12" 76 | tempfile = "^3.1" 77 | tokio = "0.1.22" 78 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (Tectonic is licensed under the MIT License. Elements of the system from which 2 | it is derived are licensed under an extremely wide variety of open-source 3 | licenses.) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the “Software”), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tectonic 2 | 3 | Tectonic is a modernized, complete, self-contained 4 | [TeX](https://en.wikipedia.org/wiki/TeX)/[LaTeX](https://www.latex-project.org/) 5 | engine, powered by [XeTeX](http://xetex.sourceforge.net/) and 6 | [TeXLive](https://www.tug.org/texlive/). 7 | 8 | ## Read this first 9 | 10 | If you just want to compile TeX documents, you should probably **click through to 11 | [the main Tectonic website](https://tectonic-typesetting.github.io/)**. This 12 | page is primarily aimed at folks interested in how Tectonic works “under the hood.” 13 | 14 | ## Developer dashboard 15 | 16 | [![Build Status](https://travis-ci.org/tectonic-typesetting/tectonic.svg?branch=master)](https://travis-ci.org/tectonic-typesetting/tectonic) 17 | [![](http://meritbadge.herokuapp.com/tectonic)](https://crates.io/crates/tectonic) 18 | [![codecov](https://codecov.io/gh/tectonic-typesetting/tectonic/branch/master/graph/badge.svg)](https://codecov.io/gh/tectonic-typesetting/tectonic) 19 | 20 | Packaging status 21 | 22 | 23 | - [User website](https://tectonic-typesetting.github.io/). 24 | - [Community discussion forum](https://tectonic.newton.cx/). 25 | - [Installation](https://tectonic-typesetting.github.io/install.html). 26 | - [Developer documentation, including build instructions](https://tectonic-typesetting.github.io/develop.html). 27 | - [API documentation](https://docs.rs/tectonic/). 28 | - [Issues](https://github.com/tectonic-typesetting/tectonic/issues/). 29 | - [Changelog](./CHANGELOG.md). 30 | 31 | ## Technical ecosystem 32 | 33 | If you’re interested in Tectonic as a software tool, you might also want to check out: 34 | 35 | - You can use Tectonic as a 36 | [GitHub Action](https://github.com/features/actions) with 37 | [compile-latex](https://github.com/marketplace/actions/compile-latex), 38 | thanks to [Vinay Sharma](https://github.com/vinay0410). 39 | - [tt.ente.ninja](https://tt.ente.ninja), which runs Tectonic against a subset 40 | of the [arxiv.org](https://arxiv.org/) corpus à la the Rust tool 41 | [Crater](https://github.com/rust-lang/crater) — a 42 | [project](https://github.com/Mrmaxmeier/tectonic-on-arXiv) by 43 | [@Mrmaxmeier](https://github.com/Mrmaxmeier/) 44 | 45 | ## The “reference sources” 46 | 47 | Much of the core code of Tectonic is derived from 48 | [XeTeX](http://xetex.sourceforge.net/), and we strive to track and maintain 49 | compatibility with upstream as much as possible. However, the nature of the 50 | Tectonic project is such that its source code is going to diverge from that of 51 | XeTeX over time. We can do our best to track the *semantics* of changes to 52 | XeTeX, but the expression of those changes in source form may well change 53 | greatly over time. 54 | 55 | In this repository, the Git submodule `reference_sources` links to the 56 | [“staging repository”](https://github.com/tectonic-typesetting/tectonic-staging) 57 | that tracks the XeTeX source 58 | code that we use as a reference. In particular, the version of the reference 59 | code in the submodule is the most recent code whose semantics are *guaranteed* 60 | to be expressed in Tectonic, to the best of our efforts. You don’t need to 61 | clone `reference_sources` to build Tectonic (which is good because everyone is 62 | always super confused by how Git submodules work!). It just provides a 63 | convenient way for Git to track the exact reference code that we are using at 64 | any given time. 65 | 66 | Please see 67 | [the tectonic-staging README](https://github.com/tectonic-typesetting/tectonic-staging#readme) 68 | for more information. (Or at least, more words on the topic.) 69 | 70 | 71 | ## Features 72 | 73 | The Tectonic build can be customized with the following features: 74 | 75 | ##### serialization (enabled by default) 76 | 77 | This feature enables (de)serialization using the [serde](https://serde.rs/) 78 | crate. At the moment, this is only used to read per-user configuration from a 79 | [TOML](https://github.com/toml-lang/toml) file. If this feature is disabled, 80 | the per-user configuration file will be silently ignored. 81 | 82 | This functionality is optional because it requires the `serde_derive` crate, 83 | which in turn uses Rust’s `proc_macro` feature. The `proc_macro` functionality 84 | [is not available on musl targets](https://github.com/rust-lang/rust/issues/40174), 85 | and so must be turned off if you wish to build a completely static Tectonic 86 | executable. 87 | -------------------------------------------------------------------------------- /RELEASE_PROCESS.md: -------------------------------------------------------------------------------- 1 | # Releasing New Versions of Tectonic 2 | 3 | 1. Announce intention to make a release. 4 | 2. Review outstanding pull requests and issues for showstoppers or easy wins. 5 | Address as many as possible. 6 | 3. Fetch from `tectonic-typesetting` and create a new branch `release-X.Y.Z` 7 | from `master`. 8 | 4. Update deps: `cargo update && cargo test --all`, then commit changes. 9 | 5. Check Arch Linux PKGBUILD file for updates and merge locally if necessary: 10 | ``` 11 | curl 'https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=tectonic' >dist/arch/PKGBUILD 12 | ``` 13 | 6. Consider updating the mdbook version used in `dist/build-mdbook.sh`; 14 | [releases listed here](https://github.com/rust-lang/mdBook/releases). 15 | 7. Backfill `CHANGELOG.md` from the Git history (which will include GitHub PR 16 | numbers, etc.) To view the history while skipping dependabot commits: 17 | ``` 18 | git log --perl-regexp --author='^((?!dependabot).*)$' v${CURRENT_VERSION}.. 19 | ``` 20 | 8. Update version number(s): 21 | - Main version in `Cargo.toml` 22 | - Un-bump versions of crates like `tectonic_xdv` if they haven't updated. 23 | - `cargo build --release` to update `Cargo.lock` as well. 24 | Commit with message `Tentative version $version` 25 | 9. Push to GitHub and create a pull request. 26 | 10. Examine the CI output. Fix any failures, committing any changes and 27 | re-pushing. No need to rewrite history at this juncture. 28 | 11. `mkdir ~/tectonictmp; mv .envrc target $other_files ~/tectonictmp ; git clean -fxd` 29 | 12. Attempt to `cargo publish`. Fix anything that needs fixing, committing 30 | changes. 31 | 13. Do a `git rebase -i` to rewrite the history to make the version-bump 32 | commit the most recent one, rewording its commit message to remove the 33 | "Tentative". Re-push and re-CI if anything changed that might affect the 34 | build. 35 | 14. Download the new package from 36 | . Put its 37 | sha512 sum into `dist/arch/PKGBULD` and update the package version and 38 | release in that file. Rewrite the version-bump commit to include the 39 | changes (`git commit --amend -C HEAD`). 40 | 15. `git tag v${version}` and, if necessary, `git tag submodule-v${version}`. 41 | 16. Back to development: 42 | - Bump main version in `Cargo.toml` and crates like `tectonic_xdv`. 43 | - `cargo build` to update the lockfile 44 | - Commit with message `Back to development, version $version`. 45 | 17. Re-push to the release PR branch and wait for CI. 46 | 18. Merge, assuming nothing bad happened with the CI. 47 | 19. Pull the new `master` locally to get GitHub’s merge commit. 48 | 20. `git push --tags`, which should trigger some automated release processes. 49 | Monitor them. 50 | 21. Create new release on GitHub, filling with contents of `CHANGELOG.md`. 51 | 22. Update 52 | [conda-forge package](https://github.com/conda-forge/tectonic-feedstock) — 53 | the bot should notice the new version on Crates.io and automatically file 54 | a pull request quickly. 55 | 23. Announce on forum. 56 | 24. Update website. 57 | 58 | 59 | ### To check 60 | 61 | - Do we need to take any further steps to release the AppImage? 62 | - Uploading artifacts like static binaries to the GitHub release 63 | - `cargo publish` from CI, not manually (need some way to test-publish first). 64 | -------------------------------------------------------------------------------- /bibtex/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tectonic_bibtex" 3 | version = "0.0.1-dev" 4 | authors = ["Peter Williams "] 5 | edition = "2018" 6 | 7 | [build-dependencies] 8 | cc = "^1.0" 9 | 10 | [dependencies] 11 | tectonic_bridge = { version = "0.0.1-dev", path = "../bridge" } 12 | libc = "0.2" 13 | lazy_static = "1" 14 | -------------------------------------------------------------------------------- /bridge/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tectonic_bridge" 3 | version = "0.0.1-dev" 4 | authors = ["Peter Williams "] 5 | edition = "2018" 6 | 7 | [build-dependencies] 8 | cc = "^1.0" 9 | 10 | [dependencies] 11 | libc = "0.2" 12 | -------------------------------------------------------------------------------- /bridge/build.rs: -------------------------------------------------------------------------------- 1 | // build.rs -- build helper script for Tectonic. 2 | // Copyright 2016-2019 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | fn main() { 6 | let mut stub_cfg = cc::Build::new(); 7 | 8 | stub_cfg 9 | .flag("-Wall") 10 | .file("stub/stub_errno.c") 11 | .include("."); 12 | 13 | stub_cfg.compile("tectonic_bridge"); 14 | } 15 | -------------------------------------------------------------------------------- /bridge/src/macro_stub.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! no_mangle_extern_fn { 3 | ($( 4 | $(#[$meta:meta])* 5 | $v:vis fn $symbol:ident => $extern_symbol:ident($($argname:ident: $argtype:ty),*) 6 | -> $rettype:ty; 7 | )*) => { 8 | extern "C" { 9 | $( 10 | //#[no_mangle] 11 | $(#[$meta])* 12 | fn $extern_symbol($($argname: $argtype),*) -> $rettype; 13 | )* 14 | } 15 | }; 16 | } 17 | #[macro_export] 18 | macro_rules! forward_stub_fn { 19 | ($( 20 | $(#[$meta:meta])* 21 | $v:vis fn $symbol:ident => $extern_symbol:ident($($argname:ident: $argtype:ty),*) 22 | -> $rettype:ty; 23 | )*) => { 24 | $( 25 | #[allow(unused_variables)] 26 | $(#[$meta])* 27 | $v unsafe fn $symbol($($argname: $argtype),*) -> $rettype { 28 | $extern_symbol($($argname),*) 29 | } 30 | )* 31 | }; 32 | } 33 | #[macro_export] 34 | macro_rules! extern_and_forward_stub { 35 | ($( 36 | $(#[$meta:meta])* 37 | $v:vis fn $symbol:ident => $extern_symbol:ident($($argname:ident: $argtype:ty),*) 38 | -> $rettype:ty; 39 | )*) => { 40 | no_mangle_extern_fn!($( 41 | $(#[$meta])* 42 | $v fn $symbol => $extern_symbol($($argname : $argtype),*) 43 | -> $rettype; 44 | )*); 45 | forward_stub_fn!($( 46 | $(#[$meta])* 47 | $v fn $symbol => $extern_symbol($($argname : $argtype),*) 48 | -> $rettype; 49 | )*); 50 | }; 51 | } 52 | -------------------------------------------------------------------------------- /bridge/src/stub_errno.rs: -------------------------------------------------------------------------------- 1 | extern_and_forward_stub! { 2 | pub fn errno => tt_errno() -> libc::c_int; 3 | pub fn set_errno => tt_set_errno(v: libc::c_int) -> (); 4 | } 5 | 6 | pub const ZERO: libc::c_int = 0; 7 | 8 | // FIXME: Are these same on all platforms? 9 | pub const EINTR: libc::c_int = 4; 10 | pub const ERANGE: libc::c_int = 34; 11 | -------------------------------------------------------------------------------- /bridge/stub/stub_errno.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* first pass to make c compiler happy */ 4 | 5 | int tt_errno(void); 6 | void tt_set_errno(int newval); 7 | 8 | /* stubs */ 9 | 10 | int tt_errno() { 11 | return errno; 12 | } 13 | 14 | void tt_set_errno(int newval) { 15 | errno = newval; 16 | } -------------------------------------------------------------------------------- /cfg_support/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2019-2020 the Tectonic Project 2 | # Licensed under the MIT License. 3 | 4 | [package] 5 | name = "tectonic_cfg_support" 6 | version = "0.0.3-dev" 7 | authors = [ 8 | "Matt Rice ", 9 | "Peter Williams " 10 | ] 11 | description = """ 12 | A build.rs support crate that helps deal with CARGO_CFG_TARGET_* variables. 13 | When cross-compiling, these variables must be used instead of constructs such 14 | as `cfg!(target_arch = ...)` because the build.rs script is compiled to 15 | target the build host environment, not the true target environment. 16 | """ 17 | homepage = "https://tectonic-typesetting.github.io/" 18 | documentation = "https://docs.rs/tectonic" 19 | repository = "https://github.com/tectonic-typesetting/tectonic/" 20 | readme = "README.md" 21 | license = "MIT" 22 | edition = "2018" 23 | 24 | [dependencies] 25 | lazy_static = "^1.4" 26 | -------------------------------------------------------------------------------- /cfg_support/LICENSE: -------------------------------------------------------------------------------- 1 | tectonic_cfg_support is licensed under the MIT License. 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the “Software”), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /cfg_support/README.md: -------------------------------------------------------------------------------- 1 | # The `tectonic_cfg_support` crate 2 | 3 | This crate helps `build.rs` files deal with `CARGO_CFG_TARGET_*` variables. When 4 | cross-compiling, these variables must be used instead of constructs such as 5 | `cfg!(target_arch = ...)` because the `build.rs` script is compiled to target 6 | the build host environment, not the true target environment. This crate is part 7 | of [the Tectonic project](https://tectonic-typesetting.github.io/). 8 | 9 | [![](http://meritbadge.herokuapp.com/tectonic_cfg_support)](https://crates.io/crates/tectonic_cfg_support) 10 | 11 | - [API documentation](https://docs.rs/tectonic_cfg_support/). 12 | - [Main Git repository](https://github.com/tectonic-typesetting/tectonic/). 13 | -------------------------------------------------------------------------------- /dist/appimage/.gitignore: -------------------------------------------------------------------------------- 1 | /*.AppImage 2 | /*.zsync 3 | 4 | -------------------------------------------------------------------------------- /dist/appimage/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2018 the Tectonic Project 3 | # Licensed under the MIT license 4 | 5 | # Create an AppImage of Tectonic. If the environment variable 6 | # $TECTONIC_APPIMAGE_TAG is set, the created file will have a name like 7 | # `tectonic-${TECTONIC_APPIMAGE_TAG}-$ARCH.AppImage`; otherwise the tag will 8 | # be the version of the Tectonic binary. You can also set $UPDATE_INFORMATION 9 | # to embed update info in the AppImage and cause the creation of a zsync file. 10 | # The format of the variable should be as described here: 11 | # https://github.com/AppImage/AppImageSpec/blob/master/draft.md#update-information 12 | # 13 | # This script is structured so that it can be run locally, but it's expected 14 | # that it will primarily be used inside CI. 15 | 16 | curl=curl 17 | 18 | workdir="$(cd $(dirname $0) && pwd)" # these paths needs to be absolute for linuxdeploy 19 | top="$(cd $workdir/../.. && pwd)" 20 | assets="$workdir" # this might change ... 21 | arch=$(uname -m) 22 | 23 | set -ex 24 | 25 | # Ensure we have an up-to-date release binary, and get a version tag if needed. 26 | 27 | cd "$top" 28 | cargo build --release 29 | 30 | if [ -z "$TECTONIC_APPIMAGE_TAG" ] ; then 31 | TECTONIC_APPIMAGE_TAG="$(cargo run --quiet --release -- --version |awk '{print $2}')" 32 | fi 33 | 34 | # Everything else is done here: 35 | 36 | cd "$workdir" 37 | 38 | # Pull down linuxdeploy if needed. 39 | 40 | linuxdeployapp=linuxdeploy-$arch.AppImage 41 | 42 | if [ ! -x $linuxdeployapp ] ; then 43 | $curl -fsSL https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/$linuxdeployapp >$linuxdeployapp 44 | chmod +x $linuxdeployapp 45 | fi 46 | 47 | # Now that we have linuxdeploy, let's actually make the thing. 48 | 49 | tectonicapp=Tectonic-$arch.AppImage 50 | appdir="$(mktemp -d)" 51 | 52 | OUTPUT="tectonic-$TECTONIC_APPIMAGE_TAG-$arch.AppImage" \ 53 | ./$linuxdeployapp \ 54 | --icon-file="$assets/tectonic.svg" \ 55 | --desktop-file="$assets/tectonic.desktop" \ 56 | --appdir="$appdir" \ 57 | --executable="$top/target/release/tectonic" \ 58 | --output=appimage 59 | 60 | rm -rf "$appdir" 61 | -------------------------------------------------------------------------------- /dist/appimage/tectonic.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Version=1.0 3 | Type=Application 4 | Name=Tectonic 5 | Comment=Tectonic is a modernized, complete, self-contained TeX/LaTeX engine, powered by XeTeX and TeXLive. 6 | Categories=ConsoleOnly;Office;Publishing;Science; 7 | TryExec=tectonic 8 | Exec=tectonic %F 9 | Icon=tectonic 10 | Terminal=true 11 | MimeType=image/svg+xml; 12 | 13 | -------------------------------------------------------------------------------- /dist/arch/PKGBUILD: -------------------------------------------------------------------------------- 1 | # Maintainer: tectonic-deploy 2 | # Maintainer: Daniel M. Capella 3 | # Contributor: Jan Tojnar 4 | 5 | # The master version of this file is maintained here: 6 | # 7 | # https://github.com/tectonic-typesetting/tectonic/blob/master/dist/arch/PKGBUILD 8 | # 9 | # The version on aur.archlinux.org is updated automatically through a Travis 10 | # CI deploy script that's invoked when new tags are pushed to the GitHub 11 | # tectonic repository. 12 | 13 | pkgname=tectonic 14 | pkgver=0.1.12 15 | pkgrel=0 16 | arch=('x86_64') 17 | pkgdesc='Modernized, complete, self-contained TeX/LaTeX engine, powered by XeTeX and TeXLive' 18 | url=https://tectonic-typesetting.github.io/ 19 | license=('MIT') 20 | depends=('fontconfig' 'harfbuzz-icu' 'openssl') 21 | makedepends=('rust') 22 | source=("$pkgname-$pkgver.tar.gz::https://crates.io/api/v1/crates/$pkgname/$pkgver/download") 23 | sha512sums=('837f38346d6b2c07d960d087f7eda1a0ca57735e18a71f956010c59bf9a45628ce40b4efc541f0b69cefe35261ffeb7d44e941448f5c550aeba3782f8b9d903b') 24 | 25 | build() { 26 | cd $pkgname-$pkgver 27 | cargo build --release --target-dir "./target" 28 | } 29 | 30 | check() { 31 | cd $pkgname-$pkgver 32 | cargo test --release --target-dir "./target" 33 | } 34 | 35 | package() { 36 | cd $pkgname-$pkgver 37 | install -Dm755 target/release/tectonic "$pkgdir"/usr/bin/tectonic 38 | install -Dm644 LICENSE "$pkgdir"/usr/share/licenses/$pkgname/LICENSE 39 | } 40 | -------------------------------------------------------------------------------- /dist/arch/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | cd "$TRAVIS_BUILD_DIR/dist/arch" 5 | 6 | # Set up to run makepkg 7 | wget https://www.archlinux.org/packages/core/x86_64/pacman/download/ -O pacman.pkg.tar.xz 8 | tar -Jxf pacman.pkg.tar.xz 9 | bindir="$(pwd)/usr/bin" 10 | export PATH="$bindir:$PATH" 11 | export LIBRARY="$(pwd)/usr/share/makepkg" 12 | config="$(pwd)/etc/makepkg.conf" 13 | 14 | # Get the repo 15 | git config --global --add core.sshCommand "ssh -o StrictHostKeyChecking=false -i /tmp/deploy_key" 16 | git clone ssh://aur@aur.archlinux.org/tectonic.git aur 17 | 18 | # Update it 19 | cp PKGBUILD aur 20 | cd aur 21 | /bin/bash "$bindir/makepkg" --config="$config" --printsrcinfo >.SRCINFO 22 | 23 | # Commit 24 | git add PKGBUILD .SRCINFO 25 | git config user.email "tectonic-deploy@example.com" 26 | git config user.name "tectonic-deploy" 27 | git commit -m "Release $TRAVIS_TAG" 28 | 29 | # Deploy to AUR 30 | git push origin master 31 | -------------------------------------------------------------------------------- /dist/build-mdbook.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | # A helper script to build a book built via the [mdbook] documentation 6 | # program. 7 | # 8 | # [mdbook]: https://rust-lang-nursery.github.io/mdBook/ 9 | # 10 | # Arguments: 11 | # 12 | # $1 - path to the mdbook directory in this project, relative to PWD 13 | 14 | set -e 15 | 16 | # Parameters 17 | 18 | src_path="$1" 19 | 20 | # Configuration that we expect to be stable. 21 | 22 | mdbook_version=0.3.5 23 | ci_platform=x86_64-unknown-linux-gnu 24 | mdbook_binary_fn="mdbook-v${mdbook_version}-${ci_platform}.tar.gz" 25 | mdbook_binary_url="https://github.com/rust-lang-nursery/mdBook/releases/download/v${mdbook_version}/${mdbook_binary_fn}" 26 | mdbook_binary_sha256=e03cc253650fa0b4780fab4d75df64c48d35d48f452fcf61c5ec0ae652f9bd8e 27 | 28 | # Get an mdbook executable. If we end up with multiple books to build, 29 | # this script might run multiple times, so avoid the work if possible. 30 | 31 | mdbook="$(pwd)/mdbook" 32 | 33 | if [ ! -x "$mdbook" ] ; then 34 | echo "Getting mdbook executable ..." 35 | wget -q --progress=dot "$mdbook_binary_url" 36 | echo "$mdbook_binary_sha256 $mdbook_binary_fn" |sha256sum -c 37 | tar xzf "$mdbook_binary_fn" 38 | rm -f "$mdbook_binary_fn" 39 | fi 40 | 41 | # Build the book. 42 | 43 | echo "Building book ..." 44 | pushd "$src_path" 45 | "$mdbook" build 46 | "$mdbook" test 47 | popd 48 | 49 | # And that's it. 50 | 51 | echo "Success." 52 | -------------------------------------------------------------------------------- /dist/deploy_key.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/dist/deploy_key.enc -------------------------------------------------------------------------------- /dist/deploy_key.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC7wVMCqB1/4opOJ4vrN/t9FMc+J32XqeqaPft2iSajqB4vpUuPzA5TwFsWWbOr6kKfpy2tHD47VLnHqTFJI6GiDNvCWTHm6pigk3UwB2jucM7fNytRewbRs8NL4mv5licxgszw8nifp8wZTyHPmMIrguROwuR7/WGefX/baTMmS9P7ykO5mmta4fPN+cYaU53648zZLyeSK5LV7pmKwjjosvC0Z2RYxBAQxb9Rfd2lhYgmKaHE7bYlBbazMA2Gn+NLKBVTwn4H/s/e5tsKCB8dhMfRrvZzhFimJ/U6bOFS6dB+EfjdE3TMKZbgjiSDiqESm14lFGSqS0DnrsNOmwEN peter@lambda.localdomain 2 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/Dockerfile: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2019 The Tectonic Project 2 | # Licensed under the MIT License. 3 | 4 | FROM ubuntu:18.04 5 | 6 | ARG uid=1000 7 | 8 | ADD setup_priv.sh / 9 | RUN sh /setup_priv.sh 10 | 11 | ADD sudoers /etc/sudoers.d/nopasswd 12 | USER rust 13 | ENV PATH=/alpine/home/rust/.cargo/bin:/usr/sbin:/usr/bin:/sbin:/bin 14 | 15 | ADD setup_unpriv.sh / 16 | ADD toolwrapper.sh /alpine/home/rust/ 17 | ADD linkwrapper.sh /alpine/home/rust/ 18 | RUN sh /setup_unpriv.sh 19 | 20 | # With the following customizations, we can just "docker run -v 21 | # $tectonic:/alpine/home/rust/src $image cargo ..." and The Right Thing should 22 | # happen: 23 | 24 | ADD cargo-config.toml /alpine/home/rust/.cargo/config 25 | ENV RUSTFLAGS="-L /alpine/usr/lib -l static=expat -l static=bz2 -l static=uuid -l static=stdc++ -C target-feature=+crt-static" 26 | ENV TARGET_CC="x86_64-unknown-linux-musl-cc" 27 | ENV TARGET_CXX="x86_64-unknown-linux-musl-g++" 28 | ENV PKG_CONFIG_ALLOW_CROSS=1 29 | 30 | WORKDIR /alpine/home/rust/src 31 | CMD ["cargo", "test", "--release", "--all"] 32 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/cargo-config.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | target = "x86_64-unknown-linux-musl" 3 | 4 | [target.x86_64-unknown-linux-musl] 5 | linker = "/alpine/home/rust/linkwrapper.sh" 6 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/linkwrapper.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | # This script derived from GitHub user @dl00: 6 | # 7 | 8 | platform="x86_64-unknown-linux-musl" 9 | 10 | gcclibdir="$(echo /alpine/usr/lib/gcc/*/*/crtbeginT.o)" 11 | gcclibdir="${gcclibdir%/crtbeginT.o}" 12 | 13 | args=() 14 | 15 | for arg in "$@"; do 16 | if [[ $arg = *"Bdynamic"* ]]; then 17 | true # we do not want this arg 18 | elif [[ $arg = *"crti.o"* ]]; then 19 | args+=("$arg" "$gcclibdir/crtbeginT.o" "-Bstatic") 20 | elif [[ $arg = *"crtn.o"* ]]; then 21 | args+=("-lgcc" "-lgcc_eh" "-lc" "$gcclibdir/crtend.o" "$arg") 22 | else 23 | args+=("$arg") 24 | fi 25 | done 26 | 27 | exec $platform-g++ "${args[@]}" 28 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/setup_priv.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | set -ex 6 | 7 | alpine_pkgs="\ 8 | g++ \ 9 | fontconfig-dev \ 10 | freetype-static \ 11 | glib-static \ 12 | graphite2-dev \ 13 | graphite2-static \ 14 | harfbuzz-dev \ 15 | harfbuzz-static \ 16 | icu-dev \ 17 | icu-static \ 18 | libpng-static \ 19 | openssl-dev \ 20 | zlib-dev \ 21 | " 22 | 23 | cd / 24 | 25 | export TERM=dumb 26 | apt-get update 27 | # use curl for rust's installer so it is quieter 28 | # needed until we can configure the options passed to the installer 29 | # https://github.com/rust-lang/rustup.rs/issues/1928 30 | apt-get install -y \ 31 | build-essential \ 32 | sudo \ 33 | wget \ 34 | curl 35 | apt-get clean 36 | rm -rf /var/lib/apt/lists/* 37 | 38 | wget -q --progress=dot https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.10.0/alpine-chroot-install 39 | echo 'dcceb34aa63767579f533a7f2e733c4d662b0d1b alpine-chroot-install' |sha1sum -c 40 | 41 | # This command will error out when it attempts to bind-mount things like /proc 42 | # because that is not allowed inside an unprivileged Docker container. 43 | # Fortunately we can get away without making those mounts, so we can just 44 | # ignore the error. Unfortunately this means that we'll continue on blithely 45 | # if some other error occurs ... 46 | sh alpine-chroot-install -b v3.9 -m "http://dl-cdn.alpinelinux.org/alpine/" || true 47 | rm -f alpine-chroot-install 48 | 49 | /alpine/enter-chroot sh -c "apk update && apk add $alpine_pkgs" 50 | 51 | useradd rust --user-group --create-home --home-dir /alpine/home/rust \ 52 | --shell /bin/bash --groups sudo --uid $uid 53 | /alpine/enter-chroot sh -c "adduser -h /home/rust -H -D rust -u $uid" 54 | 55 | rm -f "$0" # self-destruct 56 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/setup_unpriv.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | set -ex 6 | 7 | platform="x86_64-unknown-linux-musl" 8 | 9 | cd 10 | 11 | wget -q --progress=dot -O rustup.sh https://sh.rustup.rs 12 | sh rustup.sh -y --default-toolchain stable 13 | rm -f rustup.sh 14 | 15 | rustup target add $platform 16 | 17 | sudo chmod +x $HOME/*.sh 18 | 19 | for tool in cc g++ ld pkg-config ; do 20 | ln -s ../../toolwrapper.sh $HOME/.cargo/bin/$platform-$tool 21 | done 22 | 23 | cat <$HOME/.cargo/bin/pkg-config 24 | #! /bin/bash 25 | exec $platform-pkg-config "\$@" 26 | EOF 27 | chmod +x $HOME/.cargo/bin/pkg-config 28 | 29 | sudo rm -f "$0" # self-destruct 30 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/sudoers: -------------------------------------------------------------------------------- 1 | # Allow group sudo to use `sudo` without a password. 2 | %sudo ALL=(ALL:ALL) NOPASSWD:ALL 3 | -------------------------------------------------------------------------------- /dist/docker/x86_64-alpine-linux-musl/toolwrapper.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | # Wrap toolchain invocations to happen inside the Alpine chroot. 6 | 7 | platform="x86_64-unknown-linux-musl" 8 | 9 | # $0 10 | 11 | exename="$(basename $0)" 12 | 13 | if [[ $exename != $platform-* ]] ; then 14 | echo >&2 "error: executable name ($exename) must start with \"$platform-\"" 15 | exit 1 16 | fi 17 | 18 | exename="${exename#$platform-}" 19 | 20 | # working directory 21 | 22 | curdir=$(pwd) 23 | 24 | if [[ $curdir != /alpine/* ]] ; then 25 | echo >&2 "error: working directory must be inside /alpine/ prefix" 26 | exit 1 27 | fi 28 | 29 | curdir="${curdir#/alpine}" 30 | 31 | # args. We use printf to avoid "echo" eating flag args like "-n", and a global 32 | # sed substitution in case a single arg contains multiple paths (could happen 33 | # with something like -Wl,foo,bar,baz). 34 | 35 | args=() 36 | 37 | for arg in "$@"; do 38 | args+=("$(printf %s "$arg" |sed -e s@/alpine@@g)") 39 | done 40 | 41 | # ready to go! 42 | 43 | ### set -x # <= for debugging 44 | exec /alpine/enter-chroot -u rust sh -c "cd $curdir && \"\$@\"" -- "$exename" "${args[@]}" 45 | -------------------------------------------------------------------------------- /dist/force-push-tree.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | # Copyright 2019 The Tectonic Project 3 | # Licensed under the MIT License. 4 | 5 | # A helper script to deploy a tree of files to another Git repository by 6 | # copying them, amend-committing, and force-pushing. The main use case for 7 | # this script is to deploy generated HTML trees to make them available using 8 | # GitHub pages. 9 | # 10 | # Arguments: 11 | # 12 | # $1 - path to the file tree to be deployed 13 | # $2 - HTTPS URL of the GitHub repository that will receive the files 14 | # $3 - path within the destination Git repository where the tree will land 15 | # $4 - brief free text identifying the commit/version of what's being deployed 16 | # (used in the Git logs) 17 | # 18 | # Environment: 19 | # 20 | # $GITHUB_TOKEN - a bearer-token authentication token that can be used to 21 | # authenticate writes to the repository specified by $2. 22 | # 23 | # See the code for how the token authentication is set up -- it is somewhat 24 | # magical. 25 | 26 | set -e 27 | 28 | # Parameters 29 | 30 | src_path="$1" 31 | dest_repo_url="$2" 32 | dest_repo_path="$3" 33 | commit_desc="$4" 34 | 35 | # Configuration that we expect to be stable. 36 | 37 | git_user_email="notifications@github.com" 38 | git_user_name="Tectonic CI" 39 | 40 | # Set up Git and authentication. 41 | # Derived from: https://www.appveyor.com/docs/how-to/git-push/ 42 | 43 | echo "Setting up Git ..." 44 | git config --global credential.helper store 45 | echo "https://$GITHUB_TOKEN:x-oauth-basic@github.com" >~/.git-credentials 46 | git config --global user.email "$git_user_email" 47 | git config --global user.name "$git_user_name" 48 | 49 | # Set up the target repo. 50 | 51 | echo "Cloning target repository $dest_repo_url ..." 52 | tmprepo="$(mktemp -d)" 53 | git clone "$dest_repo_url" "$tmprepo" 54 | mkdir -p "$tmprepo/$dest_repo_path" 55 | rm -rf "$tmprepo/$dest_repo_path" 56 | 57 | # Update the HTML and commit. 58 | 59 | echo "Committing and pushing changes ..." 60 | cp -a "$src_path" "$tmprepo/$dest_repo_path" 61 | 62 | pushd "$tmprepo" 63 | git add "$dest_repo_path" 64 | git commit --amend -m "Most recent update: $dest_repo_path - $commit_desc" 65 | git push -f 66 | popd 67 | 68 | # And that's it. 69 | echo "Success." 70 | -------------------------------------------------------------------------------- /dist/prepare-win-ci-deps.bat: -------------------------------------------------------------------------------- 1 | rem Copyright 2019 the Tectonic Project 2 | rem Licensed under the MIT License. 3 | rem 4 | rem This script installs vcpkg on windows os at %VCPKG_ROOT% directory 5 | 6 | if not exist %VCPKG_ROOT% mkdir %VCPKG_ROOT% 7 | cd %VCPKG_ROOT% 8 | git init 9 | git remote add origin https://github.com/crlf0710/tectonic-prebuilt-deps.git 10 | git fetch origin master 11 | git checkout -f -B master origin/master 12 | FOR /D %%G in ("packages\*") DO xcopy /e /f /y "%%~G\*" "installed\%VCPKG_DEFAULT_TRIPLET%\" 13 | if not exist installed\vcpkg\updates mkdir installed\vcpkg\updates 14 | -------------------------------------------------------------------------------- /docs/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | title = "The Tectonic Typesetting System" 3 | authors = ["Tectonic Contributors"] 4 | description = "A manual for the Tectonic typesetting system" 5 | language = "en" 6 | multilingual = false 7 | src = "src" 8 | 9 | [output.html] 10 | # see lively issue: https://github.com/rust-lang-nursery/mdBook/issues/847 11 | google-analytics = false 12 | # FIXME these do not seem to appear anywhere? 13 | git-repository-url = "https://github.com/tectonic-typesetting/tectonic" 14 | git-repository-icon = "fa-github" 15 | -------------------------------------------------------------------------------- /docs/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # The Tectonic Typesetting System 2 | 3 | [Overview](index.md) 4 | 5 | - [Introduction](introduction/index.md) 6 | - [Getting Started](getting-started/index.md) 7 | - [Tectonic’s Command Line Interface](cli/index.md) 8 | - [The Tectonic Implementation of the TeX Language](lang-impl/index.md) 9 | - [Cookbook](cookbook/index.md) 10 | * [build tectonic with vcpkg on macOS](cookbook/vcpkg.md) 11 | -------------------------------------------------------------------------------- /docs/src/cli/index.md: -------------------------------------------------------------------------------- 1 | # Tectonic's Command Line Interface 2 | 3 | Tectonic has many command line options. 4 | If you have Tectonic installed, you can view them with `tectonic --help`. 5 | 6 | The following are the available flags. 7 | 8 | | Short | Full | Explanation | 9 | |:------|:--------------------------|:-----------------------------------------------------------------------------------------------| 10 | | `-h` | `--help` | Prints help information | 11 | | `-k` | `--keep-intermediates` | Keep the intermediate files generated during processing | 12 | | | `--keep-logs` | Keep the log files generated during processing | 13 | | `-C` | `--only-cached` | Use only resource files cached locally | 14 | | `-p` | `--print` | Print the engine's chatter during processing | 15 | | | `--synctex` | Generate SyncTeX data | 16 | | `-V` | `--version` | Prints version information | 17 | 18 | The following are the available options. 19 | 20 | | Short | Full | Explanation | 21 | |:------|:--------------------------|:-----------------------------------------------------------------------------------------------| 22 | | `-b` | `--bundle ` | Use this Zip-format bundle file to find resource files instead of the default | 23 | | `-c` | `--chatter ` | How much chatter to print when running [default: default] [possible values: default, minimal] | 24 | | | `--format ` | The name of the "format" file used to initialize the TeX engine [default: latex] | 25 | | | `--hide ...` | Tell the engine that no file at exists, if it tries to read it | 26 | | | `--makefile-rules ` | Write Makefile-format rules expressing the dependencies of this run to | 27 | | `-o` | `--outdir ` | The directory in which to place output files [default: the directory containing INPUT] | 28 | | | `--outfmt ` | The kind of output to generate [default: pdf] [possible values: pdf, html, xdv, aux, format] | 29 | | | `--pass ` | Which engines to run [default: default] [possible values: default, tex, bibtex_first] | 30 | | `-r` | `--reruns ` | Rerun the TeX engine exactly this many times after the first | 31 | | `-w` | `--web-bundle ` | Use this URL find resource files instead of the default | 32 | 33 | If you specify a path, make sure to put it into brackets when it contains spaces. 34 | 35 | Then, after specifying flags and options, you have to provide the actual input to tectonic. 36 | This can be a (path to a) file, or "-" to process the standard input stream. 37 | 38 | In short, the usage is `tectonic [FLAGS] [OPTIONS] `. 39 | 40 | An example would be `tectonic --synctex --reruns 0 -o ../out/ main.tex`. -------------------------------------------------------------------------------- /docs/src/cookbook/index.md: -------------------------------------------------------------------------------- 1 | # Cookbook 2 | 3 | This section is a miscellaneous collection of tutorials and tips for working with and installing Tectonic. 4 | -------------------------------------------------------------------------------- /docs/src/cookbook/vcpkg.md: -------------------------------------------------------------------------------- 1 | # build tectonic with vcpkg on macOS 2 | 3 | This tutorial will walk you through how to build a _mostly_ staticly linked 4 | `tectonic` binary on macOS. Why does this matter? Static binaries are more 5 | portable and so free you from having to support complex user environments. 6 | Thanks to 7 | [mcgoo's work](https://github.com/tectonic-typesetting/tectonic/pull/420) we can 8 | use `vcpkg`. [vcpkg](https://vcpkg.readthedocs.io/en/latest/) is a C/C++ package 9 | manager from Microsoft. 10 | 11 | ## prerequisites 12 | 13 | This guide assumes you have [git](https://git-scm.com/) installed and are 14 | comfortable using the command-line. You'll also need to install 15 | [homebrew](https://brew.sh/) to install for `vcpkg`'s gcc dependency. Package 16 | management is _fun_. 17 | 18 | 1. install `gcc`: 19 | 20 | ```sh 21 | brew install gcc 22 | ``` 23 | 24 | ## setting up your environment 25 | 26 | 1. Install vcpkg 27 | 28 | 1. Clone the vcpkg repository: 29 | ```sh 30 | git clone https://github.com/Microsoft/vcpkg 31 | ``` 32 | 2. Install it for your system: 33 | ```sh 34 | ./bootstrap-vcpkg.sh 35 | ``` 36 | 37 | 2. Install `tectonic` dependencies using vcpkg. \* Run the following command in 38 | your checkout of the `vcpkg` repository 39 | `sh ./vcpkg install freetype harfbuzz\[icu,graphite2\]` It should print 40 | something like the following: 41 | 42 | ```sh 43 | $ ./vcpkg install freetype harfbuzz\[icu,graphite2\] 44 | The following packages will be built and installed: 45 | * bzip2[core]:x64-osx 46 | freetype[core]:x64-osx 47 | * gettext[core]:x64-osx 48 | * graphite2[core]:x64-osx 49 | harfbuzz[core,graphite2,icu,ucdn]:x64-osx 50 | * icu[core]:x64-osx 51 | * libiconv[core]:x64-osx 52 | * libpng[core]:x64-osx 53 | * ragel[core]:x64-osx 54 | * zlib[core]:x64-osx 55 | Additional packages (*) will be modified to complete this operation. 56 | Starting package 1/10: graphite2:x64-osx 57 | Building package graphite2[core]:x64-osx... 58 | ``` 59 | 60 | ## build tectonic 61 | 62 | Now we only need to configure tectonic so it knows we are using `vcpkg` to build 63 | the binary. 64 | 65 | 1. Run `cargo build` with the appropriate environment variables: 66 | ```sh 67 | TECTONIC_DEP_BACKEND="vcpkg" VCPKG_ROOT=/Users/me/vcpkg/ cargo build --release 68 | ``` 69 | 70 | You'll need to set `VCPKG_ROOT` to the full path of your vcpkg checkout. 71 | `TECTONIC_DEP_BACKEND="vcpkg"` tells `tectonic` to use `vcpkg` instead of trying 72 | to resolve the libraries using `pkgconfig`. `VCPKG_ROOT=/Users/me/vcpkg/` is the 73 | root of the `vcpkg` tree where we just installed the required libraries. 74 | 75 | This will take a couple minutes but should eventually print something like: 76 | 77 | ```sh 78 | Finished release [optimized] target(s) in 3m 39s 79 | ``` 80 | 81 | Congratulations! You should now have a mostly staticly linked binary in 82 | `target/release` suitable for sharing with users or whatever your needs are. 83 | 84 | ## caveats 85 | 86 | I say _mostly_ staticly linked because the binary itself presumes system 87 | libraries. You can observe those links by executing `otool` on a given binary: 88 | 89 | ```sh 90 | otool -L target/release/tectonic 91 | ``` 92 | 93 | In my case it printed the following: 94 | 95 | ```sh 96 | target/release/tectonic: 97 | /System/Library/Frameworks/Foundation.framework/Versions/C/Foundation (compatibility version 300.0.0, current version 1454.98.0) 98 | /System/Library/Frameworks/CoreFoundation.framework/Versions/A/CoreFoundation (compatibility version 150.0.0, current version 1454.98.0) 99 | /System/Library/Frameworks/CoreGraphics.framework/Versions/A/CoreGraphics (compatibility version 64.0.0, current version 1161.21.2) 100 | /System/Library/Frameworks/CoreText.framework/Versions/A/CoreText (compatibility version 1.0.0, current version 1.0.0) 101 | /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (compatibility version 45.0.0, current version 1561.60.100) 102 | /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.0) 103 | /System/Library/Frameworks/Security.framework/Versions/A/Security (compatibility version 1.0.0, current version 58286.70.14) 104 | /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.50.4) 105 | /usr/lib/libresolv.9.dylib (compatibility version 1.0.0, current version 1.0.0) 106 | /usr/lib/libobjc.A.dylib (compatibility version 1.0.0, current version 228.0.0) 107 | ``` 108 | -------------------------------------------------------------------------------- /docs/src/getting-started/index.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | TODO: installation; quick demonstration of how Tectonic is different from traditional TeX engines. 4 | -------------------------------------------------------------------------------- /docs/src/index.md: -------------------------------------------------------------------------------- 1 | # The Tectonic Typesetting System 2 | 3 | Tectonic is a modernized, complete, self-contained 4 | [TeX](https://en.wikipedia.org/wiki/TeX)/[LaTeX](https://www.latex-project.org/) 5 | engine, powered by [XeTeX](http://xetex.sourceforge.net/) and 6 | [TeXLive](https://www.tug.org/texlive/). This book aims to document the core 7 | elements of the Tectonic software system. 8 | 9 | Without further ado, we suggest you start with the [Introduction]! 10 | 11 | [Introduction]: ./introduction/index.md 12 | 13 | 14 | ## Contributions are welcome! 15 | 16 | This book is, clearly, a work in progress! Contributions of any kind are most 17 | welcome — please see the discussion in 18 | [GitHub issue #62](https://github.com/tectonic-typesetting/tectonic/issues/62) 19 | for some ideas of things that should be documented here. 20 | 21 | The documentation is written in [Markdown] (specifically, CommonMark using 22 | [pulldown-cmark]) and rendered into HTML using [mdbook]. The source code lives 23 | in the `docs/` subdirectory of [the main tectonic repository]. To make and view 24 | changes, all you need to do is [install mdbook], then run the command: 25 | 26 | ```sh 27 | $ mdbook serve 28 | ``` 29 | 30 | in the `docs/` directory. 31 | 32 | [Markdown]: https://commonmark.org/ 33 | [pulldown-cmark]: https://crates.io/crates/pulldown-cmark 34 | [mdbook]: https://rust-lang-nursery.github.io/mdBook/ 35 | [the main tectonic repository]: https://github.com/tectonic-typesetting/tectonic 36 | [install mdbook]: https://github.com/rust-lang-nursery/mdBook#installation 37 | 38 | Members of the Tectonic community are eager to help if you run into any issues — 39 | please launch a thread on [the Tectonic forum] if you’d like to get involved! 40 | 41 | [the Tectonic forum]: https://tectonic.newton.cx/ 42 | -------------------------------------------------------------------------------- /docs/src/lang-impl/index.md: -------------------------------------------------------------------------------- 1 | # The Tectonic Implementation of the TeX Language 2 | 3 | TODO: links to TeX language guides 4 | 5 | TODO: document customizations, e.g. `\TectonicCodaTokens`. 6 | -------------------------------------------------------------------------------- /dpx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tectonic_dvipdfmx" 3 | version = "0.0.1-dev" 4 | authors = ["Peter Williams "] 5 | edition = "2018" 6 | license = "GPL" 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | tectonic_bridge = { version = "0.0.1-dev", path = "../bridge" } 11 | libc = "0.2" 12 | libpng-sys = "1.1.8" 13 | libz-sys = { version = "1", optional = true} 14 | md-5 = "0.8.0" 15 | sha2 = "0.8.0" 16 | rand = "0.7.2" 17 | chrono = "0.4.9" 18 | euclid = "0.20" 19 | indexmap = "1.3.0" 20 | png = "0.16" 21 | itoa = "0.4.6" 22 | once_cell = "1.5.2" 23 | arrayvec = "0.5" 24 | bitflags = "1.2.1" 25 | phf = { version = "0.8.0", features = ["macros"] } 26 | 27 | [features] 28 | default = ['libz-sys'] 29 | legacy-libz = ['libz-sys'] 30 | 31 | [build-dependencies] 32 | cc = "^1.0" 33 | -------------------------------------------------------------------------------- /dpx/LICENSE: -------------------------------------------------------------------------------- 1 | DVIPDFMx, an eXtended version of DVIPDFM by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho, Matthias Franz, and Shunsaku Hirata, 4 | the DVIPDFMx project team. 5 | 6 | Copyright (c) 2006 SIL. (xdvipdfmx extensions for XeTeX support) 7 | 8 | Copyright (C) 1998, 1999 by Mark A. Wicks 9 | 10 | This program is free software; you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation; either version 2 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program; if not, write to the Free Software 22 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 23 | -------------------------------------------------------------------------------- /dpx/build.rs: -------------------------------------------------------------------------------- 1 | // build.rs -- build helper script for Tectonic. 2 | // Copyright 2016-2019 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | /// The Tectonic build script. Not only do we have internal C/C++ code, we 6 | /// also depend on several external C/C++ libraries, so there's a lot to do 7 | /// here. It would be great to streamline things. 8 | /// 9 | /// TODO: this surely needs to become much smarter and more flexible. 10 | use std::env; 11 | use std::path::PathBuf; 12 | 13 | fn main() { 14 | let target = env::var("TARGET").unwrap(); 15 | let mut ccfg = cc::Build::new(); 16 | let cflags = [ 17 | "-Wall", 18 | "-Wcast-qual", 19 | "-Wdate-time", 20 | "-Wendif-labels", 21 | "-Wextra", 22 | "-Wextra-semi", 23 | "-Wformat=2", 24 | "-Winit-self", 25 | "-Wlogical-op", 26 | "-Wmissing-declarations", 27 | "-Wmissing-include-dirs", 28 | "-Wmissing-prototypes", 29 | "-Wmissing-variable-declarations", 30 | "-Wnested-externs", 31 | "-Wold-style-definition", 32 | "-Wpointer-arith", 33 | "-Wredundant-decls", 34 | "-Wstrict-prototypes", 35 | "-Wswitch-bool", 36 | "-Wundef", 37 | "-Wwrite-strings", 38 | // TODO: Fix existing warnings before enabling these: 39 | // "-Wbad-function-cast", 40 | // "-Wcast-align", 41 | // "-Wconversion", 42 | // "-Wdouble-promotion", 43 | // "-Wshadow", 44 | // "-Wsuggest-attribute=format", 45 | // "-Wsuggest-attribute=const", 46 | // "-Wsuggest-attribute=noreturn", 47 | // "-Wsuggest-attribute=pure", 48 | // "-Wunreachable-code-aggresive", 49 | "-Wno-unused-parameter", 50 | "-Wno-implicit-fallthrough", 51 | "-Wno-sign-compare", 52 | "-std=gnu11", 53 | ]; 54 | 55 | for flag in &cflags { 56 | ccfg.flag_if_supported(flag); 57 | } 58 | 59 | ccfg.flag("-Wall").file("shims/dpx_shims.c").include("."); 60 | 61 | if target.contains("-msvc") { 62 | ccfg.flag("/EHsc"); 63 | } 64 | 65 | // OK, back to generic build rules. 66 | ccfg.compile("dpx_shims.a"); 67 | 68 | for file in PathBuf::from("shims").read_dir().unwrap() { 69 | let file = file.unwrap(); 70 | println!("cargo:rerun-if-changed={}", file.path().display()); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /dpx/shims/dpx_shims.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int dpx_sprintf ( char * str, const char * format, ... ); 6 | 7 | // shims 8 | 9 | int dpx_sprintf(char *str, const char *format, ...) 10 | { 11 | va_list arglist; 12 | int r; 13 | va_start(arglist, format); 14 | r = vsprintf(str, format, arglist); 15 | va_end(arglist); 16 | return r; 17 | } 18 | 19 | #ifdef _MSC_VER 20 | 21 | int dpx_win32_mktemp_s(char *nameTemplate, 22 | size_t sizeInChars); 23 | 24 | int dpx_win32_mktemp_s(char *nameTemplate, 25 | size_t sizeInChars) 26 | { 27 | return _mktemp_s(nameTemplate, sizeInChars); 28 | } 29 | 30 | #endif 31 | -------------------------------------------------------------------------------- /dpx/src/dpx_dpxconf.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow( 23 | mutable_transmutes, 24 | non_camel_case_types, 25 | non_snake_case, 26 | non_upper_case_globals 27 | )] 28 | 29 | pub(crate) type __off_t = i64; 30 | pub(crate) type __off64_t = i64; 31 | #[derive(Copy, Clone)] 32 | #[repr(C)] 33 | pub(crate) struct paper<'a> { 34 | pub(crate) name: &'a [u8], 35 | pub(crate) pswidth: f64, 36 | pub(crate) psheight: f64, 37 | } 38 | 39 | pub(crate) static mut paperspecs: [paper; 21] = [ 40 | paper { 41 | name: b"letter", 42 | pswidth: 612., 43 | psheight: 792., 44 | }, 45 | paper { 46 | name: b"legal", 47 | pswidth: 612., 48 | psheight: 1008., 49 | }, 50 | paper { 51 | name: b"ledger", 52 | pswidth: 1224., 53 | psheight: 792., 54 | }, 55 | paper { 56 | name: b"tabloid", 57 | pswidth: 792., 58 | psheight: 1224., 59 | }, 60 | paper { 61 | name: b"a6", 62 | pswidth: 297.638, 63 | psheight: 419.528, 64 | }, 65 | paper { 66 | name: b"a5", 67 | pswidth: 419.528, 68 | psheight: 595.276, 69 | }, 70 | paper { 71 | name: b"a4", 72 | pswidth: 595.276, 73 | psheight: 841.89, 74 | }, 75 | paper { 76 | name: b"a3", 77 | pswidth: 841.89, 78 | psheight: 1190.55, 79 | }, 80 | paper { 81 | name: b"b6", 82 | pswidth: 364.25, 83 | psheight: 515.91, 84 | }, 85 | paper { 86 | name: b"b5", 87 | pswidth: 515.91, 88 | psheight: 728.5, 89 | }, 90 | paper { 91 | name: b"b4", 92 | pswidth: 728.5, 93 | psheight: 1031.81, 94 | }, 95 | paper { 96 | name: b"b3", 97 | pswidth: 1031.81, 98 | psheight: 1457., 99 | }, 100 | paper { 101 | name: b"b5var", 102 | pswidth: 515.91, 103 | psheight: 651.97, 104 | }, 105 | paper { 106 | name: b"jisb6", 107 | pswidth: 364.25, 108 | psheight: 515.91, 109 | }, 110 | paper { 111 | name: b"jisb5", 112 | pswidth: 515.91, 113 | psheight: 728.5, 114 | }, 115 | paper { 116 | name: b"jisb4", 117 | pswidth: 728.5, 118 | psheight: 1031.81, 119 | }, 120 | paper { 121 | name: b"jisb3", 122 | pswidth: 1031.81, 123 | psheight: 1457., 124 | }, 125 | paper { 126 | name: b"isob6", 127 | pswidth: 354.331, 128 | psheight: 498.898, 129 | }, 130 | paper { 131 | name: b"isob5", 132 | pswidth: 498.898, 133 | psheight: 708.661, 134 | }, 135 | paper { 136 | name: b"isob4", 137 | pswidth: 708.661, 138 | psheight: 1000.63, 139 | }, 140 | paper { 141 | name: b"isob3", 142 | pswidth: 1000.63, 143 | psheight: 1417.32, 144 | }, 145 | ]; 146 | 147 | pub(crate) unsafe fn paperinfo(ppformat: &[u8]) -> Option<*const paper> { 148 | if ppformat.is_empty() { 149 | return None; 150 | } 151 | for ppinfo in &paperspecs { 152 | if ppformat == ppinfo.name { 153 | return Some(ppinfo as *const paper); 154 | } 155 | } 156 | None 157 | } 158 | /* HAVE_LIBPAPER */ 159 | /* HAVE_LIBPAPER */ 160 | /* 161 | pub(crate) unsafe fn dumppaperinfo() { 162 | for ppinfo in &paperspecs { 163 | let wd = ppinfo.pswidth; 164 | let ht = ppinfo.psheight; 165 | println!( 166 | "{}: {:.2} {:.2} ({:.2}mm {:.2}mm)", 167 | ppinfo.display(), 168 | wd, 169 | ht, 170 | 25.4 * wd / 72., 171 | 25.4 * ht / 72., 172 | ); 173 | } 174 | }*/ 175 | -------------------------------------------------------------------------------- /dpx/src/dpx_error.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow( 23 | mutable_transmutes, 24 | non_camel_case_types, 25 | non_snake_case, 26 | non_upper_case_globals 27 | )] 28 | 29 | pub(crate) type Result = std::result::Result; 30 | pub(crate) const ERR1: Result<()> = Err(unsafe { std::num::NonZeroI32::new_unchecked(1) }); 31 | pub(crate) const ERR: Result<()> = Err(unsafe { std::num::NonZeroI32::new_unchecked(-1) }); 32 | pub(crate) const fn ERROR() -> Result { 33 | Err(unsafe { std::num::NonZeroI32::new_unchecked(-1) }) 34 | } 35 | 36 | use crate::bridge::ttstub_output_open_stdout; 37 | 38 | use bridge::OutputHandleWrapper; 39 | pub(crate) type message_type_t = _message_type; 40 | pub(crate) type _message_type = u32; 41 | pub(crate) const DPX_MESG_WARN: _message_type = 1; 42 | pub(crate) const DPX_MESG_INFO: _message_type = 0; 43 | pub(crate) static mut _last_message_type: message_type_t = DPX_MESG_INFO; 44 | pub(crate) static mut _dpx_quietness: i32 = 0; 45 | 46 | pub(crate) unsafe fn shut_up(quietness: i32) { 47 | _dpx_quietness = quietness; 48 | } 49 | pub(crate) static mut _dpx_message_handle: Option = None; 50 | 51 | static mut _dpx_message_buf: [u8; 1024] = [0; 1024]; 52 | pub(crate) fn _dpx_ensure_output_handle() { 53 | if let Some(handle) = unsafe { ttstub_output_open_stdout() } { 54 | unsafe { 55 | _dpx_message_handle = Some(handle); 56 | } 57 | } else { 58 | panic!("xdvipdfmx cannot get output logging handle?!"); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /dpx/src/dpx_mfileio.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow( 23 | mutable_transmutes, 24 | non_camel_case_types, 25 | non_snake_case, 26 | non_upper_case_globals 27 | )] 28 | 29 | use crate::bridge::ReadByte; 30 | use std::io::{Read, Seek, SeekFrom}; 31 | 32 | use std::ptr; 33 | 34 | /* Note: this is really just a random array used in other files. */ 35 | pub(crate) static mut work_buffer_u8: [u8; 1024] = [0; 1024]; 36 | /* Tectonic-enabled versions */ 37 | /* Modified versions of the above functions based on the Tectonic I/O system. */ 38 | 39 | pub(crate) unsafe fn tt_mfgets( 40 | buffer: *mut i8, 41 | length: usize, 42 | file: &mut R, 43 | ) -> *mut i8 { 44 | let mut ch = Some(0); 45 | let mut i = 0; 46 | while i < length - 1 { 47 | ch = file.read_byte(); 48 | if let Some(ch) = ch.filter(|&c| c != b'\n' && c != b'\r') { 49 | *buffer.add(i) = ch as i8; 50 | i += 1; 51 | } else { 52 | break; 53 | } 54 | } 55 | *buffer.add(i) = '\u{0}' as i32 as i8; 56 | if ch.is_none() && i == 0 { 57 | return ptr::null_mut(); 58 | } 59 | if ch == Some(b'\r') { 60 | if file.read_byte().filter(|&c| c != b'\n').is_some() { 61 | file.seek(SeekFrom::Current(-1)).unwrap(); 62 | } 63 | } 64 | buffer 65 | } 66 | 67 | /* PDF reading starts around here */ 68 | /* As each lines may contain null-characters, so outptr here is NOT 69 | * null-terminated string. Returns -1 for when EOF is already reached, and -2 70 | * if buffer has no enough space. 71 | */ 72 | #[derive(Copy, Clone, Debug)] 73 | pub(crate) enum MfReadErr { 74 | Eof, 75 | NotEnoughSpace, 76 | } 77 | 78 | use arrayvec::ArrayVec; 79 | pub(crate) unsafe fn tt_mfreadln( 80 | size: usize, 81 | handle: &mut R, 82 | ) -> Result, MfReadErr> { 83 | let mut c; 84 | let mut buf = ArrayVec::<[u8; 1024]>::new(); 85 | loop { 86 | c = handle.read_byte(); 87 | if let Some(c) = c.filter(|&c| c != b'\n' && c != b'\r') { 88 | if buf.len() >= size { 89 | return Err(MfReadErr::NotEnoughSpace); 90 | } 91 | buf.push(c as u8); 92 | } else { 93 | break; 94 | } 95 | } 96 | if c.is_none() && buf.is_empty() { 97 | return Err(MfReadErr::Eof); 98 | } 99 | if c == Some(b'\r') { 100 | if handle.read_byte().filter(|&c| c != b'\n').is_some() { 101 | handle.seek(SeekFrom::Current(-1)).unwrap(); 102 | } 103 | } 104 | Ok(buf) 105 | } 106 | -------------------------------------------------------------------------------- /dpx/src/dpx_unicode.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow( 23 | mutable_transmutes, 24 | non_camel_case_types, 25 | non_snake_case, 26 | non_upper_case_globals 27 | )] 28 | 29 | pub(crate) unsafe fn UC_is_valid(ucv: i32) -> bool { 30 | !(ucv < 0 || ucv as i64 > 0x10ffff || ucv as i64 >= 0xd800 && ucv as i64 <= 0xdfff) 31 | } 32 | 33 | pub(crate) unsafe fn UC_UTF16BE_is_valid_string(slice: &[u8]) -> bool { 34 | if slice.len() < 2 || slice.len() % 2 != 0 { 35 | return false; 36 | } 37 | for c in std::char::decode_utf16( 38 | slice 39 | .chunks_exact(2) 40 | .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]])), 41 | ) { 42 | if c.is_err() { 43 | return false; 44 | } 45 | } 46 | true 47 | } 48 | 49 | pub(crate) unsafe fn UC_UTF8_is_valid_string(slice: &[u8]) -> bool { 50 | if slice.is_empty() { 51 | return false; 52 | } 53 | std::str::from_utf8(slice).is_ok() 54 | } 55 | 56 | pub(crate) unsafe fn UC_UTF16BE_encode_char(ucv: i32, pp: &mut *mut u8, endptr: *mut u8) -> usize { 57 | if let Some(ucv) = std::char::from_u32(ucv as u32) { 58 | let mut b = [0; 2]; 59 | let c16 = ucv.encode_utf16(&mut b); 60 | let p: *mut u8 = *pp; 61 | if p.add(c16.len() * 2) > endptr { 62 | return 0; 63 | } 64 | if c16.len() == 1 { 65 | let b16 = c16[0].to_be_bytes(); 66 | *p.offset(0) = b16[0]; 67 | *p.offset(1) = b16[1]; 68 | *pp = (*pp).offset(2); 69 | 2 70 | } else { 71 | let high = c16[0].to_be_bytes(); 72 | let low = c16[1].to_be_bytes(); 73 | *p.offset(0) = high[0]; 74 | *p.offset(1) = high[1]; 75 | *p.offset(2) = low[0]; 76 | *p.offset(3) = low[1]; 77 | *pp = (*pp).offset(2); 78 | 4 79 | } 80 | } else { 81 | 0 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /dpx/src/shims.rs: -------------------------------------------------------------------------------- 1 | // These functions are not exported directly on windows. So we use a shims to call them. 2 | extern "C" { 3 | #[link_name = "dpx_sprintf"] 4 | pub(crate) fn sprintf(s: *mut libc::c_char, format: *const libc::c_char, ...) -> libc::c_int; 5 | } 6 | -------------------------------------------------------------------------------- /dpx/src/specials/color.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow()] 23 | 24 | use super::util::spc_util_read_colorspec; 25 | use super::{Handler, SpcArg, SpcEnv}; 26 | use super::{Result, ERR, ERROR}; 27 | use crate::dpx_dpxutil::ParseCIdent; 28 | use crate::dpx_pdfcolor::{pdf_color_clear_stack, pdf_color_pop, pdf_color_push, pdf_color_set}; 29 | use crate::dpx_pdfdoc::pdf_doc_set_bgcolor; 30 | use crate::spc_warn; 31 | use crate::SkipBlank; 32 | 33 | /* Color stack is actually placed into pdfcolor.c. 34 | * The reason why we need to place stack there is 35 | * that we must reinstall color after grestore and 36 | * other operations that can change current color 37 | * implicitely. 38 | */ 39 | unsafe fn spc_handler_color_push(spe: &mut SpcEnv, args: &mut SpcArg) -> Result<()> { 40 | if let Ok(mut colorspec) = spc_util_read_colorspec(spe, args, true) { 41 | let color_clone = colorspec.clone(); 42 | pdf_color_push(&mut colorspec, &color_clone); 43 | Ok(()) 44 | } else { 45 | ERR 46 | } 47 | } 48 | 49 | unsafe fn spc_handler_color_pop(mut _spe: &mut SpcEnv, _args: &mut SpcArg) -> Result<()> { 50 | pdf_color_pop(); 51 | Ok(()) 52 | } 53 | /* Invoked by the special command "color rgb .625 0 0". 54 | * DVIPS clears the color stack, and then saves and sets the given color. 55 | */ 56 | unsafe fn spc_handler_color_default(spe: &mut SpcEnv, args: &mut SpcArg) -> Result<()> { 57 | if let Ok(colorspec) = spc_util_read_colorspec(spe, args, true) { 58 | pdf_color_clear_stack(); 59 | pdf_color_set(&colorspec, &colorspec); 60 | Ok(()) 61 | } else { 62 | ERR 63 | } 64 | } 65 | /* This is from color special? */ 66 | unsafe fn spc_handler_background(spe: &mut SpcEnv, args: &mut SpcArg) -> Result<()> { 67 | if let Ok(colorspec) = spc_util_read_colorspec(spe, args, true) { 68 | pdf_doc_set_bgcolor(Some(&colorspec)); 69 | Ok(()) 70 | } else { 71 | ERR 72 | } 73 | } 74 | 75 | pub(crate) fn spc_color_check_special(mut buf: &[u8]) -> bool { 76 | buf.skip_blank(); 77 | if let Some(q) = buf.parse_c_ident() { 78 | q == "color" || q == "background" 79 | } else { 80 | false 81 | } 82 | } 83 | 84 | pub(crate) unsafe fn spc_color_setup_handler(spe: &mut SpcEnv, ap: &mut SpcArg) -> Result { 85 | ap.cur.skip_blank(); 86 | let q = ap.cur.parse_c_ident(); 87 | if q.is_none() { 88 | return ERROR(); 89 | } 90 | ap.cur.skip_blank(); 91 | let exec = match q.unwrap().as_ref() { 92 | "background" => { 93 | ap.command = Some("background"); 94 | spc_handler_background 95 | } 96 | "color" => { 97 | /* color */ 98 | /* cmyk, rgb, ... */ 99 | let mut p = &ap.cur[..]; 100 | if let Some(q) = p.parse_c_ident() { 101 | match q.as_ref() { 102 | "push" => { 103 | ap.command = Some("push"); 104 | ap.cur = p; 105 | spc_handler_color_push 106 | } 107 | "pop" => { 108 | ap.command = Some("pop"); 109 | ap.cur = p; 110 | spc_handler_color_pop 111 | } 112 | _ => { 113 | ap.command = Some(""); 114 | spc_handler_color_default 115 | } 116 | } 117 | } else { 118 | return ERROR(); 119 | } 120 | } 121 | _ => { 122 | spc_warn!(spe, "Not color/background special?"); 123 | return ERROR(); 124 | } 125 | }; 126 | ap.cur.skip_blank(); 127 | Ok(exec) 128 | } 129 | -------------------------------------------------------------------------------- /dpx/src/specials/dvipdfmx.rs: -------------------------------------------------------------------------------- 1 | /* This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks. 2 | 3 | Copyright (C) 2002-2018 by Jin-Hwan Cho and Shunsaku Hirata, 4 | the dvipdfmx project team. 5 | 6 | Copyright (C) 1998, 1999 by Mark A. Wicks 7 | 8 | This program is free software; you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation; either version 2 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program; if not, write to the Free Software 20 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. 21 | */ 22 | #![allow()] 23 | 24 | use super::{Handler, SpcArg, SpcEnv}; 25 | use super::{Result, ERROR}; 26 | use crate::dpx_dpxutil::ParseCIdent; 27 | use crate::dpx_pdfparse::SkipWhite; 28 | use crate::spc_warn; 29 | 30 | unsafe fn spc_handler_null(_spe: &mut SpcEnv, args: &mut SpcArg) -> Result<()> { 31 | args.cur = &[]; 32 | Ok(()) 33 | } 34 | static DVIPDFMX_HANDLERS: phf::Map<&'static str, Handler> = phf::phf_map! { 35 | "config" => spc_handler_null, 36 | }; 37 | 38 | pub(crate) fn spc_dvipdfmx_check_special(mut buf: &[u8]) -> bool { 39 | buf.skip_white(); 40 | buf.starts_with(b"dvipdfmx:") 41 | } 42 | 43 | pub(crate) unsafe fn spc_dvipdfmx_setup_handler( 44 | spe: &mut SpcEnv, 45 | ap: &mut SpcArg, 46 | ) -> Result { 47 | ap.cur.skip_white(); 48 | if !ap.cur.starts_with(b"dvipdfmx:") { 49 | spc_warn!(spe, "Not dvipdfmx: special???"); 50 | return ERROR(); 51 | } 52 | ap.cur = &ap.cur[b"dvipdfmx:".len()..]; 53 | ap.cur.skip_white(); 54 | if let Some(q) = ap.cur.parse_c_ident() { 55 | if let Some((key, &exec)) = DVIPDFMX_HANDLERS.get_entry(q.as_str()) { 56 | ap.command = Some(key); 57 | ap.cur.skip_white(); 58 | return Ok(exec); 59 | } 60 | } 61 | ERROR() 62 | } 63 | -------------------------------------------------------------------------------- /engine/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2018 the Tectonic Project 2 | # Licensed under the MIT License. 3 | 4 | [package] 5 | name = "tectonic_engine" 6 | version = "0.0.1-dev" 7 | authors = ["Peter Williams "] 8 | description = """ 9 | Core layout engine of XeTeX and Tectonic. 10 | """ 11 | build = "build.rs" 12 | homepage = "https://tectonic-typesetting.github.io/" 13 | documentation = "https://docs.rs/tectonic" 14 | repository = "https://github.com/tectonic-typesetting/tectonic/" 15 | readme = "README.md" 16 | license = "MIT" 17 | edition = "2018" 18 | 19 | [build-dependencies] 20 | cc = "^1.0.46" 21 | pkg-config = "^0.3" # note: sync dist/docker/*/pkg-config-rs.sh with the version in Cargo.lock 22 | regex = "^1.3" 23 | sha2 = "^0.8" 24 | tectonic_cfg_support = { path = "../cfg_support", version = "0.0.3-dev" } 25 | vcpkg = "0.2.8" 26 | 27 | [features] 28 | # developer feature to compile with the necessary flags for profiling tectonic. 29 | profile = [] 30 | 31 | [dependencies] 32 | bridge = { package = "tectonic_bridge", version = "0.0.1-dev", path = "../bridge" } 33 | bibtex = { package = "tectonic_bibtex", version = "0.0.1-dev", path = "../bibtex" } 34 | dpx = { package = "tectonic_dvipdfmx", version = "0.0.1-dev", path = "../dpx" } 35 | bitflags = "1.2.1" 36 | libc = "0.2" 37 | chrono = "0.4.9" 38 | #log = { version = "0.4", features = ["std"] } 39 | harfbuzz-sys = {version = "^0.3", default-features = false} 40 | euclid = "0.20" 41 | enumn = "0.1.2" 42 | derive_more = "0.99.7" 43 | md5 = "0.7" 44 | fontconfig_sys = { package = "servo-fontconfig", version = "0.5.1" } 45 | once_cell = "1.5.2" 46 | enum_dispatch = "0.3.5" 47 | freetype = "0.7.0" 48 | freetype-sys = "0.13.1" 49 | 50 | [dependencies.font-kit] 51 | version = "0.10.0" 52 | # to minimize code change, we're using harfbuzz with freetype only 53 | # but as font-kit takes over, this can go, and macOS gets to use Core Text, and Windows gets to use DirectWrite 54 | features = ["loader-freetype", "loader-freetype-default"] 55 | 56 | [target.'cfg(target_os = "macos")'.dependencies] 57 | core-foundation = "0.9.1" 58 | core-graphics = "0.22.1" 59 | core-text = "19.1.0" 60 | objc = "0.2.7" 61 | objc-foundation = "0.1.1" 62 | objc_id = "0.1.1" 63 | -------------------------------------------------------------------------------- /engine/src/stub_icu.rs: -------------------------------------------------------------------------------- 1 | pub(crate) type UChar = u16; 2 | pub(crate) type UErrorCode = i32; 3 | 4 | #[repr(C)] 5 | #[derive(Debug, Copy, Clone)] 6 | pub struct UBreakIterator { 7 | _unused: [u8; 0], 8 | } 9 | #[repr(C)] 10 | #[derive(Debug, Copy, Clone)] 11 | pub struct UConverter { 12 | _unused: [u8; 0], 13 | } 14 | #[repr(C)] 15 | #[derive(Debug, Copy, Clone)] 16 | pub struct UBiDi { 17 | _unused: [u8; 0], 18 | } 19 | 20 | pub(crate) type UConverterType = i32; 21 | pub(crate) type UBiDiLevel = u8; 22 | 23 | pub(crate) const UCNV_UTF32_LittleEndian: UConverterType = 8; 24 | pub(crate) const U_ZERO_ERROR: UErrorCode = 0; 25 | 26 | pub(crate) type UBiDiDirection = u32; 27 | pub(crate) const UBIDI_RTL: UBiDiDirection = 1; 28 | pub(crate) const UBIDI_MIXED: UBiDiDirection = 2; 29 | 30 | pub(crate) type UBreakIteratorType = u32; 31 | pub(crate) const UBRK_LINE: UBreakIteratorType = 2; 32 | 33 | extern_and_forward_stub! { 34 | pub(crate) fn ubidi_open => tt_ubidi_open() -> *mut UBiDi; 35 | pub(crate) fn ubidi_close => tt_ubidi_close(pBiDi: *mut UBiDi) -> (); 36 | pub(crate) fn ubidi_setPara => tt_ubidi_setPara( 37 | pBiDi: *mut UBiDi, 38 | text: *const UChar, 39 | length: i32, 40 | paraLevel: UBiDiLevel, 41 | embeddingLevels: *mut UBiDiLevel, 42 | pErrorCode: *mut UErrorCode 43 | ) -> (); 44 | pub(crate) fn ubidi_getDirection => tt_ubidi_getDirection(pBiDi: *const UBiDi) -> UBiDiDirection; 45 | pub(crate) fn ubidi_getVisualRun => tt_ubidi_getVisualRun( 46 | pBiDi: *mut UBiDi, 47 | runIndex: i32, 48 | pLogicalStart: *mut i32, 49 | pLength: *mut i32 50 | ) -> UBiDiDirection; 51 | pub(crate) fn ubidi_countRuns => tt_ubidi_countRuns(pBiDi: *mut UBiDi, pErrorCode: *mut UErrorCode) -> i32; 52 | pub(crate) fn ubrk_next => tt_ubrk_next(bi: *mut UBreakIterator) -> i32; 53 | pub(crate) fn ubrk_close => tt_ubrk_close(bi: *mut UBreakIterator) -> (); 54 | pub(crate) fn ubrk_open => tt_ubrk_open( 55 | type_0: UBreakIteratorType, 56 | locale: *const i8, 57 | text: *const UChar, 58 | textLength: i32, 59 | status: *mut UErrorCode 60 | ) -> *mut UBreakIterator; 61 | pub(crate) fn ubrk_setText => tt_ubrk_setText( 62 | bi: *mut UBreakIterator, 63 | text: *const UChar, 64 | textLength: i32, 65 | status: *mut UErrorCode 66 | ) -> (); 67 | pub(crate) fn ucnv_open => tt_ucnv_open(converterName: *const i8, err: *mut UErrorCode) -> *mut UConverter; 68 | pub(crate) fn ucnv_close => tt_ucnv_close(converter: *mut UConverter) -> (); 69 | pub(crate) fn ucnv_toAlgorithmic => tt_ucnv_toAlgorithmic( 70 | algorithmicType: UConverterType, 71 | cnv: *mut UConverter, 72 | target: *mut i8, 73 | targetCapacity: i32, 74 | source: *const i8, 75 | sourceLength: i32, 76 | pErrorCode: *mut UErrorCode 77 | ) -> i32; 78 | pub(crate) fn ucnv_fromUChars => tt_ucnv_fromUChars ( 79 | cnv: *mut UConverter, 80 | dest: *mut i8, 81 | destCapacity: i32, 82 | src: *const UChar, 83 | srcLength: i32, 84 | pErrorCode: *mut UErrorCode 85 | ) -> i32; 86 | pub(crate) fn ucnv_toUChars => tt_ucnv_toUChars ( 87 | cnv: *mut UConverter, 88 | dest: *mut UChar, 89 | destCapacity: i32, 90 | src: *const i8, 91 | srcLength: i32, 92 | pErrorCode: *mut UErrorCode 93 | ) -> i32; 94 | } 95 | -------------------------------------------------------------------------------- /engine/src/stub_teckit.rs: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------ 2 | Copyright (C) 2002-2014 SIL International. All rights reserved. 3 | 4 | Distributable under the terms of either the Common Public License or the 5 | GNU Lesser General Public License, as specified in the LICENSING.txt file. 6 | 7 | File: TECkit_Engine.h 8 | Responsibility: Jonathan Kew 9 | Last reviewed: Not yet. 10 | 11 | Description: 12 | Public API to the TECkit conversion engine. 13 | -------------------------------------------------------------------------*/ 14 | #[repr(C)] 15 | #[derive(Debug, Copy, Clone)] 16 | pub struct Opaque_TECkit_Converter { 17 | _unused: [u8; 0], 18 | } 19 | 20 | extern "C" { 21 | pub(crate) fn TECkit_CreateConverter( 22 | mapping: *mut u8, 23 | mappingSize: u32, 24 | mapForward: u8, 25 | sourceForm: u16, 26 | targetForm: u16, 27 | converter: *mut TECkit_Converter, 28 | ) -> TECkit_Status; 29 | pub(crate) fn TECkit_ConvertBuffer( 30 | converter: TECkit_Converter, 31 | inBuffer: *const u8, 32 | inLength: u32, 33 | inUsed: *mut u32, 34 | outBuffer: *mut u8, 35 | outLength: u32, 36 | outUsed: *mut u32, 37 | inputIsComplete: u8, 38 | ) -> TECkit_Status; 39 | } 40 | 41 | pub(crate) type TECkit_Status = i64; 42 | 43 | pub(crate) type TECkit_Converter = *mut Opaque_TECkit_Converter; 44 | -------------------------------------------------------------------------------- /engine/src/xetex_engine_interface.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] 2 | 3 | use crate::xetex_ini::{ 4 | halt_on_error_p, in_initex_mode, semantic_pagination_enabled, shell_escape_enabled, 5 | synctex_enabled, 6 | }; 7 | 8 | /* tectonic/core-strutils.h: miscellaneous C string utilities 9 | Copyright 2016-2018 the Tectonic Project 10 | Licensed under the MIT License. 11 | */ 12 | /* Note that we explicitly do *not* change this on Windows. For maximum 13 | * portability, we should probably accept *either* forward or backward slashes 14 | * as directory separators. */ 15 | /* engine-interface.c: programmatic interface to control the engine behavior 16 | Copyright 2016-2018 The Tectonic Project 17 | Licensed under the MIT License. 18 | */ 19 | /* These functions aren't used within the C/C++ library, but are called 20 | * by the Rust code to configure the XeTeX engine before launching it. */ 21 | pub unsafe fn tt_xetex_set_int_variable(var_name: &str, value: i32) -> i32 { 22 | if var_name == "halt_on_error_p" { 23 | halt_on_error_p = value 24 | } else if var_name == "in_initex_mode" { 25 | in_initex_mode = value != 0 26 | } else if var_name == "synctex_enabled" { 27 | synctex_enabled = value != 0 28 | } else if var_name == "semantic_pagination_enabled" { 29 | semantic_pagination_enabled = value != 0 30 | } else if var_name == "shell_escape_enabled" { 31 | shell_escape_enabled = value != 0 32 | } else { 33 | return 1; 34 | } /* Uh oh: unrecognized variable */ 35 | 0 36 | /* success */ 37 | } 38 | /*pub(crate) unsafe fn tt_xetex_set_string_variable( 39 | mut _var_name: *mut i8, 40 | mut _value: *mut i8, 41 | ) -> i32 { 42 | /* Currently unused; see Git history for how we used to set output_comment */ 43 | 1 44 | }*/ 45 | -------------------------------------------------------------------------------- /engine/src/xetex_font_info_coretext.rs: -------------------------------------------------------------------------------- 1 | #![cfg(target_os = "macos")] 2 | #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] 3 | 4 | use crate::xetex_aatfont::getFileNameFromCTFont; 5 | use std::ptr; 6 | 7 | pub(crate) type Boolean = u8; 8 | 9 | use crate::cf_prelude::*; 10 | 11 | use crate::xetex_font_info::XeTeXFontInst; 12 | 13 | #[derive(Clone)] 14 | pub(crate) struct XeTeXFontInst_Mac { 15 | pub(crate) super_: XeTeXFontInst, 16 | pub(crate) m_descriptor: CTFontDescriptorRef, 17 | pub(crate) m_fontRef: CTFontRef, 18 | } 19 | 20 | impl core::ops::Deref for XeTeXFontInst_Mac { 21 | type Target = XeTeXFontInst; 22 | 23 | fn deref(&self) -> &Self::Target { 24 | &self.super_ 25 | } 26 | } 27 | impl core::ops::DerefMut for XeTeXFontInst_Mac { 28 | fn deref_mut(&mut self) -> &mut Self::Target { 29 | &mut self.super_ 30 | } 31 | } 32 | 33 | impl Drop for XeTeXFontInst_Mac { 34 | fn drop(&mut self) { 35 | unsafe { 36 | if !self.m_descriptor.is_null() { 37 | CFRelease(self.m_descriptor as CFTypeRef); 38 | } 39 | if !self.m_fontRef.is_null() { 40 | CFRelease(self.m_fontRef as CFTypeRef); 41 | }; 42 | } 43 | } 44 | } 45 | 46 | impl XeTeXFontInst_Mac { 47 | pub(crate) unsafe fn ctor( 48 | descriptor: CTFontDescriptorRef, 49 | pointSize: f32, 50 | status: &mut bool, 51 | ) -> Self { 52 | let mut super_ = XeTeXFontInst::base_ctor("", 0, pointSize, status); 53 | let mut m_descriptor = descriptor; 54 | let mut m_fontRef = 0 as CTFontRef; 55 | 56 | if m_descriptor.is_null() { 57 | *status = true; 58 | return Self { 59 | super_, 60 | m_descriptor, 61 | m_fontRef, 62 | }; 63 | } 64 | if *status != false { 65 | m_descriptor = 0 as CTFontDescriptorRef 66 | } 67 | // Create a copy of original font descriptor with font cascading (fallback) disabled 68 | let emptyCascadeList = CFArrayCreate( 69 | 0 as CFAllocatorRef, 70 | 0 as *mut *const libc::c_void, 71 | 0, 72 | &kCFTypeArrayCallBacks, 73 | ); 74 | let mut values: [*const libc::c_void; 1] = [emptyCascadeList as *const libc::c_void]; 75 | let mut attributeKeys: [*const libc::c_void; 1] = 76 | [kCTFontCascadeListAttribute as *const libc::c_void]; 77 | let attributes = CFDictionaryCreate( 78 | 0 as CFAllocatorRef, 79 | attributeKeys.as_mut_ptr(), 80 | values.as_mut_ptr(), 81 | 1, 82 | &kCFTypeDictionaryKeyCallBacks, 83 | &kCFTypeDictionaryValueCallBacks, 84 | ); 85 | CFRelease(emptyCascadeList as CFTypeRef); 86 | m_descriptor = CTFontDescriptorCreateCopyWithAttributes(m_descriptor, attributes); 87 | CFRelease(attributes as CFTypeRef); 88 | m_fontRef = CTFontCreateWithFontDescriptor( 89 | m_descriptor, 90 | super_.m_pointSize as f64 * 72. / 72.27, 91 | ptr::null(), 92 | ); 93 | if !m_fontRef.is_null() { 94 | let mut index = 0; 95 | let pathname = getFileNameFromCTFont(m_fontRef, &mut index); 96 | super_ = XeTeXFontInst::base_ctor(&pathname, index as i32, super_.m_pointSize, status); 97 | } else { 98 | *status = true; 99 | CFRelease(m_descriptor as CFTypeRef); 100 | m_descriptor = 0 as CTFontDescriptorRef 101 | }; 102 | 103 | Self { 104 | super_, 105 | m_descriptor, 106 | m_fontRef, 107 | } 108 | } 109 | 110 | pub(crate) unsafe fn create( 111 | descriptor: CTFontDescriptorRef, 112 | pointSize: f32, 113 | status: &mut bool, 114 | ) -> Box { 115 | Box::new(Self::ctor(descriptor, pointSize, status)) 116 | } 117 | 118 | pub(crate) unsafe fn wrapper( 119 | pathname: &str, 120 | index: i32, 121 | pointSize: f32, 122 | status: &mut bool, 123 | ) -> Box { 124 | Box::new(Self { 125 | super_: XeTeXFontInst::base_ctor(pathname, index, pointSize, status), 126 | m_descriptor: 0 as CTFontDescriptorRef, 127 | m_fontRef: 0 as CTFontRef, 128 | }) 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /engine/src/xetex_texmfmp.rs: -------------------------------------------------------------------------------- 1 | #![allow(non_camel_case_types, non_snake_case, non_upper_case_globals)] 2 | 3 | use crate::xetex_stringpool::{ 4 | make_string, pool_ptr, str_pool, PoolString, EMPTY_STRING, TOO_BIG_CHAR, 5 | }; 6 | use bridge::ttstub_get_file_md5; 7 | use std::ffi::CString; 8 | 9 | use std::env; 10 | 11 | pub(crate) type str_number = i32; 12 | /* texmfmp.c: Hand-coded routines for TeX or Metafont in C. Originally 13 | written by Tim Morgan, drawing from other Unix ports of TeX. This is 14 | a collection of miscellany, everything that's easier (or only 15 | possible) to do in C. 16 | 17 | This file is public domain. */ 18 | static mut last_source_name: String = String::new(); 19 | static mut last_lineno: i32 = 0; 20 | pub(crate) fn get_date_and_time() -> (i32, i32, i32, i32) { 21 | use chrono::prelude::*; 22 | 23 | let tm = match env::var("SOURCE_DATE_EPOCH").ok() { 24 | Some(s) => { 25 | let epoch = u64::from_str_radix(&s, 10).expect("invalid build date (not a number)"); 26 | std::time::SystemTime::UNIX_EPOCH 27 | .checked_add(std::time::Duration::from_secs(epoch)) 28 | .expect("time overflow") 29 | .into() 30 | } 31 | None => Local::now(), 32 | }; 33 | 34 | let year = tm.year(); 35 | let month = tm.month(); 36 | let day = tm.day(); 37 | let minutes = tm.hour() * 60 + tm.minute(); 38 | 39 | (minutes as _, day as _, month as _, year) 40 | } 41 | pub(crate) unsafe fn maketexstring(s: &str) -> i32 { 42 | if s.is_empty() { 43 | return EMPTY_STRING; 44 | } 45 | PoolString::check_capacity(s.len()); 46 | let v: Vec = s.encode_utf16().collect(); 47 | let len = v.len(); 48 | str_pool[pool_ptr..pool_ptr + len].copy_from_slice(v.as_slice()); 49 | pool_ptr += len; 50 | make_string() 51 | } 52 | pub(crate) fn gettexstring(s: str_number) -> String { 53 | if s >= TOO_BIG_CHAR { 54 | String::from_utf16(PoolString::from(s).as_slice()).unwrap() 55 | } else { 56 | String::new() 57 | } 58 | } 59 | pub(crate) unsafe fn is_new_source(srcfilename: str_number, lineno: i32) -> bool { 60 | use std::path::Path; 61 | Path::new(&gettexstring(srcfilename)) != Path::new(&last_source_name) || lineno != last_lineno 62 | } 63 | pub(crate) unsafe fn remember_source_info(srcfilename: str_number, lineno: i32) { 64 | last_source_name = gettexstring(srcfilename); 65 | last_lineno = lineno; 66 | } 67 | pub(crate) unsafe fn make_src_special(srcfilename: str_number, lineno: i32) -> String { 68 | // Always put a space after the number, which makes things easier to parse. 69 | format!("src:{} {}", lineno, &gettexstring(srcfilename)) 70 | } 71 | /* Converts any given string in into an allowed PDF string which is 72 | * hexadecimal encoded; 73 | * sizeof(out) should be at least lin*2+1. 74 | */ 75 | unsafe fn convertStringToHexString(in_0: &[u8; 16], out: &mut [u8; 33]) { 76 | const HEXCHARS: &[u8] = b"0123456789ABCDEF"; 77 | let mut j = 0; 78 | for &i in in_0 { 79 | let c = i; 80 | out[j] = HEXCHARS[(c >> 4 & 0xf) as usize]; 81 | j += 1; 82 | out[j] = HEXCHARS[(c & 0xf) as usize]; 83 | j += 1; 84 | } 85 | out[j] = 0; 86 | } 87 | /* Functions originating in texmfmp.c */ 88 | pub(crate) unsafe fn getmd5sum(s: &str, file: bool) -> String { 89 | let ret; 90 | let xname = CString::new(s).unwrap(); 91 | let digest = if file { 92 | let mut digest: [i8; 16] = [0; 16]; 93 | ret = ttstub_get_file_md5(xname.as_ptr(), digest.as_mut_ptr()); 94 | std::mem::transmute(digest) 95 | } else { 96 | ret = 0; 97 | md5::compute(xname.as_bytes()) 98 | }; 99 | if ret != 0 { 100 | return String::new(); 101 | } 102 | let mut outbuf: [u8; 33] = [0; 33]; 103 | convertStringToHexString(&digest, &mut outbuf); 104 | let mut v = Vec::with_capacity(2 * 16); 105 | for i in 0..(2 * 16) { 106 | v.push(outbuf[i as usize]) 107 | } 108 | String::from_utf8(v).unwrap() 109 | } 110 | -------------------------------------------------------------------------------- /engine/tectonic/core-foundation.h: -------------------------------------------------------------------------------- 1 | /* tectonic/core-foundation.h: the first header to include in Tectonic's C/C++ code 2 | Copyright 2016-2018 the Tectonic Project 3 | Licensed under the MIT License. 4 | */ 5 | 6 | /* This header should (eventually ...) be included first in all of Tectonic's 7 | * C/C++ files. It essentially concerns itself with defining basic types and 8 | * portability. 9 | */ 10 | 11 | #ifndef TECTONIC_CORE_FOUNDATION_H 12 | #define TECTONIC_CORE_FOUNDATION_H 13 | 14 | /* High-level defines */ 15 | 16 | #define _DARWIN_USE_64_BIT_INODE 1 17 | 18 | /* Some versions of g++ do not define PRId64 and friends unless we #define 19 | * this before including inttypes.h. This was apparently an idea that was 20 | * proposed for C++11 but didn't make it into the final standard. */ 21 | #define __STDC_FORMAT_MACROS 22 | 23 | /* Universal headers */ 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | /* Convenience for C++: this way Emacs doesn't try to indent the prototypes, 41 | * which I find annoying. */ 42 | 43 | #ifdef __cplusplus 44 | #define BEGIN_EXTERN_C extern "C" { 45 | #define END_EXTERN_C } 46 | #else 47 | #define BEGIN_EXTERN_C 48 | #define END_EXTERN_C 49 | #endif 50 | 51 | /* Portability: NORETURN annotation */ 52 | 53 | #if defined __GNUC__ && __GNUC__ >= 3 54 | #define NORETURN __attribute__((__noreturn__)) 55 | #else 56 | #define NORETURN 57 | #endif 58 | 59 | /* Portability: annotations to validate args of printf-like functions */ 60 | 61 | #if defined __GNUC__ && __GNUC__ >= 3 62 | #define PRINTF_FUNC(ifmt,iarg) __attribute__((format(printf, ifmt, iarg))) 63 | #else 64 | #define PRINTF_FUNC(ifmt,iarg) 65 | #endif 66 | 67 | /* Portability: inline annotation */ 68 | 69 | #ifdef _MSC_VER 70 | # ifndef __cplusplus 71 | # define inline __inline 72 | # endif 73 | #endif 74 | 75 | /* Portability: MSVC variations on various common functions */ 76 | 77 | #ifdef _MSC_VER 78 | # if defined(_VC_CRT_MAJOR_VERSION) && _VC_CRT_MAJOR_VERSION < 14 79 | # define snprintf _snprintf 80 | # define strtoll _strtoi64 81 | # endif 82 | #endif 83 | 84 | /* Portability: ssize_t 85 | * 86 | * On Unix, sys/types.h gives ssize_t. On MSVC we need to do the following: 87 | */ 88 | 89 | #if defined(_MSC_VER) 90 | #include 91 | typedef SSIZE_T ssize_t; 92 | #endif 93 | 94 | /* Portability: M_PI 95 | * 96 | * MSVC doesn't always define it. Based on research, sometimes #defining 97 | * _USE_MATH_DEFINES should fix the problem, but it's not clear if that 98 | * *always* works in all versions, and it's easy to cut to the heart of the 99 | * matter: 100 | */ 101 | 102 | #ifndef M_PI 103 | # define M_PI 3.14159265358979 104 | #endif 105 | 106 | /* Portability: printf format arguments for various non-core types. 107 | * 108 | * ought to define these for most types. We use a custom one for 109 | * size_t since older MSVC doesn't provide %z. 110 | */ 111 | 112 | #ifndef PRId64 113 | # if defined(SIZEOF_LONG) 114 | # if SIZEOF_LONG == 8 115 | # define PRId64 "ld" 116 | # else 117 | # define PRId64 "lld" 118 | # endif 119 | # elif defined(_WIN32) 120 | # define PRId64 "I64d" 121 | # else 122 | # error "unhandled compiler/platform for PRId64 definition" 123 | # endif 124 | #endif 125 | 126 | #ifndef PRIdPTR 127 | # define PRIdPTR "ld" 128 | #endif 129 | #ifndef PRIxPTR 130 | # define PRIxPTR "lx" 131 | #endif 132 | 133 | #ifdef _WIN32 134 | # define PRIuZ "Iu" 135 | # define PRIXZ "IX" 136 | #else 137 | # define PRIuZ "zu" 138 | # define PRIXZ "zX" 139 | #endif 140 | 141 | #endif /* not TECTONIC_CORE_FOUNDATION_H */ 142 | -------------------------------------------------------------------------------- /engine/tectonic/stub_stdio.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int xetex_sprintf ( char * str, const char * format, ... ); 6 | 7 | int xetex_sprintf ( char * str, const char * format, ... ) { 8 | va_list arglist; 9 | int r; 10 | va_start(arglist, format); 11 | r = vsprintf(str, format, arglist); 12 | va_end(arglist); 13 | return r; 14 | } 15 | -------------------------------------------------------------------------------- /engine/tectonic/teckit-Common.h: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------ 2 | Copyright (C) 2002-2016 SIL International. All rights reserved. 3 | 4 | Distributable under the terms of either the Common Public License or the 5 | GNU Lesser General Public License, as specified in the LICENSING.txt file. 6 | 7 | File: TECkit_Common.h 8 | Responsibility: Jonathan Kew 9 | Last reviewed: Not yet. 10 | 11 | Description: 12 | Public definitions used by TECkit engine and compiler 13 | -------------------------------------------------------------------------*/ 14 | 15 | /* 16 | Common types and defines for the engine and compiler 17 | 18 | History: 19 | 16-Sep-2006 jk updated version to 2.4 (adding new compiler APIs for Bob E) 20 | 23-May-2005 jk patch for 64-bit architectures (thanks to Ulrik P) 21 | 18-Mar-2005 jk updated minor version for 2.3 (engine unchanged, XML option in compiler) 22 | 23-Sep-2003 jk updated for version 2.1 - extended status values 23 | xx-xxx-2002 jk version 2.0 initial release 24 | */ 25 | 26 | #ifndef __TECkit_Common_H__ 27 | #define __TECkit_Common_H__ 28 | 29 | #include "core-foundation.h" 30 | 31 | #define kCurrentTECkitVersion 0x00020004 /* 16.16 version number */ 32 | 33 | #ifndef __MACTYPES__ 34 | #ifndef MAC_TYPES /* these are all predefined if using a Mac prefix */ 35 | typedef unsigned char UInt8; 36 | typedef unsigned short UInt16; 37 | typedef unsigned int UInt32; /* NB: assumes int is 4 bytes */ 38 | #ifndef ZCONF_H /* n.b. if also using zlib.h, it must precede TECkit headers */ 39 | typedef UInt8 Byte; 40 | #endif 41 | typedef Byte* BytePtr; 42 | typedef UInt16 UniChar; 43 | 44 | typedef char* Ptr; 45 | typedef Byte* TextPtr; 46 | #endif 47 | #endif 48 | 49 | /* 50 | all public functions return a status code 51 | */ 52 | typedef long TECkit_Status; 53 | 54 | /* 55 | possible TECkit_Status return values 56 | */ 57 | #define kStatus_NoError 0 /* this is usually the desired result! */ 58 | 59 | /* positive values are informational status values */ 60 | /* low byte is the basic status of the conversion process */ 61 | #define kStatusMask_Basic 0x000000FF 62 | #define kStatus_OutputBufferFull 1 /* ConvertBuffer or Flush: output buffer full, so not all input was processed */ 63 | #define kStatus_NeedMoreInput 2 /* ConvertBuffer: processed all input data, ready for next chunk */ 64 | 65 | /* only returned in version 2.1 or later, with DontUseReplacementChar option */ 66 | #define kStatus_UnmappedChar 3 /* ConvertBuffer or Flush: stopped at unmapped character */ 67 | 68 | /* additional warning status in 2.1, only returned if 2.1-specific options are used */ 69 | /* one byte of the status value is used for warning flags */ 70 | #define kStatusMask_Warning 0x0000FF00 71 | #define kStatus_UsedReplacement 0x00000100 /* ConvertBuffer or Flush: used default replacement character during mapping */ 72 | 73 | /* negative values are errors */ 74 | #define kStatus_InvalidForm -1 /* inForm or outForm parameter doesn't match mapping (bytes/Unicode mismatch) */ 75 | #define kStatus_ConverterBusy -2 /* can't initiate a conversion, as the converter is already in the midst of an operation */ 76 | #define kStatus_InvalidConverter -3 /* converter object is corrupted (or not really a TECkit_Converter at all) */ 77 | #define kStatus_InvalidMapping -4 /* compiled mapping data is not recognizable */ 78 | #define kStatus_BadMappingVersion -5 /* compiled mapping is not a version we can handle */ 79 | #define kStatus_Exception -6 /* an internal error has occurred */ 80 | #define kStatus_NameNotFound -7 /* couldn't find the requested name in the compiled mapping */ 81 | #define kStatus_IncompleteChar -8 /* bad input data (lone surrogate, incomplete UTF8 sequence) */ 82 | #define kStatus_CompilationFailed -9 /* mapping compilation failed (syntax errors, etc) */ 83 | #define kStatus_OutOfMemory -10 /* unable to allocate required memory */ 84 | 85 | /* 86 | encoding form constants for TECkit_CreateConverter and TECkit_Compile 87 | */ 88 | #define kForm_EncodingFormMask 0x000F 89 | #define kForm_Unspecified 0 /* invalid as argument to TECkit_CreateConverter */ 90 | #define kForm_Bytes 1 91 | #define kForm_UTF8 2 92 | #define kForm_UTF16BE 3 93 | #define kForm_UTF16LE 4 94 | #define kForm_UTF32BE 5 95 | #define kForm_UTF32LE 6 96 | 97 | #endif 98 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | Cargo.lock 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "tectonic-fuzz" 4 | version = "0.0.1" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | 8 | [package.metadata] 9 | cargo-fuzz = true 10 | 11 | [dependencies.tectonic] 12 | path = ".." 13 | [dependencies.libfuzzer-sys] 14 | git = "https://github.com/rust-fuzz/libfuzzer-sys.git" 15 | 16 | # Prevent this from interfering with workspaces 17 | [workspace] 18 | members = ["."] 19 | 20 | [[bin]] 21 | name = "compile" 22 | path = "fuzz_targets/compile.rs" 23 | -------------------------------------------------------------------------------- /fuzz/README.md: -------------------------------------------------------------------------------- 1 | # Tectonic Fuzzing Support 2 | 3 | This is *beta-level* support for [fuzzing] the Tectonic engine using 4 | [cargo-fuzz]. At the moment, the fuzzing does not work very well because it 5 | exposes memory leaks in the Tectonic engine; this is desirable in and of 6 | itself, but the exposed leaks have proven to be tricky to fix. (Patches most 7 | welcome, of course! The limiting factor here is definitely developer 8 | bandwidth.) 9 | 10 | See the script `./run-fuzzer.sh` for an example of how to, well, run the 11 | fuzzer. Note that this script sets a [rustup directory override] to build 12 | Tectonic using the nightly compilers, since nightly is currently required by 13 | the fuzzer. 14 | 15 | To tell the fuzzer to ignore memory leaks, run a command like the following 16 | from the toplevel directory of the Tectonic repository: 17 | 18 | ```sh 19 | cargo +nightly fuzz run compile fuzz/corpus fuzz/seeds -- -detect_leaks=0 20 | ``` 21 | 22 | [fuzzing]: https://en.wikipedia.org/wiki/Fuzzing 23 | [cargo-fuzz]: https://github.com/rust-fuzz/cargo-fuzz 24 | [rustup directory override]: https://github.com/rust-lang/rustup.rs#directory-overrides 25 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/compile.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | #[macro_use] extern crate libfuzzer_sys; 3 | extern crate tectonic; 4 | 5 | fuzz_target!(|data: &[u8]| { 6 | if let Ok(data) = std::str::from_utf8(data) { 7 | let _ = tectonic::latex_to_pdf(data); 8 | } 9 | }); 10 | -------------------------------------------------------------------------------- /fuzz/run-fuzzer.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -e 3 | set -o pipefail 4 | HERE="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" 5 | PARENT="$HERE/../" 6 | 7 | (test -x "${HOME}/.cargo/bin/cargo-fuzz" || cargo install cargo-fuzz) 8 | 9 | # it is important for `cargo fuzz` to be run from the project root 10 | cd "$PARENT" 11 | 12 | mkdir -p "$HERE/corpus" 13 | rustup override set nightly 14 | 15 | # run `compile` target using `seeds` as a start point and put new corpus state into `corpus` using 4 parallel jobs 16 | cargo fuzz run compile "$HERE/corpus" "$HERE/seeds" -j 4 --all-features 17 | -------------------------------------------------------------------------------- /fuzz/seeds/hello.tex: -------------------------------------------------------------------------------- 1 | \documentclass{article} 2 | \begin{document} 3 | Hello, world! 4 | \end{document} 5 | -------------------------------------------------------------------------------- /src/app_dirs.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | use crate::errors::Result; 5 | use app_dirs2::AppDataType; 6 | use std::path::PathBuf; 7 | 8 | pub use app_dirs2::sanitized; 9 | 10 | const APP_INFO: app_dirs2::AppInfo = app_dirs2::AppInfo { 11 | name: "Tectonic", 12 | author: "TectonicProject", 13 | }; 14 | 15 | #[cfg(feature = "serialization")] 16 | pub fn user_config() -> Result { 17 | Ok(app_dirs2::app_root(AppDataType::UserConfig, &APP_INFO)?) 18 | } 19 | 20 | #[cfg(feature = "serialization")] 21 | pub fn get_user_config() -> Result { 22 | Ok(app_dirs2::get_app_root(AppDataType::UserConfig, &APP_INFO)?) 23 | } 24 | 25 | pub fn user_cache_dir(path: &str) -> Result { 26 | Ok(app_dirs2::app_dir(AppDataType::UserCache, &APP_INFO, path)?) 27 | } 28 | -------------------------------------------------------------------------------- /src/digest.rs: -------------------------------------------------------------------------------- 1 | // src/digest.rs -- thin wrapper for digest computations 2 | // Copyright 2017-2018 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | //! Helpers to tidy up the computation of digests in various places. 6 | 7 | pub use sha2::Digest; 8 | pub use sha2::Sha256 as DigestComputer; 9 | use std::fs; 10 | use std::path::{Path, PathBuf}; 11 | use std::str::FromStr; 12 | use std::string::ToString; 13 | 14 | use crate::errors::{Error, ErrorKind, Result}; 15 | 16 | // Generic helpers 17 | 18 | pub fn bytes_to_hex(bytes: &[u8]) -> String { 19 | bytes 20 | .iter() 21 | .map(|b| format!("{:02x}", b)) 22 | .collect::>() 23 | .concat() 24 | } 25 | 26 | pub fn hex_to_bytes(text: &str, dest: &mut [u8]) -> Result<()> { 27 | let n = dest.len(); 28 | let text_len = text.len(); 29 | 30 | if text_len != 2 * n { 31 | return Err(ErrorKind::BadLength(2 * n, text_len).into()); 32 | } 33 | 34 | for i in 0..n { 35 | dest[i] = u8::from_str_radix(&text[i * 2..(i + 1) * 2], 16)?; 36 | } 37 | 38 | Ok(()) 39 | } 40 | 41 | // The specific implementation we're using: SHA256. 42 | 43 | const N_BYTES: usize = 32; 44 | pub const DIGEST_NAME: &str = "SHA256SUM"; 45 | pub const DIGEST_LEN: usize = 64; 46 | 47 | pub fn create() -> DigestComputer { 48 | Default::default() 49 | } 50 | 51 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] 52 | pub struct DigestData([u8; N_BYTES]); 53 | 54 | impl DigestData { 55 | pub fn zeros() -> DigestData { 56 | DigestData([0u8; N_BYTES]) 57 | } 58 | 59 | pub fn of_nothing() -> DigestData { 60 | let dc = create(); 61 | Self::from(dc) 62 | } 63 | 64 | /// Given a base path, create a child path from this digest's value. The 65 | /// child path has a subdirectory from the hex value of the first byte of 66 | /// the digest, then a name consisting of the rest of the hex data. **The 67 | /// first-byte subdirectory and all parent directories are created when 68 | /// you call this function!** 69 | pub fn create_two_part_path(&self, base: &Path) -> Result { 70 | let mut p = base.to_path_buf(); 71 | p.push(format!("{:02x}", self.0[0])); 72 | fs::create_dir_all(&p)?; 73 | p.push(bytes_to_hex(&self.0[1..])); 74 | Ok(p) 75 | } 76 | } 77 | 78 | impl ToString for DigestData { 79 | fn to_string(&self) -> String { 80 | bytes_to_hex(&self.0) 81 | } 82 | } 83 | 84 | impl FromStr for DigestData { 85 | type Err = Error; 86 | 87 | fn from_str(s: &str) -> Result { 88 | let mut result = DigestData::zeros(); 89 | hex_to_bytes(s, &mut result.0)?; 90 | Ok(result) 91 | } 92 | } 93 | 94 | impl From for DigestData { 95 | fn from(s: DigestComputer) -> DigestData { 96 | let mut result = DigestData::zeros(); 97 | let res = s.result(); 98 | result.0.copy_from_slice(res.as_slice()); 99 | result 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/engines/bibtex.rs: -------------------------------------------------------------------------------- 1 | // src/engines/bibtex.rs -- Rustic interface to the bibtex processor. 2 | // Copyright 2017 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | use std::ffi::CString; 6 | 7 | use super::tex::TexResult; 8 | use super::{ExecutionState, IoEventBackend, TectonicBridgeApi}; 9 | use crate::errors::{ErrorKind, Result}; 10 | use crate::io::IoStack; 11 | use crate::status::StatusBackend; 12 | use crate::unstable_opts::UnstableOptions; 13 | 14 | const MIN_CROSSREFS: i32 = 2; 15 | 16 | #[derive(Default)] 17 | pub struct BibtexEngine {} 18 | 19 | impl BibtexEngine { 20 | pub fn new() -> BibtexEngine { 21 | Default::default() 22 | } 23 | 24 | pub fn process( 25 | &mut self, 26 | io: &mut IoStack, 27 | events: &mut dyn IoEventBackend, 28 | status: &mut dyn StatusBackend, 29 | aux: &str, 30 | unstables: &UnstableOptions, 31 | ) -> Result { 32 | let _guard = super::ENGINE_LOCK.lock().unwrap(); // until we're thread-safe ... 33 | 34 | let caux = CString::new(aux)?; 35 | 36 | let /*mut*/ state = ExecutionState::new(io, events, status); 37 | let bridge = TectonicBridgeApi::new(&state); 38 | 39 | let config = super::BibtexConfig { 40 | min_crossrefs: unstables.min_crossrefs.unwrap_or(MIN_CROSSREFS), 41 | }; 42 | 43 | unsafe { 44 | match super::bibtex_simple_main(&*bridge, &config, caux.as_ptr()) { 45 | 0 => Ok(TexResult::Spotless), 46 | 1 => Ok(TexResult::Warnings), 47 | 2 => Ok(TexResult::Errors), 48 | 3 => Err(ErrorKind::Msg("unspecified fatal bibtex error".into()).into()), 49 | 99 => { 50 | let msg = super::tt_get_error_message().to_string(); 51 | Err(ErrorKind::Msg(msg).into()) 52 | } 53 | x => Err(ErrorKind::Msg(format!( 54 | "internal error: unexpected 'history' value {}", 55 | x 56 | )) 57 | .into()), 58 | } 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/engines/spx2html.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | //! Convert Tectonic’s SPX format to HTML 5 | //! 6 | //! Yay, an engine actually written in pure Rust! 7 | 8 | use std::ffi::OsStr; 9 | use std::io::Write; 10 | use tectonic_xdv::{FileType, XdvEvents, XdvParser}; 11 | 12 | use super::IoEventBackend; 13 | use crate::errors::{Error, Result}; 14 | use crate::io::{IoProvider, IoStack, OpenResult, OutputHandle}; 15 | use crate::status::StatusBackend; 16 | use crate::{errmsg, tt_warning}; 17 | 18 | #[derive(Default)] 19 | pub struct Spx2HtmlEngine {} 20 | 21 | impl Spx2HtmlEngine { 22 | pub fn new() -> Spx2HtmlEngine { 23 | Default::default() 24 | } 25 | 26 | pub fn process( 27 | &mut self, 28 | io: &mut IoStack, 29 | events: &mut dyn IoEventBackend, 30 | status: &mut dyn StatusBackend, 31 | spx: &str, 32 | ) -> Result<()> { 33 | let mut input = io.input_open_name(OsStr::new(spx), status).must_exist()?; 34 | events.input_opened(input.name(), input.origin()); 35 | 36 | // FIXME? The engine should probably be responsible for choosing this. 37 | let outname = { 38 | let mut s = spx.to_owned(); 39 | 40 | if spx.ends_with(".spx") { 41 | s.truncate(s.len() - 4); 42 | } 43 | 44 | s.push_str(".html"); 45 | s 46 | }; 47 | 48 | { 49 | let state = State::new(outname, io, events, status); 50 | let (state, _n_bytes) = XdvParser::process(&mut input, state)?; 51 | state.finished(); 52 | } 53 | 54 | let (name, digest_opt) = input.into_name_digest(); 55 | events.input_closed(name, digest_opt); 56 | 57 | Ok(()) 58 | } 59 | } 60 | 61 | struct State<'a, 'b: 'a> { 62 | outname: String, 63 | io: &'a mut IoStack<'b>, 64 | events: &'a mut dyn IoEventBackend, 65 | status: &'a mut dyn StatusBackend, 66 | cur_output: Option, 67 | warned_lost_chars: bool, 68 | buf: Vec, 69 | } 70 | 71 | impl<'a, 'b: 'a> State<'a, 'b> { 72 | pub fn new( 73 | outname: String, 74 | io: &'a mut IoStack<'b>, 75 | events: &'a mut dyn IoEventBackend, 76 | status: &'a mut dyn StatusBackend, 77 | ) -> Self { 78 | Self { 79 | outname, 80 | io, 81 | events, 82 | status, 83 | cur_output: None, 84 | warned_lost_chars: false, 85 | buf: Vec::new(), 86 | } 87 | } 88 | } 89 | 90 | #[inline(always)] 91 | fn as_printable_ascii(c: i32) -> Option { 92 | if c > 0x20 && c < 0x7F { 93 | Some(c as u8) 94 | } else { 95 | None 96 | } 97 | } 98 | 99 | impl<'a, 'b: 'a> State<'a, 'b> { 100 | pub fn finished(self) { 101 | if let Some(oh) = self.cur_output { 102 | let (name, digest) = oh.into_name_digest(); 103 | self.events.output_closed(name, digest); 104 | } 105 | } 106 | } 107 | 108 | impl<'a, 'b: 'a> XdvEvents for State<'a, 'b> { 109 | type Error = Error; 110 | 111 | fn handle_header(&mut self, filetype: FileType, _comment: &[u8]) -> Result<()> { 112 | if filetype != FileType::Spx { 113 | return Err(errmsg!("file should be SPX format but got {}", filetype)); 114 | } 115 | 116 | self.cur_output = Some(match self.io.output_open_name(OsStr::new(&self.outname)) { 117 | OpenResult::Ok(h) => h, 118 | OpenResult::NotAvailable => { 119 | return Err(errmsg!("no way to write output file \"{}\"", self.outname)); 120 | } 121 | OpenResult::Err(e) => { 122 | return Err(e); 123 | } 124 | }); 125 | 126 | self.events.output_opened(OsStr::new(&self.outname)); 127 | 128 | Ok(()) 129 | } 130 | 131 | fn handle_char_run(&mut self, chars: &[i32]) -> Result<()> { 132 | let dest = match self.cur_output { 133 | Some(ref mut h) => h, 134 | None => { 135 | if !self.warned_lost_chars { 136 | tt_warning!( 137 | self.status, 138 | "losing characters in SPX file: no current output" 139 | ); 140 | self.warned_lost_chars = true; 141 | } 142 | 143 | return Ok(()); 144 | } 145 | }; 146 | 147 | self.buf.clear(); 148 | 149 | for c in chars.iter() { 150 | if let Some(b) = as_printable_ascii(*c) { 151 | self.buf.push(b); 152 | } 153 | } 154 | 155 | if !self.buf.is_empty() { 156 | self.buf.push(0x0a); // newline 157 | dest.write_all(&self.buf)?; 158 | } 159 | 160 | Ok(()) 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/engines/xdvipdfmx.rs: -------------------------------------------------------------------------------- 1 | // src/engines/xdvipdfmx.rs -- Rustic interface to the xdvipdfmx translator. 2 | // Copyright 2017 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | use super::{ExecutionState, IoEventBackend, TectonicBridgeApi}; 6 | use crate::errors::{ErrorKind, Result}; 7 | use crate::io::IoStack; 8 | use crate::status::StatusBackend; 9 | use crate::unstable_opts::UnstableOptions; 10 | 11 | pub struct XdvipdfmxEngine { 12 | enable_compression: bool, 13 | deterministic_tags: bool, 14 | } 15 | 16 | impl XdvipdfmxEngine { 17 | pub fn new() -> XdvipdfmxEngine { 18 | XdvipdfmxEngine { 19 | enable_compression: true, 20 | deterministic_tags: false, 21 | } 22 | } 23 | 24 | pub fn with_compression(mut self, enable_compression: bool) -> Self { 25 | self.enable_compression = enable_compression; 26 | self 27 | } 28 | 29 | pub fn with_deterministic_tags(mut self, flag: bool) -> Self { 30 | self.deterministic_tags = flag; 31 | self 32 | } 33 | 34 | pub fn process( 35 | &mut self, 36 | io: &mut IoStack, 37 | events: &mut dyn IoEventBackend, 38 | status: &mut dyn StatusBackend, 39 | dvi: &str, 40 | pdf: &str, 41 | unstables: &UnstableOptions, 42 | ) -> Result { 43 | let _guard = super::ENGINE_LOCK.lock().unwrap(); // until we're thread-safe ... 44 | 45 | let paperspec_str = unstables.paper_size.clone(); 46 | 47 | // We default to "letter" paper size by default 48 | let config = super::XdvipdfmxConfig { 49 | paperspec: paperspec_str.map_or("letter".into(), |s| s.into()), 50 | }; 51 | 52 | let /*mut*/ state = ExecutionState::new(io, events, status); 53 | let bridge = TectonicBridgeApi::new(&state); 54 | 55 | unsafe { 56 | match super::dvipdfmx_simple_main( 57 | &*bridge, 58 | &config, 59 | dvi, 60 | pdf, 61 | self.enable_compression, 62 | self.deterministic_tags, 63 | ) { 64 | 99 => { 65 | let msg = super::tt_get_error_message().to_string(); 66 | Err(ErrorKind::Msg(msg).into()) 67 | } 68 | x => Ok(x as i32), 69 | } 70 | } 71 | } 72 | } 73 | 74 | impl Default for XdvipdfmxEngine { 75 | fn default() -> Self { 76 | XdvipdfmxEngine::new() 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/io/dirbundle.rs: -------------------------------------------------------------------------------- 1 | use std::ffi::OsStr; 2 | use std::fs::File; 3 | use std::io::BufReader; 4 | use std::path::PathBuf; 5 | 6 | use super::{Bundle, InputHandle, InputOrigin, IoProvider, OpenResult}; 7 | use crate::status::StatusBackend; 8 | 9 | pub struct DirBundle { 10 | dir: PathBuf, 11 | } 12 | 13 | impl DirBundle { 14 | pub fn new(dir: PathBuf) -> DirBundle { 15 | DirBundle { dir } 16 | } 17 | } 18 | 19 | impl IoProvider for DirBundle { 20 | fn input_open_name( 21 | &mut self, 22 | name: &OsStr, 23 | _status: &mut dyn StatusBackend, 24 | ) -> OpenResult { 25 | let mut path = self.dir.clone(); 26 | path.push(name); 27 | 28 | if path.is_file() { 29 | match File::open(path) { 30 | Err(e) => OpenResult::Err(e.into()), 31 | Ok(f) => OpenResult::Ok(InputHandle::new( 32 | name, 33 | BufReader::new(f), 34 | InputOrigin::Filesystem, 35 | )), 36 | } 37 | } else { 38 | OpenResult::NotAvailable 39 | } 40 | } 41 | } 42 | 43 | impl Bundle for DirBundle {} 44 | -------------------------------------------------------------------------------- /src/io/format_cache.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | #![deny(missing_docs)] 5 | 6 | //! Code for locally caching compiled format files. 7 | 8 | use std::ffi::OsStr; 9 | use std::io::{BufReader, Write}; 10 | use std::path::PathBuf; 11 | 12 | use super::{InputHandle, InputOrigin, IoProvider, OpenResult}; 13 | use crate::digest::DigestData; 14 | use crate::errors::{ErrorKind, Result}; 15 | use crate::status::StatusBackend; 16 | 17 | /// A local cache for compiled format files. 18 | /// 19 | /// The format cache takes care of saving compiled format files. It uses the 20 | /// same root cache directory as the `LocalCache` item, but is implemented 21 | /// separately so that there is a way to save the format files associated with 22 | /// backends that may not have their own LocalCache. 23 | pub struct FormatCache { 24 | bundle_digest: DigestData, 25 | formats_base: PathBuf, 26 | } 27 | 28 | impl FormatCache { 29 | /// Create a new `FormatCache`. 30 | /// 31 | /// The `bundle_digest` should be the result of the `Bundle::get_digest()` 32 | /// call for whichever bundle is active. The `formats_base` path should be 33 | /// a local cache directory. 34 | pub fn new(bundle_digest: DigestData, formats_base: PathBuf) -> FormatCache { 35 | FormatCache { 36 | bundle_digest, 37 | formats_base, 38 | } 39 | } 40 | 41 | /// Get an on-disk path name for a given format file. This function simply 42 | /// produces a path that may or may not exist. 43 | fn path_for_format(&mut self, name: &OsStr) -> Result { 44 | // Remove all extensions from the format name. PathBuf.file_stem() doesn't 45 | // do what we want since it only strips one extension, so here we go: 46 | 47 | let stem = match name.to_str().and_then(|s| s.splitn(2, '.').next()) { 48 | Some(s) => s, 49 | None => { 50 | return Err(ErrorKind::Msg(format!( 51 | "incomprehensible format file name \"{}\"", 52 | name.to_string_lossy() 53 | )) 54 | .into()); 55 | } 56 | }; 57 | 58 | let mut p = self.formats_base.clone(); 59 | p.push(format!( 60 | "{}-{}-{}.fmt", 61 | self.bundle_digest.to_string(), 62 | stem, 63 | crate::FORMAT_SERIAL 64 | )); 65 | Ok(p) 66 | } 67 | } 68 | 69 | impl IoProvider for FormatCache { 70 | fn input_open_format( 71 | &mut self, 72 | name: &OsStr, 73 | _status: &mut dyn StatusBackend, 74 | ) -> OpenResult { 75 | let path = match self.path_for_format(name) { 76 | Ok(p) => p, 77 | Err(e) => return OpenResult::Err(e), 78 | }; 79 | 80 | let f = match super::try_open_file(&path) { 81 | OpenResult::Ok(f) => f, 82 | OpenResult::NotAvailable => return OpenResult::NotAvailable, 83 | OpenResult::Err(e) => return OpenResult::Err(e), 84 | }; 85 | 86 | OpenResult::Ok(InputHandle::new_read_only( 87 | name, 88 | BufReader::new(f), 89 | InputOrigin::Other, 90 | )) 91 | } 92 | 93 | fn write_format( 94 | &mut self, 95 | name: &str, 96 | data: &[u8], 97 | _status: &mut dyn StatusBackend, 98 | ) -> Result<()> { 99 | let final_path = self.path_for_format(OsStr::new(name))?; 100 | let mut temp_dest = tempfile::Builder::new() 101 | .prefix("format_") 102 | .rand_bytes(6) 103 | .tempfile_in(&self.formats_base)?; 104 | temp_dest.write_all(data)?; 105 | temp_dest.persist(&final_path)?; 106 | Ok(()) 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/io/stack.rs: -------------------------------------------------------------------------------- 1 | // src/io/stack.rs -- a stack of other IoProviders 2 | // Copyright 2016-2017 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | use std::ffi::OsStr; 6 | 7 | use super::{InputHandle, IoProvider, OpenResult, OutputHandle}; 8 | use crate::status::StatusBackend; 9 | 10 | /// An IoStack is an IoProvider that delegates to an ordered list of 11 | /// subordinate IoProviders. It also checks the order in which files are read 12 | /// and written to detect "circular" access patterns that indicate whether we 13 | /// need to run multiple passes of the TeX engine. 14 | 15 | pub struct IoStack<'a> { 16 | items: Vec<&'a mut dyn IoProvider>, 17 | } 18 | 19 | impl<'a> IoStack<'a> { 20 | pub fn new(items: Vec<&'a mut dyn IoProvider>) -> IoStack<'a> { 21 | IoStack { items } 22 | } 23 | } 24 | 25 | impl<'a> IoProvider for IoStack<'a> { 26 | fn output_open_name(&mut self, name: &OsStr) -> OpenResult { 27 | for item in &mut self.items { 28 | let r = item.output_open_name(name); 29 | 30 | match r { 31 | OpenResult::NotAvailable => continue, 32 | _ => return r, 33 | }; 34 | } 35 | 36 | OpenResult::NotAvailable 37 | } 38 | 39 | fn output_open_stdout(&mut self) -> OpenResult { 40 | for item in &mut self.items { 41 | let r = item.output_open_stdout(); 42 | 43 | match r { 44 | OpenResult::NotAvailable => continue, 45 | _ => return r, 46 | }; 47 | } 48 | 49 | OpenResult::NotAvailable 50 | } 51 | 52 | fn input_open_name( 53 | &mut self, 54 | name: &OsStr, 55 | status: &mut dyn StatusBackend, 56 | ) -> OpenResult { 57 | for item in &mut self.items { 58 | let r = item.input_open_name(name, status); 59 | 60 | match r { 61 | OpenResult::NotAvailable => continue, 62 | _ => return r, 63 | }; 64 | } 65 | 66 | OpenResult::NotAvailable 67 | } 68 | 69 | fn input_open_primary(&mut self, status: &mut dyn StatusBackend) -> OpenResult { 70 | for item in &mut self.items { 71 | let r = item.input_open_primary(status); 72 | 73 | match r { 74 | OpenResult::NotAvailable => continue, 75 | _ => return r, 76 | }; 77 | } 78 | 79 | OpenResult::NotAvailable 80 | } 81 | 82 | fn input_open_format( 83 | &mut self, 84 | name: &OsStr, 85 | status: &mut dyn StatusBackend, 86 | ) -> OpenResult { 87 | for item in &mut self.items { 88 | let r = item.input_open_format(name, status); 89 | 90 | match r { 91 | OpenResult::NotAvailable => continue, 92 | _ => return r, 93 | }; 94 | } 95 | 96 | OpenResult::NotAvailable 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/io/zipbundle.rs: -------------------------------------------------------------------------------- 1 | // src/io/zipbundle.rs -- I/O on files in a Zipped-up "bundle" 2 | // Copyright 2016-2018 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | use std::ffi::OsStr; 6 | use std::fs::File; 7 | use std::io::{Cursor, Read, Seek}; 8 | use std::path::Path; 9 | use zip::result::ZipError; 10 | use zip::ZipArchive; 11 | 12 | use super::{Bundle, InputHandle, InputOrigin, IoProvider, OpenResult}; 13 | use crate::errors::Result; 14 | use crate::status::StatusBackend; 15 | 16 | pub struct ZipBundle { 17 | zip: ZipArchive, 18 | } 19 | 20 | impl ZipBundle { 21 | pub fn new(reader: R) -> Result> { 22 | Ok(ZipBundle { 23 | zip: ZipArchive::new(reader)?, 24 | }) 25 | } 26 | } 27 | 28 | impl ZipBundle { 29 | pub fn open>(path: P) -> Result> { 30 | Self::new(File::open(path)?) 31 | } 32 | } 33 | 34 | impl IoProvider for ZipBundle { 35 | fn input_open_name( 36 | &mut self, 37 | name: &OsStr, 38 | _status: &mut dyn StatusBackend, 39 | ) -> OpenResult { 40 | // We need to be able to look at other items in the Zip file while 41 | // reading this one, so the only path forward is to read the entire 42 | // contents into a buffer right now. RAM is cheap these days. 43 | 44 | // If `name` cannot be converted to Unicode, we return NotAvailable. I 45 | // *think* that's what we should do. 46 | 47 | let namestr = match name.to_str() { 48 | Some(s) => s, 49 | None => return OpenResult::NotAvailable, 50 | }; 51 | 52 | let mut zipitem = match self.zip.by_name(namestr) { 53 | Ok(f) => f, 54 | Err(e) => { 55 | return match e { 56 | ZipError::Io(sube) => OpenResult::Err(sube.into()), 57 | ZipError::FileNotFound => OpenResult::NotAvailable, 58 | _ => OpenResult::Err(e.into()), 59 | }; 60 | } 61 | }; 62 | 63 | let mut buf = Vec::with_capacity(zipitem.size() as usize); 64 | 65 | if let Err(e) = zipitem.read_to_end(&mut buf) { 66 | return OpenResult::Err(e.into()); 67 | } 68 | 69 | OpenResult::Ok(InputHandle::new_read_only( 70 | name, 71 | Cursor::new(buf), 72 | InputOrigin::Other, 73 | )) 74 | } 75 | } 76 | 77 | impl Bundle for ZipBundle {} 78 | -------------------------------------------------------------------------------- /src/status/mod.rs: -------------------------------------------------------------------------------- 1 | // src/status/mod.rs -- communicating status updates to the user 2 | // Copyright 2017-2018 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | //! A framework for showing status messages to the user. 6 | 7 | pub mod plain; 8 | pub mod termcolor; 9 | 10 | use std::cmp; 11 | use std::fmt::Arguments; 12 | use std::result::Result as StdResult; 13 | use std::str::FromStr; 14 | 15 | use crate::errors::Error; 16 | 17 | #[repr(usize)] 18 | #[derive(Clone, Copy, Eq, Debug)] 19 | pub enum ChatterLevel { 20 | Minimal = 0, 21 | Normal, 22 | } 23 | 24 | impl FromStr for ChatterLevel { 25 | type Err = &'static str; 26 | 27 | fn from_str(a_str: &str) -> StdResult { 28 | match a_str { 29 | "default" => Ok(ChatterLevel::Normal), 30 | "minimal" => Ok(ChatterLevel::Minimal), 31 | _ => Err("unsupported or unknown chatter level"), 32 | } 33 | } 34 | } 35 | 36 | impl PartialEq for ChatterLevel { 37 | #[inline] 38 | fn eq(&self, other: &ChatterLevel) -> bool { 39 | *self as usize == *other as usize 40 | } 41 | } 42 | 43 | impl PartialOrd for ChatterLevel { 44 | #[inline] 45 | fn partial_cmp(&self, other: &ChatterLevel) -> Option { 46 | Some(self.cmp(other)) 47 | } 48 | } 49 | 50 | impl Ord for ChatterLevel { 51 | #[inline] 52 | fn cmp(&self, other: &ChatterLevel) -> cmp::Ordering { 53 | (*self as usize).cmp(&(*other as usize)) 54 | } 55 | } 56 | 57 | #[derive(Clone, Copy, PartialEq, Eq, Debug)] 58 | pub enum MessageKind { 59 | Note, 60 | Warning, 61 | Error, 62 | } 63 | 64 | pub trait StatusBackend { 65 | /// Report a message to the status backend. 66 | fn report(&mut self, kind: MessageKind, args: Arguments, err: Option<&Error>); 67 | 68 | /// Issue a note-level status, idealy highlighting a particular phrase. 69 | /// 70 | /// This is a bit of a hack. For [`driver::ProcessingSession::run`], I 71 | /// like the UX when we issue notes in this style. It's a bit more 72 | /// high-level than intended for this trait, but we can provide a nice 73 | /// sensible default implementation, so whatever. 74 | fn note_highlighted(&mut self, before: &str, highlighted: &str, after: &str) { 75 | self.report( 76 | MessageKind::Note, 77 | format_args!("{}{}{}", before, highlighted, after), 78 | None, 79 | ) 80 | } 81 | 82 | /// This is used to print TeX engine logs after it encountered errors. This prints the log, 83 | /// surrounded by lines of equal signs. 84 | fn dump_error_logs(&mut self, output: &[u8]); 85 | } 86 | 87 | /// Report a formatted informational message to the user. 88 | /// 89 | /// An `Error` object may be provided, in which case it will be shown to the 90 | /// user as well. Generally, though, one would expect to use `tt_warning!` or 91 | /// `tt_error!` if there’s an Error available. 92 | #[macro_export] 93 | macro_rules! tt_note { 94 | ($dest:expr, $( $fmt_args:expr ),*) => { 95 | $dest.report($crate::status::MessageKind::Note, format_args!($( $fmt_args ),*), None) 96 | }; 97 | ($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => { 98 | $dest.report($crate::status::MessageKind::Note, format_args!($( $fmt_args ),*), Some(&$err)) 99 | }; 100 | } 101 | 102 | /// Report a formatted warning message to the user. 103 | /// 104 | /// An `Error` object may be provided, in which case it will be shown to the 105 | /// user as well. 106 | #[macro_export] 107 | macro_rules! tt_warning { 108 | ($dest:expr, $( $fmt_args:expr ),*) => { 109 | $dest.report($crate::status::MessageKind::Warning, format_args!($( $fmt_args ),*), None) 110 | }; 111 | ($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => { 112 | $dest.report($crate::status::MessageKind::Warning, format_args!($( $fmt_args ),*), Some(&$err)) 113 | }; 114 | } 115 | 116 | /// Report a formatted error message to the user. 117 | /// 118 | /// An `Error` object may be provided, in which case it will be shown to the 119 | /// user as well. 120 | #[macro_export] 121 | macro_rules! tt_error { 122 | ($dest:expr, $( $fmt_args:expr ),*) => { 123 | $dest.report($crate::status::MessageKind::Error, format_args!($( $fmt_args ),*), None) 124 | }; 125 | ($dest:expr, $( $fmt_args:expr ),* ; $err:expr) => { 126 | $dest.report($crate::status::MessageKind::Error, format_args!($( $fmt_args ),*), Some(&$err)) 127 | }; 128 | } 129 | 130 | #[derive(Default)] 131 | pub struct NoopStatusBackend {} 132 | 133 | impl NoopStatusBackend { 134 | pub fn new() -> NoopStatusBackend { 135 | Default::default() 136 | } 137 | } 138 | 139 | impl StatusBackend for NoopStatusBackend { 140 | fn report(&mut self, _kind: MessageKind, _args: Arguments, _err: Option<&Error>) {} 141 | fn dump_error_logs(&mut self, _output: &[u8]) {} 142 | } 143 | -------------------------------------------------------------------------------- /src/status/plain.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Arguments; 2 | 3 | use super::{ChatterLevel, MessageKind, StatusBackend}; 4 | use crate::errors::Error; 5 | use std::io::{self, Write}; 6 | 7 | pub struct PlainStatusBackend { 8 | chatter: ChatterLevel, 9 | } 10 | 11 | impl PlainStatusBackend { 12 | pub fn new(chatter: ChatterLevel) -> Self { 13 | PlainStatusBackend { chatter } 14 | } 15 | } 16 | 17 | impl StatusBackend for PlainStatusBackend { 18 | fn report(&mut self, kind: MessageKind, args: Arguments, err: Option<&Error>) { 19 | if kind == MessageKind::Note && self.chatter <= ChatterLevel::Minimal { 20 | return; 21 | } 22 | 23 | let prefix = match kind { 24 | MessageKind::Note => "note:", 25 | MessageKind::Warning => "warning:", 26 | MessageKind::Error => "error:", 27 | }; 28 | if kind == MessageKind::Note { 29 | println!("{} {}", prefix, args); 30 | } else { 31 | eprintln!("{} {}", prefix, args); 32 | } 33 | if let Some(e) = err { 34 | for item in e.iter() { 35 | eprintln!("caused by: {}", item); 36 | } 37 | if let Some(backtrace) = e.backtrace() { 38 | eprintln!("debugging: backtrace follows:"); 39 | eprintln!("{:?}", backtrace); 40 | } 41 | } 42 | } 43 | 44 | fn note_highlighted(&mut self, before: &str, highlighted: &str, after: &str) { 45 | if self.chatter > ChatterLevel::Minimal { 46 | self.report( 47 | MessageKind::Note, 48 | format_args!("{}{}{}", before, highlighted, after), 49 | None, 50 | ); 51 | } 52 | } 53 | 54 | fn dump_error_logs(&mut self, output: &[u8]) { 55 | eprintln!( 56 | "===============================================================================" 57 | ); 58 | 59 | io::stderr() 60 | .write_all(output) 61 | .expect("write to stderr failed"); 62 | 63 | eprintln!( 64 | "===============================================================================" 65 | ); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/unstable_opts.rs: -------------------------------------------------------------------------------- 1 | // src/bin/tectonic.rs -- Command-line driver for the Tectonic engine. 2 | // Copyright 2020 the Tectonic Project 3 | // Licensed under the MIT License. 4 | 5 | //! Unstable options for the Tectonic engine. 6 | //! 7 | //! This is similar to the -Z options on rustc - they're unstable options that are not guaranteed 8 | //! to be reliable or very polished. In particular, many of these prevent the build from being 9 | //! reproducible. 10 | 11 | use crate::errors::{Error, Result}; 12 | use std::default::Default; 13 | use std::str::FromStr; 14 | 15 | const HELPMSG: &str = r#"Available unstable options: 16 | 17 | -Z help Lists all unstable options 18 | -Z continue-on-errors Keep compiling even when severe errors occur 19 | -Z min-crossrefs= Equivalent to bibtex's -min-crossrefs flag - "include after 20 | crossrefs" [default: 2] 21 | -Z paper-size= Change the default paper size [default: letter] 22 | "#; 23 | 24 | // Re-add this when \write18 gets implemented 25 | // -Z shell-escape Enable \write18 26 | 27 | // Each entry of this should correspond to a field of UnstableOptions. 28 | #[derive(Debug)] 29 | pub enum UnstableArg { 30 | ContinueOnErrors, 31 | Help, 32 | MinCrossrefs(i32), 33 | PaperSize(String), 34 | ShellEscapeEnabled, 35 | } 36 | 37 | impl FromStr for UnstableArg { 38 | type Err = Error; 39 | 40 | /// Parse from the argument to -Z 41 | fn from_str(s: &str) -> Result { 42 | let mut splitter = s.splitn(2, '='); 43 | let arg = splitter.next().unwrap(); // splitn will always have at least 1 item 44 | let value = splitter.next(); 45 | 46 | // For structopt/clap, if you pass a value to a flag which doesn't accept one, it's 47 | // silently ignored. 48 | 49 | match arg { 50 | "help" => Ok(UnstableArg::Help), 51 | 52 | "continue-on-errors" => Ok(UnstableArg::ContinueOnErrors), 53 | 54 | "min-crossrefs" => value 55 | .ok_or_else(|| { 56 | "'-Z min-crossrefs ' requires a value but none was supplied".into() 57 | }) 58 | .and_then(|s| { 59 | FromStr::from_str(s).map_err(|e| format!("-Z min-crossrefs: {}", e).into()) 60 | }) 61 | .map(UnstableArg::MinCrossrefs), 62 | 63 | "paper-size" => value 64 | .ok_or_else(|| { 65 | "'-Z paper-size ' requires a value but none was supplied".into() 66 | }) 67 | .map(|s| UnstableArg::PaperSize(s.to_string())), 68 | 69 | // Re-add this when \write18 gets implemetned 70 | // "shell-escape" => Ok(UnstableArg::ShellEscapeEnabled), 71 | _ => Err(format!("Unknown unstable option '{}'", arg).into()), 72 | } 73 | } 74 | } 75 | 76 | #[derive(Debug, Default)] 77 | pub struct UnstableOptions { 78 | pub continue_on_errors: bool, 79 | pub paper_size: Option, 80 | pub shell_escape: bool, 81 | pub min_crossrefs: Option, 82 | } 83 | 84 | impl UnstableOptions { 85 | pub fn from_unstable_args(uargs: I) -> Self 86 | where 87 | I: Iterator, 88 | { 89 | let mut opts = UnstableOptions::default(); 90 | 91 | for u in uargs { 92 | use UnstableArg::*; 93 | match u { 94 | Help => { 95 | print!("{}", HELPMSG); 96 | std::process::exit(0); 97 | } 98 | ContinueOnErrors => opts.continue_on_errors = true, 99 | MinCrossrefs(num) => opts.min_crossrefs = Some(num), 100 | PaperSize(size) => opts.paper_size = Some(size), 101 | ShellEscapeEnabled => opts.shell_escape = true, 102 | } 103 | } 104 | 105 | opts 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /tests/assets/ckx.map: -------------------------------------------------------------------------------- 1 | %% ckx.map (Chinese and Korean): CID-keyed font mapping file for dvipdfmx 2 | %% ====================================================================== 3 | %% 4 | %% This file is read by dvipdfmx in TeX Live 5 | %% See the file cid-x.map for a specification of the format. 6 | %% 7 | 8 | %% Aleph 9 | %% 10 | %% Chinese 11 | %% Arphic TrueType fonts as Adobe-{CNS1,GB1} CID-keyed font. 12 | %ombkai UniCNS-UCS2-H bkai00mp 13 | %ombsmi UniCNS-UCS2-H bsmi00lp 14 | %omgkai UniGB-UCS2-H gkai00mp 15 | %omgbsn UniGB-UCS2-H gbsn00lp 16 | 17 | %% Korean 18 | %omgtm UniKS-UCS2-H HYGoThic-Medium 19 | %ombtr UniKS-UCS2-H batang 20 | 21 | %% 22 | %% CJK-LaTeX/HLaTeX Examples 23 | %% 24 | 25 | %% Arphic Public Licence fonts: 26 | %% AR PL KaitiM, AR PL Mingti2L, AR PL SungtiL 27 | 28 | %bkai@Big5@ ETen-B5-H bkai00mp 29 | %bsmi@Big5@ ETen-B5-H bsmi00lp 30 | %bkaiv@Big5@ ETen-B5-V bkai00mp 31 | %bsmiv@Big5@ ETen-B5-V bsmi00lp 32 | 33 | %% Korean MS-Windows fonts with stylistic variants 34 | %jbtm@UKS@ UniKS-UCS2-H !batang 35 | %jbtmo@UKS@ UniKS-UCS2-H !batang,Italic 36 | %jbtb@UKS@ UniKS-UCS2-H !batang,Bold 37 | %jbtbo@UKS@ UniKS-UCS2-H !batang,BoldItalic 38 | 39 | %% Baekmuk fonts (used with HLaTeX package) 40 | %bbtm@KS-HLaTeX@ KSCms-UHC-H batang 41 | %bbtmo@UKS-HLaTeX@ UniKS-UCS2-H batang -s .167 42 | 43 | %% Bitstream Cyberbit 44 | %% Available at: 45 | %% http://ftp.netscape.com/pub/communicator/extras/fonts/windows/ReadMe.htm 46 | %cyberb@Unicode@ unicode cyberbit 47 | -------------------------------------------------------------------------------- /tests/assets/cmbsy10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbsy10.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx10.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx5.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx5.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx6.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx6.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx7.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx7.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx8.tfm -------------------------------------------------------------------------------- /tests/assets/cmbx9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmbx9.tfm -------------------------------------------------------------------------------- /tests/assets/cmcsc10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmcsc10.tfm -------------------------------------------------------------------------------- /tests/assets/cmdunh10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmdunh10.tfm -------------------------------------------------------------------------------- /tests/assets/cmex10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmex10.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi10.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi5.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi5.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi6.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi6.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi7.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi7.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi8.tfm -------------------------------------------------------------------------------- /tests/assets/cmmi9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmi9.tfm -------------------------------------------------------------------------------- /tests/assets/cmmib10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmmib10.tfm -------------------------------------------------------------------------------- /tests/assets/cmr10.pfb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr10.pfb -------------------------------------------------------------------------------- /tests/assets/cmr10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr10.tfm -------------------------------------------------------------------------------- /tests/assets/cmr5.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr5.tfm -------------------------------------------------------------------------------- /tests/assets/cmr6.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr6.tfm -------------------------------------------------------------------------------- /tests/assets/cmr7.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr7.tfm -------------------------------------------------------------------------------- /tests/assets/cmr8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr8.tfm -------------------------------------------------------------------------------- /tests/assets/cmr9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmr9.tfm -------------------------------------------------------------------------------- /tests/assets/cmsl10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsl10.tfm -------------------------------------------------------------------------------- /tests/assets/cmsl8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsl8.tfm -------------------------------------------------------------------------------- /tests/assets/cmsl9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsl9.tfm -------------------------------------------------------------------------------- /tests/assets/cmsltt10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsltt10.tfm -------------------------------------------------------------------------------- /tests/assets/cmss10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmss10.tfm -------------------------------------------------------------------------------- /tests/assets/cmssbx10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmssbx10.tfm -------------------------------------------------------------------------------- /tests/assets/cmssi10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmssi10.tfm -------------------------------------------------------------------------------- /tests/assets/cmssq8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmssq8.tfm -------------------------------------------------------------------------------- /tests/assets/cmssqi8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmssqi8.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy10.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy5.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy5.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy6.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy6.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy7.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy7.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy8.tfm -------------------------------------------------------------------------------- /tests/assets/cmsy9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmsy9.tfm -------------------------------------------------------------------------------- /tests/assets/cmti10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmti10.tfm -------------------------------------------------------------------------------- /tests/assets/cmti7.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmti7.tfm -------------------------------------------------------------------------------- /tests/assets/cmti8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmti8.tfm -------------------------------------------------------------------------------- /tests/assets/cmti9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmti9.tfm -------------------------------------------------------------------------------- /tests/assets/cmtt10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmtt10.tfm -------------------------------------------------------------------------------- /tests/assets/cmtt8.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmtt8.tfm -------------------------------------------------------------------------------- /tests/assets/cmtt9.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmtt9.tfm -------------------------------------------------------------------------------- /tests/assets/cmu10.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/cmu10.tfm -------------------------------------------------------------------------------- /tests/assets/issue393_ungetc_trigger.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/issue393_ungetc_trigger.pdf -------------------------------------------------------------------------------- /tests/assets/lmroman12-regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/lmroman12-regular.otf -------------------------------------------------------------------------------- /tests/assets/manfnt.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/manfnt.tfm -------------------------------------------------------------------------------- /tests/assets/plain.tex: -------------------------------------------------------------------------------- 1 | \input knuth-plain \dump 2 | -------------------------------------------------------------------------------- /tests/assets/png_gray_4bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_gray_4bit.png -------------------------------------------------------------------------------- /tests/assets/png_graya.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_graya.png -------------------------------------------------------------------------------- /tests/assets/png_palette_4bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_palette_4bit.png -------------------------------------------------------------------------------- /tests/assets/png_palette_alpha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_palette_alpha.png -------------------------------------------------------------------------------- /tests/assets/png_rgba.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_rgba.png -------------------------------------------------------------------------------- /tests/assets/png_rgba_16_bit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/png_rgba_16_bit.png -------------------------------------------------------------------------------- /tests/assets/redbox.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/assets/redbox.png -------------------------------------------------------------------------------- /tests/assets/tectonic-format-latex.tex: -------------------------------------------------------------------------------- 1 | % Copyright 2018 the Tectonic Project 2 | % Licensed under the MIT License. 3 | % 4 | % HACK ALERT!!!! 5 | % 6 | % Most parts of Tectonic default to using the "latex" format because that's 7 | % what people almost always want. We also sometimes want to show "best 8 | % practices" examples in the docs that will call for specifying the "latex" 9 | % format. That's all good, but it does complicate the test suite -- we'd like 10 | % to be able to run doctests or executable tests without having to actually go 11 | % ahead and download the core set of LaTeX support files from whatever the 12 | % current bundle is. 13 | % 14 | % This file is a hack to help enable this. It extends the "plain" format with 15 | % an absolutely minimal set of control sequences necessary to compile fake 16 | % documents that look like LaTeX. This helps our documentation look more 17 | % realistic while avoiding making the test suite much more heavyweight. 18 | 19 | \input knuth-plain% 20 | \message{THIS IS A FAKE LATEX FORMAT}% 21 | 22 | \def\fmtname{fakelatex}% 23 | \def\fmtversion{1.0}% 24 | \def\documentclass#1{}% 25 | \def\begin#1{}% 26 | \let\realend=\end% 27 | \def\end#1{\par\vfill\supereject\realend}% 28 | 29 | \dump 30 | -------------------------------------------------------------------------------- /tests/assets/tectonic-format-plain.tex: -------------------------------------------------------------------------------- 1 | \input plain \dump 2 | -------------------------------------------------------------------------------- /tests/bibtex.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2016-2019 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | use std::collections::HashSet; 5 | use std::default::Default; 6 | 7 | use tectonic::engines::NoopIoEventBackend; 8 | use tectonic::io::stdstreams::GenuineStdoutIo; 9 | use tectonic::io::{FilesystemIo, IoProvider, IoStack, MemoryIo}; 10 | use tectonic::status::NoopStatusBackend; 11 | use tectonic::BibtexEngine; 12 | 13 | #[path = "util/mod.rs"] 14 | mod util; 15 | use crate::util::{test_path, ExpectedInfo}; 16 | 17 | struct TestCase { 18 | stem: String, 19 | } 20 | 21 | impl TestCase { 22 | fn new(stem: &str) -> Self { 23 | TestCase { 24 | stem: stem.to_owned(), 25 | } 26 | } 27 | 28 | fn go(&mut self) { 29 | util::set_test_root(); 30 | 31 | let mut p = test_path(&["bibtex"]); 32 | 33 | p.push(&self.stem); 34 | 35 | p.set_extension("aux"); 36 | let auxname = p.file_name().unwrap().to_str().unwrap().to_owned(); 37 | 38 | // MemoryIo layer that will accept the outputs. 39 | let mut mem = MemoryIo::new(true); 40 | 41 | let mut assets = FilesystemIo::new(&test_path(&["bibtex"]), false, false, HashSet::new()); 42 | 43 | let mut genio = GenuineStdoutIo::new(); 44 | 45 | let io_list: Vec<&mut dyn IoProvider> = vec![&mut genio, &mut mem, &mut assets]; 46 | 47 | let mut io = IoStack::new(io_list); 48 | 49 | let mut events = NoopIoEventBackend::new(); 50 | let mut status = NoopStatusBackend::new(); 51 | 52 | BibtexEngine::new() 53 | .process( 54 | &mut io, 55 | &mut events, 56 | &mut status, 57 | &auxname, 58 | &Default::default(), 59 | ) 60 | .unwrap(); 61 | 62 | // Check that outputs match expectations. 63 | 64 | let expected_bbl = ExpectedInfo::read_with_extension(&mut p, "bbl"); 65 | let expected_blg = ExpectedInfo::read_with_extension(&mut p, "blg"); 66 | 67 | let files = mem.files.borrow(); 68 | 69 | expected_bbl.test_from_collection(&files); 70 | expected_blg.test_from_collection(&files); 71 | } 72 | } 73 | 74 | #[test] 75 | fn single_entry() { 76 | TestCase::new("single_entry").go() 77 | } 78 | -------------------------------------------------------------------------------- /tests/bibtex/single_entry.aux: -------------------------------------------------------------------------------- 1 | \relax 2 | \citation{Nobody06} 3 | \bibdata{single_entry} 4 | \bibcite{Nobody06}{1} 5 | \bibstyle{plain} 6 | -------------------------------------------------------------------------------- /tests/bibtex/single_entry.bbl: -------------------------------------------------------------------------------- 1 | \begin{thebibliography}{1} 2 | 3 | \bibitem{Nobody06} 4 | Nobody Jr. 5 | \newblock My article, 2006. 6 | 7 | \end{thebibliography} 8 | -------------------------------------------------------------------------------- /tests/bibtex/single_entry.bib: -------------------------------------------------------------------------------- 1 | @misc{ Nobody06, 2 | author = "Nobody Jr", 3 | title = "My Article", 4 | year = "2006" } 5 | -------------------------------------------------------------------------------- /tests/bibtex/single_entry.blg: -------------------------------------------------------------------------------- 1 | This is BibTeX, Version 0.99d 2 | Capacity: max_strings=35307, hash_size=35307, hash_prime=30011 3 | The top-level auxiliary file: single_entry.aux 4 | The style file: plain.bst 5 | Database file #1: single_entry.bib 6 | -------------------------------------------------------------------------------- /tests/driver.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | //! This test rig is a total hack to quickly exercise `src/driver.rs`. 5 | //! 6 | //! I should make it real, but I just want Codecov to stop complaining about 7 | //! my test coverage. To re-use the `tex-outputs` test artifacts, the 8 | //! ProcessingSessionBuilder will need to learn how to tell `xdvipdfmx` to 9 | //! enable the reproducibility options used in the `tex-outputs` test rig. 10 | 11 | use tectonic::config::PersistentConfig; 12 | use tectonic::driver::ProcessingSessionBuilder; 13 | use tectonic::status::termcolor::TermcolorStatusBackend; 14 | use tectonic::status::ChatterLevel; 15 | 16 | mod util; 17 | 18 | // Keep these alphabetized. 19 | 20 | #[test] 21 | fn the_letter_a() { 22 | util::set_test_root(); 23 | 24 | let _config = PersistentConfig::default(); 25 | 26 | // The "Normal" chatter escapes the test rig's attempts to eat stdout ... 27 | let mut status = TermcolorStatusBackend::new(ChatterLevel::Minimal); 28 | 29 | let bundle = util::TestBundle::default(); 30 | 31 | let tempdir = tempfile::Builder::new() 32 | .prefix("tectonic_driver_test") 33 | .tempdir() 34 | .unwrap(); 35 | 36 | let mut pbuilder = ProcessingSessionBuilder::default(); 37 | pbuilder 38 | .primary_input_path(util::test_path(&["tex-outputs", "the_letter_a.tex"])) 39 | .tex_input_name("the_letter_a.tex") 40 | .format_name("plain") 41 | .format_cache_path(util::test_path(&[])) 42 | .output_dir(tempdir.path()) 43 | .bundle(Box::new(bundle)); 44 | 45 | let mut session = pbuilder 46 | .create(&mut status) 47 | .expect("couldn't create processing session"); 48 | 49 | session 50 | .run(&mut status) 51 | .expect("failed to execute processing session"); 52 | } 53 | -------------------------------------------------------------------------------- /tests/executable/subdirectory/content/1.tex: -------------------------------------------------------------------------------- 1 | This is content/1.tex 2 | 3 | \bye 4 | -------------------------------------------------------------------------------- /tests/executable/subdirectory/relative_include.tex: -------------------------------------------------------------------------------- 1 | This is the main document. 2 | 3 | \input content/1.tex 4 | 5 | \bye 6 | -------------------------------------------------------------------------------- /tests/executable/test space.tex: -------------------------------------------------------------------------------- 1 | This file has a name which contains spaces. 2 | 3 | \bye 4 | -------------------------------------------------------------------------------- /tests/formats.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2017-2018 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | /// Test that we can operate in "initex" mode to generate format files as 5 | /// expected. Unlike TeX we set things up so that formats should be 6 | /// byte-for-byte reproducible given the same inputs. Since formats are big, 7 | /// we check for equality by examining their SHA256 digests. 8 | /// 9 | /// Note that since gzip compression is done transparently in the I/O layer, 10 | /// the desired SHA256 is that of the *uncompressed* format data, not the 11 | /// gzipped file that ends up on disk. When implementing this test I wasted a 12 | /// lot of time on that mistake! 13 | /// 14 | /// Temporarily set the constant DEBUG to true to dump out the generated files 15 | /// to disk, which may be helpful in debugging. There is probably a less gross 16 | /// way to implement that option. 17 | use std::collections::{HashMap, HashSet}; 18 | use std::default::Default; 19 | use std::ffi::{OsStr, OsString}; 20 | use std::str::FromStr; 21 | 22 | use tectonic::digest::DigestData; 23 | use tectonic::engines::IoEventBackend; 24 | use tectonic::io::filesystem::{FilesystemIo, FilesystemPrimaryInputIo}; 25 | use tectonic::io::{IoStack, MemoryIo}; 26 | use tectonic::status::NoopStatusBackend; 27 | use tectonic::TexEngine; 28 | 29 | mod util; 30 | use crate::util::test_path; 31 | 32 | const DEBUG: bool = false; // TODO: this is kind of ugly 33 | 34 | /// A stunted version of driver::FileSummary for examining the format file 35 | /// SHA256 sum. 36 | #[derive(Clone, Debug, Eq, PartialEq)] 37 | struct FileSummary { 38 | write_digest: Option, 39 | } 40 | 41 | impl FileSummary { 42 | fn new() -> FileSummary { 43 | FileSummary { write_digest: None } 44 | } 45 | } 46 | 47 | /// Similarly, a stunted verion of CliIoEvents. 48 | struct FormatTestEvents(HashMap); 49 | 50 | impl FormatTestEvents { 51 | fn new() -> FormatTestEvents { 52 | FormatTestEvents(HashMap::new()) 53 | } 54 | } 55 | 56 | impl IoEventBackend for FormatTestEvents { 57 | fn output_opened(&mut self, name: &OsStr) { 58 | self.0.insert(name.to_os_string(), FileSummary::new()); 59 | } 60 | 61 | fn output_closed(&mut self, name: OsString, digest: DigestData) { 62 | let summ = self 63 | .0 64 | .get_mut(&name) 65 | .expect("closing file that wasn't opened?"); 66 | summ.write_digest = Some(digest); 67 | } 68 | } 69 | 70 | fn test_format_generation(texname: &str, fmtname: &str, sha256: &str) { 71 | util::set_test_root(); 72 | 73 | let mut p = test_path(&["assets"]); 74 | 75 | // Filesystem IoProviders for input files. 76 | let fs_allow_writes = DEBUG; 77 | let mut fs_support = FilesystemIo::new(&p, fs_allow_writes, false, HashSet::new()); 78 | p.push(texname); 79 | let mut fs_primary = FilesystemPrimaryInputIo::new(&p); 80 | 81 | // MemoryIo layer that will accept the outputs. 82 | let mem_stdout_allowed = !DEBUG; 83 | let mut mem = MemoryIo::new(mem_stdout_allowed); 84 | 85 | // IoEvents manager that will give us output SHA256 sums. 86 | let mut events = FormatTestEvents::new(); 87 | 88 | use tectonic::io::GenuineStdoutIo; 89 | let mut stdout = GenuineStdoutIo::new(); 90 | 91 | // Run the engine! 92 | { 93 | let mut io = if DEBUG { 94 | IoStack::new(vec![&mut stdout, &mut fs_primary, &mut fs_support]) 95 | } else { 96 | IoStack::new(vec![&mut mem, &mut fs_primary, &mut fs_support]) 97 | }; 98 | 99 | TexEngine::new() 100 | .initex_mode(true) 101 | .process( 102 | &mut io, 103 | &mut events, 104 | &mut NoopStatusBackend::new(), 105 | "unused.fmt", 106 | texname, 107 | &Default::default(), 108 | ) 109 | .unwrap(); 110 | } 111 | 112 | // Did we get what we expected? 113 | 114 | let want_digest = DigestData::from_str(sha256).unwrap(); 115 | 116 | for (name, info) in &events.0 { 117 | if name.to_string_lossy() == fmtname { 118 | let observed = info.write_digest.unwrap(); 119 | 120 | if observed != want_digest { 121 | println!( 122 | "expected {} to have SHA256 = {}", 123 | fmtname, 124 | want_digest.to_string() 125 | ); 126 | println!("instead, got {}", observed.to_string()); 127 | panic!(); 128 | } 129 | } 130 | } 131 | } 132 | 133 | // Keep these alphabetized. 134 | 135 | #[test] 136 | fn plain_format() { 137 | test_format_generation( 138 | "plain.tex", 139 | "plain.fmt", 140 | "83684a4992274fdae8de1b142146f85b0bf1a4f3489c54a04e12739d578dd863", 141 | ) 142 | } 143 | -------------------------------------------------------------------------------- /tests/tex-outputs/a4paper.log: -------------------------------------------------------------------------------- 1 | ** 2 | (a4paper.tex [1] ) 3 | Output written on a4paper.xdv (1 page, 212 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/a4paper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/a4paper.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/a4paper.tex: -------------------------------------------------------------------------------- 1 | a\bye 2 | -------------------------------------------------------------------------------- /tests/tex-outputs/a4paper.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/a4paper.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding.log: -------------------------------------------------------------------------------- 1 | ** 2 | (file_encoding.tex (file_encoding_utf8.txt 3 | Missing character: There is no 🌍 in font cmr10! 4 | ) (file_encoding_utf16be.txt 5 | Missing character: There is no 🌍 in font cmr10! 6 | ) 7 | (file_encoding_utf16le.txt 8 | Missing character: There is no 🌍 in font cmr10! 9 | ) [1] ) 10 | Output written on file_encoding.xdv (1 page, 300 bytes). 11 | -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding.tex: -------------------------------------------------------------------------------- 1 | \input file_encoding_utf8.txt 2 | 3 | \input file_encoding_utf16be.txt 4 | 5 | \input file_encoding_utf16le.txt 6 | 7 | \bye 8 | -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/file_encoding.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding_utf16be.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/file_encoding_utf16be.txt -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding_utf16le.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/file_encoding_utf16le.txt -------------------------------------------------------------------------------- /tests/tex-outputs/file_encoding_utf8.txt: -------------------------------------------------------------------------------- 1 | this is utf8 2 | 🌍 3 | -------------------------------------------------------------------------------- /tests/tex-outputs/hallöchen 🐨 welt 🌍.log: -------------------------------------------------------------------------------- 1 | ** 2 | (hallöchen 🐨 welt 🌍.tex 3 | Missing character: There is no ö in font cmr10! 4 | Missing character: There is no 🐨 in font cmr10! 5 | Missing character: There is no 🌍 in font cmr10! 6 | [1] ) 7 | Output written on hallöchen 🐨 welt 🌍.xdv (1 page, 248 bytes). 8 | -------------------------------------------------------------------------------- /tests/tex-outputs/hallöchen 🐨 welt 🌍.tex: -------------------------------------------------------------------------------- 1 | hello world 2 | \jobname 3 | \bye 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/hallöchen 🐨 welt 🌍.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/hallöchen 🐨 welt 🌍.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/issue393_ungetc.log: -------------------------------------------------------------------------------- 1 | ** 2 | (issue393_ungetc.tex 3 | Overfull \vbox (161.76743pt too high) has occurred while \output is active 4 | \vbox(643.20255+0.0)x614.29498 5 | .\XeTeXpdffile( issue393_ungetc_trigger.pdf" 6 | .\glue(\topskip) 10.0 7 | .\hbox(0.0+0.0)x469.75499 8 | .\glue 0.0 plus 1.0fill 9 | 10 | 11 | [1] ) 12 | Output written on issue393_ungetc.xdv (1 page, 284 bytes). 13 | -------------------------------------------------------------------------------- /tests/tex-outputs/issue393_ungetc.tex: -------------------------------------------------------------------------------- 1 | \XeTeXpdffile issue393_ungetc_trigger.pdf 2 | \bye 3 | -------------------------------------------------------------------------------- /tests/tex-outputs/issue393_ungetc.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/issue393_ungetc.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/md5_of_hello.log: -------------------------------------------------------------------------------- 1 | ** 2 | (md5_of_hello.tex [1] ) 3 | Output written on md5_of_hello.xdv (1 page, 244 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/md5_of_hello.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/md5_of_hello.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/md5_of_hello.tex: -------------------------------------------------------------------------------- 1 | \mdfivesum{hello}\bye 2 | -------------------------------------------------------------------------------- /tests/tex-outputs/md5_of_hello.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/md5_of_hello.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/negative_roman_numeral.log: -------------------------------------------------------------------------------- 1 | ** 2 | (negative_roman_numeral.tex [1] ) 3 | Output written on negative_roman_numeral.xdv (1 page, 212 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/negative_roman_numeral.tex: -------------------------------------------------------------------------------- 1 | \romannumeral -1 a\bye 2 | -------------------------------------------------------------------------------- /tests/tex-outputs/negative_roman_numeral.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/negative_roman_numeral.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/otf_basic.log: -------------------------------------------------------------------------------- 1 | ** 2 | (otf_basic.tex 3 | Missing character: There is no 𐐷 in font [lmroman12-regular]! 4 | [1] ) 5 | Output written on otf_basic.xdv (1 page, 6300 bytes). 6 | -------------------------------------------------------------------------------- /tests/tex-outputs/otf_basic.tex: -------------------------------------------------------------------------------- 1 | % Use a native OTF font. 2 | \font\x="[lmroman12-regular]" 3 | \x Unicode “like this” with enough characters to get us breaking the paragraph 4 | into different lines, with some discretionary hyphenation and whatnot to make 5 | things interesting. How much text will it take? Well, it will take less if we 6 | use long words like antidisestablishmentarianism hypermetasuperconductivity 7 | semihemidemiquaver prepostantepenultimate. The paragraph needs to be of 8 | sufficient length for it to work out better to stick some hyphens in there, so 9 | we need to keep on typing and typing and typing and come on TeX don't leave us 10 | hanging. Here's a DESERET SMALL LETTER YEE to get us outside of the BMP: 𐐷. 11 | \bye 12 | -------------------------------------------------------------------------------- /tests/tex-outputs/otf_basic.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/otf_basic.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/pdfoutput.log: -------------------------------------------------------------------------------- 1 | ** 2 | (pdfoutput.tex [1] ) 3 | Output written on pdfoutput.xdv (1 page, 212 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/pdfoutput.tex: -------------------------------------------------------------------------------- 1 | % This tests that things don't blow up if we touch the first new 2 | % state variable: \pdfoutput 3 | \pdfoutput=1 4 | a% 5 | \bye 6 | -------------------------------------------------------------------------------- /tests/tex-outputs/pdfoutput.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/pdfoutput.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/png_formats.log: -------------------------------------------------------------------------------- 1 | ** 2 | (png_formats.tex [1] ) 3 | Output written on png_formats.xdv (1 page, 672 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/png_formats.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/png_formats.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/png_formats.tex: -------------------------------------------------------------------------------- 1 | % Blender's 2.74 splashscreen rendered in a bunch of exotic PNG formats 2 | . 3 | \XeTeXpicfile png_rgba_16_bit.png 4 | \XeTeXpicfile png_graya.png 5 | 6 | . 7 | \XeTeXpicfile png_rgba.png 8 | \XeTeXpicfile png_palette_alpha.png 9 | 10 | . 11 | \XeTeXpicfile png_palette_4bit.png 12 | \XeTeXpicfile png_gray_4bit.png 13 | 14 | \bye -------------------------------------------------------------------------------- /tests/tex-outputs/png_formats.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/png_formats.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/redbox_png.log: -------------------------------------------------------------------------------- 1 | ** 2 | (redbox_png.tex [1] ) 3 | Output written on redbox_png.xdv (1 page, 304 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/redbox_png.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/redbox_png.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/redbox_png.tex: -------------------------------------------------------------------------------- 1 | % A small image inline in a paragraph. 2 | Hello {\XeTeXpicfile redbox.png } here is some text. 3 | 4 | \bye 5 | -------------------------------------------------------------------------------- /tests/tex-outputs/redbox_png.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/redbox_png.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/synctex.log: -------------------------------------------------------------------------------- 1 | ** 2 | (synctex.tex [1] ) 3 | Output written on synctex.xdv (1 page, 212 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/synctex.synctex.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/synctex.synctex.gz -------------------------------------------------------------------------------- /tests/tex-outputs/synctex.tex: -------------------------------------------------------------------------------- 1 | \synctex=1 2 | a\bye 3 | -------------------------------------------------------------------------------- /tests/tex-outputs/synctex.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/synctex.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_errinside.log: -------------------------------------------------------------------------------- 1 | ** 2 | (tectoniccodatokens_errinside.tex) 3 | ! Undefined control sequence. 4 | \anundefinedcontrolsequence 5 | 6 | <*> 7 | 8 | No pages of output. 9 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_errinside.tex: -------------------------------------------------------------------------------- 1 | \TectonicCodaTokens{\anundefinedcontrolsequence} 2 | No explicit end. 3 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_noend.log: -------------------------------------------------------------------------------- 1 | ** 2 | (tectoniccodatokens_noend.tex) 3 | ! Emergency stop 4 | *** (job aborted, no legal \end found) 5 | No pages of output. 6 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_noend.tex: -------------------------------------------------------------------------------- 1 | \TectonicCodaTokens{No implicit end.} 2 | No explicit end. 3 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_ok.log: -------------------------------------------------------------------------------- 1 | ** 2 | (tectoniccodatokens_ok.tex \TectonicCodaTokens) coda [1] 3 | Output written on tectoniccodatokens_ok.xdv (1 page, 228 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_ok.tex: -------------------------------------------------------------------------------- 1 | \let\x=\TectonicCodaTokens 2 | \message{\meaning\x} 3 | \x{\message{coda}\end} 4 | No explicit end. 5 | -------------------------------------------------------------------------------- /tests/tex-outputs/tectoniccodatokens_ok.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/tectoniccodatokens_ok.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/tex_logo.log: -------------------------------------------------------------------------------- 1 | ** 2 | (tex_logo.tex [1] ) 3 | Output written on tex_logo.xdv (1 page, 228 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/tex_logo.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/tex_logo.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/tex_logo.tex: -------------------------------------------------------------------------------- 1 | \def\makeatletter{\catcode `\@ 11\relax}% 2 | \makeatletter% 3 | \def\@{\spacefactor \@m {}}% 4 | \def\TeXlogo{T\kern -.1667em\lower .5ex\hbox {E}\kern -.125em X\@}% 5 | \TeXlogo% 6 | \bye -------------------------------------------------------------------------------- /tests/tex-outputs/tex_logo.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/tex_logo.xdv -------------------------------------------------------------------------------- /tests/tex-outputs/the_letter_a.log: -------------------------------------------------------------------------------- 1 | ** 2 | (the_letter_a.tex [1] ) 3 | Output written on the_letter_a.xdv (1 page, 212 bytes). 4 | -------------------------------------------------------------------------------- /tests/tex-outputs/the_letter_a.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/the_letter_a.pdf -------------------------------------------------------------------------------- /tests/tex-outputs/the_letter_a.tex: -------------------------------------------------------------------------------- 1 | a\bye 2 | -------------------------------------------------------------------------------- /tests/tex-outputs/the_letter_a.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/tex-outputs/the_letter_a.xdv -------------------------------------------------------------------------------- /tests/trip/etrip.fot: -------------------------------------------------------------------------------- 1 | (etrip e-IniTeX: e-Trip format loaded. 2 | This is the e-Trip test [2014-01-22] for e-TeX v3.14159265-2.6. 3 | (You are using e-TeX version/revision 2.6) 4 | Missing character: There is no c in font nullfont! 5 | 6 | 7 | current interactionmode (l.230): nonstop 8 | 9 | current interactionmode (l.231): scroll 10 | 11 | current interactionmode (l.232): errorstop 12 | 13 | ! Bad interaction mode (-1). 14 | l.237 \interactionmode=-1 15 | \1 1 % nonstop 16 | current interactionmode (l.237): nonstop 17 | ! Bad interaction mode (4). 18 | l.238 \interactionmode=4 19 | \1 1 % nonstop 20 | current interactionmode (l.238): nonstop 21 | 22 | current interactionmode (l.239): scroll 23 | 24 | current interactionmode (l.239): scroll 25 | 26 | current interactionmode (l.239): nonstop 27 | 28 | current interactionmode (l.239): nonstop 29 | 30 | current interactionmode (l.239): errorstop 31 | 32 | current interactionmode (l.239): errorstop 33 | 34 | current interactionmode (l.240): errorstop 35 | 36 | 1 (l.243) 37 | 1 (l.245) 38 | current interactionmode (l.247): errorstop 39 | 40 | current interactionmode (l.247): scroll 41 | 42 | current interactionmode (l.247): nonstop 43 | 44 | \openout1 = `etrip.out'. 45 | 46 | -------------------------------------------------------------------------------- /tests/trip/etrip.out: -------------------------------------------------------------------------------- 1 | \endgroup 2 | \fi 3 | \bgroup 4 | \iffalse \else 5 | -------------------------------------------------------------------------------- /tests/trip/etrip.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/trip/etrip.tfm -------------------------------------------------------------------------------- /tests/trip/etrip.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/trip/etrip.xdv -------------------------------------------------------------------------------- /tests/trip/trip.fot: -------------------------------------------------------------------------------- 1 | (trip ## 2 | ! Bad number (-7). 3 | 4 | 8 5 | l.94 \openout-'78 6 | terminal \openout10=tr\romannumeral1 \gobble\newcs pos 7 | 8 | Completed box being shipped out [0.0.0.0.11] 9 | ! Missing number, treated as zero. 10 | 11 | { 12 | ... 13 | l.106 \penalty-10000 14 | % now we'll compute silently for awhile, after default ... 15 | 16 | 17 | ! OK (see the transcript file). 18 | l.441 ...e-100000{\if01{\else unbal}\fi}\showlists 19 | \tracingonline1% 20 | {|escapechar} 21 | {^^?global} 22 | {^^?global} 23 | {Āend} 24 | ! Missing } inserted. 25 | 26 | } 27 | 28 | Āend 29 | l.442 ...l\tracingoutput1\global\escapechar256\end 30 | 31 | {end-group character }} 32 | > 3. 33 | {Āshowthe Ādeadcycles 34 | Āglobal Āadvance Ācountz by1Āglobal Āglobalde... 35 | 36 | Āend 37 | l.442 ...l\tracingoutput1\global\escapechar256\end 38 | 39 | ! You can't use `Āend' in internal vertical mode. 40 | Āend 41 | 42 | ...efs -1 Āgdef Ālocal {}Āunvbox 255Āend 43 | Ārb } 44 | 45 | Āend 46 | l.442 ...l\tracingoutput1\global\escapechar256\end 47 | 48 | ! Unbalanced output routine. 49 | ...-1 Āgdef Ālocal {}Āunvbox 255Āend Ārb 50 | } 51 | 52 | Āend 53 | l.442 ...l\tracingoutput1\global\escapechar256\end 54 | 55 | ! Output loop---3 consecutive dead cycles. 56 | 57 | Āend 58 | l.442 ...l\tracingoutput1\global\escapechar256\end 59 | 60 | 61 | Completed box being shipped out [-1.2.-1118806.0.11.196608.327680.1572864.10737 62 | 41823] 63 | ! Unbalanced write command. 64 | Āif 01{Āelse unbal}Āfi 65 | 66 | 67 | }Āendwrite 68 | 69 | Āend 70 | l.442 ...l\tracingoutput1\global\escapechar256\end 71 | 72 | ) 73 | (Āend occurred inside a group at level 1) 74 | 75 | ### semi simple group (level 1) entered at line 429 (Ābegingroup) 76 | ### bottom level 77 | (Āend occurred when Āif on line 442 was incomplete) 78 | (Āend occurred when Āifcase on line 419 was incomplete) 79 | (Āend occurred when Āiftrue on line 413 was incomplete) 80 | (see the transcript file for additional information) 81 | Output written on trip.xdv (16 pages, 3256 bytes). 82 | Transcript written on trip.log. 83 | -------------------------------------------------------------------------------- /tests/trip/trip.tfm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/trip/trip.tfm -------------------------------------------------------------------------------- /tests/trip/trip.xdv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/trip/trip.xdv -------------------------------------------------------------------------------- /tests/trip/tripos.tex: -------------------------------------------------------------------------------- 1 | 2 | \uppercase {0{\outputpenalty }} 3 | [\uppercase {mmmmmmmmmm}[ 4 | -------------------------------------------------------------------------------- /tests/xenia/.version: -------------------------------------------------------------------------------- 1 | b20b848-dirty 2 | -------------------------------------------------------------------------------- /tests/xenia/gemini.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/xenia/gemini.pdf -------------------------------------------------------------------------------- /tests/xenia/mode.tex: -------------------------------------------------------------------------------- 1 | % In "journal" mode we don't monkey with TeX internals and use commands they 2 | % want like \object{}. 3 | % 4 | % This command is kept in this tiny, separate file so we can activate the 5 | % different modes just by overwriting this one file in the tarball 6 | % directories. 7 | 8 | \let\pwifjournal=\iffalse 9 | \def\version{git version \texttt{\input .version}} 10 | -------------------------------------------------------------------------------- /tests/xenia/pdm.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/xenia/pdm.pdf -------------------------------------------------------------------------------- /tests/xenia/phased.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/xenia/phased.pdf -------------------------------------------------------------------------------- /tests/xenia/schematic.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/xenia/schematic.pdf -------------------------------------------------------------------------------- /tests/xenia/setup.tex: -------------------------------------------------------------------------------- 1 | \usepackage{amsmath} 2 | 3 | 4 | % Font stuff 5 | \usepackage[T1]{fontenc} 6 | 7 | 8 | % AASTeX6/emulateapj have overly conservative figure widths, I think because 9 | % some people's figures don't have good margins. Override. 10 | \pwifjournal\else 11 | \makeatletter 12 | \renewcommand\plotone[1]{% 13 | \centering \leavevmode \setlength{\plot@width}{0.99\linewidth} 14 | \includegraphics[width={\eps@scaling\plot@width}]{#1}% 15 | }% 16 | \makeatother 17 | \fi 18 | 19 | 20 | % AASTeX6/emulateapj also set this to zero for some reason. I'm tweaking this 21 | % based on a table with no notes, etc.; maybe the space gets too big if you 22 | % have them?? 23 | %%%\pwifjournal\else 24 | %%% \setlength{\belowdeluxetableskip}{10pt} 25 | %%%\fi 26 | 27 | 28 | % for \autoref: I prefer capitalizing and dropping the "subs" 29 | \def\sectionautorefname{Section} 30 | \def\subsectionautorefname{Section} 31 | \def\subsubsectionautorefname{Section} 32 | \newcommand\theappendix{the~\hyperref[appendix]{Appendix}} 33 | 34 | 35 | % Evil magic to patch natbib to only highlight the year paper refs, not the 36 | % authors too. From http://tex.stackexchange.com/questions/23227/. 37 | \pwifjournal\else 38 | \usepackage{etoolbox} 39 | \makeatletter 40 | \patchcmd{\NAT@citex} 41 | {\@citea\NAT@hyper@{% 42 | \NAT@nmfmt{\NAT@nm}% 43 | \hyper@natlinkbreak{\NAT@aysep\NAT@spacechar}{\@citeb\@extra@b@citeb}% 44 | \NAT@date}} 45 | {\@citea\NAT@nmfmt{\NAT@nm}% 46 | \NAT@aysep\NAT@spacechar\NAT@hyper@{\NAT@date}}{}{} 47 | \patchcmd{\NAT@citex} 48 | {\@citea\NAT@hyper@{% 49 | \NAT@nmfmt{\NAT@nm}% 50 | \hyper@natlinkbreak{\NAT@spacechar\NAT@@open\if*#1*\else#1\NAT@spacechar\fi}% 51 | {\@citeb\@extra@b@citeb}% 52 | \NAT@date}} 53 | {\@citea\NAT@nmfmt{\NAT@nm}% 54 | \NAT@spacechar\NAT@@open\if*#1*\else#1\NAT@spacechar\fi\NAT@hyper@{\NAT@date}} 55 | {}{} 56 | \makeatother 57 | \fi 58 | 59 | 60 | % Bibliography 61 | \usepackage{natbib} 62 | \bibliographystyle{yahapj} 63 | \renewcommand{\bibpreamble}{\RaggedRight} 64 | \setlength{\bibsep}{0pt} % single-space 65 | \def\bibfont{\small} 66 | 67 | 68 | % Awesome object system 69 | \makeatletter 70 | \newcommand\pkgw@simpfx{http://simbad.u-strasbg.fr/simbad/sim-id?Ident=} 71 | \newcommand\MakeObj[4][\@empty]{% [shortname]{ident}{url-escaped}{formalname} 72 | \pwifjournal% 73 | \expandafter\newcommand\csname pkgwobj@c@#2\endcsname[1]{\protect\object[#4]{##1}}% 74 | \else% 75 | \expandafter\newcommand\csname pkgwobj@c@#2\endcsname[1]{\href{\pkgw@simpfx #3}{##1}}% 76 | \fi% 77 | \expandafter\newcommand\csname pkgwobj@f#2\endcsname{#4}% 78 | \ifx\@empty#1% 79 | \expandafter\newcommand\csname pkgwobj@s#2\endcsname{#4}% 80 | \else% 81 | \expandafter\newcommand\csname pkgwobj@s#2\endcsname{#1}% 82 | \fi}% 83 | \newcommand{\obj}[1]{% 84 | \expandafter\ifx\csname pkgwobj@c@#1\endcsname\relax% 85 | \textbf{[unknown object!]}% 86 | \else% 87 | \csname pkgwobj@c@#1\endcsname{\csname pkgwobj@s#1\endcsname}% 88 | \fi} 89 | \newcommand{\objf}[1]{% 90 | \expandafter\ifx\csname pkgwobj@c@#1\endcsname\relax% 91 | \textbf{[unknown object!]}% 92 | \else% 93 | \csname pkgwobj@c@#1\endcsname{\csname pkgwobj@f#1\endcsname}% 94 | \fi} 95 | \makeatother 96 | 97 | 98 | % Objects 99 | \MakeObj[WISE~1122$+$25]{1122+25}{WISEP\%20J112254.73\%2b255021.5}{WISEP~J112254.73$+$255021.5} 100 | \MakeObj{phasecal}{NVSS\%20J112555\%2b260630}{NVSS~J112555$+$260630} 101 | \MakeObj{3c286}{3C\%20286}{3C~286} 102 | \MakeObj{lhs302}{LHS\%20302}{LHS~302} 103 | \MakeObj[2M~1047$+$21]{1047+21}{2MASSI\%20J1047539\%2b212423}{2MASSI~J1047539$+$212423} 104 | \MakeObj{n33370}{NLTT\%2033370}{NLTT~33370~AB} 105 | \MakeObj{n33370b}{NLTT\%2033370}{NLTT~33370~B} 106 | \MakeObj{1048-39}{DENIS\%20J1048.0-3956}{DENIS~J1048.0$-$3956} 107 | \MakeObj{1237+65}{2MASS\%20J12373919\%2b6526148}{2MASS~J12373919$+$6526148} 108 | \MakeObj{0746+20}{2MASSW\%20J0746425\%2b200032}{2MASSW~J0746425$+$200032} 109 | \MakeObj{1835+32}{LSR\%20J1835\%2b3259}{LSR~J1835$+$3259} 110 | \MakeObj{tvlm513}{TVLM\%20513-46546}{TVLM~513--46546} 111 | \MakeObj{0036+18}{2MASS\%20J00361617\%2b1821104}{2MASS~J00361617$+$1821104} 112 | 113 | % Standard macros 114 | \newcommand\apx{\ensuremath{\sim}} 115 | \newcommand\citeeg[1]{\citep[\emph{e.g.},][]{#1}} 116 | \renewcommand\deg{\ensuremath{^\circ}} 117 | \newcommand\dt{\ensuremath{{\Delta t}}} 118 | \newcommand\eg{\emph{e.g.}} 119 | \newcommand\etal{\emph{et~al.}} 120 | \newcommand\ha{\ensuremath{\text{H}\alpha}} 121 | \newcommand\ie{\emph{i.e.}} 122 | \newcommand\kms{km~s$^{-1}$} 123 | \newcommand\lbol{\ensuremath{\text{L}_\text{bol}}} 124 | \newcommand\lnura{\ensuremath{\langle\text{L}_{\nu,\text{R}}\rangle}} 125 | \newcommand\lsun{\ensuremath{\text{L}_\odot}} 126 | \newcommand\rjup{\ensuremath{\text{R}_\text{J}}} 127 | \newcommand\tb{\ensuremath{T_b}} 128 | \newcommand\todo[1]{\textcolor{red}{#1}} 129 | \newcommand\ujy{$\mu$Jy} 130 | \newcommand\ujybm{$\mu$Jy~bm$^{-1}$} 131 | -------------------------------------------------------------------------------- /tests/xenia/vla.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/crlf0710/tectonic/3fc2980490c06a488426993b3a85fcb58f059e40/tests/xenia/vla.pdf -------------------------------------------------------------------------------- /xdv/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Copyright 2018-2019 the Tectonic Project 2 | # Licensed under the MIT License. 3 | 4 | [package] 5 | name = "tectonic_xdv" 6 | version = "0.1.9-dev" 7 | authors = ["Peter Williams "] 8 | description = """ 9 | A decoder for the XDV and SPX file formats used by XeTeX and Tectonic. 10 | """ 11 | homepage = "https://tectonic-typesetting.github.io/" 12 | documentation = "https://docs.rs/tectonic" 13 | repository = "https://github.com/tectonic-typesetting/tectonic/" 14 | readme = "README.md" 15 | license = "MIT" 16 | edition = "2018" 17 | 18 | [dependencies] 19 | byteorder = "^1.3" 20 | 21 | [dev-dependencies] 22 | clap = "^2.33" 23 | -------------------------------------------------------------------------------- /xdv/README.md: -------------------------------------------------------------------------------- 1 | # The `tectonic_xdv` create 2 | 3 | This crate is part of 4 | [the Tectonic project](https://tectonic-typesetting.github.io/en-US/). It can 5 | decode XDV and SPX files. 6 | 7 | [![](http://meritbadge.herokuapp.com/tectonic_xdv)](https://crates.io/crates/tectonic_xdv) 8 | 9 | - [API documentation](https://docs.rs/tectonic_xdv/). 10 | - [Main Git repository](https://github.com/tectonic-typesetting/tectonic/). 11 | -------------------------------------------------------------------------------- /xdv/examples/xdvdump.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2018 the Tectonic Project 2 | // Licensed under the MIT License. 3 | 4 | //! Parse an XDV/SPX file and dump some stats about its contents. 5 | 6 | use clap::{crate_version, App, Arg}; 7 | use std::fmt::{Display, Error as FmtError, Formatter}; 8 | use std::fs::File; 9 | use std::io; 10 | use std::process; 11 | use std::str; 12 | use tectonic_xdv::{FileType, XdvError}; 13 | 14 | /// We'd like to use String as our error type, but we also would like to 15 | /// use the `XdvParser::process()` function, which when imposes the requirement 16 | /// that String: From, which we can't satisfy. So, we have to create 17 | /// a wrapper type. 18 | /// 19 | /// We can't implement `From for Error` because `Error: Display`, 20 | /// which conflicts with the language's blanket `From for T` 21 | /// implementation. 22 | #[derive(Clone, Debug, Eq, PartialEq)] 23 | struct Error(String); 24 | 25 | impl Display for Error { 26 | fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> { 27 | write!(f, "{}", self.0) 28 | } 29 | } 30 | 31 | impl From for Error { 32 | fn from(e: io::Error) -> Self { 33 | Error(format!("{}", e)) // note: weirdly, can't use `Self` on this line 34 | } 35 | } 36 | 37 | impl From for Error { 38 | fn from(e: XdvError) -> Self { 39 | Error(format!("{}", e)) 40 | } 41 | } 42 | 43 | struct Stats {} 44 | 45 | impl Stats { 46 | pub fn new() -> Self { 47 | Stats {} 48 | } 49 | } 50 | 51 | impl tectonic_xdv::XdvEvents for Stats { 52 | type Error = Error; 53 | 54 | fn handle_header(&mut self, filetype: FileType, comment: &[u8]) -> Result<(), Self::Error> { 55 | println!("file type: {}", filetype); 56 | 57 | match str::from_utf8(comment) { 58 | Ok(s) => { 59 | println!("comment: {}", s); 60 | } 61 | Err(e) => { 62 | println!("cannot parse comment: {}", e); 63 | } 64 | }; 65 | 66 | Ok(()) 67 | } 68 | 69 | fn handle_begin_page( 70 | &mut self, 71 | counters: &[i32], 72 | previous_bop: i32, 73 | ) -> Result<(), Self::Error> { 74 | println!( 75 | "new page: [{} {} {} {} {} {} {} {} {} {}] {}", 76 | counters[0], 77 | counters[1], 78 | counters[2], 79 | counters[3], 80 | counters[4], 81 | counters[5], 82 | counters[6], 83 | counters[7], 84 | counters[8], 85 | counters[9], 86 | previous_bop 87 | ); 88 | Ok(()) 89 | } 90 | 91 | fn handle_special(&mut self, contents: &[u8]) -> Result<(), Self::Error> { 92 | match str::from_utf8(contents) { 93 | Ok(s) => { 94 | println!("special: {}", s); 95 | } 96 | Err(e) => { 97 | println!("cannot UTF8-parse special: {}", e); 98 | } 99 | }; 100 | 101 | Ok(()) 102 | } 103 | 104 | fn handle_char_run(&mut self, chars: &[i32]) -> Result<(), Self::Error> { 105 | let all_ascii_printable = chars.iter().all(|c| *c > 0x20 && *c < 0x7F); 106 | println!( 107 | "chars: {:?} all_ascii_printable={:?}", 108 | chars, all_ascii_printable 109 | ); 110 | Ok(()) 111 | } 112 | } 113 | 114 | fn main() { 115 | let matches = App::new("xdvdump") 116 | .version(crate_version!()) 117 | .about("Parse an XDV or SPX file and report some stats about its contents") 118 | .arg( 119 | Arg::with_name("PATH") 120 | .help("The path to the XDV or SPX file") 121 | .required(true) 122 | .index(1), 123 | ) 124 | .get_matches(); 125 | 126 | let path = matches.value_of_os("PATH").unwrap(); 127 | 128 | let file = match File::open(&path) { 129 | Ok(f) => f, 130 | Err(e) => { 131 | eprintln!( 132 | "error: could not open \"{}\": {}", 133 | path.to_string_lossy(), 134 | e 135 | ); 136 | process::exit(1); 137 | } 138 | }; 139 | 140 | let (_stats, n_bytes) = match tectonic_xdv::XdvParser::process(file, Stats::new()) { 141 | Ok(x) => x, 142 | Err(e) => { 143 | eprintln!( 144 | "error: failed to parse \"{}\": {}", 145 | path.to_string_lossy(), 146 | e 147 | ); 148 | process::exit(1); 149 | } 150 | }; 151 | 152 | println!("{} bytes parsed.", n_bytes); 153 | } 154 | --------------------------------------------------------------------------------