diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 000000000..a253042ba --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,23 @@ +[target.x86_64-apple-darwin] +rustflags = ["-C", "link-args=-rdynamic"] + +[target.x86_64-unknown-linux-gnu] +rustflags = ["-C", "link-args=-rdynamic"] + +# Since autotools sets these and we are *not* forcing them here, this will not +# affect release builds. It will affect `cargo run`, `cargo clippy` and others +# making it easier to test locally since the Lua loader path and other +# resources will be relative to the current sources. +[env] +SILE_PATH = { value = "", relative = true } +CONFIGURE_DATADIR = { value = "", relative = true } + +[target.'cfg(all())'] +rustflags = [ + # CLIPPY LINT SETTINGS + # This is a workaround to configure lints for the entire workspace, pending the ability to configure this via TOML. + # See: `https://github.com/rust-lang/cargo/issues/5034` + # `https://github.com/EmbarkStudios/rust-ecosystem/issues/22#issuecomment-947011395` + "-Asuspicious_double_ref_op", + "-Aclippy::ptr_arg", +] diff --git a/.cirrus.yml b/.cirrus.yml index 378771fe8..ab336409b 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -2,8 +2,8 @@ task: name: Cirrus CI (FreeBSD) freebsd_instance: matrix: - - image_family: freebsd-13-0 - - image_family: freebsd-12-3 + - image_family: freebsd-13-2 + - image_family: freebsd-12-4 env: MAKEFLAGS: -j$(nproc) -Otarget CFLAGS: -I/usr/local/include -fPIC @@ -15,39 +15,37 @@ task: fingerprint_script: cat Makefile-fonts luarocks_cache: folder: /usr/local/lib/luarocks - fingerprint_script: cat sile-dev-1.rockspec + fingerprint_script: cat sile.rockspec.in luarocks_lua_cache: folder: /usr/local/share/lua - fingerprint_script: cat sile-dev-1.rockspec + fingerprint_script: cat sile.rockspec.in luarocks_lib_cache: folder: /usr/local/lib/lua - fingerprint_script: cat sile-dev-1.rockspec + fingerprint_script: cat sile.rockspec.in dependencies_script: - - pkg install -y autoconf automake fontconfig GentiumPlus git gmake harfbuzz libtool pkgconf png - - pkg install -y lua54 lua54-lpeg lua54-luafilesystem lua54-luarocks lua54-luasec lua54-luasocket - # lua54-luaexpat exists but seems boken on 13.0, system doesn't have expat 2.4+ so 1.5.x isn't an option either - - luarocks54 install luaexpat 1.4.1 - - luarocks54 install cassowary - - luarocks54 install cldr - - luarocks54 install compat53 - - luarocks54 install cosmo - - luarocks54 install fluent - - luarocks54 install linenoise - - luarocks54 install loadkit - - luarocks54 install lua-zlib - - luarocks54 install lua_cliargs - - luarocks54 install luaepnf - - luarocks54 install luarepl - - luarocks54 install luautf8 - - luarocks54 install penlight - - luarocks54 install vstruct + - pkg install -y autoconf automake fontconfig GentiumPlus git gmake harfbuzz jq libtool pkgconf png rust + - pkg install -y luajit lua51-luaexpat lua51-lpeg lua51-luafilesystem lua51-luarocks lua51-luasec lua51-luasocket + - luarocks51 install cassowary + - luarocks51 install cldr + - luarocks51 install compat53 + - luarocks51 install fluent + - luarocks51 install linenoise + - luarocks51 install loadkit + - luarocks51 install lua-zlib + - luarocks51 install lua_cliargs + - luarocks51 install luaepnf + - luarocks51 install luarepl + - luarocks51 install luautf8 + - luarocks51 install penlight + - luarocks51 install vstruct bootstrap_script: - git fetch --prune --tags ||: - ./bootstrap.sh configure_script: | ./configure MAKE=gmake \ - --enable-developer LUAROCKS=false LUACHECK=false BUSTED=false \ + --enable-developer LUAROCKS=false LUACHECK=false BUSTED=false PDFINFO=false NIX=false \ --disable-font-variations \ + --with-system-lua-sources \ --with-system-luarocks \ --without-manual make_script: diff --git a/.editorconfig b/.editorconfig index f935e92c5..c8de8f6b5 100644 --- a/.editorconfig +++ b/.editorconfig @@ -6,15 +6,28 @@ insert_final_newline = true charset = utf-8 trim_trailing_whitespace = true +[{Makefile*,*.mk,*.mk.in}] +indent_style = tab +indent_size = 4 + +[*.m4] +indent_style = space +indent_size = 8 + +[*.md] +trim_trailing_whitespace = false + [{*.lua,*.lua.in,sile.in,*rockspec,.busted,.luacheckrc}] indent_style = space -indent_size = 2 +indent_size = 3 max_line_length = 120 +[*rockspec] +max_line_length = 190 + [*.pl] indent_style = space -indent_size = 4 +indent_size = 2 -[makefile*] -indent_style = tab +[*.rs] indent_size = 4 diff --git a/.envrc b/.envrc new file mode 100644 index 000000000..3550a30f2 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use flake diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index f9f35e4d9..b1dff4f29 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -3,16 +3,23 @@ name: Build on: [ push, pull_request ] concurrency: - group: ${{ github.workflow}}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: build-ubuntu: + strategy: + matrix: + configuration: + - [ 'dynamic', '' ] + - [ 'embeded', '--enable-embeded-resources' ] + - [ 'system', '--with-system-lua-sources' ] runs-on: ubuntu-22.04 + name: Build Ubuntu ${{ matrix.configuration[0] }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cache test fonts @@ -27,21 +34,22 @@ jobs: with: path: | lua_modules - key: luarocks-${{ hashFiles('Makefile-luarocks', 'sile-dev-1.rockspec') }} - - name: Fetch tags - run: | - git fetch --prune --tags ||: + key: luarocks-${{ hashFiles('Makefile-luarocks', 'sile.rockspec.in') }} + - name: Cache Rust + uses: Swatinem/rust-cache@v2 - name: Install system dependencies run: | sudo apt-get update - sudo apt-get install fonts-sil-gentiumplus libarchive-tools libfontconfig1-dev libharfbuzz-dev libicu-dev liblua5.3-dev libpng-dev lua5.3 lua-sec lua-socket lua-zlib-dev luarocks poppler-utils + sudo apt-get install fonts-sil-gentiumplus ghostscript graphviz jq libarchive-tools libfontconfig1-dev libharfbuzz-dev libicu-dev libluajit-5.1-dev libpng-dev luajit lua-sec lua-socket lua-zlib-dev luarocks poppler-utils + - name: Setup ‘cargo’ + uses: actions-rs/toolchain@v1 - name: Configure run: | ./bootstrap.sh ./configure \ --disable-font-variations \ - --without-system-luarocks \ - --with-manual + --with-manual \ + ${{ matrix.configuration[1] }} echo "VERSION=$(./build-aux/git-version-gen .tarball-version)" >> $GITHUB_ENV echo "MAKEFLAGS=-j$(nproc) -Otarget" >> $GITHUB_ENV - name: Make @@ -58,7 +66,7 @@ jobs: path: sile-${{ env.VERSION }}.zip - name: Release uses: softprops/action-gh-release@v1 - if: startsWith(github.ref, 'refs/tags/v') + if: github.repository == 'sile-typesetter/sile' && startsWith(github.ref, 'refs/tags/v') with: body_path: sile-${{ env.VERSION }}.md files: | @@ -68,9 +76,10 @@ jobs: build-nix: runs-on: ubuntu-22.04 + name: Build Nix steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cache test fonts @@ -80,14 +89,10 @@ jobs: .fonts .sources key: fonts-${{ hashFiles('Makefile-fonts') }} - - name: Fetch tags - run: | - git fetch --prune --tags ||: - name: Install Nix - uses: cachix/install-nix-action@v20 - with: - extra_nix_config: | - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + uses: DeterminateSystems/nix-installer-action@v6 + - name: Cache Nix dependencies + uses: DeterminateSystems/magic-nix-cache-action@v2 - name: Setup developer environment run: | nix develop --command ./bootstrap.sh diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml index 3bcabe48d..9059e5e76 100644 --- a/.github/workflows/commitlint.yml +++ b/.github/workflows/commitlint.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Run ‘commitlint’ linter diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 1b4ac17d4..527cdff82 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -3,7 +3,7 @@ name: Coverage on: [ push, pull_request ] concurrency: - group: ${{ github.workflow}}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true # Coverage slows down tests *a lot*. @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cache test fonts @@ -31,10 +31,7 @@ jobs: with: path: | lua_modules - key: luarocks-luajit-${{ hashFiles('Makefile-luarocks', 'sile-dev-1.rockspec') }} - - name: Fetch tags - run: | - git fetch --prune --tags ||: + key: luarocks-luajit-${{ hashFiles('Makefile-luarocks', 'sile.rockspec.in') }} - name: Setup ‘lua’ uses: leafo/gh-actions-lua@v10 with: @@ -63,10 +60,8 @@ jobs: run: | ./bootstrap.sh ./configure \ - --enable-developer LUACHECK=false \ + --enable-developer LUACHECK=false NIX=false \ --disable-font-variations \ - --without-system-luarocks \ - --with-luajit \ --without-manual - name: Make run: | diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index dd4b75f68..cdaa8290b 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -4,6 +4,7 @@ on: push: branches: - master + - develop - rel* tags: - latest @@ -17,12 +18,9 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Fetch tags - run: | - git fetch --prune --tags ||: - name: Configure run: | echo "REF=${GITHUB_REF##refs/*/}" >> $GITHUB_ENV diff --git a/.github/workflows/luacheck.yml b/.github/workflows/luacheck.yml index ffcb08fec..83f552b94 100644 --- a/.github/workflows/luacheck.yml +++ b/.github/workflows/luacheck.yml @@ -8,6 +8,6 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Luacheck uses: lunarmodules/luacheck@v1 diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index 730aabe23..20befbf78 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -3,7 +3,7 @@ name: Run Flake on: [ push, pull_request ] concurrency: - group: ${{ github.workflow}}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -12,17 +12,13 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Fetch tags - run: | - git fetch --prune --tags ||: - name: Install Nix - uses: cachix/install-nix-action@v20 - with: - extra_nix_config: | - access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} + uses: DeterminateSystems/nix-installer-action@v6 + - name: Cache Nix dependencies + uses: DeterminateSystems/magic-nix-cache-action@v2 # Upstream package sometimes has flags set that disable flake checking - name: Setup test env run: | diff --git a/.github/workflows/rust_lint.yml b/.github/workflows/rust_lint.yml new file mode 100644 index 000000000..8c315d67f --- /dev/null +++ b/.github/workflows/rust_lint.yml @@ -0,0 +1,42 @@ +name: Rust Lint + +on: [ push, pull_request ] + +jobs: + + rustfmt: + strategy: + fail-fast: false + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + components: rustfmt + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + - name: Run rustfmt + run: | + git ls-files '*.rs' '*.rs.in' | xargs rustfmt --check --config skip_children=true + + clippy: + strategy: + fail-fast: false + runs-on: ubuntu-22.04 + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rs/toolchain@v1 + with: + profile: minimal + components: clippy + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + - uses: actions-rs/clippy-check@v1 + with: + token: ${{ github.token }} + args: --features luajit,vendored -- -D warnings diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 5c4eda17c..6393d6469 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -3,7 +3,7 @@ name: Test on: [ push, pull_request ] concurrency: - group: ${{ github.workflow}}-${{ github.ref }} + group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: true jobs: @@ -23,7 +23,7 @@ jobs: name: Test on Lua ${{ matrix.luaVersion[0] }} steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 with: fetch-depth: 0 - name: Cache test fonts @@ -38,15 +38,20 @@ jobs: with: path: | lua_modules - key: luarocks-${{ matrix.luaVersion[0] }}-${{ hashFiles('Makefile-luarocks', 'sile-dev-1.rockspec') }} - - name: Fetch tags + key: luarocks-${{ matrix.luaVersion[0] }}-${{ hashFiles('Makefile-luarocks', 'sile.rockspec.in') }} + - name: Cache Rust + uses: Swatinem/rust-cache@v2 + - name: Install system dependencies run: | - git fetch --prune --tags ||: + sudo apt-get update + sudo apt-get install fonts-sil-gentiumplus libarchive-tools libfontconfig1-dev libharfbuzz-dev libicu-dev libpng-dev poppler-utils - name: Setup ‘lua’ uses: leafo/gh-actions-lua@v10 with: luaVersion: ${{ matrix.luaVersion[0] }} luaCompileFlags: ${{ matrix.luaVersion[1] }} + - name: Setup ‘cargo’ + uses: actions-rs/toolchain@v1 - name: Setup ‘luarocks’ uses: leafo/gh-actions-luarocks@v4 - name: Prep system Lua for use @@ -61,30 +66,34 @@ jobs: LUA_INCLUDE=-I$(deepest $PWD/.lua/include) MAKEFLAGS=-j$(nproc) -Otarget EOF - - name: Install system dependencies - run: | - sudo apt-get update - sudo apt-get install fonts-sil-gentiumplus libarchive-tools libfontconfig1-dev libharfbuzz-dev libicu-dev libpng-dev poppler-utils - name: Configure run: | ./bootstrap.sh ./configure \ - --enable-developer LUACHECK=false \ + ${{ matrix.luaVersion[1] }} \ + --enable-developer LUACHECK=false NIX=false \ --disable-font-variations \ - --without-system-luarocks \ - --with${{ startsWith(matrix.luaVersion[0], '5') && 'out' || '' }}-luajit \ + --with${{ !startsWith(matrix.luaVersion[0], 'luajit') && 'out' || '' }}-luajit \ --without-manual - name: Make run: | make + - name: Prove SILE runs at all + run: | + make selfcheck - name: Test Busted - timeout-minutes: ${{ runner.debug && 60 || 6 }} + continue-on-error: ${{ matrix.luaVersion[0] == '5.1' }} + timeout-minutes: ${{ runner.debug && 20 || 2 }} run: | make busted - name: Test Regressions - timeout-minutes: ${{ runner.debug && 60 || 6 }} + timeout-minutes: ${{ runner.debug && 20 || 2 }} run: | make regressions + - name: Test Cargo + timeout-minutes: ${{ runner.debug && 20 || 2 }} + run: | + make cargo-test - name: Upload artifacts uses: actions/upload-artifact@v3 with: diff --git a/.github/workflows/versioning.yml b/.github/workflows/versioning.yml index 0977caebd..cf0f3f497 100644 --- a/.github/workflows/versioning.yml +++ b/.github/workflows/versioning.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-22.04 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Update release tags uses: Actions-R-Us/actions-tagger@v2 env: diff --git a/.gitignore b/.gitignore index 5afcaef87..674cc4563 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,7 @@ build-aux/ltversion.m4 build-aux/lt~obsolete.m4 build-aux/missing build-aux/list-dist-files.sh +build-aux/rust_boilerplate.mk tests/regressions.pl # Other autojunk @@ -65,7 +66,6 @@ node_modules lua_modules lua_modules_dist package-lock.json -yarn.lock # Editor entrails .vscode/ @@ -76,17 +76,29 @@ tags Makefile-distfiles documentation/*.pdf gource.webm +*.rockspec .fonts/* .sources/* -/sile +sile sile.1 -/.version -/.version-prev -/.tarball-version -/.built-subdirs +sile-lua +sile-lua.1 +.version +.version-prev +.tarball-version +.built-subdirs *.so +*.o core/version.lua core/features.lua +target/ +completions/ +core/pathsetup.lua +src/embed.rs +src/embed-includes.rs # Nix symlink to builds -/result +result +result-man +result-doc +result-dev diff --git a/.husky/.gitignore b/.husky/.gitignore deleted file mode 100644 index 31354ec13..000000000 --- a/.husky/.gitignore +++ /dev/null @@ -1 +0,0 @@ -_ diff --git a/.husky/commit-msg b/.husky/commit-msg index d71a03b9f..b56767669 100755 --- a/.husky/commit-msg +++ b/.husky/commit-msg @@ -1,4 +1,4 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" +#!/usr/bin/env sh +. "$(dirname -- "$0")/_/husky.sh" -yarn commitlint --edit $1 +npx --no -- commitlint --edit "$1" diff --git a/.husky/pre-commit b/.husky/pre-commit deleted file mode 100755 index 6700f5128..000000000 --- a/.husky/pre-commit +++ /dev/null @@ -1,2 +0,0 @@ -#!/bin/sh -. "$(dirname "$0")/_/husky.sh" diff --git a/.luacheckrc b/.luacheckrc index 55574cc41..da4ce6a8d 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -26,8 +26,8 @@ globals = { "luautf8", "pl", "fluent", - "SYSTEM_SILE_PATH", - "SHARED_LIB_EXT" + "executablePath", + "extendSilePath" } max_line_length = false ignore = { diff --git a/.luarc.json b/.luarc.json index 3b7cf8fcd..3fd760fee 100644 --- a/.luarc.json +++ b/.luarc.json @@ -7,8 +7,8 @@ "luautf8", "pl", "fluent", - "SYSTEM_SILE_PATH", - "SHARED_LIB_EXT" + "executablePath", + "extendSilePath" ], "Lua.workspace.preloadFileSize": 5120, "Lua.workspace.checkThirdParty": false diff --git a/CHANGELOG.md b/CHANGELOG.md index 9f3e04e28..2cde910bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,65 @@ All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. +### [0.14.13](https://github.com/sile-typesetter/sile/compare/v0.14.12...v0.14.13) (2023-10-30) + + +### Features + +* **classes:** Add landscape option to base class ([#1892](https://github.com/sile-typesetter/sile/issues/1892)) ([0fb9ade](https://github.com/sile-typesetter/sile/commit/0fb9adefca3adf9ff8a56755e69474960792d85a)) +* **cli:** Allow multiple runtime SILE_PATH segments (backport from develop branch) ([e7c8fe2](https://github.com/sile-typesetter/sile/commit/e7c8fe219686aa327032154484bf78be86c0baaa)) + + +### Bug Fixes + +* **frames:** Update frame constraints with new frame IDs after \makecolumns ([b2d6b4f](https://github.com/sile-typesetter/sile/commit/b2d6b4f7b095d74f3d39123904495a8e024c0f05)) + +### [0.14.12](https://github.com/sile-typesetter/sile/compare/v0.14.11...v0.14.12) (2023-10-11) + + +### Features + +* **i18n:** Add Portuguese localizations for bibtex package ([#1859](https://github.com/sile-typesetter/sile/issues/1859)) ([f716c35](https://github.com/sile-typesetter/sile/commit/f716c35109d36c7cb2118ab9c7c65227d9941e01)) +* **utilities:** Add utility function for console messages without trace info ([18526ce](https://github.com/sile-typesetter/sile/commit/18526ce75eeb8deb12e9b232e727993409ed8e06)) + + +### Bug Fixes + +* **build:** Make sure vendored luarocks isn't a phony target that runs repeatedly ([713434d](https://github.com/sile-typesetter/sile/commit/713434dadbc271299c8548dd2f2d4af57c1eec62)) +* **core:** Allocate exactly what we use, not a guess with an extra just in case ([640ded0](https://github.com/sile-typesetter/sile/commit/640ded0a90e427124f555a2a48d263cde5300d7d)) +* **core:** Correct usage of HarfBuzz when passing a filtered list of shapers ([f488643](https://github.com/sile-typesetter/sile/commit/f4886437d0ebf229db1c2779a8a324bf441efc1a)) +* **core:** Fixup class loader so cache is all Lua module specs ([#1863](https://github.com/sile-typesetter/sile/issues/1863)) ([7efff5b](https://github.com/sile-typesetter/sile/commit/7efff5b7e94f0c4897910c064ef842e6be2e4ab1)) +* **packages:** Don't warn on TOC content change if not actually used ([87c443d](https://github.com/sile-typesetter/sile/commit/87c443d1571f571b595c3e32febdcb03129f5b9a)) + +### [0.14.11](https://github.com/sile-typesetter/sile/compare/v0.14.10...v0.14.11) (2023-08-23) + + +### Bug Fixes + +* **core:** Leave legacy masterFilename alone but use first input filename internally ([29667a7](https://github.com/sile-typesetter/sile/commit/29667a752181dd40abe18672f6175fe10a9c5546)) +* **core:** Make masterFilename actually a filename ([759131e](https://github.com/sile-typesetter/sile/commit/759131e6c87517b56a433dccde29658dbe6df023)) +* **packages:** Avoid mix-and-matching indents in fixed-width specimin blocks ([de41cac](https://github.com/sile-typesetter/sile/commit/de41cac06a911e7c56f0ba4d1248a6da5999e6f3)) +* **utilities:** Use real semver parser for deprecation warnings ([5f0fed5](https://github.com/sile-typesetter/sile/commit/5f0fed51b2a9597272da62f00c15f8836f8c7bd1)) + +## [0.14.10](https://github.com/sile-typesetter/sile/compare/v0.14.9...v0.14.10) (2023-07-11) + + +### Features + +* **cli:** Allow more than one input document ([d20cbd8](https://github.com/sile-typesetter/sile/commit/d20cbd8a0b7a197ca87ca1dd1a39640fa746e301)) +* **i18n:** Add localized strings for Cantonese and Chinese ([cb67d36](https://github.com/sile-typesetter/sile/commit/cb67d3686117258adaca546298063d23c66135f9)) +* **packages:** Add document class styling in autodoc ([e70fa50](https://github.com/sile-typesetter/sile/commit/e70fa509673c32977a1e1f0545373229198c8aa8)) +* **packages:** Provide API for registering raw handlers linked to packages ([45cd3ac](https://github.com/sile-typesetter/sile/commit/45cd3ac96acbe3f2dd572ce0c3c72c7599090e6b)) + + +### Bug Fixes + +* **build:** Avoid build artifacts being listed for installation ([29c2ccd](https://github.com/sile-typesetter/sile/commit/29c2ccd227774caa4accb90bb0d23825aafccfd1)) +* **core:** Avoid stack overflow in Harfbuzz module ([#1793](https://github.com/sile-typesetter/sile/issues/1793)) ([5001efe](https://github.com/sile-typesetter/sile/commit/5001efe0cfeb421ce5796f8303bf046bb68c8326)) +* **outputters:** Setup --makedeps to play along without explicit --output ([6ff2e16](https://github.com/sile-typesetter/sile/commit/6ff2e16f24224bc2781edc38be8cb9e1418fb30e)) +* **packages:** Converters package no longer worked after 0.13.0 ([433795c](https://github.com/sile-typesetter/sile/commit/433795c3979688469a098a9966a595a4b0d34818)) +* **packages:** Correct chord line height and chord font use ([65961c6](https://github.com/sile-typesetter/sile/commit/65961c6629244817220bac8a6f386a9a738b7f0b)), closes [#1351](https://github.com/sile-typesetter/sile/issues/1351) + ## [0.14.9](https://github.com/sile-typesetter/sile/compare/v0.14.8...v0.14.9) (2023-04-11) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90373dcb7..1f362b3ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,7 @@ if (WIN32) endif() -add_library(justenoughlibtexpdf SHARED src/justenoughlibtexpdf.c src/imagebbox.c) +add_library(justenoughlibtexpdf SHARED justenough/justenoughlibtexpdf.c justenough/imagebbox.c) add_dependencies(justenoughlibtexpdf libtexpdf lua) target_include_directories(justenoughlibtexpdf PUBLIC "${CMAKE_SOURCE_DIR}" @@ -149,7 +149,7 @@ target_link_libraries(justenoughlibtexpdf PUBLIC libtexpdf lua51.lib) target_link_options(justenoughlibtexpdf PUBLIC /EXPORT:luaopen_justenoughlibtexpdf) -add_library(justenoughharfbuzz SHARED src/justenoughharfbuzz.c src/hb-utils.c src/hb-utils.h) +add_library(justenoughharfbuzz SHARED justenough/justenoughharfbuzz.c justenough/hb-utils.c justenough/hb-utils.h) add_dependencies(justenoughharfbuzz harfbuzz lua) target_include_directories(justenoughharfbuzz PUBLIC "${TMP_INSTALL_DIR}/include" @@ -162,7 +162,7 @@ target_compile_definitions(justenoughharfbuzz PUBLIC HAVE_HARFBUZZ_SUBSET) target_link_libraries(justenoughharfbuzz PUBLIC harfbuzz.lib harfbuzz-subset.lib lua51.lib) target_link_options(justenoughharfbuzz PUBLIC /EXPORT:luaopen_justenoughharfbuzz) -add_library(justenoughicu SHARED src/justenoughicu.c) +add_library(justenoughicu SHARED justenough/justenoughicu.c) add_dependencies(justenoughicu icu lua) target_include_directories(justenoughicu PUBLIC "${TMP_INSTALL_DIR}/include" @@ -173,7 +173,7 @@ target_link_directories(justenoughicu PUBLIC target_link_libraries(justenoughicu PUBLIC icuio.lib icuin.lib icuuc.lib icudt.lib lua51.lib) target_link_options(justenoughicu PUBLIC /EXPORT:luaopen_justenoughicu) -add_library(justenoughfontconfig SHARED src/justenoughfontconfig.c) +add_library(justenoughfontconfig SHARED justenough/justenoughfontconfig.c) add_dependencies(justenoughfontconfig fontconfig) target_include_directories(justenoughfontconfig PUBLIC "${TMP_INSTALL_DIR}/include" @@ -184,7 +184,7 @@ target_link_directories(justenoughfontconfig PUBLIC target_link_libraries(justenoughfontconfig PUBLIC fontconfig-static.lib lua51.lib expat.lib freetype.lib libpng16_static.lib zlibstatic.lib) target_link_options(justenoughfontconfig PUBLIC /EXPORT:luaopen_justenoughfontconfig) -add_library(fontmetrics SHARED src/fontmetrics.c src/hb-utils.c src/hb-utils.h) +add_library(fontmetrics SHARED justenough/fontmetrics.c justenough/hb-utils.c justenough/hb-utils.h) add_dependencies(fontmetrics harfbuzz lua) target_include_directories(fontmetrics PUBLIC "${TMP_INSTALL_DIR}/include" @@ -196,7 +196,7 @@ target_link_directories(fontmetrics PUBLIC target_link_libraries(fontmetrics PUBLIC harfbuzz.lib lua51.lib) target_link_options(fontmetrics PUBLIC /EXPORT:luaopen_fontmetrics) -add_library(svg SHARED src/svg.c) +add_library(svg SHARED justenough/svg.c) add_dependencies(svg lua) target_include_directories(svg PUBLIC "${TMP_INSTALL_DIR}/include" diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..9159408f5 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,1752 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "adler" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" + +[[package]] +name = "aho-corasick" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "anstyle-parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +dependencies = [ + "anstyle", + "windows-sys", +] + +[[package]] +name = "anyhow" +version = "1.0.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" + +[[package]] +name = "arc-swap" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bddcadddf5e9015d310179a59bb28c4d4b9920ad0f11e8e14dbadf654890c9a6" + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2f7349907b712260e64b0afe2f84692af14a454be26187d9df565c7f69266a" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "btoi" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd6407f73a9b8b6162d8a2ef999fe6afd7cc15902ebf42c5cd296addf17e0ad" +dependencies = [ + "num-traits", +] + +[[package]] +name = "cc" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +dependencies = [ + "libc", +] + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "clap" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d04704f56c2cde07f43e8e2c154b43f216dc5c92fc98ada720177362f953b956" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e231faeaca65ebd1ea3c737966bf858971cd38c3849107aa3ea7de90a804e45" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", + "terminal_size", +] + +[[package]] +name = "clap_complete" +version = "4.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ae8ba90b9d8b007efe66e55e48fb936272f5ca00349b5b0e89877520d35ea7" +dependencies = [ + "clap", +] + +[[package]] +name = "clap_derive" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0862016ff20d69b84ef8247369fabf5c008a7417002411897d40ee1f4532b873" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "clap_lex" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" + +[[package]] +name = "clap_mangen" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b44f35c514163027542f7147797ff930523eea288e03642727348ef1a9666f6b" +dependencies = [ + "clap", + "roff", +] + +[[package]] +name = "clru" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8191fa7302e03607ff0e237d4246cc043ff5b3cb9409d995172ba3bea16b807" + +[[package]] +name = "cmake" +version = "0.1.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130" +dependencies = [ + "cc", +] + +[[package]] +name = "colorchoice" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" + +[[package]] +name = "core-foundation" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "194a7a9e6de53fa55116934067c844d9d749312f75c6f6d0980e8c252f8c2146" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" + +[[package]] +name = "core-graphics" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bb142d41022986c1d8ff29103a1411c8a3dfad3552f87a4f8dc50d61d4f4e33" +dependencies = [ + "bitflags 1.3.2", + "core-foundation", + "libc", +] + +[[package]] +name = "core-text" +version = "19.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d74ada66e07c1cefa18f8abfba765b486f250de2e4a999e5727fc0dd4b4a25" +dependencies = [ + "core-foundation", + "core-graphics", + "foreign-types", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a17b76ff3a4162b0b27f354a0c87015ddad39d35f9c0c36607a3bdd175dde1f1" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deranged" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2696e8a945f658fd14dc3b87242e6b80cd0f36ff04ea560fa39082368847946" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dunce" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ce8c6da7551ec6c462cbaf3bfbc75131ebbfa1c944aeaa9dab51ca1c5f0c3b" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "errno" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "add4f07d43996f76ef320709726a556a9d4f965d9410d8d0271132d2f8293480" +dependencies = [ + "errno-dragonfly", + "libc", + "windows-sys", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "faster-hex" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "239f7bfb930f820ab16a9cd95afc26f88264cf6905c960b340a615384aa3338a" +dependencies = [ + "serde", +] + +[[package]] +name = "fastrand" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5" + +[[package]] +name = "filetime" +version = "0.2.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4029edd3e734da6fe05b6cd7bd2960760a616bd2ddd0d59a0124746d6272af0" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "windows-sys", +] + +[[package]] +name = "flate2" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6c98ee8095e9d1dcbf2fcc6d95acccb90d1c81db1e44725c6a984b1dbdfb010" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freetype" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee38378a9e3db1cc693b4f88d166ae375338a0ff75cb8263e1c601d51f35dc6" +dependencies = [ + "freetype-sys", + "libc", +] + +[[package]] +name = "freetype-sys" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a37d4011c0cc628dfa766fcc195454f4b068d7afdc2adfd28861191d866e731a" +dependencies = [ + "cmake", + "libc", + "pkg-config", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gix" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06a8c9f9452078f474fecd2880de84819b8c77224ab62273275b646bf785f906" +dependencies = [ + "gix-actor", + "gix-commitgraph", + "gix-config", + "gix-date", + "gix-diff", + "gix-discover", + "gix-features", + "gix-fs", + "gix-glob", + "gix-hash", + "gix-hashtable", + "gix-index", + "gix-lock", + "gix-macros", + "gix-object", + "gix-odb", + "gix-pack", + "gix-path", + "gix-ref", + "gix-refspec", + "gix-revision", + "gix-revwalk", + "gix-sec", + "gix-tempfile", + "gix-trace", + "gix-traverse", + "gix-url", + "gix-utils", + "gix-validate", + "once_cell", + "parking_lot", + "signal-hook", + "smallvec", + "thiserror", + "unicode-normalization", +] + +[[package]] +name = "gix-actor" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8c6778cc03bca978b2575a03e04e5ba6f430a9dd9b0f1259f0a8a9a5e5cc66" +dependencies = [ + "bstr", + "btoi", + "gix-date", + "itoa", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-bitmap" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ccab4bc576844ddb51b78d81b4a42d73e6229660fa614dfc3d3999c874d1959" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-chunk" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b42ea64420f7994000130328f3c7a2038f639120518870436d31b8bde704493" +dependencies = [ + "thiserror", +] + +[[package]] +name = "gix-commitgraph" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4676ede3a7d37e7028e2889830349a6aca22efc1d2f2dd9fa3351c1a8ddb0c6a" +dependencies = [ + "bstr", + "gix-chunk", + "gix-features", + "gix-hash", + "memmap2", + "thiserror", +] + +[[package]] +name = "gix-config" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1108c4ac88248dd25cc8ab0d0dae796e619fb72d92f88e30e00b29d61bb93cc4" +dependencies = [ + "bstr", + "gix-config-value", + "gix-features", + "gix-glob", + "gix-path", + "gix-ref", + "gix-sec", + "memchr", + "once_cell", + "smallvec", + "thiserror", + "unicode-bom", + "winnow", +] + +[[package]] +name = "gix-config-value" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea7505b97f4d8e7933e29735a568ba2f86d8de466669d9f0e8321384f9972f47" +dependencies = [ + "bitflags 2.4.0", + "bstr", + "gix-path", + "libc", + "thiserror", +] + +[[package]] +name = "gix-date" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7df669639582dc7c02737642f76890b03b5544e141caba68a7d6b4eb551e0d" +dependencies = [ + "bstr", + "itoa", + "thiserror", + "time", +] + +[[package]] +name = "gix-diff" +version = "0.35.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b45e342d148373bd9070d557e6fb1280aeae29a3e05e32506682d027278501eb" +dependencies = [ + "gix-hash", + "gix-object", + "thiserror", +] + +[[package]] +name = "gix-discover" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da4cacda5ee9dd1b38b0e2506834e40e66c08cf050ef55c344334c76745f277b" +dependencies = [ + "bstr", + "dunce", + "gix-hash", + "gix-path", + "gix-ref", + "gix-sec", + "thiserror", +] + +[[package]] +name = "gix-features" +version = "0.34.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f414c99e1a7abc69b21f3225a6539d203b0513f1d1d448607c4ea81cdcf9ee59" +dependencies = [ + "crc32fast", + "flate2", + "gix-hash", + "gix-trace", + "libc", + "once_cell", + "prodash", + "sha1_smol", + "thiserror", + "walkdir", +] + +[[package]] +name = "gix-fs" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "404795da3d4c660c9ab6c3b2ad76d459636d1e1e4b37b0c7ff68eee898c298d4" +dependencies = [ + "gix-features", +] + +[[package]] +name = "gix-glob" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ac79c444193b0660fe0c0925d338bd338bd643e32138784dccfb12c628b892" +dependencies = [ + "bitflags 2.4.0", + "bstr", + "gix-features", + "gix-path", +] + +[[package]] +name = "gix-hash" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ccf425543779cddaa4a7c62aba3fa9d90ea135b160be0a72dd93c063121ad4a" +dependencies = [ + "faster-hex", + "thiserror", +] + +[[package]] +name = "gix-hashtable" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "409268480841ad008e81c17ca5a293393fbf9f2b6c2f85b8ab9de1f0c5176a16" +dependencies = [ + "gix-hash", + "hashbrown", + "parking_lot", +] + +[[package]] +name = "gix-index" +version = "0.24.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e9599fc30b3d6aad231687a403f85dfa36ae37ccf1b68ee1f621ad5b7fc7a0d" +dependencies = [ + "bitflags 2.4.0", + "bstr", + "btoi", + "filetime", + "gix-bitmap", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-traverse", + "itoa", + "memmap2", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-lock" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1568c3d90594c60d52670f325f5db88c2d572e85c8dd45fabc23d91cadb0fd52" +dependencies = [ + "gix-tempfile", + "gix-utils", + "thiserror", +] + +[[package]] +name = "gix-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d8acb5ee668d55f0f2d19a320a3f9ef67a6999ad483e11135abcc2464ed18b6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "gix-object" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e5528d5b2c984044d547e696e44a8c45fa122e83cd8c2ac1da69bd474336be8" +dependencies = [ + "bstr", + "btoi", + "gix-actor", + "gix-date", + "gix-features", + "gix-hash", + "gix-validate", + "itoa", + "smallvec", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-odb" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0446eca295459deb3d6dd6ed7d44a631479f1b7381d8087166605c7a9f717c6" +dependencies = [ + "arc-swap", + "gix-date", + "gix-features", + "gix-hash", + "gix-object", + "gix-pack", + "gix-path", + "gix-quote", + "parking_lot", + "tempfile", + "thiserror", +] + +[[package]] +name = "gix-pack" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be19ee650300d7cbac5829b637685ec44a8d921a7c2eaff8a245d8f2f008870c" +dependencies = [ + "clru", + "gix-chunk", + "gix-features", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-path", + "gix-tempfile", + "memmap2", + "parking_lot", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-path" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a1d370115171e3ae03c5c6d4f7d096f2981a40ddccb98dfd704c773530ba73b" +dependencies = [ + "bstr", + "gix-trace", + "home", + "once_cell", + "thiserror", +] + +[[package]] +name = "gix-quote" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "475c86a97dd0127ba4465fbb239abac9ea10e68301470c9791a6dd5351cdc905" +dependencies = [ + "bstr", + "btoi", + "thiserror", +] + +[[package]] +name = "gix-ref" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cccbfa8d5cd9b86465f27a521e0c017de54b92d9fd37c143e49c658a2f04f3a" +dependencies = [ + "gix-actor", + "gix-date", + "gix-features", + "gix-fs", + "gix-hash", + "gix-lock", + "gix-object", + "gix-path", + "gix-tempfile", + "gix-validate", + "memmap2", + "thiserror", + "winnow", +] + +[[package]] +name = "gix-refspec" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678ba30d95baa5462df9875628ed40655d5f5b8aba7028de86ed57f36e762c6c" +dependencies = [ + "bstr", + "gix-hash", + "gix-revision", + "gix-validate", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-revision" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3e80a5992ae446fe1745dd26523b86084e3f1b6b3e35377fe09b4f35ac8f151" +dependencies = [ + "bstr", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "gix-trace", + "thiserror", +] + +[[package]] +name = "gix-revwalk" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b806349bc1f668e09035800e07ac8045da4e39a8925a245d93142c4802224ec1" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-sec" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92b9542ac025a8c02ed5d17b3fc031a111a384e859d0be3532ec4d58c40a0f28" +dependencies = [ + "bitflags 2.4.0", + "gix-path", + "libc", + "windows", +] + +[[package]] +name = "gix-tempfile" +version = "9.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2762b91ff95e27ff3ea95758c0d4efacd7435a1be3629622928b8276de0f72a8" +dependencies = [ + "gix-fs", + "libc", + "once_cell", + "parking_lot", + "signal-hook", + "signal-hook-registry", + "tempfile", +] + +[[package]] +name = "gix-trace" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96b6d623a1152c3facb79067d6e2ecdae48130030cf27d6eb21109f13bd7b836" + +[[package]] +name = "gix-traverse" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec6358f8373fb018af8fc96c9d2ec6a5b66999e2377dc40b7801351fec409ed" +dependencies = [ + "gix-commitgraph", + "gix-date", + "gix-hash", + "gix-hashtable", + "gix-object", + "gix-revwalk", + "smallvec", + "thiserror", +] + +[[package]] +name = "gix-url" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c79d595b99a6c7ab274f3c991735a0c0f5a816a3da460f513c48edf1c7bf2cc" +dependencies = [ + "bstr", + "gix-features", + "gix-path", + "home", + "thiserror", + "url", +] + +[[package]] +name = "gix-utils" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85d89dc728613e26e0ed952a19583744e7f5240fcd4aa30d6c824ffd8b52f0f" +dependencies = [ + "fastrand", +] + +[[package]] +name = "gix-validate" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05cab2b03a45b866156e052aa38619f4ece4adcb2f79978bfc249bc3b21b8c5" +dependencies = [ + "bstr", + "thiserror", +] + +[[package]] +name = "globset" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "759c97c1e17c55525b57192c06a267cda0ac5210b222d6b82189a2338fa1c13d" +dependencies = [ + "aho-corasick", + "bstr", + "fnv", + "log", + "regex", +] + +[[package]] +name = "harfbuzz-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf8c27ca13930dc4ffe474880040fe9e0f03c2121600dc9c95423624cab3e467" +dependencies = [ + "cc", + "core-graphics", + "core-text", + "foreign-types", + "freetype", + "pkg-config", +] + +[[package]] +name = "hashbrown" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfda62a12f55daeae5015f81b0baea145391cb4520f86c248fc615d72640d12" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "home" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "idna" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" +dependencies = [ + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" + +[[package]] +name = "libc" +version = "0.2.148" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cdc71e17332e86d2e1d38c1f99edcb6288ee11b815fb1a4b049eaa2114d369b" + +[[package]] +name = "linux-raw-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3852614a3bd9ca9804678ba6be5e3b8ce76dfc902cae004e3e0c44051b6e88db" + +[[package]] +name = "lock_api" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "lua-src" +version = "546.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c26d4af78361e025a3d03a2b964cd1592aff7495f4d4f7947218c084c6fdca8" +dependencies = [ + "cc", +] + +[[package]] +name = "luajit-src" +version = "210.4.8+resty107baaf" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05167e8b2a2185758d83ed23541e5bd8bce37072e4204e0ef2c9b322bc87c4e" +dependencies = [ + "cc", + "which", +] + +[[package]] +name = "memchr" +version = "2.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" + +[[package]] +name = "memmap2" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f49388d20533534cd19360ad3d6a7dadc885944aa802ba3995040c5ec11288c6" +dependencies = [ + "libc", +] + +[[package]] +name = "miniz_oxide" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +dependencies = [ + "adler", +] + +[[package]] +name = "mlua" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c3a7a7ff4481ec91b951a733390211a8ace1caba57266ccb5f4d4966704e560" +dependencies = [ + "bstr", + "mlua-sys", + "mlua_derive", + "num-traits", + "once_cell", + "rustc-hash", +] + +[[package]] +name = "mlua-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ec8b54eddb76093069cce9eeffb4c7b3a1a0fe66962d7bd44c4867928149ca3" +dependencies = [ + "cc", + "cfg-if", + "lua-src", + "luajit-src", + "pkg-config", +] + +[[package]] +name = "mlua_derive" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f359220f24e6452dd82a3f50d7242d4aab822b5594798048e953d7a9e0314c6" +dependencies = [ + "itertools", + "once_cell", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn 2.0.38", +] + +[[package]] +name = "num-traits" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_threads" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2819ce041d2ee131036f4fc9d6ae7ae125a3a40e97ba64d04fe799ad9dabbb44" +dependencies = [ + "libc", +] + +[[package]] +name = "once_cell" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" + +[[package]] +name = "parking_lot" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-targets", +] + +[[package]] +name = "percent-encoding" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" + +[[package]] +name = "pkg-config" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d433d9f1a3e8c1263d9456598b16fec66f4acc9a74dacffd35c7bb09b3a1328" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prodash" +version = "26.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "794b5bf8e2d19b53dcdcec3e4bba628e20f5b6062503ba89281fa7037dd7bbcf" + +[[package]] +name = "quote" +version = "1.0.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", +] + +[[package]] +name = "regex" +version = "1.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" + +[[package]] +name = "roff" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b833d8d034ea094b1ea68aa6d5c740e0d04bad9d16568d08ba6f76823a114316" + +[[package]] +name = "rust-embed" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e7d90385b59f0a6bf3d3b757f3ca4ece2048265d70db20a2016043d4509a40" +dependencies = [ + "rust-embed-impl", + "rust-embed-utils", + "walkdir", +] + +[[package]] +name = "rust-embed-impl" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3d8c6fd84090ae348e63a84336b112b5c3918b3bf0493a581f7bd8ee623c29" +dependencies = [ + "proc-macro2", + "quote", + "rust-embed-utils", + "syn 2.0.38", + "walkdir", +] + +[[package]] +name = "rust-embed-utils" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "873feff8cb7bf86fdf0a71bb21c95159f4e4a37dd7a4bd1855a940909b583ada" +dependencies = [ + "globset", + "sha2", + "walkdir", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustix" +version = "0.38.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25469e9ae0f3d0047ca8b93fc56843f38e6774f0914a107ff8b41be8be8e0b7" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.188" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "sha1_smol" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012" + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + +[[package]] +name = "sile" +version = "0.14.8" +dependencies = [ + "anyhow", + "clap", + "clap_complete", + "clap_mangen", + "harfbuzz-sys", + "mlua", + "rust-embed", + "vergen", +] + +[[package]] +name = "smallvec" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" +dependencies = [ + "cfg-if", + "fastrand", + "redox_syscall", + "rustix", + "windows-sys", +] + +[[package]] +name = "terminal_size" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7" +dependencies = [ + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1177e8c6d7ede7afde3585fd2513e611227efd6481bd78d2e82ba1ce16557ed4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10712f02019e9288794769fba95cd6847df9874d49d871d062172f9dd41bc4cc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.38", +] + +[[package]] +name = "time" +version = "0.3.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "426f806f4089c493dcac0d24c29c01e2c38baf8e30f1b716ee37e83d200b18fe" +dependencies = [ + "deranged", + "itoa", + "libc", + "num_threads", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +dependencies = [ + "time-core", +] + +[[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "unicode-bidi" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" + +[[package]] +name = "unicode-bom" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98e90c70c9f0d4d1ee6d0a7d04aa06cb9bbd53d8cfbdd62a0269a7c2eb640552" + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unicode-normalization" +version = "0.1.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5713f0fc4b5db668a2ac63cdb7bb4469d8c9fed047b1d0292cc7b0ce2ba921" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "url" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "143b538f18257fac9cad154828a57c6bf5157e1aa604d4816b5995bf6de87ae5" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", +] + +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + +[[package]] +name = "vergen" +version = "8.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85e7dc29b3c54a2ea67ef4f953d5ec0c4085035c0ae2d325be1c0d2144bd9f16" +dependencies = [ + "anyhow", + "gix", + "rustversion", + "time", +] + +[[package]] +name = "version_check" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" + +[[package]] +name = "walkdir" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "winnow" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c2e3184b9c4e92ad5167ca73039d0c42476302ab603e2fec4487511f38ccefc" +dependencies = [ + "memchr", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..fa0e6157d --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,87 @@ +[package] +name = "sile" +version = "0.14.8" +edition = "2021" +rust-version = "1.71.0" +description = "Simon’s Improved Layout Engine" +authors = [ + "Simon Cozens", + "Caleb Maclennan ", + "Olivier Nicole", + "Didier Willis" +] +readme = "README.md" +homepage = "https://sile-typesetter.org" +repository = "https://github.com/sile-typesetter/sile" +license = "MIT" +build = "build-aux/build.rs" +# links = "svg" + +[[bin]] +name = "sile" +required-features = [ "cli" ] + +[features] +default = [ "cli", "bash", "elvish", "fish", "manpage", "powershell", "zsh" ] +lua54 = [ "mlua/lua54" ] +lua53 = [ "mlua/lua53" ] +lua52 = [ "mlua/lua52" ] +lua51 = [ "mlua/lua51" ] +luajit = [ "mlua/luajit" ] +vendored = [ "mlua/vendored" ] +static = [ "rust-embed" ] +variations = [] +completions = [ "cli", "clap_complete" ] +cli = [ "clap" ] +bash = [ "completions" ] +elvish = [ "completions" ] +fish = [ "completions" ] +manpage = [ "clap_mangen" ] +powershell = [ "completions" ] +zsh = [ "completions" ] + +[profile.release] +lto = true + +[dependencies] + + [dependencies.anyhow] + version = "1.0" + + [dependencies.clap] + version = "4.4" + optional = true + features = [ "derive", "string", "wrap_help" ] + + [dependencies.mlua] + version = "0.9" + features = [ "macros" ] + + [dependencies.rust-embed] + version = "8.0" + optional = true + features = [ "include-exclude" ] + + [dependencies.harfbuzz-sys] + version = "0.5" + optional = true + +[build-dependencies] + + [build-dependencies.clap_complete] + version = "4.4" + optional = true + + [build-dependencies.clap_mangen] + version = "0.2" + optional = true + + [build-dependencies.clap] + version = "4.4" + optional = true + features = [ "derive" ] + + [build-dependencies.vergen] + version = "8.2" + default-features = false + features = [ "build", "cargo", "git", "gitoxide" ] diff --git a/Dockerfile b/Dockerfile index cab044aab..335d543d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -26,7 +26,7 @@ WORKDIR /src RUN build-aux/docker-bootstrap.sh RUN ./bootstrap.sh -RUN ./configure --without-manual +RUN ./configure --with-system-lua-sources --without-manual RUN make RUN make check RUN make install DESTDIR=/pkgdir diff --git a/LICENSE b/LICENSE deleted file mode 100644 index c1a6832f1..000000000 --- a/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2012-2022 Simon Cozens, Caleb Maclennan. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 000000000..2b0c94e73 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,25 @@ +The MIT License (MIT) +===================== + +Copyright © `2012-2023` `Simon Cozens, Caleb Maclennan` + +Permission is hereby granted, free of charge, to any person +obtaining a copy of this software and associated documentation +files (the “Software”), to deal in the Software without +restriction, including without limitation the rights to use, +copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. diff --git a/Makefile-fonts b/Makefile-fonts index dcb7c15c6..57cb88644 100644 --- a/Makefile-fonts +++ b/Makefile-fonts @@ -1,3 +1,5 @@ +.PHONY: fonttooling + if FONT_DOWNLOAD_TOOLS .fonts: fonttooling @@ -6,7 +8,6 @@ if FONT_DOWNLOAD_TOOLS .sources: fonttooling [ -h .sources ] || mkdir -p $@ -.PHONY: fonttooling fonttooling: $(if $(BSDTAR),,$(error Please set BSDTAR with path or `./configure --enable-developer`)) $(if $(CURL),,$(error Please set CURL with path or `./configure --enable-developer`)) diff --git a/Makefile-luarocks b/Makefile-luarocks index 46e5c8ef3..6042815d9 100644 --- a/Makefile-luarocks +++ b/Makefile-luarocks @@ -1,19 +1,22 @@ .PHONY: installrocks + LUAMODSPEC := sile-dev-1.rockspec if !SYSTEM_LUAROCKS LUAMODLOCK := sile-dev-1.rockslock LOCALLUAROCKS := $(LUAROCKS) --tree lua_modules --lua-version $(LUA_VERSION) -TMPFILE != mktemp genrockslock := $(LOCALLUAROCKS) $(LUAROCKSARGS) list --porcelain | $(AWK) '{print $$1 " " $$2}' rocksmatch := ( T=$$(mktemp); trap 'rm -f "$$T"' EXIT HUP TERM; $(genrockslock) > "$$T"; $(CMP) -s $(LUAMODLOCK) "$$T" ) -installrocks: $(LUAMODLOCK) $(shell $(rocksmatch) || echo lua_modules) +LUAROCKSMANIFEST := lua_modules/lib/luarocks/rocks-$(LUA_VERSION)/manifest + +installrocks: $(LUAMODLOCK) $(shell $(rocksmatch) || echo $(LUAROCKSMANIFEST)) -lua_modules: $(LUAMODSPEC) $(shell $(rocksmatch) || echo force) +$(LUAROCKSMANIFEST): $(LUAMODSPEC) $(shell $(rocksmatch) || echo force) $(LOCALLUAROCKS) $(LUAROCKSARGS) install --only-deps $< + touch $@ -$(LUAMODLOCK): lua_modules $(LUAMODSPEC) +$(LUAMODLOCK): $(LUAROCKSMANIFEST) $(LUAMODSPEC) $(genrockslock) > $@ else LUAMODLOCK := diff --git a/Makefile.am b/Makefile.am index 5ffe5aac6..23a68cdac 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,14 +5,14 @@ ACLOCAL_AMFLAGS = -I build-aux .SECONDEXPANSION: .DELETE_ON_ERROR: -if SYSTEM_LIBTEXPDF -SUBDIRS = src -else -SUBDIRS = libtexpdf src +SUBDIRS = +if !SYSTEM_LIBTEXPDF +SUBDIRS += libtexpdf endif +SUBDIRS += justenough . -licensedir = $(datarootdir)/licenses/$(TRANSFORMED_PACKAGE_NAME) docdir = $(datarootdir)/doc/$(TRANSFORMED_PACKAGE_NAME) +licensedir = $(datarootdir)/licenses/$(TRANSFORMED_PACKAGE_NAME) datadir = $(datarootdir)/$(TRANSFORMED_PACKAGE_NAME) pkgdatadir = $(datadir) @@ -42,55 +42,109 @@ TESTPREVIEWS ?= $(addsuffix .pdf,$(basename $(filter-out $(_DISABLEDSRCS),$(_TES # BUILT_SOURCES and EXTRA_DIST) this doesn't induce a race. include $(wildcard Makefile-distfiles) +FIGURES = documentation/fig-input-to-output.pdf + MANUAL := documentation/sile.pdf -SILE := $(PACKAGE_NAME) +SILELUA := $(PACKAGE_NAME)-lua if MANUAL _MANUAL = $(MANUAL) + endif +$(MANUAL): $(FIGURES) + +BUILT_LUA_SOURCES = core/features.lua core/pathsetup.lua core/version.lua + +bin_PROGRAMS = sile +bin_SCRIPTS = sile-lua +dist_man_MANS = sile-lua.1 +sile_SOURCES = src/bin/sile.rs src/lib.rs src/cli.rs +EXTRA_sile_SOURCES = +if !EMBEDED_RESOURCES nobase_dist_pkgdata_DATA = $(SILEDATA) $(LUALIBRARIES) -nobase_nodist_pkgdata_DATA = core/features.lua core/version.lua $(LUAMODULES) -dist_man_MANS = sile.1 +nobase_nodist_pkgdata_DATA = $(BUILT_LUA_SOURCES) $(LUAMODULES) +endif dist_doc_DATA = README.md CHANGELOG.md dist_pdf_DATA = $(_MANUAL) -dist_license_DATA = LICENSE lua-libraries/LICENSE-lunamark -bin_SCRIPTS = sile +dist_license_DATA = LICENSE.md EXTRA_DIST = spec tests documentation sile-dev-1.rockspec fontconfig.conf EXTRA_DIST += Makefile-distfiles -EXTRA_DIST += build-aux/action-updater.js build-aux/decore-automake.sh build-aux/git-version-gen build-aux/list-dist-files.sh +EXTRA_DIST += build-aux/action-updater.js build-aux/cargo-updater.js build-aux/decore-automake.sh build-aux/git-version-gen build-aux/list-dist-files.sh EXTRA_DIST += Dockerfile build-aux/docker-bootstrap.sh build-aux/docker-fontconfig.conf hooks/build -EXTRA_DIST += default.nix flake.nix flake.lock libtexpdf.git-rev shell.nix +EXTRA_DIST += default.nix flake.nix flake.lock shell.nix build-aux/pkg.nix EXTRA_DIST += package.json # imported by both Nix and Docker -EXTRA_DIST += $(MANUAL) +EXTRA_DIST += $(MANUAL) $(FIGURES) +EXTRA_DIST += src/embed.rs.in -BUILT_SOURCES = .version core/features.lua core/version.lua Makefile-distfiles +BUILT_SOURCES = $(BUILT_LUA_SOURCES) Makefile-distfiles -CLEANFILES = $(bin_SCRIPTS) $(dist_man_MANS) $(BUILT_SOURCES) $(DEPFILES) $(ACTUALS) $(TESTPDFS) $(MANUAL) $(_BUILT_SUBDIRS) .version-prev +CLEANFILES = $(DEPFILES) $(ACTUALS) $(TESTPDFS) $(MANUAL) $(_BUILT_SUBDIRS) + +include $(top_srcdir)/build-aux/rust_boilerplate.mk Makefile-distfiles: $(wildcard .version .tarball-version) | $(LUAMODLOCK) $(SHELL) build-aux/list-dist-files.sh > $@ -_BRANCH_REF != $(AWK) '{print ".git/" $$2}' .git/HEAD 2>/dev/null ||: - -.version: $(_BRANCH_REF) - @if [ -e "$(srcdir)/.tarball-version" ]; then \ - printf "$(VERSION)" > $@; \ - else \ - touch "$@-prev"; \ - if [ -e "$@" ]; then \ - cp "$@" "$@-prev"; \ - fi; \ - ./build-aux/git-version-gen "$(srcdir)/.tarball-version" > $@; \ - cmp -s "$@" "$@-prev" || ( autoreconf configure.ac --force && build-aux/decore-automake.sh ); \ - fi +if EMBEDED_RESOURCES +_EMBEDED_SOURCES = src/embed.rs src/embed-includes.rs +nodist_sile_SOURCES = $(_EMBEDED_SOURCES) +BUILT_SOURCES += $(_EMBEDED_SOURCES) +CLEANFILES += $(_EMBEDED_SOURCES) +$(CARGO_BIN): justenough/.libs/fontmetrics.a +$(CARGO_BIN): justenough/.libs/justenoughfontconfig.a +$(CARGO_BIN): justenough/.libs/justenoughharfbuzz.a +$(CARGO_BIN): justenough/.libs/justenoughicu.a +$(CARGO_BIN): justenough/.libs/justenoughlibtexpdf.a +$(CARGO_BIN): justenough/.libs/svg.a +$(CARGO_BIN): libtexpdf/.libs/libtexpdf.a + +src/embed-includes.rs: Makefile-distfiles + { + echo $(BUILT_LUA_SOURCES) + $(GREP) -E '^(SILEDATA|LUALIBRARIES|LUAMODULES) = ' $< + } | + $(SED) -E -e 's/^.* = //;s/ /\n/g' | + while read file; do + echo "#[include = \"$${file}\"]" + done > $@ + +src/embed.rs: src/embed.rs.in src/embed-includes.rs + $(SED) \ + -e '/@INCLUDE_EMDED_INCLUDES@/r $(word 2,$^)' \ + -e '/@INCLUDE_EMDED_INCLUDES@/d' \ + $< > $@ +endif EMBEDED_RESOURCES + +if LUAJIT +MLUAVER = luajit +else +MLUAVER = lua$(LUA_SHORT_VERSION) +endif +CARGO_FEATURE_ARGS = --features $(MLUAVER) + +if !SYSTEM_LUA_SOURCES +CARGO_FEATURE_ARGS += --features vendored +endif + +if EMBEDED_RESOURCES +CARGO_FEATURE_ARGS += --features static +endif + +if FONT_VARIATIONS +CARGO_FEATURE_ARGS += --features variations +endif + +DEPDIR := .deps +LOCALFONTS := FONTCONFIG_FILE=$(PWD)/fontconfig.conf +LOCALPATHS := SILE_PATH="$(PWD);libtexpdf/.libs;justenough/.libs" +SILEFLAGS ?= -m $(DEPDIR)/$(basename $@).d -d versions -f fontconfig -libtexpdf.git-rev: $(_BRANCH_REF) - $(GIT) submodule status -- libtexpdf | $(AWK) '{ print $$1 }' > $@ +dist-hook: $(MANUAL) dist-hook-distfiles -dist-hook: $(MANUAL) +.PHONY: dist-hook-distfiles +dist-hook-distfiles: cd $(distdir) - printf "$(VERSION)" > .tarball-version $(SED) -i -e '/^LUAMODULES =/s/=.*/=/' Makefile-distfiles $(top_srcdir)/build-aux/decore-automake.sh $(SED) -i -e '/^LUAMODULES/d;/^\tlua_modules/d' Makefile.in @@ -98,25 +152,6 @@ dist-hook: $(MANUAL) # Whether to force tests to run from scratch CLEAN ?= -RELTYPE ?= - -.PHONY: tagrelease -tagrelease: - test -z $$($(GIT) tag --points-at HEAD) || exit 0 # end if we are already on a release tag - $(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything staged but not committed - $(GIT) diff-files --quiet || exit 1 # die if any tracked files have unstagged changes - npm run release -- $(and $(RELTYPE),--release-as $(RELTYPE)) - -.PHONY: prerelease -prerelease: test docs update_libtexpdf - -.PHONY: release-preview -release-preview: - npm run release -- --dry-run $(and $(RELTYPE),--release-as $(RELTYPE)) - -.PHONY: release -release: tagrelease - dist: sile-$(VERSION).pdf sile-$(VERSION).md sile-$(VERSION).pdf: $(MANUAL) @@ -126,31 +161,13 @@ sile-%.md: CHANGELOG.md $(SED) -e '/\.\.\.v$*/,/\.\.\.v/!d' CHANGELOG.md | \ $(SED) -e '1,3d;N;$$!P;$$!D;$$d' > $@ -.PHONY: update_libtexpdf -update_libtexpdf: - $(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything already staged - $(GIT) submodule update --init --remote -- libtexpdf - $(GIT) submodule status -- libtexpdf | $(AWK) '{ print $$1 }' > libtexpdf.git-rev - $(GIT) add -- libtexpdf libtexpdf.git-rev - $(GIT) diff-index --quiet --cached HEAD || $(GIT) commit -m "chore(build): Pin latest libtexpdf library submodule" - -DEPDIR := .deps -REGRESSIONSCRIPT := ./tests/regressions.pl -LOCALTESTFONTS := FONTCONFIG_FILE=$(PWD)/fontconfig.conf -SILEFLAGS ?= -m $(DEPDIR)/$(basename $@).d -d versions -f fontconfig -BUSTEDFLAGS ?= $(and $(SILE_COVERAGE),-c) - -TESTPDFS = $(addsuffix .pdf,$(basename $(TESTSRCS))) -EXPECTEDS ?= $(filter $(addsuffix .expected,$(basename $(TESTSRCS))),$(TESTEXPECTS)) -ACTUALS = $(addsuffix .actual,$(basename $(EXPECTEDS))) - check: selfcheck .PHONY: selfcheck -selfcheck: | $(_BUILT_SUBDIRS) +selfcheck: | $(bin_PROGRAMS) $(_BUILT_SUBDIRS) output=$$(mktemp -t selfcheck-XXXXXX.pdf) trap 'rm -f $$output' EXIT HUP TERM - echo "foo" | ./$(SILE) -o $$output - + echo "foo" | $(LOCALPATHS) ./$(bin_PROGRAMS) -o $$output - $(PDFINFO) $$output | $(GREP) "SILE v$(VERSION)" .PHONY: docs @@ -160,7 +177,8 @@ docs: $(MANUAL) # garantee the TOC is up to date, simplify when #230 is fixed. hastoc = [ -f $(subst .pdf,.toc,$@) ] && echo true || echo false pages = $(PDFINFO) $@ | $(AWK) '$$1 == "Pages:" {print $$2}' || echo 0 -silepass = $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) $< -o $@ && pg0=$${pg} pg=$$($(pages)) || false +localsile = $(LOCALFONTS) $(LOCALPATHS) ./$(bin_PROGRAMS) $(SILEFLAGS) +silepass = $(localsile) $< -o $@ && pg0=$${pg} pg=$$($(pages)) || false define runsile = set -e pg=$$($(pages)) hadtoc=$$($(hastoc)) @@ -180,15 +198,20 @@ _DOCS_DEPS = $(and $$(filter documentation/%,$@),$(addprefix .fonts/,$(DOCSFONTF # TODO: remove _BUILT_SUBDIRS hack and replace it with something sensible when # these subdirs don't do crazy things like copying files outside of their own trees! _BUILT_SUBDIRS = .built-subdirs -_SUBDIR_TELLS = justenoughfontconfig.so justenoughharfbuzz.so justenoughicu.so justenoughlibtexpdf.so libtexpdf/.libs/libtexpdf.so.0.0.0 +_SUBDIR_TELLS = justenough/.libs/fontmetrics.so \ + justenough/.libs/justenoughfontconfig.so \ + justenough/.libs/justenoughharfbuzz.so \ + justenough/.libs/justenoughicu.so \ + justenough/.libs/justenoughlibtexpdf.so \ + justenough/.libs/svg.so \ + libtexpdf/.libs/libtexpdf.so.0.0.0 $(_BUILT_SUBDIRS): $(_SUBDIR_TELLS) touch $@ $(_SUBDIR_TELLS): $(MAKE) $(AM_MAKEFLAGS) all-recursive -# $(error Running `make install`, `make dist`, or other end-game targets before `make all` unspported.) -patterndeps = $(_FORCED) $(_TEST_DEPS) $(_DOCS_DEPS) | $(DEPDIRS) $(LUAMODLOCK) $(_BUILT_SUBDIRS) +patterndeps = $(_FORCED) $(_TEST_DEPS) $(_DOCS_DEPS) | $(bin_PROGRAMS) $(DEPDIRS) $(LUAMODLOCK) $(_BUILT_SUBDIRS) %.pdf: %.sil $$(patterndeps) $(runsile) @@ -199,16 +222,52 @@ patterndeps = $(_FORCED) $(_TEST_DEPS) $(_DOCS_DEPS) | $(DEPDIRS) $(LUAMODLOCK) %.pdf: %.nil $$(patterndeps) $(runsile) +%.pdf: %.dot + $(DOT) -Tpdf $< -o $@.gs + $(GS) -q -sDEVICE=pdfwrite -dCompatibilityLevel=1.5 -o $@ $@.gs + .PHONY: force force: ; -PHONY_DEVELOPER_TARGETS = regressions test lint luarocks-lint luacheck busted coverage benchmark compare update_expecteds regression_previews docker docker-dep-check docker-ghcr-to-hub docker-build-push gource.webm +PHONY_DEVELOPER_TARGETS = benchmark busted compare coverage docker docker-build-push \ + docker-dep-check docker-ghcr-to-hub gource.webm lint luacheck luarocks-lint \ + prerelease regression_previews regressions release release-preview tagrelease \ + test update_expecteds update_libtexpdf .PHONY: $(PHONY_DEVELOPER_TARGETS) if DEVELOPER +RELTYPE ?= + +tagrelease: + test -z $$($(GIT) tag --points-at HEAD) || exit 0 # end if we are already on a release tag + $(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything staged but not committed + $(GIT) diff-files --quiet || exit 1 # die if any tracked files have unstagged changes + npm run release -- $(and $(RELTYPE),--release-as $(RELTYPE)) + +prerelease: test docs update_libtexpdf + +release-preview: + npm run release -- --dry-run $(and $(RELTYPE),--release-as $(RELTYPE)) + +release: tagrelease + +update_libtexpdf: + $(GIT) diff-index --quiet --cached HEAD || exit 1 # die if anything already staged + $(GIT) submodule update --init --remote -- libtexpdf + $(NIX) flake lock --override-input libtexpdf-src github:sile-typesetter/libtexpdf/$(shell $(GIT) submodule status -- libtexpdf | awk '{print $$1}') + $(GIT) add -- libtexpdf flake.lock + $(GIT) diff-index --quiet --cached HEAD || $(GIT) commit -m "chore(build): Pin latest libtexpdf library submodule" + +TESTPDFS = $(addsuffix .pdf,$(basename $(TESTSRCS))) +EXPECTEDS ?= $(filter $(addsuffix .expected,$(basename $(TESTSRCS))),$(TESTEXPECTS)) +ACTUALS = $(addsuffix .actual,$(basename $(EXPECTEDS))) + +REGRESSIONSCRIPT := ./tests/regressions.pl +BUSTEDFLAGS ?= + regressions: $(TESTSRCS) $(ACTUALS) - $(LOCALTESTFONTS) $(REGRESSIONSCRIPT) $(TESTSRCS) + $(LOCALFONTS) $(REGRESSIONSCRIPT) $(TESTSRCS) test: regressions busted @@ -220,19 +279,19 @@ luarocks-lint: $(LUAMODSPEC) luacheck: $(LUACHECK) -j$(shell nproc) -q . -busted: $(SILE) $(addprefix .fonts/,$(TESTFONTFILES)) $(BUSTEDSPECS) +busted: $(SILELUA) $(addprefix .fonts/,$(TESTFONTFILES)) $(BUSTEDSPECS) set -f; IFS=';' -if SYSTEM_LUAROCKS - packagecpath=(./{,core/}?.$(SHARED_LIB_EXT)) packagepath=(./{,lua-libraries/}?{,/init}.lua) -else - packagecpath=(./{,core/,lua_modules/lib/lua/$(LUA_VERSION)/}?.$(SHARED_LIB_EXT)) - packagepath=(./{,lua_modules/share/lua/$(LUA_VERSION)/,lua-libraries/}?{,/init}.lua) + packagecpath=(./{,core/,{libtexpdf,justenough}/.libs/}?.$(SHARED_LIB_EXT)) +if !SYSTEM_LUAROCKS + packagepath+=(./lua_modules/share/lua/$(LUA_VERSION)/?{,/init}.lua) + packagecpath+=(./lua_modules/lib/lua/$(LUA_VERSION)/?.$(SHARED_LIB_EXT)) endif - $(LOCALTESTFONTS) $(BUSTED) --cpath="$${packagecpath[*]};;" --lpath="$${packagepath[*]};;" $(BUSTEDFLAGS) . +# Note: use of --lua causes this to be passed back through a shell loosing one layer of quoting. Drop single quotes if removing. + $(LOCALFONTS) $(BUSTED) --lua=$(LUA) --lpath="'$${packagepath[*]};;'" --cpath="'$${packagecpath[*]};;'" $(BUSTEDFLAGS) . coverage: export SILE_COVERAGE=1 -coverage: BUSTEDFLAGS = -c +coverage: BUSTEDFLAGS += -c coverage: regression_previews busted HEADSHA ?= HEAD @@ -257,27 +316,27 @@ time-%.json: benchmark-%/time.json update_expecteds: $(EXPECTEDS) tests/%.expected: tests/%.sil $$(patterndeps) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ tests/%.expected: tests/%.xml $$(patterndeps) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ tests/%.expected: tests/%.nil $$(patterndeps) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ regression_previews: $(TESTPREVIEWS) tests/%.actual: tests/%.sil $$(patterndeps) -$(if $(CLEAN),rm -f $@,:) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ tests/%.actual: tests/%.xml $$(patterndeps) -$(if $(CLEAN),rm -f $@,:) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ tests/%.actual: tests/%.nil $$(patterndeps) -$(if $(CLEAN),rm -f $@,:) - $(LOCALTESTFONTS) ./$(SILE) $(SILEFLAGS) -b debug $< -o $@ + $(localsile) -b debug $< -o $@ DEPFILES = $(addsuffix .d,$(addprefix $(DEPDIR)/,$(basename $(TESTSRCS) $(MANUAL)))) DEPDIRS = $(sort $(dir $(DEPFILES))) @@ -349,9 +408,9 @@ gource.webm: gource -a 0.2 -s 0.2 -i 0 --logo /tmp/sile-logo.jpg -b 000000 --max-file-lag 5 --hide filenames --date-format '%Y-%m-%d' --user-image-dir /tmp/gravatars --user-filter simoncozens --key -1920x1080 -o - | \ ffmpeg -y -r 60 -f image2pipe -vcodec ppm -i - -vcodec libvpx -b 10000K $@ -else +else !DEVELOPER $(PHONY_DEVELOPER_TARGETS): @: $(error "Please reconfigure using --enable-developer to use developer tooling") -endif +endif !DEVELOPER diff --git a/README.md b/README.md index a3e1857e1..17db398e9 100644 --- a/README.md +++ b/README.md @@ -65,15 +65,14 @@ Arch Linux has a prebuilt [SILE package][arch-sile] in the official package repo $ pacman -S sile ``` -The official package uses Lua 5.4. -Alternatively, a package that uses LuaJIT may be built manually from the [Arch User Repository][aur] using [sile-luajit][aur-sile-luajit]. -A VCS package is also available as [sile-git][aur-sile-git] to build from the latest Git commit. +The official package uses LuaJIT. +If you install LuaRocks for use with SILE via `pacman`, use the `lua51-*` variants to match LuaJIT. #### Fedora A [COPR][copr] repository is available for Fedora users with packages of SILE -and all the necessary dependencies including fonts. -Fedora 36 and Fedora 37 are supported. +and all the necessary dependencies. +Fedora 38 and later are supported. There is work in progress to get the packages added to the official Fedora repository. ```console @@ -197,7 +196,7 @@ If you try to `brew link` and you get a series of messages including something l export PKG_CONFIG_PATH="/usr/local/opt/icu4c/lib/pkgconfig" Optionally you may install the Lua libraries listed in the [rockspec][] to your system (using either your system's package manager or [luarocks][] (`luarocks install --only-deps sile-dev-1.rockspec`). -By default all the required Lua libraries will be downloaded and bundled alongside the SILE the instalation. +By default all the required Lua libraries will be downloaded and bundled alongside the SILE the installation. If you downloaded a source tarball these dependencies are included, if you are using a git clone of the source repository the build system will require `luarocks` to fetch them during build. Note that OpenSSL development headers will be required for one of the Lua modules to compile¹. If your system has all the required packages already you may add `--with-system-luarocks` to the `./configure` command to avoid bundling them. @@ -330,8 +329,6 @@ There's also an [FAQ][faq] available. Please report bugs and send patches and pull requests at the [github repository][github]. For questions and discussion, please join the [mailing list][list-en]. -日本語利用者は[メーリングリスト][list-ja]に参加してください。 - ## License Terms SILE is distributed under the [MIT licence][license]. @@ -353,15 +350,12 @@ SILE is distributed under the [MIT licence][license]. [libtexpdf]: https://github.com/sile-typesetter/libtexpdf [arch-sile]: https://archlinux.org/packages/community/x86_64/sile/ [aur]: https://wiki.archlinux.org/index.php/Arch_User_Repository - [aur-sile-luajit]: https://aur.archlinux.org/packages/sile-luajit/ - [aur-sile-git]: https://aur.archlinux.org/packages/sile-git/ [typesetting]: https://en.wikipedia.org/wiki/Typesetting [tex]: https://en.wikipedia.org/wiki/TeX [indesign]: https://en.wikipedia.org/wiki/Adobe_InDesign [brew]: http://brew.sh [brewfonts]: https://github.com/Homebrew/homebrew-cask-fonts [list-en]: https://groups.google.com/d/forum/sile-users - [list-ja]: https://groups.google.com/d/forum/sile-users-ja [nix]: https://nixos.org/nix [nix-flakes]: https://nixos.wiki/wiki/Flakes#Installing_flakes [ports]: http://ports.su/print/sile diff --git a/action.yml b/action.yml index f6f5c4ef4..6b80884ca 100644 --- a/action.yml +++ b/action.yml @@ -7,7 +7,7 @@ inputs: default: "" runs: using: docker - image: docker://ghcr.io/sile-typesetter/sile:v0.14.9 + image: docker://ghcr.io/sile-typesetter/sile:v0.14.13 entrypoint: sh args: - -c diff --git a/bootstrap.sh b/bootstrap.sh index 3e2165b3e..07eaf4c6a 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -35,6 +35,13 @@ else ./build-aux/git-version-gen .tarball-version > .version fi +# Autoreconf uses a perl script to inline includes from Makefile.am into +# Makefile.in before ./configure is even run ... which is where we're going to +# use AC_SUBST to setup project specific build options. We need to pre-seed +# a file to avoid a file not found error on first run. The configure process +# will rebuild this and also re-include it into the final Makefile. +touch build-aux/rust_boilerplate.mk + autoreconf --install # See discussion in https://github.com/sile-typesetter/sile/issues/82 and diff --git a/build-aux/adl_recursive_eval.m4 b/build-aux/adl_recursive_eval.m4 index 71f2aeb24..4f9a17a54 100644 --- a/build-aux/adl_recursive_eval.m4 +++ b/build-aux/adl_recursive_eval.m4 @@ -3,13 +3,16 @@ dnl ================================= dnl Interpolate the VALUE in loop until it doesn't change, dnl and set the result to $RESULT. dnl WARNING: It's easy to get an infinite loop with some unsane input. -AC_DEFUN([adl_RECURSIVE_EVAL], -[_lcl_receval="$1" -$2=`(test "x$prefix" = xNONE && prefix="$ac_default_prefix" - test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" - _lcl_receval_old='' - while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do - _lcl_receval_old="[$]_lcl_receval" - eval _lcl_receval="\"[$]_lcl_receval\"" - done - echo "[$]_lcl_receval")`]) \ No newline at end of file +AC_DEFUN([adl_RECURSIVE_EVAL], [ + _lcl_receval="$1" + $2=`( + test "x$prefix" = xNONE && prefix="$ac_default_prefix" + test "x$exec_prefix" = xNONE && exec_prefix="${prefix}" + _lcl_receval_old='' + while test "[$]_lcl_receval_old" != "[$]_lcl_receval"; do + _lcl_receval_old="[$]_lcl_receval" + eval _lcl_receval="\"[$]_lcl_receval\"" + done + echo "[$]_lcl_receval" + )` +]) diff --git a/build-aux/ax_build_date_epoch.m4 b/build-aux/ax_build_date_epoch.m4 index dbecb067a..81ac87c7b 100644 --- a/build-aux/ax_build_date_epoch.m4 +++ b/build-aux/ax_build_date_epoch.m4 @@ -53,18 +53,15 @@ #serial 2 -AC_DEFUN([AX_BUILD_DATE_EPOCH], -[dnl -AC_MSG_CHECKING([for build time]) -ax_date_fmt="m4_default($2,%s)" -AS_IF([test x"$SOURCE_DATE_EPOCH" = x], - [$1=`date "+$ax_date_fmt"`], - [ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \ - || date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null` - AS_IF([test x"$ax_build_date" = x], - [m4_ifval([$3], - [$3], - [AC_MSG_ERROR([malformed SOURCE_DATE_EPOCH])])], - [$1=$ax_build_date])]) -AC_MSG_RESULT([$$1]) +AC_DEFUN([AX_BUILD_DATE_EPOCH], [dnl + AC_MSG_CHECKING([for build time]) + ax_date_fmt="m4_default($2,%s)" + AS_IF([test x"$SOURCE_DATE_EPOCH" = x], + [$1=`date "+$ax_date_fmt"`], + [ax_build_date=`date -u -d "@$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null \ + || date -u -r "$SOURCE_DATE_EPOCH" "+$ax_date_fmt" 2>/dev/null` + AS_IF([test x"$ax_build_date" = x], + [m4_ifval([$3], [$3], [AC_MSG_ERROR([malformed SOURCE_DATE_EPOCH])])], + [$1=$ax_build_date])]) + AC_MSG_RESULT([$$1]) ])dnl AX_BUILD_DATE_EPOCH diff --git a/build-aux/ax_font.m4 b/build-aux/ax_font.m4 index 93b319c5c..c955106ba 100644 --- a/build-aux/ax_font.m4 +++ b/build-aux/ax_font.m4 @@ -1,21 +1,21 @@ AC_DEFUN([AX_FONT], [ - AC_PROG_GREP - if test -z "$FCMATCH"; then - AC_PATH_PROG(FCMATCH, fc-match) + AC_PROG_GREP if test -z "$FCMATCH"; then - AC_MSG_ERROR([can't find fc-match]) + AC_PATH_PROG(FCMATCH, fc-match) + if test -z "$FCMATCH"; then + AC_MSG_ERROR([can't find fc-match]) + fi fi - fi - pushdef([FONT],$1) - AC_MSG_CHECKING(whether font family FONT is available) - AS_IF([test "$FCMATCH" = "true"],[ - AC_MSG_RESULT(skip) - ],[ - AS_IF([$FCMATCH "FONT" family | $GREP -qx "FONT"],[ - AC_MSG_RESULT(yes) + pushdef([FONT],$1) + AC_MSG_CHECKING(whether font family FONT is available) + AS_IF([test "$FCMATCH" = "true"],[ + AC_MSG_RESULT(skip) ],[ - AC_MSG_FAILURE([font family FONT not found]) + AS_IF([$FCMATCH "FONT" family | $GREP -qx "FONT"],[ + AC_MSG_RESULT(yes) + ],[ + AC_MSG_FAILURE([font family FONT not found]) + ]) ]) - ]) - popdef([FONT]) + popdef([FONT]) ])dnl diff --git a/build-aux/ax_git_version.m4 b/build-aux/ax_git_version.m4 new file mode 100644 index 000000000..b59807835 --- /dev/null +++ b/build-aux/ax_git_version.m4 @@ -0,0 +1,9 @@ +AC_DEFUN([AX_GIT_VERSION], [ + + AC_PROG_AWK + AC_PROG_GREP + AX_PROGVAR([cmp]) + + AX_TRANSFORM_PACKAGE_NAME + +]) diff --git a/build-aux/ax_lua.m4 b/build-aux/ax_lua.m4 index e9b952257..12ce133e6 100644 --- a/build-aux/ax_lua.m4 +++ b/build-aux/ax_lua.m4 @@ -1,30 +1,230 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_lua.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_PROG_LUA[([MINIMUM-VERSION], [TOO-BIG-VERSION], [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_HEADERS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_LIBS[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# AX_LUA_READLINE[([ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])] +# +# DESCRIPTION +# +# Detect a Lua interpreter, optionally specifying a minimum and maximum +# version number. Set up important Lua paths, such as the directories in +# which to install scripts and modules (shared libraries). +# +# Also detect Lua headers and libraries. The Lua version contained in the +# header is checked to match the Lua interpreter version exactly. When +# searching for Lua libraries, the version number is used as a suffix. +# This is done with the goal of supporting multiple Lua installs (5.1, +# 5.2, 5.3, and 5.4 side-by-side). +# +# A note on compatibility with previous versions: This file has been +# mostly rewritten for serial 18. Most developers should be able to use +# these macros without needing to modify configure.ac. Care has been taken +# to preserve each macro's behavior, but there are some differences: +# +# 1) AX_WITH_LUA is deprecated; it now expands to the exact same thing as +# AX_PROG_LUA with no arguments. +# +# 2) AX_LUA_HEADERS now checks that the version number defined in lua.h +# matches the interpreter version. AX_LUA_HEADERS_VERSION is therefore +# unnecessary, so it is deprecated and does not expand to anything. +# +# 3) The configure flag --with-lua-suffix no longer exists; the user +# should instead specify the LUA precious variable on the command line. +# See the AX_PROG_LUA description for details. +# +# Please read the macro descriptions below for more information. +# +# This file was inspired by Andrew Dalke's and James Henstridge's +# python.m4 and Tom Payne's, Matthieu Moy's, and Reuben Thomas's ax_lua.m4 +# (serial 17). Basically, this file is a mash-up of those two files. I +# like to think it combines the best of the two! +# +# AX_PROG_LUA: Search for the Lua interpreter, and set up important Lua +# paths. Adds precious variable LUA, which may contain the path of the Lua +# interpreter. If LUA is blank, the user's path is searched for an +# suitable interpreter. +# +# Optionally a LUAJIT option may be set ahead of time to look for and +# validate a LuaJIT install instead of PUC Lua. Usage might look like: +# +# AC_ARG_WITH(luajit, [AS_HELP_STRING([--with-luajit], +# [Prefer LuaJIT over PUC Lua, even if the latter is newer. Default: no]) +# ]) +# AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != 'xno']) +# +# If MINIMUM-VERSION is supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION will be accepted. If +# TOO-BIG-VERSION is also supplied, then only Lua interpreters with a +# version number greater or equal to MINIMUM-VERSION and less than +# TOO-BIG-VERSION will be accepted. +# +# The Lua version number, LUA_VERSION, is found from the interpreter, and +# substituted. LUA_PLATFORM is also found, but not currently supported (no +# standard representation). +# +# Finally, the macro finds four paths: +# +# luadir Directory to install Lua scripts. +# pkgluadir $luadir/$PACKAGE +# luaexecdir Directory to install Lua modules. +# pkgluaexecdir $luaexecdir/$PACKAGE +# +# These paths are found based on $prefix, $exec_prefix, Lua's +# package.path, and package.cpath. The first path of package.path +# beginning with $prefix is selected as luadir. The first path of +# package.cpath beginning with $exec_prefix is used as luaexecdir. This +# should work on all reasonable Lua installations. If a path cannot be +# determined, a default path is used. Of course, the user can override +# these later when invoking make. +# +# luadir Default: $prefix/share/lua/$LUA_VERSION +# luaexecdir Default: $exec_prefix/lib/lua/$LUA_VERSION +# +# These directories can be used by Automake as install destinations. The +# variable name minus 'dir' needs to be used as a prefix to the +# appropriate Automake primary, e.g. lua_SCRIPS or luaexec_LIBRARIES. +# +# If an acceptable Lua interpreter is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is preformed. If ACTION-IF-NOT- +# FOUND is blank, then it will default to printing an error. To prevent +# the default behavior, give ':' as an action. +# +# AX_LUA_HEADERS: Search for Lua headers. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_INCLUDE, which +# may contain Lua specific include flags, e.g. -I/usr/include/lua5.1. If +# LUA_INCLUDE is blank, then this macro will attempt to find suitable +# flags. +# +# LUA_INCLUDE can be used by Automake to compile Lua modules or +# executables with embedded interpreters. The *_CPPFLAGS variables should +# be used for this purpose, e.g. myprog_CPPFLAGS = $(LUA_INCLUDE). +# +# This macro searches for the header lua.h (and others). The search is +# performed with a combination of CPPFLAGS, CPATH, etc, and LUA_INCLUDE. +# If the search is unsuccessful, then some common directories are tried. +# If the headers are then found, then LUA_INCLUDE is set accordingly. +# +# The paths automatically searched are: +# +# * /usr/include/luaX.Y +# * /usr/include/lua/X.Y +# * /usr/include/luaXY +# * /usr/local/include/luaX.Y +# * /usr/local/include/lua-X.Y +# * /usr/local/include/lua/X.Y +# * /usr/local/include/luaXY +# +# (Where X.Y is the Lua version number, e.g. 5.1.) +# +# The Lua version number found in the headers is always checked to match +# the Lua interpreter's version number. Lua headers with mismatched +# version numbers are not accepted. +# +# If headers are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_LIBS: Search for Lua libraries. Requires that AX_PROG_LUA be +# expanded before this macro. Adds precious variable LUA_LIB, which may +# contain Lua specific linker flags, e.g. -llua5.1. If LUA_LIB is blank, +# then this macro will attempt to find suitable flags. +# +# LUA_LIB can be used by Automake to link Lua modules or executables with +# embedded interpreters. The *_LIBADD and *_LDADD variables should be used +# for this purpose, e.g. mymod_LIBADD = $(LUA_LIB). +# +# This macro searches for the Lua library. More technically, it searches +# for a library containing the function lua_load. The search is performed +# with a combination of LIBS, LIBRARY_PATH, and LUA_LIB. +# +# If the search determines that some linker flags are missing, then those +# flags will be added to LUA_LIB. +# +# If libraries are found, then ACTION-IF-FOUND is performed, otherwise +# ACTION-IF-NOT-FOUND is performed. If ACTION-IF-NOT-FOUND is blank, then +# it will default to printing an error. To prevent the default behavior, +# set the action to ':'. +# +# AX_LUA_READLINE: Search for readline headers and libraries. Requires the +# AX_LIB_READLINE macro, which is provided by ax_lib_readline.m4 from the +# Autoconf Archive. +# +# If a readline compatible library is found, then ACTION-IF-FOUND is +# performed, otherwise ACTION-IF-NOT-FOUND is performed. +# +# LICENSE +# +# Copyright (c) 2023 Caleb Maclennan +# Copyright (c) 2015 Reuben Thomas +# Copyright (c) 2014 Tim Perkins +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 45 + dnl ========================================================================= dnl AX_PROG_LUA([MINIMUM-VERSION], [TOO-BIG-VERSION], dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) dnl ========================================================================= AC_DEFUN([AX_PROG_LUA], [ + dnl Check for required tools. + AC_REQUIRE([AC_PROG_GREP]) + AC_REQUIRE([AC_PROG_SED]) + dnl Make LUA a precious variable. AC_ARG_VAR([LUA], [The Lua interpreter, e.g. /usr/bin/lua5.1]) dnl Find a Lua interpreter. AM_COND_IF([LUAJIT], - [_ax_lua_interpreter_list="luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3"], - [_ax_lua_interpreter_list="lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50"]) + [_ax_lua_interpreter_list='luajit luajit-2.1.0-beta3 luajit-2.0.5 luajit-2.0.4 luajit-2.0.3'], + [_ax_lua_interpreter_list='lua lua5.4 lua54 lua5.3 lua53 lua5.2 lua52 lua5.1 lua51 lua5.0 lua50']) m4_if([$1], [], [ dnl No version check is needed. Find any Lua interpreter. AS_IF([test "x$LUA" = 'x'], - [AC_PATH_PROGS([LUA], [_ax_lua_interpreter_list], [:])]) + [AC_PATH_PROGS([LUA], [$_ax_lua_interpreter_list], [:])]) ax_display_LUA='lua' - dnl At least check if this is a Lua interpreter. + AS_IF([test "x$LUA" != 'x:'], + [ dnl At least check if this is a Lua interpreter. AC_MSG_CHECKING([if $LUA is a Lua interpreter]) _AX_LUA_CHK_IS_INTRP([$LUA], [AC_MSG_RESULT([yes])], [ AC_MSG_RESULT([no]) AC_MSG_ERROR([not a Lua interpreter]) ]) + ]) ], [ dnl A version check is needed. AS_IF([test "x$LUA" != 'x'], @@ -71,27 +271,38 @@ AC_DEFUN([AX_PROG_LUA], m4_default([$4], [AC_MSG_ERROR([cannot find suitable Lua interpreter])]) ], [ dnl Query Lua for its version number. - AC_CACHE_CHECK([for $ax_display_LUA version], [ax_cv_lua_version], - [ ax_cv_lua_version=`$LUA -e 'print(_VERSION:match "(%d+%.%d+)")'` ]) - AS_IF([test "x$ax_cv_lua_version" = 'x'], - [AC_MSG_ERROR([invalid Lua version number])]) - AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) - AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) - - AM_COND_IF([LUAJIT], [ - AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version], - [ ax_cv_luajit_version=`$LUA -e 'print(jit and jit.version:match "(%d+%..+)")'` ]) - AS_IF([test "x$ax_cv_luajit_version" = 'x'], - [AC_MSG_ERROR([invalid Lua version number])]) - AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version]) - AC_SUBST([LUAJIT_SHORT_VERSION], [`echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|'`]) - ]) + AC_CACHE_CHECK([for $ax_display_LUA version], + [ax_cv_lua_version], + [ dnl Get the interpreter version in X.Y format. This should work for + dnl interpreters version 5.0 and beyond. + ax_cv_lua_version=[`$LUA -e ' + -- return a version number in X.Y format + local _, _, ver = string.find(_VERSION, "^Lua (%d+%.%d+)") + print(ver or "")'`] + ]) + AS_IF([test "x$ax_cv_lua_version" = 'x'], + [AC_MSG_ERROR([invalid Lua version number])]) + AC_SUBST([LUA_VERSION], [$ax_cv_lua_version]) + AC_SUBST([LUA_SHORT_VERSION], [`echo "$LUA_VERSION" | $SED 's|\.||'`]) + + AM_COND_IF([LUAJIT], [ + AC_CACHE_CHECK([for $ax_display_LUA jit version], [ax_cv_luajit_version], + [ ax_cv_luajit_version=[`$LUA -e ' + local _, _, ver = string.find(jit and jit.version, "(%d+%..+)") + print(ver or "")'`] + ]) + AS_IF([test "x$ax_cv_luajit_version" = 'x'], + [AC_MSG_ERROR([invalid Lua jit version number])]) + AC_SUBST([LUAJIT_VERSION], [$ax_cv_luajit_version]) + AC_SUBST([LUAJIT_SHORT_VERSION], [$(echo "$LUAJIT_VERSION" | $SED 's|\.|§|;s|\..*||;s|§|.|')]) + ]) dnl The following check is not supported: dnl At times (like when building shared libraries) you may want to know dnl which OS platform Lua thinks this is. - AC_CACHE_CHECK([for $ax_display_LUA platform], [ax_cv_lua_platform], - [ax_cv_lua_platform=`$LUA -e "print('unknown')"`]) + AC_CACHE_CHECK([for $ax_display_LUA platform], + [ax_cv_lua_platform], + [ax_cv_lua_platform=[`$LUA -e 'print("unknown")'`]]) AC_SUBST([LUA_PLATFORM], [$ax_cv_lua_platform]) dnl Use the values of $prefix and $exec_prefix for the corresponding @@ -116,12 +327,12 @@ AC_DEFUN([AX_PROG_LUA], ax_cv_lua_luadir="$LUA_PREFIX/share/lua/$LUA_VERSION" dnl Try to find a path with the prefix. - _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [package.path]) + _AX_LUA_FND_PRFX_PTH([$LUA], [$ax_lua_prefix], [script]) AS_IF([test "x$ax_lua_prefixed_path" != 'x'], [ dnl Fix the prefix. _ax_strip_prefix=`echo "$ax_lua_prefix" | $SED 's|.|.|g'` ax_cv_lua_luadir=`echo "$ax_lua_prefixed_path" | \ - $SED "s,^$_ax_strip_prefix,$LUA_PREFIX,"` + $SED "s|^$_ax_strip_prefix|$LUA_PREFIX|"` ]) ]) AC_SUBST([luadir], [$ax_cv_lua_luadir]) @@ -143,12 +354,12 @@ AC_DEFUN([AX_PROG_LUA], dnl Try to find a path with the prefix. _AX_LUA_FND_PRFX_PTH([$LUA], - [$ax_lua_exec_prefix], [package.cpath]) + [$ax_lua_exec_prefix], [module]) AS_IF([test "x$ax_lua_prefixed_path" != 'x'], [ dnl Fix the prefix. _ax_strip_prefix=`echo "$ax_lua_exec_prefix" | $SED 's|.|.|g'` ax_cv_lua_luaexecdir=`echo "$ax_lua_prefixed_path" | \ - $SED "s,^$_ax_strip_prefix,$LUA_EXEC_PREFIX,"` + $SED "s|^$_ax_strip_prefix|$LUA_EXEC_PREFIX|"` ]) ]) AC_SUBST([luaexecdir], [$ax_cv_lua_luaexecdir]) @@ -162,7 +373,7 @@ AC_DEFUN([AX_PROG_LUA], dnl AX_WITH_LUA is now the same thing as AX_PROG_LUA. AC_DEFUN([AX_WITH_LUA], [ - AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA]]) + AC_MSG_WARN([[$0 is deprecated, please use AX_PROG_LUA instead]]) AX_PROG_LUA ]) @@ -172,8 +383,19 @@ dnl _AX_LUA_CHK_IS_INTRP(PROG, [ACTION-IF-TRUE], [ACTION-IF-FALSE]) dnl ========================================================================= AC_DEFUN([_AX_LUA_CHK_IS_INTRP], [ - dnl Just print _VERSION because all Lua interpreters have this global. - AS_IF([$1 -e "print('Hello ' .. _VERSION .. '!')"], + dnl A minimal Lua factorial to prove this is an interpreter. This should work + dnl for Lua interpreters version 5.0 and beyond. + _ax_lua_factorial=[`$1 2>/dev/null -e ' + -- a simple factorial + function fact (n) + if n == 0 then + return 1 + else + return n * fact(n-1) + end + end + print("fact(5) is " .. fact(5))'`] + AS_IF([test "$_ax_lua_factorial" = 'fact(5) is 120'], [$2], [$3]) ]) @@ -184,47 +406,81 @@ dnl [ACTION-IF-TRUE], [ACTION-IF-FALSE]) dnl ========================================================================= AC_DEFUN([_AX_LUA_CHK_VER], [ - AS_IF([$1 2>/dev/null -e ' - function norm (v) - i,j=v:match "(%d+)%.(%d+)" if i then return 100 * i + j end - end - v, toobig=norm (_VERSION), norm "$3" or math.huge - os.exit ((v >= norm ("$2") and v < toobig) and 0 or 1)'], - [$4], [$5]) + dnl Check that the Lua version is within the bounds. Only the major and minor + dnl version numbers are considered. This should work for Lua interpreters + dnl version 5.0 and beyond. + _ax_lua_good_version=[`$1 -e ' + -- a script to compare versions + function verstr2num(verstr) + local _, _, majorver, minorver = string.find(verstr, "^(%d+)%.(%d+)") + if majorver and minorver then + return tonumber(majorver) * 100 + tonumber(minorver) + end + end + local minver = verstr2num("$2") + local _, _, trimver = string.find(_VERSION, "^Lua (.*)") + local ver = verstr2num(trimver) + local maxver = verstr2num("$3") or 1e9 + if minver <= ver and ver < maxver then + print("yes") + else + print("no") + end'`] + AS_IF([test "x$_ax_lua_good_version" = "xyes"], + [$4], [$5]) ]) AC_DEFUN([_AX_LUAJIT_CHK_VER], [ AS_IF([$1 2>/dev/null -e ' - function norm (v) - i,j=v:match "(%d+)%.(%d+)" if i then return 100 * i + j end - end - v, toobig=norm (jit.version), norm "$3" or math.huge - os.exit ((v >= norm ("$2") and v < toobig) and 0 or 1)'], - [$4], [$5]) + function norm (v) + i,j=v:match "(%d+)%.(%d+)" if i then return 100 * i + j end + end + v, toobig=norm (jit.version), norm "$3" or math.huge + os.exit ((v >= norm ("$2") and v < toobig) and 0 or 1)'], + [$4], [$5]) ]) dnl ========================================================================= -dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, LUA-PATH-VARIABLE) +dnl _AX_LUA_FND_PRFX_PTH(PROG, PREFIX, SCRIPT-OR-MODULE-DIR) dnl ========================================================================= AC_DEFUN([_AX_LUA_FND_PRFX_PTH], [ - dnl Invokes the Lua interpreter PROG to print the path variable - dnl LUA-PATH-VARIABLE, usually package.path or package.cpath. Paths are - dnl then matched against PREFIX. Then ax_lua_prefixed_path is set to the - dnl shortest sub path beginning with PREFIX up to the last directory - dnl that does not contain a '?', if any. - - ax_lua_prefixed_path=`$1 2>/dev/null -e ' - $3:gsub ("(@<:@^;@:>@+)", - function (p) - p = p:gsub ("%?.*$", ""):gsub ("/@<:@^/@:>@*$", "") - if p:match ("^$2") and (not shortest or #shortest > #p) then - shortest = p + dnl Get the script or module directory by querying the Lua interpreter, + dnl filtering on the given prefix, and selecting the shallowest path. If no + dnl path is found matching the prefix, the result will be an empty string. + dnl The third argument determines the type of search, it can be 'script' or + dnl 'module'. Supplying 'script' will perform the search with package.path + dnl and LUA_PATH, and supplying 'module' will search with package.cpath and + dnl LUA_CPATH. This is done for compatibility with Lua 5.0. + + ax_lua_prefixed_path=[`$1 -e ' + -- get the path based on search type + local searchtype = "$3" + local paths = "" + if searchtype == "script" then + paths = (package and package.path) or LUA_PATH + elseif searchtype == "module" then + paths = (package and package.cpath) or LUA_CPATH + end + -- search for the prefix + local prefix = "'$2'" + local minpath = "" + local mindepth = 1e9 + string.gsub(paths, "(@<:@^;@:>@+)", + function (path) + path = string.gsub(path, "%?.*$", "") + path = string.gsub(path, "/@<:@^/@:>@*$", "") + if string.find(path, prefix) then + local depth = string.len(string.gsub(path, "@<:@^/@:>@", "")) + if depth < mindepth then + minpath = path + mindepth = depth + end end end) - print (shortest or "")'` + print(minpath)'`] ]) @@ -241,13 +497,35 @@ AC_DEFUN([AX_LUA_HEADERS], AC_MSG_ERROR([cannot check Lua headers without knowing LUA_VERSION]) ]) + AM_COND_IF([LUAJIT],[ + dnl Check for LUAJIT_VERSION. + AC_MSG_CHECKING([if LUAJIT_VERSION is defined]) + AS_IF([test "x$LUAJIT_VERSION" != 'x'], + [AC_MSG_RESULT([yes])], + [ AC_MSG_RESULT([no]) + AC_MSG_ERROR([cannot check Lua jit headers without knowing LUAJIT_VERSION]) + ]) + ]) + dnl Make LUA_INCLUDE a precious variable. AC_ARG_VAR([LUA_INCLUDE], [The Lua includes, e.g. -I/usr/include/lua5.1]) dnl Some default directories to search. AM_COND_IF([LUAJIT], - [_ax_lua_include_list="/usr/include/luajit-$LUAJIT_VERSION /usr/include/luajit-$LUAJIT_SHORT_VERSION /usr/local/include/luajit-$LUAJIT_VERSION /usr/local/include/luajit-$LUAJIT_SHORT_VERSION"], - [_ax_lua_include_list="/usr/include/lua$LUA_VERSION /usr/include/lua/$LUA_VERSION /usr/include/lua$LUA_SHORT_VERSION /usr/local/include/lua$LUA_VERSION /usr/local/include/lua-$LUA_VERSION /usr/local/include/lua/$LUA_VERSION /usr/local/include/lua$LUA_SHORT_VERSION"]) + [_ax_lua_include_list=" + /usr/include/luajit-$LUAJIT_VERSION + /usr/include/luajit-$LUAJIT_SHORT_VERSION + /usr/local/include/luajit-$LUAJIT_VERSION + /usr/local/include/luajit-$LUAJIT_SHORT_VERSION"], + [_ax_lua_include_list=" + /usr/include/lua$LUA_VERSION + /usr/include/lua-$LUA_VERSION + /usr/include/lua/$LUA_VERSION + /usr/include/lua$LUA_SHORT_VERSION + /usr/local/include/lua$LUA_VERSION + /usr/local/include/lua-$LUA_VERSION + /usr/local/include/lua/$LUA_VERSION + /usr/local/include/lua$LUA_SHORT_VERSION"]) dnl Try to find the headers. _ax_lua_saved_cppflags=$CPPFLAGS @@ -258,26 +536,26 @@ AC_DEFUN([AX_LUA_HEADERS], dnl Try some other directories if LUA_INCLUDE was not set. AS_IF([test "x$LUA_INCLUDE" = 'x' && - test "x$ac_cv_header_lua_h" != "xyes" || - test "x$with_luajit" = "xyes" && - test "x$ac_cv_header_luajit_h" != 'xyes'], - [ dnl Try some common include paths. - for _ax_include_path in $_ax_lua_include_list; do - test ! -d "$_ax_include_path" && continue + test "x$ac_cv_header_lua_h" != 'xyes' || + test "x$with_luajit" != 'xno' && + test "x$ac_cv_header_luajit_h" != 'xyes'], + [ dnl Try some common include paths. + for _ax_include_path in $_ax_lua_include_list; do + test ! -d "$_ax_include_path" && continue AC_MSG_CHECKING([for Lua headers in]) AC_MSG_RESULT([$_ax_include_path]) AS_UNSET([ac_cv_header_lua_h]) - AS_UNSET([ac_cv_header_luajit_h]) AS_UNSET([ac_cv_header_lualib_h]) AS_UNSET([ac_cv_header_lauxlib_h]) AS_UNSET([ac_cv_header_luaconf_h]) + AS_UNSET([ac_cv_header_luajit_h]) _ax_lua_saved_cppflags=$CPPFLAGS CPPFLAGS="$CPPFLAGS -I$_ax_include_path" AC_CHECK_HEADERS([lua.h lualib.h lauxlib.h luaconf.h]) - AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])]) + AM_COND_IF([LUAJIT], [AC_CHECK_HEADERS([luajit.h])]) CPPFLAGS=$_ax_lua_saved_cppflags AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], @@ -287,46 +565,44 @@ AC_DEFUN([AX_LUA_HEADERS], done ]) - AS_IF([test "x$ac_cv_header_lua_h" = 'xyes' && test "x$cross_compiling" != 'xyes'], + AS_IF([test "x$ac_cv_header_lua_h" = 'xyes'], [ dnl Make a program to print LUA_VERSION defined in the header. - dnl TODO This probably shouldn't be a runtime test. - - AC_CACHE_CHECK([for Lua header version], - [ax_cv_lua_header_version], - [ _ax_lua_saved_cppflags=$CPPFLAGS - CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" - AC_RUN_IFELSE( - [ AC_LANG_SOURCE([[ + dnl TODO It would be really nice if we could do this without compiling a + dnl program, then it would work when cross compiling. But I'm not sure how + dnl to do this reliably. For now, assume versions match when cross compiling. + + AS_IF([test "x$cross_compiling" != 'xyes'], + [ AC_CACHE_CHECK([for Lua header version], + [ax_cv_lua_header_version], + [ _ax_lua_saved_cppflags=$CPPFLAGS + CPPFLAGS="$CPPFLAGS $LUA_INCLUDE" + AC_COMPUTE_INT(ax_cv_lua_header_version_major,[LUA_VERSION_NUM/100],[AC_INCLUDES_DEFAULT #include -#include -#include -int main(int argc, char ** argv) -{ - if(argc > 1) printf("%s", LUA_VERSION); - exit(EXIT_SUCCESS); -} -]]) - ], - [ ax_cv_lua_header_version=`./conftest$EXEEXT p | \ - $SED "s|^Lua \(.*\)|\1|" | \ - $GREP -E -o "^@<:@0-9@:>@+\.@<:@0-9@:>@+"` +],[ax_cv_lua_header_version_major=unknown]) + AC_COMPUTE_INT(ax_cv_lua_header_version_minor,[LUA_VERSION_NUM%100],[AC_INCLUDES_DEFAULT +#include +],[ax_cv_lua_header_version_minor=unknown]) + AS_IF([test "x$ax_cv_lua_header_version_major" = xunknown || test "x$ax_cv_lua_header_version_minor" = xunknown],[ + ax_cv_lua_header_version=unknown + ],[ + ax_cv_lua_header_version="$ax_cv_lua_header_version_major.$ax_cv_lua_header_version_minor" + ]) + CPPFLAGS=$_ax_lua_saved_cppflags + ]) + + dnl Compare this to the previously found LUA_VERSION. + AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) + AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], + [ AC_MSG_RESULT([yes]) + ax_header_version_match='yes' ], - [ax_cv_lua_header_version='unknown']) - CPPFLAGS=$_ax_lua_saved_cppflags - ]) - - dnl Compare this to the previously found LUA_VERSION. - AC_MSG_CHECKING([if Lua header version matches $LUA_VERSION]) - AS_IF([test "x$ax_cv_lua_header_version" = "x$LUA_VERSION"], - [ AC_MSG_RESULT([yes]) - ax_header_version_match='yes' + [ AC_MSG_RESULT([no]) + ax_header_version_match='no' + ]) ], - [ AC_MSG_RESULT([no]) - ax_header_version_match='no' + [ AC_MSG_WARN([cross compiling so assuming header version number matches]) + ax_header_version_match='yes' ]) - ], - [ - ax_header_version_match='yes' ]) dnl Was LUA_INCLUDE specified? @@ -342,7 +618,7 @@ int main(int argc, char ** argv) dnl AX_LUA_HEADERS_VERSION no longer exists, use AX_LUA_HEADERS. AC_DEFUN([AX_LUA_HEADERS_VERSION], [ - AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS]]) + AC_MSG_WARN([[$0 is deprecated, please use AX_LUA_HEADERS instead]]) ]) @@ -397,18 +673,27 @@ AC_DEFUN([AX_LUA_LIBS], dnl Try to find the Lua libs. _ax_lua_saved_libs=$LIBS LIBS="$LIBS $LUA_LIB" - AM_COND_IF([LUAJIT], - [AC_SEARCH_LIBS([lua_load], - [luajit$LUA_VERSION luajit$LUA_SHORT_VERSION luajit-$LUA_VERSION luajit-$LUA_SHORT_VERSION luajit], - [_ax_found_lua_libs='yes'], - [_ax_found_lua_libs='no'], - [$_ax_lua_extra_libs])], - [AC_SEARCH_LIBS([lua_load], - [lua$LUA_VERSION lua$LUA_SHORT_VERSION lua-$LUA_VERSION lua-$LUA_SHORT_VERSION lua], - [_ax_found_lua_libs='yes'], - [_ax_found_lua_libs='no'], - [$_ax_lua_extra_libs])]) - LIBS=$_ax_lua_saved_libs + AM_COND_IF([LUAJIT], + [AC_SEARCH_LIBS([lua_load], + [ luajit$LUA_VERSION \ + luajit$LUA_SHORT_VERSION \ + luajit-$LUA_VERSION \ + luajit-$LUA_SHORT_VERSION \ + luajit], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs])], + [AC_SEARCH_LIBS([lua_load], + [ lua$LUA_VERSION \ + lua$LUA_SHORT_VERSION \ + lua-$LUA_VERSION \ + lua-$LUA_SHORT_VERSION \ + lua \ + ], + [_ax_found_lua_libs='yes'], + [_ax_found_lua_libs='no'], + [$_ax_lua_extra_libs])]) + LIBS=$_ax_lua_saved_libs AS_IF([test "x$ac_cv_search_lua_load" != 'xno' && test "x$ac_cv_search_lua_load" != 'xnone required'], diff --git a/build-aux/ax_progvar.m4 b/build-aux/ax_progvar.m4 index 131c80617..e584ed4aa 100644 --- a/build-aux/ax_progvar.m4 +++ b/build-aux/ax_progvar.m4 @@ -1,5 +1,5 @@ AC_DEFUN([AX_PROGVAR], [ - test -n "$m4_toupper($1)" || { AC_PATH_PROG(m4_toupper($1), m4_default($2,$1)) } - test -n "$m4_toupper($1)" || AC_MSG_ERROR([m4_default($2,$1) is required]) - ]) + test -n "$m4_toupper($1)" || { AC_PATH_PROG(m4_toupper($1), m4_default($2,$1)) } + test -n "$m4_toupper($1)" || AC_MSG_ERROR([m4_default($2,$1) is required]) +]) diff --git a/build-aux/ax_rust_boilerplate.m4 b/build-aux/ax_rust_boilerplate.m4 new file mode 100644 index 000000000..12ccecde4 --- /dev/null +++ b/build-aux/ax_rust_boilerplate.m4 @@ -0,0 +1,47 @@ +AC_DEFUN_ONCE([AX_RUST_BOILERPLATE], [ + + AX_TRANSFORM_PACKAGE_NAME + AX_SHELL_COMPLETION_DIRS + + AC_ARG_ENABLE(debug, + AS_HELP_STRING([--enable-debug], + [Build Rust code with debugging information])) + AM_CONDITIONAL([DEBUG_RELEASE], [test "x$debug_release" = "xyes"]) + + AC_ARG_ENABLE([dependency-checks], + AS_HELP_STRING([--disable-dependency-checks], + [Disable build tooling dependency checks])) + AM_CONDITIONAL([DEPENDENCY_CHECKS], [test "x$enable_dependency_checks" != "xno"]) + + AC_ARG_ENABLE([developer], + AS_HELP_STRING([--enable-developer], + [Check for and enable tooling required only for developers])) + AM_CONDITIONAL([DEVELOPER], [test "x$enable_developer" = "xyes"]) + + AC_MSG_NOTICE([checking for tools used by automake to build Rust projects]) + AC_PROG_INSTALL + AX_PROGVAR([cargo]) + AX_PROGVAR([jq]) + AX_PROGVAR([rustc]) + AX_PROGVAR([cmp]) + AX_PROGVAR([xargs]) + AM_COND_IF([DEPENDENCY_CHECKS], [ + AM_COND_IF([DEVELOPER], [ + AX_PROGVAR([git]) + AX_PROGVAR([rustfmt]) + ]) + ]) + + AC_MSG_CHECKING([whether to build Rust code with debugging information]) + AM_COND_IF([DEBUG_RELEASE], [ + AC_MSG_RESULT(yes) + RUST_TARGET_SUBDIR=debug + ], [ + AC_MSG_RESULT(no) + RUST_TARGET_SUBDIR=release + ]) + AC_SUBST([RUST_TARGET_SUBDIR]) + + AC_CONFIG_FILES([build-aux/rust_boilerplate.mk]) + +]) diff --git a/build-aux/ax_shell_completion_dirs.m4 b/build-aux/ax_shell_completion_dirs.m4 new file mode 100644 index 000000000..1545cfd94 --- /dev/null +++ b/build-aux/ax_shell_completion_dirs.m4 @@ -0,0 +1,47 @@ +AC_DEFUN_ONCE([AX_SHELL_COMPLETION_DIRS], [ + + AX_TRANSFORM_PACKAGE_NAME + + AC_ARG_WITH([bash-completion-dir], + AS_HELP_STRING([--with-bash-completion-dir[=PATH]], + [Install the bash auto-completion script in this directory. @<:@default=yes@:>@]), + [], + [with_bash_completion_dir=yes]) + AM_CONDITIONAL([ENABLE_BASH_COMPLETION], + [test "x$with_bash_completion_dir" != "xno"]) + + AM_COND_IF([ENABLE_BASH_COMPLETION], + [PKG_CHECK_MODULES([BASH_COMPLETION], [bash-completion >= 2.0], + [BASH_COMPLETION_DIR="$(pkg-config --define-variable=datadir=$datadir --variable=completionsdir bash-completion)"], + [BASH_COMPLETION_DIR="$datadir/bash-completion/completions"])], + [BASH_COMPLETION_DIR="$with_bash_completion_dir"]) + AC_SUBST([BASH_COMPLETION_DIR]) + + AC_ARG_WITH([fish-completion-dir], + AS_HELP_STRING([--with-fish-completion-dir[=PATH]], + [Install the fish auto-completion script in this directory. @<:@default=yes@:>@]), + [], + [with_fish_completion_dir=yes]) + AM_CONDITIONAL([ENABLE_FISH_COMPLETION],[test "x$with_fish_completion_dir" != "xno"]) + + AM_COND_IF([ENABLE_FISH_COMPLETION], + [PKG_CHECK_MODULES([FISH_COMPLETION], [fish >= 3.0], + [FISH_COMPLETION_DIR="$(pkg-config --define-variable=datadir=$datadir --variable=completionsdir fish)"], + [FISH_COMPLETION_DIR="$datadir/fish/vendor_completions.d"])], + [FISH_COMPLETION_DIR="$with_fish_completion_dir"]) + AC_SUBST([FISH_COMPLETION_DIR]) + + AC_ARG_WITH([zsh-completion-dir], + AS_HELP_STRING([--with-zsh-completion-dir[=PATH]], + [Install the zsh auto-completion script in this directory. @<:@default=yes@:>@]), + [], + [with_zsh_completion_dir=yes]) + AM_CONDITIONAL([ENABLE_ZSH_COMPLETION], + [test "x$with_zsh_completion_dir" != "xno"]) + + AM_COND_IF([ENABLE_ZSH_COMPLETION], + [ZSH_COMPLETION_DIR="$datadir/zsh/site-functions"], + [ZSH_COMPLETION_DIR="$with_zsh_completion_dir"]) + AC_SUBST([ZSH_COMPLETION_DIR]) + +]) diff --git a/build-aux/ax_subst_man_date.m4 b/build-aux/ax_subst_man_date.m4 index 3eea16d76..edb697064 100644 --- a/build-aux/ax_subst_man_date.m4 +++ b/build-aux/ax_subst_man_date.m4 @@ -1,13 +1,13 @@ AC_DEFUN([AX_SUBST_MAN_DATE], [ - ax_date_fmt="m4_default($1,%d %B %Y)" - ax_src_file="m4_default($2,*.1.in)" - AS_IF([test ! -e .gitignore], - [ - AX_PROGVAR([date]) - AX_BUILD_DATE_EPOCH(MAN_DATE, "$ax_date_fmt") - ], [ - AX_PROGVAR([git]) - MAN_DATE=$($GIT log -1 --format="%cd" --date=format:"$ax_date_fmt" -- $ax_src_file) - ]) - AC_SUBST([MAN_DATE]) + ax_date_fmt="m4_default($1,%d %B %Y)" + ax_src_file="m4_default($2,*.1.in)" + AS_IF([test ! -e .gitignore], + [ + AX_PROGVAR([date]) + AX_BUILD_DATE_EPOCH(MAN_DATE, "$ax_date_fmt") + ], [ + AX_PROGVAR([git]) + MAN_DATE=$($GIT log -1 --format="%cd" --date=format:"$ax_date_fmt" -- $ax_src_file) + ]) + AC_SUBST([MAN_DATE]) ]) diff --git a/build-aux/ax_subst_transformed_package_name.m4 b/build-aux/ax_subst_transformed_package_name.m4 deleted file mode 100644 index a6ae77472..000000000 --- a/build-aux/ax_subst_transformed_package_name.m4 +++ /dev/null @@ -1,5 +0,0 @@ -AC_DEFUN([AX_SUBST_TRANSFORMED_PACKAGE_NAME], [ - AC_PROG_SED - TRANSFORMED_PACKAGE_NAME="$(printf "$PACKAGE_NAME" | $SED -e "$(printf "$program_transform_name" | $SED -e 's/\$\$/\$/')")" - AC_SUBST([TRANSFORMED_PACKAGE_NAME]) -]) diff --git a/build-aux/ax_transform_package_name.m4 b/build-aux/ax_transform_package_name.m4 new file mode 100644 index 000000000..0ff3298ca --- /dev/null +++ b/build-aux/ax_transform_package_name.m4 @@ -0,0 +1,18 @@ +# The autotools supplied AC_ARG_PROGAM enables renaming operations, but it +# supplies them as a sed operation that can be applied to multiple binaries. +# This isn't convenient to use if we're just renaming the top level package, so +# we go ahead and *do* the transformation and save for use as a substitution. + +AC_DEFUN_ONCE([AX_TRANSFORM_PACKAGE_NAME], [ + + AC_PROG_SED + + TRANSFORMED_PACKAGE_NAME="$(printf "$PACKAGE_NAME" | $SED -e "$(printf "$program_transform_name" | $SED -e 's/\$\$/\$/')")" + AC_SUBST([TRANSFORMED_PACKAGE_NAME]) + + PACKAGE_VAR="$(printf "$PACKAGE_NAME" | $SED -e "s/-/_/g")" + AC_SUBST([PACKAGE_VAR]) + + AC_ARG_PROGRAM + +]) diff --git a/build-aux/build.rs b/build-aux/build.rs new file mode 100644 index 000000000..fdb215eb8 --- /dev/null +++ b/build-aux/build.rs @@ -0,0 +1,135 @@ +#[cfg(feature = "completions")] +use clap::CommandFactory; +#[cfg(feature = "completions")] +use clap_complete::generator::generate_to; +#[cfg(feature = "completions")] +use clap_complete::shells::{Bash, Elvish, Fish, PowerShell, Zsh}; +#[cfg(feature = "manpage")] +use clap_mangen::Man; +#[cfg(feature = "completions")] +use std::fs; +#[cfg(any(feature = "static", feature = "completions"))] +use std::path::Path; +use std::{collections, env}; +use vergen::EmitBuilder; + +#[cfg(feature = "completions")] +include!("../src/cli.rs"); + +fn main() { + if let Ok(val) = env::var("AUTOTOOLS_DEPENDENCIES") { + for dependency in val.split(' ') { + println!("cargo:rerun-if-changed={dependency}"); + } + } + let mut builder = EmitBuilder::builder(); + // If passed a version from automake, use that instead of vergen's formatting + if let Ok(val) = env::var("VERSION_FROM_AUTOTOOLS") { + println!("cargo:rustc-env=VERGEN_GIT_DESCRIBE={val}") + } else { + builder = *builder.git_describe(true, true, None); + }; + builder.emit().expect("Unable to generate the cargo keys!"); + pass_on_configure_details(); + #[cfg(feature = "manpage")] + generate_manpage(); + #[cfg(feature = "completions")] + generate_shell_completions(); + #[cfg(feature = "static")] + { + let dir = env::var("CARGO_MANIFEST_DIR").unwrap(); + println!( + "cargo:rustc-link-search=native={}", + Path::new(&dir).join("justenough").join(".libs").display() + ); + println!( + "cargo:rustc-link-search=native={}", + Path::new(&dir).join("libtexpdf").join(".libs").display() + ); + println!("cargo:rustc-link-arg=-l:fontmetrics.a"); + println!("cargo:rustc-link-arg=-l:justenoughfontconfig.a"); + println!("cargo:rustc-link-arg=-l:justenoughharfbuzz.a"); + println!("cargo:rustc-link-arg=-l:justenoughicu.a"); + println!("cargo:rustc-link-arg=-l:justenoughlibtexpdf.a"); + println!("cargo:rustc-link-arg=-l:svg.a"); + println!("cargo:rustc-link-arg=-l:libtexpdf.a"); + + // These are automatically linked by Cargo, but sadly *previous* to our links above + println!("cargo:rustc-link-arg=-lharfbuzz"); // needed by justenoughharfbuzz + #[cfg(feature = "variations")] + println!("cargo:rustc-link-arg=-lharfbuzz-subset"); // needed by justenoughharfbuzz + println!("cargo:rustc-link-arg=-lfontconfig"); // needed by justenoughfontconfig + println!("cargo:rustc-link-arg=-licui18n"); // needed by justenoughicu + println!("cargo:rustc-link-arg=-licuuc"); // needed by justenoughicu + println!("cargo:rustc-link-arg=-lm"); // needed by svg + println!("cargo:rustc-link-arg=-lz"); // needed by libtexpdf + println!("cargo:rustc-link-arg=-lpng"); // needed by libtexpdf + } +} + +/// Generate man page +#[cfg(feature = "manpage")] +fn generate_manpage() { + let out_dir = match env::var_os("OUT_DIR") { + None => return, + Some(out_dir) => out_dir, + }; + let manpage_dir = Path::new(&out_dir); + fs::create_dir_all(manpage_dir).expect("Unable to create directory for generated manpages"); + let app = Cli::command(); + let bin_name: &str = app + .get_bin_name() + .expect("Could not retrieve bin-name from generated Clap app"); + let app = Cli::command(); + let man = Man::new(app); + let mut buffer: Vec = Default::default(); + man.render(&mut buffer) + .expect("Unable to render man page to UTF-8 string"); + fs::write(manpage_dir.join(format!("{bin_name}.1")), buffer) + .expect("Unable to write manepage to file"); +} + +/// Generate shell completion files from CLI interface +#[cfg(feature = "completions")] +fn generate_shell_completions() { + let out_dir = match env::var_os("OUT_DIR") { + None => return, + Some(out_dir) => out_dir, + }; + let completions_dir = Path::new(&out_dir).join("completions"); + fs::create_dir_all(&completions_dir) + .expect("Could not create directory in which to place completions"); + let app = Cli::command(); + let bin_name: &str = app + .get_bin_name() + .expect("Could not retrieve bin-name from generated Clap app"); + let mut app = Cli::command(); + #[cfg(feature = "bash")] + generate_to(Bash, &mut app, bin_name, &completions_dir) + .expect("Unable to generate bash completions"); + #[cfg(feature = "elvish")] + generate_to(Elvish, &mut app, bin_name, &completions_dir) + .expect("Unable to generate elvish completions"); + #[cfg(feature = "fish")] + generate_to(Fish, &mut app, bin_name, &completions_dir) + .expect("Unable to generate fish completions"); + #[cfg(feature = "powershell")] + generate_to(PowerShell, &mut app, bin_name, &completions_dir) + .expect("Unable to generate powershell completions"); + #[cfg(feature = "zsh")] + generate_to(Zsh, &mut app, bin_name, &completions_dir) + .expect("Unable to generate zsh completions"); +} + +/// Pass through some variables set by autoconf/automake about where we're installed to cargo for +/// use in finding resources at runtime +fn pass_on_configure_details() { + let mut autoconf_vars = collections::HashMap::new(); + autoconf_vars.insert("CONFIGURE_PREFIX", String::from("./")); + autoconf_vars.insert("CONFIGURE_BINDIR", String::from("./")); + autoconf_vars.insert("CONFIGURE_DATADIR", String::from("./")); + for (var, default) in autoconf_vars { + let val = env::var(var).unwrap_or(default); + println!("cargo:rustc-env={var}={val}"); + } +} diff --git a/build-aux/cargo-updater.js b/build-aux/cargo-updater.js new file mode 100644 index 000000000..1b64fda44 --- /dev/null +++ b/build-aux/cargo-updater.js @@ -0,0 +1,12 @@ +const TOML = require('@iarna/toml') + +module.exports.readVersion = function (contents) { + const data = TOML.parse(contents) + return data.package.version +} + +module.exports.writeVersion = function (contents, version) { + const data = TOML.parse(contents) + data.package.version = version + return TOML.stringify(data) +} diff --git a/build-aux/create-homebrew-formula.pl b/build-aux/create-homebrew-formula.pl index 6fcb9748b..b4ce6cdfb 100644 --- a/build-aux/create-homebrew-formula.pl +++ b/build-aux/create-homebrew-formula.pl @@ -13,7 +13,6 @@ my %rock_url_templates = ( cassowary => "https://github.com/sile-typesetter/cassowary.lua/archive/vVERSION.tar.gz", - cosmo => "https://github.com/mascarenhas/cosmo/archive/vVERSION.tar.gz", linenoise => "https://github.com/hoelzro/lua-linenoise/archive/VERSION.tar.gz", lpeg => "http://www.inf.puc-rio.br/~roberto/lpeg/lpeg-VERSION.tar.gz", lua_cliargs => "https://github.com/amireh/lua_cliargs/archive/vUNSTRIPPEDVERSION.tar.gz", diff --git a/build-aux/git-version-gen b/build-aux/git-version-gen index 2a79f1f67..b971f41c2 100755 --- a/build-aux/git-version-gen +++ b/build-aux/git-version-gen @@ -1,4 +1,5 @@ -#!/bin/sh +#!/usr/bin/env sh + # Print a version string. scriptversion=2012-03-18.17; # UTC @@ -165,8 +166,8 @@ then # Newer: v6.10-77-g0f8faeb # Older: v6.10-g0f8faeb case $v in - *-*-*) : git describe is okay three part flavor ;; - *-*) + *-*-g*) : git describe is okay three part flavor ;; + *-g*) : git describe is older two part flavor # Recreate the number of commits and rewrite such that the # result is the same as if we were using the newer version @@ -181,7 +182,7 @@ then ;; esac - v=`echo "$v" | sed 's/-/.r/'`; + v=`echo "$v" | sed 's/-\([0-9]\)/.r\1/'`; v_from_git=1 else v=UNKNOWN diff --git a/build-aux/git_version.mk b/build-aux/git_version.mk new file mode 100644 index 000000000..97a35ad8b --- /dev/null +++ b/build-aux/git_version.mk @@ -0,0 +1,34 @@ +.SECONDEXPANSION: + +# EXTRA_@PACKAGE_VAR@_SOURCES += .version +EXTRA_DIST += build-aux/git-version-gen +BUILT_SOURCES += .version +CLEANFILES += .version .version-prev + +_BRANCH_REF != $(AWK) '{print ".git/" $$2}' .git/HEAD 2>/dev/null ||: +.version: $(_BRANCH_REF) + @if [ -e "$(srcdir)/.tarball-version" ]; then \ + printf "$(VERSION)" > $@; \ + else \ + touch "$@-prev"; \ + if [ -e "$@" ]; then \ + cp "$@" "$@-prev"; \ + fi; \ + ./build-aux/git-version-gen "$(srcdir)/.tarball-version" > $@; \ + $(CMP) -s "$@" "$@-prev" || autoreconf configure.ac --force; \ + fi + +check-version: check-git-version + +.PHONY: check-git-version +check-git-version: $(PACKAGE_NAME)$(EXEEXT) | .version + $(GREP) -Fx '$(VERSION)' $| + ./$< --version | $(GREP) -Ff $| + +dist-hook: dist-tarball-version + +.PHONY: dist-tarball-version +dist-tarball-version: + printf "$(VERSION)" > "$(distdir)/.tarball-version" + +# vim: ft=automake diff --git a/build-aux/list-dist-files.sh.in b/build-aux/list-dist-files.sh.in index 3c0ae546a..c007dcb00 100755 --- a/build-aux/list-dist-files.sh.in +++ b/build-aux/list-dist-files.sh.in @@ -7,7 +7,7 @@ finder () { } printf '%s' "SILEDATA =" -finder core -name '*.lua' -not -name '*_spec.lua' -not -name 'version.lua' +finder core -name '*.lua' -not -name '*_spec.lua' -not -name 'version.lua' -not -name 'features.lua' -not -name 'pathsetup.lua' finder classes inputters languages outputters packages shapers typesetters pagebuilders -name '*.lua' -not -name '*_spec.lua' finder classes i18n packages -name '*.ftl' finder packages -name '*.svg' diff --git a/build-aux/pkg.nix b/build-aux/pkg.nix new file mode 100644 index 000000000..2b53be20d --- /dev/null +++ b/build-aux/pkg.nix @@ -0,0 +1,188 @@ +# NOTE: This file is supposed to be similar to what is in Nixpkgs, except for +# the `version`, `src` and `libtexpdf-src` attributes that are given by the +# `flake.nix`. In Nixpkgs, we don't need `libtexpdf-src` because we use +# `fetchFromGitHub` with fetchSubmodules = true;`. +{ lib +, stdenv +, version, src, libtexpdf-src +, autoreconfHook +, gitMinimal +, pkg-config +, jq +, cargo +, rustc +, rustPlatform +, makeWrapper +, poppler_utils +, harfbuzz +, icu +, fontconfig +, lua +, libiconv +, darwin +, makeFontsConf +, gentium +, runCommand +}: + +let + luaEnv = lua.withPackages(ps: with ps; [ + cassowary + cldr + fluent + linenoise + loadkit + lpeg + lua-zlib + lua_cliargs + luaepnf + luaexpat + luafilesystem + luarepl + luasec + luasocket + luautf8 + penlight + vstruct + # lua packages needed for testing + busted + luacheck + # NOTE: Add lua packages here, to change the luaEnv also read by `flake.nix` + ] ++ lib.optionals (lib.versionOlder lua.luaversion "5.2") [ + bit32 + ] ++ lib.optionals (lib.versionOlder lua.luaversion "5.3") [ + compat53 + ]); +in stdenv.mkDerivation (finalAttrs: { + pname = "sile"; + inherit version src; + + preAutoreconf = '' + # Add the libtexpdf src instead of the git submodule. (From some reason + # without --no-preserve=mode flag, libtexpdf/ is unwriteable). As explained + # before, in Nixpkgs, we won't need to run these commands. + rm -rf ./libtexpdf + cp --no-preserve=mode -r ${libtexpdf-src} ./libtexpdf/ + # pretend to be a tarball release so sile --version will not say `vUNKNOWN`. + echo ${finalAttrs.version} > .tarball-version + ''; + + nativeBuildInputs = [ + autoreconfHook + gitMinimal + pkg-config + jq + cargo + rustc + rustPlatform.cargoSetupHook + poppler_utils + makeWrapper + ]; + # In Nixpkgs, we don't copy the Cargo.lock file from the repo to Nixpkgs' + # repo, but we inherit src, and specify a hash (it is a fixed output + # derivation). `nix-update` and `nixpkgs-update` should be able to catch that + # hash and update it as well when performing updates. + cargoDeps = rustPlatform.importCargoLock { + lockFile = ../Cargo.lock; + }; + + buildInputs = [ + luaEnv + harfbuzz + icu + fontconfig + libiconv + ] ++ lib.optionals stdenv.isDarwin [ + darwin.apple_sdk.frameworks.AppKit + ]; + + configureFlags = [ + # Nix will supply all the Lua dependencies, so stop the build system from + # bundling vendored copies of them. + "--with-system-lua-sources" + "--with-system-luarocks" + # The automake check target uses pdfinfo to confirm the output of a test + # run, and uses autotools to discover it. This flake build eschews that + # test because it is run from the source directory but the binary is + # already built with system paths, so it can't be checked under Nix until + # after install. After install the Makefile isn't available of course, so + # we have our own copy of it with a hard coded path to `pdfinfo`. By + # specifying some binary here we skip the configure time test for + # `pdfinfo`, by using `false` we make sure that if it is expected during + # build time we would fail to build since we only provide it at test time. + "PDFINFO=false" + #"--with-manual" In Nixpkgs we add this flag, here its not important enough + ] ++ lib.optionals (!lua.pkgs.isLuaJIT) [ + "--without-luajit" + ]; + + postPatch = '' + patchShebangs build-aux/*.sh build-aux/git-version-gen + '' + lib.optionalString stdenv.isDarwin '' + sed -i -e 's|@import AppKit;|#import |' src/macfonts.m + ''; + + NIX_LDFLAGS = lib.optionalString stdenv.isDarwin "-framework AppKit"; + + FONTCONFIG_FILE = makeFontsConf { + fontDirectories = [ + gentium + ]; + }; + + enableParallelBuilding = true; + + # Autoconf wants to check that Makefile imports are valid files even before + # it potentially generates said files from substitution templates. The + # upstream project uses a bootstrap.sh to create this. Since we skip that, we + # have to fix this race condition ourselves. + postUnpack = '' + touch source/build-aux/rust_boilerplate.mk + ''; + + # remove forbidden references to $TMPDIR + preFixup = lib.optionalString stdenv.isLinux '' + for f in "$out"/bin/*; do + if isELF "$f"; then + patchelf --shrink-rpath --allowed-rpath-prefixes "$NIX_STORE" "$f" + fi + done + ''; + + passthru = { + # So it will be easier to inspect this environment, in comparison to others + inherit luaEnv; + # Copied from Makefile.am + tests.test = lib.optionalAttrs (!(stdenv.isDarwin && stdenv.isAarch64)) ( + runCommand "${finalAttrs.pname}-test" { + nativeBuildInputs = [ poppler_utils finalAttrs.finalPackage ]; + inherit (finalAttrs) FONTCONFIG_FILE; + } '' + output=$(mktemp -t selfcheck-XXXXXX.pdf) + echo "foo" | sile -o $output - + pdfinfo $output | grep "SILE v${finalAttrs.version}" > $out + ''); + }; + + outputs = [ "out" "doc" "man" "dev" ]; + + meta = { + description = "A typesetting system"; + longDescription = '' + SILE is a typesetting system; its job is to produce beautiful + printed documents. Conceptually, SILE is similar to TeX—from + which it borrows some concepts and even syntax and + algorithms—but the similarities end there. Rather than being a + derivative of the TeX family SILE is a new typesetting and + layout engine written from the ground up using modern + technologies and borrowing some ideas from graphical systems + such as InDesign. + ''; + homepage = "https://sile-typesetter.org"; + # In nixpkgs we use a version specific URL for CHANGELOG.md + changelog = "https://github.com/sile-typesetter/sile/raw/master/CHANGELOG.md"; + platforms = lib.platforms.unix; + maintainers = with lib.maintainers; [ doronbehar alerque ]; + license = lib.licenses.mit; + }; +}) diff --git a/build-aux/rust_boilerplate.mk.in b/build-aux/rust_boilerplate.mk.in new file mode 100644 index 000000000..fc61df56d --- /dev/null +++ b/build-aux/rust_boilerplate.mk.in @@ -0,0 +1,95 @@ +export VERSION_FROM_AUTOTOOLS = v$(VERSION) + +@PACKAGE_VAR@_SOURCES += Cargo.toml build-aux/build.rs +EXTRA_@PACKAGE_VAR@_SOURCES += Cargo.lock .version +dist_man_MANS += @PACKAGE_NAME@.1 + +CLEANFILES += $(bin_PROGRAMS) $(BUILT_SOURCES) $(dist_man_MANS) + +CARGO_RELEASE_ARGS = +if !DEBUG_RELEASE +CARGO_RELEASE_ARGS += --release --locked +endif + +CARGO_ENV = CARGO_TARGET_DIR=@abs_top_builddir@/target +CARGO_BIN = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/$(PACKAGE_NAME) +_RUST_OUT = @abs_top_builddir@/target/@RUST_TARGET_SUBDIR@/.cargo_out_dir + +include $(top_srcdir)/build-aux/git_version.mk +include $(top_srcdir)/build-aux/shell_completion_dirs.mk + +@PACKAGE_NAME@$(EXEEXT): $(CARGO_BIN) + $(INSTALL) $< $@ + +# Leave some tips for cargo to use so CLI knows where it is +export CONFIGURE_PREFIX = $(prefix)/ +export CONFIGURE_DATADIR = $(datadir)/ +export CONFIGURE_BINDIR = $(bindir)/ + +CARGO_VERBOSE = $(cargo_verbose_$(V)) +cargo_verbose_ = $(cargo_verbose_$(AM_DEFAULT_VERBOSITY)) +cargo_verbose_0 = +cargo_verbose_1 = --verbose + +$(COMPLETIONS_OUT_DIR): + $(MKDIR_P) $@ + +$(PACKAGE_NAME).1: $(CARGO_BIN) + $(INSTALL) -m644 $$(cat $(_RUST_OUT))/$@ $@ + +$(COMPLETIONS_OUT_DIR)/$(TRANSFORMED_PACKAGE_NAME): $(CARGO_BIN) | $(COMPLETIONS_OUT_DIR) + $(INSTALL) -m755 $$(cat $(_RUST_OUT))/$(COMPLETIONS_OUT_DIR)/$(PACKAGE_NAME).bash $@ + +$(COMPLETIONS_OUT_DIR)/$(TRANSFORMED_PACKAGE_NAME).elv: $(CARGO_BIN) | $(COMPLETIONS_OUT_DIR) + $(INSTALL) -m755 $$(cat $(_RUST_OUT))/$(COMPLETIONS_OUT_DIR)/$(PACKAGE_NAME).elv $@ + +$(COMPLETIONS_OUT_DIR)/$(TRANSFORMED_PACKAGE_NAME).fish: $(CARGO_BIN) | $(COMPLETIONS_OUT_DIR) + $(INSTALL) -m755 $$(cat $(_RUST_OUT))/$(COMPLETIONS_OUT_DIR)/$(PACKAGE_NAME).fish $@ + +$(COMPLETIONS_OUT_DIR)/_$(TRANSFORMED_PACKAGE_NAME).ps1: $(CARGO_BIN) | $(COMPLETIONS_OUT_DIR) + $(INSTALL) -m755 $$(cat $(_RUST_OUT))/$(COMPLETIONS_OUT_DIR)/_$(PACKAGE_NAME).ps1 $@ + +$(COMPLETIONS_OUT_DIR)/_$(TRANSFORMED_PACKAGE_NAME): $(CARGO_BIN) | $(COMPLETIONS_OUT_DIR) + $(INSTALL) -m755 $$(cat $(_RUST_OUT))/$(COMPLETIONS_OUT_DIR)/_$(PACKAGE_NAME) $@ + +$(_RUST_OUT) $(CARGO_BIN): $(@PACKAGE_VAR@_SOURCES) $(EXTRA_@PACKAGE_VAR@_SOURCES) + cd $(top_srcdir) + set -e + export AUTOTOOLS_DEPENDENCIES="$^" + $(CARGO_ENV) $(CARGO) build $(CARGO_VERBOSE) $(CARGO_FEATURE_ARGS) $(CARGO_RELEASE_ARGS) + $(CARGO_ENV) $(CARGO) build --quiet --message-format=json $(CARGO_FEATURE_ARGS) $(CARGO_RELEASE_ARGS) | + $(JQ) -sr 'map(select(.reason == "build-script-executed")) | last | .out_dir' > $(_RUST_OUT) + +RUST_DEVELOPER_TARGETS = cargo-test clippy rustfmt +.PHONY: $(RUST_DEVELOPER_TARGETS) + +if DEVELOPER + +test: cargo-test +lint: rustfmt clippy +clean-local: clean-cargo +check-local: cargo-test + +rustfmt: + $(GIT) ls-files '*.rs' '*.rs.in' | $(XARGS) $(RUSTFMT) --check --config skip_children=true + +clippy: + cd $(srcdir) + $(CARGO_ENV) $(CARGO) $(CARGO_VERBOSE) clippy $(CARGO_FEATURE_ARGS) -- -D warnings + +clean-cargo: + cd $(top_srcdir) + $(CARGO_ENV) $(CARGO) $(CARGO_VERBOSE) clean + +cargo-test: $(PACKAGE_NAME)$(EXEEXT) + cd $(srcdir) + $(CARGO_ENV) $(CARGO) $(CARGO_VERBOSE) test $(CARGO_FEATURE_ARGS) --locked + +else !DEVELOPER + +$(RUST_DEVELOPER_TARGETS): + @: $(error "Please reconfigure using --enable-developer to use developer tooling") + +endif !DEVELOPER + +# vim: ft=automake diff --git a/build-aux/shell_completion_dirs.mk b/build-aux/shell_completion_dirs.mk new file mode 100644 index 000000000..5e29abbe7 --- /dev/null +++ b/build-aux/shell_completion_dirs.mk @@ -0,0 +1,21 @@ +COMPLETIONS_OUT_DIR = completions + +if ENABLE_BASH_COMPLETION +bashcompletiondir = $(BASH_COMPLETION_DIR) +nodist_bashcompletion_DATA = $(COMPLETIONS_OUT_DIR)/$(TRANSFORMED_PACKAGE_NAME) +CLEANFILES += $(nodist_bashcompletion_DATA) +endif + +if ENABLE_FISH_COMPLETION +fishcompletiondir = $(FISH_COMPLETION_DIR) +nodist_fishcompletion_DATA = $(COMPLETIONS_OUT_DIR)/$(TRANSFORMED_PACKAGE_NAME).fish +CLEANFILES += $(nodist_fishcompletion_DATA) +endif + +if ENABLE_ZSH_COMPLETION +zshcompletiondir = $(ZSH_COMPLETION_DIR) +nodist_zshcompletion_DATA = $(COMPLETIONS_OUT_DIR)/_$(TRANSFORMED_PACKAGE_NAME) +CLEANFILES += $(nodist_zshcompletion_DATA) +endif + +# vim: ft=automake diff --git a/classes/base.lua b/classes/base.lua index e91b2feec..7c7768bd3 100644 --- a/classes/base.lua +++ b/classes/base.lua @@ -79,7 +79,12 @@ end function class:setOptions (options) options = options or {} - options.papersize = options.papersize or "a4" + -- Classes that add options with dependencies should explicitly handle them, then exempt them from furthur processing. + -- The landscape option is handled explicitly before papersize, then the "rest" of options that are not interdependent. + self.options.landscape = SU.boolean(options.landscape, false) + options.landscape = nil + self.options.papersize = options.papersize or "a4" + options.papersize = nil for option, value in pairs(options) do self.options[option] = value end @@ -101,10 +106,16 @@ function class:declareOptions () end return self._name end) + self:declareOption("landscape", function(_, landscape) + if landscape then + self.landscape = landscape + end + return self.landscape + end) self:declareOption("papersize", function (_, size) if size then self.papersize = size - SILE.documentState.paperSize = SILE.papersize(size) + SILE.documentState.paperSize = SILE.papersize(size, self.options.landscape) SILE.documentState.orgPaperSize = SILE.documentState.paperSize SILE.newFrame({ id = "page", @@ -139,15 +150,19 @@ function class.declareSettings (_) }) end -function class:loadPackage (packname, options) +function class:loadPackage (packname, options, reload) local pack = require(("packages.%s"):format(packname)) if type(pack) == "table" and pack.type == "package" then -- new package - self.packages[pack._name] = pack(options) + self.packages[pack._name] = pack(options, reload) else -- legacy package self:initPackage(pack, options) end end +function class:reloadPackage (packname, options) + return self:loadPackage(packname, options, true) +end + function class:initPackage (pack, options) SU.deprecated("class:initPackage(options)", "package(options)", "0.14.0", "0.16.0", [[ This package appears to be a legacy format package. It returns a table @@ -575,9 +590,9 @@ function class:finish () end SILE.typesetter:runHooks("pageend") -- normally run by the typesetter self:endPage() - if SILE.typesetter then - assert(SILE.typesetter:isQueueEmpty(), "queues not empty") - end + if SILE.typesetter and not SILE.typesetter:isQueueEmpty() then + SU.error("Queues are not empty as expected after ending last page", true) + end SILE.outputter:finish() self:runHooks("finish") end diff --git a/classes/docbook.lua b/classes/docbook.lua index 93c429189..4c967f5f8 100644 --- a/classes/docbook.lua +++ b/classes/docbook.lua @@ -21,7 +21,7 @@ function class:_init (options) self:loadPackage("footnotes") -- SILE sensibly does not define a pixels unit because it has no meaning in its frame of reference. However the -- Docbook standard requires them and even defaults to them for bare numbers, even while warning against their use. - -- Here we define a px arbitrarily to be the equivilent point unit if output was 300 DPI. + -- Here we define a px arbitrarily to be the equivalent point unit if output was 300 DPI. SILE.units.px = { definition = "0.24pt" } diff --git a/classes/markdown.lua b/classes/markdown.lua deleted file mode 100644 index e2a997e73..000000000 --- a/classes/markdown.lua +++ /dev/null @@ -1,70 +0,0 @@ --- You will need my lunamark fork from https://github.com/simoncozens/lunamark --- for the AST writer. - -local book = require("classes.book") -local class = pl.class(book) -class._name = "markdown" - -SILE.inputs.markdown = { - order = 2, - appropriate = function (fn, _) - return fn:match("md$") or fn:match("markdown$") - end, - process = function (data) - local lunamark = require("lunamark") - local reader = lunamark.reader.markdown - local writer = lunamark.writer.ast.new() - local parse = reader.new(writer) - local t = parse(data) - t = { [1] = t, id = "document", options = { class = "markdown" }} - -- SILE.inputs.common.init(fn, t) - SILE.process(t[1]) - end -} - -function class:_init (options) - book._init(self, options) - self:loadPackage("url") - self:loadPackage("image") -end - -function class:registerCommands () - - book.registerCommands(self) - - self:registerCommand("sect1", function (options, content) - SILE.call("chapter", options, content) - end) - - self:registerCommand("sect2", function (options, content) - SILE.call("section", options, content) - end) - - self:registerCommand("sect3", function (options, content) - SILE.call("subsection", options, content) - end) - - self:registerCommand("emphasis", function (options, content) - SILE.call("em", options, content) - end) - - self:registerCommand("paragraph", function (_, content) - SILE.process(content) - SILE.call("par") - end) - - self:registerCommand("bulletlist", function (_, content) - SILE.process(content) - end) - - self:registerCommand("link", function (_, content) - SILE.call("verbatim:font", {}, content) - end) - - self:registerCommand("image", function (_, content) - SILE.call("img", {src=content.src}) - end) - -end - -return class diff --git a/classes/plain.lua b/classes/plain.lua index 876f2abe0..12ef08f95 100644 --- a/classes/plain.lua +++ b/classes/plain.lua @@ -76,7 +76,7 @@ function class:registerCommands () self:registerCommand("noindent", function (_, content) if #SILE.typesetter.state.nodes ~= 0 then - SU.warn("\\noindent called after nodes already recieved in a paragraph, the setting will have no effect because the parindent (if any) has already been output") + SU.warn("\\noindent called after nodes already received in a paragraph, the setting will have no effect because the parindent (if any) has already been output") end SILE.settings:set("current.parindent", SILE.nodefactory.glue()) SILE.process(content) diff --git a/classes/tplain.lua b/classes/tplain.lua index c43a1f7a8..f1c6ec189 100644 --- a/classes/tplain.lua +++ b/classes/tplain.lua @@ -14,7 +14,7 @@ class.defaultFrameset.content = { } -- The classes tplain and tbook inherit from plain and book respectively but also --- have this bit in common; this makes it accessable +-- have this bit in common; this makes it accessible function class:_t_common () self:loadPackage("font-fallback") self:loadPackage("hanmenkyoshi") diff --git a/configure.ac b/configure.ac index 4981a75bc..51446e1d8 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,13 @@ AC_PREREQ([2.69]) -AC_INIT([sile], [m4_esyscmd(build-aux/git-version-gen .tarball-version)], [simon@simon-cozens.org]) +AC_INIT([sile], [m4_esyscmd(build-aux/git-version-gen .tarball-version)], [caleb@alerque.com]) AC_CONFIG_AUX_DIR([build-aux]) AC_CONFIG_MACRO_DIR([build-aux]) AM_INIT_AUTOMAKE([foreign tar-pax dist-xz dist-zip no-dist-gzip color-tests subdir-objects]) AM_SILENT_RULES([yes]) +AX_GIT_VERSION +AX_TRANSFORM_PACKAGE_NAME + AM_CONDITIONAL([IS_SDIST], [test ! -e .gitignore]) # Checks for programs. @@ -14,9 +17,6 @@ AC_PROG_OBJC AC_PROG_AWK AC_PROG_GREP AC_PROG_SED - -AX_PROGVAR([cmp]) -AX_PROGVAR([git]) AX_PROGVAR([find]) LT_PREREQ([2.2]) @@ -24,6 +24,8 @@ LT_INIT([dlopen]) AC_CANONICAL_HOST +AX_RUST_BOILERPLATE + AC_ARG_ENABLE([dependency-checks], AS_HELP_STRING([--disable-dependency-checks], [Disable dependency checks])) @@ -34,6 +36,11 @@ AC_ARG_ENABLE([developer], [Check for and enable tooling required only for developers])) AM_CONDITIONAL([DEVELOPER], [test "x$enable_developer" = "xyes"]) +AC_ARG_ENABLE([embeded], + AS_HELP_STRING([--enable-embeded-resources], + [Compile resources such as Lua module files directly into the Rust CLI binary])) +AM_CONDITIONAL([EMBEDED_RESOURCES], [test "x$enable_embeded_resources" = "xyes"]) + AC_ARG_ENABLE([font-variations], AS_HELP_STRING([--disable-font-variations], [Disable support for OpenType variations and variable fonts that requires HarfBuzz subsetter library])) @@ -66,10 +73,16 @@ AC_ARG_WITH([system-luarocks], AM_CONDITIONAL([SYSTEM_LUAROCKS], [test "x$with_system_luarocks" = "xyes"]) AC_SUBST([SYSTEM_LUAROCKS]) +AC_ARG_WITH([system-lua-sources], + AS_HELP_STRING([--with-system-lua-sources], + [Don’t compile against vendored Lua sources, use system headers])) +AM_CONDITIONAL([SYSTEM_LUA_SOURCES], [test "x$with_system_lua_sources" = "xyes"]) +AC_SUBST([SYSTEM_LUA_SOURCES]) + AC_ARG_WITH([luajit], - AS_HELP_STRING([--with-luajit], - [Run under LuaJIT instead of Lua])) -AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" = "xyes"]) + AS_HELP_STRING([--without-luajit], + [Prefer LuaJIT over PUC Lua, even if the latter is newer])) +AM_CONDITIONAL([LUAJIT], [test "x$with_luajit" != "xno"]) AC_ARG_WITH([manual], AS_HELP_STRING([--with-manual], @@ -80,10 +93,17 @@ AM_CONDITIONAL([FONT_DOWNLOAD_TOOLS], [test -z ${DEVELOPER_TRUE} || (test -z ${M AC_SUBST([FONT_DOWNLOAD_TOOLS]) AM_COND_IF([DEPENDENCY_CHECKS], [ + AC_MSG_NOTICE([checking for SILE specific build dependencies]) AX_FONT(Gentium Plus) - AC_PATH_PROG([PDFINFO], [pdfinfo]) + AX_PROGVAR([pdfinfo]) + AX_PROGVAR([jq]) + AX_PROGVAR([xargs]) + AM_COND_IF([MANUAL], [ + AX_PROGVAR([dot]) + AX_PROGVAR([gs]) + ]) AC_MSG_CHECKING([for OS X]) have_appkit=no @@ -146,15 +166,7 @@ AM_COND_IF([DEPENDENCY_CHECKS], [ fi ]) - case $host_os in - msys) - LUA_VERSION=5.3 # By fiat. This is obviously not good. - ;; - *) - AX_PROG_LUA([5.1]) - ;; - esac - + AX_PROG_LUA([5.1]) AX_LUA_HEADERS AX_LUA_LIBS @@ -168,7 +180,6 @@ AM_COND_IF([DEPENDENCY_CHECKS], [ AS_IF([test "$LUA_SHORT_VERSION" -lt 53], AX_LUA_MODULE([compat53], [compat53]) ) - AX_LUA_MODULE([cosmo], [cosmo]) AX_LUA_MODULE([cldr], [cldr]) AX_LUA_MODULE([fluent], [fluent]) AX_LUA_MODULE([linenoise], [linenoise]) @@ -197,9 +208,11 @@ AM_COND_IF([DEPENDENCY_CHECKS], [ ]) AM_COND_IF([DEVELOPER], [ + AX_PROGVAR([git]) AX_PROGVAR([busted]) AX_PROGVAR([luacheck]) AX_PROGVAR([luarocks]) + AX_PROGVAR([nix]) AX_PROGVAR([perl]) ]) @@ -235,21 +248,41 @@ esac AC_SUBST([SHARED_LIB_EXT]) AC_SUBST([LUAROCKSARGS]) -AX_SUBST_TRANSFORMED_PACKAGE_NAME - -adl_RECURSIVE_EVAL(["${datadir}/${TRANSFORMED_PACKAGE_NAME}"], [SILE_PATH]) -AC_DEFINE_UNQUOTED([SILE_PATH],["${SILE_PATH}"],[Path for SILE packages and classes]) +# Avoid need for `--datarootdir=$(cd ..; pwd)` hack to run locally for +# tests/manual build when developer mode is enabled +AM_COND_IF([DEVELOPER], [ + adl_RECURSIVE_EVAL(["$(pwd)"], [SILE_PATH]) + datarootdir="$(cd ..; pwd)" +],[ + adl_RECURSIVE_EVAL(["${datadir}/${TRANSFORMED_PACKAGE_NAME}"], [SILE_PATH]) +]) +AC_DEFINE_UNQUOTED([SILE_PATH], ["${SILE_PATH}"], [Path for SILE packages and classes]) AC_SUBST([SILE_PATH]) +# In order for our Rust CLI binary to use the same default package.path as the system Lua, +# we test the system Lua (required only at build not run time) for its current package.path. +adl_RECURSIVE_EVAL(["$(${LUA} -e 'print(package.path)')"], [LUA_PATH]) +AC_DEFINE_UNQUOTED([LUA_PATH], ["${LUA_PATH}"],[System Lua package path]) +AC_SUBST([LUA_PATH]) + +adl_RECURSIVE_EVAL(["$(${LUA} -e 'print(package.cpath)')"], [LUA_CPATH]) +AC_DEFINE_UNQUOTED([LUA_CPATH], ["${LUA_CPATH}"], [System Lua package cpath]) +AC_SUBST([LUA_CPATH]) + adl_RECURSIVE_EVAL(["${libdir}/${TRANSFORMED_PACKAGE_NAME}"], [SILE_LIB_PATH]) -AC_DEFINE_UNQUOTED([SILE_LIB_PATH],["${SILE_LIB_PATH}"],[Path for SILE libraries]) +AC_DEFINE_UNQUOTED([SILE_LIB_PATH],["${SILE_LIB_PATH}"], [Path for SILE libraries]) AC_SUBST([SILE_LIB_PATH]) +AC_SUBST([ROCKSPECWARNING], ["DO NOT EDIT! Modify template sile.rockspec.in"]) +AC_SUBST([ROCKREV], [1]) + AX_SUBST_MAN_DATE AC_CONFIG_FILES([build-aux/list-dist-files.sh], [chmod +x build-aux/list-dist-files.sh]) -AC_CONFIG_FILES([Makefile src/Makefile sile.1 core/features.lua core/version.lua]) -AC_CONFIG_FILES([sile tests/regressions.pl], [chmod +x sile tests/regressions.pl]) +AC_CONFIG_FILES([Makefile justenough/Makefile sile-lua.1 core/features.lua core/pathsetup.lua core/version.lua]) +AC_CONFIG_FILES([sile-lua:sile.in], [chmod +x sile-lua]) +AC_CONFIG_FILES([tests/regressions.pl], [chmod +x tests/regressions.pl]) +AC_CONFIG_FILES([sile-dev-1.rockspec:sile.rockspec.in]) AC_ARG_PROGRAM diff --git a/core/break.lua b/core/break.lua index 3a18cf692..1f041885a 100644 --- a/core/break.lua +++ b/core/break.lua @@ -91,7 +91,7 @@ end -- up to the that total. Returning values that don't add up may produce -- unexpected results. -- --- TeX wizards shall also note that this is slighty different from +-- TeX wizards shall also note that this is slightly different from -- Knuth's definition "nline l1 i1 l2 i2 ... lN iN". function lineBreak:parShape(_) return 0, self.hsize, 0 @@ -176,7 +176,7 @@ function lineBreak:tryBreak() -- 855 end -- 861 if self.r.lineNumber > self.old_l then - if debugging then SU.debug("break", "Mimimum demerits = " .. self.minimumDemerits) end + if debugging then SU.debug("break", "Minimum demerits = " .. self.minimumDemerits) end if self.minimumDemerits < awful_bad and (self.old_l ~= self.easy_line or self.r == self.activeListHead) then self:createNewActiveNodes(breakType) end diff --git a/core/cli.lua b/core/cli.lua index 2fc777046..2226f9896 100644 --- a/core/cli.lua +++ b/core/cli.lua @@ -2,37 +2,55 @@ local cli = pl.class() cli.parseArguments = function () local cliargs = require("cliargs") - local print_version = function() - print(SILE.full_version) + local print_version = function(flag) + print(flag == "V" and "SILE " .. SILE.version or SILE.full_version) os.exit(0) end cliargs:set_colsz(0, 120) cliargs:set_name("sile") cliargs:set_description([[ - The SILE typesetter reads a single input file, by default in either SIL or XML format, - and processes it to generate a single output file, by default in PDF format. The - output file will be written to the same name as the input file with the extension - changed to .pdf. Additional input or output formats can be handled by requiring a - module that adds support for them first. + The SILE typesetter reads an input file(s), by default in either SIL or XML format, and + processes them to generate an output file, by default in PDF format. The output will be written + to a file with the same name as the first input file with the extension changed to .pdf unless + the `--output` argument is used. Additional input or output formats can be handled by loading + a module with the `--use` argument to add support for them first. ]]) - cliargs:splat("INPUT", "input document, by default in SIL or XML format") - cliargs:option("-b, --backend=VALUE", "choose an alternative output backend") - cliargs:option("-c, --class=VALUE", "override default document class") - cliargs:option("-d, --debug=VALUE", "show debug information for tagged aspects of SILE’s operation", {}) - cliargs:option("-e, --evaluate=VALUE", "evaluate Lua expression before processing input", {}) - cliargs:option("-E, --evaluate-after=VALUE", "evaluate Lua expression after processing input", {}) - cliargs:option("-f, --fontmanager=VALUE", "choose an alternative font manager") - cliargs:option("-I, --include=FILE", "deprecated, see --use, --preamble, or --postamble", {}) - cliargs:option("-m, --makedeps=FILE", "generate a list of dependencies in Makefile format") - cliargs:option("-o, --output=FILE", "explicitly set output file name") - cliargs:option("-O, --options=PARAMETER=VALUE[,PARAMETER=VALUE]", "set document class options", {}) - cliargs:option("-p, --preamble=FILE", "process SIL, XML, or other content before the input document", {}) - cliargs:option("-P, --postamble=FILE", "process SIL, XML, or other content after the input document", {}) - cliargs:option("-u, --use=MODULE[[PARAMETER=VALUE][,PARAMETER=VALUE]]", "load and initialize a module before processing input", {}) - cliargs:flag("-q, --quiet", "suppress warnings and informational messages during processing") - cliargs:flag("-t, --traceback", "display detailed location trace on errors and warnings") - cliargs:flag("-h, --help", "display this help, then exit") - cliargs:flag("-v, --version", "display version information, then exit", print_version) + cliargs:splat("INPUTS", + "Input document filename(s), by default in SIL, XML, or Lua formats.", nil, 999) + cliargs:option("-b, --backend=VALUE", + "Specify the output backend") + cliargs:option("-c, --class=VALUE", + "Override the default or specified document class") + cliargs:option("-d, --debug=VALUE", + "Show debug information for tagged aspects of SILE’s operation", {}) + cliargs:option("-e, --evaluate=VALUE", + "Evaluate Lua expression before processing input", {}) + cliargs:option("-E, --evaluate-after=VALUE", + "Evaluate Lua expression after processing input", {}) + cliargs:option("-f, --fontmanager=VALUE", + "Specify which font manager to use") + cliargs:option("-I, --include=FILE", + "Deprecated, see --use, --preamble, --postamble, or multiple input files", {}) + cliargs:option("-m, --makedeps=FILE", + "Generate a Makefile format list of dependencies and white them to a file") + cliargs:option("-o, --output=FILE", + "Explicitly set output file name") + cliargs:option("-O, --options=PARAMETER=VALUE[,PARAMETER=VALUE]", + "Set or override document class options", {}) + cliargs:option("-p, --preamble=FILE", + "Include the contents of a SIL, XML, or other resource file before the input document content", {}) + cliargs:option("-P, --postamble=FILE", + "Include the contents of a SIL, XML, or other resource file after the input document content", {}) + cliargs:option("-u, --use=MODULE[[PARAMETER=VALUE][,PARAMETER=VALUE]]", + "Load and initialize a class, inputter, shaper, or other module before processing the main input", {}) + cliargs:flag("-q, --quiet", + "Suppress warnings and informational messages during processing") + cliargs:flag("-t, --traceback", + "Display detailed location trace on errors and warnings") + cliargs:flag("-h, --help", + "Display this help, then exit") + cliargs:flag("-V, --version", + "Print version", print_version) -- Work around cliargs not processing - as an alias for STDIO streams: -- https://github.com/amireh/lua_cliargs/issues/67 local _arg = pl.tablex.imap(luautf8.gsub, _G.arg, "^-$", "STDIO") @@ -42,16 +60,20 @@ cli.parseArguments = function () local code = parse_err:match("^Usage:") and 0 or 1 os.exit(code) end - if opts.INPUT then - if opts.INPUT == "STDIO" then - opts.INPUT = "-" + if opts.INPUTS and #opts.INPUTS > 0 then + local has_input_filename = false + pl.tablex.foreachi(opts.INPUTS, function (v, k) + if v == "STDIO" then + opts.INPUTS[k] = "-" + elseif not has_input_filename then + has_input_filename = true + end + end) + if not has_input_filename and not opts.output then + SU.error("Unable to derive an output filename (perhaps because input is a STDIO stream).\n".. + " Please use --output to set one explicitly.") end - SILE.input.filename = opts.INPUT - -- Turn slashes around in the event we get passed a path from a Windows shell - local filename = opts.INPUT:gsub("\\", "/") - -- Strip extension - SILE.masterFilename = string.match(filename, "(.+)%..-$") or filename - SILE.masterDir = SILE.masterFilename:match("(.-)[^%/]+$") + SILE.input.filenames = opts.INPUTS end if opts.backend then SILE.backend = opts.backend @@ -97,9 +119,8 @@ cli.parseArguments = function () for _, path in ipairs(opts.postamble) do table.insert(SILE.input.postambles, path) end - for _, path in ipairs(opts.include) do + if opts.include then SU.deprecated("-I/--include", "-u/--use or -p/--preamble", "0.14.0", "0.15.0") - table.insert(SILE.input.includes, path) end -- http://lua-users.org/wiki/VarargTheSecondClassCitizen local summary = function (...) diff --git a/core/deprecations.lua b/core/deprecations.lua index faaece860..204baa9cc 100644 --- a/core/deprecations.lua +++ b/core/deprecations.lua @@ -49,9 +49,8 @@ SILE.baseClass = setmetatable({}, { __index = nobaseclass }) -SILE.defaultTypesetter = function (frame) +SILE.defaultTypesetter = function () SU.deprecated("SILE.defaultTypesetter", "SILE.typesetters.base", "0.14.6", "0.15.0") - return SILE.typesetters.base(frame) end SILE.toPoints = function (_, _) @@ -82,3 +81,25 @@ function SILE.doTexlike (doc) [[Add format argument "sil" to skip content detection and assume SIL input]]) return SILE.processString(doc, "sil") end + +local nopackagemanager = function () + SU.deprecated("SILE.PackageManager", nil, "0.13.2", "0.15.0", [[ + The built in SILE package manager has been completely deprecated. In its place + SILE can now load classes, packages, and other resources installed via + LuaRocks. Any SILE package may be published on LuaRocks.org or any private + repository. Rocks may be installed to the host system root filesystem, a user + directory, or a custom location. Please see the SILE manual for usage + instructions. Package authors especially can review the template repository + on GitHub for how to create a package. + ]]) +end + +SILE.PackageManager = {} +setmetatable(SILE.PackageManager, { + __index = nopackagemanager +}) + +-- luacheck: ignore updatePackage +-- luacheck: ignore installPackage +updatePackage = nopackagemanager +installPackage = nopackagemanager diff --git a/core/makedeps.lua b/core/makedeps.lua index 41718b6da..5e194821c 100644 --- a/core/makedeps.lua +++ b/core/makedeps.lua @@ -25,7 +25,7 @@ local makeDeps = { write = function (self) self:add_modules() if type(self.filename) ~= "string" then - self.filename = SILE.masterFilename .. ".d" + self.filename = pl.path.splitext(SILE.input.filenames[1]) .. ".d" end local depfile, err = io.open(self.filename, "w") if not depfile then return SU.error(err) end diff --git a/core/nodefactory.lua b/core/nodefactory.lua index dd3511336..9d986e007 100644 --- a/core/nodefactory.lua +++ b/core/nodefactory.lua @@ -255,7 +255,7 @@ end getmetatable(nodefactory.unshaped).__index = function (_, _) -- if k == "width" then SU.error("Can't get width of unshaped node", true) end -- TODO: No idea why porting to proper Penlight classes this ^^^^^^ started - -- killing everything. Perhaps becaus this function started working and would + -- killing everything. Perhaps because this function started working and would -- actually need to return rawget(self, k) or something? end diff --git a/core/packagemanager.lua b/core/packagemanager.lua deleted file mode 100644 index 10b98b70e..000000000 --- a/core/packagemanager.lua +++ /dev/null @@ -1,211 +0,0 @@ -local lfs = require("lfs") - -local catalogueURL = "https://raw.githubusercontent.com/sile-typesetter/sile-packages/master/packages.lua" -local packageHome = tostring(SYSTEM_SILE_PATH) .. "/packagemanager/" -local catalogueHome = packageHome .. "catalogue.lua" -local installedCatalogue = packageHome .. "installed.lua" - -local http = require("ssl.https") -local recentlyUpdated = false -local recentlyReloaded = false -local origcpath = package.cpath -- for idempotence -local origpath = package.path - -SILE.PackageManager = { - installed = {}, - Catalogue = {} -} - -local _deprecated = function () - SU.deprecated("SILE.PackageManager", nil, "0.13.2", "0.15.0", [[ - The built in SILE package manager has been completely deprecated. In its place - SILE can now load classes, packages, and other resources installed via - LuaRocks. Any SILE package may be published on LuaRocks.org or any private - repository. Rocks may be installed to the host system root filesystem, a user - directory, or a custom location. Please see the SILE manual for usage - instructions. Package authors especially can review the template repository - on GitHub for how to create a package. - ]]) -end - -local function loadInSandbox(untrusted_code) - _deprecated() - if _ENV then -- simple Lua 5.2 version check - local env = {} - local untrusted_function, message = load(untrusted_code, nil, 't', env) - if not untrusted_function then return nil, message end - return pcall(untrusted_function) - else - if untrusted_code:byte(1) == 27 then return nil, "binary bytecode prohibited" end - local untrusted_function, message = load(untrusted_code) - if not untrusted_function then return nil, message end - -- luacheck: globals setfenv env - -- (At least there is in Lua 5.1) - setfenv(untrusted_function, env) - return pcall(untrusted_function) - end -end - -local function dumpTable(tbl) - _deprecated() - if type(tbl) == 'table' then - local str = '{ ' - for k, v in pairs(tbl) do - if type(k) ~= 'number' then k = '"'..k..'"' end - str = str .. '['..k..'] = ' .. dumpTable(v) .. ',' - end - return str .. '} ' - else - -- This only works because we are only storing strings! - return '"' .. tostring(tbl) .. '"' - end -end - -local function fixupPaths() - local paths = "" - local cpaths = "" - for pkg, _ in pairs(SILE.PackageManager.installed) do - _deprecated() - paths = paths .. packageHome .. pkg .. '/?.lua;' - cpaths = cpaths .. packageHome .. pkg .. "/?."..SHARED_LIB_EXT.. ";" - end - if paths:len() >= 1 then package.path = paths .. ";" .. origpath end - if cpaths:len() >= 1 then package.cpath = cpaths .. ";" .. origcpath end -end - -local function saveInstalled() - _deprecated() - local dump = dumpTable(SILE.PackageManager.installed) - local file, err = io.open(installedCatalogue, "w") - if err then - SU.error("Could not write installed package list at"..installedCatalogue..": "..err) - end - file:write("return "..dump) - file:close() - fixupPaths() -end - -local function updateCatalogue () - if not lfs.attributes(packageHome) then - if not lfs.mkdir(packageHome) then - SU.error("Error making package manager home directory: "..packageHome) - end - end - print("Loading catalogue from "..catalogueURL) - local result, statuscode, _ = http.request(catalogueURL) - if statuscode ~= 200 then - SU.error("Could not load catalogue from "..catalogueURL..": "..statuscode) - end - local file, err = io.open(catalogueHome, "w") - if err then - SU.error("Could not write package catalogue at"..catalogueHome..": "..err) - end - print("Writing "..(#result).." bytes to "..catalogueHome) - file:write(result) - file:close() - recentlyUpdated = true - recentlyReloaded = false -end - -local function loadInstalledCatalogue() - local file = io.open(installedCatalogue, "r") - if file ~= nil then - local contents = file:read("*all") - local success, res = loadInSandbox(contents) - if not success then - SU.error("Error loading installed package list: "..res) - end - SILE.PackageManager.installed = res - end -end - -local function reloadCatalogue() - local file = io.open(catalogueHome, "r") - if file ~= nil then - local contents = file:read("*all") - local success, res = loadInSandbox(contents) - if not success then - SU.error("Error loading package catalogue: "..res) - end - SILE.PackageManager.Catalogue = res - end - loadInstalledCatalogue() - print("Package catalogue reloaded") - recentlyReloaded = true -end - --- These functions are global so they can be used from the REPL --- luacheck: ignore updatePackage --- luacheck: ignore installPackage - -function updatePackage(packageName, branch) - _deprecated() - local target = packageHome .. packageName - -- Are we already there? - if SILE.PackageManager.installed[packageName] == branch and branch ~= "master" then - print("Nothing to do!") - return true - end - local cwd = lfs.currentdir() - local _, err = lfs.chdir(target) - if err then - SU.warn("Package directory "..target.." went away! Trying again...") - SILE.PackageManager.installed[packageName] = nil - saveInstalled() - installPackage(packageName) - end - - local ret = os.execute("git pull") - if not ret then - SU.error("Error updating repository for package "..packageName..": "..ret) - end - ret = os.execute("git checkout "..branch) - if not ret then - SU.error("Error updating repository for package "..packageName..": "..ret) - end - lfs.chdir(cwd) - SILE.PackageManager.installed[packageName] = branch - saveInstalled() -end - -function installPackage(packageName) - _deprecated() - if not recentlyUpdated then updateCatalogue() end - if not recentlyReloaded then reloadCatalogue() end - if not SILE.PackageManager.Catalogue[packageName] then - -- Add support for URL-based package names later. - SU.error("Can't install "..packageName..": package not known") - end - - local metadata = SILE.PackageManager.Catalogue[packageName] - - -- Check dependencies - if metadata.depends then - for _, pkg in ipairs(metadata.depends) do - if not SILE.PackageManager.installed[pkg] then - print(packageName.." requires "..pkg..", installing that...") - installPackage(pkg) - end - end - end - - -- Clone repo in temp directory - if metadata.repository then - local branch = metadata.version or "master" - local target = packageHome .. packageName - if lfs.attributes(target) then - updatePackage(packageName, branch) - else - local ret = os.execute("git clone -c advice.detachedHead=false -b "..branch.." "..metadata.repository.." "..target) - if not ret then -- This should return status code but it's returning true for me... - SU.error("Error cloning repository for package "..packageName..": "..ret) - end - end - SILE.PackageManager.installed[packageName] = branch - saveInstalled() - end -end - --- Set up the world -loadInstalledCatalogue() -fixupPaths() diff --git a/core/pagebuilder.lua b/core/pagebuilder.lua index 36b68f159..c0e904fc3 100644 --- a/core/pagebuilder.lua +++ b/core/pagebuilder.lua @@ -1,3 +1 @@ SU.deprecated("core.pagebuilder", "pagebuilder.base", "0.14.6", "0.15.0") - -return require("pagebuilders.base") diff --git a/core/papersize.lua b/core/papersize.lua index 2b8a9d062..f1b9c4a04 100644 --- a/core/papersize.lua +++ b/core/papersize.lua @@ -63,16 +63,19 @@ local papersize = { } setmetatable(papersize, { - __call = function (self, size) + __call = function (self, size, landscape) + local geometry local _, _, x, y = string.find(size, "(.+)%s+x%s+(.+)") if x and y then - return { SILE.measurement(x):tonumber(), SILE.measurement(y):tonumber() } + geometry = { SILE.measurement(x):tonumber(), SILE.measurement(y):tonumber() } else - size = string.lower(size:gsub("[-%s]+", "")) - if self[size] then - return self[size] - end + local preset_name = string.lower(size:gsub("[-%s]+", "")) + geometry = self[preset_name] end + if SU.boolean(landscape) then + geometry[1], geometry[2] = geometry[2], geometry[1] + end + if geometry then return geometry end SU.error(string.format([[Unable to parse papersize '%s'. Custom sizes may be entered with 'papersize= x '. Predefined paper sizes include: %s]], diff --git a/core/pathsetup.lua.in b/core/pathsetup.lua.in new file mode 100644 index 000000000..3a5f482bb --- /dev/null +++ b/core/pathsetup.lua.in @@ -0,0 +1,116 @@ +-- Allow autoconf to setup system lua paths at compile time, not run time (only used in developer mode) +if "@LUA_PATH@" ~= "" then + package.path = "@LUA_PATH@" + package.cpath = "@LUA_CPATH@" +end + +local executable = debug.getinfo(3, "S").source +local luaversion = _VERSION:match("%d+%.%d+") + +-- Normalize possibly dirty Lua path formatting shortcut: /./ → / +-- Even leafo/gh-actions-luarocks takes this shortcut which inhibits duplicate cleanup. +package.path = package.path:gsub("/%./", "/") +package.cpath = package.cpath:gsub("/%./", "/") + +-- Utility function so that last-added paths take precidence and are not duplicated. +local function prepend_and_dedup (segment, path) + local escaped = segment:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') -- copied from pl.utils.escape() which we can't load yet + local striped = path:gsub(("^%s"):format(escaped), ""):gsub((";%s"):format(escaped), "") + return ("%s;%s"):format(segment, striped) +end + +-- Prepend paths specifically for Lua module.s +local function prependPath (path) + package.path = prepend_and_dedup(path .. "/?/init.lua", package.path) + package.path = prepend_and_dedup(path .. "/?.lua", package.path) +end + +-- Prepand paths specifically for C modules. +local function prependCPath (path) + package.cpath = prepend_and_dedup(path .. "/?.@SHARED_LIB_EXT@", package.cpath) +end + +-- Take a given path and iterate over permutations of paths that LuaRocks might have installed a rock to that are +-- specific to a given Lua version version +local function extendPathsRocks (path) + prependCPath(path .. "/lib/lua/" .. luaversion) + prependCPath(path .. "/lib/lua/" .. luaversion .. "/sile") + prependPath(path .. "/share/lua/" .. luaversion) + prependPath(path .. "/share/lua/" .. luaversion .. "/sile") +end + +-- Take a given path and iterate over the permutations of subdirectories we expect to finde SILE/Lua/C modules under. +-- The second argument enables extra paths that we *only* expect to find in SILE source checkouts, and should not be +-- found in system, user, toolkit, or project level paths. +local function extendPaths (path, silesourcedir) + extendPathsRocks(path .. "/lua_modules") + prependCPath(path) + prependPath(path) + if silesourcedir then + prependPath(path .. "/lua-libraries") + else + prependCPath(path .. "/sile") + prependPath(path .. "/sile") + end + -- These paths are *only* used in developer mode for build testing + if "@DEVELOPER_FALSE@" ~= "" then -- see ./configure --(en|dis)able-developer + prependCPath(path .. "/libtexpdf/.libs") + prependCPath(path .. "/justenough/.libs") + end +end + +-- Facilitate loading SILE classes & packages from system LuaRocks by adding variants of the default Lua paths with sile +-- appended, stashed to be prepended later. Also weed out CWD relative paths, we add them in a different order later. +local luapath = {} +local extpath = {} +for path in package.path:gmatch("[^;]+") do + table.insert(extpath, tostring(path:gsub("%?", "sile/?"))) + table.insert(luapath, path) +end +package.path = table.concat(luapath, ";") + +-- This path is set by autoconf at configure time, and could be the full path to the source directory if in developer +-- mode or the expeted system istalation location otherwise. +extendPaths("@SILE_PATH@", true) +extendPaths("@SILE_LIB_PATH@", true) + +-- If the configure time option to use system luarocks is disabled, use ones local to the source (again could be the +-- development mode source directory or expected system instalation location). +if "@SYSTEM_LUAROCKS_FALSE@" == "" then -- see ./configure --with[out]-system-luarocks + extendPathsRocks("@SILE_PATH@/lua_modules") +end + +-- Stuff the variants of system Lua Rocks paths with sile suffixes added back at higher priority that regular paths. +package.path = table.concat(extpath, ";") .. ";" .. package.path + +-- Deal with the *run time* variant of SILE_PATH, which may be more than one path. This could be references to a source +-- tree for development work, a fork of some SILE core libraries, or just a way to stuff toolkits into the path besides +-- the default project local or system paths without exporting Lua environment variables. +local pathvar = os.getenv("SILE_PATH") +if pathvar then + for path in string.gmatch(pathvar, "[^;]+") do + if not path:match("^./") and path:len() >= 1 then + extendPaths(path, true) + end + end +end + +-- Add the current working directory, presumably a local project, as one of the highest priority paths. +local executable_dir = executable:gsub("(.*)(/.*)", "%1") +if executable_dir:match("^@") then + -- Running from a nix flake reports this, but we don't want anything special to get added. + extendPaths(".") +else + -- If executable_dir is just an alternate name of PWD, we don't need to duplicate it. + -- Also ignore Rust binary thinking its executable_dir is in its source directory. + if executable_dir ~= "./" and executable_dir ~= "src" then + extendPaths(executable_dir) + end + extendPathsRocks("./lua_modules") + extendPaths(".") +end + +-- Stuff internal utility features into the global namespace so they could be manipulated externally (undocumented). +_G.extendSilePath = extendPaths +_G.extendSilePathRocks = extendPathsRocks +_G.executablePath = executable diff --git a/core/settings.lua b/core/settings.lua index 4db053fdc..ac3b200cf 100644 --- a/core/settings.lua +++ b/core/settings.lua @@ -1,6 +1,5 @@ local deprecator = function () SU.deprecated("SILE.settings.*", "SILE.settings:*", "0.13.0", "0.15.0") - return SILE.settings end local settings = pl.class() @@ -96,18 +95,18 @@ function settings:_init() end function settings:pushState () - if not self then self = deprecator() end + if not self then return deprecator() end table.insert(self.stateQueue, self.state) self.state = pl.tablex.copy(self.state) end function settings:popState () - if not self then self = deprecator() end + if not self then return deprecator() end self.state = table.remove(self.stateQueue) end function settings:declare (spec) - if not spec then self, spec = deprecator(), self end + if not spec then return deprecator() end if spec.name then SU.deprecated("'name' argument of SILE.settings:declare", "'parameter' argument of SILE.settings:declare", "0.10.10", "0.11.0") end @@ -121,7 +120,7 @@ end --- Reset all settings to their default value. function settings:reset () - if not self then self = deprecator() end + if not self then return deprecator() end for k,_ in pairs(self.state) do self:set(k, self.defaults[k]) end @@ -131,7 +130,7 @@ end -- that is at the head of the settings stack (normally the document -- level). function settings:toplevelState () - if not self then self = deprecator() end + if not self then return deprecator() end if #self.stateQueue ~= 0 then for parameter, _ in pairs(self.state) do -- Bypass self:set() as the latter performs some tests and a cast, @@ -148,7 +147,7 @@ function settings:get (parameter) if parameter == "current.parindent" then return SILE.typesetter and SILE.typesetter.state.parindent end - if not parameter then self, parameter = deprecator(), self end + if not parameter then return deprecator() end if not self.declarations[parameter] then SU.error("Undefined setting '"..parameter.."'") end @@ -184,7 +183,7 @@ function settings:set (parameter, value, makedefault, reset) end return end - if type(self) ~= "table" then self, parameter, value, makedefault, reset = deprecator(), self, parameter, value, makedefault end + if type(self) ~= "table" then return deprecator() end if not self.declarations[parameter] then SU.error("Undefined setting '"..parameter.."'") end @@ -203,14 +202,14 @@ function settings:set (parameter, value, makedefault, reset) end function settings:temporarily (func) - if not func then self, func = deprecator(), self end + if not func then return deprecator() end self:pushState() func() self:popState() end function settings:wrap () -- Returns a closure which applies the current state, later - if not self then self = deprecator() end + if not self then return deprecator() end local clSettings = pl.tablex.copy(self.state) return function(content) table.insert(self.stateQueue, self.state) diff --git a/core/sile.lua b/core/sile.lua index d19f5cdf8..99e320727 100644 --- a/core/sile.lua +++ b/core/sile.lua @@ -29,6 +29,9 @@ fluent = require("fluent")() -- Includes for _this_ scope local lfs = require("lfs") +-- Developer tooling profiler +local ProFi + SILE.utilities = require("core.utilities") SU = SILE.utilities -- regrettable global alias @@ -56,13 +59,12 @@ SILE.documentState = {} SILE.rawHandlers = {} -- User input values, currently from CLI options, potentially all the inuts --- needed for a user to use a SILE-as-a-library verion to produce documents --- programatically. +-- needed for a user to use a SILE-as-a-library version to produce documents +-- programmatically. SILE.input = { - filename = "", + filenames = {}, evaluates = {}, evaluateAfters = {}, - includes = {}, uses = {}, options = {}, preambles = {}, @@ -94,7 +96,7 @@ SILE.nodefactory = require("core.nodefactory") -- NOTE: -- See remainaing internal libraries loaded at the end of this file because --- they run core SILE functions on load istead of waiting to be called (or +-- they run core SILE functions on load instead of waiting to be called (or -- depend on others that do). local function runEvals (evals, arg) @@ -131,6 +133,14 @@ SILE.init = function () SILE.outputter = SILE.outputters.dummy() end SILE.pagebuilder = SILE.pagebuilders.base() + io.stdout:setvbuf("no") + if SU.debugging("profile") then + ProFi = require("ProFi") + ProFi:start() + end + if SILE.makeDeps then + SILE.makeDeps:add(_G.executablePath) + end runEvals(SILE.input.evaluates, "evaluate") end @@ -189,7 +199,9 @@ SILE.require = function (dependency, pathprefix, deprecation_ack) dependency = dependency:gsub(".lua$", "") local status, lib if pathprefix then - status, lib = pcall(require, pl.path.join(pathprefix, dependency)) + -- Note this is not a *path*, it is a module identifier: + -- https://github.com/sile-typesetter/sile/issues/1861 + status, lib = pcall(require, pl.stringx.join('.', { pathprefix, dependency })) end if not status then local prefixederror = lib @@ -280,7 +292,7 @@ function SILE.processString (doc, format, filename, options) -- a specific inputter to use, use it at the exclusion of all content type -- detection local inputter - if filename and filename:gsub("STDIN", "-") == SILE.input.filename and SILE.inputter then + if filename and pl.path.normcase(pl.path.normpath(filename)) == pl.path.normcase(SILE.input.filenames[1]) and SILE.inputter then inputter = SILE.inputter else format = format or detectFormat(doc, filename) @@ -290,7 +302,7 @@ function SILE.processString (doc, format, filename, options) inputter = SILE.inputters[format](options) -- If we did content detection *and* this is the master file, save the -- inputter for posterity and postambles - if filename and filename:gsub("STDIN", "-") == SILE.input.filename then + if filename and pl.path.normcase(filename) == pl.path.normcase(SILE.input.filenames[1]:gsub("^-$", "STDIN")) then SILE.inputter = inputter end end @@ -306,10 +318,19 @@ function SILE.processFile (filename, format, options) filename = "STDIN" doc = io.stdin:read("*a") else - filename = SILE.resolveFile(filename) - if not filename then - SU.error("Could not find file") + -- Turn slashes around in the event we get passed a path from a Windows shell + filename = filename:gsub("\\", "/") + if not SILE.masterFilename then + SILE.masterFilename = pl.path.splitext(pl.path.normpath(filename)) + end + if SILE.input.filenames[1] and not SILE.masterDir then + SILE.masterDir = pl.path.dirname(SILE.input.filenames[1]) end + if SILE.masterDir and SILE.masterDir:len() >= 1 then + _G.extendSilePath(SILE.masterDir) + _G.extendSilePathRocks(SILE.masterDir .. "/lua_modules") + end + filename = SILE.resolveFile(filename) or SU.error("Could not find file") local mode = lfs.attributes(filename).mode if mode ~= "file" and mode ~= "named pipe" then SU.error(filename.." isn't a file or named pipe, it's a ".. mode .."!") @@ -356,7 +377,7 @@ function SILE.resolveFile (filename, pathprefix) candidates[#candidates+1] = "?" -- Iterate through the directory of the master file, the SILE_PATH variable, and the current directory -- Check for prefixed paths first, then the plain path in that fails - if SILE.masterFilename then + if SILE.masterDir then for path in SU.gtoke(SILE.masterDir..";"..tostring(os.getenv("SILE_PATH")), ";") do if path.string and path.string ~= "nil" then if pathprefix then candidates[#candidates+1] = pl.path.join(path.string, pathprefix, "?") end @@ -369,8 +390,8 @@ function SILE.resolveFile (filename, pathprefix) local resolved, err = package.searchpath(filename, path, "/") if resolved then if SILE.makeDeps then SILE.makeDeps:add(resolved) end - else - SU.warn(("Unable to find file '%s': %s"):format(filename, err)) + elseif SU.debugging("paths") then + SU.debug("paths", ("Unable to find file '%s': %s"):format(filename, err)) end return resolved end @@ -437,13 +458,19 @@ function SILE.finish () if not SILE.quiet then io.stderr:write("\n") end + if SU.debugging("profile") then + ProFi:stop() + ProFi:writeReport(pl.path.splitext(SILE.input.filenames[1]) .. '.profile.txt') + end + if SU.debugging("versions") then + SILE.shaper:debugVersions() + end end -- Internal libraries that run core SILE functions on load SILE.settings = require("core.settings")() require("core.hyphenator-liang") require("core.languages") -require("core.packagemanager") SILE.linebreak = require("core.break") require("core.frame") SILE.cli = require("core.cli") @@ -451,7 +478,7 @@ SILE.repl = require("core.repl") SILE.font = require("core.font") -- For warnings and shims scheduled for removal that are easier to keep track --- of when they are not spead across so many locations... +-- of when they are not spread across so many locations... require("core/deprecations") return SILE diff --git a/core/typesetter.lua b/core/typesetter.lua index cf7ca1a52..d2d35c79a 100644 --- a/core/typesetter.lua +++ b/core/typesetter.lua @@ -1,3 +1 @@ SU.deprecated("core.typesetter", "typesetters.base", "0.14.0", "0.15.0") - -return require("typesetters.base") diff --git a/core/utilities-numbers.lua b/core/utilities-numbers.lua index 8666edc4a..4eef0ea43 100644 --- a/core/utilities-numbers.lua +++ b/core/utilities-numbers.lua @@ -60,7 +60,7 @@ local icuFormat = function (num, lang, options) -- ICU locale: see https://unicode-org.github.io/icu/userguide/locale/ -- Ex. "en", "en-US", "sr-Latn"... local iculocale = lang or "" - -- ICU keywork for a numbering system specifier: @numbers=xxxx + -- ICU keyword for a numbering system specifier: @numbers=xxxx -- The specifiers are defined here: -- https://github.com/unicode-org/cldr/blob/main/common/bcp47/number.xml if options.system then diff --git a/core/utilities.lua b/core/utilities.lua index 4959efe61..05bef5ad3 100644 --- a/core/utilities.lua +++ b/core/utilities.lua @@ -1,5 +1,6 @@ local bitshim = require("bitshim") local luautf8 = require("lua-utf8") +local semver = require("semver") local utilities = {} @@ -31,9 +32,9 @@ end local _skip_traceback_levels = 2 -utilities.error = function(message, bug) +utilities.error = function (message, isbug) _skip_traceback_levels = 3 - utilities.warn(message, bug) + utilities.warn(message, isbug) _skip_traceback_levels = 2 io.stderr:flush() SILE.outputter:finish() -- Only really useful from the REPL but no harm in trying @@ -41,10 +42,10 @@ utilities.error = function(message, bug) error(message, 2) end -utilities.warn = function(message, bug) +utilities.warn = function (message, isbug) if SILE.quiet then return end io.stderr:write("\n! " .. message) - if SILE.traceback or bug then + if SILE.traceback or isbug then io.stderr:write(" at:\n" .. SILE.traceStack:locationTrace()) if _skip_traceback_levels == 2 then io.stderr:write(debug.traceback("", _skip_traceback_levels) or "\t! debug.traceback() did not identify code location") @@ -55,6 +56,11 @@ utilities.warn = function(message, bug) io.stderr:write("\n") end +utilities.msg = function (message) + if SILE.quiet then return end + io.stderr:write("\n! " .. message .. "\n") +end + utilities.debugging = function (category) return SILE.debugFlags.all and category ~= "profile" or SILE.debugFlags[category] end @@ -89,18 +95,19 @@ utilities.gtoke = function (string, pattern) end utilities.deprecated = function (old, new, warnat, errorat, extra) + warnat, errorat = semver(warnat or 0), semver(errorat or 0) + local current = SILE.version and semver(SILE.version:match("v([0-9]*.[0-9]*.[0-9]*)")) or warnat -- SILE.version is defined *after* most of SILE loads. It’s available at -- runtime but not useful if we encounter deprecated code in core code. Users -- will never encounter this failure, but as a developer it’s hard to test a -- deprecation when core code refactoring is an all-or-nothing proposition. - -- Hence we fake it ‘till we make it, all deprecations internally are warings. + -- Hence we fake it ‘till we make it, all deprecations internally are warnings. local brackets = old:sub(1,1) == '\\' and "" or "()" - local _semver = SILE.version and SILE.version:match("v([0-9]*.[0-9]*.[0-9]*)") or warnat local _new = new and "Please use " .. (new .. brackets) .. " instead." or "Plase don't use it." - local msg = (old .. brackets) .. " was deprecated in SILE v" .. warnat .. ". " .. _new .. (extra and "\n" .. extra .. "\n\n" or "") - if errorat and _semver >= errorat then + local msg = (old .. brackets) .. " was deprecated in SILE v" .. tostring(warnat) .. ". " .. _new .. (extra and "\n" .. extra .. "\n\n" or "") + if errorat and current >= errorat then SU.error(msg) - elseif warnat and _semver >= warnat then + elseif warnat and current >= warnat then SU.warn(msg) end end diff --git a/documentation/c01-whatis.sil b/documentation/c01-whatis.sil index f892b9536..b89e603fb 100644 --- a/documentation/c01-whatis.sil +++ b/documentation/c01-whatis.sil @@ -19,7 +19,7 @@ There are several important differences. The job of a word processor is to produce a document that looks exactly like what you type on the screen. By contrast, the job of a typesetting system is to take raw content and produce a document that looks as good as possible. The input for SILE is a text document that includes instructions about how the content should be laid out on a page. -In order to obtain the typeset result, the input file must be \em{processed} to render the desired output. +In order to obtain the typeset result, the input file(s) must be \em{processed} to render the desired output. Word processors often describe themselves as WYSIWYG: What You See Is What You Get. SILE is cheerfully \em{not} WYSIWYG. @@ -115,5 +115,5 @@ In the final chapters of this book, we’ll look at some extended examples of cr SILE\footnote{In case you’re wondering, the author pronounces it \font[family=Gentium Plus]{/saɪəl/}, to rhyme with “trial”.} takes some textual instructions and turns them into PDF output. It has features inspired by TeX and InDesign, but seeks to be more flexible, extensible and programmable than either of them. -It’s useful both for typesetting documents (such as this very documentation) written in the SILE language, and as a processing system for styling and outputting structured data. +It’s useful for typesetting structured content whether they are documents written in the SIL input syntax (such as this very documentation), XML, or in some other structured data syntax that needs styling and outputting. \end{document} diff --git a/documentation/c02-gettingstarted.sil b/documentation/c02-gettingstarted.sil index dd5ac1399..255c7d436 100644 --- a/documentation/c02-gettingstarted.sil +++ b/documentation/c02-gettingstarted.sil @@ -27,6 +27,8 @@ For comparisons of editors see \url{https://alternativeto.net/category/developer \section{A basic SILE document} Once you have an editor, it’s time to consider a SILE input file. +There are several different input formats that could be used including SIL and XML. +For the purpose of this documentation we'll mostly use the SIL input format, but all examples translate 1-to-1 to XML. To begin with, here’s the most basic SILE file of all: \begin[type=autodoc:codeblock]{raw} @@ -254,7 +256,7 @@ This can be done with: Now the \code{sile} command will be available from any directory. \begin{autodoc:note} -If you wish you, can skip the install step and use the compiled SILE executable diretly from the source directory. +If you wish you, can skip the install step and use the compiled SILE executable directly from the source directory. As configured above, this will only work from a shell with the CWD set to the SILE source. To make it usable from anywhere, you can configure it with the source directory baked in as the installation location. @@ -298,7 +300,7 @@ All the available CLI options are documented both in the help output (\code{sile This manual will only mention a few in passing as they come up in other other topics. \begin{autodoc:note} -SILE output filenames are generated by replacing the extension from the master input filename with the proper extension for the outputter. +SILE generates output filenames by replacing the extension from the first input filename with the default extension for the outputter. For most outputters this will be \code{.pdf} but, for example, the text backend will append \code{.txt} instead. If you want to write to a different filename altogether, use the \code{--output file.pdf} command line option. You can use \code{--output -} to write the output directly to the system IO stream—useful if you wish to use SILE as part of a pipeline. @@ -308,9 +310,9 @@ You can use \code{--output -} to write the output directly to the system IO stre In \url{https://sile-typesetter.org/examples/docbook.xml}, you will find a typical DocBook 5.0 article. Normally turning DocBook to print involves a complicated dance of XSLT processors, format object processors, and/or strange LaTeX packages. -But SILE can read XML files directly, and comes with a \code{docbook} class, which tells SILE how to render (admittedly, a subset of) the DocBook tags onto a page. +But SILE can read XML files directly, and comes with a \autodoc:class{docbook} class, which tells SILE how to render (admittedly, a subset of) the DocBook tags onto a page. -Hence, turning \code{dockbook.xml} into \code{docbook.pdf} is as simple as: +Hence, turning \code{docbook.xml} into \code{docbook.pdf} is as simple as: \begin{terminal} $ sile --class docbook docbook.xml @@ -320,9 +322,9 @@ Loading docbook \end{terminal} The \code{-c} flag sets the default class, a necessary step because DocBook XML files do not come wrapped in a tag that specifies a SILE class. -The \code{docbook} class will provide the commands necessary to process the tags typically found in DocBook files. +The \autodoc:class{docbook} class will provide the commands necessary to process the tags typically found in DocBook files. -In Chapter 9, we’ll look at how the \code{docbook} class works, and how you can define processing expectations for other XML formats. +In Chapter 9, we’ll look at how the \autodoc:class{docbook} class works, and how you can define processing expectations for other XML formats. \section{Running SILE remotely as a CI job} diff --git a/documentation/c03-input.sil b/documentation/c03-input.sil index 3fee528e4..3151e7f94 100644 --- a/documentation/c03-input.sil +++ b/documentation/c03-input.sil @@ -1,7 +1,29 @@ \begin{document} -\chapter{SILE’s Input Language} +\chapter{SILE’s Input} -Let’s now go back and reconsider the first SILE file we saw: +First, we need to clear some air. +In our own earlier documentation we gave the impression the primary way to feed content into SILE was a propriatry language. +This was \em{never} really true since XML was always fully supported as an alternative. +It is even less true now that 3rd party plugins can add their own input formats. + +Hence this chanpter has been renamed. +The original chapter title was "SILE’s Input Language", as if there was only one. +The truth is there \em{is} an input syntax we call "SIL", but even that is perhaps best thought of as a structured data syntax rather than a unique language. +The input strings \code{\\em\{foo\}} in SIL input syntax is 100\% equivalent to \code{foo} in XML input syntax. +The SIL input syntax is provided as an easier to type alternative than XML which can be a bit verbose and tedious to work with by hand. +On the other hand if you're handling data written by some other program, XML might be a much better solution. + +3rd party packages can also add their own input formats. +The stipulation is that an inputter parses some content and returns an AST with content tags and options recognized by SILE. +Markdown, Djot, and others have packages available that provide inputters, allowing them to be first class input candidates. +Many others like Dockbook and TEI are just XML with a special tag schema. +SILE can be extended to cover any tag schema. + +With that in mind, lets go back and reconsider the first SILE file we saw that was in SIL input syntax. + +\section{The SIL flavor} + +In the Getting Started chapter we looked at this document: \begin[type=autodoc:codeblock]{raw} \begin{document} @@ -39,6 +61,14 @@ Once some of the basic document properties have been set up using these fixed si For example, once the paper size is set, percentage of page width (\code{\%pw}) and height(\code{\%ph}) become valid units. In Chapter 4 we will meet more of these relative units, and in Chapter 7 we will meet some other ways of specifying lengths to make them stretchable or shrinkable. +\subsection{Setting orientation as landscape} + +The orientation of the page is defined as "portrait" by default, but if you want to set it as landscape there is an option for that: + +\begin[type=autodoc:codeblock]{raw} +\begin[landscape=true]{document} +\end{raw} + \section{Ordinary text} On the whole, ordinary text isn’t particularly interesting—it’s just typeset. @@ -120,7 +150,7 @@ So if you type \code{affluent fishing}, then, depending on your font, your outpu ‘\autodoc:example{affluent fishing}’. If you specifically want to break up the ligatures, insert empty groups (using the grouping characters \code{\{} and \code{\}}) in the middle of the possible ligatures: \code{af\{\}f\{\}luent f\{\}ishing}: ‘\autodoc:example{af{}f{}luent f{}ishing}’. -See the section on the \code{features} package for more information on how to control the display of ligatures and other font features. +See the section on the \autodoc:package{features} package for more information on how to control the display of ligatures and other font features. \section{Commands} @@ -181,8 +211,8 @@ However, in some cases the environment form of the command will be easier to rea \section{The XML flavor} -While we’re on the subject of alternative forms, SILE can actually process its input in a completely different file format. -What we’ve seen so far has been SILE’s “TeX-like flavor,” but it can also directly read and process XML files. +As mentioned at the start of the chapter, SILE can actually process its input in a completely different file format. +What we’ve seen so far has been SILE’s “TeX-like” SIL syntax flavor, but it can also directly read and process XML files. (If it isn’t well-formed XML, then SILE will get very upset.) Any XML tags within the input file will then be regarded as SILE commands, and tag attributes are interpreted as command parameters. @@ -203,7 +233,7 @@ The example above, in XML flavor, would look like this: Hi there! \end{raw} -We don’t expect humans to write their documents in SILE’s XML flavor—the TeX-like SILE flavor is much better for that—but having an XML flavor allows for computers to deal with SILE a lot more easily. +We don’t expect humans to write their documents in SILE’s XML flavor—the TeX-like SIL flavor is much better for that—but having an XML flavor allows for computers to deal with SILE a lot more easily. One could create graphical user interfaces to edit SILE documents, or convert other XML formats to SILE. However, there is an even smarter way of processing XML with SILE. diff --git a/documentation/c04-useful.sil b/documentation/c04-useful.sil index 16c18409a..c4ffacd3c 100644 --- a/documentation/c04-useful.sil +++ b/documentation/c04-useful.sil @@ -83,7 +83,7 @@ The full list of attributes to the \autodoc:command{\font} command are: It’s quite fiddly to be always changing font specifications manually; later we’ll see some ways to automate the process. -SILE’s \code{plain} class notably provides the \autodoc:command{\em{…}} command as a shortcut for \autodoc:command{\font[style=italic]{…}}, and the \autodoc:command{\strong{…}} command as a a shortcut for \autodoc:command{\font[weight=700]{…}}. +SILE’s \autodoc:class{plain} class notably provides the \autodoc:command{\em{…}} command as a shortcut for \autodoc:command{\font[style=italic]{…}}, and the \autodoc:command{\strong{…}} command as a a shortcut for \autodoc:command{\font[weight=700]{…}}. Note for parameters that accept multiple values, values may be separated with commas. Be sure to wrap the value in quotes so the commas don't get parsed as new parameters. @@ -94,22 +94,22 @@ For example \autodoc:command{\font[variations="wght=150,wdth=122"]} can be used \section{Document structure} SILE provides a number of different \em{class}es of document (similar to LaTeX classes). -By default, you get the \code{plain} class, which has very little support for structured documents. -There is also the \code{book} class, which adds support for right and left page masters, running headers, footnotes, and chapter, section and subsection headings. +By default, you get the \autodoc:class{plain} class, which has very little support for structured documents. +There is also the \autodoc:class{book} class, which adds support for right and left page masters, running headers, footnotes, and chapter, section and subsection headings. -To use the commands in this section, you will need to request the \code{book} class by specifying, in your \code{\\begin\{document\}} command, the \autodoc:parameter{class=book} parameter; +To use the commands in this section, you will need to request the \autodoc:class{book} class by specifying, in your \code{\\begin\{document\}} command, the \autodoc:parameter{class=book} parameter; for example, the document you are currently reading begins with the command \code{\\begin[class=book]\{document\}}. \subsection{Chapters and sections} -If you choose the book class, you can divide your document into different sections using the commands \autodoc:command{\chapter{…}}, \autodoc:command{\section{…}}, and \autodoc:command{\subsection{…}}. +If you choose the \autodoc:class{book} class, you can divide your document into different sections using the commands \autodoc:command{\chapter{…}}, \autodoc:command{\section{…}}, and \autodoc:command{\subsection{…}}. The argument to each command is the name of the chapter or section, respectively. Chapters will be opened on a new right-hand page, and the chapter name will form the left running header. Additionally, the section name and number will form the right running header. \begin{autodoc:note} Chapters, sections and subsections will be automatically numbered starting from 1. -To alter the numbering, see the documentation for the \code{counters} package in the next chapter. +To alter the numbering, see the documentation for the \autodoc:package{counters} package in the next chapter. To produce an unnumbered chapter, provide the parameter \autodoc:parameter{numbering=false}. \end{autodoc:note} @@ -122,7 +122,7 @@ Footnotes can be added to a book with the \autodoc:command{\footnote{…}} comma The argument to the command will be set as a footnote at the bottom of the page. Footnotes are automatically numbered from 1 at the start of each chapter. -\section{Indentation and spacing} +\section{Paragraph indentation} Paragraphs in SILE normally begin with an indentation (by default, 20 points in width). To turn this off, you can use the \autodoc:command{\noindent} command at the start of a paragraph. @@ -131,16 +131,40 @@ A \autodoc:command{\noindent} can be cancelled by following it with an \autodoc: You can completely turn off indentation for the whole of the document by changing its size to zero. We’ll see how to change the size of the indentation in the settings chapter, but the easiest way to set it to zero for the whole of the document (rather than for just one paragraph) is to issue the command \autodoc:command{\neverindent}. -To increase the vertical space between paragraphs or other elements, the commands \autodoc:command{\smallskip}, \autodoc:command{\medskip} and \autodoc:command{\bigskip} are available to add a 3pt, 6pt, and 12pt gap, respectively. -There will be a \autodoc:command{\bigskip} after this paragraph. +\section{Horizontal spacing} -\bigskip% There are also commands to increase the horizontal space in a line; - from the smallest to the largest, \autodoc:command{\thinspace} (1/6th of an em), \autodoc:command{\enspace} (1 en), \autodoc:command{\quad} (1 em), and \autodoc:command{\quad} (2em). + from the smallest to the largest, \autodoc:command{\thinspace} (1/6th of an em), \autodoc:command{\enspace} (1 en), \autodoc:command{\quad} (1 em), and \autodoc:command{\qquad} (2em). If you want to add a very long stretchy space, you can use the command \autodoc:command{\hfill}. Doing this in conjunction with a line break will cause the line before the break to be flush left, like this.\cr{}The command \autodoc:command{\cr} is a shortcut for \autodoc:command{\hfill\break}. +\section{Vertical spacing} + +To increase the vertical space between paragraphs or other elements, the commands \autodoc:command{\smallskip}, \autodoc:command{\medskip} and \autodoc:command{\bigskip} are available to add a 3pt, 6pt, and 12pt gap, respectively. +There will be a \autodoc:command{\bigskip} after this paragraph. + +\bigskip% +Besides this predefined skips, you can also use \autodoc:command{\skip[height=]} to add a vertical space of a given height. + +If you want to add a very long stretchy vertical space, you can use the command \autodoc:command{\vfill}. + +When playing with vertical spaces, there is however a few additional considerations to take into account. +Without entering into the details, they are usually ignored at the beginning of a frame. +Would you want to enforce them there, you therefore need to have some initial content. +An empty \autodoc:command{\hbox} can do the trick. +Additionally, there are cases where SILE automaticall inserts a \autodoc:command{\vfill} command at the end of a frame, so you may need to ensure you terminated a paragraph and introduced your own frame break in order to avoid it. +The following example illustrates both techniques. + +\begin[type=autodoc:codeblock]{raw} +\hbox{}% This is an empty initial line +\skip[height=2cm] +A paragraph around 2 centimeters below the top of the frame. +\vfill +A paragraph pushed at the bottom of the frame.\par +\break +\end{raw} + \section{Text alignment} \begin{raggedright} @@ -168,7 +192,7 @@ This paragraph is centered on the page. SILE automatically determines line and page breaks. In later chapters we will introduce some \em{settings} which can be used to tweak this process. -However, SILE’s \code{plain} class also provides some commands to help the process on its way. +However, SILE’s \autodoc:class{plain} class also provides some commands to help the process on its way. The following four commands can be used to control line breaks (when used \em{within} a paragraph), as well as page breaks (when used \em{between} paragraphs):% \footnote{The names are similar to those used in (La)TeX, but their semantics differ slightly.} diff --git a/documentation/c05-packages.sil b/documentation/c05-packages.sil index 01c048b1e..b3f6089a4 100644 --- a/documentation/c05-packages.sil +++ b/documentation/c05-packages.sil @@ -1,253 +1,365 @@ \begin{document} \chapter{SILE Packages} -SILE comes with a number of packages which provide additional functionality. -In fact, the actual “core” of SILE’s functionality is small and -extensible, with most of the interesting features being provided by add-on -packages. SILE packages are written in the Lua programming language, and can -define new commands, change the way that the SILE system operates, or indeed -do anything that is possible to do in Lua. - -As mentioned above, loading a package is done through the \code{\\script} command, -which runs Lua code. By convention packages live in the \code{packages/} subdirectory -of either your input file’s location, your current working directory, or SILE’s -installation directory. For instance, we’ll soon be talking about the -\code{grid} package, which normally can be found as -\code{/usr/local/lib/sile/packages/grid/init.lua}. To load this, we’d say: +SILE comes with a number of standard packages which provide additional functionality. +In fact, the actual “core” of SILE’s functionality is small and extensible, with most of the interesting features being provided by add-on packages. +SILE ships with the core libraries plus a small collection of packages covering some common needs; more can be added from 3rd party sources. +SILE packages are written in the Lua programming language, and can define new commands, change the way that the SILE system operates, or indeed do anything that is possible to do in Lua. + +\section{Loading a package} + +Loading a package is done through the \code{\\use} command. +By convention packages live in a \code{packages/} +For instance, we’ll soon be talking about the \autodoc:package{grid} package, which normally can be found as \code{sile/packages/grid/init.lua} in wherever your system installed the SILE resource files. +To load this, we’d say: \begin[type=autodoc:codeblock]{raw} \use[module=packages.grid] \end{raw} -\autodoc:note{SILE searches for paths in a variety of -directories: first, in the directory in which your input file is located, -then the current working directory; next, if the environment variable -\code{SILE_PATH} is set, it will look in that directory; then it will look in -the standard installation directories \code{/usr/lib/sile} and -\code{/usr/local/lib/sile}. Unlike TeX, it does not descend into subdirectories -when looking for a file; if you have arranged your personal macro, class -or package files into subdirectories, you will need to provide a full relative -path to them.} - -\section{image} +\autodoc:note{ +By default SILE will look for packages in a variety of directories: + +\begin{enumerate} +\item{The directory where your input source file is located.} +\item{The current working directory.} +\item{The environment variable \code{SILE_PATH}, if defined.} +\item{The default Lua search path.} +\item{Various directories depending on where and how SILE is installed on your system.} +\end{enumerate} + +SILE does not descend into subdirectories when looking for a file. +If you have arranged your personal class or package files into subdirectories, +you will need to provide a full relative path to them.} + +\section{The SILE ecosystem} + +The SILE installation includes a core collection of modules we hope are generally useful. +But there’s more out there! +As mentioned earlier in this manual, a number of third-party contributed collections of modules can be installed via the LuaRocks package manager. + +\autodoc:note{ +A non-authoritative list of third-party modules may be consulted at \url{https://luarocks.org/m/sile}. To publish your own modules to LuaRocks, see the \code{package-template.sile} repository. +} + +A SILE compatible LuaRock simply installs the relevant class, package, language, internationalization resources, or similar files in a \code{sile} directory. +This directory could be in your system Lua directory, in your user directory, or any other location you specify. + +By default, LuaRocks will install these modules to the Lua search path. + +\begin[type=autodoc:codeblock]{raw} +$ luarocks install markdown.sile +$ sile ... +\end{raw} + +Depending on your system, this probably requires root permissions. +If you either don’t have root permissions or don’t want to pollute your system’s root file system, you can also install as a user. +To use packages installed as a user you will need to have LuaRocks add its user tree to your Lua search path before running SILE. + +\begin[type=autodoc:codeblock]{raw} +$ luarocks --local install markdown.sile +$ eval $(luarocks --local path) +$ sile ... +\end{raw} + +Of course, you can add that eval statement to your shell profile to always include your user directory in your Lua path. +You can also add your own entries to the top of the search path list by setting the \code{SILE_PATH} variable. For example: + +\begin[type=autodoc:codeblock]{raw} +$ export SILE_PATH="/path/to/my/library/" +$ sile ... +\end{raw} + +Note that modules are not limited to just packages. +They can include classes, languages, internationalization resources, or anything else provided by SILE.\footnote{% +Also because external locations are searched before SILE itself, they can even override any core part of SILE itself. +As such you should probably make sure you review what a package does before installing it!} + +\use[module=packages.unichar] +\define[command=status:high]{\font[size=9pt]{\color[color=green]{\font[family=Symbola]{\unichar{U+25CF}} Good maturity}}\novbreak\noindent} +\define[command=status:medium]{\font[size=9pt]{\color[color=olivedrab]{\font[family=Symbola]{\unichar{U+25CD}} Usable with limitations}}\novbreak\noindent} +\define[command=status:low]{\font[size=9pt]{\color[color=darkslateblue]{\font[family=Symbola]{\unichar{U+25CB}} Experimental}}\novbreak\noindent} + +\section{Graphics} + +As well as processing text, SILE can also include images. + +\subsection{image} +\status:high \package-documentation{image} -\section{folio} -\package-documentation{folio} +\subsection{svg} +\status:medium +\package-documentation{svg} -\section{rules} -\package-documentation{rules} +\subsection{converters} +\status:medium +\package-documentation{converters} + +\section{Text & Characters} + +This section covers a range of different topics from initial capitals to text transforms, through URL formatting. + +\subsection{dropcaps} +\status:high +\package-documentation{dropcaps} + +\subsection{lorem} +\status:high +\package-documentation{lorem} + +\subsection{textcase} +\status:high +\package-documentation{textcase} -\section{color} +\subsection{unichar} +\status:high +\package-documentation{unichar} + +\subsection{url} +\status:medium +\package-documentation{url} + +\subsection{gutenberg} +\status:low +\package-documentation{gutenberg} + +\section{Colors} + +Color perception is a complicated topic, depending on many factors. +SILE currently provides a few packages for handling coloring, in a simple acceptation of the term. + +\subsection{color} +\status:medium \package-documentation{color} -\section{background} +\subsection{background} +\status:medium \package-documentation{background} -\section{rotate} -\package-documentation{rotate} +\section{Fillers & Rules} -\section{features} -\package-documentation{features} +Line-filling patterns or rules, rectangular blobs of inks... What else to say? -\section{unichar} -\package-documentation{unichar} +\subsection{leaders} +\status:high +\package-documentation{leaders} -\section{bidi} -\package-documentation{bidi} +\subsection{rules} +\status:medium +\package-documentation{rules} -\section{pullquote} -\package-documentation{pullquote} +\section{Boxes & Effects} -\section{raiselower} +You can manipulate boxed elements to achieve a variety of effects. + +\subsection{raiselower} +\status:high \package-documentation{raiselower} -\section{grid} -\package-documentation{grid} +\subsection{rebox} +\status:high +\package-documentation{rebox} -\section{linespacing} -\package-documentation{linespacing} +\subsection{rotate} +\status:medium +\package-documentation{rotate} + +\subsection{scalebox} +\status:high +\package-documentation{scalebox} + +\section{Mathematical formulas} +\status:medium +\package-documentation{math} + +\section{Specialized environments} -\section{verbatim} +SILE’s standard set of packages provides a few high-level environment. +Some are quite expected from a typesetting system, and other also possibly serve as an illustration for class and package designers, regarding how to use varying techniques. + +\subsection{lists} +\status:high +\package-documentation{lists} + +\subsection{pullquote} +\status:medium +\package-documentation{pullquote} + +\subsection{verbatim} +\status:medium \package-documentation{verbatim} -\section{font-fallback} -\package-documentation{font-fallback} +\subsection{specimen} +\status:high +\package-documentation{specimen} -\section{boustrophedon} +\subsection{boustrophedon} +\status:high \package-documentation{boustrophedon} -\section{chordmode} +\subsection{chordmode} +\status:high \package-documentation{chordmode} -\section{converters} -\package-documentation{converters} +\section{Advanced font features} -\section{cropmarks} -\package-documentation{cropmarks} +The following packages leverage SILE’s font default handling and the \autodoc:command{\font} command with new capabilities. -\section{date} -\package-documentation{date} - -\section{debug} -\package-documentation{debug} +\subsection{features} +\status:high +\package-documentation{features} -\section{dropcaps} -\package-documentation{dropcaps} +\subsection{font-fallback} +\status:medium +\package-documentation{font-fallback} -\section{leaders} -\package-documentation{leaders} +\section{Advanced line-spacing} -\section{lorem} -\package-documentation{lorem} +We will later document the default line-spacing algorithm used by SILE and the available settings that may be tuned. +Still, some packages are proposed for \em{altering} that algorithm and may be useful in some contexts. -\section{specimen} -\package-documentation{specimen} +\subsection{grid} +\status:medium +\package-documentation{grid} -\section{textcase} -\package-documentation{textcase} +\subsection{linespacing} +\status:low +\package-documentation{linespacing} -\section{url} -\package-documentation{url} +\section{Document parts} -\section{Packages usually used by other packages} +You \em{probably} don’t need to load the auxiliary packages in this section directly. +Their main job is to provide more basic functionality to other packages and classes. +Classes compose functionality from different auxiliary packages. +Nevertheless, these packages also provide several user-facing commands of interest. -In addition, there are certain packages that you \em{probably} don’t need -to use directly, as their main job is to provide more -basic functionality to other packages and classes. Classes such as the -\code{book} class compose functionality from different auxiliary packages. +\subsection{folio} +\status:high +\package-documentation{folio} \subsection{footnotes} +\status:medium \package-documentation{footnotes} -\subsection{color-fonts} -\package-documentation{color-fonts} +\subsection{tableofcontents} +\status:medium +\package-documentation{tableofcontents} -\subsection{counters} -\package-documentation{counters} +\section{Bibliographies & Indexes} -\subsection{pdf} -\package-documentation{pdf} +This section is devoted to packages collating references, in a broad sense. + +\subsection{bibtex} +\status:medium +\package-documentation{bibtex} + +\subsection{indexer} +\status:low +\package-documentation{indexer} + +\section{Miscellaneous utilities} + +This section introduces packages that could not fit in another category. + +\subsection{date} +\status:high +\package-documentation{date} + +\subsection{debug} +\status:high +\package-documentation{debug} \subsection{ifattop} +\status:low \package-documentation{ifattop} +\section{Frames and page layouts} + +As we mentioned in the first chapter, SILE uses frames as an indication of where to put text onto +the page. + +\subsection{cropmarks} +\status:low +\package-documentation{cropmarks} + \subsection{frametricks} +\status:low \package-documentation{frametricks} -\subsection{insertions} -\package-documentation{insertions} - \subsection{twoside} +\status:medium \package-documentation{twoside} \subsection{masters} +\status:high \package-documentation{masters} -\subsection{infonode} -\package-documentation{infonode} - -\subsection{inputfilter} -\package-documentation{inputfilter} - \subsection{break-firstfit} +\status:medium \package-documentation{break-firstfit} -\subsection{chapterverse} -\package-documentation{chapterverse} - -\subsection{parallel} -\package-documentation{parallel} - -\subsection{rebox} -\package-documentation{rebox} - -\subsection{scalebox} -\package-documentation{scalebox} - -\subsection{tableofcontents} -\package-documentation{tableofcontents} - -\subsection{xmltricks} -\package-documentation{xmltricks} - -\section{Experimental packages} - -\subsection{autodoc} -\package-documentation{autodoc} - \subsection{balanced-frames} +\status:low \package-documentation{balanced-frames} -\subsection{bibtex} -\package-documentation{bibtex} +\section{Low-level internal packages} -\subsection{gutenberg} -\package-documentation{gutenberg} +In addition, there are packages that you \em{very probably} don’t need to use directly when typesetting documents. -\subsection{indexer} -\package-documentation{indexer} - -\subsection{lists} -\package-documentation{lists} - -\subsection{math} -\package-documentation{math} - -\subsection{pdfstructure} -\package-documentation{pdfstructure} +\subsection{bidi} +\status:high +\package-documentation{bidi} -\subsection{svg} -\package-documentation{svg} +\subsection{color-fonts} +\status:high +\package-documentation{color-fonts} -\section{The package manager} +\subsection{counters} +\status:high +\package-documentation{counters} -The SILE installation includes a core collection of packages we hope are generally useful. -But there’s more out there! -SILE can use packages installed via the LuaRocks package manager. -It can also be configured to read packages from any directory of your choosing. +\subsection{insertions} +\status:medium +\package-documentation{insertions} -A SILE compatible LuaRock simply installs the relevant class, package, language, internationalization resources, or similar files in a \code{sile} directory. -This directory could be in your system Lua directory, in your user directory, or any other location you specify. +\subsection{infonode} +\status:high +\package-documentation{infonode} -By default SILE will look for packages in: +\subsection{inputfilter} +\status:high +\package-documentation{inputfilter} -\begin{enumerate} -\item{The directory where the SILE source file is located.} -\item{The current working directory.} -\item{The default Lua search path.} -\item{The directory where SILE is installed on the system.} -\end{enumerate} +\subsection{chapterverse} +\status:low +\package-documentation{chapterverse} -By default, LuaRocks will install packages to the Lua search path. +\subsection{parallel} +\status:low +\package-documentation{parallel} -\begin[type=autodoc:codeblock]{raw} -$ luarocks install lilypond.sile -$ sile ... -\end{raw} +\subsection{autodoc} +\status:high +\package-documentation{autodoc} -Depending on your system, this probably requires root permissions. -If you either don’t have root permissions or don’t want to pollute your system’s root file system, you can also install as a user. -To use packages installed as a user you will need to have LuaRocks add its user tree to your Lua search path before running SILE. +\subsection{pdf} +\status:medium +\package-documentation{pdf} -\begin[type=autodoc:codeblock]{raw} -$ luarocks --local install lilypond.sile -$ eval $(luarocks --local path) -$ sile ... -\end{raw} +\subsection{pdfstructure} +\status:medium +\package-documentation{pdfstructure} -Of course, you can add that eval statement to your shell profile to always include your user directory in your Lua path. -You can also add your own entries to the top of the search path list by setting the \code{SILE_PATH} variable. For example: +\section{Highly experimental packages} -\begin[type=autodoc:codeblock]{raw} -$ export SILE_PATH="/path/to/my/library/" -$ sile ... -\end{raw} +The following packages are not documented here: +\autodoc:package{complex-spaces}, +\autodoc:package{pagebuilder-bestfit}, +\autodoc:package{pandoc}, +\autodoc:package{simpletable}, +\autodoc:package{xmltricks}. -To write a package and publish to LuaRocks, see the \code{package-template.sile} repository. +\autodoc:note{These packages are not ready for use in production and are subject to change without notice in future versions.} -Packages are not limited to just the package interface. -They can include classes, languages, internationalization resources, or anything else provided by SILE. -Also because external locations are searched before SILE itself, they can even override any core part of SILE itself. -As such you should probably make sure you review what a package does before installing it! \end{document} diff --git a/documentation/c06-macroscommands.sil b/documentation/c06-macroscommands.sil index 879372b4d..4cab133df 100644 --- a/documentation/c06-macroscommands.sil +++ b/documentation/c06-macroscommands.sil @@ -17,7 +17,7 @@ that you need to keep entering again and again. \define[command=SILE]{\font[family=Gentium Plus]{% Book Basic has no +smcp, but readers don't need to know, since we're only using Book Basic as a holdover from old SILE which did. S\lower[height=0.5ex]{I}L\kern[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}} For instance, let’s suppose that we want to design a nice little -“bumpy road” logo for SILE. (Afficionados of T\kern[width=-.1667em]\lower[height=0.5ex]{E}\kern[width=-.125em]X and friends will be familiar with the concept of +“bumpy road” logo for SILE. (Aficionados of T\kern[width=-.1667em]\lower[height=0.5ex]{E}\kern[width=-.125em]X and friends will be familiar with the concept of bumpy road logos.) Our logo will look like this: \SILE. It’s not a great logo, but we’ll use it as \SILE’s logo for the purposes of this section. @@ -25,7 +25,7 @@ To typeset this logo, we need to ask \SILE to: typeset an ‘S’; typeset an ‘I’ lowered by a certain amount (half an ex, as it happens); typeset an ‘L’; walk backwards along the line a tiny bit; typeset a smaller-sized ‘E’ raised by a certain -amount, using the \code{features} package to choose a small capital ‘E’. +amount, using the \autodoc:package{features} package to choose a small capital ‘E’. In \SILE code, that looks like: @@ -65,7 +65,7 @@ L% \end{raw} We are using the built-in SILE command \autodoc:command{\define}. -\autodoc:command{\define} takes an option called \code{command}; +\autodoc:command{\define} takes an option called \autodoc:parameter{command}; its value is the name of the command we are defining. The content of the \autodoc:command{\define} command is a series of SILE instructions to be executed when the command is used. @@ -92,7 +92,7 @@ of any text up until the nearest comma, semicolon, or closing square bracket. Now let’s move on to the next level. Sometimes you will want to create commands which are not simply replacements, but which have arguments of their -own. As an example, let’s say we use the \code{color} package to turn a bit of +own. As an example, let’s say we use the \autodoc:package{color} package to turn a bit of text red \color[color=red]{like this}. The usual way to do that is to say \begin[type=autodoc:codeblock]{raw} diff --git a/documentation/c07-settings.sil b/documentation/c07-settings.sil index 4c01506f5..78775ad64 100644 --- a/documentation/c07-settings.sil +++ b/documentation/c07-settings.sil @@ -256,7 +256,7 @@ Now we can finally complete our implementation of centering: \set[parameter=typesetter.parfillskip,value=0pt] And this is (more or less) how the \autodoc:environment{center} environment is defined in -the \code{plain} class: we make the margins able to expand but the spaces not able +the \autodoc:class{plain} class: we make the margins able to expand but the spaces not able to expand; we turn off indenting at the start of the paragraph, and we turn off the filling glue at the end of the paragraph. \par diff --git a/documentation/c08-language.sil b/documentation/c08-language.sil index ebbb74fb7..bf42557d0 100644 --- a/documentation/c08-language.sil +++ b/documentation/c08-language.sil @@ -25,7 +25,7 @@ Language support may include: \item{frame advance and writing direction} \item{spacing} \item{choice of glyphs within a font} -\item{localization of programatically inserted strings} +\item{localization of programmatically inserted strings} \end{itemize} For example, Sindhi and Urdu users will expect the Arabic letter \em{heh} (\font[family=LateefGR]{ه}) to combine with other letters in different ways to standard Arabic shaping. @@ -50,31 +50,19 @@ then in Urdu: \font[family=LateefGR,language=urd]{ههه}. \section{Direction} -SILE is written to be \em{direction-agnostic,} which means that it has -no fixed idea about which way text should flow on a page. Latin scripts -are generally written left-to-right with individual lines starting from -the top of the page and advancing towards the bottom. Japanese can be -written in the same way, but traditionally is typeset down the page with -lines of text moving from the right of the page to the left. - -To describe this, SILE uses the concept of a \em{writing direction,} -which denotes the way each individual line appears on the page—left -to right for Latin scripts, right to left for Arabic, Hebrew and so on, -top to bottom for traditional Japanese—and a \em{page advance direction,} -which denotes the way the lines “stack up”. Each of these directions can -take one of four values: \code{LTR}, \code{RTL}, \code{TTB}, or \code{BTT}. -A \em{direction specification} is made up of either a writing direction -(\code{LTR} or \code{RTL}), in which case the page advance direction is -understood to be \code{TTB}, or a writing direction and a page advance -direction joined by a hyphen. - -Each frame has its own writing direction. By default, this is \code{LTR-TTB}. -Normally you would set the writing direction once, in the master frames of -your document class. One easy way to do this in the \code{plain} document -class is to pass the \autodoc:parameter{direction} parameter to the -\code{\\begin\{document\}} command. For example, Mongolian is written top -to bottom with text lines moving from the left to the right of the page, -so to create a Mongolian document, use: +SILE is written to be \em{direction-agnostic,} which means that it has no fixed idea about which way text should flow on a page. +Latin scripts are generally written left-to-right with individual lines starting from the top of the page and advancing towards the bottom. +Japanese can be written in the same way, but traditionally is typeset down the page with lines of text moving from the right of the page to the left. + +To describe this, SILE uses the concept of a \em{writing direction,} which denotes the way each individual line appears on the page—left to right for Latin scripts, right to left for Arabic, Hebrew and so on, top to bottom for traditional Japanese—and a \em{page advance direction,} which denotes the way the lines “stack up”. +Each of these directions can take one of four values: \code{LTR}, \code{RTL}, \code{TTB}, or \code{BTT}. +A \em{direction specification} is made up of either a writing direction (\code{LTR} or \code{RTL}), in which case the page advance direction is understood to be \code{TTB}, or a writing direction and a page advance direction joined by a hyphen. + +Each frame has its own writing direction. +By default, this is \code{LTR-TTB}. +Normally you would set the writing direction once, in the master frames of your document class. +One easy way to do this in the \autodoc:class{plain} document class is to pass the \autodoc:parameter{direction} parameter to the \code{\\begin\{document\}} command. +For example, Mongolian is written top to bottom with text lines moving from the left to the right of the page, so to create a Mongolian document, use: \begin[type=autodoc:codeblock]{raw} \begin[direction=TTB-LTR]{document} @@ -83,40 +71,32 @@ so to create a Mongolian document, use: \end{document} \end{raw} -To change the writing direction for a single frame, use -\autodoc:command{\thisframedirection[direction=]}. +To change the writing direction for a single frame, use \autodoc:command{\thisframedirection[direction=]}. -SILE uses the Unicode bidirectional algorithm to handle texts written in -mixed directionalities. See \url{https://sile-typesetter.org/examples/i18n.sil} -for an example which brings together multiple scripts and directionalities. +SILE uses the Unicode bidirectional algorithm to handle texts written in mixed directionalities. +See \url{https://sile-typesetter.org/examples/i18n.sil} for an example which brings together multiple scripts and directionalities. \section{Hyphenation} -SILE hyphenates words based on its current language. (Language is set using the -\autodoc:command{\font} command above.) SILE comes with support for hyphenating a wide -variety of languages, and also aims to encode specific typesetting knowledge about +SILE hyphenates words based on its current language. +(Language is set using the \autodoc:command{\font} command above.) +SILE comes with support for hyphenating a wide variety of languages, and also aims to encode specific typesetting knowledge about languages. -The default hyphen character is “-”, which can be tweaked by the \autodoc:command{\font} -parameter \autodoc:parameter{hyphenchar}. It accepts a Unicode character or Unicode codepoint -in \code{[Uu]+} or Hexadecimal \code{0[Xx]} format—for instance, -\autodoc:command{\font[family=Rachana,language=ml,hyphenchar=U+200C]}. +The default hyphen character is “-”, which can be tweaked by the \autodoc:command{\font} parameter \autodoc:parameter{hyphenchar}. +It accepts a Unicode character or Unicode codepoint in \code{[Uu]+} or Hexadecimal \code{0[Xx]} format—for instance, \autodoc:command{\font[family=Rachana,language=ml,hyphenchar=U+200C]}. -SILE comes with a special “language” called \code{und}, which has no hyphenation -patterns available. If you switch to this language, text will not be hyphenated. -The command \autodoc:command{\nohyphenation{…}} is provided as a shortcut for -\autodoc:command{\font[language=und]{…}}. +SILE comes with a special “language” called \code{und}, which has no hyphenation patterns available. +If you switch to this language, text will not be hyphenated. +The command \autodoc:command{\nohyphenation{…}} is provided as a shortcut for \autodoc:command{\font[language=und]{…}}. -The hyphenator uses the same algorithm as TeX and can use TeX hyphenation -pattern files if they are converted to Lua format. To implement hyphenation -for a new language, first check to see if TeX hyphenation dictionaries -are available; if not, work through the resources at -\url{http://tug.org/docs/liang/}. +The hyphenator uses the same algorithm as TeX and can use TeX hyphenation pattern files if they are converted to Lua format. +To implement hyphenation for a new language, first check to see if TeX hyphenation dictionaries are available; if not, work through the resources at \url{http://tug.org/docs/liang/}. \section{Localization} -A small handful of strings may be programatically added to documents depending on language, context, and options. -For example by default in English the \code{book} class will prepend “Chapter ” before chapter numbers output by the \autodoc:command{\chapter} command. +A small handful of strings may be programmatically added to documents depending on language, context, and options. +For example by default in English the \autodoc:class{book} class will prepend “Chapter ” before chapter numbers output by the \autodoc:command{\chapter} command. These localized strings are managed internally using the Fluent localization system.% \footnote{See Project Fluent (\url{https://projectfluent.org}) for details on the data format and uses.} Some default localizations are provided for a handful of languages, but it is quite likely SILE will not (yet) have your language. @@ -148,17 +128,15 @@ A particularly common string to override might be the table of contents heading: \section{Support for specific languages} -The following section shows some of the support features that SILE provides -for specific languages apart from hyphenation and language-specific glyph +The following section shows some of the support features that SILE provides for specific languages apart from hyphenation and language-specific glyph selection: \subsection{Amharic} -SILE inserts word break opportunities after Ethiopic word spaces and full -stops. Amharic can be typeset in two styles: with space surrounding -punctuation or space after punctuation. You can set the setting -\autodoc:setting[check=false]{languages.am.justification} to either \code{left} -or \code{centered} to control which style is used. The default is \code{left}. +SILE inserts word break opportunities after Ethiopic word spaces and full stops. +Amharic can be typeset in two styles: with space surrounding punctuation or space after punctuation. +You can set the setting \autodoc:setting[check=false]{languages.am.justification} to either \code{left} or \code{centered} to control which style is used. +The default is \code{left}. \begin{autodoc:codeblock} \\font[family=Noto Sans Ethiopic,language=am] @@ -189,22 +167,20 @@ on how to typeset this, you have options: \subsection{French} -In French typesetting, there is normally a non-breakable space between text -and “high” punctuation (a thin fixed space before question marks, exclamation -marks, and semicolons, and an inter-word space before colons), and also spaces -within “guillemets” (quotation marks). SILE will automatically apply the -correct space. The size of these spaces is determined by -\autodoc:setting[check=false]{languages.fr.thinspace}, -\autodoc:setting[check=false]{languages.fr.colonspace} and -\autodoc:setting[check=false]{languages.fr.guillspace}. +In French typesetting, there is normally a non-breakable space between text and “high” punctuation (a thin fixed space before question marks, exclamation marks, and semicolons, and an inter-word space before colons), and also spaces within “guillemets” (quotation marks). +SILE will automatically apply the correct space. +The size of these spaces is determined by + \autodoc:setting[check=false]{languages.fr.thinspace}, + \autodoc:setting[check=false]{languages.fr.colonspace} and + \autodoc:setting[check=false]{languages.fr.guillspace}. \subsection{Japanese / Chinese} SILE aims to conform with the W3G document “Requirements for Japanese Text Layout”\footnote{\url{https://www.w3.org/TR/jlreq/}} which describes the typographic conventions for Japanese (and also Chinese) text. Breaking rules \em{(kinzoku shori)} and intercharacter spacing is fully supported on selecting the Japanese language. -The easiest way to set up the other elements of Japanese typesetting such as the \em{hanmen} grid and optional vertical typesetting support is by using the \code{jplain} or \code{jbook} classes. -For other languages with similar layout requirements, more generic \code{tplain} and \code{tbook} classes are available that setup the layout elements without also setting the default language and font to Japanese specific values. -These are also good condidates to use as base classes and extend for more language-specific classes. +The easiest way to set up the other elements of Japanese typesetting such as the \em{hanmen} grid and optional vertical typesetting support is by using the \autodoc:class{jplain} or \autodoc:class{jbook} classes. +For other languages with similar layout requirements, more generic \autodoc:class{tplain} and \autodoc:class{tbook} classes are available that setup the layout elements without also setting the default language and font to Japanese specific values. +These are also good candidates to use as base classes and extend for more language-specific classes. \package-documentation{hanmenkyoshi} @@ -218,10 +194,7 @@ SILE implements syllable-based line breaking for Burmese and Javanese text. \subsection{Uyghur} -Uyghur is the only Arabic script based language which uses hyphenation, -and SILE supports hyphenation. Because Arabic fonts aren’t normally designed -with hyphenation in mind, you may need to tweak some settings to ensure that -Uyghur is typeset correctly. As well as choosing the \code{hyphenchar} (see -the hyphenation section above), the setting \autodoc:setting[check=false]{languages.ug.hyphenoffset} -inserts a space between the text and the hyphen. +Uyghur is the only Arabic script based language which uses hyphenation, and SILE supports hyphenation. +Because Arabic fonts aren’t normally designed with hyphenation in mind, you may need to tweak some settings to ensure that Uyghur is typeset correctly. +As well as choosing the \code{hyphenchar} (see the hyphenation section above), the setting \autodoc:setting[check=false]{languages.ug.hyphenoffset} inserts a space between the text and the hyphen. \end{document} diff --git a/documentation/c09-concepts.sil b/documentation/c09-concepts.sil index 94e102843..8c41bc8a8 100644 --- a/documentation/c09-concepts.sil +++ b/documentation/c09-concepts.sil @@ -1,5 +1,4 @@ \begin[class=book]{document} -\include[src=documentation/macros.sil] \chapter{The Nitty Gritty} We are finally at the bottom of our delve into SILE’s commands and settings. @@ -10,8 +9,7 @@ we will also explain how to interact with these components at the Lua level.} \section{Measurements and lengths} -Before dabbling into more advanced topics, let’s introduce “measurements” and “lengths” in SILE, -the two available Lua constructs for representing dimensions. +Before dabbling into more advanced topics, let’s introduce “measurements” and “lengths” in SILE, the two available Lua constructs for representing dimensions. Measurements are specified in terms of \code{SILE.measurement} objects. It is a basic construct with an amount and a unit. Let us illustrate two common ways for creating such an object in Lua (from a string, with same syntax as in command parameters; or from a Lua table). @@ -97,12 +95,9 @@ local vglue = SILE.nodefactory.vglue({ height = l }) S\lower[height=0.5ex]{I}L\kern[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}} \define[command=SILEglue]{\font[family=Gentium Plus]{% S\lower[height=0.5ex]{I}L\glue[width=-.2em]\raise[height=0.6ex]{\font[features=+smcp]{e}}}} -\autodoc:command{\kern}’s are a type of \autodoc:command{\glue}, only different in that -while a \autodoc:command{\glue} can be broken at the end of a line, a \autodoc:command{\kern} -can’t. Hearkening back to our \SILEkern example from the \em{Macros and -Commands} chapter, consider that example, repeated enough times to cause a -linebreak, but with \autodoc:command{\glue}’s everywhere \autodoc:command{\kern}’s are used -instead: +\autodoc:command{\kern}’s are a type of \autodoc:command{\glue}, only different in that while a \autodoc:command{\glue} can be broken at the end of a line, a \autodoc:command{\kern} +can’t. +Hearkening back to our \SILEkern example from the \em{Macros and Commands} chapter, consider that example, repeated enough times to cause a linebreak, but with \autodoc:command{\glue}’s everywhere \autodoc:command{\kern}’s are used instead: \begin{autodoc:example}% \SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue% @@ -115,10 +110,8 @@ instead: \SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue\SILEglue% \end{autodoc:example} \par -Note end of lines where \SILEglue is broken -between its ‘L’ and ‘\raise[height=0.6ex]{\font[family=Gentium Plus,features=+smcp]{e}}’. -Instead, if we typeset the same line using \autodoc:command{\kern}’s as we had -originally: +Note end of lines where \SILEglue is broken between its ‘L’ and ‘\raise[height=0.6ex]{\font[family=Gentium Plus,features=+smcp]{e}}’. +Instead, if we typeset the same line using \autodoc:command{\kern}’s as we had originally: \begin{autodoc:example}% \SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern% @@ -131,8 +124,7 @@ originally: \SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern\SILEkern% \end{autodoc:example} -The line just continues on right off the page. Why this is a useful feature is -more obvious if there are spaces between them: +The line just continues on right off the page. Why this is a useful feature is more obvious if there are spaces between them: \begin{autodoc:example}% Glues: @@ -148,57 +140,41 @@ Kerns: \section{The typesetter} -SILE’s typesetting is organised by the \code{SILE.typesetter} object. It -maintains two queues of material that it is still working on: the node queue and -the output queue. Material in these queues is content that has been parsed but -not yet rendered to the canvas and can still be manipulated. The node queue -(\code{SILE.typesetter.state.nodes}) contains new horizontal boxes and glue -that have not yet been broken up into lines. The output queue -(\code{SILE.typesetter.state.outputQueue}) consists of vertical material -(lines) which have not yet been broken up into pages. Line breaking and page -breaking happen when the typesetter moves between horizontal and vertical mode. - -As new content is parsed it is added to the node queue in as small chunks as -possible. These chunks must remain together no matter where they end up on -a line. This might include individual symbols, syllables, or objects such as -images. As soon as new content which requires a vertical break is encountered, -the node queue is processed to derive any missing shaping information about -each node, then the sequence of node is broken up into lines. Once all the -“horizontal mode” nodes are broken into lines and those lines are added to the -output queue, the other new vertical content can be processed. At any point you -can force the current queue of horizontal content (the node queue) to be shaped -into lines and added to the vertical output queue by calling the function -\code{SILE.typesetter:leaveHmode()}. This is handy when for writing custom -functions, but it is a fairly low level control. (It is unlikely to be -useful while writing a document.) A related but higher level command, -\autodoc:command{\par}, is more frequently used when writing a document and embedded in -the content. The \autodoc:command{\par} command first calls -\code{SILE.typesetter:leaveHmode()}, then inserts a vertical skip according to -the \autodoc:setting{document.parskip} setting, then goes on to reset a number of -settings that are typically paragraph-related such as hanging indents. - -When writing a custom command, if you want to manually add a vertical space to -the output, first ensure that the material in the current paragraph has been all -properly boxed-up and moved onto the output queue by calling -\code{SILE.typesetter:leaveHmode()}, then add your desired glue to the output -queue. +SILE’s typesetting is organised by the \code{SILE.typesetter} object. +It maintains two queues of material that it is still working on: the node queue and the output queue. +Material in these queues is content that has been parsed but not yet rendered to the canvas and can still be manipulated. +The node queue (\code{SILE.typesetter.state.nodes}) contains new horizontal boxes and glue +that have not yet been broken up into lines. +The output queue (\code{SILE.typesetter.state.outputQueue}) consists of vertical material +(lines) which have not yet been broken up into pages. +Line breaking and page breaking happen when the typesetter moves between horizontal and vertical mode. + +As new content is parsed it is added to the node queue in as small chunks as possible. +These chunks must remain together no matter where they end up on a line. +This might include individual symbols, syllables, or objects such as images. +As soon as new content which requires a vertical break is encountered, the node queue is processed to derive any missing shaping information about each node, then the sequence of node is broken up into lines. +Once all the “horizontal mode” nodes are broken into lines and those lines are added to the output queue, the other new vertical content can be processed. +At any point you can force the current queue of horizontal content (the node queue) to be shaped +into lines and added to the vertical output queue by calling the function \code{SILE.typesetter:leaveHmode()}. + +When writing a custom command, if you want to manually add a vertical space to the output, first ensure that the material in the current paragraph has been all properly boxed-up and moved onto the output queue by calling \code{SILE.typesetter:leaveHmode()}, then add your desired glue to the output queue. This is exactly what the \autodoc:command{\skip} and similar commands do. -Adding boxes and glue to the typesetter’s queues is -such a common operation that the typesetter has some utility methods to construct -the nodes and add them for you: +It might be a good point to better explain here the actual difference between just leaving horizontal mode, and the related, but higher level, \autodoc:command{\par} command. +The latter is more frequently used when writing a document. +It first calls \code{SILE.typesetter:leaveHmode()}, but then also inserts a vertical skip according to the \autodoc:setting{document.parskip} setting, and goes on to reset a number of settings that are typically paragraph-related such as hanging indents. +When designing you own commands, there are therefore some cases when you may just need to call \code{SILE.typesetter:leaveHmode()} and handle everything else in your own code; and situations when invoking \code{SILE.call("par")} might be more adequate, resulting in an effective paragraph to be terminated. + +Adding boxes and glue to the typesetter’s queues is such a common operation that the typesetter has some utility methods to construct the nodes and add them for you: \begin[type=autodoc:codeblock]{raw} SILE.typesetter:leaveHmode() SILE.typesetter:pushVglue({ height = l }) \end{raw} -Adding boxes yourself is a little more complicated, because boxes need to -know how to display themselves on the page. To facilitate this, they normally -store a \code{value} and an \code{outputYourself} member function. For instance, -the \code{image} package does something very simple: it adds a horizontal -box to the node queue which knows the width and height of the image, the source, -and instructions to the output engine to display the image: +Adding boxes yourself is a little more complicated, because boxes need to know how to display themselves on the page. +To facilitate this, they normally store a \code{value} and an \code{outputYourself} member function. +For instance, the \autodoc:package{image} package does something very simple: it adds a horizontal box to the node queue which knows the width and height of the image, the source, and instructions to the output engine to display the image: \begin[type=autodoc:codeblock]{raw} SILE.typesetter:pushHbox({ @@ -215,57 +191,41 @@ SILE.typesetter:pushHbox({ end}); \end{raw} -Adding horizontal and vertical penalties to the typesetter’s queues is similarly -done with the \code{SILE.typesetter:pushPenalty(\{penalty = x\})} and -\code{SILE.typesetter:pushVpenalty(\{penalty = y\})} methods. +Adding horizontal and vertical penalties to the typesetter’s queues is similarly done with the \code{SILE.typesetter:pushPenalty(\{penalty = x\})} and \code{SILE.typesetter:pushVpenalty(\{penalty = y\})} methods. \section{Frames} As we have previously mentioned, SILE arranges text into frames on the page. -The overall layout of a page, including the apparent margins between content -and the page edge and other content regions, is controlled by defining the -position of the frame or frames into which the content will be flowed. +The overall layout of a page, including the apparent margins between content and the page edge and other content regions, is controlled by defining the position of the frame or frames into which the content will be flowed. -Normally those frames are defined by your document class, but you can actually -create your own frames on a per-page basis using the \autodoc:command{\pagetemplate} -and \autodoc:command{\frame} commands. There are very few situations in which you will -actually want to do this, but if you can understand this, it will help you -to understand how to define your own document classes. +Normally those frames are defined by your document class, but you can actually create your own frames on a per-page basis using the \autodoc:command{\pagetemplate} and \autodoc:command{\frame} commands. +There are very few situations in which you will actually want to do this, but if you can understand this, it will help you to understand how to define your own document classes. For instance, in a couple of page’s time, we’re going to implement a two-column layout. -SILE uses a \em{constraint solver} system to declare its frames, which means -that you can tell it how the frames relate to each other and it will compute -where the frames should be physically placed on the page. - -Here is how we will go about it. We need to start with a page break, because -SILE will not appreciate you changing the page layout after it’s started to -determine how to put text onto that page.\footnote{You can use -the \code{frametricks} package to get around this limitation—split the current -frame and start fiddling around with the positions of the new frames that -\code{frametricks} created for you.} How do we get to the start of a new -page? Remember that the \autodoc:command{\eject} (another word for \autodoc:command{\break} in -vertical mode) only adds a penalty to the end of the output queue; page breaking -is triggered when we leave horizontal mode, and the way to do that is \autodoc:command{\par}. +SILE uses a \em{constraint solver} system to declare its frames, which means that you can tell it how the frames relate to each other and it will compute where the frames should be physically placed on the page. + +Here is how we will go about it. +We need to start with a page break, because SILE will not appreciate you changing the page layout after it’s started to determine how to put text onto that page.\footnote{You can use the \autodoc:package{frametricks} package to get around this limitation—split the current frame and start fiddling around with the positions of the new frames that \autodoc:package{frametricks} created for you.} +How do we get to the start of a new page? +Remember that the \autodoc:command{\eject} (another word for \autodoc:command{\break} in vertical mode) only adds a penalty to the end of the output queue; + page breaking is triggered when we leave horizontal mode, and the way to do that is \autodoc:command{\par}. So we start with \autodoc:command{\eject\par} and then we will begin a \autodoc:command{\pagetemplate}. -Within \autodoc:command{\pagetemplate} we need to tell SILE which frame to begin typesetting -onto: +Within \autodoc:command{\pagetemplate} we need to tell SILE which frame to begin typesetting onto: \begin[type=autodoc:codeblock]{raw} \eject\par \begin[first-content-frame=leftCol]{pagetemplate} \end{raw} -Now we will declare our columns. But we’re actually going to start by declaring -the gutter first, because that’s something that we know and can define; we’re -going to stipulate that the gutter width will be 3\% of the page width: +Now we will declare our columns. +But we’re actually going to start by declaring the gutter first, because that’s something that we know and can define; we’re going to stipulate that the gutter width will be 3\% of the page width: \begin[type=autodoc:codeblock]{raw} \frame[id=gutter,width=3%pw] \end{raw} \begin{autodoc:note}% -Declarations of frame dimensions are like ordinary SILE \code{}s, -except with three additional features: +Declarations of frame dimensions are like ordinary SILE \code{}s, except with three additional features: \begin{itemize} \item{You can refer to properties of other frames using the \code{top()}, @@ -285,13 +245,10 @@ except with three additional features: \end{itemize} \end{autodoc:note} -Next we declare the left and right column frames. The \code{book} class -gives us some frames already, one of which, \code{content}, defines a typeblock -with a decent size and positioning on the page. -We will use the boundaries of this frame to declare our columns: the left -margin of the left column is the left margin of the typeblock, and the right margin of -the right column is the right margin of the typeblock. But we also want -a few other parameters to ensure that: +Next we declare the left and right column frames. +The \autodoc:class{book} class gives us some frames already, one of which, \code{content}, defines a typeblock with a decent size and positioning on the page. +We will use the boundaries of this frame to declare our columns: the left margin of the left column is the left margin of the typeblock, and the right margin of the right column is the right margin of the typeblock. +But we also want a few other parameters to ensure that: \begin{itemize} \item{the gutter is placed between our two columns} @@ -328,10 +285,8 @@ Let’s do it. \showframe[id=rightCol] So there we have it: a two-column page layout. -In the next chapter we’ll use the knowledge of how to declare frames to -help us to create our own document class files. In the meantime, here is -some dummy text to demonstrate the fact that text does indeed flow between -the two columns naturally: +In the next chapter we’ll use the knowledge of how to declare frames to help us to create our own document class files. +In the meantime, here is some dummy text to demonstrate the fact that text does indeed flow between the two columns naturally: \lorem[words=500] \end{document} diff --git a/documentation/c10-classdesign.sil b/documentation/c10-classdesign.sil index 779d18717..2a45581c0 100644 --- a/documentation/c10-classdesign.sil +++ b/documentation/c10-classdesign.sil @@ -15,7 +15,7 @@ Their use below is straightforward and is expected to be covered by examples, bu \section{Designing a package} -Packages live somewhere in the \code{packages/} subdirectory of either where your input file is located, your current working directory, or your SILE path (typically \code{/usr/local/share/sile}). +Packages live somewhere in the \code{packages/} subdirectory of either where your first input file is located, your current working directory, or your SILE path. \subsection{Implementing a bare package} @@ -169,7 +169,7 @@ end \subsection{Defining raw handlers} -“Raw handlers” allow packages to register new handlers (or callbacks) for use with the \autodoc:environment{raw} environment, which content is read as-is by SILE, without being interpretated. +“Raw handlers” allow packages to register new handlers (or callbacks) for use with the \autodoc:environment{raw} environment, which content is read as-is by SILE, without being interpreted. This is intended for advanced use cases where you may want to provide a way for users to embed arbitrary content (likely in another syntax), and you will provide the complete parsing and handling for it.\footnote{% This may be used to implement a “clever” verbatim environment. It is also used, for instance, by the \strong{markdown.sile} 3rd-party collection to embed Markdown or Djot content directly in a (SIL or XML) document.} @@ -214,12 +214,12 @@ For now, we can conclude our primer on packages, as you should already have all \section{Designing a document class} -Document classes live somewhere in the \code{classes/} subdirectory of either where your input file is located, your current working directory, or your SILE path (typically \code{/usr/local/share/sile}). +Document classes live somewhere in the \code{classes/} subdirectory of either where your input file is located, your current working directory, or your SILE path. \subsection{Implementing a bare class} -A minimum working class inherits from the \strong{base} class. -Most of the time, however, you will prefer inheriting at least from the \strong{plain} class, which already provides a lot of things users will expect, including most of the basic commands presented early in this manual. +A minimum working class inherits from the \autodoc:class{base} class. +Most of the time, however, you will prefer inheriting at least from the \autodoc:class{plain} class, which already provides a lot of things users will expect, including most of the basic commands presented early in this manual. Let’s assume this is the case, and simply create a file \code{classes/myclass.lua} with the following content. \begin[type=autodoc:codeblock]{raw} @@ -278,7 +278,7 @@ In that case, do not forget invoking the superclass method, so that its own opti \begin[type=autodoc:codeblock]{raw} function class:setOptions (options) - options.myoption = options.layout or "default" + options.myoption = options.myoption or "default" base.setOptions(self, options) -- Note: set parent options end \end{raw} @@ -292,7 +292,7 @@ replicates the layout of the Hartley & Marks edition of Robert Bringhurst’s \em{The Elements of Typographical Style}. We are designing a book-like class, and so we will inherit from SILE’s -standard \strong{book} class found in \code{classes/book.lua}. +standard \autodoc:class{book} class found in \code{classes/book.lua}. Let’s briefly have a look at \code{book.lua} to see how it works.\footnote{% Note that the official SILE classes have some extra tooling to handle legacy class models trying to inherit from them. You don’t need those deprecation shims in your own classes when following these examples.} @@ -335,9 +335,9 @@ Initially the height of the footnotes is zero (and so the typeblock takes up the its bottom is fixed and therefore its top will be adjusted, and the bottom of the main typeblock frame will also be correspondingly adjusted. The folio frame (which holds the page number) lives below the footnotes, and the running headers live above the \code{content} frame. -Normally, as in the \strong{plain} class and anything inheriting from it, +Normally, as in the \autodoc:class{plain} class and anything inheriting from it, this would be enough to populate the pages’ frameset. -Instead the \code{book} class includes its own extension to the class with a callback \code{_init()} function which loads the \code{masters} package and generates a master frameset using the default frameset defined above. +Instead the \autodoc:class{book} class includes its own extension to the class with a callback \code{_init()} function which loads the \autodoc:package{masters} package and generates a master frameset using the default frameset defined above. \begin[type=autodoc:codeblock]{raw} function book:_init (options) @@ -353,14 +353,14 @@ function book:_init (options) end \end{raw} -Next, we use the \code{twoside} package to mirror our right-page master into a left-page master: +Next, we use the \autodoc:package{twoside} package to mirror our right-page master into a left-page master: \begin[type=autodoc:codeblock]{raw} self:loadPackage("twoside", { oddPageMaster = "right", evenPageMaster = "left" }) self:mirrorMaster("right", "left") \end{raw} -The \strong{book} class also loads the table of contents package which sets up commands for sectioning,and declares various things that need to be done at the start and end of each page. +The \autodoc:class{book} class also loads the table of contents package which sets up commands for sectioning,and declares various things that need to be done at the start and end of each page. Since we will be inheriting from the book class, we will have all these definitions already available to us. All we need to do is set up our new class, and then define what is different about it. Here is how we set up the inheritance: @@ -397,13 +397,13 @@ bringhurst.defaultFrameset = { } \end{raw} -Note that we’ve deliberately copied the frame definitions for the folio and footnote frames from the \strong{book} class, but if we had tried to reuse the \code{runningHead} frame definition it would have been too high because the typeblock is higher on the page than the standard \strong{book} class, and the running heads are defined relative to them. +Note that we’ve deliberately copied the frame definitions for the folio and footnote frames from the \autodoc:class{book} class, but if we had tried to reuse the \code{runningHead} frame definition it would have been too high because the typeblock is higher on the page than the standard \autodoc:class{book} class, and the running heads are defined relative to them. So, we needed to change the definition the running header frame to bring them down a bit lower. If all we want to do in our new class is to create a different page shape, this is all we need. -The \code{_init()} function inherited from the book class will take care of setting these frames up with mirrored masters. +The \code{_init()} function inherited from \autodoc:class{book} class will take care of setting these frames up with mirrored masters. -If we had wanted to load additional packages into our class as, say, the \strong{bible} class does, +If we had wanted to load additional packages into our class as, say, the \autodoc:class{bible} class does, we would need to define our own \code{_init()} function and call our parent class’s \code{_init()} function as well. For example to load the \autodoc:package{infonode} package into our class, we could add this function: @@ -445,9 +445,9 @@ We will not cover it here, but class authors may also provide their own hook loc \end{itemize} For an example, we will check out the \autodoc:package{tableofcontents} package for the hooks it sets, -but also the \autodoc:command[check=false]{\tocentry} command it registers that gets called manually in the \strong{book} class. +but also the \autodoc:command[check=false]{\tocentry} command it registers that gets called manually in the \autodoc:class{book} class. Let’s demonstrate roughly how the that package works. -We’ll be using the \code{infonodes} package to collect the information about which pages contain table of content items. +We’ll be using the \autodoc:package{infonode} package to collect the information about which pages contain table of content items. First, we set up our infonodes by creating a command that can be called by sectioning commands. In other words, \autodoc:command[check=false]{\chapter}, \autodoc:command[check=false]{\section}, etc., should call \autodoc:command[check=false]{\tocentry} to store the page reference for this section. @@ -468,7 +468,7 @@ Infonodes work on a per-page basis, so if we want to save them throughout the wh table. In order to be useful, we also need to make sure we store their page numbers. -\note{SILE provides the \code{SILE.scratch} variable for you to store global information in. +\autodoc:note{SILE provides the \code{SILE.scratch} variable for you to store global information in. You should use a portion of this table namespaced to your class or package.} Here is a routine we can call at the end of each page to move the TOC nodes: @@ -496,7 +496,7 @@ Here is a function to be called by the \code{finish} output routine: function package.writeToc (_) -- (Simplified from the actual implementation.) local tocdata = pl.pretty.write(SILE.scratch.tableofcontents) - local tocfile, err = io.open(SILE.masterFilename .. '.toc', "w") + local tocfile, err = io.open(pl.path.splitext(SILE.input.filenames[1]) .. '.toc', "w") if not tocfile then return SU.error(err) end tocfile:write("return " .. tocdata) tocfile:close() diff --git a/documentation/c11-inputoutput.sil b/documentation/c11-inputoutput.sil new file mode 100644 index 000000000..40bff9cf8 --- /dev/null +++ b/documentation/c11-inputoutput.sil @@ -0,0 +1,198 @@ +\begin{document} +\chapter{Designing Inputters & Outputters} + +Let’s dabble further into SILE’s internals. +As mentioned earlier in this manual, SILE relies on “input handlers” to parse content and construct an abstract syntax tree (AST) which can then be interpreted and rendered. +The actual rendering relies on an “output backend” to generate a result in the expected target format. + +\center{\img[src=documentation/fig-input-to-output.pdf, width=99%lw]} + +The standard distribution includes “inputters” (as we call them in brief) for the SIL language and its XML flavor,\footnote{% +Actually, SILE preloads \em{three} inputters: SIL, XML, and also one for Lua scripts. +} but SILE is not tied to supporting \em{only} these formats. +Adding another input format is just a matter of implementing the corresponding inputter. +This is exactly what third party modules adding “native” support for Markdown, Djot, and other markup languages achieve. +This chapter will give you a high-level overview of the process. + +As for “outputter” backends, most users are likely interested in the one responsible for PDF output. +The standard distribution includes a few other backends: text-only output, debug output (mostly used internally for regression testing), and a few experimental ones. + +\section{Designing an input handler} + +Inputters usually live somewhere in the \code{inputters/} subdirectory of either where your first input file is located, your current working directory, or your SILE path. + +\subsection{Initial boilerplate} + +A minimum working inputter inherits from the \autodoc:package{base} inputter. +We need to declare the name of our new inputter, its priority order, and (at least) two methods. + +When a file or string is processed and its format is not explicitly provided, SILE looks for the first inputter claiming to know this format. +Potential inputters are queried sequentially according to their priority order, an integer value. +For instance, +\begin{itemize} +\item{The XML inputter has a priority of 2.} +\item{The SIL inputter has a priority of 50.} +\end{itemize} + +In this tutorial example, we are going to use a priority of 2. +Please note that depending on your input format and the way it can be analyzed in order to determine whether a given content is in that format, this value might not be appropriate. +At some point, you will have to consider where in the sequence your inputter needs to be evaluated. + +We will return to the topic later below. +For now, let’s start with a file \code{inputters/myformat.lua} with the following content. + +\begin[type=autodoc:codeblock]{raw} +local base = require("inputters.base") + +local inputter = pl.class(base) +inputter._name = "myformat" +inputter.order = 2 + +function inputter.appropriate (round, filename, _) + -- We will later change it. + return false +end + +function inputter:parse (doc) + local tree = {} + -- Later we will work on parsing the input document into an AST tree + return tree +end + +return inputter +\end{raw} + +You have written you very first inputter, or more precisely minimal \em{boilerplate} code for one. +One possible way to use it would be to load it from command line, before processing some file in the supported format: + +\begin[type=autodoc:codeblock]{raw} +sile -u inputters.myformat somefile.xy +\end{raw} + +However, this will not work yet. +We must code up a few real functions now. + +\subsection{Content appropriation} + +What we first need is to tell SILE how to choose our inputter when it is given a file in our input format. +The \code{appropriate()} method of our inputter is responsible for providing the corresponding logic. +It is a static method (so it does not have a \code{self} argument), and it takes up to three arguments: +\begin{itemize} +\item{the round, an integer between 1 and 3.} +\item{the file name if we are processing a file (so \code{nil} in case we are processing some string directly, for instance via a raw command handler).} +\item{the textual content (of the file or string being processed).} +\end{itemize} +It is expected to return a boolean value, \code{true} if this handler is appropriate and \code{false} otherwise. + +Earlier, we said that inputters were checked in their priority order. +This was not fully complete. +Let’s add another piece to our puzzle: Inputters are actually checked orderly indeed, but three times. +This allows for quick compatiblitity checks to supersede resource-intensive ones. +\begin{itemize} +\item{Round 1 expects the file name to be checked: for instance, we could base our decision on recognized file extensions.} +\item{Round 2 expects some portion of the content string to be checked: for instance, we could base our decision on sniffing for some sequence of characters expected to occur early in the document (or any other content inspection strategy).} +\item{Round 3 expects the entire content to be successfully parsed.} +\end{itemize} + +For instance, say you are designing an inputter for HTML. +The \em{appropriation} logic might look as follows. + +\begin[type=autodoc:codeblock]{raw} +function inputter.appropriate (round, filename, doc) + if round == 1 then + return filename:match(".html$") + elseif round == 2 then + local sniff = doc:sub(1, 100) + local promising = sniff:match("") + or sniff:match("") or sniff:match("} tags can be nested; to start a subsection, you place another \code{
} tag inside the current \code{
}. So in order to know what level we are currently on, we need a stack. We also need to keep track of what section number we are on at \em{each} level. For instance, with the expected section numbers and titles in XML comments: -\begin{verbatim} +\begin[type=autodoc:codeblock]{raw}
A : \autodoc:example{1. A}
B: \autodoc:example{1.1 B}
@@ -126,7 +126,7 @@ For instance, with the expected section numbers and titles in XML comments:
E: \autodoc:example{1.3 E}
F: \autodoc:example{2. F} -\end{verbatim} +\end{raw} So, we will keep two variables: the current level, and the counters for all of the levels so far. Each time we enter a \code{section}, we increase the current level counter: diff --git a/documentation/c12-tricks.sil b/documentation/c13-tricks.sil similarity index 97% rename from documentation/c12-tricks.sil rename to documentation/c13-tricks.sil index 16f3f777d..ce2a636e4 100644 --- a/documentation/c12-tricks.sil +++ b/documentation/c13-tricks.sil @@ -6,7 +6,7 @@ We’ll conclude our tour of SILE by looking at some tricky situations which req \section{Parallel text} The example \url{https://sile-typesetter.org/examples/parallel.sil} contains a rendering of Chapter 1 of Matthew’s Gospel in English and Greek. -It uses the \strong{diglot} class to align the two texts side-by-side. +It uses the \autodoc:class{diglot} class to align the two texts side-by-side. The latter provides the \autodoc:command[check=false]{\left} and \autodoc:command[check=false]{\right} commands to start entering text on the left column or the right column respectively, and the \autodoc:command[check=false]{\sync} command to ensure that the two columns are in sync with each other. It’s an instructive example of what can be done in a SILE class, so we will take it apart and see how it works. @@ -37,7 +37,7 @@ end Next we create two new typesetters, one for each column, and we tell each one how to find the other: -\begin{verbatim} +\begin[type=autodoc:codeblock]{raw} function diglot:_init (options) self.leftTypesetter = SILE.typesetters.base() self.rightTypesetter = SILE.typesetters.base() @@ -45,7 +45,7 @@ function diglot:_init (options) self.leftTypesetter.other = self.rightTypesetter return plain._init(self) end -\end{verbatim} +\end{raw} Each column needs its own font, so we provide commands to store this information. The \autodoc:command[check=false]{\leftfont} and \autodoc:command[check=false]{\rightfont} macros simply store their options to be passed to the \autodoc:command[check=false]{\font} command every time \autodoc:command[check=false]{\left} and \autodoc:command[check=false]{\right} are called. @@ -329,7 +329,7 @@ We haven’t used \code{SILE.call} here because it performs all its operations o If we wanted to make things cleaner, we could swap typesetters by assigning \code{discovery.innerTypesetter} to \code{SILE.typesetter} and then calling ordinary commands, rather than doing the settings and glue insertion “by hand”. \end{autodoc:note} -In the future it may make sense for there to be a standard \code{sidenotes} package in SILE, but it has been instructive to see a couple of “non-standard”examples to understand how the internals of SILE can be leveraged to create such a package. +In the future it may make sense for there to be a standard \autodoc:package{sidenotes} package in SILE, but it has been instructive to see a couple of “non-standard”examples to understand how the internals of SILE can be leveraged to create such a package. Your homework is to create one! \section{SILE as a library} @@ -397,7 +397,7 @@ Running SILE with the \code{--debug \em{facility}} switch will turn on debugging \item{\code{versions} gives a report on the versions of libraries and fonts in use. Please include this information when reporting bugs in SILE!} \item{Any package or other area of SILE’s operation may define their own debugging tags; - the \code{insertions} package does this, as do the Japanese and Uyghur language support systems (\code{--debug uyghur}). + the \autodoc:package{insertions} package does this, as do the Japanese and Uyghur language support systems (\code{--debug uyghur}). Often the debug flag is the name of the package or the function.} \end{itemize} @@ -444,7 +444,7 @@ stack traceback: Sometimes it’s useful for you to try out Lua code within the SILE environment; SILE contains a REPL (read-evaluate-print loop) for you to enter Lua code and print the results back to you. -If you call SILE with no input file name, it enters the REPL: +If you call SILE with no input file names, it enters the REPL: \begin{autodoc:codeblock} \sileversion diff --git a/documentation/developers.sil b/documentation/developers.sil deleted file mode 100644 index 316494a78..000000000 --- a/documentation/developers.sil +++ /dev/null @@ -1,50 +0,0 @@ -\begin[papersize=a4,class=book]{document} -\use[module=packages.pdf] -\use[module=packages.image] -\include[src=documentation/macros.xml] -\define[command=silehp]{\url{http://www.sile-typesetter.org/}} -\font[size=11pt,family=Gentium Book Basic] -\nofolios -\par\center{\font[size=50pt,weight=600,family=Helvetica Neue]{The}\par -\center{\img[src=documentation/sile-logo.pdf,height=40mm]}\par -\font[size=50pt,weight=600,family=Helvetica Neue]{Developers’ Guide}} -\bigskip -\font[size=12pt]{\hss for SILE version \script{SILE.typesetter:typeset(SILE.version) \hss \par} -\skip[height=2in] -\font[size=20pt]{\hss Simon Cozens \hss \par} -\open-double-page -\tableofcontents -\set-counter[id=folio,value=0] - -\chapter{Code Organisation} - -This book assumes that you’ve already read the SILE Book, and are now looking for further information about how SILE actually works. - -The SILE CLI itself is generated from the \code{sile.in} file and only handles some of the executable lifecycle stuff. -The main core of SILE including most of the top level API is in \code{core/sile.lua}. -This is also the file you would require in your own projects to get access to the SILE core as a library. -It requires a lot of other things on its own, including some other file in \code{core}. - -Some of the major components, especially those which have alternatives, are in module directories. -For example classes are in \code{classes}, inputters in \code{inputters}, etc. -These are typically loaded on-demand by accessing them at \code{SILE.classes.classname}, \code{SILE.inputters.format}, etc. -Most documents will use one class, one inputter, one outputter, one shaper, one typesetter, etc. -However it is certainly possible to mix and match many alternatives in a single project. - -\chapter{From Input To PDF} -Next we’ll give you a high-level overview of the process flow: what SILE does to get you from input to output. - -\section{SILE startup} - -\section{From input to AST} - -The typesetting process is then kicked off by a call to \code{SILE.processFile} in \code{sile.lua}, which chooses the input handler based on the file type. -Inputters such as \code{SILE.inputters.sil} provide \code{:parse()} method which takes raw content and converts it to an abstract syntax tree. -After coversion to a tree the inputter \code{:process()} method to walk though it, typically by handing it off to \code{SILE.process()} - -\section{Typesetting and commands} - -\code{SILE.process} - -\code{SILE.typesetter} -\end{document} diff --git a/documentation/fig-input-to-output.dot b/documentation/fig-input-to-output.dot new file mode 100644 index 000000000..a3c901133 --- /dev/null +++ b/documentation/fig-input-to-output.dot @@ -0,0 +1,47 @@ +digraph G { + rankdir = "LR"; + margin = 0.25; + fontname = "Gentium Book Basic"; + + node [fontname = "Gentium Book Basic"]; + edge [arrowhead = "vee"]; + + inputfiles [shape = note, style = filled, fillcolor = aliceblue, label = "Input\nfile(s)"] + outputfile [shape = note, style = filled, fillcolor = aliceblue, label = "Output\nfile"] + inputter [shape = component, style = filled, fillcolor = darkolivegreen2] + command [label = "Command\nprocessing", shape = box] + typesetter [label = "Typesetter", shape = box] + paragraphing [label = "Shaping,\nHyphenation,\nLine breaking,\nEtc.", shape = box] + pagebreaking [label = "Page\nbreaking", shape = box] + frame [label = "Frame\nabstraction", shape = box] + outputter [shape = component, style = filled, fillcolor = darkolivegreen2] + + subgraph input { + rank = same; + inputfiles -> inputter + } + + subgraph process { + cluster = true; + style = rounded; + color = grey; + margin = 18; + node [style = filled, fillcolor = linen]; + + label = "Processing & Typesetting"; + + command -> typesetter + typesetter -> frame [arrowhead = none] + typesetter -> paragraphing + frame -> pagebreaking [arrowhead = none] + paragraphing -> pagebreaking + } + + inputter -> command [label = "AST\nnodes"] + pagebreaking -> outputter [label = "drawing\nfunctions"] + + subgraph output { + rank = same; + outputter -> outputfile + } +} diff --git a/documentation/macros.sil b/documentation/macros.sil index 01893271b..c31988a57 100644 --- a/documentation/macros.sil +++ b/documentation/macros.sil @@ -17,7 +17,6 @@ \use[module=packages.simpletable] % Autodoc-related shims \define[command=examplefont]{\autodoc:example{\process}} -\define[command=note]{\autodoc:note{\process}} % Document fonts \define[command=sectionsfont]{\font[weight=600,family=Roboto Condensed]{\process}} \define[command=tableofcontents:headerfont]{\font[weight=600,size=22pt,family=Roboto Condensed]{\process}} @@ -25,11 +24,11 @@ \define[command=book:sectionfont]{\sectionsfont{\font[size=15pt]{\process}}} \define[command=book:subsectionfont]{\sectionsfont{\font[size=13pt]{\process}}} \define[command=code]{\font[family=Hack,size=0.8em,language=und,style=roman]{\process}} -\define[command=verbatim:font]{\font[family=Hack,size=9pt]} +\define[command=verbatim:font]{\font[family=Hack,size=8.5pt]} % Custom commands \define[command=terminal]{\verbatim{\set[parameter=document.lskip,value=36pt]\process\smallskip}} \define[command=changed]{ -\note{\strong{The material in this section has changed significantly since the previous release of SILE.}} +\autodoc:note{\strong{The material in this section has changed significantly since the previous release of SILE.}} } \define[command=sup]{\raise[height=0.6ex]{\font[size=0.8em]{\process}}} \end{document} diff --git a/documentation/sile.sil b/documentation/sile.sil index 70b1d5ba9..a46b192eb 100644 --- a/documentation/sile.sil +++ b/documentation/sile.sil @@ -7,6 +7,7 @@ % and little resources for fine-checking and tuning, we prefer (possibly large) underfull lines % to ugly overfull ones: \set[parameter=linebreak.emergencyStretch,value=20%lw] +\set[parameter=autodoc.highlighting, value=true] \font[size=11pt,family=Gentium Book Basic] \nofolios \pdf:metadata[key=Title, value=The SILE Book] @@ -59,6 +60,7 @@ Didier Willis\break % Developers' guide \include[src=documentation/c09-concepts.sil] \include[src=documentation/c10-classdesign.sil] -\include[src=documentation/c11-xmlproc.sil] -\include[src=documentation/c12-tricks.sil] +\include[src=documentation/c11-inputoutput.sil] +\include[src=documentation/c12-xmlproc.sil] +\include[src=documentation/c13-tricks.sil] \end{document} diff --git a/flake.lock b/flake.lock index d5602393a..0dca04683 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "flake-compat": { "flake": false, "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", "owner": "edolstra", "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", "type": "github" }, "original": { @@ -17,12 +17,15 @@ } }, "flake-utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1667395993, - "narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=", + "lastModified": 1694529238, + "narHash": "sha256-zsNZZGTGnMOf9YpHKJqMSsa0dXbfmxeoJ7xHlrt+xmY=", "owner": "numtide", "repo": "flake-utils", - "rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f", + "rev": "ff7b65b44d01cf9ba6a71320833626af21126384", "type": "github" }, "original": { @@ -38,11 +41,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1694102001, + "narHash": "sha256-vky6VPK1n1od6vXbqzOXnekrQpTL4hbPAwUhT5J9c9E=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "9e21c80adf67ebcb077d75bd5e7d724d21eeafd6", "type": "github" }, "original": { @@ -51,13 +54,29 @@ "type": "github" } }, + "libtexpdf-src": { + "flake": false, + "locked": { + "lastModified": 1662109873, + "narHash": "sha256-yThIb+D/m1xeJZESojRd3u4OugbWl7f2s8oJohspwZs=", + "owner": "sile-typesetter", + "repo": "libtexpdf", + "rev": "736a5e7530c13582ea704a061a358d0caa774916", + "type": "github" + }, + "original": { + "owner": "sile-typesetter", + "repo": "libtexpdf", + "type": "github" + } + }, "nixpkgs": { "locked": { - "lastModified": 1674487464, - "narHash": "sha256-Jgq50e4S4JVCYpWLqrabBzDp/1mfaxHCh8/OOorHTy0=", + "lastModified": 1696577711, + "narHash": "sha256-94VRjvClIKDym1QRqPkX5LTQoAwZ1E6QE/3dWtOXSIQ=", "owner": "nixos", "repo": "nixpkgs", - "rev": "3954218cf613eba8e0dcefa9abe337d26bc48fd0", + "rev": "a2eb207f45e4a14a1e3019d9e3863d1e208e2295", "type": "github" }, "original": { @@ -72,8 +91,24 @@ "flake-compat": "flake-compat", "flake-utils": "flake-utils", "gitignore": "gitignore", + "libtexpdf-src": "libtexpdf-src", "nixpkgs": "nixpkgs" } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 2fe4eacb5..756e3dbc4 100644 --- a/flake.nix +++ b/flake.nix @@ -8,6 +8,13 @@ url = "github:hercules-ci/gitignore.nix"; inputs.nixpkgs.follows = "nixpkgs"; }; + # TODO: Should this be replaced with libtexpdf package from nixpkgs? or + # should we keep it that way, so that it'd be easy to test new versions + # of libtexpdf when developing? + inputs.libtexpdf-src = { + url = "github:sile-typesetter/libtexpdf"; + flake = false; + }; # https://nixos.wiki/wiki/Flakes#Using_flakes_project_from_a_legacy_Nix inputs.flake-compat = { @@ -20,53 +27,17 @@ , flake-utils , flake-compat , gitignore + , libtexpdf-src }: flake-utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; - # TODO: Should this be replaced with libtexpdf package from nixpkgs? or - # should we keep it that way, so that it'd be easy to test new versions - # of libtexpdf when developing? - libtexpdf-src = builtins.fetchGit { - url = "https://github.com/sile-typesetter/libtexpdf"; - rev = "${(pkgs.lib.fileContents "${self}/libtexpdf.git-rev")}"; - }; inherit (gitignore.lib) gitignoreSource; # https://discourse.nixos.org/t/passing-git-commit-hash-and-tag-to-build-with-flakes/11355/2 version_rev = if (self ? rev) then (builtins.substring 0 7 self.rev) else "dirty"; - # Prepare a different luaEnv to be used in the overridden expression, - # this is also the place to choose a different lua interpreter, such as - # lua5_4 or luajit - luaEnv = pkgs.lua5_3.withPackages(ps: with ps; [ - cassowary - cldr - cosmo - fluent - linenoise - loadkit - lpeg - lua-zlib - lua_cliargs - luaepnf - luaexpat - luafilesystem - luarepl - luasec - luasocket - luautf8 - penlight - vstruct - # lua packages needed for testing - busted - luacheck - # If we want to test things with lua5.2 or an even older lua, we uncomment these - #bit32 - #compat53 - ]); - # Use the expression from Nixpkgs instead of rewriting it here. - sile = pkgs.sile.overrideAttrs(oldAttr: rec { + sile = pkgs.callPackage ./build-aux/pkg.nix { version = "${(pkgs.lib.importJSON ./package.json).version}-${version_rev}-flake"; src = pkgs.lib.cleanSourceWith { # Ignore many files that gitignoreSource doesn't ignore, see: @@ -95,57 +66,49 @@ ]); src = gitignoreSource ./.; }; - # Add the libtexpdf src instead of the git submodule. - # Also pretend to be a tarball release so sile --version will not say `vUNKNOWN`. - preAutoreconf = '' - rm -rf ./libtexpdf - # From some reason without this flag, libtexpdf/ is unwriteable - cp --no-preserve=mode -r ${libtexpdf-src} ./libtexpdf/ - echo ${version} > .tarball-version - ''; - # Don't build the manual as it's time consuming, and it requires fonts - # that are not available in the sandbox due to internet connection - # missing. - configureFlags = pkgs.lib.lists.remove "--with-manual" oldAttr.configureFlags; - nativeBuildInputs = oldAttr.nativeBuildInputs ++ [ - pkgs.autoreconfHook - ]; - buildInputs = [ - # Add here inputs needed for development, and not for Nixpkgs' build. - pkgs.libarchive - pkgs.perl - # This line, along with the `pkgs.list.drop 1` line afterwards, - # replaces the luaEnv originated in `oldAttr.buildInputs`. - luaEnv - ] ++ ( - # Add all buildInputs from Nixpkgs' derivation, besides the 1st - # one, which is Nixpkgs' luaEnv. NOTE it's not mandatory to `drop` - # the first buildInput of `oldAttr` as so, because the first `lua` - # interpreter that would have been found otherwise would have been - # the one belonging to the first `luaEnv` of the final - # `buildInputs`. However, we'd like to keep the `buildInputs` clean - # never the less. - pkgs.lib.lists.drop 1 oldAttr.buildInputs - ); - meta = oldAttr.meta // { - changelog = "https://github.com/sile-typesetter/sile/raw/master/CHANGELOG.md"; - }; - }); + inherit libtexpdf-src; + }; + inherit (sile.passthru) luaEnv; in rec { devShells = { default = pkgs.mkShell { - inherit (sile) checkInputs buildInputs FONTCONFIG_FILE; + inherit (sile) + buildInputs + nativeCheckInputs + FONTCONFIG_FILE + ; configureFlags = sile.configureFlags ++ [ "--enable-developer" ]; - nativeBuildInputs = sile.nativeBuildInputs ++ [ pkgs.luarocks-nix ]; - # This is written in Nixpkgs' expression as well, but we need to write - # this here so that the overridden luaEnv will be used instead. - passthru = { - inherit luaEnv; - }; + nativeBuildInputs = sile.nativeBuildInputs ++ [ + pkgs.luarocks + # For commitlint git hook + pkgs.yarn + # For npx + pkgs.nodejs + # For gs, dot, and bsdtar used in building the manual + pkgs.ghostscript + pkgs.graphviz + pkgs.libarchive + ]; + }; + }; + packages = { + sile-lua5_2 = sile; + sile-lua5_3 = sile.override { + lua = pkgs.lua5_3; + }; + sile-lua5_4 = sile.override { + lua = pkgs.lua5_4; + }; + sile-luajit = sile.override { + lua = pkgs.luajit; + }; + sile-clang = sile.override { + lua = pkgs.luajit; + # Use the same clang version as Nixpkgs' rust clang stdenv + stdenv = pkgs.rustc.llvmPackages.stdenv; }; }; - packages.sile = sile; - defaultPackage = sile; + defaultPackage = packages.sile-luajit; apps = rec { default = sile; sile = { diff --git a/hooks/build b/hooks/build index 44325bff4..609df1bc8 100755 --- a/hooks/build +++ b/hooks/build @@ -5,8 +5,9 @@ set -e REVISION=$(git describe --long --tags | sed 's/^v//;s/\([^-]*-g\)/r\1/;s/-/./g') -RUNTIME_DEPS='fontconfig gentium-plus-font libpng harfbuzz icu lua zlib' -BUILD_DEPS='git luarocks poppler' +RUNTIME_DEPS='fontconfig freetype2 gentium-plus-font glibc harfbuzz icu libpng luajit zlib'\ +' libertinus-font luarocks noto-fonts-cjk ttf-hack' +BUILD_DEPS='cargo git jq poppler' : "${DOCKER_BUILDKIT:=1}" export DOCKER_BUILDKIT diff --git a/i18n/ja.ftl b/i18n/ja.ftl index ba9e6cafb..612fa4b30 100644 --- a/i18n/ja.ftl +++ b/i18n/ja.ftl @@ -1,3 +1,3 @@ -book-chapter-title = 第 { $number } 章 +book-chapter-title = 第{ $number }章 tableofcontents-title = 目次 diff --git a/i18n/pt.ftl b/i18n/pt.ftl index 8991d7c61..5c0ee077c 100644 --- a/i18n/pt.ftl +++ b/i18n/pt.ftl @@ -1,3 +1,11 @@ +bibliography-and = e + +bibliography-edited-by = Editado por { $name } + +bibliography-et-al = et al. + +bibliography-translated-by = Traduzido por { $name } + book-chapter-title = Capítulo { $number } tableofcontents-title = Conteúdo diff --git a/i18n/yue.ftl b/i18n/yue.ftl new file mode 100644 index 000000000..cc5d7c615 --- /dev/null +++ b/i18n/yue.ftl @@ -0,0 +1,11 @@ +bibliography-and = 同 + +bibliography-edited-by = { $name }編輯 + +bibliography-translated-by = { $name }翻譯 + +book-chapter-title = 第{ $number }章 + +tableofcontents-not-generated = 重新運行SILE以便處理目錄啦! + +tableofcontents-title = 目錄 diff --git a/i18n/zh-hans.ftl b/i18n/zh-hans.ftl new file mode 100644 index 000000000..b3d0077c8 --- /dev/null +++ b/i18n/zh-hans.ftl @@ -0,0 +1,11 @@ +bibliography-and = 和 + +bibliography-edited-by = 由{ $name }编辑 + +bibliography-translated-by = 由{ $name }翻译 + +book-chapter-title = 第{ $number }章 + +tableofcontents-not-generated = 重新運行SILE以便处理目录! + +tableofcontents-title = 目次 diff --git a/i18n/zh-hant.ftl b/i18n/zh-hant.ftl new file mode 100644 index 000000000..c7a082b73 --- /dev/null +++ b/i18n/zh-hant.ftl @@ -0,0 +1,11 @@ +bibliography-and = 和 + +bibliography-edited-by = 由{ $name }編輯 + +bibliography-translated-by = 由{ $name }翻譯 + +book-chapter-title = 第{ $number }章 + +tableofcontents-not-generated = 重新運行SILE以便處理目錄! + +tableofcontents-title = 目次 diff --git a/inputters/base.lua b/inputters/base.lua index 7a1d74eb6..a00c67673 100644 --- a/inputters/base.lua +++ b/inputters/base.lua @@ -23,7 +23,7 @@ function inputter:classInit (options) class = constructor._name end class = SILE.input.class or class or options.class or "plain" - options.class = nil -- don't pass already consumed class option to contstructor + options.class = nil -- don't pass already consumed class option to constructor constructor = self._docclass or constructor or SILE.require(class, "classes", true) if constructor.id then SU.deprecated("std.object", "pl.class", "0.13.0", "0.14.0", string.format(_deprecated, constructor.id)) diff --git a/justenough/Makefile.am b/justenough/Makefile.am new file mode 100644 index 000000000..db862d281 --- /dev/null +++ b/justenough/Makefile.am @@ -0,0 +1,64 @@ +ACLOCAL_AMFLAGS = -I ../build-aux + +if LINKLUA +MY_LUA_LIB = $(LUA_LIB) +UNDEFINED = -no-undefined +else +MY_LUA_LIB = +UNDEFINED = +endif + +if SYSTEM_LIBTEXPDF +LIBTEXPDF_LIB = -ltexpdf +else +LIBTEXPDF_LIB = ../libtexpdf/libtexpdf.la +endif + +AM_CFLAGS = $(WARNING_CFLAGS) -I../libtexpdf -I./justenough/ +AM_LDFLAGS = -module -avoid-version $(UNDEFINED) + +pkglib_LTLIBRARIES = justenoughharfbuzz.la +justenoughharfbuzz_la_SOURCES = justenoughharfbuzz.c hb-utils.c hb-utils.h compat-5.3.h +justenoughharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) +justenoughharfbuzz_la_CFLAGS = $(AM_CFLAGS) $(HARFBUZZ_CFLAGS) $(HARFBUZZ_SUBSET_CFLAGS) $(LUA_INCLUDE) $(COMPAT53_CFLAGS) +justenoughharfbuzz_la_LIBADD = $(HARFBUZZ_LIBS) $(HARFBUZZ_SUBSET_LIBS) $(MY_LUA_LIB) + +pkglib_LTLIBRARIES += justenoughfontconfig.la +justenoughfontconfig_la_SOURCES = justenoughfontconfig.c silewin32.h compat-5.3.h +justenoughfontconfig_la_LDFLAGS = $(AM_LDFLAGS) +justenoughfontconfig_la_CFLAGS = $(AM_CFLAGS) $(FONTCONFIG_CFLAGS) $(LUA_INCLUDE) $(COMPAT53_CFLAGS) +justenoughfontconfig_la_LIBADD = $(FONTCONFIG_LIBS) $(MY_LUA_LIB) + +if APPKIT +pkglib_LTLIBRARIES += macfonts.la +macfonts_la_SOURCES = macfonts.m compat-5.3.h +macfonts_la_LDFLAGS = $(AM_LDFLAGS) +macfonts_la_OBJCFLAGS = -U VERSION $(HARFBUZZ_CFLAGS) $(LUA_INCLUDE) -fmodules $(COMPAT53_CFLAGS) +macfonts_la_LIBADD = $(HARFBUZZ_LIBS) $(MY_LUA_LIB) +endif + +pkglib_LTLIBRARIES += justenoughlibtexpdf.la +justenoughlibtexpdf_la_SOURCES = justenoughlibtexpdf.c imagebbox.c compat-5.3.h +justenoughlibtexpdf_la_LDFLAGS = $(AM_LDFLAGS) +justenoughlibtexpdf_la_CFLAGS = $(AM_CFLAGS) $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(LIBPAPER_INCLUDES) $(LUA_INCLUDE) $(COMPAT53_CFLAGS) +justenoughlibtexpdf_la_LIBADD = $(LIBTEXPDF_LIB) $(LIBPNG_LIBS) $(ZLIB_LIBS) $(LIBPAPER_LIBS) $(MY_LUA_LIB) + +pkglib_LTLIBRARIES += fontmetrics.la +fontmetrics_la_SOURCES = fontmetrics.c hb-utils.c hb-utils.h compat-5.3.h +fontmetrics_la_LDFLAGS = $(AM_LDFLAGS) +fontmetrics_la_CFLAGS = $(AM_CFLAGS) $(LUA_INCLUDE) $(HARFBUZZ_CFLAGS) $(COMPAT53_CFLAGS) +fontmetrics_la_LIBADD = $(MY_LUA_LIB) $(HARFBUZZ_LIBS) + +pkglib_LTLIBRARIES += svg.la +svg_la_SOURCES = svg.c nanosvg.h compat-5.3.h +svg_la_LDFLAGS = $(AM_LDFLAGS) +svg_la_CFLAGS = $(AM_CFLAGS) $(LUA_INCLUDE) $(COMPAT53_CFLAGS) +svg_la_LIBADD = $(MY_LUA_LIB) + +if ICU +pkglib_LTLIBRARIES += justenoughicu.la +justenoughicu_la_SOURCES = justenoughicu.c compat-5.3.h +justenoughicu_la_LDFLAGS = $(AM_LDFLAGS) +justenoughicu_la_CFLAGS = $(AM_CFLAGS) $(ICU_CFLAGS) $(LUA_INCLUDE) $(COMPAT53_CFLAGS) +justenoughicu_la_LIBADD = $(ICU_LIBS) $(MY_LUA_LIB) +endif diff --git a/justenough/compat-5.3.c b/justenough/compat-5.3.c new file mode 100644 index 000000000..1901a8267 --- /dev/null +++ b/justenough/compat-5.3.c @@ -0,0 +1,957 @@ +#include +#include +#include +#include +#include +#include +#include "compat-5.3.h" + +/* don't compile it again if it already is included via compat53.h */ +#ifndef COMPAT53_C_ +#define COMPAT53_C_ + + + +/* definitions for Lua 5.1 only */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + +#ifndef COMPAT53_FOPEN_NO_LOCK +# if defined(_MSC_VER) +# define COMPAT53_FOPEN_NO_LOCK 1 +# else /* otherwise */ +# define COMPAT53_FOPEN_NO_LOCK 0 +# endif /* VC++ only so far */ +#endif /* No-lock fopen_s usage if possible */ + +#if defined(_MSC_VER) && COMPAT53_FOPEN_NO_LOCK +# include +#endif /* VC++ _fsopen for share-allowed file read */ + +#ifndef COMPAT53_HAVE_STRERROR_R +# if (!defined(_WIN32)) && \ + ((defined(_POSIX_C_SOURCE) && _POSIX_C_SOURCE >= 200112L) || \ + (defined(_XOPEN_SOURCE) && _XOPEN_SOURCE >= 600) || \ + defined(__APPLE__)) +# define COMPAT53_HAVE_STRERROR_R 1 +# else /* none of the defines matched: define to 0 */ +# define COMPAT53_HAVE_STRERROR_R 0 +# endif /* have strerror_r of some form */ +#endif /* strerror_r */ + +#ifndef COMPAT53_HAVE_STRERROR_S +# if defined(_MSC_VER) || (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L && \ + defined(__STDC_LIB_EXT1__) && __STDC_LIB_EXT1__) +# define COMPAT53_HAVE_STRERROR_S 1 +# else /* not VC++ or C11 */ +# define COMPAT53_HAVE_STRERROR_S 0 +# endif /* strerror_s from VC++ or C11 */ +#endif /* strerror_s */ + +#ifndef COMPAT53_LUA_FILE_BUFFER_SIZE +# define COMPAT53_LUA_FILE_BUFFER_SIZE 4096 +#endif /* Lua File Buffer Size */ + + +static char* compat53_strerror (int en, char* buff, size_t sz) { +#if COMPAT53_HAVE_STRERROR_R + /* use strerror_r here, because it's available on these specific platforms */ + if (sz > 0) { + buff[0] = '\0'; + /* we don't care whether the GNU version or the XSI version is used: */ + if (strerror_r(en, buff, sz)) { + /* Yes, we really DO want to ignore the return value! + * GCC makes that extra hard, not even a (void) cast will do. */ + } + if (buff[0] == '\0') { + /* Buffer is unchanged, so we probably have called GNU strerror_r which + * returned a static constant string. Chances are that strerror will + * return the same static constant string and therefore be thread-safe. */ + return strerror(en); + } + } + return buff; /* sz is 0 *or* strerror_r wrote into the buffer */ +#elif COMPAT53_HAVE_STRERROR_S + /* for MSVC and other C11 implementations, use strerror_s since it's + * provided by default by the libraries */ + strerror_s(buff, sz, en); + return buff; +#else + /* fallback, but strerror is not guaranteed to be threadsafe due to modifying + * errno itself and some impls not locking a static buffer for it ... but most + * known systems have threadsafe errno: this might only change if the locale + * is changed out from under someone while this function is being called */ + (void)buff; + (void)sz; + return strerror(en); +#endif +} + + +COMPAT53_API int lua_absindex (lua_State *L, int i) { + if (i < 0 && i > LUA_REGISTRYINDEX) + i += lua_gettop(L) + 1; + return i; +} + + +static void compat53_call_lua (lua_State *L, char const code[], size_t len, + int nargs, int nret) { + lua_rawgetp(L, LUA_REGISTRYINDEX, (void*)code); + if (lua_type(L, -1) != LUA_TFUNCTION) { + lua_pop(L, 1); + if (luaL_loadbuffer(L, code, len, "=none")) + lua_error(L); + lua_pushvalue(L, -1); + lua_rawsetp(L, LUA_REGISTRYINDEX, (void*)code); + } + lua_insert(L, -nargs-1); + lua_call(L, nargs, nret); +} + + +static const char compat53_arith_code[] = + "local op,a,b=...\n" + "if op==0 then return a+b\n" + "elseif op==1 then return a-b\n" + "elseif op==2 then return a*b\n" + "elseif op==3 then return a/b\n" + "elseif op==4 then return a%b\n" + "elseif op==5 then return a^b\n" + "elseif op==6 then return -a\n" + "end\n"; + +COMPAT53_API void lua_arith (lua_State *L, int op) { + if (op < LUA_OPADD || op > LUA_OPUNM) + luaL_error(L, "invalid 'op' argument for lua_arith"); + luaL_checkstack(L, 5, "not enough stack slots"); + if (op == LUA_OPUNM) + lua_pushvalue(L, -1); + lua_pushnumber(L, op); + lua_insert(L, -3); + compat53_call_lua(L, compat53_arith_code, + sizeof(compat53_arith_code)-1, 3, 1); +} + + +static const char compat53_compare_code[] = + "local a,b=...\n" + "return a<=b\n"; + +COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op) { + int result = 0; + switch (op) { + case LUA_OPEQ: + return lua_equal(L, idx1, idx2); + case LUA_OPLT: + return lua_lessthan(L, idx1, idx2); + case LUA_OPLE: + luaL_checkstack(L, 5, "not enough stack slots"); + idx1 = lua_absindex(L, idx1); + idx2 = lua_absindex(L, idx2); + lua_pushvalue(L, idx1); + lua_pushvalue(L, idx2); + compat53_call_lua(L, compat53_compare_code, + sizeof(compat53_compare_code)-1, 2, 1); + result = lua_toboolean(L, -1); + lua_pop(L, 1); + return result; + default: + luaL_error(L, "invalid 'op' argument for lua_compare"); + } + return 0; +} + + +COMPAT53_API void lua_copy (lua_State *L, int from, int to) { + int abs_to = lua_absindex(L, to); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushvalue(L, from); + lua_replace(L, abs_to); +} + + +COMPAT53_API void lua_len (lua_State *L, int i) { + switch (lua_type(L, i)) { + case LUA_TSTRING: + lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); + break; + case LUA_TTABLE: + if (!luaL_callmeta(L, i, "__len")) + lua_pushnumber(L, (lua_Number)lua_objlen(L, i)); + break; + case LUA_TUSERDATA: + if (luaL_callmeta(L, i, "__len")) + break; + /* FALLTHROUGH */ + default: + luaL_error(L, "attempt to get length of a %s value", + lua_typename(L, lua_type(L, i))); + } +} + + +COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p) { + int abs_i = lua_absindex(L, i); + lua_pushlightuserdata(L, (void*)p); + lua_rawget(L, abs_i); + return lua_type(L, -1); +} + +COMPAT53_API void lua_rawsetp (lua_State *L, int i, const void *p) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 1, "not enough stack slots"); + lua_pushlightuserdata(L, (void*)p); + lua_insert(L, -2); + lua_rawset(L, abs_i); +} + + +COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum) { + lua_Number n = lua_tonumber(L, i); + if (isnum != NULL) { + *isnum = (n != 0 || lua_isnumber(L, i)); + } + return n; +} + + +COMPAT53_API void luaL_checkversion (lua_State *L) { + (void)L; +} + + +COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg) { + if (!lua_checkstack(L, sp+LUA_MINSTACK)) { + if (msg != NULL) + luaL_error(L, "stack overflow (%s)", msg); + else { + lua_pushliteral(L, "stack overflow"); + lua_error(L); + } + } +} + + +COMPAT53_API int luaL_getsubtable (lua_State *L, int i, const char *name) { + int abs_i = lua_absindex(L, i); + luaL_checkstack(L, 3, "not enough stack slots"); + lua_pushstring(L, name); + lua_gettable(L, abs_i); + if (lua_istable(L, -1)) + return 1; + lua_pop(L, 1); + lua_newtable(L); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + lua_settable(L, abs_i); + return 0; +} + + +COMPAT53_API lua_Integer luaL_len (lua_State *L, int i) { + lua_Integer res = 0; + int isnum = 0; + luaL_checkstack(L, 1, "not enough stack slots"); + lua_len(L, i); + res = lua_tointegerx(L, -1, &isnum); + lua_pop(L, 1); + if (!isnum) + luaL_error(L, "object length is not an integer"); + return res; +} + + +COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + luaL_checkstack(L, nup+1, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + lua_pushstring(L, l->name); + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -(nup + 1)); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_settable(L, -(nup + 3)); /* table must be below the upvalues, the name and the closure */ + } + lua_pop(L, nup); /* remove upvalues */ +} + + +COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname) { + luaL_checkstack(L, 1, "not enough stack slots"); + luaL_getmetatable(L, tname); + lua_setmetatable(L, -2); +} + + +COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname) { + void *p = lua_touserdata(L, i); + luaL_checkstack(L, 2, "not enough stack slots"); + if (p == NULL || !lua_getmetatable(L, i)) + return NULL; + else { + int res = 0; + luaL_getmetatable(L, tname); + res = lua_rawequal(L, -1, -2); + lua_pop(L, 2); + if (!res) + p = NULL; + } + return p; +} + + +static int compat53_countlevels (lua_State *L) { + lua_Debug ar; + int li = 1, le = 1; + /* find an upper bound */ + while (lua_getstack(L, le, &ar)) { li = le; le *= 2; } + /* do a binary search */ + while (li < le) { + int m = (li + le)/2; + if (lua_getstack(L, m, &ar)) li = m + 1; + else le = m; + } + return le - 1; +} + +static int compat53_findfield (lua_State *L, int objidx, int level) { + if (level == 0 || !lua_istable(L, -1)) + return 0; /* not found */ + lua_pushnil(L); /* start 'next' loop */ + while (lua_next(L, -2)) { /* for each pair in table */ + if (lua_type(L, -2) == LUA_TSTRING) { /* ignore non-string keys */ + if (lua_rawequal(L, objidx, -1)) { /* found object? */ + lua_pop(L, 1); /* remove value (but keep name) */ + return 1; + } + else if (compat53_findfield(L, objidx, level - 1)) { /* try recursively */ + lua_remove(L, -2); /* remove table (but keep name) */ + lua_pushliteral(L, "."); + lua_insert(L, -2); /* place '.' between the two names */ + lua_concat(L, 3); + return 1; + } + } + lua_pop(L, 1); /* remove value */ + } + return 0; /* not found */ +} + +static int compat53_pushglobalfuncname (lua_State *L, lua_Debug *ar) { + int top = lua_gettop(L); + lua_getinfo(L, "f", ar); /* push function */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + if (compat53_findfield(L, top + 1, 2)) { + lua_copy(L, -1, top + 1); /* move name to proper place */ + lua_pop(L, 2); /* remove pushed values */ + return 1; + } + else { + lua_settop(L, top); /* remove function and global table */ + return 0; + } +} + +static void compat53_pushfuncname (lua_State *L, lua_Debug *ar) { + if (*ar->namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, "function " LUA_QS, ar->name); + else if (*ar->what == 'm') /* main? */ + lua_pushliteral(L, "main chunk"); + else if (*ar->what == 'C') { + if (compat53_pushglobalfuncname(L, ar)) { + lua_pushfstring(L, "function " LUA_QS, lua_tostring(L, -1)); + lua_remove(L, -2); /* remove name */ + } + else + lua_pushliteral(L, "?"); + } + else + lua_pushfstring(L, "function <%s:%d>", ar->short_src, ar->linedefined); +} + +#define COMPAT53_LEVELS1 12 /* size of the first part of the stack */ +#define COMPAT53_LEVELS2 10 /* size of the second part of the stack */ + +COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, + const char *msg, int level) { + lua_Debug ar; + int top = lua_gettop(L); + int numlevels = compat53_countlevels(L1); + int mark = (numlevels > COMPAT53_LEVELS1 + COMPAT53_LEVELS2) ? COMPAT53_LEVELS1 : 0; + if (msg) lua_pushfstring(L, "%s\n", msg); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level == mark) { /* too many levels? */ + lua_pushliteral(L, "\n\t..."); /* add a '...' */ + level = numlevels - COMPAT53_LEVELS2; /* and skip to last ones */ + } + else { + lua_getinfo(L1, "Slnt", &ar); + lua_pushfstring(L, "\n\t%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + lua_pushliteral(L, " in "); + compat53_pushfuncname(L, &ar); + lua_concat(L, lua_gettop(L) - top); + } + } + lua_concat(L, lua_gettop(L) - top); +} + + +COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname) { + const char *serr = NULL; + int en = errno; /* calls to Lua API may change this value */ + char buf[512] = { 0 }; + if (stat) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + serr = compat53_strerror(en, buf, sizeof(buf)); + if (fname) + lua_pushfstring(L, "%s: %s", fname, serr); + else + lua_pushstring(L, serr); + lua_pushnumber(L, (lua_Number)en); + return 3; + } +} + + +static int compat53_checkmode (lua_State *L, const char *mode, const char *modename, int err) { + if (mode && strchr(mode, modename[0]) == NULL) { + lua_pushfstring(L, "attempt to load a %s chunk (mode is '%s')", modename, mode); + return err; + } + return LUA_OK; +} + + +typedef struct { + lua_Reader reader; + void *ud; + int has_peeked_data; + const char *peeked_data; + size_t peeked_data_size; +} compat53_reader_data; + + +static const char *compat53_reader (lua_State *L, void *ud, size_t *size) { + compat53_reader_data *data = (compat53_reader_data *)ud; + if (data->has_peeked_data) { + data->has_peeked_data = 0; + *size = data->peeked_data_size; + return data->peeked_data; + } else + return data->reader(L, data->ud, size); +} + + +COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char *source, const char *mode) { + int status = LUA_OK; + compat53_reader_data compat53_data = { 0, NULL, 1, 0, 0 }; + compat53_data.reader = reader; + compat53_data.ud = data; + compat53_data.peeked_data = reader(L, data, &(compat53_data.peeked_data_size)); + if (compat53_data.peeked_data && compat53_data.peeked_data_size && + compat53_data.peeked_data[0] == LUA_SIGNATURE[0]) /* binary file? */ + status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); + else + status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); + if (status != LUA_OK) + return status; + /* we need to call the original 5.1 version of lua_load! */ +#undef lua_load + return lua_load(L, compat53_reader, &compat53_data, source); +#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) +} + + +typedef struct { + int n; /* number of pre-read characters */ + FILE *f; /* file being read */ + char buff[COMPAT53_LUA_FILE_BUFFER_SIZE]; /* area for reading file */ +} compat53_LoadF; + + +static const char *compat53_getF (lua_State *L, void *ud, size_t *size) { + compat53_LoadF *lf = (compat53_LoadF *)ud; + (void)L; /* not used */ + if (lf->n > 0) { /* are there pre-read characters to be read? */ + *size = lf->n; /* return them (chars already in buffer) */ + lf->n = 0; /* no more pre-read characters */ + } + else { /* read a block from file */ + /* 'fread' can return > 0 *and* set the EOF flag. If next call to + 'compat53_getF' called 'fread', it might still wait for user input. + The next check avoids this problem. */ + if (feof(lf->f)) return NULL; + *size = fread(lf->buff, 1, sizeof(lf->buff), lf->f); /* read block */ + } + return lf->buff; +} + + +static int compat53_errfile (lua_State *L, const char *what, int fnameindex) { + char buf[512] = {0}; + const char *serr = compat53_strerror(errno, buf, sizeof(buf)); + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + + +static int compat53_skipBOM (compat53_LoadF *lf) { + const char *p = "\xEF\xBB\xBF"; /* UTF-8 BOM mark */ + int c; + lf->n = 0; + do { + c = getc(lf->f); + if (c == EOF || c != *(const unsigned char *)p++) return c; + lf->buff[lf->n++] = (char)c; /* to be read by the parser */ + } while (*p != '\0'); + lf->n = 0; /* prefix matched; discard it */ + return getc(lf->f); /* return next character */ +} + + +/* +** reads the first character of file 'f' and skips an optional BOM mark +** in its beginning plus its first line if it starts with '#'. Returns +** true if it skipped the first line. In any case, '*cp' has the +** first "valid" character of the file (after the optional BOM and +** a first-line comment). +*/ +static int compat53_skipcomment (compat53_LoadF *lf, int *cp) { + int c = *cp = compat53_skipBOM(lf); + if (c == '#') { /* first line is a comment (Unix exec. file)? */ + do { /* skip first line */ + c = getc(lf->f); + } while (c != EOF && c != '\n'); + *cp = getc(lf->f); /* skip end-of-line, if present */ + return 1; /* there was a comment */ + } + else return 0; /* no comment */ +} + + +COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode) { + compat53_LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + lf.f = stdin; + } + else { + lua_pushfstring(L, "@%s", filename); +#if defined(_MSC_VER) + /* This code is here to stop a deprecation error that stops builds + * if a certain macro is defined. While normally not caring would + * be best, some header-only libraries and builds can't afford to + * dictate this to the user. A quick check shows that fopen_s this + * goes back to VS 2005, and _fsopen goes back to VS 2003 .NET, + * possibly even before that so we don't need to do any version + * number checks, since this has been there since forever. */ + + /* TO USER: if you want the behavior of typical fopen_s/fopen, + * which does lock the file on VC++, define the macro used below to 0 */ +#if COMPAT53_FOPEN_NO_LOCK + lf.f = _fsopen(filename, "r", _SH_DENYNO); /* do not lock the file in any way */ + if (lf.f == NULL) + return compat53_errfile(L, "open", fnameindex); +#else /* use default locking version */ + if (fopen_s(&lf.f, filename, "r") != 0) + return compat53_errfile(L, "open", fnameindex); +#endif /* Locking vs. No-locking fopen variants */ +#else + lf.f = fopen(filename, "r"); /* default stdlib doesn't forcefully lock files here */ + if (lf.f == NULL) return compat53_errfile(L, "open", fnameindex); +#endif + } + if (compat53_skipcomment(&lf, &c)) /* read initial portion */ + lf.buff[lf.n++] = '\n'; /* add line to correct line numbers */ + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ +#if defined(_MSC_VER) + if (freopen_s(&lf.f, filename, "rb", lf.f) != 0) + return compat53_errfile(L, "reopen", fnameindex); +#else + lf.f = freopen(filename, "rb", lf.f); /* reopen in binary mode */ + if (lf.f == NULL) return compat53_errfile(L, "reopen", fnameindex); +#endif + compat53_skipcomment(&lf, &c); /* re-read initial portion */ + } + if (c != EOF) + lf.buff[lf.n++] = (char)c; /* 'c' is the first character of the stream */ + status = lua_load(L, &compat53_getF, &lf, lua_tostring(L, -1), mode); + readstatus = ferror(lf.f); + if (filename) fclose(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from 'lua_load' */ + return compat53_errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode) { + int status = LUA_OK; + if (sz > 0 && buff[0] == LUA_SIGNATURE[0]) { + status = compat53_checkmode(L, mode, "binary", LUA_ERRSYNTAX); + } + else { + status = compat53_checkmode(L, mode, "text", LUA_ERRSYNTAX); + } + if (status != LUA_OK) + return status; + return luaL_loadbuffer(L, buff, sz, name); +} + + +#if !defined(l_inspectstat) && \ + (defined(unix) || defined(__unix) || defined(__unix__) || \ + defined(__TOS_AIX__) || defined(_SYSTYPE_BSD) || \ + (defined(__APPLE__) && defined(__MACH__))) +/* some form of unix; check feature macros in unistd.h for details */ +# include +/* check posix version; the relevant include files and macros probably + * were available before 2001, but I'm not sure */ +# if defined(_POSIX_VERSION) && _POSIX_VERSION >= 200112L +# include +# define l_inspectstat(stat,what) \ + if (WIFEXITED(stat)) { stat = WEXITSTATUS(stat); } \ + else if (WIFSIGNALED(stat)) { stat = WTERMSIG(stat); what = "signal"; } +# endif +#endif + +/* provide default (no-op) version */ +#if !defined(l_inspectstat) +# define l_inspectstat(stat,what) ((void)0) +#endif + + +COMPAT53_API int luaL_execresult (lua_State *L, int stat) { + const char *what = "exit"; + if (stat == -1) + return luaL_fileresult(L, 0, NULL); + else { + l_inspectstat(stat, what); + if (*what == 'e' && stat == 0) + lua_pushboolean(L, 1); + else + lua_pushnil(L); + lua_pushstring(L, what); + lua_pushinteger(L, stat); + return 3; + } +} + + +COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B) { + /* make it crash if used via pointer to a 5.1-style luaL_Buffer */ + B->b.p = NULL; + B->b.L = NULL; + B->b.lvl = 0; + /* reuse the buffer from the 5.1-style luaL_Buffer though! */ + B->ptr = B->b.buffer; + B->capacity = LUAL_BUFFERSIZE; + B->nelems = 0; + B->L2 = L; +} + + +COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s) { + if (B->capacity - B->nelems < s) { /* needs to grow */ + char* newptr = NULL; + size_t newcap = B->capacity * 2; + if (newcap - B->nelems < s) + newcap = B->nelems + s; + if (newcap < B->capacity) /* overflow */ + luaL_error(B->L2, "buffer too large"); + newptr = (char*)lua_newuserdata(B->L2, newcap); + memcpy(newptr, B->ptr, B->nelems); + if (B->ptr != B->b.buffer) + lua_replace(B->L2, -2); /* remove old buffer */ + B->ptr = newptr; + B->capacity = newcap; + } + return B->ptr+B->nelems; +} + + +COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l) { + memcpy(luaL_prepbuffsize(B, l), s, l); + luaL_addsize(B, l); +} + + +COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B) { + size_t len = 0; + const char *s = lua_tolstring(B->L2, -1, &len); + if (!s) + luaL_error(B->L2, "cannot convert value to string"); + if (B->ptr != B->b.buffer) + lua_insert(B->L2, -2); /* userdata buffer must be at stack top */ + luaL_addlstring(B, s, len); + lua_remove(B->L2, B->ptr != B->b.buffer ? -2 : -1); +} + + +void luaL_pushresult (luaL_Buffer_53 *B) { + lua_pushlstring(B->L2, B->ptr, B->nelems); + if (B->ptr != B->b.buffer) + lua_replace(B->L2, -2); /* remove userdata buffer */ +} + + +#endif /* Lua 5.1 */ + + + +/* definitions for Lua 5.1 and Lua 5.2 */ +#if defined( LUA_VERSION_NUM ) && LUA_VERSION_NUM <= 502 + + +COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len) { +#undef lua_pushlstring + lua_pushlstring(L, len > 0 ? s : "", len); +#define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) + return lua_tostring(L, -1); +} + + +COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i) { + index = lua_absindex(L, index); + lua_pushinteger(L, i); + lua_gettable(L, index); + return lua_type(L, -1); +} + + +#ifndef LUA_EXTRASPACE +#define LUA_EXTRASPACE (sizeof(void*)) +#endif + +COMPAT53_API void *lua_getextraspace (lua_State *L) { + int is_main = 0; + void *ptr = NULL; + luaL_checkstack(L, 4, "not enough stack slots available"); + lua_pushliteral(L, "__compat53_extraspace"); + lua_pushvalue(L, -1); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 1); + lua_pushliteral(L, "k"); + lua_setfield(L, -2, "__mode"); + lua_setmetatable(L, -2); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } + lua_replace(L, -2); + is_main = lua_pushthread(L); + lua_rawget(L, -2); + ptr = lua_touserdata(L, -1); + if (!ptr) { + lua_pop(L, 1); + ptr = lua_newuserdata(L, LUA_EXTRASPACE); + if (is_main) { + memset(ptr, '\0', LUA_EXTRASPACE); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + lua_pushboolean(L, 1); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } else { + void* mptr = NULL; + lua_pushboolean(L, 1); + lua_rawget(L, -3); + mptr = lua_touserdata(L, -1); + if (mptr) + memcpy(ptr, mptr, LUA_EXTRASPACE); + else + memset(ptr, '\0', LUA_EXTRASPACE); + lua_pop(L, 1); + lua_pushthread(L); + lua_pushvalue(L, -2); + lua_rawset(L, -4); + } + } + lua_pop(L, 2); + return ptr; +} + + +COMPAT53_API int lua_isinteger (lua_State *L, int index) { + if (lua_type(L, index) == LUA_TNUMBER) { + lua_Number n = lua_tonumber(L, index); + lua_Integer i = lua_tointeger(L, index); + if (i == n) + return 1; + } + return 0; +} + + +COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum) { + int ok = 0; + lua_Number n = lua_tonumberx(L, i, &ok); + if (ok) { + if (n == (lua_Integer)n) { + if (isnum) + *isnum = 1; + return (lua_Integer)n; + } + } + if (isnum) + *isnum = 0; + return 0; +} + + +static void compat53_reverse (lua_State *L, int a, int b) { + for (; a < b; ++a, --b) { + lua_pushvalue(L, a); + lua_pushvalue(L, b); + lua_replace(L, a); + lua_replace(L, b); + } +} + + +COMPAT53_API void lua_rotate (lua_State *L, int idx, int n) { + int n_elems = 0; + idx = lua_absindex(L, idx); + n_elems = lua_gettop(L)-idx+1; + if (n < 0) + n += n_elems; + if ( n > 0 && n < n_elems) { + luaL_checkstack(L, 2, "not enough stack slots available"); + n = n_elems - n; + compat53_reverse(L, idx, idx+n-1); + compat53_reverse(L, idx+n, idx+n_elems-1); + compat53_reverse(L, idx, idx+n_elems-1); + } +} + + +COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i) { + luaL_checkstack(L, 1, "not enough stack slots available"); + index = lua_absindex(L, index); + lua_pushinteger(L, i); + lua_insert(L, -2); + lua_settable(L, index); +} + + +#if !defined(lua_str2number) +# define lua_str2number(s, p) strtod((s), (p)) +#endif + +COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s) { + char* endptr; + lua_Number n = lua_str2number(s, &endptr); + if (endptr != s) { + while (*endptr != '\0' && isspace((unsigned char)*endptr)) + ++endptr; + if (*endptr == '\0') { + lua_pushnumber(L, n); + return endptr - s + 1; + } + } + return 0; +} + + +COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len) { + if (!luaL_callmeta(L, idx, "__tostring")) { + int t = lua_type(L, idx), tt = 0; + char const* name = NULL; + switch (t) { + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + case LUA_TSTRING: + case LUA_TNUMBER: + lua_pushvalue(L, idx); + break; + case LUA_TBOOLEAN: + if (lua_toboolean(L, idx)) + lua_pushliteral(L, "true"); + else + lua_pushliteral(L, "false"); + break; + default: + tt = luaL_getmetafield(L, idx, "__name"); + name = (tt == LUA_TSTRING) ? lua_tostring(L, -1) : lua_typename(L, t); + lua_pushfstring(L, "%s: %p", name, lua_topointer(L, idx)); + if (tt != LUA_TNIL) + lua_replace(L, -2); + break; + } + } else { + if (!lua_isstring(L, -1)) + luaL_error(L, "'__tostring' must return a string"); + } + return lua_tolstring(L, -1, len); +} + + +COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb) { + luaL_checkstack(L, 3, "not enough stack slots available"); + luaL_getsubtable(L, LUA_REGISTRYINDEX, "_LOADED"); + if (lua_getfield(L, -1, modname) == LUA_TNIL) { + lua_pop(L, 1); + lua_pushcfunction(L, openf); + lua_pushstring(L, modname); + lua_call(L, 1, 1); + lua_pushvalue(L, -1); + lua_setfield(L, -3, modname); + } + if (glb) { + lua_pushvalue(L, -1); + lua_setglobal(L, modname); + } + lua_replace(L, -2); +} + + +#endif /* Lua 5.1 and 5.2 */ + + +#endif /* COMPAT53_C_ */ + + +/********************************************************************* +* This file contains parts of Lua 5.2's and Lua 5.3's source code: +* +* Copyright (C) 1994-2014 Lua.org, PUC-Rio. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*********************************************************************/ + diff --git a/justenough/compat-5.3.h b/justenough/compat-5.3.h new file mode 100644 index 000000000..6f66dadb9 --- /dev/null +++ b/justenough/compat-5.3.h @@ -0,0 +1,421 @@ +#ifndef COMPAT53_H_ +#define COMPAT53_H_ + +#include +#include +#include +#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) +extern "C" { +#endif +#include +#include +#include +#if defined(__cplusplus) && !defined(COMPAT53_LUA_CPP) +} +#endif + + +#undef COMPAT53_INCLUDE_SOURCE +#if defined(COMPAT53_PREFIX) +/* - change the symbol names of functions to avoid linker conflicts + * - compat-5.3.c needs to be compiled (and linked) separately + */ +# if !defined(COMPAT53_API) +# define COMPAT53_API extern +# endif +#else /* COMPAT53_PREFIX */ +/* - make all functions static and include the source. + * - compat-5.3.c doesn't need to be compiled (and linked) separately + */ +# define COMPAT53_PREFIX compat53 +# undef COMPAT53_API +# if defined(__GNUC__) || defined(__clang__) +# define COMPAT53_API __attribute__((__unused__)) static +# else +# define COMPAT53_API static +# endif +# define COMPAT53_INCLUDE_SOURCE +#endif /* COMPAT53_PREFIX */ + +#define COMPAT53_CONCAT_HELPER(a, b) a##b +#define COMPAT53_CONCAT(a, b) COMPAT53_CONCAT_HELPER(a, b) + + + +/* declarations for Lua 5.1 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 501 + +/* XXX not implemented: + * lua_arith (new operators) + * lua_upvalueid + * lua_upvaluejoin + * lua_version + * lua_yieldk + */ + +#ifndef LUA_OK +# define LUA_OK 0 +#endif +#ifndef LUA_OPADD +# define LUA_OPADD 0 +#endif +#ifndef LUA_OPSUB +# define LUA_OPSUB 1 +#endif +#ifndef LUA_OPMUL +# define LUA_OPMUL 2 +#endif +#ifndef LUA_OPDIV +# define LUA_OPDIV 3 +#endif +#ifndef LUA_OPMOD +# define LUA_OPMOD 4 +#endif +#ifndef LUA_OPPOW +# define LUA_OPPOW 5 +#endif +#ifndef LUA_OPUNM +# define LUA_OPUNM 6 +#endif +#ifndef LUA_OPEQ +# define LUA_OPEQ 0 +#endif +#ifndef LUA_OPLT +# define LUA_OPLT 1 +#endif +#ifndef LUA_OPLE +# define LUA_OPLE 2 +#endif + +/* LuaJIT/Lua 5.1 does not have the updated + * error codes for thread status/function returns (but some patched versions do) + * define it only if it's not found + */ +#if !defined(LUA_ERRGCMM) +/* Use + 2 because in some versions of Lua (Lua 5.1) + * LUA_ERRFILE is defined as (LUA_ERRERR+1) + * so we need to avoid it (LuaJIT might have something at this + * integer value too) + */ +# define LUA_ERRGCMM (LUA_ERRERR + 2) +#endif /* LUA_ERRGCMM define */ + +typedef size_t lua_Unsigned; + +typedef struct luaL_Buffer_53 { + luaL_Buffer b; /* make incorrect code crash! */ + char *ptr; + size_t nelems; + size_t capacity; + lua_State *L2; +} luaL_Buffer_53; +#define luaL_Buffer luaL_Buffer_53 + +/* In PUC-Rio 5.1, userdata is a simple FILE* + * In LuaJIT, it's a struct where the first member is a FILE* + * We can't support the `closef` member + */ +typedef struct luaL_Stream { + FILE *f; +} luaL_Stream; + +#define lua_absindex COMPAT53_CONCAT(COMPAT53_PREFIX, _absindex) +COMPAT53_API int lua_absindex (lua_State *L, int i); + +#define lua_arith COMPAT53_CONCAT(COMPAT53_PREFIX, _arith) +COMPAT53_API void lua_arith (lua_State *L, int op); + +#define lua_compare COMPAT53_CONCAT(COMPAT53_PREFIX, _compare) +COMPAT53_API int lua_compare (lua_State *L, int idx1, int idx2, int op); + +#define lua_copy COMPAT53_CONCAT(COMPAT53_PREFIX, _copy) +COMPAT53_API void lua_copy (lua_State *L, int from, int to); + +#define lua_getuservalue(L, i) \ + (lua_getfenv((L), (i)), lua_type((L), -1)) +#define lua_setuservalue(L, i) \ + (luaL_checktype((L), -1, LUA_TTABLE), lua_setfenv((L), (i))) + +#define lua_len COMPAT53_CONCAT(COMPAT53_PREFIX, _len) +COMPAT53_API void lua_len (lua_State *L, int i); + +#define lua_pushstring(L, s) \ + (lua_pushstring((L), (s)), lua_tostring((L), -1)) + +#ifndef luaL_newlibtable +# define luaL_newlibtable(L, l) \ + (lua_createtable((L), 0, sizeof((l))/sizeof(*(l))-1)) +#endif +#ifndef luaL_newlib +# define luaL_newlib(L, l) \ + (luaL_newlibtable((L), (l)), luaL_register((L), NULL, (l))) +#endif + +#define lua_pushglobaltable(L) \ + lua_pushvalue((L), LUA_GLOBALSINDEX) + +#define lua_rawgetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawgetp) +COMPAT53_API int lua_rawgetp (lua_State *L, int i, const void *p); + +#define lua_rawsetp COMPAT53_CONCAT(COMPAT53_PREFIX, _rawsetp) +COMPAT53_API void lua_rawsetp(lua_State *L, int i, const void *p); + +#define lua_rawlen(L, i) lua_objlen((L), (i)) + +#define lua_tointeger(L, i) lua_tointegerx((L), (i), NULL) + +#define lua_tonumberx COMPAT53_CONCAT(COMPAT53_PREFIX, _tonumberx) +COMPAT53_API lua_Number lua_tonumberx (lua_State *L, int i, int *isnum); + +#define luaL_checkversion COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkversion) +COMPAT53_API void luaL_checkversion (lua_State *L); + +#define lua_load COMPAT53_CONCAT(COMPAT53_PREFIX, _load_53) +COMPAT53_API int lua_load (lua_State *L, lua_Reader reader, void *data, const char* source, const char* mode); + +#define luaL_loadfilex COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadfilex) +COMPAT53_API int luaL_loadfilex (lua_State *L, const char *filename, const char *mode); + +#define luaL_loadbufferx COMPAT53_CONCAT(COMPAT53_PREFIX, L_loadbufferx) +COMPAT53_API int luaL_loadbufferx (lua_State *L, const char *buff, size_t sz, const char *name, const char *mode); + +#define luaL_checkstack COMPAT53_CONCAT(COMPAT53_PREFIX, L_checkstack_53) +COMPAT53_API void luaL_checkstack (lua_State *L, int sp, const char *msg); + +#define luaL_getsubtable COMPAT53_CONCAT(COMPAT53_PREFIX, L_getsubtable) +COMPAT53_API int luaL_getsubtable (lua_State* L, int i, const char *name); + +#define luaL_len COMPAT53_CONCAT(COMPAT53_PREFIX, L_len) +COMPAT53_API lua_Integer luaL_len (lua_State *L, int i); + +#define luaL_setfuncs COMPAT53_CONCAT(COMPAT53_PREFIX, L_setfuncs) +COMPAT53_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup); + +#define luaL_setmetatable COMPAT53_CONCAT(COMPAT53_PREFIX, L_setmetatable) +COMPAT53_API void luaL_setmetatable (lua_State *L, const char *tname); + +#define luaL_testudata COMPAT53_CONCAT(COMPAT53_PREFIX, L_testudata) +COMPAT53_API void *luaL_testudata (lua_State *L, int i, const char *tname); + +#define luaL_traceback COMPAT53_CONCAT(COMPAT53_PREFIX, L_traceback) +COMPAT53_API void luaL_traceback (lua_State *L, lua_State *L1, const char *msg, int level); + +#define luaL_fileresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_fileresult) +COMPAT53_API int luaL_fileresult (lua_State *L, int stat, const char *fname); + +#define luaL_execresult COMPAT53_CONCAT(COMPAT53_PREFIX, L_execresult) +COMPAT53_API int luaL_execresult (lua_State *L, int stat); + +#define lua_callk(L, na, nr, ctx, cont) \ + ((void)(ctx), (void)(cont), lua_call((L), (na), (nr))) +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + ((void)(ctx), (void)(cont), lua_pcall((L), (na), (nr), (err))) + +#define lua_resume(L, from, nargs) \ + ((void)(from), lua_resume((L), (nargs))) + +#define luaL_buffinit COMPAT53_CONCAT(COMPAT53_PREFIX, _buffinit_53) +COMPAT53_API void luaL_buffinit (lua_State *L, luaL_Buffer_53 *B); + +#define luaL_prepbuffsize COMPAT53_CONCAT(COMPAT53_PREFIX, _prepbufsize_53) +COMPAT53_API char *luaL_prepbuffsize (luaL_Buffer_53 *B, size_t s); + +#define luaL_addlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _addlstring_53) +COMPAT53_API void luaL_addlstring (luaL_Buffer_53 *B, const char *s, size_t l); + +#define luaL_addvalue COMPAT53_CONCAT(COMPAT53_PREFIX, _addvalue_53) +COMPAT53_API void luaL_addvalue (luaL_Buffer_53 *B); + +#define luaL_pushresult COMPAT53_CONCAT(COMPAT53_PREFIX, _pushresult_53) +COMPAT53_API void luaL_pushresult (luaL_Buffer_53 *B); + +#undef luaL_buffinitsize +#define luaL_buffinitsize(L, B, s) \ + (luaL_buffinit((L), (B)), luaL_prepbuffsize((B), (s))) + +#undef luaL_prepbuffer +#define luaL_prepbuffer(B) \ + luaL_prepbuffsize((B), LUAL_BUFFERSIZE) + +#undef luaL_addchar +#define luaL_addchar(B, c) \ + ((void)((B)->nelems < (B)->capacity || luaL_prepbuffsize((B), 1)), \ + ((B)->ptr[(B)->nelems++] = (c))) + +#undef luaL_addsize +#define luaL_addsize(B, s) \ + ((B)->nelems += (s)) + +#undef luaL_addstring +#define luaL_addstring(B, s) \ + luaL_addlstring((B), (s), strlen((s))) + +#undef luaL_pushresultsize +#define luaL_pushresultsize(B, s) \ + (luaL_addsize((B), (s)), luaL_pushresult((B))) + +#if defined(LUA_COMPAT_APIINTCASTS) +#define lua_pushunsigned(L, n) \ + lua_pushinteger((L), (lua_Integer)(n)) +#define lua_tounsignedx(L, i, is) \ + ((lua_Unsigned)lua_tointegerx((L), (i), (is))) +#define lua_tounsigned(L, i) \ + lua_tounsignedx((L), (i), NULL) +#define luaL_checkunsigned(L, a) \ + ((lua_Unsigned)luaL_checkinteger((L), (a))) +#define luaL_optunsigned(L, a, d) \ + ((lua_Unsigned)luaL_optinteger((L), (a), (lua_Integer)(d))) +#endif + +#endif /* Lua 5.1 only */ + + + +/* declarations for Lua 5.1 and 5.2 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM <= 502 + +typedef int lua_KContext; + +typedef int (*lua_KFunction)(lua_State *L, int status, lua_KContext ctx); + +#define lua_dump(L, w, d, s) \ + ((void)(s), lua_dump((L), (w), (d))) + +#define lua_getfield(L, i, k) \ + (lua_getfield((L), (i), (k)), lua_type((L), -1)) + +#define lua_gettable(L, i) \ + (lua_gettable((L), (i)), lua_type((L), -1)) + +#define lua_pushlstring COMPAT53_CONCAT(COMPAT53_PREFIX, _pushlstring_53) +COMPAT53_API const char *lua_pushlstring (lua_State *L, const char *s, size_t len); + +#define lua_geti COMPAT53_CONCAT(COMPAT53_PREFIX, _geti) +COMPAT53_API int lua_geti (lua_State *L, int index, lua_Integer i); + +#define lua_getextraspace COMPAT53_CONCAT(COMPAT53_PREFIX, _getextraspace) +COMPAT53_API void *lua_getextraspace (lua_State *L); + +#define lua_isinteger COMPAT53_CONCAT(COMPAT53_PREFIX, _isinteger) +COMPAT53_API int lua_isinteger (lua_State *L, int index); + +#define lua_tointegerx COMPAT53_CONCAT(COMPAT53_PREFIX, _tointegerx_53) +COMPAT53_API lua_Integer lua_tointegerx (lua_State *L, int i, int *isnum); + +#define lua_numbertointeger(n, p) \ + ((*(p) = (lua_Integer)(n)), 1) + +#define lua_rawget(L, i) \ + (lua_rawget((L), (i)), lua_type((L), -1)) + +#define lua_rawgeti(L, i, n) \ + (lua_rawgeti((L), (i), (n)), lua_type((L), -1)) + +#define lua_rotate COMPAT53_CONCAT(COMPAT53_PREFIX, _rotate) +COMPAT53_API void lua_rotate (lua_State *L, int idx, int n); + +#define lua_seti COMPAT53_CONCAT(COMPAT53_PREFIX, _seti) +COMPAT53_API void lua_seti (lua_State *L, int index, lua_Integer i); + +#define lua_stringtonumber COMPAT53_CONCAT(COMPAT53_PREFIX, _stringtonumber) +COMPAT53_API size_t lua_stringtonumber (lua_State *L, const char *s); + +#define luaL_tolstring COMPAT53_CONCAT(COMPAT53_PREFIX, L_tolstring) +COMPAT53_API const char *luaL_tolstring (lua_State *L, int idx, size_t *len); + +#define luaL_getmetafield(L, o, e) \ + (luaL_getmetafield((L), (o), (e)) ? lua_type((L), -1) : LUA_TNIL) + +#define luaL_newmetatable(L, tn) \ + (luaL_newmetatable((L), (tn)) ? (lua_pushstring((L), (tn)), lua_setfield((L), -2, "__name"), 1) : 0) + +#define luaL_requiref COMPAT53_CONCAT(COMPAT53_PREFIX, L_requiref_53) +COMPAT53_API void luaL_requiref (lua_State *L, const char *modname, + lua_CFunction openf, int glb ); + +#endif /* Lua 5.1 and Lua 5.2 */ + + + +/* declarations for Lua 5.2 */ +#if defined(LUA_VERSION_NUM) && LUA_VERSION_NUM == 502 + +/* XXX not implemented: + * lua_isyieldable + * lua_arith (new operators) + * lua_pushfstring (new formats) + */ + +#define lua_getglobal(L, n) \ + (lua_getglobal((L), (n)), lua_type((L), -1)) + +#define lua_getuservalue(L, i) \ + (lua_getuservalue((L), (i)), lua_type((L), -1)) + +#define lua_rawgetp(L, i, p) \ + (lua_rawgetp((L), (i), (p)), lua_type((L), -1)) + +#define LUA_KFUNCTION(_name) \ + static int (_name)(lua_State *L, int status, lua_KContext ctx); \ + static int (_name ## _52)(lua_State *L) { \ + lua_KContext ctx; \ + int status = lua_getctx(L, &ctx); \ + return (_name)(L, status, ctx); \ + } \ + static int (_name)(lua_State *L, int status, lua_KContext ctx) + +#define lua_pcallk(L, na, nr, err, ctx, cont) \ + lua_pcallk((L), (na), (nr), (err), (ctx), cont ## _52) + +#define lua_callk(L, na, nr, ctx, cont) \ + lua_callk((L), (na), (nr), (ctx), cont ## _52) + +#define lua_yieldk(L, nr, ctx, cont) \ + lua_yieldk((L), (nr), (ctx), cont ## _52) + +#ifdef lua_call +# undef lua_call +# define lua_call(L, na, nr) \ + (lua_callk)((L), (na), (nr), 0, NULL) +#endif + +#ifdef lua_pcall +# undef lua_pcall +# define lua_pcall(L, na, nr, err) \ + (lua_pcallk)((L), (na), (nr), (err), 0, NULL) +#endif + +#ifdef lua_yield +# undef lua_yield +# define lua_yield(L, nr) \ + (lua_yieldk)((L), (nr), 0, NULL) +#endif + +#endif /* Lua 5.2 only */ + + + +/* other Lua versions */ +#if !defined(LUA_VERSION_NUM) || LUA_VERSION_NUM < 501 || LUA_VERSION_NUM > 504 + +# error "unsupported Lua version (i.e. not Lua 5.1, 5.2, 5.3, or 5.4)" + +#endif /* other Lua versions except 5.1, 5.2, 5.3, and 5.4 */ + + + +/* helper macro for defining continuation functions (for every version + * *except* Lua 5.2) */ +#ifndef LUA_KFUNCTION +#define LUA_KFUNCTION(_name) \ + static int (_name)(lua_State *L, int status, lua_KContext ctx) +#endif + + +#if defined(COMPAT53_INCLUDE_SOURCE) +# include "compat-5.3.c" +#endif + + +#endif /* COMPAT53_H_ */ + diff --git a/src/fontmetrics.c b/justenough/fontmetrics.c similarity index 59% rename from src/fontmetrics.c rename to justenough/fontmetrics.c index 0d7396d98..6ff556075 100644 --- a/src/fontmetrics.c +++ b/justenough/fontmetrics.c @@ -6,8 +6,11 @@ #include "hb-utils.h" +// #define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" -int get_typographic_extents (lua_State *L) { + +int fm_get_typographic_extents (lua_State *L) { double upem; double ascender; double descender; @@ -35,7 +38,7 @@ int get_typographic_extents (lua_State *L) { } -int glyphwidth (lua_State* L) { +int fm_glyphwidth (lua_State* L) { size_t font_l; unsigned int gid = luaL_checknumber(L, 1); hb_font_t* hbFont = get_hb_font(L, 2); @@ -45,35 +48,14 @@ int glyphwidth (lua_State* L) { return 1; } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif static const struct luaL_Reg lib_table [] = { - {"get_typographic_extents", get_typographic_extents}, - {"glyphwidth", glyphwidth}, + {"get_typographic_extents", fm_get_typographic_extents}, + {"glyphwidth", fm_glyphwidth}, {NULL, NULL} }; + int luaopen_fontmetrics (lua_State *L) { lua_newtable(L); luaL_setfuncs(L, lib_table, 0); diff --git a/src/hb-utils.c b/justenough/hb-utils.c similarity index 99% rename from src/hb-utils.c rename to justenough/hb-utils.c index 024fe4595..7a79111fa 100644 --- a/src/hb-utils.c +++ b/justenough/hb-utils.c @@ -1,9 +1,8 @@ -#include "hb-utils.h" - #include #include #include +#include "hb-utils.h" #include "silewin32.h" static hb_variation_t* scan_variation_string(const char* cp1, unsigned int* ret) { @@ -148,5 +147,3 @@ hb_font_t* get_hb_font(lua_State *L, int index) { return font; } - - diff --git a/src/hb-utils.h b/justenough/hb-utils.h similarity index 100% rename from src/hb-utils.h rename to justenough/hb-utils.h diff --git a/src/imagebbox.c b/justenough/imagebbox.c similarity index 97% rename from src/imagebbox.c rename to justenough/imagebbox.c index 60c159b15..51acb1f26 100644 --- a/src/imagebbox.c +++ b/justenough/imagebbox.c @@ -1,6 +1,7 @@ #include #include -#include "libtexpdf/libtexpdf.h" + +#include int get_pdf_bbox(FILE* f, long page_no, double* llx, double* lly, double* urx, double* ury) { pdf_obj* page; @@ -68,5 +69,3 @@ int get_image_bbox(FILE* f, long page_no, double* llx, double* lly, double* urx, *yresol = ydensity != 0 ? 72 / ydensity : 0; return 0; } - - diff --git a/src/justenoughfontconfig.c b/justenough/justenoughfontconfig.c similarity index 86% rename from src/justenoughfontconfig.c rename to justenough/justenoughfontconfig.c index aa7b069fc..91d1b5225 100644 --- a/src/justenoughfontconfig.c +++ b/justenough/justenoughfontconfig.c @@ -9,7 +9,10 @@ #include "silewin32.h" -int face_from_options(lua_State* L) { +// #define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + +int je_face_from_options(lua_State* L) { FcChar8 * font_path, * fullname, * familyname; FcPattern* p; FcPattern* matched; @@ -156,31 +159,8 @@ int face_from_options(lua_State* L) { return 1; } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - static const struct luaL_Reg lib_table [] = { - {"_face", face_from_options}, + {"_face", je_face_from_options}, {NULL, NULL} }; @@ -189,4 +169,3 @@ int luaopen_justenoughfontconfig (lua_State *L) { luaL_setfuncs(L, lib_table, 0); return 1; } - diff --git a/src/justenoughharfbuzz.c b/justenough/justenoughharfbuzz.c similarity index 90% rename from src/justenoughharfbuzz.c rename to justenough/justenoughharfbuzz.c index 336d49baa..52ae3073e 100644 --- a/src/justenoughharfbuzz.c +++ b/justenough/justenoughharfbuzz.c @@ -12,9 +12,11 @@ #include #include +#include "hb-utils.h" #include "silewin32.h" -#include "hb-utils.h" +/* #define COMPAT53_PREFIX compat53 */ +#include "compat-5.3.h" /* The following function stolen from XeTeX_ext.c */ static hb_tag_t @@ -135,7 +137,7 @@ static char** scan_shaper_list(char* cp1) { return res; } -int shape (lua_State *L) { +int je_hb_shape (lua_State *L) { size_t font_l; const char * text = luaL_checkstring(L, 1); hb_font_t * hbFont = get_hb_font(L, 2); @@ -144,10 +146,10 @@ int shape (lua_State *L) { const char * lang = luaL_checkstring(L, 5); double point_size = luaL_checknumber(L, 6); const char * featurestring = luaL_checkstring(L, 7); - char * shaper_list_string = luaL_checkstring(L, 8); - char ** shaper_list = NULL; + char * shaper_list_string = (char *)luaL_checkstring(L, 8); + const char * const* shaper_list = NULL; if (strlen(shaper_list_string) > 0) { - shaper_list = scan_shaper_list(shaper_list_string); + shaper_list = (const char * const*)scan_shaper_list(shaper_list_string); } hb_direction_t direction; @@ -185,12 +187,12 @@ int shape (lua_State *L) { } glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); - lua_checkstack(L, glyph_count); for (j = 0; j < glyph_count; ++j) { char namebuf[255]; hb_glyph_extents_t extents = {0,0,0,0}; hb_font_get_glyph_extents(hbFont, glyph_info[j].codepoint, &extents); + lua_checkstack(L, 3); lua_newtable(L); lua_pushstring(L, "name"); hb_font_get_glyph_name( hbFont, glyph_info[j].codepoint, namebuf, 255 ); @@ -206,21 +208,25 @@ int shape (lua_State *L) { baseline, and we should use that and take out this condition. */ if (direction != HB_DIRECTION_TTB) { if (glyph_pos[j].x_offset) { + lua_checkstack(L, 2); lua_pushstring(L, "x_offset"); lua_pushnumber(L, glyph_pos[j].x_offset * point_size / upem); lua_settable(L, -3); } if (glyph_pos[j].y_offset) { + lua_checkstack(L, 2); lua_pushstring(L, "y_offset"); lua_pushnumber(L, glyph_pos[j].y_offset * point_size / upem); lua_settable(L, -3); } } + lua_checkstack(L, 2); lua_pushstring(L, "gid"); lua_pushinteger(L, glyph_info[j].codepoint); lua_settable(L, -3); + lua_checkstack(L, 2); lua_pushstring(L, "index"); lua_pushinteger(L, glyph_info[j].cluster); lua_settable(L, -3); @@ -242,17 +248,21 @@ int shape (lua_State *L) { width = glyphAdvance; glyphAdvance = height; } + lua_checkstack(L, 2); lua_pushstring(L, "glyphAdvance"); lua_pushnumber(L, glyphAdvance); lua_settable(L, -3); + lua_checkstack(L, 2); lua_pushstring(L, "width"); lua_pushnumber(L, width); lua_settable(L, -3); + lua_checkstack(L, 2); lua_pushstring(L, "height"); lua_pushnumber(L, height); lua_settable(L, -3); + lua_checkstack(L, 2); lua_pushstring(L, "depth"); lua_pushnumber(L, -tHeight - height); lua_settable(L, -3); @@ -277,7 +287,7 @@ static int has_table(hb_face_t* face, hb_tag_t tag) { return ret; } -int instanciate(lua_State *L) { +int je_hb_instanciate(lua_State *L) { unsigned int data_l = 0; const char * data_s = NULL; #ifdef HAVE_HARFBUZZ_SUBSET @@ -367,7 +377,7 @@ int instanciate(lua_State *L) { return 1; } -int get_glyph_dimensions(lua_State *L) { +int je_hb_get_glyph_dimensions(lua_State *L) { hb_font_t* hbFont = get_hb_font(L, 1); double point_size = (unsigned int)luaL_checknumber(L, 2); hb_codepoint_t glyphId = (hb_codepoint_t)luaL_checknumber(L, 3); @@ -402,7 +412,7 @@ int get_glyph_dimensions(lua_State *L) { return 1; } -int get_harfbuzz_version (lua_State *L) { +int je_hb_get_harfbuzz_version (lua_State *L) { unsigned int major; unsigned int minor; unsigned int micro; @@ -413,7 +423,7 @@ int get_harfbuzz_version (lua_State *L) { return 1; } -int version_lessthan (lua_State *L) { +int je_hb_version_lessthan (lua_State *L) { unsigned int major = luaL_checknumber(L, 1); unsigned int minor = luaL_checknumber(L, 2); unsigned int micro = luaL_checknumber(L, 3); @@ -421,7 +431,7 @@ int version_lessthan (lua_State *L) { return 1; } -int list_shapers (lua_State *L) { +int je_hb_list_shapers (lua_State *L) { const char **shaper_list = hb_shape_list_shapers (); int i = 0; @@ -432,7 +442,7 @@ int list_shapers (lua_State *L) { return i; } -int get_table (lua_State *L) { +int je_hb_get_table (lua_State *L) { size_t tag_l; hb_face_t * face = hb_font_get_face(get_hb_font(L, 1)); const char * tag_s = luaL_checklstring(L, 2, &tag_l); @@ -448,44 +458,19 @@ int get_table (lua_State *L) { return 1; } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - static const struct luaL_Reg lib_table [] = { - {"_shape", shape}, - {"get_glyph_dimensions", get_glyph_dimensions}, - {"version", get_harfbuzz_version}, - {"shapers", list_shapers}, - {"get_table", get_table}, - {"instanciate", instanciate}, - {"version_lessthan", version_lessthan}, + {"_shape", je_hb_shape}, + {"get_glyph_dimensions", je_hb_get_glyph_dimensions}, + {"version", je_hb_get_harfbuzz_version}, + {"shapers", je_hb_list_shapers}, + {"get_table", je_hb_get_table}, + {"instanciate", je_hb_instanciate}, + {"version_lessthan", je_hb_version_lessthan}, {NULL, NULL} }; int luaopen_justenoughharfbuzz (lua_State *L) { lua_newtable(L); luaL_setfuncs(L, lib_table, 0); - //lua_setglobal(L, "harfbuzz"); return 1; } - diff --git a/src/justenoughicu.c b/justenough/justenoughicu.c similarity index 92% rename from src/justenoughicu.c rename to justenough/justenoughicu.c index d58ad07f6..1788e34cf 100644 --- a/src/justenoughicu.c +++ b/justenough/justenoughicu.c @@ -2,6 +2,7 @@ #include #include #include + #include #include #include @@ -9,12 +10,16 @@ #include #include #include + #include #include #include #include "silewin32.h" +// #define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" + typedef int32_t (*conversion_function_t)(UChar *dest, int32_t destCapacity, const UChar *src, int32_t srcLength, const char *locale, UErrorCode *pErrorCode); #define utf8_to_uchar(in, in_l, out, out_l) { \ @@ -27,7 +32,7 @@ typedef int32_t (*conversion_function_t)(UChar *dest, int32_t destCapacity, cons u_strFromUTF8(out, out_l, &out_l, in, in_l, &err); \ } -int icu_case(lua_State *L) { +int je_icu_case(lua_State *L) { size_t input_l; const char* input = luaL_checklstring(L, 1, &input_l); @@ -96,7 +101,7 @@ int icu_case(lua_State *L) { return luaL_error(L, "Error in UTF8 conversion %s", u_errorName(err)); } -int icu_breakpoints(lua_State *L) { +int je_icu_breakpoints(lua_State *L) { const char* input = luaL_checkstring(L, 1); int input_l = strlen(input); const char* locale = luaL_checkstring(L, 2); @@ -168,7 +173,7 @@ int icu_breakpoints(lua_State *L) { return breakcount; } -int icu_canonicalize_language(lua_State *L) { +int je_icu_canonicalize_language(lua_State *L) { const char* lang = luaL_checkstring(L, 1); char locale[200], minimized[200], result[200]; UErrorCode error = 0; @@ -190,7 +195,7 @@ int icu_canonicalize_language(lua_State *L) { #define MAX_ICU_FORMATTED_NUMBER_STRING 512 -int icu_format_number(lua_State *L) { +int je_icu_format_number(lua_State *L) { double a = luaL_checknumber(L, 1); const char* locale = luaL_checkstring(L, 2); UNumberFormatStyle numFormatStyle = luaL_checkinteger(L, 3); @@ -213,7 +218,7 @@ int icu_format_number(lua_State *L) { return 1; } -int icu_bidi_runs(lua_State *L) { +int je_icu_bidi_runs(lua_State *L) { size_t input_l; const char* input = luaL_checklstring(L, 1, &input_l); const char* direction = luaL_checkstring(L, 2); @@ -292,7 +297,7 @@ int icu_bidi_runs(lua_State *L) { return count; } -int icu_collation_create(lua_State *L) { +int je_icu_collation_create(lua_State *L) { int nargs = lua_gettop(L); const char* locale = luaL_checkstring(L, 1); @@ -311,7 +316,7 @@ int icu_collation_create(lua_State *L) { int numericOrdering = UCOL_ON; // N.B. default is UCOL_OFF int backwards = UCOL_OFF; // So-called 'french collation', default is UCOL_OFF // NOT IMPLEMENTED: maxVariable punct / maxVariable space - // This affects 'alternate handling' with espect to spaces and/or punctuations + // This affects 'alternate handling' with expect to spaces and/or punctuations // I never used it and I'm lazy - assume default is ok... int caseFirst = UCOL_OFF; // n.B. default is UCOL_OFF int caseLevel = UCOL_OFF; // n.B. default is UCOL_OFF @@ -452,7 +457,7 @@ int icu_collation_create(lua_State *L) { return 1; } -int icu_collation_destroy(lua_State *L) { +int je_icu_collation_destroy(lua_State *L) { UCollator *collator = (UCollator *)lua_touserdata(L, 1); if (!collator) { return luaL_error(L, "Collation cleanup called with invalid input"); @@ -461,7 +466,7 @@ int icu_collation_destroy(lua_State *L) { return 0; } -int icu_compare(lua_State *L) { +int je_icu_compare(lua_State *L) { UCollator *collator = (UCollator *)lua_touserdata(L, 1); if (!collator) { return luaL_error(L, "Comparison called with invalid first argument (collator)"); @@ -490,38 +495,21 @@ int icu_compare(lua_State *L) { // UCollationResult result = ucol_strcollIter(collation, &s1iter, &s2iter, &status); } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 && !LUAJIT -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ +int je_icu_version(lua_State *L) { + lua_pushstring(L, U_ICU_VERSION); + return 1; } -#endif static const struct luaL_Reg lib_table [] = { - {"breakpoints", icu_breakpoints}, - {"case", icu_case}, - {"bidi_runs", icu_bidi_runs}, - {"canonicalize_language", icu_canonicalize_language}, - {"format_number", icu_format_number}, - {"collation_create", icu_collation_create}, - {"collation_destroy", icu_collation_destroy}, - {"compare", icu_compare}, + {"breakpoints", je_icu_breakpoints}, + {"case", je_icu_case}, + {"bidi_runs", je_icu_bidi_runs}, + {"canonicalize_language", je_icu_canonicalize_language}, + {"format_number", je_icu_format_number}, + {"collation_create", je_icu_collation_create}, + {"collation_destroy", je_icu_collation_destroy}, + {"compare", je_icu_compare}, + {"version", je_icu_version}, {NULL, NULL} }; diff --git a/src/justenoughlibtexpdf.c b/justenough/justenoughlibtexpdf.c similarity index 77% rename from src/justenoughlibtexpdf.c rename to justenough/justenoughlibtexpdf.c index 0ef178f26..46a3d7a60 100644 --- a/src/justenoughlibtexpdf.c +++ b/justenough/justenoughlibtexpdf.c @@ -1,20 +1,20 @@ #include #include #include -#include #include #include #include -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -#define lua_rawlen lua_strlen -#endif +#include + +/* #define COMPAT53_PREFIX compat53 */ +#include "compat-5.3.h" pdf_doc *p = NULL; double height = 0.0; double precision = 65536.0; -char* producer = "SILE"; +const char* producer = "SILE"; #define ASSERT_PDF_OPENED(p) \ if (!p) { \ @@ -22,12 +22,12 @@ char* producer = "SILE"; return 0; \ } -int pdf_init (lua_State *L) { +int je_pdf_init (lua_State *L) { pdf_rect mediabox; - const char* fn = luaL_checkstring(L, 1); + const char* fn = luaL_checkstring(L, 1); double w = luaL_checknumber(L, 2); height = luaL_checknumber(L, 3); - producer = luaL_checkstring(L, 4); + const char* producer = luaL_checkstring(L, 4); p = texpdf_open_document(fn, 0, w, height, 0,0,0); texpdf_init_device(p, 1/precision, 2, 0); @@ -46,19 +46,19 @@ int pdf_init (lua_State *L) { return 0; } -int pdf_endpage(lua_State *L) { +int je_pdf_endpage(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_doc_end_page(p); return 0; }; -int pdf_beginpage(lua_State *L) { +int je_pdf_beginpage(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_doc_begin_page(p, 1,0,height); return 0; } -int pdf_changepagesize(lua_State *L) { +int je_pdf_changepagesize(lua_State *L) { pdf_rect mediabox; double pageno = luaL_checknumber(L, 1); @@ -73,7 +73,7 @@ int pdf_changepagesize(lua_State *L) { return 0; } -int pdf_finish(lua_State *L) { +int je_pdf_finish(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_files_close(); texpdf_close_device (); @@ -82,7 +82,7 @@ int pdf_finish(lua_State *L) { return 0; } -int pdf_loadfont(lua_State *L) { +int je_pdf_loadfont(lua_State *L) { const char * filename; int index = 0; double ptsize; @@ -144,13 +144,13 @@ int pdf_loadfont(lua_State *L) { return 1; } -int pdf_setdirmode(lua_State *L) { +int je_pdf_setdirmode(lua_State *L) { int layout_dir = luaL_checkinteger(L,1); texpdf_dev_set_dirmode(layout_dir); return 0; } -int pdf_setstring(lua_State *L) { +int je_pdf_setstring(lua_State *L) { double x = luaL_checknumber(L, 1); double y = luaL_checknumber(L, 2); const char* s = luaL_checkstring(L, 3); @@ -162,7 +162,7 @@ int pdf_setstring(lua_State *L) { return 0; } -int pdf_setrule(lua_State *L) { +int je_pdf_setrule(lua_State *L) { double x = luaL_checknumber(L, 1); double y = luaL_checknumber(L, 2); double w = luaL_checknumber(L, 3); @@ -174,7 +174,7 @@ int pdf_setrule(lua_State *L) { /* Colors */ -int pdf_colorpush_rgb(lua_State *L) { +int je_pdf_colorpush_rgb(lua_State *L) { double r = luaL_checknumber(L, 1); double g = luaL_checknumber(L, 2); double b = luaL_checknumber(L, 3); @@ -186,7 +186,7 @@ int pdf_colorpush_rgb(lua_State *L) { return 0; } -int pdf_colorpush_cmyk(lua_State *L) { +int je_pdf_colorpush_cmyk(lua_State *L) { double c = luaL_checknumber(L, 1); double m = luaL_checknumber(L, 2); double y = luaL_checknumber(L, 3); @@ -200,7 +200,7 @@ int pdf_colorpush_cmyk(lua_State *L) { return 0; } -int pdf_colorpush_gray(lua_State *L) { +int je_pdf_colorpush_gray(lua_State *L) { double l = luaL_checknumber(L, 1); pdf_color color; @@ -211,7 +211,7 @@ int pdf_colorpush_gray(lua_State *L) { return 0; } -int pdf_setcolor_rgb(lua_State *L) { +int je_pdf_setcolor_rgb(lua_State *L) { double r = luaL_checknumber(L, 1); double g = luaL_checknumber(L, 2); double b = luaL_checknumber(L, 3); @@ -224,7 +224,7 @@ int pdf_setcolor_rgb(lua_State *L) { return 0; } -int pdf_setcolor_cmyk(lua_State *L) { +int je_pdf_setcolor_cmyk(lua_State *L) { double c = luaL_checknumber(L, 1); double m = luaL_checknumber(L, 2); double y = luaL_checknumber(L, 3); @@ -238,7 +238,7 @@ int pdf_setcolor_cmyk(lua_State *L) { return 0; } -int pdf_setcolor_gray(lua_State *L) { +int je_pdf_setcolor_gray(lua_State *L) { double l = luaL_checknumber(L, 1); pdf_color color; @@ -249,7 +249,7 @@ int pdf_setcolor_gray(lua_State *L) { return 0; } -int pdf_colorpop(lua_State *L) { +int je_pdf_colorpop(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_color_pop(p); return 0; @@ -257,7 +257,7 @@ int pdf_colorpop(lua_State *L) { /* PDF "specials" */ -int pdf_destination(lua_State *L) { +int je_pdf_destination(lua_State *L) { pdf_obj* array = texpdf_new_array(); const char* name = luaL_checkstring(L, 1); double x = luaL_checknumber(L, 2); @@ -277,7 +277,7 @@ int pdf_destination(lua_State *L) { return 0; } -int pdf_bookmark(lua_State *L) { +int je_pdf_bookmark(lua_State *L) { const char* dictionary = luaL_checkstring(L, 1); int level = luaL_checknumber(L, 2); pdf_obj* dict = texpdf_parse_pdf_dict(&dictionary, dictionary + strlen(dictionary), NULL); @@ -299,12 +299,12 @@ int pdf_bookmark(lua_State *L) { return 0; } -int pdf_begin_annotation(lua_State *L) { +int je_pdf_begin_annotation(lua_State *L) { // In theory we should track boxes and breaking state and etc. return 0; } -int pdf_end_annotation(lua_State *L) { +int je_pdf_end_annotation(lua_State *L) { const char* dictionary = luaL_checkstring(L, 1); pdf_rect rect; pdf_obj* dict; @@ -325,7 +325,7 @@ int pdf_end_annotation(lua_State *L) { return 0; } -int pdf_metadata(lua_State *L) { +int je_pdf_metadata(lua_State *L) { const char* key = luaL_checkstring(L, 1); const char* value = luaL_checkstring(L, 2); int len = lua_rawlen(L, 2); @@ -339,7 +339,7 @@ int pdf_metadata(lua_State *L) { } /* Images */ -int pdf_drawimage(lua_State *L) { +int je_pdf_drawimage(lua_State *L) { const char* filename = luaL_checkstring(L, 1); transform_info ti; double x = luaL_checknumber(L, 2); @@ -361,7 +361,7 @@ int pdf_drawimage(lua_State *L) { extern int get_image_bbox(FILE* f, long page_no, double* llx, double* lly, double* urx, double* ury, double* xresol, double* yresol); -int pdf_imagebbox(lua_State *L) { +int je_pdf_imagebbox(lua_State *L) { const char* filename = luaL_checkstring(L, 1); long page_no = (long)luaL_checkinteger(L, 2); double llx = 0; @@ -400,7 +400,7 @@ int pdf_imagebbox(lua_State *L) { return 6; } -int pdf_transform(lua_State *L) { +int je_pdf_transform(lua_State *L) { pdf_tmatrix matrix; double a = luaL_checknumber(L, 1); double b = luaL_checknumber(L, 2); @@ -415,21 +415,21 @@ int pdf_transform(lua_State *L) { return 0; } -int pdf_gsave(lua_State *L) { +int je_pdf_gsave(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_graphics_mode(p); texpdf_dev_gsave(p); return 0; } -int pdf_grestore(lua_State *L) { +int je_pdf_grestore(lua_State *L) { ASSERT_PDF_OPENED(p); texpdf_graphics_mode(p); texpdf_dev_grestore(p); return 0; } -int pdf_add_content(lua_State *L) { +int je_pdf_add_content(lua_State *L) { const char* input = luaL_checkstring(L, 1); int input_l = lua_rawlen(L, 1); ASSERT_PDF_OPENED(p); @@ -440,7 +440,7 @@ int pdf_add_content(lua_State *L) { return 0; } -int pdf_parse(lua_State *L) { +int je_pdf_parse(lua_State *L) { const char* input = luaL_checkstring(L, 1); int input_l = lua_rawlen(L, 1); pdf_obj* o = texpdf_parse_pdf_object(&input, input+input_l, NULL); @@ -452,7 +452,7 @@ int pdf_parse(lua_State *L) { } } -int pdf_add_dict(lua_State *L) { +int je_pdf_add_dict(lua_State *L) { pdf_obj* dict = lua_touserdata(L, 1); pdf_obj* key = lua_touserdata(L, 2); pdf_obj* value = lua_touserdata(L, 3); @@ -460,20 +460,20 @@ int pdf_add_dict(lua_State *L) { return 0; } -int pdf_reference(lua_State *L) { +int je_pdf_reference(lua_State *L) { pdf_obj* o1 = lua_touserdata(L, 1); pdf_obj* o2 = texpdf_ref_obj(o1); lua_pushlightuserdata(L, o2); return 1; } -int pdf_release(lua_State *L) { +int je_pdf_release(lua_State *L) { pdf_obj* o1 = lua_touserdata(L, 1); texpdf_release_obj(o1); return 0; } -int pdf_get_dictionary(lua_State *L) { +int je_pdf_get_dictionary(lua_State *L) { const char* dict = luaL_checkstring(L, 1); pdf_obj *o = texpdf_doc_get_dictionary(p, dict); if (o) { @@ -484,7 +484,7 @@ int pdf_get_dictionary(lua_State *L) { } } -int pdf_lookup_dictionary(lua_State *L) { +int je_pdf_lookup_dictionary(lua_State *L) { pdf_obj* dict = lua_touserdata(L, 1); const char* key = luaL_checkstring(L, 2); pdf_obj *o = texpdf_lookup_dict(dict, key); @@ -496,7 +496,7 @@ int pdf_lookup_dictionary(lua_State *L) { } } -int pdf_push_array(lua_State *L) { +int je_pdf_push_array(lua_State *L) { pdf_obj* array = lua_touserdata(L, 1); if (!PDF_OBJ_ARRAYTYPE(array)) { return luaL_error(L, "push_array called on non-array"); @@ -506,7 +506,7 @@ int pdf_push_array(lua_State *L) { return 0; } -int pdf_get_array(lua_State *L) { +int je_pdf_get_array(lua_State *L) { pdf_obj* array = lua_touserdata(L, 1); if (!PDF_OBJ_ARRAYTYPE(array)) { return luaL_error(L, "push_array called on non-array"); @@ -521,7 +521,7 @@ int pdf_get_array(lua_State *L) { } } -int pdf_array_length(lua_State *L) { +int je_pdf_array_length(lua_State *L) { pdf_obj* array = lua_touserdata(L, 1); if (!PDF_OBJ_ARRAYTYPE(array)) { return luaL_error(L, "push_array called on non-array"); @@ -530,80 +530,57 @@ int pdf_array_length(lua_State *L) { return 1; } -int pdf_new_string(lua_State *L) { +int je_pdf_new_string(lua_State *L) { const char* s = luaL_checkstring(L, 1); int l = lua_rawlen(L, 1); lua_pushlightuserdata(L, texpdf_new_string(s, l)); return 1; } -int pdf_version(lua_State *L) { +int je_pdf_version(lua_State *L) { lua_pushstring(L, texpdf_library_version()); return 1; } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - static const struct luaL_Reg lib_table [] = { - {"init", pdf_init}, - {"beginpage", pdf_beginpage}, - {"change_page_size", pdf_changepagesize}, - {"endpage", pdf_endpage}, - {"finish", pdf_finish}, - {"loadfont", pdf_loadfont}, - {"setdirmode", pdf_setdirmode}, - {"setstring", pdf_setstring}, - {"setrule", pdf_setrule}, - {"setcolor_rgb", pdf_setcolor_rgb}, - {"setcolor_cmyk", pdf_setcolor_cmyk}, - {"setcolor_gray", pdf_setcolor_gray}, - {"drawimage", pdf_drawimage}, - {"imagebbox", pdf_imagebbox}, - {"colorpop", pdf_colorpop}, - {"colorpush_rgb", pdf_colorpush_rgb}, - {"colorpush_cmyk", pdf_colorpush_cmyk}, - {"colorpush_gray", pdf_colorpush_gray}, - {"setmatrix", pdf_transform}, - {"gsave", pdf_gsave}, - {"grestore", pdf_grestore}, - {"destination", pdf_destination}, - {"bookmark", pdf_bookmark}, - {"begin_annotation", pdf_begin_annotation}, - {"end_annotation", pdf_end_annotation}, - {"metadata", pdf_metadata}, - {"version", pdf_version}, - {"add_content", pdf_add_content}, - {"get_dictionary", pdf_get_dictionary}, - {"parse", pdf_parse}, - {"add_dict", pdf_add_dict}, - {"lookup_dictionary", pdf_lookup_dictionary}, - {"reference", pdf_reference}, - {"release", pdf_release}, - {"push_array", pdf_push_array}, - {"get_array", pdf_get_array}, - {"array_length", pdf_array_length}, - {"string", pdf_new_string}, + {"init", je_pdf_init}, + {"beginpage", je_pdf_beginpage}, + {"change_page_size", je_pdf_changepagesize}, + {"endpage", je_pdf_endpage}, + {"finish", je_pdf_finish}, + {"loadfont", je_pdf_loadfont}, + {"setdirmode", je_pdf_setdirmode}, + {"setstring", je_pdf_setstring}, + {"setrule", je_pdf_setrule}, + {"setcolor_rgb", je_pdf_setcolor_rgb}, + {"setcolor_cmyk", je_pdf_setcolor_cmyk}, + {"setcolor_gray", je_pdf_setcolor_gray}, + {"drawimage", je_pdf_drawimage}, + {"imagebbox", je_pdf_imagebbox}, + {"colorpop", je_pdf_colorpop}, + {"colorpush_rgb", je_pdf_colorpush_rgb}, + {"colorpush_cmyk", je_pdf_colorpush_cmyk}, + {"colorpush_gray", je_pdf_colorpush_gray}, + {"setmatrix", je_pdf_transform}, + {"gsave", je_pdf_gsave}, + {"grestore", je_pdf_grestore}, + {"destination", je_pdf_destination}, + {"bookmark", je_pdf_bookmark}, + {"begin_annotation", je_pdf_begin_annotation}, + {"end_annotation", je_pdf_end_annotation}, + {"metadata", je_pdf_metadata}, + {"version", je_pdf_version}, + {"add_content", je_pdf_add_content}, + {"get_dictionary", je_pdf_get_dictionary}, + {"parse", je_pdf_parse}, + {"add_dict", je_pdf_add_dict}, + {"lookup_dictionary", je_pdf_lookup_dictionary}, + {"reference", je_pdf_reference}, + {"release", je_pdf_release}, + {"push_array", je_pdf_push_array}, + {"get_array", je_pdf_get_array}, + {"array_length", je_pdf_array_length}, + {"string", je_pdf_new_string}, {NULL, NULL} }; diff --git a/src/macfonts.m b/justenough/macfonts.m similarity index 87% rename from src/macfonts.m rename to justenough/macfonts.m index 479999aa5..d63850d47 100644 --- a/src/macfonts.m +++ b/justenough/macfonts.m @@ -7,29 +7,8 @@ #include #include - -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif +// #define COMPAT53_PREFIX compat53 +#include "compat-5.3.h" #define MAX_NAME_LEN 512 @@ -119,7 +98,7 @@ static void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { } -int face_from_options(lua_State* L) { +int je_face_from_options(lua_State* L) { uint32_t index = 0; const char *family = "Gentium"; char * font_path; @@ -204,7 +183,7 @@ int face_from_options(lua_State* L) { static const struct luaL_Reg lib_table [] = { - {"_face", face_from_options}, + {"_face", je_face_from_options}, {NULL, NULL} }; diff --git a/src/nanosvg.h b/justenough/nanosvg.h similarity index 100% rename from src/nanosvg.h rename to justenough/nanosvg.h diff --git a/src/podofo_wrap.cxx b/justenough/podofo_wrap.cxx similarity index 100% rename from src/podofo_wrap.cxx rename to justenough/podofo_wrap.cxx diff --git a/src/silewin32.h b/justenough/silewin32.h similarity index 100% rename from src/silewin32.h rename to justenough/silewin32.h diff --git a/src/svg.c b/justenough/svg.c similarity index 84% rename from src/svg.c rename to justenough/svg.c index fd3406efa..03fb49a0d 100644 --- a/src/svg.c +++ b/justenough/svg.c @@ -1,13 +1,17 @@ #include #include #include -#define NANOSVG_IMPLEMENTATION // Expands implementation -#include "nanosvg.h" #include #include #include +#define NANOSVG_IMPLEMENTATION // Expands implementation +#include "nanosvg.h" + +/* #define COMPAT53_PREFIX compat53 */ +#include "compat-5.3.h" + static char* safe_append(char* output, int* output_l, int* max_output, char* s2) { int append_len = strlen(s2) + 1; // strlen doesn't count \0 if (*output_l + append_len > *max_output) { @@ -98,29 +102,6 @@ int svg_to_ps(lua_State *L) { return 3; } -#if !defined LUA_VERSION_NUM -/* Lua 5.0 */ -#define luaL_Reg luaL_reg -#endif - -#if !defined LUA_VERSION_NUM || LUA_VERSION_NUM==501 -/* -** Adapted from Lua 5.2.0 -*/ -void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { - luaL_checkstack(L, nup+1, "too many upvalues"); - for (; l->name != NULL; l++) { /* fill the table with given functions */ - int i; - lua_pushstring(L, l->name); - for (i = 0; i < nup; i++) /* copy upvalues to the top */ - lua_pushvalue(L, -(nup+1)); - lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ - lua_settable(L, -(nup + 3)); - } - lua_pop(L, nup); /* remove upvalues */ -} -#endif - static const struct luaL_Reg lib_table [] = { {"svg_to_ps", svg_to_ps}, {NULL, NULL} diff --git a/libtexpdf.git-rev b/libtexpdf.git-rev deleted file mode 100644 index 81e684cec..000000000 --- a/libtexpdf.git-rev +++ /dev/null @@ -1 +0,0 @@ -d1030b48014adcb09d41dae20cc8fccd4a77a04e diff --git a/lua-libraries/LICENSE-lunamark b/lua-libraries/LICENSE-lunamark deleted file mode 100644 index 48d92db43..000000000 --- a/lua-libraries/LICENSE-lunamark +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (C) 2009-2016 John MacFarlane - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/lua-libraries/lunamark.lua b/lua-libraries/lunamark.lua deleted file mode 100644 index cf8344ece..000000000 --- a/lua-libraries/lunamark.lua +++ /dev/null @@ -1,94 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Copyright © 2009-2011 John MacFarlane. --- --- Released under the MIT license (see LICENSE in the source for details). --- --- ## Description --- --- Lunamark is a lua library for conversion of markdown to --- other textual formats. Currently HTML, Docbook, ConTeXt, --- LaTeX, and Groff man are the supported output formats, --- but lunamark's modular architecture makes it easy to add --- writers and modify the markdown parser (written with a PEG --- grammar). --- --- Lunamark's markdown parser currently supports the following --- extensions (which can be turned on or off individually): --- --- - Smart typography (fancy quotes, dashes, ellipses) --- - Significant start numbers in ordered lists --- - Footnotes --- - Definition lists --- --- More extensions will be supported in later versions. --- --- The library is as portable as lua and has very good performance. --- It is about as fast as the author's own C library --- [peg-markdown](http://github.com/jgm/peg-markdown). --- --- ## Simple usage example --- --- local lunamark = require("lunamark") --- local writer = lunamark.writer.html.new() --- local parse = lunamark.reader.markdown.new(writer, { smart = true }) --- local result, metadata = parse("Here's 'my *text*'...") --- print(result) --- assert(result == 'Here’s ‘my text’…') --- --- ## Customizing the writer --- --- Render emphasized text using `` tags rather than ``. --- --- local unicode = require("unicode") --- function writer.emphasis(s) --- return {"",s,""} --- end --- local parse = lunamark.reader.markdown.new(writer, { smart = true }) --- local result, metadata = parse("*Beiß* nicht in die Hand, die dich *füttert*.") --- print(result) --- assert(result == 'Beiß nicht in die Hand, die dich füttert.') --- --- Eliminate hyperlinks: --- --- function writer.link(lab,url,tit) --- return lab --- end --- local parse = lunamark.reader.markdown.new(writer, { smart = true }) --- local result, metadata = parse("[hi](/url) there") --- print(result) --- assert(result == 'hi there') --- --- ## Customizing the parser --- --- Parse CamelCase words as wikilinks: --- --- lpeg = require("lpeg") --- local writer = lunamark.writer.html.new() --- function add_wikilinks(syntax) --- local capword = lpeg.R("AZ")^1 * lpeg.R("az")^1 --- local parse_wikilink = lpeg.C(capword^2) --- / function(wikipage) --- return writer.link(writer.string(wikipage), --- "/" .. wikipage, --- "Go to " .. wikipage) --- end --- syntax.Inline = parse_wikilink + syntax.Inline --- return syntax --- end --- local parse = lunamark.reader.markdown.new(writer, { alter_syntax = add_wikilinks }) --- local result, metadata = parse("My text with WikiLinks.\n") --- print(result) --- assert(result == 'My text with WikiLinks.') --- - -local G = {} - -setmetatable(G,{ __index = function(t,name) - local mod = require("lunamark." .. name) - rawset(t,name,mod) - return t[name] - end }) - -return G diff --git a/lua-libraries/lunamark/entities.lua b/lua-libraries/lunamark/entities.lua deleted file mode 100644 index 420d8b0c7..000000000 --- a/lua-libraries/lunamark/entities.lua +++ /dev/null @@ -1,282 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Functions for dealing with HTML/XML entities. - -local M = {} - -local luautf8=require("lua-utf8") -utf8_char = luautf8.char - -local character_entities = { - ["quot"] = 0x0022, - ["amp"] = 0x0026, - ["apos"] = 0x0027, - ["lt"] = 0x003C, - ["gt"] = 0x003E, - ["nbsp"] = 160, - ["iexcl"] = 0x00A1, - ["cent"] = 0x00A2, - ["pound"] = 0x00A3, - ["curren"] = 0x00A4, - ["yen"] = 0x00A5, - ["brvbar"] = 0x00A6, - ["sect"] = 0x00A7, - ["uml"] = 0x00A8, - ["copy"] = 0x00A9, - ["ordf"] = 0x00AA, - ["laquo"] = 0x00AB, - ["not"] = 0x00AC, - ["shy"] = 173, - ["reg"] = 0x00AE, - ["macr"] = 0x00AF, - ["deg"] = 0x00B0, - ["plusmn"] = 0x00B1, - ["sup2"] = 0x00B2, - ["sup3"] = 0x00B3, - ["acute"] = 0x00B4, - ["micro"] = 0x00B5, - ["para"] = 0x00B6, - ["middot"] = 0x00B7, - ["cedil"] = 0x00B8, - ["sup1"] = 0x00B9, - ["ordm"] = 0x00BA, - ["raquo"] = 0x00BB, - ["frac14"] = 0x00BC, - ["frac12"] = 0x00BD, - ["frac34"] = 0x00BE, - ["iquest"] = 0x00BF, - ["Agrave"] = 0x00C0, - ["Aacute"] = 0x00C1, - ["Acirc"] = 0x00C2, - ["Atilde"] = 0x00C3, - ["Auml"] = 0x00C4, - ["Aring"] = 0x00C5, - ["AElig"] = 0x00C6, - ["Ccedil"] = 0x00C7, - ["Egrave"] = 0x00C8, - ["Eacute"] = 0x00C9, - ["Ecirc"] = 0x00CA, - ["Euml"] = 0x00CB, - ["Igrave"] = 0x00CC, - ["Iacute"] = 0x00CD, - ["Icirc"] = 0x00CE, - ["Iuml"] = 0x00CF, - ["ETH"] = 0x00D0, - ["Ntilde"] = 0x00D1, - ["Ograve"] = 0x00D2, - ["Oacute"] = 0x00D3, - ["Ocirc"] = 0x00D4, - ["Otilde"] = 0x00D5, - ["Ouml"] = 0x00D6, - ["times"] = 0x00D7, - ["Oslash"] = 0x00D8, - ["Ugrave"] = 0x00D9, - ["Uacute"] = 0x00DA, - ["Ucirc"] = 0x00DB, - ["Uuml"] = 0x00DC, - ["Yacute"] = 0x00DD, - ["THORN"] = 0x00DE, - ["szlig"] = 0x00DF, - ["agrave"] = 0x00E0, - ["aacute"] = 0x00E1, - ["acirc"] = 0x00E2, - ["atilde"] = 0x00E3, - ["auml"] = 0x00E4, - ["aring"] = 0x00E5, - ["aelig"] = 0x00E6, - ["ccedil"] = 0x00E7, - ["egrave"] = 0x00E8, - ["eacute"] = 0x00E9, - ["ecirc"] = 0x00EA, - ["euml"] = 0x00EB, - ["igrave"] = 0x00EC, - ["iacute"] = 0x00ED, - ["icirc"] = 0x00EE, - ["iuml"] = 0x00EF, - ["eth"] = 0x00F0, - ["ntilde"] = 0x00F1, - ["ograve"] = 0x00F2, - ["oacute"] = 0x00F3, - ["ocirc"] = 0x00F4, - ["otilde"] = 0x00F5, - ["ouml"] = 0x00F6, - ["divide"] = 0x00F7, - ["oslash"] = 0x00F8, - ["ugrave"] = 0x00F9, - ["uacute"] = 0x00FA, - ["ucirc"] = 0x00FB, - ["uuml"] = 0x00FC, - ["yacute"] = 0x00FD, - ["thorn"] = 0x00FE, - ["yuml"] = 0x00FF, - ["OElig"] = 0x0152, - ["oelig"] = 0x0153, - ["Scaron"] = 0x0160, - ["scaron"] = 0x0161, - ["Yuml"] = 0x0178, - ["fnof"] = 0x0192, - ["circ"] = 0x02C6, - ["tilde"] = 0x02DC, - ["Alpha"] = 0x0391, - ["Beta"] = 0x0392, - ["Gamma"] = 0x0393, - ["Delta"] = 0x0394, - ["Epsilon"] = 0x0395, - ["Zeta"] = 0x0396, - ["Eta"] = 0x0397, - ["Theta"] = 0x0398, - ["Iota"] = 0x0399, - ["Kappa"] = 0x039A, - ["Lambda"] = 0x039B, - ["Mu"] = 0x039C, - ["Nu"] = 0x039D, - ["Xi"] = 0x039E, - ["Omicron"] = 0x039F, - ["Pi"] = 0x03A0, - ["Rho"] = 0x03A1, - ["Sigma"] = 0x03A3, - ["Tau"] = 0x03A4, - ["Upsilon"] = 0x03A5, - ["Phi"] = 0x03A6, - ["Chi"] = 0x03A7, - ["Psi"] = 0x03A8, - ["Omega"] = 0x03A9, - ["alpha"] = 0x03B1, - ["beta"] = 0x03B2, - ["gamma"] = 0x03B3, - ["delta"] = 0x03B4, - ["epsilon"] = 0x03B5, - ["zeta"] = 0x03B6, - ["eta"] = 0x03B7, - ["theta"] = 0x03B8, - ["iota"] = 0x03B9, - ["kappa"] = 0x03BA, - ["lambda"] = 0x03BB, - ["mu"] = 0x03BC, - ["nu"] = 0x03BD, - ["xi"] = 0x03BE, - ["omicron"] = 0x03BF, - ["pi"] = 0x03C0, - ["rho"] = 0x03C1, - ["sigmaf"] = 0x03C2, - ["sigma"] = 0x03C3, - ["tau"] = 0x03C4, - ["upsilon"] = 0x03C5, - ["phi"] = 0x03C6, - ["chi"] = 0x03C7, - ["psi"] = 0x03C8, - ["omega"] = 0x03C9, - ["thetasym"] = 0x03D1, - ["upsih"] = 0x03D2, - ["piv"] = 0x03D6, - ["ensp"] = 0x2002, - ["emsp"] = 0x2003, - ["thinsp"] = 0x2009, - ["ndash"] = 0x2013, - ["mdash"] = 0x2014, - ["lsquo"] = 0x2018, - ["rsquo"] = 0x2019, - ["sbquo"] = 0x201A, - ["ldquo"] = 0x201C, - ["rdquo"] = 0x201D, - ["bdquo"] = 0x201E, - ["dagger"] = 0x2020, - ["Dagger"] = 0x2021, - ["bull"] = 0x2022, - ["hellip"] = 0x2026, - ["permil"] = 0x2030, - ["prime"] = 0x2032, - ["Prime"] = 0x2033, - ["lsaquo"] = 0x2039, - ["rsaquo"] = 0x203A, - ["oline"] = 0x203E, - ["frasl"] = 0x2044, - ["euro"] = 0x20AC, - ["image"] = 0x2111, - ["weierp"] = 0x2118, - ["real"] = 0x211C, - ["trade"] = 0x2122, - ["alefsym"] = 0x2135, - ["larr"] = 0x2190, - ["uarr"] = 0x2191, - ["rarr"] = 0x2192, - ["darr"] = 0x2193, - ["harr"] = 0x2194, - ["crarr"] = 0x21B5, - ["lArr"] = 0x21D0, - ["uArr"] = 0x21D1, - ["rArr"] = 0x21D2, - ["dArr"] = 0x21D3, - ["hArr"] = 0x21D4, - ["forall"] = 0x2200, - ["part"] = 0x2202, - ["exist"] = 0x2203, - ["empty"] = 0x2205, - ["nabla"] = 0x2207, - ["isin"] = 0x2208, - ["notin"] = 0x2209, - ["ni"] = 0x220B, - ["prod"] = 0x220F, - ["sum"] = 0x2211, - ["minus"] = 0x2212, - ["lowast"] = 0x2217, - ["radic"] = 0x221A, - ["prop"] = 0x221D, - ["infin"] = 0x221E, - ["ang"] = 0x2220, - ["and"] = 0x2227, - ["or"] = 0x2228, - ["cap"] = 0x2229, - ["cup"] = 0x222A, - ["int"] = 0x222B, - ["there4"] = 0x2234, - ["sim"] = 0x223C, - ["cong"] = 0x2245, - ["asymp"] = 0x2248, - ["ne"] = 0x2260, - ["equiv"] = 0x2261, - ["le"] = 0x2264, - ["ge"] = 0x2265, - ["sub"] = 0x2282, - ["sup"] = 0x2283, - ["nsub"] = 0x2284, - ["sube"] = 0x2286, - ["supe"] = 0x2287, - ["oplus"] = 0x2295, - ["otimes"] = 0x2297, - ["perp"] = 0x22A5, - ["sdot"] = 0x22C5, - ["lceil"] = 0x2308, - ["rceil"] = 0x2309, - ["lfloor"] = 0x230A, - ["rfloor"] = 0x230B, - ["lang"] = 0x27E8, - ["rang"] = 0x27E9, - ["loz"] = 0x25CA, - ["spades"] = 0x2660, - ["clubs"] = 0x2663, - ["hearts"] = 0x2665, - ["diams"] = 0x2666, -} - ---- Given a string of decimal digits, returns a UTF-8 encoded --- string encoding a unicode character. -function M.dec_entity(s) - return utf8_char(tonumber(s)) -end - ---- Given a string of hexadecimal digits, returns a UTF-8 encoded --- string encoding a unicode character. -function M.hex_entity(s) - return utf8_char(tonumber("0x"..s)) -end - ---- Given a character entity name (like `ouml`), returns a UTF-8 encoded --- string encoding a unicode character. -function M.char_entity(s) - local n = character_entities[s] - return utf8_char(n) -end - -return M diff --git a/lua-libraries/lunamark/reader.lua b/lua-libraries/lunamark/reader.lua deleted file mode 100644 index 2ee5d0c00..000000000 --- a/lua-libraries/lunamark/reader.lua +++ /dev/null @@ -1,20 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Provides access to all lunamark readers without preloading --- them. Reader modules are loaded only when needed. --- --- local readers = require("lunamark.reader") --- local htmlreader = readers.html -- html reader loaded now --- local myformat = 'markdown' --- local myreader = readers[myformat] -- markdown reader loaded now - -local G = {} - -setmetatable(G,{ __index = function(t,name) - local mod = require("lunamark.reader." .. name) - rawset(t,name,mod) - return t[name] - end }) - -return G diff --git a/lua-libraries/lunamark/reader/markdown.lua b/lua-libraries/lunamark/reader/markdown.lua deleted file mode 100644 index d829b5919..000000000 --- a/lua-libraries/lunamark/reader/markdown.lua +++ /dev/null @@ -1,1172 +0,0 @@ --- (c) 2009-2011 John MacFarlane, Hans Hagen. Released under MIT license. --- See the file LICENSE in the source for details. - -local util = require("lunamark.util") -local lpeg = require("lpeg") -local entities = require("lunamark.entities") -local lower, upper, gsub, format, length = - string.lower, string.upper, string.gsub, string.format, string.len -local P, R, S, V, C, Cg, Cb, Cmt, Cc, Ct, B, Cs = - lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cg, lpeg.Cb, - lpeg.Cmt, lpeg.Cc, lpeg.Ct, lpeg.B, lpeg.Cs -local lpegmatch = lpeg.match -local expand_tabs_in_line = util.expand_tabs_in_line -local luautf8 = require("lua-utf8") - -local M = {} - -local rope_to_string = util.rope_to_string - --- Normalize a markdown reference tag. (Make lowercase, and collapse --- adjacent whitespace characters.) -local function normalize_tag(tag) - return luautf8.lower(gsub(rope_to_string(tag), "[ \n\r\t]+", " ")) -end - ------------------------------------------------------------------------------- --- Character parsers ------------------------------------------------------------------------------- - -local percent = P("%") -local at = P("@") -local comma = P(",") -local asterisk = P("*") -local dash = P("-") -local plus = P("+") -local underscore = P("_") -local period = P(".") -local hash = P("#") -local ampersand = P("&") -local backtick = P("`") -local less = P("<") -local more = P(">") -local space = P(" ") -local squote = P("'") -local dquote = P('"') -local lparent = P("(") -local rparent = P(")") -local lbracket = P("[") -local rbracket = P("]") -local circumflex = P("^") -local slash = P("/") -local equal = P("=") -local colon = P(":") -local semicolon = P(";") -local exclamation = P("!") -local tilde = P("~") -local tab = P("\t") -local newline = P("\n") -local tightblocksep = P("\001") - ---- Create a new markdown parser. --- --- * `writer` is a writer table (see [lunamark.writer.generic]). --- --- * `options` is a table with parsing options. --- The following fields are significant: --- --- `alter_syntax` --- : Function from syntax table to syntax table, --- allowing the user to change or extend the markdown syntax. --- For an example, see the documentation for `lunamark`. --- --- `references` --- : A table of references to be used in resolving links --- in the document. The keys should be all lowercase, with --- spaces and newlines collapsed into single spaces. --- Example: --- --- { foo: { url = "/url", title = "my title" }, --- bar: { url = "http://fsf.org" } } --- --- `preserve_tabs` --- : Preserve tabs instead of converting to spaces. --- --- `smart` --- : Parse quotation marks, dashes, ellipses intelligently. --- --- `startnum` --- : Make the opening number in an ordered list significant. --- --- `notes` --- : Enable footnotes as in pandoc. --- --- `definition_lists` --- : Enable definition lists as in pandoc. --- --- `citations` --- : Enable citations as in pandoc. --- --- `fenced_code_blocks` --- : Enable fenced code blocks. --- --- `pandoc_title_blocks` --- : Parse pandoc-style title block at the beginning of document: --- --- % Title --- % Author1; Author2 --- % Date --- --- `lua_metadata` --- : Enable lua metadata. This is an HTML comment block --- that starts with `"))^0 * P("-->") - - local htmlinstruction = P("" ))^0 * P("?>" ) - - local openelt_any = less * keyword * htmlattribute^0 * sp * more - - local function openelt_exact(s) - return (less * sp * keyword_exact(s) * htmlattribute^0 * sp * more) - end - - local openelt_block = sp * block_keyword * htmlattribute^0 * sp * more - - local closeelt_any = less * sp * slash * keyword * sp * more - - local function closeelt_exact(s) - return (less * sp * slash * keyword_exact(s) * sp * more) - end - - local emptyelt_any = less * sp * keyword * htmlattribute^0 * sp * slash * more - - local emptyelt_block = less * sp * block_keyword * htmlattribute^0 * sp * slash * more - - local displaytext = (any - less)^1 - - -- return content between two matched HTML tags - local function in_matched(s) - return { openelt_exact(s) - * (V(1) + displaytext + (less - closeelt_exact(s)))^0 - * closeelt_exact(s) } - end - - local function parse_matched_tags(s,pos) - local t = lower(lpegmatch(C(keyword),s,pos)) - return lpegmatch(in_matched(t),s,pos-1) - end - - local in_matched_block_tags = less * Cmt(#openelt_block, parse_matched_tags) - - local displayhtml = htmlcomment - + emptyelt_block - + openelt_exact("hr") - + in_matched_block_tags - + htmlinstruction - - local inlinehtml = emptyelt_any - + htmlcomment - + htmlinstruction - + openelt_any - + closeelt_any - - ------------------------------------------------------------------------------ - -- Entities - ------------------------------------------------------------------------------ - - local hexentity = ampersand * hash * S("Xx") * C(hexdigit ^1) * semicolon - local decentity = ampersand * hash * C(digit ^1) * semicolon - local tagentity = ampersand * C(alphanumeric^1) * semicolon - - ------------------------------------------------------------------------------ - -- Inline elements - ------------------------------------------------------------------------------ - - local Inline = V("Inline") - - local Str = normalchar^1 / writer.string - - local Ellipsis = P("...") / writer.ellipsis - - local Dash = P("---") * -dash / writer.mdash - + P("--") * -dash / writer.ndash - + P("-") * #digit * B(digit*1, 2) / writer.ndash - - local DoubleQuoted = dquote * Ct((Inline - dquote)^1) * dquote - / writer.doublequoted - - local squote_start = squote * -spacing - - local squote_end = squote * B(nonspacechar*1, 2) - - local SingleQuoted = squote_start * Ct((Inline - squote_end)^1) * squote_end - / writer.singlequoted - - local Apostrophe = squote * B(nonspacechar*1, 2) / "’" - - local Smart = Ellipsis + Dash + SingleQuoted + DoubleQuoted + Apostrophe - - local Symbol = (specialchar - tightblocksep) / writer.string - - local Code = inticks / writer.code - - local bqstart = more - local headerstart = hash - + (line * (equal^1 + dash^1) * optionalspace * newline) - local fencestart = fencehead(backtick) + fencehead(tilde) - - if options.require_blank_before_blockquote then - bqstart = fail - end - - if options.require_blank_before_header then - headerstart = fail - end - - if not options.fenced_code_blocks or - options.blank_before_fenced_code_blocks then - fencestart = fail - end - - local Endline = newline * -( -- newline, but not before... - blankline -- paragraph break - + tightblocksep -- nested list - + eof -- end of document - + bqstart - + headerstart - + fencestart - ) * spacechar^0 / writer.space - - local Space = spacechar^2 * Endline / writer.linebreak - + spacechar^1 * Endline^-1 * eof / "" - + spacechar^1 * Endline^-1 * optionalspace / writer.space - - local NonbreakingEndline - = newline * -( -- newline, but not before... - blankline -- paragraph break - + tightblocksep -- nested list - + eof -- end of document - + bqstart - + headerstart - + fencestart - ) * spacechar^0 / writer.nbsp - - local NonbreakingSpace - = spacechar^2 * Endline / writer.linebreak - + spacechar^1 * Endline^-1 * eof / "" - + spacechar^1 * Endline^-1 * optionalspace / writer.nbsp - - -- parse many p between starter and ender - local function between(p, starter, ender) - local ender2 = B(nonspacechar) * ender - return (starter * #nonspacechar * Ct(p * (p - ender2)^0) * ender2) - end - - local Strong = ( between(Inline, doubleasterisks, doubleasterisks) - + between(Inline, doubleunderscores, doubleunderscores) - ) / writer.strong - - local Emph = ( between(Inline, asterisk, asterisk) - + between(Inline, underscore, underscore) - ) / writer.emphasis - - local urlchar = anyescaped - newline - more - - local AutoLinkUrl = less - * C(alphanumeric^1 * P("://") * urlchar^1) - * more - / function(url) return writer.link(writer.string(url),url) end - - local AutoLinkEmail = less - * C((alphanumeric + S("-._+"))^1 * P("@") * urlchar^1) - * more - / function(email) return writer.link(writer.string(email),"mailto:"..email) end - - local DirectLink = (tag / parse_inlines_no_link) -- no links inside links - * spnl - * lparent - * (url + Cc("")) -- link can be empty [foo]() - * optionaltitle - * rparent - / writer.link - - local IndirectLink = tag * (C(spnl) * tag)^-1 / indirect_link - - -- parse a link or image (direct or indirect) - local Link = DirectLink + IndirectLink - - local DirectImage = exclamation - * (tag / parse_inlines) - * spnl - * lparent - * (url + Cc("")) -- link can be empty [foo]() - * optionaltitle - * rparent - / writer.image - - local IndirectImage = exclamation * tag * (C(spnl) * tag)^-1 / indirect_image - - local Image = DirectImage + IndirectImage - - local TextCitations = Ct(Cc("") - * citation_name - * ((spnl - * lbracket - * citation_headless_body - * rbracket) + Cc(""))) / - function(raw_cites) - return citations(true, raw_cites) - end - - local ParenthesizedCitations - = Ct(lbracket - * citation_body - * rbracket) / - function(raw_cites) - return citations(false, raw_cites) - end - - local Citations = TextCitations + ParenthesizedCitations - - -- avoid parsing long strings of * or _ as emph/strong - local UlOrStarLine = asterisk^4 + underscore^4 / writer.string - - local EscapedChar = S("\\") * C(escapable) / writer.string - - local InlineHtml = C(inlinehtml) / writer.inline_html - - local HtmlEntity = hexentity / entities.hex_entity / writer.string - + decentity / entities.dec_entity / writer.string - + tagentity / entities.char_entity / writer.string - - ------------------------------------------------------------------------------ - -- Block elements - ------------------------------------------------------------------------------ - - local Block = V("Block") - - local DisplayHtml = C(displayhtml) / expandtabs / writer.display_html - - local Verbatim = Cs( (blanklines - * ((indentedline - blankline))^1)^1 - ) / expandtabs / writer.verbatim - - local TildeFencedCodeBlock - = fencehead(tilde) - * Cs(fencedline(tilde)^0) - * fencetail(tilde) - - local BacktickFencedCodeBlock - = fencehead(backtick) - * Cs(fencedline(backtick)^0) - * fencetail(backtick) - - local FencedCodeBlock - = (TildeFencedCodeBlock + BacktickFencedCodeBlock) - / function(infostring, code) - return writer.fenced_code( - expandtabs(code), - writer.string(infostring)) - end - - -- strip off leading > and indents, and run through blocks - local Blockquote = Cs(( - ((leader * more * space^-1)/"" * linechar^0 * newline)^1 - * (-blankline * linechar^1 * newline)^0 - * blankline^0 - )^1) / parse_blocks / writer.blockquote - - local function lineof(c) - return (leader * (P(c) * optionalspace)^3 * newline * blankline^1) - end - - local HorizontalRule = ( lineof(asterisk) - + lineof(dash) - + lineof(underscore) - ) / writer.hrule - - local Reference = define_reference_parser / register_link - - local Paragraph = nonindentspace * Ct(Inline^1) * newline - * ( blankline^1 - + #hash - + #(leader * more * space^-1) - ) - / writer.paragraph - - local Plain = nonindentspace * Ct(Inline^1) / writer.plain - - ------------------------------------------------------------------------------ - -- Lists - ------------------------------------------------------------------------------ - - local starter = bullet + enumerator - - -- we use \001 as a separator between a tight list item and a - -- nested list under it. - local NestedList = Cs((optionallyindentedline - starter)^1) - / function(a) return "\001"..a end - - local ListBlockLine = optionallyindentedline - - blankline - (indent^-1 * starter) - - local ListBlock = line * ListBlockLine^0 - - local ListContinuationBlock = blanklines * (indent / "") * ListBlock - - local function TightListItem(starter) - return -HorizontalRule - * (Cs(starter / "" * ListBlock * NestedList^-1) / parse_blocks) - * -(blanklines * indent) - end - - local function LooseListItem(starter) - return -HorizontalRule - * Cs( starter / "" * ListBlock * Cc("\n") - * (NestedList + ListContinuationBlock^0) - * (blanklines / "\n\n") - ) / parse_blocks - end - - local BulletList = ( Ct(TightListItem(bullet)^1) - * Cc(true) * skipblanklines * -bullet - + Ct(LooseListItem(bullet)^1) - * Cc(false) * skipblanklines ) / writer.bulletlist - - local function ordered_list(s,tight,startnum) - if options.startnum then - startnum = tonumber(startnum) or 1 -- fallback for '#' - else - startnum = nil - end - return writer.orderedlist(s,tight,startnum) - end - - local OrderedList = Cg(enumerator, "listtype") * - ( Ct(TightListItem(Cb("listtype")) * TightListItem(enumerator)^0) - * Cc(true) * skipblanklines * -enumerator - + Ct(LooseListItem(Cb("listtype")) * LooseListItem(enumerator)^0) - * Cc(false) * skipblanklines - ) * Cb("listtype") / ordered_list - - local defstartchar = S("~:") - local defstart = ( defstartchar * #spacing * (tab + space^-3) - + space * defstartchar * #spacing * (tab + space^-2) - + space * space * defstartchar * #spacing * (tab + space^-1) - + space * space * space * defstartchar * #spacing - ) - - local dlchunk = Cs(line * (indentedline - blankline)^0) - - local function definition_list_item(term, defs, tight) - return { term = parse_inlines(term), definitions = defs } - end - - local DefinitionListItemLoose = C(line) * skipblanklines - * Ct((defstart * indented_blocks(dlchunk) / parse_blocks)^1) - * Cc(false) - / definition_list_item - - local DefinitionListItemTight = C(line) - * Ct((defstart * dlchunk / parse_blocks)^1) - * Cc(true) - / definition_list_item - - local DefinitionList = ( Ct(DefinitionListItemLoose^1) * Cc(false) - + Ct(DefinitionListItemTight^1) - * (skipblanklines * -DefinitionListItemLoose * Cc(true)) - ) / writer.definitionlist - - ------------------------------------------------------------------------------ - -- Lua metadata - ------------------------------------------------------------------------------ - - local function lua_metadata(s) -- run lua code in comment in sandbox - local env = { m = parse_markdown, markdown = parse_blocks } - local scode = s:match("^") - local untrusted_table, message = load(scode, nil, "t", env) - if not untrusted_table then - util.err(message, 37) - end - local ok, msg = pcall(untrusted_table) - if not ok then - util.err(msg) - end - for k,v in pairs(env) do - writer.set_metadata(k,v) - end - return "" - end - - local LuaMeta = fail - if options.lua_metadata then - LuaMeta = #P(" - - - - - - - - - - - - - - - - - - - - - -]===] - - return DZSlides -end - -return M diff --git a/lua-libraries/lunamark/writer/generic.lua b/lua-libraries/lunamark/writer/generic.lua deleted file mode 100644 index 92133e473..000000000 --- a/lua-libraries/lunamark/writer/generic.lua +++ /dev/null @@ -1,294 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Generic writer for lunamark. --- This serves as generic documentation for lunamark writers, --- which all export a table with the same functions defined. --- --- New writers can simply modify the generic writer: for example, --- --- local Xml = generic.new(options) --- --- Xml.linebreak = "" --- --- local escaped = { --- ["<" ] = "<", --- [">" ] = ">", --- ["&" ] = "&", --- ["\"" ] = """, --- ["'" ] = "'" --- } --- --- function Xml.string(s) --- return s:gsub(".",escaped) --- end - -local util = require("lunamark.util") -local M = {} -local W = {} - -local meta = {} -meta.__index = - function(_, key) - io.stderr:write(string.format("WARNING: Undefined writer function '%s'\n",key)) - return (function(...) return table.concat({...}," ") end) - end -setmetatable(W, meta) - -local rope_to_string = util.rope_to_string - ---- Returns a table with functions defining a generic lunamark writer, --- which outputs plain text with no formatting. `options` is an optional --- table with the following fields: --- --- `layout` --- : `minimize` (no space between blocks) --- : `compact` (no extra blank lines between blocks) --- : `default` (blank line between blocks) -function M.new(options) - ---- The table contains the following fields: - - options = options or {} - local metadata = {} - - --- Set metadata field `key` to `val`. - function W.set_metadata(key, val) - metadata[key] = val - return "" - end - - --- Add `val` to an array in metadata field `key`. - function W.add_metadata(key, val) - local cur = metadata[key] - if type(cur) == "table" then - table.insert(cur,val) - elseif cur then - metadata[key] = {cur, val} - else - metadata[key] = {val} - end - end - - --- Return metadata table. - function W.get_metadata() - return metadata - end - - -- Turn list of output into final result. - function W.merge(result) - return rope_to_string(result) - end - - --- A space (string). - W.space = " " - - --- Setup tasks at beginning of document. - function W.start_document() - return "" - end - - --- Finalization tasks at end of document. - function W.stop_document() - return "" - end - - --- Plain text block (not formatted as a pragraph). - function W.plain(s) - return s - end - - --- A line break (string). - W.linebreak = "\n" - - --- Line breaks to use between block elements. - W.interblocksep = "\n\n" - - --- Line breaks to use between a container (like a `
` - -- tag) and the adjacent block element. - W.containersep = "\n" - - if options.layout == "minimize" then - W.interblocksep = "" - W.containersep = "" - elseif options.layout == "compact" then - W.interblocksep = "\n" - W.containersep = "\n" - end - - --- Ellipsis (string). - W.ellipsis = "…" - - --- Em dash (string). - W.mdash = "—" - - --- En dash (string). - W.ndash = "–" - - --- Non-breaking space. - W.nbsp = " " - - --- String in curly single quotes. - function W.singlequoted(s) - return {"‘", s, "’"} - end - - --- String in curly double quotes. - function W.doublequoted(s) - return {"“", s, "”"} - end - - --- String, escaped as needed for the output format. - function W.string(s) - return s - end - - --- Inline (verbatim) code. - function W.code(s) - return s - end - - --- A link with link text `label`, uri `uri`, - -- and title `title`. - function W.link(label, uri, title) - return label - end - - --- An image link with alt text `label`, - -- source `src`, and title `title`. - function W.image(label, src, title) - return label - end - - --- A paragraph. - function W.paragraph(s) - return s - end - - --- A bullet list with contents `items` (an array). If - -- `tight` is true, returns a "tight" list (with - -- minimal space between items). - function W.bulletlist(items,tight) - return util.intersperse(items,W.interblocksep) - end - - --- An ordered list with contents `items` (an array). If - -- `tight` is true, returns a "tight" list (with - -- minimal space between items). If optional - -- number `startnum` is present, use it as the - -- number of the first list item. - function W.orderedlist(items,tight,startnum) - return util.intersperse(items,W.interblocksep) - end - - --- Inline HTML. - function W.inline_html(s) - return "" - end - - --- Display HTML (HTML block). - function W.display_html(s) - return "" - end - - --- Emphasized text. - function W.emphasis(s) - return s - end - - --- Strongly emphasized text. - function W.strong(s) - return s - end - - --- Block quotation. - function W.blockquote(s) - return s - end - - --- Verbatim block. - function W.verbatim(s) - return s - end - - --- Fenced code block, with infostring `i`. - function W.fenced_code(s, i) - return s - end - - --- Header level `level`, with text `s`. - function W.header(s, level) - return s - end - - --- Horizontal rule. - W.hrule = "" - - --- A string of one or more citations. `text_cites` is a boolean, true if the - -- citations are in-text citations. `cites` - is an array of tables, each of - -- the form `{ prenote = q, name = n, postnote = e, suppress_author = s }`, - -- where: - -- - `q` is a nil or a rope that should be inserted before the citation, - -- - `e` is a nil or a rope that should be inserted after the citation, - -- - `n` is a string with the citation name, and - -- - `s` is a boolean, true if the author should be omitted from the - -- citation. - function W.citations(text_cites, cites) - local buffer = {} - local opened_brackets = false - for i, cite in ipairs(cites) do - if i == 1 then -- Opening in-text citation - if text_cites then - buffer[#buffer + 1] = {cite.suppress_author and "-" or "", "@", - cite.name} - if cite.postnote then - opened_brackets = true - buffer[#buffer + 1] = {" [", cite.postnote} - end - else -- Opening regular citation - opened_brackets = true - buffer[#buffer + 1] = {"[", cite.prenote and {cite.prenote, " "} or "", - cite.suppress_author and "-" or "", "@", cite.name, cite.postnote and - {" ", cite.postnote}} - end - else -- Continuation citation - buffer[#buffer + 1] = {"; ", cite.prenote and {cite.prenote, " "} or "", - cite.suppress_author and "-" or "", "@", cite.name, cite.postnote and - {" ", cite.postnote}} - end - end - if opened_brackets then - buffer[#buffer + 1] = "]" - end - return buffer - end - - --- A footnote or endnote. - function W.note(contents) - return contents - end - - --- A definition list. `items` is an array of tables, - -- each of the form `{ term = t, definitions = defs, tight = tight }`, - -- where `t` is a string and `defs` is an array of strings. - -- `tight` is a boolean, true if it is a tight list. - function W.definitionlist(items, tight) - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = item.t - buffer[#buffer + 1] = util.intersperse(item.definitions, W.interblocksep) - end - return util.intersperse(buffer,W.interblocksep) - end - - --- A cosmo template to be used in producing a standalone document. - -- `$body` is replaced with the document body, `$title` with the - -- title, and so on. - W.template = [[ -$body -]] - - return util.table_copy(W) -end - -return M diff --git a/lua-libraries/lunamark/writer/groff.lua b/lua-libraries/lunamark/writer/groff.lua deleted file mode 100644 index d722fea92..000000000 --- a/lua-libraries/lunamark/writer/groff.lua +++ /dev/null @@ -1,81 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Generic groff writer for lunamark. --- This is currently used as the basis for [lunamark.writer.man]. --- In principle other groff-based writers could also extend it. - -local M = {} - -local util = require("lunamark.util") -local generic = require("lunamark.writer.generic") - ---- Returns a new Groff writer. --- For a list of all fields, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local Groff = generic.new(options) - - Groff.interblocksep = "\n\n" -- insensitive to layout - - Groff.containersep = "\n" - - Groff.linebreak = ".br\n" - - Groff.ellipsis = "\\&..." - - Groff.mdash = "\\[em]" - - Groff.ndash = "\\[en]" - - Groff.nbsp = "\\~" - - function Groff.singlequoted(s) - return {"`",s,"'"} - end - - function Groff.doublequoted(s) - return {"\\[lq]",s,"\\[rq]"} - end - - Groff.escaped = { - ["@"] = "\\@", - ["\\"] = "\\\\", - } - - local escaped_utf8_triplet = { - ["\226\128\156"] = "\\[lq]", - ["\226\128\157"] = "\\[rq]", - ["\226\128\152"] = "`", - ["\226\128\153"] = "'", - ["\226\128\148"] = "\\[em]", - ["\226\128\147"] = "\\[en]", - ["\194\160"] = "\\ ", - } - - local escape = util.escaper(Groff.escaped, escaped_utf8_triplet) - - Groff.string = escape - - function Groff.inline_html(s) - end - - function Groff.display_html(s) - end - - function Groff.code(s) - return {"\\f[C]",s,"\\f[]"} - end - - function Groff.emphasis(s) - return {"\\f[I]",s,"\\f[]"} - end - - function Groff.strong(s) - return {"\\f[B]",s,"\\f[]"} - end - - return Groff -end - -return M diff --git a/lua-libraries/lunamark/writer/html.lua b/lua-libraries/lunamark/writer/html.lua deleted file mode 100644 index eba6e8eb3..000000000 --- a/lua-libraries/lunamark/writer/html.lua +++ /dev/null @@ -1,191 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- HTML writer for lunamark. --- Extends [lunamark.writer.xml]. - -local M = {} - -local xml = require("lunamark.writer.xml") -local util = require("lunamark.util") -local flatten, intersperse, map = util.flatten, util.intersperse, util.map - ---- Return a new HTML writer. --- For a list of all fields in the writer, see [lunamark.writer.generic]. --- ---`options` is a table that can contain the following fields: --- --- `containers` --- : Put sections in `
` tags. --- --- `slides` --- : Do not allow containers to nest; when a subsection begins, --- close the section's container and start a new one. --- --- `layout` --- : `minimize` removes semantically insignificant white space. --- : `compact` removes unneeded blank lines. --- : `default` puts blank lines between block elements. -function M.new(options) - options = options or {} - local Html = xml.new(options) - - local endnotes = {} - local containersep = Html.containersep - local interblocksep = Html.interblocksep - - Html.container = "div" - Html.linebreak = "
" - Html.nbsp = " " - - function Html.code(s) - return {"", Html.string(s), ""} - end - - function Html.link(lab,src,tit) - local titattr - if type(tit) == "string" and #tit > 0 - then titattr = " title=\"" .. Html.string(tit) .. "\"" - else titattr = "" - end - return {"", lab, ""} - end - - function Html.image(lab,src,tit) - local titattr - if type(tit) == "string" and #tit > 0 - then titattr = " title=\"" .. Html.string(tit) .. "\"" - else titattr = "" - end - return {"\"","} - end - - function Html.paragraph(s) - return {"

", s, "

"} - end - - local function listitem(s) - return {"
  • ", s, "
  • "} - end - - function Html.bulletlist(items,tight) - return {"
      ", containersep, intersperse(map(items, listitem), containersep), containersep, "
    "} - end - - function Html.orderedlist(items,tight,startnum) - local start = "" - if startnum and startnum ~= 1 then - start = " start=\"" .. startnum .. "\"" - end - return {"", containersep, intersperse(map(items, listitem), containersep), containersep, ""} - end - - function Html.inline_html(s) - return s - end - - function Html.display_html(s) - return s - end - - function Html.emphasis(s) - return {"", s, ""} - end - - function Html.strong(s) - return {"", s, ""} - end - - function Html.blockquote(s) - return {"
    ", containersep, s, containersep, "
    "} - end - - function Html.verbatim(s) - return {"
    ", Html.string(s), "
    "} - end - - function Html.fenced_code(s,i) - if i ~= "" then - return {'
    ', Html.string(s), "
    "} - else - return Html.verbatim(s) - end - end - - function Html.header(s,level) - local sep = "" - if options.slides or options.containers then - local lev = (options.slides and 1) or level - local stop = Html.stop_section(lev) - if stop ~= "" then - stop = stop .. Html.interblocksep - end - sep = stop .. Html.start_section(lev) .. Html.containersep - end - return {sep, "", s, ""} - end - - Html.hrule = "
    " - - function Html.note(contents) - local num = #endnotes + 1 - local backref = ' ' - local contentsf = flatten(contents) - if contentsf[#contentsf] == "

    " then - table.insert(contentsf, #contentsf, backref) - else - contentsf[#contentsf + 1] = backref - end - endnotes[num] = {'
  • ', contentsf, '
  • '} - return {'', num, ''} - end - - function Html.start_document() - endnotes = {} - return "" - end - - function Html.stop_document() - return function() - local stop = Html.stop_section(1) -- close section containers - if stop ~= "" then stop = Html.containersep .. stop end - if #endnotes == 0 then - return stop - else - return {stop, interblocksep, '
    ', interblocksep, '
      ', - containersep, intersperse(endnotes, interblocksep), containersep, '
    '} - end - end - end - - function Html.definitionlist(items, tight) - local buffer = {} - local sep - if tight then sep = "" else sep = Html.containersep end - for _,item in ipairs(items) do - local defs = {} - for _,def in ipairs(item.definitions) do - defs[#defs + 1] = {"
    ", sep, def, sep, "
    "} - end - buffer[#buffer + 1] = {"
    ", item.term, "
    ", containersep, intersperse(defs, containersep)} - end - return {"
    ", containersep, intersperse(buffer, containersep), containersep, "
    "} - end - - Html.template = [[ - - - -$title - - -$body - - -]] - - return Html -end - -return M diff --git a/lua-libraries/lunamark/writer/html5.lua b/lua-libraries/lunamark/writer/html5.lua deleted file mode 100644 index cfb1db937..000000000 --- a/lua-libraries/lunamark/writer/html5.lua +++ /dev/null @@ -1,37 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- HTML 5 writer for lunamark. --- Extends [lunamark.writer.html], but uses `
    ` tags for sections --- if `options.containers` is true. - -local M = {} - -local html = require("lunamark.writer.html") - ---- Returns a new HTML 5 writer. --- `options` is as in `lunamark.writer.html`. --- For a list of fields, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local Html5 = html.new(options) - - Html5.container = "section" - - Html5.template = [[ - - - - -$title - - -$body - - -]] - - return Html5 -end - -return M diff --git a/lua-libraries/lunamark/writer/latex.lua b/lua-libraries/lunamark/writer/latex.lua deleted file mode 100644 index 3b9f7e3c3..000000000 --- a/lua-libraries/lunamark/writer/latex.lua +++ /dev/null @@ -1,260 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- LaTeX writer for lunamark. --- Extends the [lunamark.writer.tex]. - -local M = {} - -local tex = require("lunamark.writer.tex") -local util = require("lunamark.util") -local format = string.format - ---- Returns a new LaTeX writer. --- --- * `options` is a table with parsing options. --- The following fields are significant: --- --- `citations` --- : Enable citations as in pandoc. Either a boolean or one of --- the following strings should be specified: --- --- - `latex` -- produce basic LaTeX2e citations, --- - `natbib` -- produce citations for the Natbib package, or --- - `biblatex` -- produce citations for the BibLaTeX package. --- --- For a list of fields in the writer, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local LaTeX = tex.new(options) - - function LaTeX.code(s) - return {"\\texttt{",LaTeX.string(s),"}"} - end - - function LaTeX.link(lab,src,tit) - return {"\\href{",LaTeX.string(src),"}{",lab,"}"} - end - - function LaTeX.image(lab,src,tit) - return {"\\includegraphics{",LaTeX.string(src),"}"} - end - - local function listitem(s) - return {"\\item ",s} - end - - function LaTeX.bulletlist(items) - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = listitem(item) - end - local contents = util.intersperse(buffer,"\n") - return {"\\begin{itemize}\n",contents,"\n\\end{itemize}"} - end - - function LaTeX.orderedlist(items) - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = listitem(item) - end - local contents = util.intersperse(buffer,"\n") - return {"\\begin{enumerate}\n",contents,"\n\\end{enumerate}"} - end - - function LaTeX.emphasis(s) - return {"\\emph{",s,"}"} - end - - function LaTeX.strong(s) - return {"\\textbf{",s,"}"} - end - - function LaTeX.blockquote(s) - return {"\\begin{quote}\n",s,"\n\\end{quote}"} - end - - function LaTeX.verbatim(s) - return {"\\begin{verbatim}\n",s,"\\end{verbatim}"} - end - - LaTeX.fenced_code = LaTeX.verbatim - - function LaTeX.header(s,level) - local cmd - if level == 1 then - cmd = "\\section" - elseif level == 2 then - cmd = "\\subsection" - elseif level == 3 then - cmd = "\\subsubsection" - elseif level == 4 then - cmd = "\\paragraph" - elseif level == 5 then - cmd = "\\subparagraph" - else - cmd = "" - end - return {cmd,"{",s,"}"} - end - - LaTeX.hrule = "\\hspace{\\fill}\\rule{.6\\linewidth}{0.4pt}\\hspace{\\fill}" - - function LaTeX.note(contents) - return {"\\footnote{",contents,"}"} - end - - local function citation_optargs(cite) - if cite.prenote and cite.postnote then - return {"[", cite.prenote, "][", cite.postnote, "]"} - elseif cite.prenote and not cite.postnote then - return {"[", cite.prenote, "][]"} - elseif not cite.prenote and cite.postnote then - return {"[", cite.postnote, "]"} - else - return "" - end - end - - if options.citations == true or options.citations == "latex" then - --- Basic LaTeX2e citations - function LaTeX.citations(_, cites) - local buffer = {} - local opened_braces = false - for i, cite in ipairs(cites) do - if cite.prenote or cite.postnote then -- A separate complex citation - buffer[#buffer + 1] = {opened_braces and "}" or "", - cite.prenote and {i == 1 and "" or " ", cite.prenote, "~"} or - "", {(i == 1 or cite.prenote) and "" or " ", "\\cite"}, - cite.postnote and {"[", cite.postnote, "]"} or "", "{", cite.name, - cite_postnote and {"~", cite_postnote} or "", "}"} - opened_braces = false - else -- A string of simple citations - buffer[#buffer + 1] = {opened_braces and ", " or {i == 1 and "" or - " ", "\\cite{"}, cite.name} - opened_braces = true - end - end - if opened_braces then - buffer[#buffer + 1] = "}" - end - return buffer - end - elseif options.citations == "natbib" then - --- NatBib citations - function LaTeX.citations(text_cites, cites) - if #cites == 1 then -- A single citation - local cite = cites[1] - if text_cites then - return {"\\citet", citation_optargs(cite), "{", cite.name, "}"} - else - return {cite.suppress_author and "\\citeyearpar" or "\\citep", - citation_optargs(cite), "{", cite.name, "}"} - end - else -- A string of citations - local complex = false - local last_suppressed = nil - for _, cite in ipairs(cites) do - if cite.prenote or cite.postnote or - cite.suppress_author == not last_suppressed then - complex = true - break - end - last_suppressed = cite.suppress_author - end - if complex then -- A string of complex citations - local buffer = {"\\citetext{"} - for i, cite in ipairs(cites) do - buffer[#buffer + 1] = {i ~= 1 and "; " or "", cite.suppress_author - and "\\citeyear" or (text_cites and "\\citealt" or "\\citealp"), - citation_optargs(cite), "{", cite.name, "}"} - end - buffer[#buffer + 1] = "}" - return buffer - else -- A string of simple citations - local buffer = {} - for i, cite in ipairs(cites) do - buffer[#buffer + 1] = {i == 1 and (text_cites and "\\citet{" or - "\\citep{") or ", ", cite.name} - end - buffer[#buffer + 1] = "}" - return buffer - end - end - end - elseif options.citations == "biblatex" then - --- BibLaTeX citations - function LaTeX.citations(text_cites, cites) - if #cites == 1 then -- A single citation - local cite = cites[1] - if text_cites then - return {"\\textcite", citation_optargs(cite), "{", cite.name, "}"} - else - return {"\\autocite", cite.suppress_author and "*" or "", - citation_optargs(cite), "{", cite.name, "}"} - end - else -- A string of citations - local buffer = {text_cites and "\\textcites" or "\\autocites"} - for _, cite in ipairs(cites) do - buffer[#buffer + 1] = {citation_optargs(cite), "{", cite.name, "}"} - end - return buffer - end - end - end - - function LaTeX.definitionlist(items) - local buffer = {} - for _,item in ipairs(items) do - buffer[#buffer + 1] = format("\\item[%s]\n%s", - item.term, util.intersperse(item.definitions, LaTeX.interblocksep)) - end - local contents = util.intersperse(buffer, LaTeX.containersep) - return {"\\begin{description}\n",contents,"\n\\end{description}"} - end - - LaTeX.template = [===[ -\documentclass{article} -\usepackage{amssymb,amsmath} -\usepackage{ifxetex,ifluatex} -\ifxetex - \usepackage{fontspec,xltxtra,xunicode} - \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase} -\else - \ifluatex - \usepackage{fontspec} - \defaultfontfeatures{Mapping=tex-text,Scale=MatchLowercase} - \else - \usepackage[utf8]{inputenc} - \fi -\fi -\ifxetex - \usepackage[setpagesize=false, % page size defined by xetex - unicode=false, % unicode breaks when used with xetex - xetex]{hyperref} -\else - \usepackage[unicode=true]{hyperref} -\fi -\hypersetup{breaklinks=true, pdfborder={0 0 0}} -\setlength{\parindent}{0pt} -\setlength{\parskip}{6pt plus 2pt minus 1pt} -\setlength{\emergencystretch}{3em} % prevent overfull lines -\setcounter{secnumdepth}{0} - -\title{$title} -\author{$sepby{author}[=[$it]=][=[ \and ]=]} -\date{$date} - -\begin{document} - -$if{ title }[[\maketitle -]] -$body - -\end{document} -]===] - - return LaTeX -end - -return M diff --git a/lua-libraries/lunamark/writer/man.lua b/lua-libraries/lunamark/writer/man.lua deleted file mode 100644 index db4992a64..000000000 --- a/lua-libraries/lunamark/writer/man.lua +++ /dev/null @@ -1,130 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Groff man writer for lunamark. --- Extends [lunamark.writer.groff]. --- --- Note: continuation paragraphs in lists are not --- handled properly. - -local M = {} - -local groff = require("lunamark.writer.groff") -local util = require("lunamark.util") -local format = string.format - ---- Returns a new groff writer. --- For a list of fields, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local Man = groff.new(options) - - local endnotes = {} - - function Man.link(lab,src,tit) - return {lab," (",src,")"} - end - - function Man.image(lab,src,tit) - return {"[IMAGE (",lab,")]"} - end - - -- TODO handle continuations properly. - -- pandoc does this: - -- .IP \[bu] 2 - -- one - -- .RS 2 - -- .PP - -- cont - -- .RE - - function Man.paragraph(contents) - return {".PP\n",contents} - end - - function Man.bulletlist(items,tight) - local buffer = {} - for _,item in ipairs(items) do - local revitem = item - -- we don't want to have .IP then .PP - if revitem[1][1] == ".PP\n" then revitem[1][1] = "" end - buffer[#buffer + 1] = {".IP \\[bu] 2\n",item} - end - return util.intersperse(buffer, Man.containersep) - end - - function Man.orderedlist(items,tight,startnum) - local buffer = {} - local num = startnum or 1 - for _,item in ipairs(items) do - local revitem = item - -- we don't want to have .IP then .PP - if revitem[1][1] == ".PP\n" then revitem[1][1] = "" end - buffer[#buffer + 1] = {format(".IP \"%d.\" 4\n",num),item} - num = num + 1 - end - return util.intersperse(buffer, Man.containersep) - end - - function Man.blockquote(s) - return {".RS\n",s,"\n.RE"} - end - - function Man.verbatim(s) - return {".IP\n.nf\n\\f[C]\n",s,".fi"} - end - - Man.fenced_code = Man.verbatim - - function Man.header(s,level) - local hcode = ".SS" - if level == 1 then hcode = ".SH" end - return {hcode," ",s} - end - - Man.hrule = ".PP\n * * * * *" - - function Man.note(contents) - local num = #endnotes + 1 - endnotes[num] = {format(".SS [%d]\n",num),contents} - return format('[%d]', num) - end - - function Man.definitionlist(items,tight) - local buffer = {} - local ds - for _,item in ipairs(items) do - if tight then - ds = util.intersperse(item.definitions,"\n.RS\n.RE\n") - buffer[#buffer + 1] = {".TP\n.B ",item.term,"\n",ds,"\n.RS\n.RE"} - else - ds = util.intersperse(item.definitions,"\n.RS\n.RE\n") - buffer[#buffer + 1] = {".TP\n.B ",item.term,"\n.RS\n",ds,"\n.RE"} - end - end - local contents = util.intersperse(buffer,"\n") - return contents - end - - function Man.start_document() - endnotes = {} - return "" - end - - function Man.stop_document() - if #endnotes == 0 then - return "" - else - return {"\n.SH NOTES\n", util.intersperse(endnotes, "\n")} - end - end - - Man.template = [===[ -.TH "$title" "$section" "$date" "$left_footer" "$center_header" -$body -]===] - - return Man -end - -return M diff --git a/lua-libraries/lunamark/writer/tex.lua b/lua-libraries/lunamark/writer/tex.lua deleted file mode 100644 index 09cb24e08..000000000 --- a/lua-libraries/lunamark/writer/tex.lua +++ /dev/null @@ -1,89 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Generic TeX writer for lunamark. --- It extends [lunamark.writer.generic] and is extended by --- [lunamark.writer.latex] and [lunamark.writer.context]. - -local M = {} - -local util = require("lunamark.util") -local generic = require("lunamark.writer.generic") -local format = string.format - ---- Returns a new TeX writer. --- For a list ofy fields, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local TeX = generic.new(options) - - TeX.interblocksep = "\n\n" -- insensitive to layout - - TeX.containersep = "\n" - - TeX.linebreak = "\\\\" - - TeX.ellipsis = "\\ldots{}" - - TeX.mdash = "---" - - TeX.ndash = "--" - - TeX.nbsp = "~" - - function TeX.singlequoted(s) - return format("`%s'",s) - end - - function TeX.doublequoted(s) - return format("``%s''",s) - end - - TeX.escaped = { - ["{"] = "\\{", - ["}"] = "\\}", - ["$"] = "\\$", - ["%"] = "\\%", - ["&"] = "\\&", - ["_"] = "\\_", - ["#"] = "\\#", - ["^"] = "\\^{}", - ["\\"] = "\\char92{}", - ["~"] = "\\char126{}", - ["|"] = "\\char124{}", - ["<"] = "\\char60{}", - [">"] = "\\char62{}", - ["["] = "{[}", -- to avoid interpretation as optional argument - ["]"] = "{]}", - } - - local str_escaped = { - ["\226\128\156"] = "``", - ["\226\128\157"] = "''", - ["\226\128\152"] = "`", - ["\226\128\153"] = "'", - ["\226\128\148"] = "---", - ["\226\128\147"] = "--", - ["\194\160"] = "~", - } - - local escaper = util.escaper(TeX.escaped, str_escaped) - - TeX.string = escaper - - function TeX.inline_html(s) - return "" - end - - function TeX.display_html(s) - return "" - end - - function TeX.paragraph(s) - return s - end - - return TeX -end - -return M diff --git a/lua-libraries/lunamark/writer/xml.lua b/lua-libraries/lunamark/writer/xml.lua deleted file mode 100644 index 8d02ec1fa..000000000 --- a/lua-libraries/lunamark/writer/xml.lua +++ /dev/null @@ -1,60 +0,0 @@ --- (c) 2009-2011 John MacFarlane. Released under MIT license. --- See the file LICENSE in the source for details. - ---- Generic XML writer for lunamark. --- It extends [lunamark.writer.generic] and is extended by --- [lunamark.writer.html] and [lunamark.writer.docbook]. - -local M = {} - -local generic = require("lunamark.writer.generic") -local util = require("lunamark.util") - ---- Returns a new XML writer. --- For a list of fields, see [lunamark.writer.generic]. -function M.new(options) - options = options or {} - local Xml = generic.new(options) - - Xml.container = "section" - -- {1,2} means: a second level header inside a first-level - local header_level_stack = {} - - function Xml.start_section(level) - header_level_stack[#header_level_stack + 1] = level - return "<" .. Xml.container .. ">" - end - - function Xml.stop_section(level) - local len = #header_level_stack - if len == 0 then - return "" - else - local last = header_level_stack[len] - local res = {} - while last >= level do - header_level_stack[len] = nil - table.insert(res, "") - len = len - 1 - last = (len > 0 and header_level_stack[len]) or 0 - end - return table.concat(res, Xml.containersep) - end - end - - Xml.linebreak = "" - - local escape = util.escaper { - ["<" ] = "<", - [">" ] = ">", - ["&" ] = "&", - ["\"" ] = """, - ["'" ] = "'" - } - - Xml.string = escape - - return Xml -end - -return M diff --git a/lua-libraries/semver.lua b/lua-libraries/semver.lua new file mode 100644 index 000000000..867eec95f --- /dev/null +++ b/lua-libraries/semver.lua @@ -0,0 +1,46 @@ +-- Loosely inspired from https://github.com/kikito/semver.lua +-- (MIT License (c) 2011 Enrique García Cota) +-- but simplified to our bare needs. + +local semver = {} +local mt = {} + +function mt:__eq(other) + return self.major == other.major and + self.minor == other.minor and + self.patch == other.patch +end + +function mt:__lt(other) + if self.major ~= other.major then return self.major < other.major end + if self.minor ~= other.minor then return self.minor < other.minor end + if self.patch ~= other.patch then return self.patch < other.patch end + return false +end + +function mt:__le(other) + if self.major ~= other.major then return self.major <= other.major end + if self.minor ~= other.minor then return self.minor <= other.minor end + if self.patch ~= other.patch then return self.patch <= other.patch end + return true +end + +function mt:__tostring() + return ("%d.%d.%d"):format(self.major, self.minor, self.patch) +end + +local function new (vstr) + local major, minor, patch = vstr:match("^v?(%d+)%.(%d+)%.(%d+)") + local result = { major = tonumber(major), minor = tonumber(minor), patch = tonumber(patch) } + if not result.major and not result.minor and not result.patch then + error("Invalid version string: "..vstr) + end + local o = setmetatable(result, mt) + return o +end + +setmetatable(semver, { + __call = function(_, ...) return new(...) end +}) + +return semver diff --git a/outputters/base.lua b/outputters/base.lua index b130f4b10..a5ce0aacd 100644 --- a/outputters/base.lua +++ b/outputters/base.lua @@ -38,8 +38,8 @@ function outputter:getOutputFilename () local fname if SILE.outputFilename then fname = SILE.outputFilename - elseif SILE.masterFilename then - fname = SILE.masterFilename + elseif SILE.input.filenames[1] then + fname = pl.path.splitext(SILE.input.filenames[1]) if self.extension then fname = fname .. "." .. self.extension end diff --git a/outputters/debug.lua b/outputters/debug.lua index acc296e16..9cfe3e3bd 100644 --- a/outputters/debug.lua +++ b/outputters/debug.lua @@ -15,7 +15,7 @@ local function _round (input) -- just enough to fix the bias so our test suite works across interpreters. -- Note that even a true rounding function here will fail because the bias is -- inherent to the floating point type. Also note we are erroring in favor of - -- the *less* common option beacuse the LuaJIT VMS are hopelessly broken + -- the *less* common option because the LuaJIT VMS are hopelessly broken -- whereas normal LUA VMs can be cooerced. if input > 0 then input = input + .00000000000001 end if input < 0 then input = input - .00000000000001 end diff --git a/outputters/libtexpdf.lua b/outputters/libtexpdf.lua index 80f2e37de..9e6ab8e03 100644 --- a/outputters/libtexpdf.lua +++ b/outputters/libtexpdf.lua @@ -42,7 +42,7 @@ function outputter:newPage () pdf.beginpage() end --- pdf stucture package needs a tie in here +-- pdf structure package needs a tie in here function outputter._endHook (_) end diff --git a/outputters/text.lua b/outputters/text.lua index 2898cff25..661635c0e 100644 --- a/outputters/text.lua +++ b/outputters/text.lua @@ -59,7 +59,7 @@ function outputter:setCursor (x, y, relative) outfile:write("‫") end elseif newx > cursorX then - if newx - cursorX > spc then + if newx:tonumber() - cursorX:tonumber() > spc then outfile:write(" ") else outfile:write("‫") diff --git a/package.json b/package.json index 7f9619236..043deba9a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sile", - "version": "0.14.9", + "version": "0.14.13", "description": "The SILE Typesetter", "main": "sile", "scripts": { @@ -19,14 +19,14 @@ }, "homepage": "https://sile-typesetter.org", "devDependencies": { - "@commitlint/cli": "^17.5.1", - "@commitlint/config-conventional": "^17.4.4", - "@commitlint/prompt": "^17.5.0", - "commitizen": "^4.3.0", - "conventional-changelog-cli": "^2.2.2", - "husky": "^8.0.3", - "standard-version": "^9.5.0", - "yaml": "^2.2.1" + "@commitlint/cli": "^17.7", + "@commitlint/config-conventional": "^17.7", + "@commitlint/prompt": "^17.7", + "commitizen": "^4.3", + "conventional-changelog-cli": "^4.1", + "husky": "^8.0", + "standard-version": "^9.5", + "yaml": "^2.3" }, "config": { "commitizen": { @@ -42,6 +42,10 @@ { "filename": "package.json", "type": "json" + }, + { + "filename": "Cargo.toml", + "updater": "build-aux/cargo-updater.js" } ], "infile": "CHANGELOG.md", diff --git a/packages/autodoc/init.lua b/packages/autodoc/init.lua index 09818b426..167bba5f2 100644 --- a/packages/autodoc/init.lua +++ b/packages/autodoc/init.lua @@ -13,7 +13,9 @@ local theme = { setting = "#42280e", -- some kind of dark brown bracketed = "#656565", -- some grey package = "#172557", -- saturated space blue - note = "#525257" -- some asphalt grey hue + note = "#525257", -- some asphalt grey hue + class = "#6a2c54", -- some dark shaded magenta + codeblock = "#303040" -- dark grey with a hint of blue } local colorWrapper = function (ctype, content) @@ -170,6 +172,12 @@ function package:registerCommands () end) end) + self:registerCommand("autodoc:class:style", function (_, content) + SILE.call("font", { weight = 700 }, function() + colorWrapper("class", content) + end) + end) + self:registerCommand("autodoc:code:style", function (options, content) -- options.type is used to distinguish the type of code element and style -- it accordingly: "ast", "setting", "environment" shall select the font @@ -293,38 +301,62 @@ function package:registerCommands () -- We cannot really check package name to exist! SILE.call("autodoc:package:style", {}, { name }) - end, "Outputs a package name in code, checking its validity.") + end, "Outputs a package name.") + + -- Documenting a class name + + self:registerCommand("autodoc:class", function (_, content) + if type(content) ~= "table" then SU.error("Expected a table content") end + if #content ~= 1 then SU.error("Expected a single element") end + local name = type(content[1] == "string") and content[1] + if not name then SU.error("Unexpected class name") end + -- We cannot really check class name to exist! + + SILE.call("autodoc:class:style", {}, { name }) + end, "Outputs a class name.") -- Homogenizing the appearance of blocks of code self:registerCommand("autodoc:codeblock", function(_, content) - SILE.call("bigskip") SILE.settings:temporarily(function() -- Note: We avoid using the verbatim environment and simplify things a bit -- (and try to better enforce novbreak points of insertion) SILE.call("verbatim:font") + -- Rather than absolutizing 4 different values, just do it once and cache it + local ex = SILE.measurement("1ex"):absolute() + SILE.typesetter:leaveHmode() SILE.settings:set("typesetter.parseppattern", "\n") SILE.settings:set("typesetter.obeyspaces", true) SILE.settings:set("document.parindent", SILE.nodefactory.glue()) - SILE.settings:set("document.parskip", SILE.nodefactory.vglue("1pt")) - SILE.settings:set("document.baselineskip", SILE.nodefactory.glue("1.2em")) + SILE.settings:set("document.parskip", SILE.nodefactory.vglue(0.3*ex)) + SILE.settings:set("document.baselineskip", SILE.nodefactory.glue(2.3*ex)) SILE.settings:set("document.spaceskip", SILE.length("1spc")) SILE.settings:set("shaper.variablespaces", false) SILE.settings:set("document.language", "und") - SILE.call("autodoc:line") - SILE.call("novbreak") - SILE.process(content) - SILE.call("novbreak") SILE.typesetter:leaveHmode() + colorWrapper("codeblock", function () + SILE.call("bigskip") + colorWrapper("note", function () + SILE.call("autodoc:line") + end) + SILE.typesetter:pushVglue(-0.6*ex) + SILE.call("novbreak") + SILE.process(content) + SILE.call("novbreak") + SILE.typesetter:pushVglue(1.4*ex) + colorWrapper("note", function () + SILE.call("autodoc:line") + end) + SILE.call("smallskip") + end) end) - SILE.call("novbreak") - SILE.call("autodoc:line") - SILE.call("smallskip") end, "Outputs its content as a standardized block of code") self:registerCommand("autodoc:line", function(_, _) + SILE.call("novbreak") SILE.call("fullrule", { thickness = "0.5pt" }) - end, "Ouputs a line used for surrounding code blocks (somewhat internal)") + SILE.call("novbreak") + end, "Outputs a line used for surrounding code blocks (somewhat internal)") self:registerCommand("autodoc:example", function(_, content) -- Loosely derived from the \examplefont command from the original SILE manual... @@ -341,7 +373,7 @@ function package:registerCommands () local innerindent = SILE.measurement("1em"):absolute() SILE.settings:temporarily(function () SILE.settings:set("document.lskip", leftindent) - SILE.settings:set("document.rskip", SILE.nodefactory.glue()) + SILE.settings:set("document.rskip", leftindent) SILE.call("noindent") colorWrapper("note", function () @@ -351,10 +383,11 @@ function package:registerCommands () SILE.call("hrule", { width = 3 * linedimen, height = linethickness }) SILE.call("hrule", { width = linethickness, height = linethickness, depth = linedimen }) + SILE.call("noindent") SILE.call("novbreak") SILE.settings:temporarily(function () SILE.settings:set("document.lskip", SILE.nodefactory.glue(leftindent + innerindent)) - SILE.settings:set("document.rskip", SILE.nodefactory.glue(innerindent)) + SILE.settings:set("document.rskip", SILE.nodefactory.glue(leftindent + innerindent)) SILE.call("font", { size = "0.95em", style = "italic "}, content) SILE.call("novbreak") end) @@ -375,7 +408,7 @@ end package.documentation = [[ \begin{document} -This package extracts documentation information from other packages. +The \autodoc:package{autodoc} package extracts documentation information from other packages. It’s used to construct the SILE manual. Keeping package documentation in the package itself keeps the documentation near the implementation, which (in theory) makes it easy for documentation and implementation to be in sync. @@ -409,13 +442,12 @@ The \autodoc:command{\autodoc:parameter} commands takes either a parameter name, The \autodoc:environment{autodoc:codeblock} environment allows typesetting a block of code in a consistent way. This is not a true verbatim environment, and you still have to escape SILE’s special characters within it (unless calling commands is what you really intend doing there, obviously). -For convenience, the package also provides a \code{raw} handler going by the same name, where you do not -have to escape the special characters (backslashes, braces, percents). +For convenience, the package also provides a \code{raw} handler going by the same name, where you do not have to escape the special characters (backslashes, braces, percents). -The \autodoc:command{\autodoc:example} marks its content as an example, possibly typeset in a different choice -of font. +The \autodoc:command{\autodoc:example} marks its content as an example, possibly typeset in a different choice of font. The \autodoc:command{\autodoc:note} outputs its content as a note, in a dedicated framed and indented block. +The \autodoc:command{\autodoc:package} and \autodoc:command{\autodoc:class} commands are used to format a package and class name. \end{document} ]] diff --git a/packages/balanced-frames/init.lua b/packages/balanced-frames/init.lua index 97619d38a..644c1a175 100644 --- a/packages/balanced-frames/init.lua +++ b/packages/balanced-frames/init.lua @@ -55,7 +55,7 @@ local function buildPage (typesetter, independent) end typesetter.state.lastPenalty = 0 local oldPageBuilder = SILE.pagebuilder - SILE.pagebuilder = require("core.pagebuilder")() + SILE.pagebuilder = SILE.pagebuilders.base() while typesetter.frame and typesetter.frame.balanced do unbalanced_buildPage(typesetter, true) if typesetter.frame.next and SILE.getFrame(typesetter.frame.next).balanced == true then diff --git a/packages/base.lua b/packages/base.lua index d7f5a1f27..ae1534b92 100644 --- a/packages/base.lua +++ b/packages/base.lua @@ -14,15 +14,32 @@ local function script_path () return base end -function package:_init (_) +local settingDeclarations = { } +local rawhandlerRegistrations = {} +local commandRegistrations = { } + +function package:_init (_, reload) self.class = SILE.scratch.half_initialized_class or SILE.documentState.documentClass if not self.class then SU.error("Attempted to initialize package before class, should have been queued in the preamble", true) end self.basedir = script_path() - self:declareSettings() - self:registerRawHandlers() - self:registerCommands() + -- Note string.format(%p) would be nicer than tostring() but only LuaJIT and Lua 5.4 support it + local settingsDeclarator = tostring(self.declareSettings) + if reload or not settingDeclarations[settingsDeclarator] then + settingDeclarations[settingsDeclarator] = true + self:declareSettings() + end + local rawhandlerRegistrator = tostring(self.registerRawHandlers) + if reload or not rawhandlerRegistrations[rawhandlerRegistrator] then + rawhandlerRegistrations[rawhandlerRegistrator] = true + self:registerRawHandlers() + end + local commandRegistrator = tostring(self.registerCommands) + if reload or not commandRegistrations[commandRegistrator] then + commandRegistrations[commandRegistrator] = true + self:registerCommands() + end end function package:_post_init () @@ -33,8 +50,12 @@ function package.declareSettings (_) end function package.registerRawHandlers (_) end -function package:loadPackage (packname) - return self.class:loadPackage(packname) +function package:loadPackage (packname, options, reload) + return self.class:loadPackage(packname, options, reload) +end + +function package:reloadPackage (packname, options) + return self.class:reloadPackage(packname, options) end function package.registerCommands (_) end diff --git a/packages/bibtex/init.lua b/packages/bibtex/init.lua index 584f97223..d4b77a6b5 100644 --- a/packages/bibtex/init.lua +++ b/packages/bibtex/init.lua @@ -39,12 +39,12 @@ end) ---@diagnostic enable: undefined-global, unused-local, lowercase-global local parseBibtex = function (fn) - fn = SILE.resolveFile(fn) - local fh,e = io.open(fn) - if e then SU.error("Error reading bibliography file "..e) end + fn = SILE.resolveFile(fn) or SU.error("Unable to resolve Bibtex file "..fn) + local fh, e = io.open(fn) + if e then SU.error("Error reading bibliography file: "..e) end local doc = fh:read("*all") local t = epnf.parsestring(bibtexparser, doc) - if not(t) or not(t[1]) or t.id ~= "document" then + if not t or not t[1] or t.id ~= "document" then SU.error("Error parsing bibtex") end local entries = {} @@ -79,14 +79,8 @@ function package:registerCommands () SILE.scratch.bibtex.bib = parseBibtex(file) -- Later we'll do multiple bibliogs, but not now end) - self:registerCommand("bibstyle", function (_, content) + self:registerCommand("bibstyle", function (_, _) SU.deprecated("\\bibstyle", '\\set[parameter=bibtex.style]', "0.13.2", "0.14.0") - if type(content) == "table" then - content = content[1] - end - if type(content) == "string" then - SILE.settings:set("bibtex.style", content) - end end) self:registerCommand("cite", function (options, content) diff --git a/packages/chordmode/init.lua b/packages/chordmode/init.lua index 8167096bd..60f71ac14 100644 --- a/packages/chordmode/init.lua +++ b/packages/chordmode/init.lua @@ -18,30 +18,29 @@ function package.declareSettings (_) help = "Vertical offset between the chord name and the text." }) - SILE.settings:declare({ - parameter = "chordmode.lineheight", - type = "length", - default = SILE.length("4mm"), - help = "Length of the chord name line." - }) end function package:registerCommands () self:registerCommand("ch", function (options, content) - local chordBox = SILE.typesetter:makeHbox({ options.name }) + local chordBox = SILE.typesetter:makeHbox(function () + SILE.call("chordmode:chordfont", {}, { options.name }) + end) local origWidth = chordBox.width chordBox.width = SILE.length() - chordBox.height = SILE.settings:get("chordmode.lineheight") + SILE.call("raise", { height = SILE.settings:get("chordmode.offset") }, function () - SILE.call("chordmode:chordfont", {}, function () - SILE.typesetter:pushHbox(chordBox) - end) + SILE.typesetter:pushHbox(chordBox) end) + local lyricBox = SILE.call("hbox", {}, content) if lyricBox.width < origWidth then lyricBox.width = origWidth + SILE.length("0.5em"):absolute() end + local chordLineHeight = chordBox.height + SILE.settings:get("chordmode.offset"):absolute() + if chordLineHeight > lyricBox.height then + lyricBox.height = chordLineHeight + end end, "Insert a chord name above the text") local function _addChords (text, content) diff --git a/packages/color-fonts/init.lua b/packages/color-fonts/init.lua index f11d63630..d6955bab7 100644 --- a/packages/color-fonts/init.lua +++ b/packages/color-fonts/init.lua @@ -99,7 +99,8 @@ end package.documentation = [[ \begin{document} -The \autodoc:package{color-fonts} package adds support for fonts with a \code{COLR} OpenType table. +The \autodoc:package{color-fonts} package adds support for fonts with multi-colored glyphs (that is, +OpenType fonts with \code{COLR} and \code{CPAL} tables). This package is automatically loaded when such a font is detected. \end{document} ]] diff --git a/packages/converters/init.lua b/packages/converters/init.lua index ecc4b68aa..7cbb0c511 100644 --- a/packages/converters/init.lua +++ b/packages/converters/init.lua @@ -59,11 +59,12 @@ function package.register (_, sourceExt, targetExt, command) end function package.checkConverters (_, source) + local resolvedSrc = SILE.resolveFile(source) or SU.error("Couldn't find file " .. source) for _, converter in ipairs(SILE.scratch.converters) do local extLen = string.len(converter.sourceExt) - if ((string.len(source) > extLen) and - (string.sub(source, -extLen) == converter.sourceExt)) then - return applyConverter(source, converter) + if ((string.len(resolvedSrc) > extLen) and + (string.sub(resolvedSrc, -extLen) == converter.sourceExt)) then + return applyConverter(resolvedSrc, converter) end end return source -- No conversion needed. @@ -75,17 +76,23 @@ function package:_init () SILE.scratch.converters = {} end extendCommand("include", function (options, content, original) - local result = self:checkConverters(options.src) - if not result then + local source = SU.required(options, "src", "include (converters)") + local result = self:checkConverters(source) + if result then options["src"] = result original(options, content) + else + SU.error("Conversion failure for include '" .. source ..'"') end end) extendCommand("img", function (options, content, original) - local result = self:checkConverters(options.src) - if not result then + local source = SU.required(options, "src", "img (converters)") + local result = self:checkConverters(source) + if result then options["src"] = result original(options, content) + else + SU.error("Conversion failure for image '" .. source ..'"') end end) self:deprecatedExport("register", self.register) @@ -99,6 +106,7 @@ function package:registerCommands () end) self:registerCommand("converters:check", function (options, _) + SU.deprecated("\\converters:check", nil, "0.14.10", "0.16.0") self:checkConverters(options.source) end) @@ -125,7 +133,7 @@ We do this by registering a converter with the \autodoc:command{\converters:regi And now it just magically works: \begin[type=autodoc:codeblock]{raw} -\img[src=hello.gif, width=50px] +\img[src=hello.gif, width=50pt] \end{raw} This will execute the command \code{convert hello.gif hello.jpg} and include the converted \code{hello.jpg} file. diff --git a/packages/counters/init.lua b/packages/counters/init.lua index 7df646d24..9f8b57c5f 100644 --- a/packages/counters/init.lua +++ b/packages/counters/init.lua @@ -3,14 +3,12 @@ local base = require("packages.base") local package = pl.class(base) package._name = "counters" -SILE.formatCounter = function (counter) +SILE.formatCounter = function () SU.deprecated("SILE.formatCounter", "class:formatCounter", "0.13.0", "0.15.0") - return package.formatCounter(nil, counter) end -SILE.formatMultilevelCounter = function (counter, options) +SILE.formatMultilevelCounter = function () SU.deprecated("SILE.formatMultilevelCounter", "class:formatMultilevelCounter", "0.13.0", "0.15.0") - return package.formatMultilevelCounter(nil, counter, options) end local function getCounter (_, id) diff --git a/packages/cropmarks/init.lua b/packages/cropmarks/init.lua index f206a310d..a3c5af14f 100644 --- a/packages/cropmarks/init.lua +++ b/packages/cropmarks/init.lua @@ -64,13 +64,14 @@ end function package:registerCommands () self:registerCommand("crop:header", function (_, _) - local info = SILE.masterFilename .. " - " .. self.class:date("%x %X") .. " - " .. outcounter + local info = SILE.input.filenames[1] .. " - " .. self.class:date("%x %X") .. " - " .. outcounter SILE.typesetter:typeset(info) end) self:registerCommand("crop:setup", function (options, _) local papersize = SU.required(options, "papersize", "setting up crop marks") - local size = SILE.papersize(papersize) + local landscape = SU.boolean(options.landscape, self.class.options.landscape) + local size = SILE.papersize(papersize, landscape) local oldsize = SILE.documentState.paperSize SILE.documentState.paperSize = size local offsetx = ( SILE.documentState.paperSize[1] - oldsize[1] ) /2 diff --git a/packages/dropcaps/init.lua b/packages/dropcaps/init.lua index fbd1aa8b8..764de53df 100644 --- a/packages/dropcaps/init.lua +++ b/packages/dropcaps/init.lua @@ -92,13 +92,13 @@ To tweak the position of the dropcap, measurements may be passed to the \autodoc Other options passed to \autodoc:command{\dropcap} will be passed through to \autodoc:command{\font} when drawing the initial letter(s). This may be useful for passing OpenType options or other font preferences. -\begin{note} +\begin{autodoc:note} One caveat is that the size of the initials is calculated using the default linespacing mechanism. If you are using an alternative method from the \autodoc:package{linespacing} package, you might see strange results. Set the \autodoc:setting{document.baselineskip} to approximate your effective leading value for best results. If that doesn't work set the size manually. Using \code{SILE.setCommandDefaults()} can be helpful for so you don't have to set the size every time. -\end{note} +\end{autodoc:note} \end{document} ]] diff --git a/packages/features/init.lua b/packages/features/init.lua index 6e53f2a2c..b4903b726 100644 --- a/packages/features/init.lua +++ b/packages/features/init.lua @@ -218,11 +218,11 @@ end package.documentation = [[ \begin{document} -As mentioned in Chapter 3, SILE automatically applies ligatures defined by the fonts that you use. +SILE automatically applies ligatures defined by the fonts that you use. These ligatures are defined by tables of \em{features} within the font file. As well as ligatures (multiple glyphs displayed as a single glyph), the features tables also declare other glyph substitutions. -The \autodoc:package{features} package provides an interface to selecting the features that you want SILE to apply to a font. +The standard \autodoc:command{\font} command provides an interface to selecting the features that you want SILE to apply to a font. The features available will be specific to the font file; some fonts come with documentation explaining their supported features. Discussion of OpenType features is beyond the scope of this manual. @@ -233,7 +233,8 @@ These features can be turned on and off by passing “raw” feature names to th \end{raw} However, this is unwieldy and requires memorizing the feature codes. -\autodoc:package{features} provides two commands, \autodoc:command{\add-font-feature} and \autodoc:command{\remove-font-feature}, which make it easier to access OpenType features. + +The \autodoc:package{features} package provides two commands, \autodoc:command{\add-font-feature} and \autodoc:command{\remove-font-feature}, which make it easier to access OpenType features. The interface is patterned on the TeX package \code{fontspec}; for full documentation of the OpenType features supported, see the documentation for that package.\footnote{\code{http://texdoc.net/texmf-dist/doc/latex/fontspec/fontspec.pdf}} Here is how you would turn on discretionary and historic ligatures with the \autodoc:package{features} package: diff --git a/packages/folio/init.lua b/packages/folio/init.lua index 8d3edc88f..85e4c00e7 100644 --- a/packages/folio/init.lua +++ b/packages/folio/init.lua @@ -78,7 +78,7 @@ end package.documentation= [[ \begin{document} -The \autodoc:package{folio} package (which is automatically loaded by the \code{plain} class, and therefore by nearly every SILE class) controls the output of folios—the old-time typesetter word for page numbers. +The \autodoc:package{folio} package (which is automatically loaded by the \autodoc:class{plain} class, and therefore by nearly every SILE class) controls the output of folios—the old-time typesetter word for page numbers. It provides four commands to users: diff --git a/packages/footnotes/init.lua b/packages/footnotes/init.lua index 931d694b7..4adb97888 100644 --- a/packages/footnotes/init.lua +++ b/packages/footnotes/init.lua @@ -73,7 +73,7 @@ function package:registerCommands () -- Apply the font before boxing, so relative baselineskip applies #1027 local material SILE.call("footnote:font", {}, function () - material = SILE.call("vbox", {}, function () + material = SILE.call("vbox", {}, function () SILE.call("footnote:atstart", options) SILE.call("footnote:counter", options) SILE.process(content) @@ -108,10 +108,14 @@ end package.documentation = [[ \begin{document} -We’ve seen that the \code{book} class allows you to add footnotes to text with the \autodoc:command{\footnote} command. -This functionality exists in the class because the class loads the \autodoc:package{footnotes} package. -The \code{book} class loads the \autodoc:package{insertions} package and tells it which frame should receive the footnotes that are typeset. -Other commands provided by the \autodoc:package{footnotes} package take care of formatting the footnotes. +The \autodoc:package{footnotes} package allows you to add footnotes to text with the \autodoc:command{\footnote} command. +Other commands provided by the package, not described here, take care of formatting the footnotes. + +Usually, a document class is responsible for automatically loading this package. +Minimally, upon initialization, it needs a frame identifier for the the footnotes, and one or more frame(s) which will be reduced as the footnotes take place. +By default, it uses, respectively, the \code{footnotes} and \code{content} frames, which are assumed to be present in the default standard layout. + +For the record, it internally relies on the \autodoc:package{insertions} package and tells it which frame should receive the footnotes that are typeset. \end{document} ]] diff --git a/packages/frametricks/init.lua b/packages/frametricks/init.lua index 1bc091108..6b1ae6754 100644 --- a/packages/frametricks/init.lua +++ b/packages/frametricks/init.lua @@ -41,7 +41,7 @@ end local makecolumns = function (options) local cFrame = SILE.typesetter.frame - local cols = options.columns or 2 + local cols = options.columns local gutterWidth = options.gutter or "3%pw" local right = cFrame:right() local origId = cFrame.id @@ -174,7 +174,31 @@ function package:registerCommands () end, "Breaks the current frame in two vertically at the current location or at a point below the current location") self:registerCommand("makecolumns", function (options, _) + -- Set a default value for column count + options.columns = options.columns or 2 + local current_frame = SILE.typesetter.frame + local original_constraints = {} + -- Collect existing constraints that may need updating after makecolumns() changes them + for frameid in pairs(SILE.frames) do + if frameid ~= current_frame.id then + local frame = SILE.getFrame(frameid) + for method in pairs(frame.constraints) do + -- TODO: Remove the assumption about direction when makecolumns() takes into account frame advance direction + if method == "right" then + if frame[method](frame) == current_frame[method](current_frame) then + table.insert(original_constraints, { frame = frame, method = method }) + end + end + end + end + end makecolumns(options) + for _, info in ipairs(original_constraints) do + local frame, method = info.frame, info.method + local final_column_id = ("%s_col%d"):format(current_frame.id, options.columns-1) + local final_comumn_frame = SILE.getFrame(final_column_id) + frame:constrain(method, final_comumn_frame[method](final_comumn_frame)) + end end, "Split the current frame into multiple columns") self:registerCommand("breakframehorizontal", function (options, _) @@ -233,7 +257,6 @@ end package.documentation = [[ \begin{document} -As we mentioned in the first chapter, SILE uses frames as an indication of where to put text onto the page. The \autodoc:package{frametricks} package assists package authors by providing a number of commands to manipulate frames. The most immediately useful is \autodoc:command{\showframe}. diff --git a/packages/gutenberg/init.lua b/packages/gutenberg/init.lua index 4e787988d..a48944485 100644 --- a/packages/gutenberg/init.lua +++ b/packages/gutenberg/init.lua @@ -32,6 +32,7 @@ package.documentation = [[ \begin{document} Johann Gutenberg’s 42-line Bible is considered a masterpiece of early printing in part due to the quality of justification of every line. To achieve perfect justification color, Gutenberg used a number of ligatures, abbreviations, substitutions, and so on. + As an experiment in extending SILE’s justification engine, the \autodoc:package{gutenberg} package allows SILE to choose between a number of different options for a particular piece of text, depending on what would improve the line fitting. For instance, issuing the command \autodoc:command{\alternative{\{and\}\{&\}}} would insert either the text \autodoc:example{and} or an ampersand, depending on what best fits the current line. diff --git a/packages/image/init.lua b/packages/image/init.lua index 16db9ce8e..9d4285dfe 100644 --- a/packages/image/init.lua +++ b/packages/image/init.lua @@ -39,17 +39,15 @@ end package.documentation = [[ \begin{document} -As well as processing text, SILE can also include images. - Loading the \autodoc:package{image} package gives you the \autodoc:command{\img} command, fashioned after the HTML equivalent. It takes the following parameters: \autodoc:parameter{src=} must be the path to an image file; you may also give \autodoc:parameter{height} and/or \autodoc:parameter{width} parameters to specify the output size of the image on the paper. If the size parameters are not given, then the image will be output at its “natural” size, honoring its resolution if available. The command also supports a \autodoc:parameter{page=} option, to specify the selected page in formats supporting several pages (such as PDF). -\begin{note} +\begin{autodoc:note} With the libtexpdf backend (the default), the images can be in JPEG, PNG, EPS, or PDF formats. -\end{note} +\end{autodoc:note} Here is a 200x243 pixel image output with \autodoc:command{\img[src=documentation/gutenberg.png]}. The image has a claimed resolution of 100 pixels per inch, so ends up being two inches (144pt) wide on the page:\par diff --git a/packages/infonode/init.lua b/packages/infonode/init.lua index c53fce99f..40f65ff5e 100644 --- a/packages/infonode/init.lua +++ b/packages/infonode/init.lua @@ -43,9 +43,8 @@ function package:_init () SILE.scratch.info = { thispage = {} } end self.class:registerHook("newpage", newPageInfo) - self:deprecatedExport("newPageInfo", function (class) + self:deprecatedExport("newPageInfo", function () SU.deprecated("class:newPageInfo", nil, "0.13.0", "0.15.0", _deprecate) - return class:newPageInfo() end) end diff --git a/packages/leaders/init.lua b/packages/leaders/init.lua index 9f41d6630..7fe989982 100644 --- a/packages/leaders/init.lua +++ b/packages/leaders/init.lua @@ -75,7 +75,7 @@ function leader:outputYourself (typesetter, line) end end -- Return to our start (saved) position and move to the full leaders width. - -- (So we are sure to safely get the correct width, whathever we did above + -- (So we are sure to safely get the correct width, whatever we did above -- with the remainder space and the leader repetitions). typesetter.frame.state.cursorX = ox typesetter.frame.state.cursorY = oy diff --git a/packages/lists/init.lua b/packages/lists/init.lua index a7c862bf0..29db687a2 100644 --- a/packages/lists/init.lua +++ b/packages/lists/init.lua @@ -72,8 +72,8 @@ local trim = function (str) end local enforceListType = function (cmd) - if cmd ~= "enumerate" and cmd ~= "itemize" then - SU.error("Only 'enumerate', 'itemize' or 'item' are accepted in lists, found '"..cmd.."'") + if cmd ~= "enumerate" and cmd ~= "itemize" and cmd ~= "BulletedList" and cmd ~= "OrderedList" then + SU.error("Only items or lists are allowed as content in lists, found '"..cmd.."'") end end @@ -102,7 +102,7 @@ function package:doItem (options, content) local stepback if enumStyle.display then - -- The positionning is quite tentative... LaTeX would right justify the + -- The positioning is quite tentative... LaTeX would right justify the -- number (at least for roman numerals), i.e. -- i. Text -- ii. Text @@ -165,7 +165,7 @@ function package.doNestedList (_, listType, options, content) local counter = options.start and (SU.cast("integer", options.start) - 1) or 0 for i = 1, #content do if type(content[i]) == "table" then - if content[i].command == "item" then + if content[i].command == "item" or content[i].command == "ListItem" then counter = counter + 1 -- Enrich the node with internal properties content[i]._lists_ = { diff --git a/packages/lorem/init.lua b/packages/lorem/init.lua index 264bd6631..f789fc9d1 100644 --- a/packages/lorem/init.lua +++ b/packages/lorem/init.lua @@ -75,7 +75,8 @@ function package:registerCommands () self:registerCommand("lorem", function (options, _) local words = tonumber(options.words) or 50 - local counter = options.counter or false + local counter = SU.boolean(options.counter, false) + local times = math.floor(words/nwords) words = words - times*nwords local pos = 0 @@ -84,6 +85,7 @@ function package:registerCommands () end local text = string.rep(lorem, times) .. lorem:sub(1, pos) if counter then + SU.deprecated("\\lorem with counter", nil, "0.14.10", "0.16.0") local c = 0 text = string.gsub(text, "(%s+)", function (_) c = c + 1 diff --git a/packages/masters/init.lua b/packages/masters/init.lua index 664c7b3fa..e00806776 100644 --- a/packages/masters/init.lua +++ b/packages/masters/init.lua @@ -37,9 +37,8 @@ local function doswitch (frames) end end -local function switchMasterOnePage (class, id) +local function switchMasterOnePage (_, id) if not id then - id = class SU.deprecated("class.switchMasterOnePage", "class:switchMasterOnePage", "0.13.0", "0.15.0") end if not SILE.scratch.masters[id] then @@ -53,7 +52,6 @@ end local function switchMaster (class, id) if not id then - id, class = class, SILE.documentState.documentClass SU.deprecated("class.switchMaster", "class:switchMaster", "0.13.0", "0.15.0") end _currentMaster = id diff --git a/packages/math/base-elements.lua b/packages/math/base-elements.lua index 7736a1d49..f8332ec8a 100644 --- a/packages/math/base-elements.lua +++ b/packages/math/base-elements.lua @@ -225,7 +225,7 @@ end -- Typesetting of mbox evolves four steps: -- 1. Determine the mode for each mbox according to their parent. -- 2. Shape the mbox hierarchy from leaf to top. Get the shape and relative position. --- 3. Convert mbox into _nnode's to put in SILE's typesetting framwork +-- 3. Convert mbox into _nnode's to put in SILE's typesetting framework elements.mbox = pl.class(nodefactory.hbox) elements.mbox._type = "Mbox" @@ -254,15 +254,15 @@ function elements.mbox:_init () end function elements.mbox.styleChildren (_) - SU.error("styleChildren is a virtual function that need to be overriden by its child classes") + SU.error("styleChildren is a virtual function that need to be overridden by its child classes") end function elements.mbox.shape (_, _, _) - SU.error("shape is a virtual function that need to be overriden by its child classes") + SU.error("shape is a virtual function that need to be overridden by its child classes") end function elements.mbox.output (_, _, _, _) - SU.error("output is a virtual function that need to be overriden by its child classes") + SU.error("output is a virtual function that need to be overridden by its child classes") end function elements.mbox:getMathMetrics () @@ -860,7 +860,7 @@ function elements.text:shape () .vertGlyphConstructions[glyphs[1].gid] if constructions then local displayVariants = constructions.mathGlyphVariantRecord - -- We select the biggest variant. TODO: we shoud probably select the + -- We select the biggest variant. TODO: we should probably select the -- first variant that is higher than displayOperatorMinHeight. local biggest local m = 0 @@ -945,7 +945,7 @@ function elements.text:stretchyReshape (depth, height) local closest local closestI local m = requiredAdvance - (self.depth+self.height):tonumber() * upem/sz - SU.debug("math", "strech: m =", m) + SU.debug("math", "stretch: m =", m) for i,v in ipairs(variants) do local diff = math.abs(v.advanceMeasurement - requiredAdvance) SU.debug("math", "stretch: diff =", diff) @@ -1105,7 +1105,7 @@ function elements.mtr.shape (_) end -- done by parent table function elements.mtr.output (_) end elements.table = pl.class(elements.mbox) -elements.table._type = "table" -- TODO why case diference? +elements.table._type = "table" -- TODO why case difference? function elements.table:_init (children, options) elements.mbox._init(self) diff --git a/packages/math/init.lua b/packages/math/init.lua index 6da8fcc20..8e7229fa2 100644 --- a/packages/math/init.lua +++ b/packages/math/init.lua @@ -84,17 +84,13 @@ end package.documentation = [[ \begin{document} \use[module=packages.math] - \set[parameter=math.font.family, value=Libertinus Math] \set[parameter=math.font.size, value=11] - % Default verbatim font (Hack) is missing a few math symbols \use[module=packages.font-fallback] \font:add-fallback[family=Symbola] - \define[command=paragraph]{\smallskip\em{\process.}\novbreak\par} - -This package provides typesetting of formulas directly in a SILE document. +The \autodoc:package{math} package provides typesetting of formulas directly in a SILE document. \autodoc:note{Mathematical typesetting in SILE is still in its infancy. As such, it lacks some features and may contain bugs. diff --git a/packages/pandoc/init.lua b/packages/pandoc/init.lua index c3fc868d4..24356f1bc 100644 --- a/packages/pandoc/init.lua +++ b/packages/pandoc/init.lua @@ -49,6 +49,7 @@ function package:_init () base._init(self) self:loadPackage("footnotes") self:loadPackage("image") + self:loadPackage("lists") self:loadPackage("pdf") self:loadPackage("raiselower") self:loadPackage("rules") @@ -67,15 +68,11 @@ function package:registerCommands () SILE.typesetter:leaveHmode() end) - self:registerCommand("BulletList", function (_, content) - -- luacheck: ignore pandocListType - local pandocListType = "bullet" - SILE.settings:temporarily(function () - SILE.settings:set("document.rskip","10pt") - SILE.settings:set("document.lskip","20pt") - SILE.process(content) + self:registerCommand("BulletList", function (options, content) + local wrapper, args = handlePandocArgs(options) + wrapper(function () + SILE.call("itemize", args, content) end) - SILE.typesetter:leaveHmode() end) self:registerCommand("CodeBlock", function (options, content) @@ -128,14 +125,11 @@ function package:registerCommands () SILE.typesetter:leaveHmode() end) - self:registerCommand("OrderedList", function (_, content) - -- TODO: handle listAttributes - SILE.settings:temporarily(function () - SILE.settings:set("document.rskip","10pt") - SILE.settings:set("document.lskip","20pt") - SILE.process(content) + self:registerCommand("OrderedList", function (options, content) + local wrapper, args = handlePandocArgs(options) + wrapper(function () + SILE.call("enumerate", args, content) end) - SILE.typesetter:leaveHmode() end) self:registerCommand("Para", function (_, content) @@ -277,20 +271,11 @@ function package:registerCommands () -- Non native types - self:registerCommand("ListItem", function (_, content) - SILE.call("smallskip") - SILE.call("glue", { width = "-1em"}) - SILE.call("rebox", { width = "1em" }, function () - -- Note: Relies on Lua scope shadowing to find immediate parent list type - -- luacheck: ignore pandocListType - if pandocListType == "bullet" then - SILE.typesetter:typeset("•") - else - SILE.typesetter:typeset("-") - end + self:registerCommand("ListItem", function (options, content) + local wrapper, args = handlePandocArgs(options) + wrapper(function () + SILE.call("item", args, content) end) - SILE.process(content) - SILE.call("smallskip") end) self:registerCommand("ListItemTerm", function (_, content) diff --git a/packages/pullquote/init.lua b/packages/pullquote/init.lua index 345ed5dd5..251b1c0ee 100644 --- a/packages/pullquote/init.lua +++ b/packages/pullquote/init.lua @@ -78,8 +78,7 @@ end package.documentation = [[ \begin{document} -The \autodoc:environment{pullquote} environment formats longer quotations in an indented blockquote block with decorative quotation marks in the margins. - +The \autodoc:package{pullquote} package formats longer quotations in an indented blockquote block with decorative quotation marks in the margins. Here is some text set in a \autodoc:environment{pullquote} environment: \begin[author=Anatole France]{pullquote}% diff --git a/packages/raiselower/init.lua b/packages/raiselower/init.lua index 63b3183de..50c92cb58 100644 --- a/packages/raiselower/init.lua +++ b/packages/raiselower/init.lua @@ -37,7 +37,6 @@ end package.documentation = [[ \begin{document} If you don’t want your images, rules, or text to be placed along the baseline, you can use the \autodoc:package{raiselower} package to move them up and down. -(The \autodoc:package{footnote} package uses this to superscript the footnote reference numbers.) It provides two simple commands, \autodoc:command{\raise} and \autodoc:command{\lower}, which both take a \autodoc:parameter{height=} parameter. They will respectively raise or lower their argument by the given height. diff --git a/packages/ruby/init.lua b/packages/ruby/init.lua index ec8af8edf..0da12ec66 100644 --- a/packages/ruby/init.lua +++ b/packages/ruby/init.lua @@ -29,7 +29,7 @@ end function package:_init () base._init(self) - -- Japaneese language support defines units which are useful here + -- Japanese language support defines units which are useful here self:loadPackage("font-fallback") SILE.call("font:add-fallback", { family = "Noto Sans CJK JP" }) SILE.languageSupport.loadLanguage("ja") diff --git a/packages/rules/init.lua b/packages/rules/init.lua index 22f68851b..b6efbc3a7 100644 --- a/packages/rules/init.lua +++ b/packages/rules/init.lua @@ -107,38 +107,15 @@ function package:registerCommands () local thickness = SU.cast("measurement", options.thickness or "0.2pt") local raise = SU.cast("measurement", options.raise or "0.5em") - -- BEGIN DEPRECATION COMPATIBILITY if options.height then SU.deprecated("\\fullrule[…, height=…]", "\\fullrule[…, thickness=…]", "0.13.1", "0.15.0") - thickness = SU.cast("measurement", options.height) end if not SILE.typesetter:vmode() then SU.deprecated("\\fullrule in horizontal mode", "\\hrule or \\hrulefill", "0.13.1", "0.15.0") - if options.width then - SU.deprecated("\\fullrule with width", "\\hrule and \\raise", "0.13.1", "0.15.0") - SILE.call("raise", { height = raise }, function () - SILE.call("hrule", { - height = thickness, - width = options.width - }) - end) - else - -- This was very broken anyway, as it was overflowing the line. - -- At least we try better... - SILE.call("hrulefill", { raise = raise, thickness = thickness }) - end - return end if options.width then SU.deprecated("\\fullrule with width", "\\hrule and \\raise", "0.13.1 ", "0.15.0") - SILE.call("raise", { height = raise }, function () - SILE.call("hrule", { - height = thickness, - width = options.width - }) - end) end - -- END DEPRECATION COMPATIBILITY SILE.typesetter:leaveHmode() SILE.call("noindent") diff --git a/packages/simpletable/init.lua b/packages/simpletable/init.lua index b5d4ba577..f762ca7ac 100644 --- a/packages/simpletable/init.lua +++ b/packages/simpletable/init.lua @@ -24,7 +24,7 @@ function package:_init (options) trTag = SU.required(options, "trTag", "setting up table class") tdTag = SU.required(options, "tdTag", "setting up table class") - -- This is a post init calback instead of the usual early command registration + -- This is a post init callback instead of the usual early command registration -- method using our package loader because we don't know what commands to register -- until we've been instantiated. self.class:registerPostinit(function (_) diff --git a/packages/specimen/init.lua b/packages/specimen/init.lua index df8ac4ede..83f631ac6 100644 --- a/packages/specimen/init.lua +++ b/packages/specimen/init.lua @@ -49,6 +49,7 @@ function package:registerCommands () local w = 0 for j= 1,#tokens do w = w + tokens[j].width end local ratio = width.length / w + SILE.call("noindent") SILE.call("font", {size = fontOptions.size * ratio}, function() SILE.process({line.string}) SILE.call("par") @@ -64,7 +65,7 @@ package.documentation = [[ \use[module=packages.specimen] SILE has found itself becoming well used by type designers, who often want to create specimen documents to show off their new fonts. This package provides a few commands to help create test documents. -(The \code{fontproof} class, available from the package manager, contains many more tools for creating specimens.) +(The \autodoc:class{fontproof} class, available from the package manager, contains many more tools for creating specimens.) The \autodoc:command{\repertoire} command prints out every glyph in the font, in a simple table. The \autodoc:command{\pangrams} command prints out a few pangrams for the Latin script. Finally, \autodoc:command{\set-to-width[width=]{}} will process each line of content, changing the font size so that the output is a constant width. diff --git a/packages/tableofcontents/init.lua b/packages/tableofcontents/init.lua index 0fb8bd667..b4036660d 100644 --- a/packages/tableofcontents/init.lua +++ b/packages/tableofcontents/init.lua @@ -7,6 +7,8 @@ if not SILE.scratch._tableofcontents then SILE.scratch._tableofcontents = {} end +local toc_used = false + function package:moveTocNodes () local node = SILE.scratch.info.thispage.toc if node then @@ -19,13 +21,13 @@ end function package.writeToc (_) local tocdata = pl.pretty.write(SILE.scratch.tableofcontents) - local tocfile, err = io.open(SILE.masterFilename .. '.toc', "w") + local tocfile, err = io.open(pl.path.splitext(SILE.input.filenames[1]) .. '.toc', "w") if not tocfile then return SU.error(err) end tocfile:write("return " .. tocdata) tocfile:close() - if not pl.tablex.deepcompare(SILE.scratch.tableofcontents, SILE.scratch._tableofcontents) then - io.stderr:write("\n! Warning: table of contents has changed, please rerun SILE to update it.") + if toc_used and not pl.tablex.deepcompare(SILE.scratch.tableofcontents, SILE.scratch._tableofcontents) then + SU.msg("Notice: the table of contents has changed, please rerun SILE to update it.") end end @@ -34,7 +36,7 @@ function package.readToc (_) -- already loaded return SILE.scratch._tableofcontents end - local tocfile, _ = io.open(SILE.masterFilename .. '.toc') + local tocfile, _ = io.open(pl.path.splitext(SILE.input.filenames[1]) .. '.toc') if not tocfile then return false -- No TOC yet end @@ -115,6 +117,7 @@ function package:registerCommands () self:registerCommand("tableofcontents", function (options, _) local depth = SU.cast("integer", options.depth or 3) local linking = SU.boolean(options.linking, true) + toc_used = true local toc = self:readToc() if toc == false then SILE.call("tableofcontents:notocmessage") diff --git a/packages/twoside/init.lua b/packages/twoside/init.lua index 97e4af933..4e81ef16b 100644 --- a/packages/twoside/init.lua +++ b/packages/twoside/init.lua @@ -57,9 +57,8 @@ function package:_init (options) end self:export("oddPage", self.oddPage) self:export("mirrorMaster", mirrorMaster) - self:export("switchPage", function (class) + self:export("switchPage", function () SU.deprecated("class:switchPage", nil, "0.13.0", "0.15.0", _deprecate) - return class:switchPage() end) self.class.oddPageMaster = options.oddPageMaster self.class.evenPageMaster = options.evenPageMaster @@ -134,8 +133,9 @@ end package.documentation = [[ \begin{document} -The \code{book} class described in Chapter 4 sets up left and right mirrored page masters; the \autodoc:package{twoside} package is responsible for swapping between the two left and right frames, running headers, and so on. -Its main function in mirroring master frames does not provide any user-serviceable parts. +A book-like class usually sets up left and right mirrored page masters. +The \autodoc:package{twoside} package is then responsible for swapping between the two left and right frames, running headers, and so on. +As it is normally loaded and initialized by a document class, its main function in mirroring master frames does not provide any user-serviceable parts. It does supply a few user-facing commands for convenience. The \autodoc:command{\open-double-page} ejects whatever page is currently being processed, then checks if it landed on an even page. diff --git a/packages/unichar/init.lua b/packages/unichar/init.lua index b9640f415..7577d10e9 100644 --- a/packages/unichar/init.lua +++ b/packages/unichar/init.lua @@ -29,8 +29,8 @@ package.documentation = [[ SILE is Unicode compatible, and expects its input files to be in the UTF-8 encoding. (The actual range of Unicode characters supported will depend on the supported ranges of the fonts that SILE is using to typeset.) Some Unicode characters are hard to locate on a standard keyboard, and so are difficult to enter into SILE documents. -The \autodoc:package{unichar} package helps with this problem by providing a command to enter Unicode codepoints. -After loading \autodoc:package{unichar}, the \autodoc:command{\unichar} command becomes available: + +The \autodoc:package{unichar} package helps with this problem by providing the \autodoc:command{\unichar} command to enter Unicode codepoints. \begin[type=autodoc:codeblock]{raw} \unichar{U+263A} diff --git a/rust-toolchain b/rust-toolchain new file mode 100644 index 000000000..2bf5ad044 --- /dev/null +++ b/rust-toolchain @@ -0,0 +1 @@ +stable diff --git a/sile-dev-1.rockspec b/sile-dev-1.rockspec deleted file mode 100644 index bd9ba0058..000000000 --- a/sile-dev-1.rockspec +++ /dev/null @@ -1,36 +0,0 @@ -package = "sile" -description = { - summary = "Simon’s Improved Layout Engine", - license = "MIT" -} -version = "dev-1" -source = { - url = "..." -} -dependencies = { - "lua >= 5.1", - "bit32", -- only required on Lua < 5.2, versions vary between Rock and VM provided - "cassowary == 2.3.2-1", - "cldr == 0.3.0-0", - "compat53 == 0.8-1", -- only required on Lua < 5.3 - "cosmo == 16.06.04-1", - "fluent == 0.2.0-0", - "linenoise == 0.9-1", - "loadkit == 1.1.0-1", - "lpeg == 1.0.2-1", - "lua-zlib == 1.2-2", - "lua_cliargs == 3.0-2", - "luaepnf == 0.3-2", - "luaexpat == 1.5.1-1", - "luafilesystem == 1.8.0-1", - "luarepl == 0.10-1", - "luasec == 1.3.1-1", - "luasocket == 3.1.0-1", - "luautf8 == 0.1.5-1", - "penlight == 1.13.1-1", - "vstruct == 2.1.1-1" -} -build = { - type = "builtin", - modules = {} -} diff --git a/sile.1.in b/sile-lua.1.in similarity index 52% rename from sile.1.in rename to sile-lua.1.in index 3fdc46541..b5375705a 100644 --- a/sile.1.in +++ b/sile-lua.1.in @@ -1,94 +1,118 @@ .TH @TRANSFORMED_PACKAGE_NAME@ 1 "@MAN_DATE@" "version v@VERSION@" .SH NAME -@TRANSFORMED_PACKAGE_NAME@ \- Simon's Improved Layout Engine +@TRANSFORMED_PACKAGE_NAME@ \- Simon’s Improved Layout Engine .SH SYNOPSIS -.B @TRANSFORMED_PACKAGE_NAME@ [ -.I options -.B ] [ -.I filename.sil -.B | -.I filename.xml -.B ] +.B @TRANSFORMED_PACKAGE_NAME@ +.B [\fIoptions\fR] +.B [\fIINPUT\fR] .SH DESCRIPTION -The SILE typesetter reads a single input file, by default in either SIL or XML format, and processes it to generate a single output file, by default in PDF format. -The output will be written to the same name as the input file with the extension changed to .pdf unless the \fB\-\-output\fR flag is used. -Additional input or output formats can be handled by loading a module with \fB\-\-use\fR to add support for them first. +The SILE typesetter reads an input file(s), by default in either SIL or XML format, and processes them to generate an output file, by default in PDF format. +The output will be written to a file with the same name as the first input file with the extension changed to .pdf unless the \fB\-\-output\fR argument is used. +Additional input or output formats can be handled by loading a module with the \fB\-\-use\fR argument to add support for them first. .SH OPTIONS -.B @TRANSFORMED_PACKAGE_NAME@ accepts the following feature flags: -.TP -.BR \-t ", " \-\-traceback -Display detailed location trace on errors and warnings. -.TP -.BR \-h ", " \-\-help -Print help message and exit. -.TP -.BR \-v ", " \-\-version -Print version information and exit. -.TP -.B @TRANSFORMED_PACKAGE_NAME@ accepts the following options with values: .TP .BR \-b ", " \-\-backend= \fIvalue\fR -Choose an alternative output backend. -The default backend for producing PDF files is \fIlibtexpdf\fR. -Other available backends include \fIcairo\fR, \fIdebug\fR, \fItext\fR, and \fIdummy\fR. +Specify the output backend. +.IP +The default is \fIlibtexpdf\fR and suitible for most PDF output. +Alternatives supported out of the box include \fItext\fR, \fIdebug\fR, \fIdummy\fR, \fIcairo\fR, and \fIpodofo\fR. +Other outputters may be enabled via \fI--use\fR. .TP .BR \-c ", " \-\-class= \fIvalue\fR -Set the document class. -The default for documents that do not specify is \fIplain\fR. -Can be used to either change the default class or to override the class specified in a document. -Other default classes include \fIbase\fR, \fIbible\fR, \fIbook\fR, \fIdiglot\fR, \fIdocbook\fR, \fIdocbook.sil \fIjbook\fR, \fIjplain\fR, \fIletter\fR, \fImarkdown\fR, \fIpecha\fR, \fItbook\fR, \fItplain\fR, and \fItriglot\fR. +Override the default or specified document class. +.IP +The default class for documents that do not specify one in the root tag is \fIplain\fR. +This can be used to either change the default class or to override the class actually specified in a document. +Other bundled classes include \fIbase\fR, \fIbible\fR, \fIbook\fR, \fIdiglot\fR, \fIdocbook\fR, \fIdocbook\fR, \fIjbook\fR, \fIjplain\fR, \fIletter\fR, \fIpecha\fR, \fItbook\fR, \fItplain\fR, and \fItriglot\fR. +Others will be loaded dynamically from the module path. .TP .BR \-d ", " \-\-debug= \fIvalue\fR[,\fIvalue\fR] -Debug SILE's operation. +Show debug information for tagged aspects of SILE’s operation. +.IP Multiple debug flags may be given as a comma separated list. While packages may define their own debug flags, the most commonly used ones are \fItypesetter\fR, \fIpagebuilder\fR, \fIvboxes\fR, \fIbreak\fR, \fIframes\fR, \fIprofile\fR, and \fIversions\fR. May be specified more than once. .TP .BR \-e ", " \-\-evaluate= \fIvalue\fR Evaluate Lua expression before processing input. +.IP May be specified more than once. .TP .BR \-E ", " \-\-evaluate-after= \fIvalue\fR Evaluate Lua expression after processing input. +.IP May be specified more than once. .TP .BR \-f ", " \-\-fontmanager= \fIvalue\fR -Choose an alternative font manager. +Specify which font manager to use. +.IP The font manager is responsible for discovering the locations on font files on the system given a font name. The default font manager is \fIfontconfig\fR on non-macOS systems and \fImacfonts\fR on macOS. .TP .BR \-m ", " \-\-makedeps \fIfile\fR Generate a list of dependencies in Makefile format. +.IP +This tracks all the files (input files, Lua libraries, fonts, images, etc.) use during the typesetting process. +After completion, the list is written to FILE in the format of a dependency list for a target in a Makefile. +This can be used later to determine if a PDF needs re-rendering based on whether any inputs have changed. .TP .BR \-o ", " \-\-output= \fIfile\fR Explicitly set the output file name. +.IP +By default the basename of the first input file will be used as the output filename. +An extension will be chosen based on the output backend, typically .pdf. +With this option any arbitrary name and path can be given. +Additionally \fI-\fR can be used to write the output to STDOUT. .TP .BR \-O ", " \-\-options= \fIparameter=value\fR[,\fIparameter=value\fR] -Set document class options. +Set or override document class options. +.IP Can be used to change default options or override the ones specified in a document. -For example setting \fB\-\-options papersize=letter\fR would override both the default \fIpapersize\fR of A4 and any specific one set in the document's options. +For example setting \fB\-\-options papersize=letter\fR would override both the default \fIpapersize\fR of A4 and any specific one set in the document’s options. May be specified more than once. .TP .BR \-I ", " \-\-include= \fIfilename\fR -Deprecated, will be removed. -Please use \-\-use, \-\-preamble, or \-\-postamble. +Deprecated, use \-\-use, \-\-preamble, \-\-postamble, or multiple input files. .TP .BR \-p ", " \-\-preamble= \fIfilename\fR -Include an SIL, XML, or other content resource before the input document. +Include the contents of a SIL, XML, or other resource file before the input document content. +.IP The value should be a full filename with a path relative to PWD or an absolute path. May be specified more than once. .TP .BR \-P ", " \-\-postamble= \fIfilename\fR -Include an SIL, XML, or other content resource after the input document. +Include the contents of a SIL, XML, or other resource file after the input document content. +.IP The value should be a full filename with a path relative to PWD or an absolute path. May be specified more than once. .TP .BR \-u ", " \-\-use= \fImodule\fR [[\fIparameter=value[,parameter=value]]]\fR Load and initialize a class, inputter, shaper, or other module before processing the main input. -The value should be a loadable module name (with no extension, using \fI'.'\fR as a path separator) and will be loaded using SILE's module search path. +.IP +The value should be a loadable module name (with no extension, using \fI'.'\fR as a path separator) and will be loaded using SILE’s module search path. Options may be passed to the module by enclosing \fIkey=value\fR pairs in square brackets following the module name. This is particularly useful when the input document is not SIL or a natively recognized XML scheme and SILE needs to be taught new tricks before even trying to read the input file. If the module is a document class, it will replace \fIplain\fR as the default class for processing documents that do not specifically identify one to use. Because this executes before loading the document, it may even add an input parser or modify an existing one to support new file formats. Package modules will be added to the preamble to be loaded after the class is initialized. May be specified more than once. +.TP +.BR \-q ", " \-\-quiet +Suppress warnings and informational messages during processing. +.TP +.BR \-t ", " \-\-traceback +Display detailed location trace on errors and warnings. +.TP +.BR [INPUT] +Input document filename(s), by default in SIL, XML, or Lua formats. +.IP +One or more input files from which to process content. +The first listed file is considered the master document, others are procced in sequence. +Other inputter formats may be enabled via \fI--use\fR. +Use \fI-\fR to read a content stream from STDIN. +.TP +.BR \-h ", " \-\-help +Print help message and exit. +.TP +.BR \-v ", " \-\-version +Print version. diff --git a/sile.in b/sile.in index 94695fef6..54ad51a60 100755 --- a/sile.in +++ b/sile.in @@ -1,84 +1,26 @@ #!@LUA@ --- globals used only in core/packagemanager, soon to be deprecated -SYSTEM_SILE_PATH = "@SILE_PATH@" -SHARED_LIB_EXT = "@SHARED_LIB_EXT@" - -local executable = debug.getinfo(1, "S").source -local luaversion = _VERSION:match("%d+%.%d+") - --- Normalize possibly dirty Lua path formatting shortcut: /./ → / --- Even leafo/gh-actions-luarocks takes this shortcut which inhibits duplicate cleanup -package.path = package.path:gsub("/%./", "/") -package.cpath = package.cpath:gsub("/%./", "/") - -local function prepend_and_dedup (segment, path) - local escaped = segment:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1') -- copied from pl.utils.escape() which we can't load yet - local striped = path:gsub(("^%s"):format(escaped), ""):gsub((";%s"):format(escaped), "") - return ("%s;%s"):format(segment, striped) -end - -local function prependPath (path) - package.path = prepend_and_dedup(path .. "/?/init.lua", package.path) - package.path = prepend_and_dedup(path .. "/?.lua", package.path) -end - -local function prependCPath (path) - package.cpath = prepend_and_dedup(path .. "/?.@SHARED_LIB_EXT@", package.cpath) -end - -local function extendPaths (path, ours) - prependCPath(path) - prependPath(path) - if ours then - prependPath(path .. "/lua-libraries") - if "@SYSTEM_LUAROCKS_FALSE@" == "" then -- see ./configure --with[out]-system-luarocks - prependCPath(path .. "/lua_modules/lib/lua/" .. luaversion) - prependPath(path .. "/lua_modules/share/lua/" .. luaversion) - end - else - prependCPath(path .. "/sile") - prependPath(path .. "/sile") - end -end - --- Facilitate loading SILE classes & packages from system LuaRocks --- Also weed out CWD relative paths, we add them in a different order later -local luapath = {} -local extpath = {} -for path in package.path:gmatch("[^;]+") do - table.insert(extpath, tostring(path:gsub("%?", "sile/?"))) - table.insert(luapath, path) +-- At this point at run time we may or may not have anything useful in package.path, +-- so require() isn't something we can rely on. +local status +for path in string.gmatch("@SILE_PATH@", "[^;]+") do + status = pcall(dofile, path .. "/core/pathsetup.lua") + if status then break end end -package.path = table.concat(luapath, ";") - -extendPaths("@SILE_PATH@", true) -extendPaths("@SILE_LIB_PATH@", true) - -package.path = table.concat(extpath, ";") .. ";" .. package.path - -local pathvar = os.getenv("SILE_PATH") -if pathvar then - for path in string.gmatch(pathvar, "[^;]+") do - if not path:match("^./") and path:len() >= 1 then - extendPaths(path) - end - end +if not status then + dofile("./core/pathsetup.lua") end -local cwd = executable:gsub("(.*)(/.*)", "%1") -if cwd:match("^@") then -- Consider "ours" for the sake of Nix Flake - extendPaths(".", true) -else - if cwd ~= "./" then extendPaths(cwd) end - extendPaths(".") +-- On FreeBSD, Lua's debug module doesn't give us info above the top of the stack. +-- Since our detection is currently burried in a module it doesn't tell us the executable. +if _G.executablePath == "=[C]" then + _G.executablePath = debug.getinfo(1, "S").source end -- This global is set here and *also* in the core library, since this -- effectively passes the same table they are interchangeable (for now). SILE = require("core.sile") - -io.stdout:setvbuf 'no' +SILE.full_version = SILE.full_version .. " [Lua]" SILE.cli:parseArguments() @@ -86,26 +28,9 @@ if not os.getenv 'LUA_REPL_RLWRAP' and not SILE.quiet then io.stderr:write(SILE.full_version .. '\n') end -local ProFi -if SU.debugging("profile") then - ProFi = require("ProFi") -end - -if SILE.makeDeps then - SILE.makeDeps:add(executable) -end - SILE.init() -if SILE.masterFilename then - - if SILE.masterDir:len() >= 1 then - extendPaths(SILE.masterDir) - end - - if SU.debugging("profile") then - ProFi:start() - end +if SILE.input.filenames and #SILE.input.filenames >= 1 then -- Deprecated, notice given in core.cli when argument used for _, path in ipairs(SILE.input.includes) do @@ -125,7 +50,7 @@ if SILE.masterFilename then end local main, err = xpcall(function() - return SILE.processFile(SILE.input.filename) + pl.tablex.imap(SILE.processFile, SILE.input.filenames) end, SILE.errorHandler) if not main then @@ -139,15 +64,6 @@ if SILE.masterFilename then SILE.finish() - if SU.debugging("profile") then - ProFi:stop() - ProFi:writeReport(SILE.masterFilename..'.profile.txt') - end - - if SU.debugging("versions") then - SILE.shaper:debugVersions() - end - else SILE.repl:enter() end diff --git a/sile.rockspec.in b/sile.rockspec.in new file mode 100644 index 000000000..9cf849739 --- /dev/null +++ b/sile.rockspec.in @@ -0,0 +1,52 @@ +-- @ROCKSPECWARNING@ +rockspec_format = "3.0" +package = "@TRANSFORMED_PACKAGE_NAME@" +version = "dev-@ROCKREV@" + +source = { + url = "git+https://github.com/sile-typesetter/sile.git", + branch = "develop" +} + +description = { + summary = "Simon’s Improved Layout Engine", + detailed = [[SILE is a typesetting system; its job is to produce beautiful printed documents. + Conceptually, SILE is similar to TeX—from which it borrows some concepts and even + syntax and algorithms—but the similarities end there. Rather than being a + derivative of the TeX family SILE is a new typesetting and layout engine written + from the ground up using modern technologies and borrowing some ideas from + graphical systems such as InDesign.]], + license = "MIT", + homepage = "https://github.com/sile-typesetter/fontproof", + issues_url = "https://github.com/sile-typesetter/fontproof/issues", + maintainer = "Caleb Maclennan ", + labels = { "typesetting" } +} + +dependencies = { + "lua >= 5.1", + "bit32", -- only required on Lua < 5.2, versions vary between Rock and VM provided + "cassowary == 2.3.2-1", + "cldr == 0.3.0-0", + "compat53 == 0.8-1", -- only required on Lua < 5.3 + "fluent == 0.2.0-0", + "linenoise == 0.9-1", + "loadkit == 1.1.0-1", + "lpeg == 1.1.0-1", + "lua-zlib == 1.2-2", + "lua_cliargs == 3.0-2", + "luaepnf == 0.3-2", + "luaexpat == 1.5.1-1", + "luafilesystem == 1.8.0-1", + "luarepl == 0.10-1", + "luasec == 1.3.2-1", + "luasocket == 3.1.0-1", + "luautf8 == 0.1.5-2", + "penlight == 1.13.1-1", + "vstruct == 2.1.1-1" +} + +build = { + type = "builtin", + modules = {} +} diff --git a/spec/measurements_spec.lua b/spec/measurements_spec.lua index 52d9b1966..e4b9eec16 100644 --- a/spec/measurements_spec.lua +++ b/spec/measurements_spec.lua @@ -2,7 +2,7 @@ SILE = require("core.sile") SU.warn = function () end describe("The papersize parser", function() - local parse = SILE.paperSizeParser + local parse = SILE.papersize it("should return the correct values for a6", function() local a6 = { 297.6377985, 419.52756359999995 } assert.is.same(parse("a6"), a6) @@ -14,6 +14,11 @@ describe("The papersize parser", function() assert.is.same(parse("2in x 4in"), size) assert.is.same(parse("144 x 288"), size) end) + it("should flip x and y page geometry for landscape mode", function() + local size = { 288, 144 } + assert.is.same(parse("2in x 4in", true), size) + assert.is.same(parse("144 x 288", true), size) + end) it("error if unable to parse", function() assert.has.errors(function () parse("notapaper") end) assert.has.errors(function () parse(nil) end) diff --git a/spec/utilities_spec.lua b/spec/utilities_spec.lua index 248b3621d..8c3747286 100644 --- a/spec/utilities_spec.lua +++ b/spec/utilities_spec.lua @@ -5,6 +5,15 @@ describe("SILE.utilities", function() assert.is.truthy(SU) end) + describe("deprecated", function () + it("should compute errors based on semver", function() + SILE.version = "v0.1.10.r4-h5d5dd3b" + SU.warn = function () end + assert.has.errors(function() SU.deprecated("foo", "bar", "0.1.9", "0.1.9") end) + assert.has_no.errors(function() SU.deprecated("foo", "bar", "0.1.11", "0.1.11") end) + end) + end) + describe("utf8_to_utf16be_hexencoded ", function() it("should hex encode input", function() local str = "foo" @@ -15,6 +24,9 @@ describe("SILE.utilities", function() describe("formatNumber", function () + local icu = require("justenoughicu") + local icu73plus = tostring(icu.version()) >= "73.0" + SILE.documentState = { documentClass = { state = { } } } SILE.typesetter = SILE.typesetters.base(SILE.newFrame({ id = "dummy" })) @@ -104,7 +116,8 @@ describe("SILE.utilities", function() end) it("should format ordinal numbers", function () - assert.is.equal("1 984.", -- N.B. Contains a non-breaking space + local expectation = icu73plus and "1 984" or "1 984." + assert.is.equal(expectation, -- N.B. Contains a non-breaking space SU.formatNumber(1984, { style = "ordinal" })) end) end) @@ -122,7 +135,8 @@ describe("SILE.utilities", function() end) it("should format ordinal numbers", function () - assert.is.equal("١٬٩٨٤.", SU.formatNumber(1984, { style = "ordinal" })) + local expectation = icu73plus and "١٬٩٨٤" or "١٬٩٨٤." + assert.is.equal(expectation, SU.formatNumber(1984, { style = "ordinal" })) end) end) diff --git a/src/Makefile.am b/src/Makefile.am deleted file mode 100644 index 70f72913c..000000000 --- a/src/Makefile.am +++ /dev/null @@ -1,67 +0,0 @@ -ACLOCAL_AMFLAGS = -I ../build-aux - -if LINKLUA -MY_LUA_LIB=$(LUA_LIB) -UNDEFINED=-no-undefined -else -MY_LUA_LIB= -UNDEFINED= -endif - -if SYSTEM_LIBTEXPDF -LIBTEXPDF_LIB=-ltexpdf -else -LIBTEXPDF_LIB=../libtexpdf/libtexpdf.la -endif - -pkglib_LTLIBRARIES = justenoughharfbuzz.la justenoughlibtexpdf.la justenoughfontconfig.la fontmetrics.la svg.la -justenoughharfbuzz_la_SOURCES = justenoughharfbuzz.c hb-utils.c hb-utils.h -justenoughharfbuzz_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -justenoughharfbuzz_la_CFLAGS = $(HARFBUZZ_CFLAGS) $(HARFBUZZ_SUBSET_CFLAGS) $(LUA_INCLUDE) -justenoughharfbuzz_la_LIBADD = $(HARFBUZZ_LIBS) $(HARFBUZZ_SUBSET_LIBS) $(MY_LUA_LIB) -to_copy = .libs/justenoughharfbuzz.@SHARED_LIB_EXT@ - -justenoughfontconfig_la_SOURCES = justenoughfontconfig.c silewin32.h -justenoughfontconfig_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -justenoughfontconfig_la_CFLAGS = $(FONTCONFIG_CFLAGS) $(LUA_INCLUDE) -justenoughfontconfig_la_LIBADD = $(FONTCONFIG_LIBS) $(MY_LUA_LIB) -to_copy += .libs/justenoughfontconfig.@SHARED_LIB_EXT@ - -if APPKIT -pkglib_LTLIBRARIES += macfonts.la -macfonts_la_SOURCES = macfonts.m -macfonts_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -macfonts_la_OBJCFLAGS = -U VERSION $(HARFBUZZ_CFLAGS) $(LUA_INCLUDE) -fmodules -macfonts_la_LIBADD = $(HARFBUZZ_LIBS) $(MY_LUA_LIB) -to_copy += .libs/macfonts.@SHARED_LIB_EXT@ -endif - -justenoughlibtexpdf_la_SOURCES = justenoughlibtexpdf.c imagebbox.c -justenoughlibtexpdf_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -justenoughlibtexpdf_la_CFLAGS = -I.. $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(LIBPAPER_INCLUDES) $(LUA_INCLUDE) -justenoughlibtexpdf_la_LIBADD = $(LIBTEXPDF_LIB) $(LIBPNG_LIBS) $(ZLIB_LIBS) $(LIBPAPER_LIBS) $(MY_LUA_LIB) -to_copy += .libs/justenoughlibtexpdf.@SHARED_LIB_EXT@ - -fontmetrics_la_SOURCES = fontmetrics.c hb-utils.c hb-utils.h -fontmetrics_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -fontmetrics_la_CFLAGS = $(LUA_INCLUDE) $(HARFBUZZ_CFLAGS) -fontmetrics_la_LIBADD = $(MY_LUA_LIB) $(HARFBUZZ_LIBS) -to_copy += .libs/fontmetrics.@SHARED_LIB_EXT@ - -svg_la_SOURCES = svg.c nanosvg.h -svg_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -svg_la_CFLAGS = $(LUA_INCLUDE) -svg_la_LIBADD = $(MY_LUA_LIB) -to_copy += .libs/svg.@SHARED_LIB_EXT@ - -if ICU -pkglib_LTLIBRARIES += justenoughicu.la -justenoughicu_la_SOURCES = justenoughicu.c -justenoughicu_la_LDFLAGS = -module -avoid-version -shared $(UNDEFINED) -justenoughicu_la_CFLAGS = -I.. $(ICU_CFLAGS) $(LUA_INCLUDE) -justenoughicu_la_LIBADD = $(ICU_LIBS) $(MY_LUA_LIB) -to_copy += .libs/justenoughicu.@SHARED_LIB_EXT@ -endif - -all-local: $(pkglib_LTLIBRARIES) - cp $(to_copy) .. diff --git a/src/bin/sile.rs b/src/bin/sile.rs new file mode 100644 index 000000000..146f4e7cc --- /dev/null +++ b/src/bin/sile.rs @@ -0,0 +1,35 @@ +use clap::{CommandFactory, FromArgMatches}; + +use sile::cli::Cli; +use sile::Result; + +fn main() -> Result<()> { + let version = option_env!("VERGEN_GIT_DESCRIBE").unwrap_or_else(|| env!("CARGO_PKG_VERSION")); + let version = version.replacen('-', ".r", 1); + let long_version = sile::version()? + .strip_prefix("SILE ") + .unwrap_or("") + .to_string(); + let app = Cli::command().version(version).long_version(long_version); + #[allow(unused_variables)] + let matches = app.get_matches(); + let args = Cli::from_arg_matches(&matches).expect("Unable to parse arguments"); + sile::run( + args.input, + args.backend, + args.class, + args.debug, + args.evaluate, + args.evaluate_after, + args.fontmanager, + args.makedeps, + args.output, + args.option, + args.preamble, + args.postamble, + args.r#use, + args.quiet, + args.traceback, + )?; + Ok(()) +} diff --git a/src/cli.rs b/src/cli.rs new file mode 100644 index 000000000..9af8b7142 --- /dev/null +++ b/src/cli.rs @@ -0,0 +1,132 @@ +use clap::Parser; +use std::path::PathBuf; + +/// The SILE typesetter reads an input file(s), by default in either SIL or XML format, and +/// processes them to generate an output file, by default in PDF format. The output will be written +/// to a file with the same name as the first input file with the extension changed to .pdf unless +/// the `--output` argument is used. Additional input or output formats can be handled by loading +/// a module with the `--use` argument to add support for them first. +#[derive(Parser, Debug)] +#[clap(author, name = "SILE", bin_name = "sile")] +pub struct Cli { + /// Input document filename(s), by default in SIL, XML, or Lua formats. + /// + /// One or more input files from which to process content. + /// The first listed file is considered the master document, others are procced in sequence. + /// Other inputter formats may be enabled via`--use`. + /// Use `-` to read a content stream from STDIN. + pub input: Option>, + + /// Specify the output backend. + /// + /// The default is `libtexpdf` and suitible for most PDF output. + /// Alternatives supported out of the box include `text`, `debug`, `dummy`, `cairo`, and `podofo`. + /// Other outputters may be enabled via `--use`. + #[clap(short, long, value_name = "BACKEND")] + pub backend: Option, + + /// Override the default or specified document class. + /// + /// The default class for documents that do not specify one in the root tag is `plain`. + /// This can be used to either change the default class or to override the class actually specified in a document. + /// Other bundled classes include `base`, `bible`, `book`, `diglot`, `docbook`, `docbook`, `jbook`, `jplain`, `letter`, `pecha`, `tbook`, `tplain`, and `triglot`. + /// Others will be loaded dynamically from the module path. + #[clap(short, long)] + pub class: Option, + + /// Show debug information for tagged aspects of SILE’s operation. + /// + /// Multiple debug flags may be given as a comma separated list. + /// While packages may define their own debug flags, the most commonly used ones are `typesetter`, `pagebuilder`, `vboxes`, `break`, `frames`, `profile`, and `versions`. + /// May be specified more than once. + #[clap(short, long, value_name = "DEBUGFLAG[,DEBUGFLAG]")] + // TODO: switch to num_args(0..) to allow space separated inputs + pub debug: Option>, + + /// Evaluate Lua expression before processing input. + /// + /// May be specified more than once. + #[clap(short, long, value_name = "EXRPESION")] + pub evaluate: Option>, + + /// Evaluate Lua expression after processing input. + /// + /// May be specified more than once. + #[clap(short = 'E', long, value_name = "EXRPESION")] + pub evaluate_after: Option>, + + /// Specify which font manager to use. + /// + /// The font manager is responsible for discovering the locations on font files on the system given a font name. + /// The default font manager is `fontconfig` on non-macOS systems and `macfonts` on macOS. + #[clap(short, long, value_name = "FONTMANAGER")] + pub fontmanager: Option, + + /// Generate a Makefile format list of dependencies and white them to a file. + /// + /// This tracks all the files (input files, Lua libraries, fonts, images, etc.) use during the + /// typesetting process. + /// After completion, the list is written to FILE in the format of a dependency list for + /// a target in a Makefile. + /// This can be used later to determine if a PDF needs re-rendering based on whether any inputs + /// have changed. + #[clap(short, long, value_name = "FILE")] + pub makedeps: Option, + + /// Explicitly set the output file name. + /// + /// By default the basename of the first input file will be used as the output filename. + /// An extension will be chosen based on the output backend, typically .pdf. + /// With this option any arbitrary name and path can be given. + /// Additionally `-` can be used to write the output to STDOUT. + #[clap(short, long, value_name = "FILE")] + pub output: Option, + + /// Set or override document class options. + /// + /// Can be used to change default options or override the ones specified in a document. + /// For example setting `--options papersize=letter` would override both the default `papersize` of A4 and any specific one set in the document’s options. + /// May be specified more than once. + #[clap(short = 'O', long)] + pub option: Option>, + + /// Include the contents of a SIL, XML, or other resource file before the input document content. + /// + /// The value should be a full filename with a path relative to PWD or an absolute path. + /// May be specified more than once. + #[clap(short, long, value_name = "FILE")] + pub preamble: Option>, + + /// Include the contents of a SIL, XML, or other resource file after the input document content. + /// + /// The value should be a full filename with a path relative to PWD or an absolute path. + /// May be specified more than once. + #[clap(short = 'P', long, value_name = "FILE")] + pub postamble: Option>, + + /// Load and initialize a class, inputter, shaper, or other module before processing the main input. + /// + /// The value should be a loadable module name (with no extension, using `.` as a path separator) and will be loaded using SILE’s module search path. + /// Options may be passed to the module by enclosing `key=value` pairs in square brackets following the module name. + /// This is particularly useful when the input document is not SIL or a natively recognized XML scheme + /// and SILE needs to be taught new tricks before even trying to read the input file. + /// If the module is a document class, it will replace `plain` as the default class for processing + /// documents that do not specifically identify one to use. + /// Because this executes before loading the document, it may even add an input parser or modify an existing one to support new file formats. + /// Package modules will be added to the preamble to be loaded after the class is initialized. + /// May be specified more than once. + #[clap( + short, + long, + value_name = "MODULE[[PARAMETER=VALUE[,PARAMETER=VALUE]]]" + )] + pub r#use: Option>, + + /// Suppress warnings and informational messages during processing. + #[clap(short, long)] + pub quiet: bool, + + /// Display detailed location trace on errors and warnings. + #[clap(short, long)] + pub traceback: bool, +} diff --git a/src/embed.rs.in b/src/embed.rs.in new file mode 100644 index 000000000..f787d7347 --- /dev/null +++ b/src/embed.rs.in @@ -0,0 +1,136 @@ +use mlua::chunk; +use mlua::prelude::*; +use rust_embed::{EmbeddedFile, RustEmbed}; +use std::str; + +/// Embed everything that would otherwise be installed to datadir +#[derive(RustEmbed)] +#[folder = "."] +#[exclude = ".*"] +#[exclude = "*~"] +#[exclude = "*.in"] +#[exclude = "Make*"] +#[exclude = "autom4te.cache/*"] +#[exclude = "build-aux/*"] +#[exclude = "cmake/*"] +#[exclude = "completions/*"] +#[exclude = "documentation/*"] +#[exclude = "justenough/*"] +#[exclude = "libtexpdf/*"] +#[exclude = "libtexpdf/*"] +#[exclude = "libtool"] +#[exclude = "node_modules/*"] +#[exclude = "rust-toolchain"] +#[exclude = "sile*"] +#[exclude = "src/*"] +#[exclude = "target/*"] +#[exclude = "tests/*"] +// @INCLUDE_EMDED_INCLUDES@ -- this marker line gets replaced by a list of includes +pub struct SileModules; + +// Link Lua loader functions from C modules that Lua would otherwise be loading externally that +// we've linked into the CLI binary. Linking happens in build-aux/build.rs. +extern "C-unwind" { + fn luaopen_fontmetrics(lua: *mut mlua::lua_State) -> i32; + fn luaopen_justenoughfontconfig(lua: *mut mlua::lua_State) -> i32; + fn luaopen_justenoughharfbuzz(lua: *mut mlua::lua_State) -> i32; + fn luaopen_justenoughicu(lua: *mut mlua::lua_State) -> i32; + fn luaopen_justenoughlibtexpdf(lua: *mut mlua::lua_State) -> i32; + fn luaopen_svg(lua: *mut mlua::lua_State) -> i32; +} + +/// Register a Lua function in the loaders/searchers table to return C modules linked into the CLI +/// binary and another to return embeded Lua resources as Lua modules. See discussion in mlua: +/// https://github.com/khvzak/mlua/discussions/322 +pub fn inject_embeded_loader(lua: &Lua) { + let package: LuaTable = lua.globals().get("package").unwrap(); + let loaders: LuaTable = match package.get("loaders").unwrap() { + LuaValue::Table(loaders) => loaders, + LuaValue::Nil => package.get("searchers").unwrap(), + _ => panic!("Unable to find approprate interface to inject embeded loader"), + }; + loaders + .push(LuaFunction::wrap(|lua, module: String| unsafe { + match module.as_str() { + "fontmetrics" => lua + .create_c_function(luaopen_fontmetrics) + .map(LuaValue::Function), + "justenoughfontconfig" => lua + .create_c_function(luaopen_justenoughfontconfig) + .map(LuaValue::Function), + "justenoughharfbuzz" => lua + .create_c_function(luaopen_justenoughharfbuzz) + .map(LuaValue::Function), + "justenoughicu" => lua + .create_c_function(luaopen_justenoughicu) + .map(LuaValue::Function), + "justenoughlibtexpdf" => lua + .create_c_function(luaopen_justenoughlibtexpdf) + .map(LuaValue::Function), + "svg" => lua.create_c_function(luaopen_svg).map(LuaValue::Function), + _ => format!("C Module '{module}' is not linked in Rust binary").into_lua(lua), + } + })) + .unwrap(); + loaders + .push(LuaFunction::wrap(|lua, module: String| { + let module_path = module.replace('.', "/"); + let luaversion: LuaString = lua + .load(chunk! { + return _VERSION:match("%d+%.%d+") + }) + .eval() + .unwrap(); + let luaversion: &str = luaversion.to_str().unwrap(); + let mut package_epath: Vec<&str> = vec!["?/init.lua", "?.lua", "lua-libraries/?.lua"]; + let path = format!("lua_modules/lib/lua/{}/?/init.lua", luaversion); + package_epath.push(&path); + let path = format!("lua_modules/lib/lua/{}/?.lua", luaversion); + package_epath.push(&path); + let path = format!("lua_modules/share/lua/{}/?/init.lua", luaversion); + package_epath.push(&path); + let path = format!("lua_modules/share/lua/{}/?.lua", luaversion); + package_epath.push(&path); + let mut resource_option: Option = None; + for pattern in &package_epath { + let path = pattern.replace('?', &module_path); + let embeded = SileModules::get(&path); + if embeded.is_some() { + resource_option = embeded; + break; + } + } + match resource_option { + Some(module) => { + return LuaFunction::wrap(move |lua, ()| { + let data = str::from_utf8(module.data.as_ref()) + .expect("Embeded content is not valid UTF-8"); + lua.load(data).call::<_, LuaValue>(()) + }) + .into_lua(lua) + } + None => format!("Module '{module}' is not embeded in Rust binary").into_lua(lua), + } + })) + .unwrap(); + loaders + .push(LuaFunction::wrap(|lua, module: String| { + let module_path = module.replace('.', "/"); + let pattern = "?.ftl"; + let path = pattern.replace('?', &module_path); + match SileModules::get(&path) { + Some(module) => LuaFunction::wrap(move |lua, ()| { + let data = str::from_utf8(module.data.as_ref()) + .expect("Embeded content is not valid UTF-8"); + lua.load(chunk! { + return assert(fluent:add_messages($data)) + }) + .call::<_, LuaValue>(()) + }) + .into_lua(lua), + None => format!("FTL resource '{module_path}' is not embeded in Rust binary") + .into_lua(lua), + } + })) + .unwrap(); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 000000000..e77d704f5 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,199 @@ +// rust-embed include attributes have issues with lots of matches... +#![recursion_limit = "2048"] + +use mlua::chunk; +use mlua::prelude::*; +#[cfg(not(feature = "static"))] +use std::env; +use std::path::PathBuf; +#[cfg(feature = "cli")] +pub mod cli; + +#[cfg(feature = "static")] +pub mod embed; + +pub type Result = anyhow::Result; + +pub fn start_luavm() -> crate::Result { + let lua = unsafe { Lua::unsafe_new() }; + #[cfg(feature = "static")] + crate::embed::inject_embeded_loader(&lua); + inject_paths(&lua); + load_sile(&lua); + inject_version(&lua); + Ok(lua) +} + +pub fn inject_paths(lua: &Lua) { + #[cfg(feature = "static")] + lua.load(r#"require("core.pathsetup")"#).exec().unwrap(); + #[cfg(not(feature = "static"))] + { + let sile_path = match env::var("SILE_PATH") { + Ok(val) => val, + Err(_) => env!("CONFIGURE_DATADIR").to_string(), + }; + let sile_path: LuaString = lua.create_string(&sile_path).unwrap(); + lua.load(chunk! { + local status + for path in string.gmatch($sile_path, "[^;]+") do + status = pcall(dofile, path .. "/core/pathsetup.lua") + if status then break end + end + if not status then + dofile("./core/pathsetup.lua") + end + }) + .exec() + .unwrap(); + } +} + +pub fn inject_version(lua: &Lua) { + let sile: LuaTable = lua.globals().get("SILE").unwrap(); + let mut full_version: String = sile.get("full_version").unwrap(); + full_version.push_str(" [Rust]"); +} + +pub fn load_sile(lua: &Lua) { + let entry: LuaString = lua.create_string("core.sile").unwrap(); + let r#require: LuaFunction = lua.globals().get("require").unwrap(); + r#require.call::(entry).unwrap(); +} + +pub fn version() -> crate::Result { + let lua = start_luavm()?; + let sile: LuaTable = lua.globals().get("SILE").unwrap(); + let mut full_version: String = sile.get("full_version").unwrap(); + full_version.push_str(" [Rust]"); + Ok(full_version) +} + +// Yes I know this should be taking a struct, probably 1 with what ends up being SILE.input and one +// with other stuff the CLI may inject, but I'm playing with what a minimum/maximum set of +// parameters would look like here while maintaining compatiblitiy with the Lua CLI. +#[allow(clippy::too_many_arguments)] +pub fn run( + inputs: Option>, + backend: Option, + class: Option, + debugs: Option>, + evaluates: Option>, + evaluate_afters: Option>, + fontmanager: Option, + makedeps: Option, + output: Option, + options: Option>, + preambles: Option>, + postambles: Option>, + uses: Option>, + quiet: bool, + traceback: bool, +) -> crate::Result<()> { + let lua = start_luavm()?; + let sile: LuaTable = lua.globals().get("SILE")?; + sile.set("traceback", traceback)?; + sile.set("quiet", quiet)?; + let mut has_input_filename = false; + if let Some(flags) = debugs { + let debug_flags: LuaTable = sile.get("debugFlags")?; + for flag in flags { + debug_flags.set(flag, true)?; + } + } + let full_version: String = sile.get("full_version")?; + let sile_input: LuaTable = sile.get("input")?; + if let Some(expressions) = evaluates { + sile_input.set("evaluates", expressions)?; + } + if let Some(expressions) = evaluate_afters { + sile_input.set("evaluateAfters", expressions)?; + } + if let Some(backend) = backend { + sile.set("backend", backend)?; + } + if let Some(fontmanager) = fontmanager { + sile.set("fontmanager", fontmanager)?; + } + if let Some(class) = class { + sile_input.set("class", class)?; + } + if let Some(paths) = preambles { + sile_input.set("preambles", paths_to_strings(paths))?; + } + if let Some(paths) = postambles { + sile_input.set("postambles", paths_to_strings(paths))?; + } + if let Some(path) = makedeps { + sile_input.set("makedeps", path_to_string(&path))?; + } + if let Some(path) = output { + sile.set("outputFilename", path_to_string(&path))?; + has_input_filename = true; + } + if let Some(options) = options { + sile_input.set("options", options)?; + } + if let Some(modules) = uses { + // let parser_bits: LuaTable = sile.get("parserBits")?; + // let cliuse: LuaAnyUserData = parser_bits.get("cliuse")?; + // sile_input.get("uses")?; + for module in modules.iter() { + let module = lua.create_string(module)?; + lua.load(chunk! { + local spec = SILE.parserBits.cliuse:match($module); + table.insert(SILE.input.uses, spec) + }) + .eval()?; + // let spec = cliuse.call_function::<_, _, _>("match", module); + } + } + if !quiet { + eprintln!("{full_version}"); + } + let init: LuaFunction = sile.get("init")?; + init.call::<_, _>(())?; + if let Some(inputs) = inputs { + let input_filenames: LuaTable = lua.create_table()?; + for input in inputs.iter() { + let path = &path_to_string(input); + if !has_input_filename && path != "-" { + has_input_filename = true; + } + input_filenames.push(lua.create_string(path)?)?; + } + if !has_input_filename { + panic!( + "\nUnable to derive an output filename (perhaps because input is a STDIO stream)\nPlease use --output to set one explicitly." + ); + } + sile_input.set("filenames", input_filenames)?; + let input_uses: LuaTable = sile_input.get("uses")?; + let r#use: LuaFunction = sile.get("use")?; + for spec in input_uses.sequence_values::() { + let spec = spec?; + let module: LuaString = spec.get("module")?; + let options: LuaTable = spec.get("options")?; + r#use.call::<(LuaString, LuaTable), _>((module, options))?; + } + let input_filenames: LuaTable = sile_input.get("filenames")?; + let process_file: LuaFunction = sile.get("processFile")?; + for file in input_filenames.sequence_values::() { + process_file.call::(file?)?; + } + let finish: LuaFunction = sile.get("finish")?; + finish.call::<_, _>(())?; + } else { + let repl: LuaTable = sile.get("repl")?; + repl.call_method::<_, _>("enter", ())?; + } + Ok(()) +} + +fn path_to_string(path: &PathBuf) -> String { + path.clone().into_os_string().into_string().unwrap() +} + +fn paths_to_strings(paths: Vec) -> Vec { + paths.iter().map(path_to_string).collect() +} diff --git a/tests/bug-990.expected b/tests/bug-990.expected index 4e79f0133..cdf325b11 100644 --- a/tests/bug-990.expected +++ b/tests/bug-990.expected @@ -15,15 +15,9 @@ Mx 24.7039 My 124.9953 Set font Noto Sans CJK JP;22;800;;normal;;;LTR T 29804 w=22.0000 (第) -Mx 46.7327 -Mx 2.4640 -T 1 a=4.9280 ( ) -Mx 49.2254 +Mx 46.7338 T 19 w=12.2100 (2) -Mx 61.4641 -Mx 4.4000 -T 1 a=4.9280 ( ) -Mx 65.8928 +Mx 58.9737 T 29653 w=22.0000 (章) Mx 24.7039 My 151.1793 diff --git a/tests/landscape.expected b/tests/landscape.expected new file mode 100644 index 000000000..69035d1aa --- /dev/null +++ b/tests/landscape.expected @@ -0,0 +1,8 @@ +Set paper size 841.8897729 595.275597 +Begin page +Mx 418.5987 +My 553.7327 +Set font Gentium Plus;10;400;;normal;;;LTR +T 20 w=4.6924 (1) +End page +Finish diff --git a/tests/landscape.sil b/tests/landscape.sil new file mode 100644 index 000000000..5e3f9b645 --- /dev/null +++ b/tests/landscape.sil @@ -0,0 +1,2 @@ +\begin[landscape=true]{document} +\end{document} diff --git a/tests/makecolumns.expected b/tests/makecolumns.expected new file mode 100644 index 000000000..4e4cf097b --- /dev/null +++ b/tests/makecolumns.expected @@ -0,0 +1,8 @@ +Set paper size 419.5275636 595.275597 +Begin page +Mx 207.4176 +My 553.7327 +Set font Gentium Plus;10;400;;normal;;;LTR +T 20 w=4.6924 (1) +End page +Finish diff --git a/tests/makecolumns.sil b/tests/makecolumns.sil new file mode 100644 index 000000000..1617d0233 --- /dev/null +++ b/tests/makecolumns.sil @@ -0,0 +1,5 @@ +\begin[papersize=a5]{document} +\use[module=packages.frametricks] +\makecolumns[columns=4] +\showframe[id=all] +\end{document} diff --git a/tests/vertical.sil b/tests/vertical.sil index ffc210aa0..3c9fcf787 100644 --- a/tests/vertical.sil +++ b/tests/vertical.sil @@ -1,7 +1,7 @@ \begin[class=jplain,layout=tate]{document} \nofolios % Note: differences between Noto font versions make adding more characters to -% this test brittle. For examle 日本は昔からruns into trouble when the height +% this test brittle. For example 日本は昔からruns into trouble when the height % of 昔 is different. If the tate layout mechanism is working, it’s going % to work at two characters, if a longer string fails, it’s likely some other % mechanism or value that actually changed. diff --git a/typesetters/base.lua b/typesetters/base.lua index 53bab6eed..679334e07 100644 --- a/typesetters/base.lua +++ b/typesetters/base.lua @@ -898,7 +898,7 @@ function typesetter:breakpointsToLines (breakpoints) end if linestart < #nodes then -- Abnormal, but warn so that one has a chance to check which bits - -- are misssing at output. + -- are missing at output. SU.warn("Internal typesetter error " .. (#nodes - linestart) .. " skipped nodes") end return lines @@ -923,7 +923,7 @@ function typesetter.computeLineRatio (_, breakwidth, slice) -- zero boxes, so as to reach actual content... if slice[n].value ~= "margin" then -- ... but any other glue than a margin, at the end of a line, is actually - -- extraneous. It will however also be accounted for below, so substract + -- extraneous. It will however also be accounted for below, so subtract -- them to cancel their width. Typically, if a line break occurred at -- a space, the latter is then at the end of the line now, and must be -- ignored.