From c5187d862019942594c385cb8ce49104efb0c6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Mekina?= Date: Wed, 26 Nov 2025 23:45:50 +0100 Subject: [PATCH] add support for multi-page pdfs from stag --- .gitignore | 1 + Makefile | 18 ++++++++- flake.lock | 95 ++++++++++++++++++++++++++++++++++++++++++++--- flake.nix | 37 ++++++------------ template/pdf.typ | 97 +----------------------------------------------- 5 files changed, 120 insertions(+), 128 deletions(-) diff --git a/.gitignore b/.gitignore index 6db2869..1371298 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ /pack /result /target +/template/lib/ diff --git a/Makefile b/Makefile index af423d2..65134de 100644 --- a/Makefile +++ b/Makefile @@ -3,14 +3,17 @@ PACKS_ROOT := $(BUILD_DIR)/pack PACKDIR := $(PACKS_ROOT)/tultemplate2 BUNDLEDIR := $(PACKS_ROOT)/bundle -TO_PACK := $(shell find template -type f) template/LICENSE +LIB_MUCHPDFTOOLS := lib.typ much_pdf_tools.wasm +LIBS := $(LIB_MUCHPDFTOOLS:%=much_pdf_tools/%) + +TO_PACK := $(shell find template -type f) template/LICENSE $(LIBS:%=template/lib/%) BUNDLE_THESES := bp_cs bp_en dp_cs dp_en prj_cs prj_en sp_cs sp_en BUNDLE_TARGETS := $(TO_PACK:%=$(BUNDLEDIR)/%) $(BUNDLEDIR)/citations.bib $(BUNDLEDIR)/bp_cs.typ \ $(BUNDLE_THESES:%=$(BUNDLEDIR)/%.typ) $(BUNDLEDIR)/Makefile PACK_TARGETS := $(TO_PACK:%=$(PACKDIR)/%) $(PACKDIR)/documentation.typ \ $(PACKDIR)/documentation.pdf $(PACKDIR)/citations.bib $(PACKDIR)/Makefile -TEMPLATE_SRCS := $(shell find template -type f) +TEMPLATE_SRCS := $(shell find template -type f) $(LIBS:%=template/lib/%) # == MAIN TARGETS == @@ -41,6 +44,7 @@ documentation: $(BUILD_DIR)/documentation.pdf .PHONY: clean clean: rm -rf target + rm -rf template/lib # == ROOTS == @@ -70,6 +74,16 @@ define replace_with_file_line sed "s/$(1)/$$(sed '$(3)q;d' $(2))/g" endef +# == LIBS == + +template/lib: + mkdir -p $@ + +$(LIB_MUCHPDFTOOLS:%=template/lib/much_pdf_tools/%): | template/lib + cd template/lib && wget "https://tulsablona.zumepro.cz/lib/much_pdf_tools.tar.gz" && \ + tar -xvf much_pdf_tools.tar.gz && \ + rm much_pdf_tools.tar.gz + # == DOCUMENTATION == $(BUILD_DIR)/documentation.pdf: documentation.typ $(TEMPLATE_SRCS) | $(BUILD_DIR) diff --git a/flake.lock b/flake.lock index 9b02674..de31504 100644 --- a/flake.lock +++ b/flake.lock @@ -1,12 +1,81 @@ { "nodes": { + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1743550720, + "narHash": "sha256-hIshGgKZCgWh6AYJpJmRgFdR3WUbkY04o82X05xqQiY=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "c621e8422220273271f52058f618c94e405bb0f5", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "much_pdf_tools": { + "inputs": { + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs", + "systems": "systems" + }, + "locked": { + "lastModified": 1764192868, + "narHash": "sha256-0h4Fqg3C+VMS2+gvJUKKEjJJDWEeRcMCFkXerl9kPc0=", + "ref": "refs/heads/master", + "rev": "bf65c77e3b0e02b6f22bd20840277c603cd9c2bf", + "revCount": 1, + "type": "git", + "url": "https://git.zumepro.cz/ondrej.mekina/much_pdf_tools.git" + }, + "original": { + "type": "git", + "url": "https://git.zumepro.cz/ondrej.mekina/much_pdf_tools.git" + } + }, "nixpkgs": { "locked": { - "lastModified": 1762482733, - "narHash": "sha256-g/da4FzvckvbiZT075Sb1/YDNDr+tGQgh4N8i5ceYMg=", + "lastModified": 1745804731, + "narHash": "sha256-v/sK3AS0QKu/Tu5sHIfddiEHCvrbNYPv8X10Fpux68g=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "e1ebeec86b771e9d387dd02d82ffdc77ac753abc", + "rev": "29335f23bea5e34228349ea739f31ee79e267b88", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1743296961, + "narHash": "sha256-b1EdN3cULCqtorQ4QeWgLMrd5ZGOjLSLemfa00heasc=", + "owner": "nix-community", + "repo": "nixpkgs.lib", + "rev": "e4822aea2a6d1cdd36653c134cacfd64c97ff4fa", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nixpkgs.lib", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1764138170, + "narHash": "sha256-2bCmfCUZyi2yj9FFXYKwsDiaZmizN75cLhI/eWmf3tk=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "bb813de6d2241bcb1b5af2d3059f560c66329967", "type": "github" }, "original": { @@ -18,7 +87,8 @@ }, "root": { "inputs": { - "nixpkgs": "nixpkgs", + "much_pdf_tools": "much_pdf_tools", + "nixpkgs": "nixpkgs_2", "utils": "utils" } }, @@ -37,9 +107,24 @@ "type": "github" } }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "utils": { "inputs": { - "systems": "systems" + "systems": "systems_2" }, "locked": { "lastModified": 1731533236, diff --git a/flake.nix b/flake.nix index dcf58be..8672a86 100644 --- a/flake.nix +++ b/flake.nix @@ -2,16 +2,24 @@ inputs = { nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; utils.url = "github:numtide/flake-utils"; + much_pdf_tools.url = "git+https://git.zumepro.cz/ondrej.mekina/much_pdf_tools.git"; }; - outputs = { self, nixpkgs, utils }: + outputs = { self, nixpkgs, utils, much_pdf_tools }: utils.lib.eachDefaultSystem (system: let pkgs = import nixpkgs { inherit system; }; - buildInputs = with pkgs; [ typst gnumake jq xdg-utils zip ]; + buildInputs = with pkgs; [ typst gnumake jq xdg-utils zip wget ]; name = "tultemplate2"; envSetup = '' unset SOURCE_DATE_EPOCH + LIB=template/lib + if [ ! -e $LIB ]; then mkdir $LIB; fi + if [ ! -e $LIB/much_pdf_tools ] + then + cp -R ${much_pdf_tools.packages.${system}.much_pdf_tools} $LIB/much_pdf_tools + chmod +w $LIB/much_pdf_tools + fi ''; build_with_target = target: buildOutput: pkgs.stdenv.mkDerivation { inherit buildInputs target buildOutput; @@ -33,29 +41,6 @@ inherit buildInputs; shellHook = envSetup; }; - } // ( - with pkgs; - let - merge = buildInputs: name: stdenv.mkDerivation { - inherit name buildInputs; - dontUnpack = true; - installPhase = '' - mkdir $out - for input in $buildInputs - do - cp -R $input/. $out - done - ''; - }; - documentation = build "documentation.pdf"; - in - { - packages.documentation = documentation; - packages.default = merge [documentation] name; - packages.pack = build_with_target "pack" (builtins.map (v: "target/pack/" + v) [ - "tultemplate2" "tultemplate2.zip" - ]); - } - ) + } ); } diff --git a/template/pdf.typ b/template/pdf.typ index 20a5997..b1471be 100644 --- a/template/pdf.typ +++ b/template/pdf.typ @@ -1,99 +1,6 @@ -#let generate_jumptable(pat) = { - let jt = (); - let matched = 0; - for (idx, item) in pat.enumerate() { - jt.push(matched); - if idx != 0 and item == pat.at(matched) { - matched += 1; - } else { - matched = 0; - } - } - jt -} - -#let advance(pat, jt, ptr, cur) = { - if ptr >= pat.len() { - return ptr; - } - if pat.at(ptr) == cur { - ptr + 1 - } else { - if ptr == 0 { - 0 - } else { - advance(pat, jt, jt.at(ptr), cur) - } - } -} - -#let find(pat, jt, haystack, start_from: 0) = { - let ptr = 0; - for idx in range(start_from, haystack.len()) { - let item = haystack.at(idx); - ptr = advance(pat, jt, ptr, item); - if ptr >= pat.len() { - return idx; - } - } - none -} - -#let match(pat, haystack, start_at) = { - if start_at + pat.len() > haystack.len() { - return false; - } - for idx in range(start_at, start_at + pat.len()) { - if haystack.at(idx) != pat.at(idx - start_at) { - return false; - } - } - true -} - -#let number_of_pages(haystack) = { - let pat_type = (47, 84, 121, 112, 101); // "/Type" - let pat_page = (47, 80, 97, 103, 101); // "/Page" - let space = 32; // " " - let ascii_chars = ( - 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, - 117, 118, 119, 120, 121, 122, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, - 81, 82, 83, 84, 85, 86, 87, 88, 89, 90 - ); - let jt_type = generate_jumptable(pat_type); - - let skip_spaces(haystack, start_at) = { - for idx in range(start_at, haystack.len()) { - let item = haystack.at(idx); - if item != space { - return idx; - } - } - none - } - - let res = 0; - let ptr = 0; - while ptr < haystack.len() and type(ptr) != type(none) { - ptr = find(pat_type, jt_type, haystack, start_from: ptr); - if type(ptr) == type(none) { break; } - // Matched: "/Type" - ptr = skip_spaces(haystack, ptr + 1); - if type(ptr) == type(none) { break; } - // Matched: "/Type", spaces - if not match(pat_page, haystack, ptr) { continue; } - ptr += pat_page.len(); - // Matched: "/Type", spaces, "/Page" - if haystack.at(ptr) in ascii_chars { continue; } - // Matched: "/Type", spaces, "/Page", word_end - res += 1; - } - - res -} - #let embed_full(src) = { - let page_count = number_of_pages(src); + import "./lib/much_pdf_tools/lib.typ": pdf_count_pages; + let page_count = pdf_count_pages(src); for idx in range(0, page_count) { page( place(center + horizon, image(src, page: idx + 1, fit: "contain", width: auto, height: auto)),