- Commit
- 04d70d11e8ddf12551f6b736fdcc31c7e69db199
- Parent
- c8c8976a20630694e2fd2f88904e7bec022b6523
- Author
- Pablo <pablo-pie@riseup.net>
- Date
Added custom MuPDF bindings to the repo
Also fixed a typo in outro.html
Custum build of stapix for tikz.pablopie.xyz
Added custom MuPDF bindings to the repo
Also fixed a typo in outro.html
9 files changed, 948 insertions, 1 deletions
| Status | Name | Changes | Insertions | Deletions |
| Added | .gitmodules | 1 file changed | 3 | 0 |
| Added | mupdf-sys/.gitignore | 1 file changed | 1 | 0 |
| Added | mupdf-sys/Cargo.lock | 1 file changed | 235 | 0 |
| Added | mupdf-sys/Cargo.toml | 1 file changed | 194 | 0 |
| Added | mupdf-sys/build.rs | 1 file changed | 385 | 0 |
| Added | mupdf-sys/mupdf | 1 file changed | 1 | 0 |
| Added | mupdf-sys/src/lib.rs | 1 file changed | 39 | 0 |
| Added | mupdf-sys/src/wrapper.c | 1 file changed | 89 | 0 |
| Modified | src/outro.html | 2 files changed | 1 | 1 |
diff --git /dev/null b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "mupdf-sys/mupdf"] + path = mupdf-sys/mupdf + url = https://github.com/ArtifexSoftware/mupdf.git
diff --git /dev/null b/mupdf-sys/.gitignore @@ -0,0 +1 @@ +/target
diff --git /dev/null b/mupdf-sys/Cargo.lock @@ -0,0 +1,235 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "cc" +version = "1.2.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "mupdf-sys" +version = "1.0.0" +dependencies = [ + "bindgen", + "cc", + "pkg-config", + "regex", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
diff --git /dev/null b/mupdf-sys/Cargo.toml @@ -0,0 +1,194 @@ +[package] +name = "mupdf-sys" +version = "1.0.0" +edition = "2021" +include = [ + "COPYING*", + "LICENSE*", + "AUTHORS*", + + "*.rs", + "src/wrapper.c", + + "mupdf/resources/hyphen/hyph-all.zip", + "mupdf/resources/hyphen/hyph-std.zip", + "mupdf/generated/resources/hyphen/*.c", + + "mupdf/resources/fonts/urw/*.cff", + "mupdf/generated/resources/fonts/urw/*.c", + + "mupdf/Make*", + "mupdf/platform/win32/*", + + "mupdf/scripts/*.c", + "mupdf/source/helpers/pkcs7/*.c", + "mupdf/include/mupdf/*.h", + "mupdf/include/mupdf/helpers/*.h", + + "mupdf/source/fitz/*.h", + "mupdf/source/fitz/*.c", + "mupdf/source/fitz/*.cpp", + "mupdf/source/fitz/icc/*.icc.h", + "mupdf/include/mupdf/fitz/*.h", + + "mupdf/source/pdf/*.c", + "mupdf/source/pdf/*.h", + "mupdf/source/pdf/cmaps/*.h", + "mupdf/source/pdf/js/*.h", + "mupdf/include/mupdf/pdf/*.h", + + "mupdf/source/xps/*.c", + "mupdf/source/xps/*.h", + + "mupdf/source/svg/*.c", + "mupdf/source/svg/*.h", + + "mupdf/source/html/*.c", + "mupdf/source/html/*.h", + + "mupdf/source/reflow/*.c", + + "mupdf/source/cbz/*.c", + + "mupdf/thirdparty/brotli/c/include/brotli/*.h", + "mupdf/thirdparty/brotli/c/common/*.c", + "mupdf/thirdparty/brotli/c/common/*.h", + "mupdf/thirdparty/brotli/c/dec/*.c", + "mupdf/thirdparty/brotli/c/dec/*.h", + "mupdf/thirdparty/brotli/c/enc/*.c", + "mupdf/thirdparty/brotli/c/enc/*.h", + + "mupdf/thirdparty/freetype/*.c", + "mupdf/thirdparty/freetype/src/base/*.c", + "mupdf/thirdparty/freetype/src/base/*.h", + "mupdf/thirdparty/freetype/src/cff/*.c", + "mupdf/thirdparty/freetype/src/cff/*.h", + "mupdf/thirdparty/freetype/src/cid/*.c", + "mupdf/thirdparty/freetype/src/cid/*.h", + "mupdf/thirdparty/freetype/src/psaux/*.c", + "mupdf/thirdparty/freetype/src/psaux/*.h", + "mupdf/thirdparty/freetype/src/pshinter/*.c", + "mupdf/thirdparty/freetype/src/pshinter/*.h", + "mupdf/thirdparty/freetype/src/psnames/*.c", + "mupdf/thirdparty/freetype/src/psnames/*.h", + "mupdf/thirdparty/freetype/src/raster/*.c", + "mupdf/thirdparty/freetype/src/raster/*.h", + "mupdf/thirdparty/freetype/src/sfnt/*.c", + "mupdf/thirdparty/freetype/src/sfnt/*.h", + "mupdf/thirdparty/freetype/src/smooth/*.c", + "mupdf/thirdparty/freetype/src/smooth/*.h", + "mupdf/thirdparty/freetype/src/truetype/*.c", + "mupdf/thirdparty/freetype/src/truetype/*.h", + "mupdf/thirdparty/freetype/src/type1/*.c", + "mupdf/thirdparty/freetype/src/type1/*.h", + "mupdf/thirdparty/freetype/include", + "mupdf/scripts/freetype/*.h", + + "mupdf/thirdparty/gumbo-parser/src/*.c", + "mupdf/thirdparty/gumbo-parser/src/*.h", + "mupdf/thirdparty/gumbo-parser/visualc/include/*.h", + + "mupdf/thirdparty/harfbuzz/src/*.cc", + "mupdf/thirdparty/harfbuzz/src/*.h", + "mupdf/thirdparty/harfbuzz/src/*.hh", + "mupdf/thirdparty/harfbuzz/src/graph/gsubgpos-context.cc", + "mupdf/thirdparty/harfbuzz/src/graph/*.hh", + "mupdf/thirdparty/harfbuzz/src/OT", + + "mupdf/thirdparty/libjpeg/*.c", + "mupdf/thirdparty/libjpeg/*.h", + "mupdf/scripts/libjpeg/*.h", + + "mupdf/thirdparty/lcms2/src/*.c", + "mupdf/thirdparty/lcms2/src/*.h", + "mupdf/thirdparty/lcms2/include/*.h", + + "mupdf/thirdparty/mujs/*.c", + "mupdf/thirdparty/mujs/*.h", + + "mupdf/thirdparty/zlib/*.c", + "mupdf/thirdparty/zlib/*.h", + + "mupdf/thirdparty/jbig2dec/*.c", + "mupdf/thirdparty/jbig2dec/*.h", + + "mupdf/thirdparty/openjpeg/src/lib/openjp2/*.c", + "mupdf/thirdparty/openjpeg/src/lib/openjp2/*.h", + + "mupdf/thirdparty/leptonica/src/*.c", + "mupdf/thirdparty/leptonica/src/*.h", + + "mupdf/thirdparty/tesseract/src/*.cpp", + "mupdf/thirdparty/tesseract/src/api/*.cpp", + "mupdf/thirdparty/tesseract/src/api/*.h", + "mupdf/thirdparty/tesseract/src/arch/*.cpp", + "mupdf/thirdparty/tesseract/src/arch/*.h", + "mupdf/thirdparty/tesseract/src/ccmain/*.cpp", + "mupdf/thirdparty/tesseract/src/ccmain/*.h", + "mupdf/thirdparty/tesseract/src/ccstruct/*.cpp", + "mupdf/thirdparty/tesseract/src/ccstruct/*.h", + "mupdf/thirdparty/tesseract/src/ccutil/*.cpp", + "mupdf/thirdparty/tesseract/src/ccutil/*.h", + "mupdf/thirdparty/tesseract/src/classify/*.cpp", + "mupdf/thirdparty/tesseract/src/classify/*.h", + "mupdf/thirdparty/tesseract/src/dict/*.cpp", + "mupdf/thirdparty/tesseract/src/dict/*.h", + "mupdf/thirdparty/tesseract/src/lstm/*.cpp", + "mupdf/thirdparty/tesseract/src/lstm/*.h", + "mupdf/thirdparty/tesseract/src/textord/*.cpp", + "mupdf/thirdparty/tesseract/src/textord/*.h", + "mupdf/thirdparty/tesseract/src/viewer/*.cpp", + "mupdf/thirdparty/tesseract/src/viewer/*.h", + "mupdf/thirdparty/tesseract/src/wordrec/*.cpp", + "mupdf/thirdparty/tesseract/src/wordrec/*.h", + "mupdf/thirdparty/tesseract/src/cutil/*.cpp", + "mupdf/thirdparty/tesseract/src/cutil/*.h", + "mupdf/thirdparty/tesseract/include/tesseract/*.h", + "mupdf/scripts/tesseract/*.h", + "mupdf/scripts/tesseract/tesseract/*.h", + + "mupdf/thirdparty/extract/src/*.c", + "mupdf/thirdparty/extract/src/*.h", + "mupdf/thirdparty/extract/include/extract/*.h", + "mupdf/thirdparty/extract/src/template.docx", + "mupdf/thirdparty/extract/src/template.odt", + "mupdf/thirdparty/extract/src/docx_template_build.py", + + "mupdf/thirdparty/zxing-cpp/core/src/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/aztec/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/aztec/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/datamatrix/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/datamatrix/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/maxicode/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/maxicode/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/oned/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/oned/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/pdf417/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/pdf417/*.h", + "mupdf/thirdparty/zxing-cpp/core/src/qrcode/*.cpp", + "mupdf/thirdparty/zxing-cpp/core/src/qrcode/*.h", + "mupdf/scripts/zxing-cpp/*.cpp", + "mupdf/scripts/zxing-cpp/*.h", + + "mupdf/thirdparty/zxing-cpp/core/src/libzueci/*.c", + "mupdf/thirdparty/zxing-cpp/core/src/libzueci/*.h", + + "mupdf/thirdparty/zint/backend/*.c", + "mupdf/thirdparty/zint/backend/*.h", + "mupdf/thirdparty/zint/backend/fonts/*.h", +] +description = "Custom Rust FFI binding to MuPDF" +keywords = ["pdf", "mupdf"] +license = "AGPL-3.0" +links="mupdf-wrapper" + +[features] + +[build-dependencies] +bindgen = { version = "0.72", default-features = false, features = ["runtime"] } +cc = "1.0.50" +pkg-config = "0.3" +regex = "1.11" + +[dependencies]
diff --git /dev/null b/mupdf-sys/build.rs @@ -0,0 +1,385 @@ +use std::{ + env::{self, current_dir}, + error::Error, + ffi::{OsStr, OsString}, + io::ErrorKind, + path::{Path, PathBuf}, + process::{Command, exit}, + fs, + result, + thread, +}; + +const SUPPORTED_TARGETS: &[&str] = &["linux", "openbsd", "netbsd", "macos"]; +const BINDINGS_PATH: &str = "src/wrapper.c"; + +type Result<T> = result::Result<T, Box<dyn Error>>; + +fn main() { + if let Err(e) = run() { + eprintln!("\n{e}"); + exit(1); + } +} + +fn run() -> Result<()> { + if fs::read_dir("mupdf").map_or(true, |d| d.count() == 0) { + Err( + "The `mupdf` directory is empty, did you forget to pull the submodules?\n\ + Try `git submodule update --init --recursive`", + )? + } + + let target = Target::from_cargo().map_err(|e| { + format!( + "Unable to detect target: {e}\n\ + Cargo is required to build mupdf" + ) + })?; + + if !SUPPORTED_TARGETS.contains(&target.os.as_str()) { + Err(format!("Target {:?} is unsupported!", target.os))? + } + + let src_dir = current_dir().unwrap().join("mupdf"); + let out_dir = + PathBuf::from(env::var_os("OUT_DIR").ok_or("Missing OUT_DIR environment variable")?); + + let docs = env::var_os("DOCS_RS").is_some(); + if !docs { + let build_dir = out_dir.join("build"); + let build_dir = build_dir.to_str().ok_or_else(|| { + format!("Build dir path is required to be valid UTF-8, got {build_dir:?}") + })?; + + if let Err(e) = fs::remove_dir_all(build_dir) { + if e.kind() != ErrorKind::NotFound { + println!("cargo:warning=Unable to clear {build_dir:?}. This may lead to flaky builds that might not incorporate configurations changes: {e}"); + } + } + + copy_recursive(&src_dir, build_dir.as_ref(), &[".git".as_ref()])?; + + // ======================================================================== + let mut make = Make::default(); + make.define("FZ_ENABLE_PDF", "1"); + make.define("FZ_ENABLE_SVG", "1"); + make.define("FZ_ENABLE_CBZ", "0"); + make.define("FZ_ENABLE_IMG", "0"); + make.define("FZ_ENABLE_HTML", "0"); + make.define("FZ_ENABLE_EPUB", "0"); + make.define("FZ_ENABLE_JS", "0"); + + // NOTE: see https://github.com/ArtifexSoftware/mupdf/blob/master/source/fitz/noto.c + make.define("TOFU", "1"); + make.define("TOFU_CJK", "1"); + make.define("TOFU_NOTO", "1"); + make.define("TOFU_SYMBOL", "1"); + make.define("TOFU_EMOJI", "1"); + make.define("TOFU_SIL", "1"); + + make.build(&target, build_dir)?; + + // ======================================================================== + build_wrapper() + .map_err(|e| format!("Unable to compile mupdf wrapper:\n {e}"))?; + } + + generate_bindings(&out_dir.join("bindings.rs")) + .map_err(|e| format!("Unable to generate mupdf bindings using bindgen:\n {e}"))?; + + Ok(()) +} + +fn copy_recursive(src: &Path, dst: &Path, ignore: &[&OsStr]) -> Result<()> { + if let Err(e) = fs::create_dir(dst) { + if e.kind() != ErrorKind::AlreadyExists { + Err(format!("Unable to create {dst:?}: {e}"))?; + } + } + + for entry in fs::read_dir(src)? { + let entry = entry?; + if ignore.contains(&&*entry.file_name()) { + continue; + } + + let src_path = entry.path(); + let dst_path = dst.join(entry.file_name()); + + let file_type = entry.file_type()?; + + if file_type.is_symlink() { + let link = fs::read_link(&src_path) + .map_err(|e| format!("Couldn't read symlink {src_path:?}: {e}"))?; + let err = std::os::unix::fs::symlink(&link, &dst_path); + + match err { + Ok(_) => continue, + Err(e) => println!( + "cargo:warning=Couldn't create symlink {dst_path:?} pointing to {link:?}. This might increase the size of your target folder: {e}" + ), + } + } + + if file_type.is_file() || fs::metadata(&src_path)?.is_file() { + fs::copy(&src_path, &dst_path) + .map_err(|e| format!("Couldn't copy {src_path:?} to {dst_path:?}: {e}"))?; + } else { + copy_recursive(&src_path, &dst_path, ignore)?; + } + } + Ok(()) +} + +fn build_wrapper() -> Result<()> { + let mut build = cc::Build::new(); + + build + .file(BINDINGS_PATH) + .include("mupdf/include") + .extra_warnings(true) + .flag("-Wno-clobbered") // NOTE: remove stupid warnings on src/wrapper.c + .debug(true) + .try_compile("mupdf-wrapper")?; + + Ok(()) +} + +fn generate_bindings(path: &Path) -> Result<()> { + let mut builder = bindgen::builder(); + + builder = builder + .clang_arg("-Imupdf/include") + .header(BINDINGS_PATH); + + builder = builder + .allowlist_recursively(false) + .allowlist_type("wchar_t") + .allowlist_type("FILE") + .opaque_type("FILE") + .allowlist_item("max_align_t") + .opaque_type("max_align_t"); + + builder = builder + .allowlist_item("fz_.*") + .allowlist_item("FZ_.*") + .allowlist_item("pdf_.*") + .allowlist_item("PDF_.*") + .allowlist_type("cmap_splay") + .allowlist_item("ucdn_.*") + .allowlist_item("UCDN_.*") + .allowlist_item("Memento_.*") + .allowlist_item("mupdf_.*"); + + // remove va_list functions as for all of these versions using ... exist + builder = builder + .blocklist_function("Memento_vasprintf") // Memento_asprintf + .blocklist_function("fz_vthrow") // fz_throw + .blocklist_function("fz_vwarn") // fz_warn + .blocklist_function("fz_vlog_error_printf") // fz_log_error_printf + .blocklist_function("fz_append_vprintf") // fz_append_printf + .blocklist_function("fz_write_vprintf") // fz_write_printf + .blocklist_function("fz_vsnprintf") // fz_snprintf + .blocklist_function("fz_format_string"); // mupdf_format_string + + // TODO: make "FZ_VERSION.*" private + // build config + builder = builder + .blocklist_var("FZ_ENABLE_.*") + .blocklist_var("FZ_PLOTTERS_.*"); + + // internal implementation details, considered private + builder = builder + .blocklist_item("fz_jmp_buf") + .blocklist_function("fz_var_imp") + .blocklist_function("fz_push_try") + .blocklist_function("fz_do_.*") + .blocklist_var("FZ_JMPBUF_ALIGN") + .blocklist_type("fz_error_stack_slot") + .blocklist_type("fz_error_context") + .blocklist_type("fz_warn_context") + .blocklist_type("fz_aa_context") + .blocklist_type("fz_activity_.*") + .blocklist_function("fz_register_activity_logger") + .opaque_type("fz_context") + .blocklist_type("fz_new_context_imp") + .blocklist_type("fz_lock") + .blocklist_type("fz_unlock"); + + builder = builder + .parse_callbacks(Box::new(bindgen::CargoCallbacks::new())); + + builder + .prepend_enum_name(false) + .use_core() + .generate()? + .write_to_file(path)?; + + Ok(()) +} + +#[derive(Default)] +struct Make { + build: cc::Build, + make_flags: Vec<OsString>, +} + +impl Make { + pub fn define(&mut self, var: &str, val: &str) { + self.build.define(var, val); + } + + fn make_var(&mut self, var: &str, val: impl AsRef<OsStr>) { + let mut flag = OsString::from(var); + flag.push("="); + flag.push(val); + self.make_flags.push(flag); + } + + fn make_bool(&mut self, var: &str, val: bool) { + self.make_var(var, if val { "yes" } else { "no" }); + } + + fn cpu_flags( + &mut self, + target: &Target, + feature: &str, + flag: &str, + make_flag: &str, + define: Option<&str>, + ) { + let contains = target.cpu_features.iter().any(|f| f == feature) + && self.build.is_flag_supported(flag).unwrap_or(true); + if contains { + self.build.flag(flag); + self.make_bool(make_flag, true); + } + + if let Some(define) = define { + self.define(define, if contains { "1" } else { "0" }); + } + } + + pub fn build(mut self, target: &Target, build_dir: &str) -> Result<()> { + self.make_var( + "build", + if target.small_profile() { + "small" + } else if target.debug_profile() { + "debug" + } else { + "release" + }, + ); + + self.make_var("OUT", build_dir); + + self.make_bool("HAVE_X11", false); + self.make_bool("HAVE_GLUT", false); + self.make_bool("HAVE_CURL", false); + + self.make_bool("verbose", true); + + // ======================================================================== + self.make_bool("USE_TESSERACT", false); + self.make_bool("USE_ZXINGCPP", false); + self.make_bool("USE_LIBARCHIVE", false); + + // ======================================================================== + self.cpu_flags( + target, + "sse4.1", + "-msse4.1", + "HAVE_SSE4_1", + Some("ARCH_HAS_SSE"), + ); + self.cpu_flags(target, "avx", "-mavx", "HAVE_AVX", None); + self.cpu_flags(target, "avx2", "-mavx2", "HAVE_AVX2", None); + self.cpu_flags(target, "fma", "-mfma", "HAVE_FMA", None); + + // NOTE: arm + self.cpu_flags( + target, + "neon", + "-mfpu=neon", + "HAVE_NEON", + Some("ARCH_HAS_NEON"), + ); + // ======================================================================== + if let Ok(n) = thread::available_parallelism() { + self.make_flags.push(format!("-j{n}").into()); + } + + self.build.warnings(false); + + let compiler = self.build.get_compiler(); + self.make_var("CC", compiler.path()); + self.make_var("XCFLAGS", compiler.cflags_env()); + + self.build.cpp(true); + let compiler = self.build.get_compiler(); + self.make_var("CXX", compiler.path()); + self.make_var("XCXXFLAGS", compiler.cflags_env()); + + let make = if cfg!(any( + target_os = "freebsd", + target_os = "openbsd", + target_os = "netbsd" + )) { + "gmake" + } else { + "make" + }; + + let status = Command::new(make) + .arg("libs") + .args(&self.make_flags) + .current_dir(build_dir) + .status() + .map_err(|e| format!("Failed to call {make}: {e}"))?; + if !status.success() { + Err(match status.code() { + Some(code) => format!("{make} invocation failed with status {code}"), + None => format!("{make} invocation failed"), + })?; + } + + println!("cargo:rustc-link-search=native={build_dir}"); + println!("cargo:rustc-link-lib=static=mupdf"); + println!("cargo:rustc-link-lib=static=mupdf-third"); + + Ok(()) + } +} + +struct Target { + debug: bool, + opt_level: String, + os: String, + + cpu_features: Vec<String>, +} + +impl Target { + fn from_cargo() -> Result<Self> { + Ok(Self { + debug: env::var_os("DEBUG").is_some_and(|s| s != "0" && s != "false"), + opt_level: env::var("OPT_LEVEL")?, + os: env::var("CARGO_CFG_TARGET_OS")?, + + cpu_features: env::var("CARGO_CFG_TARGET_FEATURE")? + .split(',') + .map(str::to_owned) + .collect(), + }) + } + + fn small_profile(&self) -> bool { + !self.debug && matches!(&*self.opt_level, "s" | "z") + } + + fn debug_profile(&self) -> bool { + self.debug && !matches!(&*self.opt_level, "2" | "3") + } +}
diff --git /dev/null b/mupdf-sys/mupdf @@ -0,0 +1 @@ +Subproject commit 3d40818e511eaf8c49b92d8d3d4dc05fe76ab0f0
diff --git /dev/null b/mupdf-sys/src/lib.rs @@ -0,0 +1,39 @@ +#![no_std] +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] +#![allow(clippy::all)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); + +#[inline] +pub unsafe fn fz_new_context( + alloc: *const fz_alloc_context, + locks: *const fz_locks_context, + max_store: usize, +) -> *mut fz_context { + let version = FZ_VERSION.as_ptr() as *const i8; + fz_new_context_imp(alloc, locks, max_store, version) +} + +/// Wrapper for [`fz_new_context`]. +pub unsafe fn mupdf_new_context() -> *mut fz_context { + use core::ptr; + + let ctx = fz_new_context( + ptr::null(), + ptr::null(), + FZ_STORE_DEFAULT as usize, + ); + if ctx.is_null() { return ctx; } + + // SAFETY: this should really be wrapped with fz_try, but it can only fail if + // ctx does not contain a document handler list (?) or the list is + // already full. seems safe to assume fz_new_documment will handle + // us a sane ctx + fz_register_document_handlers(ctx); + + fz_set_warning_callback(ctx, None, ptr::null_mut()); + fz_set_error_callback(ctx, None, ptr::null_mut()); + ctx +}
diff --git /dev/null b/mupdf-sys/src/wrapper.c @@ -0,0 +1,89 @@ +#include <stdlib.h> +#include <stdint.h> + +#include <mupdf/fitz.h> +#include <mupdf/pdf.h> + +typedef struct { + const char* err_msg; + enum fz_error_type type; +} mupdf_result_t; + +fz_document* mupdf_open_doc_from_bytes(fz_context* ctx, + uint8_t* bytes, size_t size, + mupdf_result_t* result) +{ + fz_document* doc = NULL; + fz_stream* stream = NULL; + + memset(result, 0, sizeof *result); + + fz_try(ctx) + { + fz_buffer buf = {0}; + buf.data = bytes; + buf.cap = size; + buf.len = size; + + stream = fz_open_buffer(ctx, &buf); + doc = (fz_document*)pdf_open_document_with_stream(ctx, stream); + } + fz_always(ctx) + { + fz_drop_stream(ctx, stream); + } + fz_catch(ctx) + { + result->err_msg = fz_caught_message(ctx); + result->type = fz_caught(ctx); + doc = NULL; + } + + return doc; +} + +fz_buffer* mupdf_page_to_svg(fz_context* ctx, fz_document* doc, + size_t page_number, mupdf_result_t* result) +{ + fz_buffer* buf = NULL; + fz_page* page = NULL; + fz_output* out = NULL; + fz_device* dev = NULL; + + memset(result, 0, sizeof *result); + + fz_try(ctx) + { + // TODO: assert page != NULL + // TODO: page bound checks when running in debug mode? + page = fz_load_page(ctx, doc, page_number); + + fz_rect bbox = fz_bound_page(ctx, page); + float width = bbox.x1 - bbox.x0, + height = bbox.y1 - bbox.y0; + + buf = fz_new_buffer(ctx, 1024); // TODO: assert buf != NULL + out = fz_new_output_with_buffer(ctx, buf); // TODO: assert out != NULL + + // TODO: assert dev != NULL + dev = fz_new_svg_device(ctx, out, width, height, FZ_SVG_TEXT_AS_PATH, 0); + + fz_run_page(ctx, page, dev, fz_identity, NULL); + fz_close_device(ctx, dev); + fz_close_output(ctx, out); + } + fz_always(ctx) + { + fz_drop_device(ctx, dev); + fz_drop_output(ctx, out); + fz_drop_page(ctx, page); + } + fz_catch(ctx) + { + result->err_msg = fz_caught_message(ctx); + result->type = fz_caught(ctx); + buf = NULL; + } + + return buf; +}
diff --git a/src/outro.html b/src/outro.html @@ -19,7 +19,7 @@ instructions on how to include a given picture in your documents. <section id="contributing"> <h2>contributing</h2> <p> -As of now, this gallery is run by <a href="https://https://www.math.univ-toulouse.fr/~tbrevide/">Thiago Brevidelli</a>. If +As of now, this gallery is run by <a href="https://www.math.univ-toulouse.fr/~tbrevide/">Thiago Brevidelli</a>. If you would like to add your drawings to here please contact <a href="mailto:thiago.brevidelli_garcia@math.univ-toulouse.fr">thiago.brevidelli_garcia@math.univ-toulouse.fr</a>. </p>