From b8a2d5003a0cf95ddfb7c197377ae818739dd3a8 Mon Sep 17 00:00:00 2001 From: Wei Lin Date: Wed, 9 Sep 2026 00:15:08 +0800 Subject: [PATCH] Consolidate MiniPdf contribution guidance --- .agents/skills/minipdf-contribution/SKILL.md | 22 - .claude/commands/minipdf-contribution.md | 17 - .claude/skills/minipdf-contribution/SKILL.md | 102 +++ .cursor/commands/minipdf-contribution.md | 16 - .github/skills/create-release/SKILL.md | 163 ----- .../skill-minipdf-contribution/SKILL.md | 194 ----- .../assets/pull-request-template.md | 54 -- .../scripts/contribution-loop.ps1 | 660 ------------------ .../scripts/preflight.ps1 | 153 ---- .../scripts/resolve-implementation.ps1 | 29 - .../scripts/select-candidates.ps1 | 142 ---- .../skills/sync-readme-translations/SKILL.md | 36 - AGENTS.md | 85 +-- CONTRIBUTING.md | 75 +- README.md | 2 +- documents/README.fr.md | 2 +- documents/README.it.md | 2 +- documents/README.ja.md | 2 +- documents/README.ko.md | 2 +- documents/README.zh-CN.md | 2 +- documents/README.zh-TW.md | 2 +- scripts/Invoke-MiniPdfContributionLoop.ps1 | 38 - .../reports_docx/comparison_report.md | 505 ++++++-------- 23 files changed, 354 insertions(+), 1951 deletions(-) delete mode 100644 .agents/skills/minipdf-contribution/SKILL.md delete mode 100644 .claude/commands/minipdf-contribution.md create mode 100644 .claude/skills/minipdf-contribution/SKILL.md delete mode 100644 .cursor/commands/minipdf-contribution.md delete mode 100644 .github/skills/create-release/SKILL.md delete mode 100644 .github/skills/skill-minipdf-contribution/SKILL.md delete mode 100644 .github/skills/skill-minipdf-contribution/assets/pull-request-template.md delete mode 100644 .github/skills/skill-minipdf-contribution/scripts/contribution-loop.ps1 delete mode 100644 .github/skills/skill-minipdf-contribution/scripts/preflight.ps1 delete mode 100644 .github/skills/skill-minipdf-contribution/scripts/resolve-implementation.ps1 delete mode 100644 .github/skills/skill-minipdf-contribution/scripts/select-candidates.ps1 delete mode 100644 .github/skills/sync-readme-translations/SKILL.md delete mode 100644 scripts/Invoke-MiniPdfContributionLoop.ps1 diff --git a/.agents/skills/minipdf-contribution/SKILL.md b/.agents/skills/minipdf-contribution/SKILL.md deleted file mode 100644 index dd4db979..00000000 --- a/.agents/skills/minipdf-contribution/SKILL.md +++ /dev/null @@ -1,22 +0,0 @@ ---- -name: minipdf-contribution -description: "Explicitly run the vendor-neutral MiniPdf rendering contribution loop for one selected benchmark case. Ask the user to choose dotnet or rust before running commands when no implementation is specified." -argument-hint: "Choose dotnet or rust; optionally request up to two candidates" -disable-model-invocation: true ---- - -# MiniPdf Contribution Loop - -Read `CONTRIBUTING.md`, especially "Automated Rendering Improvement", and -execute `scripts/Invoke-MiniPdfContributionLoop.ps1` as the source of truth. - -If the invocation does not specify `dotnet` or `rust`, ask the user which -implementation to use and wait for an explicit answer before running any -command. Then pass the choice with `-Implementation dotnet` or -`-Implementation rust`; do not use `auto` or rely on a default. The workflow -selects one candidate unless `-CandidateCount 2` is explicitly requested. Then -carry the workflow through focused Begin/Evaluate -attempts, Validate, and Pr. Make one evidence-driven root-cause change per -attempt. Preserve unrelated changes. Never commit, push, fork, or open a pull -request without explicit user approval. Keep images and full logs in artifacts; -return only paths and compact summaries, never base64 payloads, in chat. \ No newline at end of file diff --git a/.claude/commands/minipdf-contribution.md b/.claude/commands/minipdf-contribution.md deleted file mode 100644 index c950acc7..00000000 --- a/.claude/commands/minipdf-contribution.md +++ /dev/null @@ -1,17 +0,0 @@ ---- -description: Run the automated MiniPdf rendering contribution loop for .NET or Rust -argument-hint: "[dotnet|rust]" ---- - -Read `CONTRIBUTING.md`, especially "Automated Rendering Improvement". -Requested implementation: `$ARGUMENTS` - -Use the requested implementation from the command arguments. When omitted, ask -the user to choose `dotnet` or `rust` and wait for an explicit answer before -running any command. Pass that choice explicitly; do not use `auto`. Execute the vendor-neutral -`scripts/Invoke-MiniPdfContributionLoop.ps1` workflow end to end: Start, up to -three Begin/Evaluate attempts for the single selected case, Validate, and Pr. -Diagnose and fix root causes between Begin and Evaluate. Keep images and full -logs in artifacts and return only paths and compact summaries. Preserve -unrelated changes, and do not commit, push, fork, or create the pull request -without explicit approval. \ No newline at end of file diff --git a/.claude/skills/minipdf-contribution/SKILL.md b/.claude/skills/minipdf-contribution/SKILL.md new file mode 100644 index 00000000..36632a86 --- /dev/null +++ b/.claude/skills/minipdf-contribution/SKILL.md @@ -0,0 +1,102 @@ +--- +name: minipdf-contribution +description: "Guide a first-time MiniPdf contributor through choosing an implementation language, selecting a failing visual benchmark case, making a focused rendering fix, and validating the complete benchmark. Use when contributing rendering improvements to MiniPdf in .NET, Rust, Java, Go, Python, or Node.js." +compatibility: "Designed for Claude Code, Codex, and GitHub Copilot. Requires Git, PowerShell, the selected language toolchain, Python benchmark dependencies, an installed LibreOffice executable, and a LibreOffice core source checkout." +--- + +# MiniPdf Contribution + +Complete one evidence-based rendering improvement from diagnosis through pull request preparation. Read `CONTRIBUTING.md` before changing code. Preserve unrelated work and do not commit, push, fork, or create a pull request without explicit user approval. + +Do not use emoji in responses, newly authored documentation, commit messages, or pull request content. + +## 1. Prepare LibreOffice and Its Source + +For a first-time contributor, both of these prerequisites are mandatory. Check them before selecting work: + +1. Confirm that a LibreOffice executable is available. Check `soffice` on `PATH` and common installation locations for the current operating system. A source checkout does not replace an installed LibreOffice executable. +2. Look for an existing LibreOffice core source checkout in a nearby development directory, including a sibling such as `../libreoffice-core`. + +If the source checkout is missing, tell the user it is required for renderer-behavior research and ask them to approve or choose the clone destination. Suggest a sibling directory such as `../libreoffice-core`. After the user confirms the location, clone `https://github.com/LibreOffice/core.git` there. Do not continue the contribution workflow without this checkout, and do not build or modify LibreOffice unless the user requests it. + +If the LibreOffice executable is missing, explain that cloning the source does not install LibreOffice and ask the user how they want to install or provide the executable. Do not run visual benchmarks until both the executable and source checkout are available. + +## 2. Choose an Implementation + +Ask which implementation language the user wants to improve and wait for an explicit answer: + +| Choice | Repository path | Benchmark command | +|---|---|---| +| .NET | `src/` | `scripts/Run-DotNet-VisualBenchmark.ps1` | +| Rust | `minipdf-rs/` | `scripts/Run-Rust-VisualBenchmark.ps1` | +| Java | `minipdf-java/` | `scripts/Run-Java-VisualBenchmark.ps1` | +| Go | `minipdf-go/` | `scripts/Run-Go-VisualBenchmark.ps1` | +| Python | `minipdf-python/` | `scripts/Run-Python-VisualBenchmark.ps1` | +| Node.js | `minipdf-node/` | `scripts/Run-Node-VisualBenchmark.ps1` | + +Do not choose a language on the user's behalf. + +## 3. Select a Failing Case + +Find the available reports for the selected language under: + +```text +artifacts/-benchmark///report/comparison_report.md +``` + +`` is `dotnet`, `rust`, `java`, `go`, `python`, or `node`; `` is `classic` or `issue`; and `` is `xlsx`, `docx`, or `pptx`. For example: + +```text +artifacts/java-benchmark/issue/xlsx/report/comparison_report.md +``` + +Inspect the report data and select one valid case whose overall score is below `0.95`. Prefer the lowest reproducible score across the available reports for that language. Exclude missing or invalid candidate/reference PDFs. If no current report exists for a suite and format, generate it with the corresponding full benchmark command before selecting a case. + +Record the selected language, suite, format, case name, overall score, visual average, text similarity, page counts, and relevant comparison image paths. Preserve a copy of the complete baseline report data under `artifacts/` for the final regression comparison. + +## 4. Diagnose and Improve + +1. Open the candidate, Microsoft 365 reference, LibreOffice auxiliary reference, side-by-side images, and heatmaps for the weakest pages. +2. Classify the mismatch before editing: page setup, scaling, font metrics, text layout, borders, fills, drawings, headers or footers, or pagination. +3. Use the local LibreOffice core source checkout to research the relevant rendering behavior and expected semantics before editing. Record the relevant source files and symbols in the contribution evidence. +4. Locate the selected implementation code that directly controls the mismatch. +5. State one falsifiable root-cause hypothesis and one focused check that can disprove it. +6. Add or update a focused automated test, make the smallest root-cause fix, and run that test. +7. Re-run the selected case with `-Filter ""`. Keep the change only if the intended visual result improves without a known regression. + +Do not optimize only for the similarity formula. The output must remain a valid PDF and match the document semantics. + +## 5. Run the Complete Visual Benchmark + +After the focused case improves, run the same language, suite, and format again without `-Filter` and without `-MaxCases`. This final run must regenerate every candidate PDF and all report images for that benchmark scope. + +Compare the complete final report with the preserved baseline. Reject or revise the change if any unrelated case has an unexpected page-count change, an invalid or missing PDF, missing comparison images, a `visual_avg` decrease greater than `0.002`, or an obvious broad visual regression. Review the final average and the distribution of score changes; a gain in one case must not hide widespread losses. + +Run the selected implementation's relevant unit tests and `git diff --check` after the full benchmark. Report the exact commands, before-and-after scores, page counts, image paths, and any remaining risk. + +## Cross-Language Visual Benchmarks + +Use the following form for .NET, Rust, Java, Go, Python, and Node: + +```powershell +scripts/Run--VisualBenchmark.ps1 -Suite -Format -MaxCases +``` + +Supported commands: + +```powershell +scripts/Run-DotNet-VisualBenchmark.ps1 -Suite -Format -MaxCases +scripts/Run-Rust-VisualBenchmark.ps1 -Suite -Format -MaxCases +scripts/Run-Java-VisualBenchmark.ps1 -Suite -Format -MaxCases +scripts/Run-Go-VisualBenchmark.ps1 -Suite -Format -MaxCases +scripts/Run-Python-VisualBenchmark.ps1 -Suite -Format -MaxCases +scripts/Run-Node-VisualBenchmark.ps1 -Suite -Format -MaxCases +``` + +During diagnosis, add `-Filter ""` for a focused run. For final validation, omit both `-Filter` and `-MaxCases`: + +```powershell +scripts/Run--VisualBenchmark.ps1 -Suite -Format +``` + +Use only suite and format combinations supported by the benchmark runner. Keep generated PDFs, images, heatmaps, and full logs under `artifacts/`; summarize results in chat instead of embedding image data or complete reports. \ No newline at end of file diff --git a/.cursor/commands/minipdf-contribution.md b/.cursor/commands/minipdf-contribution.md deleted file mode 100644 index e50b535e..00000000 --- a/.cursor/commands/minipdf-contribution.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -description: Run the automated MiniPdf rendering contribution loop for .NET or Rust ---- - -Read `CONTRIBUTING.md`, especially "Automated Rendering Improvement". -Requested implementation: `$ARGUMENTS` - -Infer `dotnet` or `rust` from the command arguments. When omitted, ask the user -to choose `dotnet` or `rust` and wait for an explicit answer before running any -command. Pass that choice explicitly; do not use `auto`. Execute -`scripts/Invoke-MiniPdfContributionLoop.ps1` through -Start, Begin/Evaluate (at most three attempts for the single selected case), -Validate, and Pr. Diagnose and fix the root cause between Begin and Evaluate. -Keep images and full logs in artifacts and return only paths and compact -summaries. Preserve unrelated changes, and do not commit, push, fork, or create -the pull request without explicit approval. \ No newline at end of file diff --git a/.github/skills/create-release/SKILL.md b/.github/skills/create-release/SKILL.md deleted file mode 100644 index 0a2337d2..00000000 --- a/.github/skills/create-release/SKILL.md +++ /dev/null @@ -1,163 +0,0 @@ ---- -name: create-release -description: "Create a new GitHub release for MiniPdf with CLI and GUI AOT binaries. Use when: publishing a release, creating a new version, bumping version, tagging a release, generating release notes from git diff." -argument-hint: "Optionally specify the new version number (e.g. v0.27.0)" ---- - -# Create MiniPdf Release - -Automate the full release cycle: compute version, commit, tag, generate release notes from git diff, and publish via `gh release create` so that CLI + GUI AOT workflows build and upload binaries automatically. - -## Prerequisites - -- All changes committed and pushed to `upstream main` -- `gh` CLI authenticated with repo write access -- Repository remote: `upstream` -> `mini-software/MiniPdf` - -## Version Convention - -| Component | Tag pattern | Trigger workflow | -|-----------|-------------|-----------------| -| Library + CLI + GUI | `v*` (e.g. `v0.26.0`) | `release-aot.yml` (CLI) + `release-aot-gui.yml` (GUI) + `nuget-publish.yml` | - -Version bumps follow `v{major}.{minor}.{patch}` — default increment is `+0.1.0` on the minor. - -## Procedure - -### 1. Determine Version - -```powershell -git tag --sort=-v:refname | Select-Object -First 1 -``` - -Parse the last tag, increment minor by 1, e.g. `v0.25.0` -> `v0.26.0`. -If user specifies a version, use that instead. - -### 2. Ensure Changes Are Committed and Pushed - -```powershell -git status --short -# If uncommitted changes exist, stage and commit them first -git push upstream main -``` - -### 3. Generate Diff Summary - -```powershell -git log {LAST_TAG}..HEAD --oneline -git diff {LAST_TAG} HEAD --stat -``` - -### 4. Create Tag - -```powershell -git tag {NEW_TAG} -git push upstream {NEW_TAG} -``` - -### 5. Create Release via `gh` - -Write release notes to a temp file, then create: - -```powershell -gh release create {NEW_TAG} --repo mini-software/MiniPdf --title "{NEW_TAG}" --notes-file release-notes.tmp -Remove-Item release-notes.tmp -Force -``` - -**IMPORTANT**: Do NOT use PowerShell here-strings (`@"..."@`) for multi-line notes -- they have escaping issues in the terminal. Instead, use `create_file` to write the notes file, then pass `--notes-file`. - -### 6. Verify Workflows Triggered - -```powershell -Start-Sleep 5 -gh run list --repo mini-software/MiniPdf --limit 4 --json name,status -q ".[] | {name,status}" -``` - -Expect to see: -- `Release AOT Binaries` — builds CLI for 6 platforms -- `Release GUI AOT Binaries` — builds GUI for 6 platforms -- `NuGet Publish` — publishes library to NuGet - -## Release Notes Template - -Use this structure for the release body (write via `create_file` to a temp file): - -```markdown -## What's New in {NEW_TAG} - -### Highlights - -- **Feature A** -- description -- **Fix B** -- description - -### Downloads - -| Platform | CLI | GUI | -|----------|-----|-----| -| Windows x64 | `minipdf-win-x64.zip` | `minipdf-gui-win-x64.zip` | -| Windows ARM64 | `minipdf-win-arm64.zip` | `minipdf-gui-win-arm64.zip` | -| Linux x64 | `minipdf-linux-x64.tar.gz` | `minipdf-gui-linux-x64.tar.gz` | -| Linux ARM64 | `minipdf-linux-arm64.tar.gz` | `minipdf-gui-linux-arm64.tar.gz` | -| macOS x64 | `minipdf-osx-x64.tar.gz` | `minipdf-gui-osx-x64.tar.gz` | -| macOS ARM64 | `minipdf-osx-arm64.tar.gz` | `minipdf-gui-osx-arm64.tar.gz` | - -### Changes since {LAST_TAG} - -{GIT_LOG_ONELINE} - -``` -{GIT_DIFF_STAT} -``` - -**Full Changelog**: https://github.com/mini-software/MiniPdf/compare/{LAST_TAG}...{NEW_TAG} -``` - -## Release Assets (auto-built) - -The following assets are built and uploaded automatically by GitHub Actions: - -### CLI (`release-aot.yml`) -| Artifact | Platform | -|----------|----------| -| `minipdf-win-x64.zip` | Windows x64 | -| `minipdf-win-arm64.zip` | Windows ARM64 | -| `minipdf-linux-x64.tar.gz` | Linux x64 | -| `minipdf-linux-arm64.tar.gz` | Linux ARM64 | -| `minipdf-osx-x64.tar.gz` | macOS x64 | -| `minipdf-osx-arm64.tar.gz` | macOS ARM64 | - -### GUI (`release-aot-gui.yml`) -| Artifact | Platform | Contents | -|----------|----------|----------| -| `minipdf-gui-win-x64.zip` | Windows x64 | exe + native DLLs | -| `minipdf-gui-win-arm64.zip` | Windows ARM64 | exe + native DLLs | -| `minipdf-gui-linux-x64.tar.gz` | Linux x64 | binary + .so libs | -| `minipdf-gui-linux-arm64.tar.gz` | Linux ARM64 | binary + .so libs | -| `minipdf-gui-osx-x64.tar.gz` | macOS x64 | binary + .dylib libs | -| `minipdf-gui-osx-arm64.tar.gz` | macOS ARM64 | binary + .dylib libs | - -**GUI packages must include native libraries** (libSkiaSharp, libHarfBuzzSharp, av_libglesv2) alongside the executable. The exe alone will not launch. - -## Fixing a Bad Release - -If a release needs to be recreated (e.g. workflow bug, wrong packaging): - -```powershell -# Delete remote release and tag -gh release delete {TAG} --repo mini-software/MiniPdf --yes --cleanup-tag - -# Delete and recreate local tag on latest commit -git tag -d {TAG} -git tag {TAG} -git push upstream {TAG} - -# Recreate release -gh release create {TAG} --repo mini-software/MiniPdf --title "{TAG}" --notes-file release-notes.tmp -``` - -## Rules - -- Never use `--force` on tags that other people may have fetched -- delete and recreate instead. -- Always verify both CLI and GUI workflows are triggered after creating a release. -- The system cannot auto `git push` per project policy -- always push explicitly. -- Clean up temp files (`release-notes.tmp`) after release creation. diff --git a/.github/skills/skill-minipdf-contribution/SKILL.md b/.github/skills/skill-minipdf-contribution/SKILL.md deleted file mode 100644 index 33472a8f..00000000 --- a/.github/skills/skill-minipdf-contribution/SKILL.md +++ /dev/null @@ -1,194 +0,0 @@ ---- -name: skill-minipdf-contribution -description: "Explicitly run the MiniPdf benchmark contribution loop for one selected rendering case. Ask the user to choose .NET or Rust before running commands when the invocation does not specify an implementation." -argument-hint: "Choose .NET or Rust; optionally request up to two candidates" -user-invocable: true -disable-model-invocation: true ---- - -# MiniPdf Contribution Loop - -Turn local compute time into a focused, reproducible MiniPdf rendering improvement for either implementation. This Copilot skill is one adapter for the vendor-neutral workflow in `CONTRIBUTING.md`; Claude Code, Cursor, Codex, and terminal agents use the same controller. Invoke `/skill-minipdf-contribution` explicitly. If the invocation does not specify .NET or Rust, ask the user which implementation to use and wait for an explicit answer before running preflight or any other command. Then pass the choice explicitly and select one candidate unless the user requests two. - -## Automatic Path - -Run this once from the repository root: - -```powershell -$loop = ".\scripts\Invoke-MiniPdfContributionLoop.ps1" -& $loop -Action Start -Implementation -``` - -Run `Start` only after the user chooses an implementation, and pass either `-Implementation dotnet` or `-Implementation rust`. The controller rejects `auto` and omitted implementations for `Start`. `Start` requires a clean working tree, runs implementation-specific preflight, installs benchmark Python packages, creates a local `improve/-visual-parity-*` branch, builds fresh isolated XLSX/DOCX baselines for the current HEAD, selects one document with the lowest page-level visual score for that renderer, and stores the choice in `.git/minipdf-contribution-loop/`. Pass `-CandidateCount 2` only when the user explicitly requests a two-case run. - -For Rust, `Start` first builds fresh isolated XLSX and DOCX baseline reports from the shared classic corpus. The current Rust benchmark uses Microsoft 365 as the primary scored reference and LibreOffice as an auxiliary reference. It therefore requires Cargo, desktop Excel and Word, Python, and LibreOffice. The .NET path requires the .NET SDK, Python, and LibreOffice. - -For the returned candidate: - -```powershell -& $loop -Action Begin -Format -CaseName -# Diagnose, add a focused test, and make one small root-cause edit. -& $loop -Action Evaluate -``` - -`Begin` regenerates a fresh LibreOffice reference, records the baseline, and creates a Git tree checkpoint without changing branch history. `Evaluate` reruns the focused candidate and accepts only when `overall_score` improves by at least `0.0001` and `visual_avg` does not decrease. Otherwise it restores the checkpoint automatically. Try at most three times for that candidate; after the third rejection its status becomes `skipped`, so continue with the other selected candidate. - -After one or more candidates are accepted, run: - -```powershell -& $loop -Action Validate -& $loop -Action Pr -``` - -`Validate` runs `dotnet test` for .NET or `cargo test --workspace` for Rust, then regenerates every XLSX and DOCX candidate against the exact references captured at `Start`. It rejects missing/invalid cases, page-count changes, or any `visual_avg` drop greater than `0.002`. Text extraction can vary with installed fonts, so full-suite `overall_score` changes are reported but are not the image-regression gate. `Pr` includes the implementation in its evidence, reports whether authenticated GitHub CLI is available, and prints ready push/`gh pr create` commands or browser steps. After explicit approval, commit and push the branch, then use `-Action Pr -CreatePullRequest` to open the PR automatically. - -If all candidates are skipped, do not create a PR. Report the measured attempts for each case and end the run. A later run can use refreshed benchmark reports to select the next lowest case. - -## Safety and Scope - -- Work from the MiniPdf repository root. -- Start only from a clean working tree; this is what makes automatic rollback safe. -- Support XLSX and DOCX for both .NET and Rust; PPTX remains .NET-only and is outside this loop. -- Use LibreOffice as the .NET rendering reference. The Rust benchmark currently scores Microsoft 365 as primary and displays LibreOffice as auxiliary. -- Never commit, push, create a fork, or open a PR without explicit user approval. -- Do not treat a stale report as proof of an improvement. Re-run the focused benchmark with a fresh reference. -- Keep generated PDFs, images, heatmaps, and full command output in `artifacts/`; report only paths, compact summaries, and the last relevant error lines in chat. -- Never embed image data, base64 payloads, complete benchmark reports, or complete build logs in chat. -- Start a fresh chat before `Validate` when the current prompt context approaches 150,000 tokens or contains large image/tool outputs. The persisted state under `.git/minipdf-contribution-loop/` supports resuming safely. - -## 1. Run Preflight - -Run: - -```powershell -.\.github\skills\skill-minipdf-contribution\scripts\preflight.ps1 -``` - -The script checks Git, GitHub CLI authentication, Python, LibreOffice, and local LibreOffice/POI source trees. Pass `-Implementation dotnet` to require the .NET SDK, or `-Implementation rust` to require Cargo plus desktop Excel and Word. - -Git, Python, and LibreOffice are shared requirements. The selected implementation additionally requires either the .NET SDK or Rust/Cargo; Rust visual baselines also require desktop Excel and Word. GitHub CLI and source trees are optional but strongly recommended. If a source tree is missing, give clone instructions; do not download it automatically. - -Use `-Json` when another tool or agent needs structured results. - -## 2. Select Work - -Run: - -```powershell -.\.github\skills\skill-minipdf-contribution\scripts\select-candidates.ps1 -``` - -Defaults: - -- Reports: `tests/MiniPdf.Benchmark/reports/comparison_report.json` and `tests/MiniPdf.Benchmark/reports_docx/comparison_report.json` -- Candidate count: 1 -- High-score threshold: `0.95` - -The selector chooses each document's lowest comparable visual-score page, then ranks distinct documents by page visual score, visual average, and overall score. Invalid PDFs and incomplete report rows are excluded. Use `-Json` for structured output or override inputs with `-ReportPath`, `-Count`, and `-HighScoreThreshold`. - -If the mode is `improve-existing`, attempt the returned cases in order. One case is the default work budget. For a direct selector run, pass `-Count 2` only when two cases were explicitly requested. When using `contribution-loop.ps1`, pass `-CandidateCount 2` instead. Do not force unrelated changes into one PR. - -If the mode is `create-new`, follow section 5. - -## 3. Reproduce a Candidate Manually - -The controller's `Begin` action performs this step automatically. Use these commands only for diagnosis or recovery. - -Re-run one case at a time with a fresh LibreOffice reference and heatmaps: - -```powershell -# XLSX -.\scripts\Run-Benchmark.ps1 -SkipInstall -SkipGenerate -Engine libre -Filter "" -ForceReference -Heatmaps ` - -MiniPdfDir "artifacts\skill-minipdf-contribution\xlsx\minipdf" ` - -ReferenceDir "artifacts\skill-minipdf-contribution\xlsx\reference" ` - -ReportDir "artifacts\skill-minipdf-contribution\xlsx\report" - -# DOCX -.\scripts\Run-Benchmark_docx.ps1 -SkipInstall -SkipGenerate -Engine libre -Filter "" -ForceReference -Heatmaps ` - -MiniPdfDir "artifacts\skill-minipdf-contribution\docx\minipdf" ` - -ReferenceDir "artifacts\skill-minipdf-contribution\docx\reference" ` - -ReportDir "artifacts\skill-minipdf-contribution\docx\report" - -# Rust XLSX or DOCX -.\scripts\Run-Rust-Benchmark.ps1 -Suite classic -Format -Filter "" ` - -MinimumScore 0 -ForceReference -``` - -Record the baseline `overall_score`, `visual_avg`, lowest page score, page counts, and heatmap paths. Confirm both PDFs exist and the reference was regenerated rather than skipped. - -Open the candidate, reference, and heatmap images for the lowest-scoring page. Classify the mismatch before editing: page setup, scale, font metrics, text layout, borders/fills, images/drawings, headers/footers, or pagination. - -## 4. Diagnose and Fix - -1. Search the local LibreOffice source for renderer behavior and POI source for OOXML interpretation. -2. Keep source searches narrow and save reusable findings outside the chat context. -3. Locate the nearest selected implementation code that directly computes the mismatched output (`src/MiniPdf` or `minipdf-rs`). -4. State one falsifiable local hypothesis and identify a focused check. -5. Add or update a focused unit test, then make the smallest root-cause fix. -6. Re-run the focused test and the same benchmark command. -7. Keep a change only when the evidence improves the intended behavior without creating a known regression. - -If two candidates share one root cause, validate both and keep them together. If they need unrelated fixes, complete one coherent change and recommend a separate PR for the other. - -## 5. Add a New Validation Document - -Use this path only when all valid report entries meet the high-score threshold. - -1. Inspect `tests/MiniPdf.Scripts/generate_classic_xlsx.py` and `generate_classic_docx.py`. -2. Search existing generator names, document content, and tests for the proposed Office behavior. -3. Choose the next available format-specific case number and a descriptive unique name. -4. Add a deterministic generator with fixed content, dimensions, styles, and metadata. Do not use random values, current timestamps, network assets, or machine-specific paths. -5. Generate at most two new documents, then benchmark each against a fresh LibreOffice reference. -6. Prefer fixing the lower-scoring new case. Keep the generated source document and generator change aligned with repository conventions. - -Good additions isolate one previously uncovered behavior. Avoid documents that merely vary text or colors already represented by the corpus. - -## 6. Validate - -The controller's `Validate` action is the required final gate. During an attempt, run the narrowest relevant test first. For manual recovery, the minimum checks are: - -```powershell -# .NET -dotnet test tests/MiniPdf.Tests -c Release - -# Rust -cargo test --manifest-path minipdf-rs/Cargo.toml --workspace - -git diff --check -git status --short -``` - -For every changed benchmark case, report before/after scores, page counts, and visual evidence. Do not claim improvement based only on unit tests or only on an overall average. A PR is permitted only when the controller records `ValidationApproved: true`. - -## 7. Prepare the Pull Request - -Use [the PR evidence template](./assets/pull-request-template.md). Include the root cause, why the behavior matches LibreOffice/OOXML semantics, tests, and before/after benchmark evidence. - -Check PR tooling at execution time: - -```powershell -git --version -gh --version -gh auth status -``` - -When `gh` is installed and authenticated, the controller can execute this command after the user approves commit and push: - -```powershell -gh pr create --repo mini-software/MiniPdf --base main --head : --title "" --body-file <body-file> -``` - -Equivalent controller command: - -```powershell -& $loop -Action Pr -CreatePullRequest -``` - -When `gh` is unavailable or unauthenticated, provide these exact manual steps: - -1. Fork `https://github.com/mini-software/MiniPdf` in the browser. -2. Add the fork as a remote and push the approved branch: `git push -u <fork-remote> <branch>`. -3. Open `https://github.com/mini-software/MiniPdf/compare/main...<fork-owner>:<branch>?expand=1`. -4. Paste the prepared title and PR body, review the changed files, and submit. - -Never publish benchmark artifacts from `artifacts/`; use tracked canonical report locations only when the contribution intentionally updates them. \ No newline at end of file diff --git a/.github/skills/skill-minipdf-contribution/assets/pull-request-template.md b/.github/skills/skill-minipdf-contribution/assets/pull-request-template.md deleted file mode 100644 index 1146af4c..00000000 --- a/.github/skills/skill-minipdf-contribution/assets/pull-request-template.md +++ /dev/null @@ -1,54 +0,0 @@ -## Summary - -- Describe the rendering behavior that changed. -- Explain the root cause and why the new behavior matches LibreOffice or OOXML semantics. - -## Benchmark Evidence - -| Case | Page | Before overall | After overall | Before visual | After visual | -| --- | ---: | ---: | ---: | ---: | ---: | -| `<case>` | `<page>` | `<score>` | `<score>` | `<score>` | `<score>` | - -- Candidate image: `<tracked path or attached image>` -- LibreOffice reference: `<tracked path or attached image>` -- Heatmap: `<tracked path or attached image>` -- Page counts: MiniPdf `<count>`, LibreOffice `<count>` - -## Reference Research - -- LibreOffice: `<source-relative file and finding>` -- Apache POI / OOXML: `<source-relative file and finding>` - -## Validation - -- [ ] Focused unit test -- [ ] Focused benchmark with a fresh LibreOffice reference -- [ ] `dotnet test tests/MiniPdf.Tests` -- [ ] Full XLSX/DOCX visual regression gate -- [ ] `git diff --check`## Summary - -- Describe the rendering behavior that changed. -- Explain the root cause and why the new behavior matches LibreOffice or OOXML semantics. - -## Benchmark Evidence - -| Case | Page | Before overall | After overall | Before visual | After visual | -| --- | ---: | ---: | ---: | ---: | ---: | -| `<case>` | `<page>` | `<score>` | `<score>` | `<score>` | `<score>` | - -- Candidate image: `<tracked path or attached image>` -- LibreOffice reference: `<tracked path or attached image>` -- Heatmap: `<tracked path or attached image>` -- Page counts: MiniPdf `<count>`, LibreOffice `<count>` - -## Reference Research - -- LibreOffice: `<source-relative file and finding>` -- Apache POI / OOXML: `<source-relative file and finding>` - -## Validation - -- [ ] Focused unit test -- [ ] Focused benchmark with a fresh LibreOffice reference -- [ ] `dotnet test tests/MiniPdf.Tests` -- [ ] `git diff --check` \ No newline at end of file diff --git a/.github/skills/skill-minipdf-contribution/scripts/contribution-loop.ps1 b/.github/skills/skill-minipdf-contribution/scripts/contribution-loop.ps1 deleted file mode 100644 index e0697020..00000000 --- a/.github/skills/skill-minipdf-contribution/scripts/contribution-loop.ps1 +++ /dev/null @@ -1,660 +0,0 @@ -[CmdletBinding()] -param( - [Parameter(Mandatory)] - [ValidateSet("Start", "Begin", "Evaluate", "Validate", "Pr", "Status")] - [string]$Action, - [ValidateSet("auto", "dotnet", "rust")] - [string]$Implementation = "auto", - [ValidateRange(1, 2)] - [int]$CandidateCount = 1, - [ValidateSet("xlsx", "docx")] - [string]$Format, - [string]$CaseName, - [ValidateRange(0.0, 1.0)] - [double]$MinimumImprovement = 0.0001, - [ValidateRange(0.0, 1.0)] - [double]$RegressionTolerance = 0.002, - [string]$Title, - [switch]$CreatePullRequest, - [switch]$Json -) - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version Latest - -$repositoryRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\..\..")) -$gitDirectory = (& git -C $repositoryRoot rev-parse --absolute-git-dir).Trim() -if ($LASTEXITCODE -ne 0) { throw "The MiniPdf repository could not be resolved." } -$stateDirectory = Join-Path $gitDirectory "minipdf-contribution-loop" -$statePath = Join-Path $stateDirectory "state.json" -$artifactRoot = Join-Path $repositoryRoot "artifacts\skill-minipdf-contribution" - -function Save-State($State) { - New-Item -ItemType Directory -Force -Path $stateDirectory | Out-Null - $State.UpdatedAt = (Get-Date).ToUniversalTime().ToString("o") - $State | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $statePath -Encoding utf8 -} - -function Get-State { - if (-not (Test-Path -LiteralPath $statePath -PathType Leaf)) { - throw "No contribution loop is active. Run with -Action Start first." - } - return Get-Content -LiteralPath $statePath -Raw | ConvertFrom-Json -} - -function Write-Result($Value) { - if ($Json) { - $Value | ConvertTo-Json -Depth 10 - } else { - $Value | Format-List | Out-Host - } -} - -function Invoke-CheckedCommand([scriptblock]$Command, [string]$FailureMessage) { - & $Command - if ($LASTEXITCODE -ne 0) { throw $FailureMessage } -} - -function Resolve-Python { - $repositoryPython = @( - (Join-Path $repositoryRoot ".venv\Scripts\python.exe"), - (Join-Path $repositoryRoot ".venv/bin/python") - ) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 - if ($repositoryPython) { - $env:PATH = "$(Split-Path -Parent $repositoryPython);$env:PATH" - return $repositoryPython - } - # Skip LibreOffice's bundled interpreter: it resolves as "python" when - # LibreOffice's program directory is on PATH, but its restricted embedded - # runtime cannot spawn subprocesses (needed by the benchmark scripts). - $command = Get-Command python, python3 -All -ErrorAction SilentlyContinue | - Where-Object { $_.Source -notmatch "LibreOffice" } | - Select-Object -First 1 - if (-not $command) { throw "Python 3.10+ is required." } - return $command.Source -} - -function Get-CaseScore([string]$ReportPath, [string]$ExpectedName) { - if (-not (Test-Path -LiteralPath $ReportPath -PathType Leaf)) { - throw "Benchmark report was not generated: $ReportPath" - } - $data = Get-Content -LiteralPath $ReportPath -Raw | ConvertFrom-Json - $entries = if ($data -is [System.Array]) { - @($data) - } elseif ($data.PSObject.Properties.Name -contains "results") { - @($data.results) - } else { - @($data) - } - $entry = @($entries | Where-Object { $_.name -eq $ExpectedName }) | Select-Object -First 1 - if (-not $entry -or -not $entry.pdf_valid -or $null -eq $entry.overall_score -or $null -eq $entry.visual_avg) { - throw "Case '$ExpectedName' has no valid comparable score in $ReportPath" - } - return [pscustomobject]@{ - Name = [string]$entry.name - OverallScore = [double]$entry.overall_score - VisualAverage = [double]$entry.visual_avg - MiniPdfPages = [int]$entry.minipdf_pages - ReferencePages = [int]$entry.reference_pages - ReportPath = $ReportPath - } -} - -function Ensure-SourceDocument([string]$DocumentFormat, [string]$DocumentPath) { - if (Test-Path -LiteralPath $DocumentPath -PathType Leaf) { return } - $python = Resolve-Python - $generator = if ($DocumentFormat -eq "xlsx") { - Join-Path $repositoryRoot "tests\MiniPdf.Scripts\generate_classic_xlsx.py" - } else { - Join-Path $repositoryRoot "tests\MiniPdf.Scripts\generate_classic_docx.py" - } - Invoke-CheckedCommand { & $python $generator } "Failed to generate the $DocumentFormat benchmark corpus." - if (-not (Test-Path -LiteralPath $DocumentPath -PathType Leaf)) { - throw "The selected source document was not generated: $DocumentPath" - } -} - -function Get-SourceDirectory([string]$DocumentFormat) { - if ($DocumentFormat -eq "xlsx") { return Join-Path $repositoryRoot "tests\MiniPdf.Scripts\output" } - return Join-Path $repositoryRoot "tests\MiniPdf.Scripts\output_docx" -} - -function Ensure-SourceCorpus([string]$DocumentFormat) { - $sourceDirectory = Get-SourceDirectory $DocumentFormat - if (@(Get-ChildItem -LiteralPath $sourceDirectory -Filter "*.$DocumentFormat" -File -ErrorAction SilentlyContinue).Count -gt 0) { - return $sourceDirectory - } - $python = Resolve-Python - $generator = if ($DocumentFormat -eq "xlsx") { - Join-Path $repositoryRoot "tests\MiniPdf.Scripts\generate_classic_xlsx.py" - } else { - Join-Path $repositoryRoot "tests\MiniPdf.Scripts\generate_classic_docx.py" - } - Invoke-CheckedCommand { & $python $generator } "Failed to generate the $DocumentFormat benchmark corpus." - return $sourceDirectory -} - -function Get-RustReportPath([string]$DocumentFormat) { - return Join-Path $artifactRoot "rust-baseline\$DocumentFormat\report\comparison_report.json" -} - -function Get-DotNetReportPath([string]$DocumentFormat) { - return Join-Path $artifactRoot "dotnet-baseline\$DocumentFormat\report\comparison_report.json" -} - -function Ensure-DotNetBaselineReport([string]$DocumentFormat) { - $reportPath = Get-DotNetReportPath $DocumentFormat - $null = Ensure-SourceCorpus $DocumentFormat - New-Item -ItemType Directory -Force -Path $artifactRoot | Out-Null - $baselineRoot = Join-Path $artifactRoot "dotnet-baseline\$DocumentFormat" - if (Test-Path -LiteralPath $baselineRoot) { Remove-Item -LiteralPath $baselineRoot -Recurse -Force } - $logPath = Join-Path $artifactRoot "dotnet-baseline-$DocumentFormat.log" - $runner = if ($DocumentFormat -eq "xlsx") { - Join-Path $repositoryRoot "scripts\Run-Benchmark.ps1" - } else { - Join-Path $repositoryRoot "scripts\Run-Benchmark_docx.ps1" - } - & $runner -SkipInstall -SkipGenerate -Engine libre -ForceReference -NoOpen ` - -PythonPath (Resolve-Python) ` - -MiniPdfDir (Join-Path $baselineRoot "candidate") ` - -ReferenceDir (Join-Path $baselineRoot "reference") ` - -ReportDir (Join-Path $baselineRoot "report") *> $logPath - if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $reportPath -PathType Leaf)) { - Get-Content -LiteralPath $logPath -Tail 40 | Write-Host - throw ".NET $DocumentFormat baseline generation failed. Full log: $logPath" - } - return $reportPath -} - -function Ensure-RustBaselineReport([string]$DocumentFormat) { - $reportPath = Get-RustReportPath $DocumentFormat - $null = Ensure-SourceCorpus $DocumentFormat - New-Item -ItemType Directory -Force -Path $artifactRoot | Out-Null - $baselineRoot = Join-Path $artifactRoot "rust-baseline\$DocumentFormat" - if (Test-Path -LiteralPath $baselineRoot) { Remove-Item -LiteralPath $baselineRoot -Recurse -Force } - $logPath = Join-Path $artifactRoot "rust-baseline-$DocumentFormat.log" - & (Join-Path $repositoryRoot "scripts\Run-Rust-Benchmark.ps1") ` - -Suite classic -Format $DocumentFormat -MinimumScore 0 -ForceReference ` - -CandidateDir (Join-Path $baselineRoot "candidate") ` - -ReferenceDir (Join-Path $baselineRoot "reference") ` - -AuxiliaryReferenceDir (Join-Path $baselineRoot "auxiliary-reference") ` - -ReportDir (Join-Path $baselineRoot "report") *> $logPath - if ($LASTEXITCODE -ne 0 -or -not (Test-Path -LiteralPath $reportPath -PathType Leaf)) { - Get-Content -LiteralPath $logPath -Tail 40 | Write-Host - throw "Rust $DocumentFormat baseline generation failed. Full log: $logPath" - } - return $reportPath -} - -function Invoke-FocusedBenchmark( - [string]$Renderer, - [string]$DocumentFormat, - [string]$DocumentName, - [bool]$FreshReference, - [string]$LogPath -) { - New-Item -ItemType Directory -Force -Path (Split-Path -Parent $LogPath) | Out-Null - $formatRoot = Join-Path $artifactRoot "$Renderer\$DocumentFormat" - $reportDirectory = Join-Path $formatRoot "report" - if (Test-Path -LiteralPath $reportDirectory) { - Remove-Item -LiteralPath $reportDirectory -Recurse -Force - } - if ($Renderer -eq "rust") { - $referenceDirectory = Join-Path $formatRoot "reference" - $auxiliaryDirectory = Join-Path $formatRoot "auxiliary-reference" - if ($FreshReference) { - Remove-Item -LiteralPath (Join-Path $referenceDirectory "$DocumentName.pdf") -Force -ErrorAction SilentlyContinue - Remove-Item -LiteralPath (Join-Path $auxiliaryDirectory "$DocumentName.pdf") -Force -ErrorAction SilentlyContinue - } - $arguments = @{ - Suite = "classic" - Format = $DocumentFormat - Filter = $DocumentName - MinimumScore = 0 - CandidateDir = (Join-Path $formatRoot "candidate") - ReferenceDir = $referenceDirectory - AuxiliaryReferenceDir = $auxiliaryDirectory - ReportDir = $reportDirectory - } - if ($FreshReference) { $arguments.ForceReference = $true } else { $arguments.SkipReference = $true } - & (Join-Path $repositoryRoot "scripts\Run-Rust-Benchmark.ps1") @arguments *> $LogPath - } else { - if ($FreshReference) { - $referencePdf = Join-Path (Join-Path $formatRoot "reference") "$DocumentName.pdf" - Remove-Item -LiteralPath $referencePdf -Force -ErrorAction SilentlyContinue - } - $runner = if ($DocumentFormat -eq "xlsx") { - Join-Path $repositoryRoot "scripts\Run-Benchmark.ps1" - } else { - Join-Path $repositoryRoot "scripts\Run-Benchmark_docx.ps1" - } - $arguments = @{ - SkipInstall = $true - SkipGenerate = $true - Engine = "libre" - Filter = $DocumentName - MiniPdfDir = (Join-Path $formatRoot "minipdf") - ReferenceDir = (Join-Path $formatRoot "reference") - ReportDir = $reportDirectory - Heatmaps = $true - NoOpen = $true - PythonPath = (Resolve-Python) - } - if ($FreshReference) { $arguments.ForceReference = $true } else { $arguments.SkipReference = $true } - & $runner @arguments *> $LogPath - } - if ($LASTEXITCODE -ne 0) { - Get-Content -LiteralPath $LogPath -Tail 40 | Write-Host - throw "The focused $Renderer $DocumentFormat benchmark failed. Full log: $LogPath" - } - return Get-CaseScore (Join-Path $reportDirectory "comparison_report.json") $DocumentName -} - -function New-CheckpointTree { - New-Item -ItemType Directory -Force -Path $stateDirectory | Out-Null - $temporaryIndex = Join-Path $stateDirectory "checkpoint.index" - $actualIndex = (& git -C $repositoryRoot rev-parse --git-path index).Trim() - if (-not [System.IO.Path]::IsPathRooted($actualIndex)) { $actualIndex = Join-Path $repositoryRoot $actualIndex } - Copy-Item -LiteralPath $actualIndex -Destination $temporaryIndex -Force - $previousIndex = $env:GIT_INDEX_FILE - try { - $env:GIT_INDEX_FILE = $temporaryIndex - & git -C $repositoryRoot add -A - if ($LASTEXITCODE -ne 0) { throw "Could not stage the temporary checkpoint index." } - $tree = (& git -C $repositoryRoot write-tree).Trim() - if ($LASTEXITCODE -ne 0 -or -not $tree) { throw "Could not create the attempt checkpoint." } - return $tree - } finally { - $env:GIT_INDEX_FILE = $previousIndex - Remove-Item -LiteralPath $temporaryIndex -Force -ErrorAction SilentlyContinue - } -} - -function Restore-Checkpoint([string]$Tree) { - $untracked = @(& git -C $repositoryRoot ls-files --others --exclude-standard) - foreach ($relativePath in $untracked) { - $fullPath = Join-Path $repositoryRoot $relativePath - if (Test-Path -LiteralPath $fullPath) { Remove-Item -LiteralPath $fullPath -Recurse -Force } - } - & git -C $repositoryRoot read-tree --reset -u $Tree - if ($LASTEXITCODE -ne 0) { throw "Could not restore checkpoint tree $Tree." } - & git -C $repositoryRoot reset --mixed HEAD | Out-Null - if ($LASTEXITCODE -ne 0) { throw "Could not restore the normal Git index after rollback." } -} - -function Compare-Reports( - [string]$BeforePath, - [string]$AfterPath, - [double]$Tolerance, - [string]$DocumentFormat, - [string]$SourceDirectory -) { - $before = @(Get-Content -LiteralPath $BeforePath -Raw | ConvertFrom-Json) - $after = @(Get-Content -LiteralPath $AfterPath -Raw | ConvertFrom-Json) - $beforeByName = @{} - foreach ($entry in $before) { - if ($entry.name -and $null -ne $entry.visual_avg) { $beforeByName[[string]$entry.name] = $entry } - } - $afterGroups = @($after | Where-Object { $_.name } | Group-Object name) - $afterByName = @{} - foreach ($group in $afterGroups) { - if ($group.Count -eq 1) { $afterByName[[string]$group.Name] = $group.Group[0] } - } - $expectedNames = @(Get-ChildItem -LiteralPath $SourceDirectory -Filter "*.$DocumentFormat" -File | ForEach-Object BaseName) - if ($expectedNames.Count -eq 0) { throw "No $DocumentFormat source documents were found in $SourceDirectory" } - $regressions = @() - foreach ($name in $expectedNames) { - $duplicate = @($afterGroups | Where-Object { $_.Name -eq $name -and $_.Count -ne 1 }) | Select-Object -First 1 - if ($duplicate) { - $regressions += [pscustomobject]@{ Format = $DocumentFormat; Name = $name; Metric = "duplicate"; Delta = $null } - continue - } - if (-not $afterByName.ContainsKey($name)) { - $regressions += [pscustomobject]@{ Format = $DocumentFormat; Name = $name; Metric = "missing"; Delta = $null } - continue - } - $current = $afterByName[$name] - if (-not $current.pdf_valid -or $null -eq $current.visual_avg) { - $regressions += [pscustomobject]@{ Format = $DocumentFormat; Name = $name; Metric = "invalid"; Delta = $null } - continue - } - if (-not $beforeByName.ContainsKey($name)) { continue } - $entry = $beforeByName[$name] - if ([int]$current.minipdf_pages -ne [int]$entry.minipdf_pages) { - $regressions += [pscustomobject]@{ - Format = $DocumentFormat - Name = $name - Metric = "page_count" - Before = [int]$entry.minipdf_pages - After = [int]$current.minipdf_pages - Delta = [int]$current.minipdf_pages - [int]$entry.minipdf_pages - } - } - $delta = [double]$current.visual_avg - [double]$entry.visual_avg - if ($delta -lt -$Tolerance) { - $regressions += [pscustomobject]@{ - Format = $DocumentFormat - Name = $name - Metric = "visual_avg" - Before = [double]$entry.visual_avg - After = [double]$current.visual_avg - Delta = $delta - } - } - } - return @($regressions) -} - -$null = Resolve-Python - -switch ($Action) { - "Start" { - if ($Implementation -eq "auto") { - throw "Start requires -Implementation dotnet or -Implementation rust." - } - $status = @(& git -C $repositoryRoot status --short --untracked-files=all) - if ($status.Count -gt 0) { - throw "Start requires a clean working tree so failed attempts can be restored without losing user work." - } - $Implementation = & (Join-Path $PSScriptRoot "resolve-implementation.ps1") -Implementation $Implementation - $preflightJson = & (Join-Path $PSScriptRoot "preflight.ps1") -Implementation $Implementation -Json - $preflight = ($preflightJson | Out-String) | ConvertFrom-Json - if (-not $preflight.Ready) { throw "Preflight failed. Follow the reported setup guidance and run Start again." } - $python = Resolve-Python - $pythonPackages = @("openpyxl", "pymupdf", "Pillow", "python-docx") - if ($Implementation -eq "rust") { $pythonPackages += "pywin32" } - Invoke-CheckedCommand { - & $python -m pip install @pythonPackages --quiet - } "Could not install benchmark Python dependencies." - - $branch = (& git -C $repositoryRoot branch --show-current).Trim() - if ($branch -eq "main" -or $branch -eq "master") { - $branch = "improve/$Implementation-visual-parity-$(Get-Date -Format 'yyyyMMdd-HHmmss')" - & git -C $repositoryRoot switch -c $branch - if ($LASTEXITCODE -ne 0) { throw "Could not create contribution branch $branch." } - } - - $reportPaths = if ($Implementation -eq "rust") { - @((Ensure-RustBaselineReport "xlsx"), (Ensure-RustBaselineReport "docx")) - } else { - @((Ensure-DotNetBaselineReport "xlsx"), (Ensure-DotNetBaselineReport "docx")) - } - $selectionJson = & (Join-Path $PSScriptRoot "select-candidates.ps1") -ReportPath $reportPaths -Count $CandidateCount -Json - if ($LASTEXITCODE -ne 0) { throw "Candidate selection failed." } - $selection = ($selectionJson | Out-String) | ConvertFrom-Json - New-Item -ItemType Directory -Force -Path $stateDirectory | Out-Null - $baselineDirectory = Join-Path $stateDirectory "baseline" - New-Item -ItemType Directory -Force -Path $baselineDirectory | Out-Null - Copy-Item $reportPaths[0] (Join-Path $baselineDirectory "xlsx.json") -Force - Copy-Item $reportPaths[1] (Join-Path $baselineDirectory "docx.json") -Force - $candidates = @($selection.Candidates | ForEach-Object { - [pscustomobject]@{ - Format = $_.Format - Name = $_.Name - SourceDocument = $_.SourceDocument - Attempts = 0 - Status = "pending" - } - }) - $state = [pscustomobject]@{ - Version = 2 - Implementation = $Implementation - Branch = $branch - Mode = $selection.Mode - CandidateCount = $CandidateCount - MaximumAttempts = 3 - Candidates = $candidates - ActiveAttempt = $null - Evidence = @() - AttemptHistory = @() - ValidationApproved = $false - ValidatedTree = $null - Regressions = @() - UpdatedAt = $null - } - Save-State $state - Write-Result $state - } - "Begin" { - if (-not $Format -or -not $CaseName) { throw "Begin requires -Format and -CaseName." } - $state = Get-State - if ($state.ActiveAttempt) { throw "Evaluate or roll back the active attempt before beginning another one." } - $candidate = @($state.Candidates | Where-Object { $_.Format -eq $Format -and $_.Name -eq $CaseName }) | Select-Object -First 1 - if (-not $candidate) { throw "'$Format/$CaseName' is not one of the selected candidates." } - if ($candidate.Status -ne "pending") { throw "'$CaseName' is already $($candidate.Status) and cannot be reopened." } - if ([int]$candidate.Attempts -ge [int]$state.MaximumAttempts) { throw "'$CaseName' already used all three attempts." } - Ensure-SourceDocument $Format $candidate.SourceDocument - $attemptNumber = [int]$candidate.Attempts + 1 - $baselineLogPath = Join-Path $artifactRoot "$($state.Implementation)-$Format-$CaseName-attempt-$attemptNumber-baseline.log" - $baseline = Invoke-FocusedBenchmark $state.Implementation $Format $CaseName $true $baselineLogPath - $candidate.Attempts = $attemptNumber - $candidate.Status = "attempting" - $state.ActiveAttempt = [pscustomobject]@{ - Implementation = $state.Implementation - Format = $Format - Name = $CaseName - Number = $candidate.Attempts - CheckpointTree = New-CheckpointTree - Baseline = $baseline - } - Save-State $state - Write-Result $state.ActiveAttempt - } - "Evaluate" { - $state = Get-State - if (-not $state.ActiveAttempt) { throw "No attempt is active. Run Begin before Evaluate." } - $attempt = $state.ActiveAttempt - $candidate = @($state.Candidates | Where-Object { $_.Format -eq $attempt.Format -and $_.Name -eq $attempt.Name }) | Select-Object -First 1 - try { - $evaluationLogPath = Join-Path $artifactRoot "$($state.Implementation)-$($attempt.Format)-$($attempt.Name)-attempt-$($attempt.Number)-evaluation.log" - $after = Invoke-FocusedBenchmark $state.Implementation $attempt.Format $attempt.Name $false $evaluationLogPath - } catch { - $failureMessage = $_.Exception.Message - Restore-Checkpoint $attempt.CheckpointTree - $candidate.Status = if ([int]$candidate.Attempts -ge [int]$state.MaximumAttempts) { "skipped" } else { "pending" } - $failureResult = [pscustomobject]@{ - Implementation = $state.Implementation - Format = $attempt.Format - Name = $attempt.Name - Attempt = $attempt.Number - Accepted = $false - Error = $failureMessage - Before = $attempt.Baseline - After = $null - } - $state.AttemptHistory = @($state.AttemptHistory) + $failureResult - $state.ActiveAttempt = $null - $state.ValidationApproved = $false - $state.ValidatedTree = $null - Save-State $state - throw "Focused attempt failed and was rolled back: $failureMessage" - } - $overallDelta = $after.OverallScore - [double]$attempt.Baseline.OverallScore - $visualDelta = $after.VisualAverage - [double]$attempt.Baseline.VisualAverage - $accepted = $overallDelta -ge $MinimumImprovement -and $visualDelta -ge 0 - $result = [pscustomobject]@{ - Implementation = $state.Implementation - Format = $attempt.Format - Name = $attempt.Name - Attempt = $attempt.Number - Accepted = $accepted - OverallDelta = $overallDelta - VisualDelta = $visualDelta - Before = $attempt.Baseline - After = $after - } - if ($accepted) { - $candidate.Status = "accepted" - $state.Evidence = @($state.Evidence) + $result - } else { - Restore-Checkpoint $attempt.CheckpointTree - $candidate.Status = if ([int]$candidate.Attempts -ge [int]$state.MaximumAttempts) { "skipped" } else { "pending" } - } - $state.AttemptHistory = @($state.AttemptHistory) + $result - $state.ActiveAttempt = $null - $state.ValidationApproved = $false - $state.ValidatedTree = $null - Save-State $state - Write-Result $result - } - "Validate" { - $state = Get-State - if ($state.ActiveAttempt) { throw "Evaluate the active attempt before full validation." } - if ($state.Implementation -eq "rust") { - $cargo = Get-Command cargo -ErrorAction SilentlyContinue | Select-Object -First 1 - $cargoPath = if ($cargo) { $cargo.Source } else { Join-Path $env:USERPROFILE ".cargo\bin\cargo.exe" } - Invoke-CheckedCommand { - & $cargoPath test --manifest-path (Join-Path $repositoryRoot "minipdf-rs\Cargo.toml") --workspace - } "The Rust workspace test suite failed." - } else { - Invoke-CheckedCommand { - & dotnet test (Join-Path $repositoryRoot "tests\MiniPdf.Tests") -c Release - } "The MiniPdf .NET test suite failed." - } - - $validationRoot = Join-Path $artifactRoot "full-validation\$($state.Implementation)" - $xlsxOutput = Join-Path $validationRoot "xlsx" - $docxOutput = Join-Path $validationRoot "docx" - foreach ($outputDirectory in @($xlsxOutput, $docxOutput)) { - if (Test-Path -LiteralPath $outputDirectory) { - Remove-Item -LiteralPath $outputDirectory -Recurse -Force - } - New-Item -ItemType Directory -Force -Path $outputDirectory | Out-Null - } - $xlsxLog = Join-Path $xlsxOutput "benchmark.log" - $docxLog = Join-Path $docxOutput "benchmark.log" - if ($state.Implementation -eq "rust") { - $rustBaselineRoot = Join-Path $artifactRoot "rust-baseline" - & (Join-Path $repositoryRoot "scripts\Run-Rust-Benchmark.ps1") ` - -Suite classic -Format xlsx -MinimumScore 0 -SkipReference ` - -CandidateDir (Join-Path $xlsxOutput "candidate") ` - -ReferenceDir (Join-Path $rustBaselineRoot "xlsx\reference") ` - -AuxiliaryReferenceDir (Join-Path $rustBaselineRoot "xlsx\auxiliary-reference") ` - -ReportDir (Join-Path $xlsxOutput "report") *> $xlsxLog - } else { - $dotnetBaselineRoot = Join-Path $artifactRoot "dotnet-baseline" - & (Join-Path $repositoryRoot "scripts\Run-Benchmark.ps1") -SkipInstall -SkipGenerate -SkipReference -Engine libre -NoOpen ` - -PythonPath (Resolve-Python) ` - -MiniPdfDir (Join-Path $xlsxOutput "minipdf") ` - -ReferenceDir (Join-Path $dotnetBaselineRoot "xlsx\reference") ` - -ReportDir (Join-Path $xlsxOutput "report") *> $xlsxLog - } - if ($LASTEXITCODE -ne 0) { - Get-Content -LiteralPath $xlsxLog -Tail 40 | Write-Host - throw "The full $($state.Implementation) XLSX benchmark failed. Full log: $xlsxLog" - } - if ($state.Implementation -eq "rust") { - & (Join-Path $repositoryRoot "scripts\Run-Rust-Benchmark.ps1") ` - -Suite classic -Format docx -MinimumScore 0 -SkipReference ` - -CandidateDir (Join-Path $docxOutput "candidate") ` - -ReferenceDir (Join-Path $rustBaselineRoot "docx\reference") ` - -AuxiliaryReferenceDir (Join-Path $rustBaselineRoot "docx\auxiliary-reference") ` - -ReportDir (Join-Path $docxOutput "report") *> $docxLog - } else { - & (Join-Path $repositoryRoot "scripts\Run-Benchmark_docx.ps1") -SkipInstall -SkipGenerate -SkipReference -Engine libre -NoOpen ` - -PythonPath (Resolve-Python) ` - -MiniPdfDir (Join-Path $docxOutput "minipdf") ` - -ReferenceDir (Join-Path $dotnetBaselineRoot "docx\reference") ` - -ReportDir (Join-Path $docxOutput "report") *> $docxLog - } - if ($LASTEXITCODE -ne 0) { - Get-Content -LiteralPath $docxLog -Tail 40 | Write-Host - throw "The full $($state.Implementation) DOCX benchmark failed. Full log: $docxLog" - } - - $baselineDirectory = Join-Path $stateDirectory "baseline" - $regressions = @() - $regressions += Compare-Reports (Join-Path $baselineDirectory "xlsx.json") (Join-Path $xlsxOutput "report\comparison_report.json") $RegressionTolerance "xlsx" (Join-Path $repositoryRoot "tests\MiniPdf.Scripts\output") - $regressions += Compare-Reports (Join-Path $baselineDirectory "docx.json") (Join-Path $docxOutput "report\comparison_report.json") $RegressionTolerance "docx" (Join-Path $repositoryRoot "tests\MiniPdf.Scripts\output_docx") - & git -C $repositoryRoot diff --check - if ($LASTEXITCODE -ne 0) { throw "git diff --check failed." } - $state.ValidationApproved = $regressions.Count -eq 0 - $state.ValidatedTree = if ($state.ValidationApproved) { New-CheckpointTree } else { $null } - $state.Regressions = @($regressions) - Save-State $state - if ($regressions.Count -gt 0) { - $regressions | Format-Table Format, Name, Metric, Before, After, Delta -AutoSize | Out-Host - throw "Full validation found $($regressions.Count) visual, page-count, or completeness regression(s)." - } - Write-Result ([pscustomobject]@{ Implementation = $state.Implementation; Approved = $true; RegressionTolerance = $RegressionTolerance; Regressions = @() }) - } - "Pr" { - $state = Get-State - if (-not $state.ValidationApproved) { throw "Run a successful -Action Validate before preparing a pull request." } - $currentTree = New-CheckpointTree - if (-not $state.ValidatedTree -or $currentTree -ne $state.ValidatedTree) { - throw "The working tree changed after validation. Run -Action Validate again." - } - $currentBranch = (& git -C $repositoryRoot branch --show-current).Trim() - if ($currentBranch -ne $state.Branch) { throw "Expected branch '$($state.Branch)', but '$currentBranch' is checked out." } - $accepted = @($state.Evidence | Where-Object { $_.Accepted }) - if ($accepted.Count -eq 0) { throw "No candidate improvement was accepted; a pull request cannot be prepared." } - $caseNames = @($accepted | ForEach-Object { $_.Name }) - if (-not $Title) { $Title = "Improve MiniPdf $($state.Implementation) rendering parity: $($caseNames -join ', ')" } - $bodyPath = Join-Path $artifactRoot "pull-request.md" - New-Item -ItemType Directory -Force -Path $artifactRoot | Out-Null - $rows = @($accepted | ForEach-Object { - "| $($_.Implementation) | $($_.Name) | $($_.Format) | $([double]$_.Before.OverallScore) | $([double]$_.After.OverallScore) | $([double]$_.Before.VisualAverage) | $([double]$_.After.VisualAverage) | $($_.After.MiniPdfPages)/$($_.After.ReferencePages) |" - }) - @( - "## Summary" - "" - "Improves MiniPdf $($state.Implementation) rendering parity for the automatically selected low-score benchmark cases." - "" - "## Benchmark Evidence" - "" - "| Implementation | Case | Format | Before overall | After overall | Before visual | After visual | Pages candidate/reference |" - "| --- | --- | --- | ---: | ---: | ---: | ---: | ---: |" - $rows - "" - "## Validation" - "" - "- [x] Focused $($state.Implementation) benchmarks with fresh references" - "- [x] Full $($state.Implementation) test suite" - "- [x] Full $($state.Implementation) XLSX and DOCX benchmark regression gate" - "- [x] git diff --check" - ) | Set-Content -LiteralPath $bodyPath -Encoding utf8 - - $gh = Get-Command gh -ErrorAction SilentlyContinue | Select-Object -First 1 - $authenticated = $false - if ($gh) { - & $gh.Source auth status *> $null - $authenticated = $LASTEXITCODE -eq 0 - } - if ($CreatePullRequest) { - if (-not $authenticated) { - Write-Host "GitHub CLI is unavailable or unauthenticated. Install it from https://cli.github.com/ and run 'gh auth login', or use the browser steps below." -ForegroundColor Yellow - } else { - $status = @(& git -C $repositoryRoot status --short --untracked-files=all) - if ($status.Count -gt 0) { throw "Commit the validated changes before creating the pull request." } - & git -C $repositoryRoot rev-parse --abbrev-ref --symbolic-full-name "@{upstream}" *> $null - if ($LASTEXITCODE -ne 0) { throw "Push the branch with -u before creating the pull request." } - $unpushed = [int]((& git -C $repositoryRoot rev-list --count "@{upstream}..HEAD").Trim()) - if ($LASTEXITCODE -ne 0 -or $unpushed -ne 0) { throw "Push all branch commits before creating the pull request." } - $login = (& $gh.Source api user --jq .login).Trim() - if ($LASTEXITCODE -ne 0 -or -not $login) { throw "GitHub CLI could not resolve the authenticated login." } - $head = "${login}:$($state.Branch)" - & $gh.Source pr create --repo mini-software/MiniPdf --base main --head $head --title $Title --body-file $bodyPath - if ($LASTEXITCODE -ne 0) { throw "GitHub CLI could not create the pull request. Confirm the branch is committed and pushed." } - break - } - } - $compareUrl = "https://github.com/mini-software/MiniPdf/compare/main...<fork-owner>:$($state.Branch)?expand=1" - Write-Result ([pscustomobject]@{ - PullRequestMode = if ($authenticated) { "github-cli" } else { "browser" } - Title = $Title - BodyFile = $bodyPath - PushCommand = "git push -u <fork-remote> $($state.Branch)" - CliCommand = "gh pr create --repo mini-software/MiniPdf --base main --head <fork-owner>:$($state.Branch) --title `"$Title`" --body-file `"$bodyPath`"" - ManualSteps = @( - "Fork https://github.com/mini-software/MiniPdf", - "Commit the approved changes and push $($state.Branch) to your fork", - "Open $compareUrl", - "Paste the generated title and body, review the files, and submit" - ) - }) - } - "Status" { - Write-Result (Get-State) - } -} \ No newline at end of file diff --git a/.github/skills/skill-minipdf-contribution/scripts/preflight.ps1 b/.github/skills/skill-minipdf-contribution/scripts/preflight.ps1 deleted file mode 100644 index bb6a379b..00000000 --- a/.github/skills/skill-minipdf-contribution/scripts/preflight.ps1 +++ /dev/null @@ -1,153 +0,0 @@ -[CmdletBinding()] -param( - [string]$RepositoryRoot, - [string]$LibreOfficeSourceRoot, - [string]$PoiSourceRoot, - [ValidateSet("dotnet", "rust")] - [string]$Implementation = "dotnet", - [switch]$Json -) - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version Latest - -if (-not $RepositoryRoot) { - $RepositoryRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\..\..")) -} - -function Get-CommandPath([string[]]$Names) { - foreach ($name in $Names) { - $command = Get-Command $name -ErrorAction SilentlyContinue | Select-Object -First 1 - if ($command) { return $command.Source } - } - return $null -} - -function Resolve-LibreOfficePath { - if ($env:LIBREOFFICE_PATH -and (Test-Path -LiteralPath $env:LIBREOFFICE_PATH -PathType Leaf)) { - return (Resolve-Path -LiteralPath $env:LIBREOFFICE_PATH).Path - } - $commandPath = Get-CommandPath @("soffice", "libreoffice") - if ($commandPath) { return $commandPath } - $candidates = @((Join-Path $env:ProgramFiles "LibreOffice\program\soffice.exe")) - if (${env:ProgramFiles(x86)}) { - $candidates += Join-Path ${env:ProgramFiles(x86)} "LibreOffice\program\soffice.exe" - } - foreach ($candidate in $candidates) { - if (Test-Path -LiteralPath $candidate -PathType Leaf) { - return (Resolve-Path -LiteralPath $candidate).Path - } - } - return $null -} - -function Resolve-SourceRoot([string]$ExplicitPath, [string]$EnvironmentPath, [string]$DefaultPath) { - $candidate = if ($ExplicitPath) { $ExplicitPath } elseif ($EnvironmentPath) { $EnvironmentPath } else { $DefaultPath } - if ($candidate -and (Test-Path -LiteralPath $candidate -PathType Container)) { - return (Resolve-Path -LiteralPath $candidate).Path - } - return $candidate -} - -$gitPath = Get-CommandPath @("git") -$dotnetPath = Get-CommandPath @("dotnet") -$dotnetSdks = if ($dotnetPath) { @(& $dotnetPath --list-sdks) } else { @() } -$dotnet9Available = @($dotnetSdks | Where-Object { $_ -match '^9\.' }).Count -gt 0 -$cargoPath = Get-CommandPath @("cargo") -if (-not $cargoPath) { - $cargoCandidate = Join-Path $env:USERPROFILE ".cargo\bin\cargo.exe" - if (Test-Path -LiteralPath $cargoCandidate -PathType Leaf) { $cargoPath = $cargoCandidate } -} -$repositoryPython = @( - (Join-Path $RepositoryRoot ".venv\Scripts\python.exe"), - (Join-Path $RepositoryRoot ".venv/bin/python") -) | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | Select-Object -First 1 -$pythonPath = if ($repositoryPython) { $repositoryPython } else { Get-CommandPath @("python", "python3") } -$pythonVersion = if ($pythonPath) { & $pythonPath -c "import platform; print(platform.python_version())" } else { $null } -$pythonSupported = $false -if ($pythonVersion) { $pythonSupported = [version]$pythonVersion -ge [version]"3.10" } -$ghPath = Get-CommandPath @("gh") -$libreOfficePath = Resolve-LibreOfficePath -$officeAvailable = $false -if ([System.Environment]::OSVersion.Platform -eq [System.PlatformID]::Win32NT) { - try { - $officeAvailable = $null -ne [Type]::GetTypeFromProgID("Excel.Application") -and - $null -ne [Type]::GetTypeFromProgID("Word.Application") - } catch { - $officeAvailable = $false - } -} -$resolvedLibreOfficeSource = Resolve-SourceRoot $LibreOfficeSourceRoot $env:LIBREOFFICE_SOURCE_ROOT "D:\git\libreoffice-core" -$resolvedPoiSource = Resolve-SourceRoot $PoiSourceRoot $env:POI_SOURCE_ROOT "D:\git\poi" - -$ghAuthenticated = $false -if ($ghPath) { - & $ghPath auth status *> $null - $ghAuthenticated = $LASTEXITCODE -eq 0 -} - -$checks = @( - [pscustomobject]@{ - Name = "Git"; Required = $true; Available = [bool]$gitPath; Location = $gitPath - Guidance = if ($gitPath) { $null } else { "Install Git from https://git-scm.com/downloads" } - }, - [pscustomobject]@{ - Name = ".NET 9 SDK"; Required = $Implementation -eq "dotnet"; Available = $dotnet9Available; Location = if ($dotnet9Available) { $dotnetPath } else { $null } - Guidance = if ($dotnet9Available) { $null } else { "Install .NET 9 SDK from https://dotnet.microsoft.com/download" } - }, - [pscustomobject]@{ - Name = "Rust / Cargo"; Required = $Implementation -eq "rust"; Available = [bool]$cargoPath; Location = $cargoPath - Guidance = if ($cargoPath) { $null } else { "Install Rust from https://rustup.rs/" } - }, - [pscustomobject]@{ - Name = "Python 3.10+"; Required = $true; Available = $pythonSupported; Location = if ($pythonVersion) { "$pythonPath ($pythonVersion)" } else { $pythonPath } - Guidance = if ($pythonSupported) { $null } else { "Install Python 3.10+ or create the repository .venv" } - }, - [pscustomobject]@{ - Name = "LibreOffice"; Required = $true; Available = [bool]$libreOfficePath; Location = $libreOfficePath - Guidance = if ($libreOfficePath) { $null } else { "Install LibreOffice or set LIBREOFFICE_PATH to soffice" } - }, - [pscustomobject]@{ - Name = "Microsoft Excel and Word"; Required = $Implementation -eq "rust"; Available = $officeAvailable; Location = if ($officeAvailable) { "COM automation" } else { $null } - Guidance = if ($officeAvailable) { $null } else { "The Rust visual workflow currently requires desktop Microsoft Excel and Word for primary reference PDFs" } - }, - [pscustomobject]@{ - Name = "GitHub CLI"; Required = $false; Available = [bool]$ghPath; Location = $ghPath - Guidance = if ($ghPath) { $null } else { "Optional: install from https://cli.github.com/; browser PR steps will be generated" } - }, - [pscustomobject]@{ - Name = "GitHub CLI authentication"; Required = $false; Available = $ghAuthenticated; Location = $null - Guidance = if ($ghAuthenticated) { $null } elseif ($ghPath) { "Run gh auth login, or use the browser PR workflow" } else { "Install and authenticate gh, or use the browser PR workflow" } - }, - [pscustomobject]@{ - Name = "LibreOffice source"; Required = $false; Available = (Test-Path -LiteralPath $resolvedLibreOfficeSource -PathType Container); Location = $resolvedLibreOfficeSource - Guidance = if (Test-Path -LiteralPath $resolvedLibreOfficeSource -PathType Container) { $null } else { "Clone https://github.com/LibreOffice/core and set LIBREOFFICE_SOURCE_ROOT" } - }, - [pscustomobject]@{ - Name = "Apache POI source"; Required = $false; Available = (Test-Path -LiteralPath $resolvedPoiSource -PathType Container); Location = $resolvedPoiSource - Guidance = if (Test-Path -LiteralPath $resolvedPoiSource -PathType Container) { $null } else { "Clone https://github.com/apache/poi and set POI_SOURCE_ROOT" } - } -) - -$missingRequired = @($checks | Where-Object { $_.Required -and -not $_.Available }) -$result = [pscustomobject]@{ - RepositoryRoot = [System.IO.Path]::GetFullPath($RepositoryRoot) - Implementation = $Implementation - Ready = $missingRequired.Count -eq 0 - PullRequestMode = if ($ghAuthenticated) { "github-cli" } else { "browser" } - Checks = $checks -} - -if ($Json) { - $result | ConvertTo-Json -Depth 5 -} else { - $checks | Select-Object Name, Required, Available, Location | Format-Table -AutoSize - foreach ($check in $checks | Where-Object { -not $_.Available }) { - Write-Host ("{0}: {1}" -f $check.Name, $check.Guidance) -ForegroundColor Yellow - } - Write-Host "" - Write-Host ("Ready: {0}" -f $result.Ready) - Write-Host ("Pull request mode: {0}" -f $result.PullRequestMode) -} - -$global:LASTEXITCODE = if ($missingRequired.Count -gt 0) { 1 } else { 0 } \ No newline at end of file diff --git a/.github/skills/skill-minipdf-contribution/scripts/resolve-implementation.ps1 b/.github/skills/skill-minipdf-contribution/scripts/resolve-implementation.ps1 deleted file mode 100644 index 45a247d0..00000000 --- a/.github/skills/skill-minipdf-contribution/scripts/resolve-implementation.ps1 +++ /dev/null @@ -1,29 +0,0 @@ -[CmdletBinding()] -param( - [ValidateSet("auto", "dotnet", "rust")] - [string]$Implementation = "auto" -) - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version Latest - -if ($Implementation -ne "auto") { - return $Implementation -} - -$available = @( - if (Get-Command dotnet -ErrorAction SilentlyContinue) { "dotnet" } - if (Get-Command cargo -ErrorAction SilentlyContinue) { "rust" } -) - -if ($available -contains "dotnet") { - return "dotnet" -} - -if ($available -contains "rust") { - return "rust" -} - -if ($available.Count -eq 0) { - throw "Automatic implementation selection requires the .NET SDK or Rust/Cargo." -} \ No newline at end of file diff --git a/.github/skills/skill-minipdf-contribution/scripts/select-candidates.ps1 b/.github/skills/skill-minipdf-contribution/scripts/select-candidates.ps1 deleted file mode 100644 index 4b2f8d0d..00000000 --- a/.github/skills/skill-minipdf-contribution/scripts/select-candidates.ps1 +++ /dev/null @@ -1,142 +0,0 @@ -[CmdletBinding()] -param( - [string[]]$ReportPath, - [ValidateRange(1, 20)] - [int]$Count = 1, - [ValidateRange(0.0, 1.0)] - [double]$HighScoreThreshold = 0.95, - [switch]$Json -) - -$ErrorActionPreference = "Stop" -Set-StrictMode -Version Latest -$repositoryRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\..\..")) - -function Resolve-ReportImage([string]$ReportDirectory, [string]$ImageName) { - if (-not $ImageName) { return $null } - $directPath = Join-Path $ReportDirectory $ImageName - if (Test-Path -LiteralPath $directPath -PathType Leaf) { return $directPath } - return Join-Path (Join-Path $ReportDirectory "images") $ImageName -} - -function Test-Score($Value) { - if ($null -eq $Value) { return $false } - try { $score = [double]$Value } catch { return $false } - return -not [double]::IsNaN($score) -and -not [double]::IsInfinity($score) -and $score -ge 0.0 -and $score -le 1.0 -} - -if (-not $ReportPath) { - $ReportPath = @( - (Join-Path $repositoryRoot "tests\MiniPdf.Benchmark\reports\comparison_report.json"), - (Join-Path $repositoryRoot "tests\MiniPdf.Benchmark\reports_docx\comparison_report.json") - ) -} - -$resolvedReports = @($ReportPath | Where-Object { Test-Path -LiteralPath $_ -PathType Leaf } | ForEach-Object { - (Resolve-Path -LiteralPath $_).Path -}) -if ($resolvedReports.Count -eq 0) { - throw "No benchmark report was found. Run the XLSX or DOCX benchmark first, or pass -ReportPath." -} - -$allCases = @() -foreach ($report in $resolvedReports) { - $data = Get-Content -LiteralPath $report -Raw | ConvertFrom-Json - $entries = if ($data -is [System.Array]) { - @($data) - } elseif ($data.PSObject.Properties.Name -contains "results") { - @($data.results) - } elseif ($data.PSObject.Properties.Name -contains "name") { - @($data) - } else { - throw "Unsupported report structure: $report" - } - $reportDirectory = Split-Path -Parent $report - - foreach ($entry in $entries) { - if (-not $entry.name) { continue } - $propertyNames = @($entry.PSObject.Properties.Name) - if ($propertyNames -notcontains "overall_score" -or $propertyNames -notcontains "visual_avg" -or - $propertyNames -notcontains "visual_scores" -or $propertyNames -notcontains "diff_images" -or - $propertyNames -notcontains "minipdf_pages" -or $propertyNames -notcontains "reference_pages" -or - $entry.pdf_valid -ne $true -or -not (Test-Score $entry.overall_score) -or - -not (Test-Score $entry.visual_avg) -or [int]$entry.minipdf_pages -lt 1 -or - [int]$entry.reference_pages -lt 1) { - continue - } - - $format = if ($report -match "docx" -or $entry.name -match "^docx_") { "docx" } else { "xlsx" } - $visualScores = @($entry.visual_scores) - $diffImages = @($entry.diff_images) - if ($visualScores.Count -eq 0 -or $visualScores.Count -ne $diffImages.Count) { continue } - $comparableIndexes = @(for ($index = 0; $index -lt [Math]::Min($visualScores.Count, $diffImages.Count); $index++) { - $image = $diffImages[$index] - $candidateImage = if ($image.minipdf_img) { Resolve-ReportImage $reportDirectory $image.minipdf_img } else { $null } - $referenceImage = if ($image.reference_img) { Resolve-ReportImage $reportDirectory $image.reference_img } else { $null } - if ($candidateImage -and $referenceImage -and (Test-Score $visualScores[$index]) -and - (Test-Path -LiteralPath $candidateImage -PathType Leaf) -and - (Test-Path -LiteralPath $referenceImage -PathType Leaf)) { - $index - } - }) - if ($comparableIndexes.Count -eq 0) { continue } - $eligibleIndexes = $comparableIndexes - $worstIndex = $eligibleIndexes[0] - foreach ($index in $eligibleIndexes | Select-Object -Skip 1) { - if ([double]$visualScores[$index] -lt [double]$visualScores[$worstIndex]) { - $worstIndex = $index - } - } - - $diff = if ($diffImages.Count -gt $worstIndex) { $diffImages[$worstIndex] } else { $null } - $worstPage = if ($diff -and $diff.page) { [int]$diff.page } else { $worstIndex + 1 } - $pageVisualScore = if ($visualScores.Count -gt $worstIndex) { [double]$visualScores[$worstIndex] } else { [double]$entry.visual_avg } - $sourceDirectory = if ($format -eq "docx") { "output_docx" } else { "output" } - - $allCases += [pscustomobject]@{ - Format = $format - Name = [string]$entry.name - OverallScore = [double]$entry.overall_score - VisualAverage = [double]$entry.visual_avg - WorstPage = $worstPage - PageVisualScore = $pageVisualScore - CandidateImage = if ($diff -and $diff.minipdf_img) { Resolve-ReportImage $reportDirectory $diff.minipdf_img } else { $null } - ReferenceImage = if ($diff -and $diff.reference_img) { Resolve-ReportImage $reportDirectory $diff.reference_img } else { $null } - HeatmapImage = if ($diff -and $diff.PSObject.Properties.Name -contains "heatmap_img" -and $diff.heatmap_img) { Resolve-ReportImage $reportDirectory $diff.heatmap_img } else { $null } - SourceDocument = Join-Path $repositoryRoot ("tests\MiniPdf.Scripts\{0}\{1}.{2}" -f $sourceDirectory, $entry.name, $format) - Report = $report - } - } -} - -$validCases = @($allCases | Where-Object { $_.OverallScore -ge 0.0 -and $_.OverallScore -le 1.0 }) -if ($validCases.Count -eq 0) { - throw "The reports contain no entries with a valid overall_score." -} -$duplicates = @($validCases | Group-Object Format, Name | Where-Object Count -gt 1) -if ($duplicates.Count -gt 0) { - throw "Duplicate benchmark case identities were found: $($duplicates.Name -join ', ')" -} - -$ranked = @($validCases | Sort-Object PageVisualScore, VisualAverage, OverallScore, Format, Name) -$allHigh = @($validCases | Where-Object { $_.OverallScore -lt $HighScoreThreshold }).Count -eq 0 -$selected = if ($allHigh) { @() } else { @($ranked | Select-Object -First $Count) } -$result = [pscustomobject]@{ - Mode = if ($allHigh) { "create-new" } else { "improve-existing" } - HighScoreThreshold = $HighScoreThreshold - Reports = $resolvedReports - ValidCaseCount = $validCases.Count - Candidates = $selected -} - -if ($Json) { - $result | ConvertTo-Json -Depth 6 -} else { - Write-Host ("Mode: {0}" -f $result.Mode) - Write-Host ("Valid cases: {0}; high-score threshold: {1:N4}" -f $result.ValidCaseCount, $HighScoreThreshold) - if ($allHigh) { - Write-Host "All valid cases meet the threshold. Add a deterministic, non-duplicate XLSX or DOCX validation case." - } else { - $selected | Select-Object Format, Name, OverallScore, VisualAverage, WorstPage, PageVisualScore | Format-Table -AutoSize - } -} \ No newline at end of file diff --git a/.github/skills/sync-readme-translations/SKILL.md b/.github/skills/sync-readme-translations/SKILL.md deleted file mode 100644 index 621328f8..00000000 --- a/.github/skills/sync-readme-translations/SKILL.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -name: sync-readme-translations -description: "Sync changes from the English README.md to all translated README files. Use when: updating README translations, syncing README across languages, propagating README changes to other locales." -argument-hint: "Describe what changed in README.md that needs syncing" ---- - -# Sync README Translations - -Propagate content changes from the English `README.md` to all translated versions, preserving each language's natural phrasing. - -## Target Files - -| File | Language | -|------|----------| -| `documents/README.zh-CN.md` | 简体中文 (Simplified Chinese) | -| `documents/README.zh-TW.md` | 繁體中文 (Traditional Chinese) | -| `documents/README.ja.md` | 日本語 (Japanese) | -| `documents/README.ko.md` | 한국어 (Korean) | -| `documents/README.it.md` | Italiano (Italian) | -| `documents/README.fr.md` | Français (French) | - -## Procedure - -1. **Read the English README.md** to identify the changed sections. -2. **For each translated file**, read the corresponding section and apply the same change in that language. -3. **Preserve localized phrasing** — translate new/changed text naturally; do not produce literal word-for-word translations. -4. **Keep shared elements untranslated** — badges, image paths, links, HTML tags, table structure, and code blocks must stay identical across all files. -5. **Use `apply_patch`** to batch related edits while preserving each translated file's localized wording. -6. **Verify** that the number of edits matches the number of target files × changed sections. - -## Rules - -- Never remove or reorder sections that exist in a translated file but not in English — the structures are kept in sync. -- Benchmark numbers (scores, case counts) are language-neutral; copy them verbatim. -- If a section doesn't exist yet in a translated file, add it at the same position as in the English README. -- All paths in links/images are relative to the repo root and must not change. diff --git a/AGENTS.md b/AGENTS.md index 23f0f4e5..f6e80eee 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,72 +1,13 @@ -- English -- No Emoji -- .NET security policy -- System can't auto git push - -## Documentation Workflow - -When updating README.md or documentation, sync the corresponding translated README files under `documents/` in the same change. Keep API examples, CLI examples, command tables, benchmark numbers, and report links aligned across languages. - -## Issue Summary Workflow -When user says "summary on issue #N", use `gh issue comment N --body-file -` -to post a summary of completed work to the GitHub issue. - -## Testing Workflow - -After making code changes, run the relevant validation before reporting completion. - -### Unit Tests -```powershell -dotnet test tests/MiniPdf.Tests -``` - - - -### Single File / Filtered Run -```powershell -scripts/Run-Benchmark.ps1 -Filter "border" # only cases containing "border" -scripts/Run-Benchmark_docx.ps1 -Filter "heading" -scripts/Run-Benchmark_issues.ps1 -Filter "sa8000" # focused issue run -scripts/Run-Benchmark_issues.ps1 -All # full issue benchmark -``` - -### Cross-Language Visual Benchmarks - -Use the following form for .NET, Rust, Java, Go, Python, and Node: - -```powershell -scripts/Run-<Language>-VisualBenchmark.ps1 -Suite <classic|issue> -Format <xlsx|docx|pptx> -MaxCases <n> -``` - -Rust may also use `scripts/Run-Rust-Benchmark.ps1`; it forwards to the same -shared runner. Microsoft 365 is always the primary scored reference and -LibreOffice is the required auxiliary reference. `-Engine` does not switch the -primary reference. The default minimum score is `0.95`; use `-SkipCandidate` -only when the corresponding candidate PDFs already exist under -`artifacts/<language>-benchmark/<suite>/<format>/candidates`. - -## Automated Contribution Loop - -Use the vendor-neutral workflow in `CONTRIBUTING.md` only when the user -explicitly requests the automated contribution loop. Ordinary fixes should use -the narrowest relevant tests and benchmarks. The executable entry point is: - -```powershell -scripts/Invoke-MiniPdfContributionLoop.ps1 -Action Start -Implementation dotnet -# or: scripts/Invoke-MiniPdfContributionLoop.ps1 -Action Start -Implementation rust -``` - -The workflow selects one candidate. Pass `-CandidateCount 2` only for an -explicitly requested two-case run. -Keep generated images and full logs in `artifacts/`; return only paths and -compact summaries in chat. - -Before running any contribution-loop command, ask the user to choose .NET or -Rust and wait for an explicit answer. Then pass that choice with -`-Implementation dotnet` or `-Implementation rust`; agents must not infer or -silently default the implementation. - -Preserve unrelated changes. Do not commit, push, fork, or open a pull request -without explicit user approval. - - +# Agent Instructions + +For MiniPdf rendering or visual benchmark contributions, read and follow `.claude/skills/minipdf-contribution/SKILL.md`. +# Agent Guidelines + +- Use English. +- Do not use emojis. +- Compare behavior with the default branch when relevant. +- Never mark work complete before proving it works. +- Keep changes simple, focused, and minimal. +- Find and fix root causes; do not use temporary workarounds. +- Maintain senior-engineer quality without overengineering simple problems. +- Challenge and review your work before presenting it. \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f978281c..83817850 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -118,77 +118,4 @@ Sustained contributions and sound judgment can lead to maintainer responsibility. Code volume is not the only form of merit: reviews, documentation, issue triage, release verification, security work, and community support all count. See `GOVERNANCE.md` for the selection process and -`MAINTAINERS.md` for areas that need additional ownership. - -## Automated Rendering Improvement - -The automated rendering-improvement loop works with any coding agent that can -edit repository files and run PowerShell, including GitHub Copilot, Claude Code, -Cursor, and Codex. The easiest way to start is to paste this prompt into the -agent chat: - -```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. -``` - -The agent integrations are convenience prompts; the workflow and safety gates -live in one vendor-neutral command. Agents must ask the user to choose .NET or -Rust before running it, then pass the explicit implementation. The workflow -selects one candidate: - -```powershell -.\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Start -Implementation <dotnet-or-rust> -``` - -Pass `-Implementation dotnet` or `-Implementation rust` according to the user's -answer. Do not use `auto` from an agent workflow. Pass `-CandidateCount 2` only -for an explicitly requested two-case run. The selected implementation is stored -in the loop state for subsequent actions. - -`Start` requires a clean working tree. It checks prerequisites, creates an -implementation-specific branch, builds fresh XLSX and DOCX baselines, and -selects the largest visual difference. - -For each selected candidate, run: - -```powershell -.\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Begin -Format <xlsx-or-docx> -CaseName <case-name> -# Let the coding agent diagnose, test, and make one focused root-cause change. -.\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Evaluate -``` - -An attempt is accepted only when its overall score improves and its visual score -does not decrease. A rejected or failed attempt is restored automatically. Each -candidate gets at most three attempts before the loop skips it. - -After at least one accepted improvement: - -```powershell -.\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Validate -.\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Pr -``` - -`Validate` runs the selected implementation's full test suite and XLSX/DOCX -visual regression gate. `Pr` generates benchmark evidence and either a GitHub -CLI command or browser instructions. Committing, pushing, and opening the pull -request still require explicit user approval. - -Keep generated PDFs, screenshots, heatmaps, reports, and complete command output -under `artifacts/`. Agent responses should contain paths, score summaries, and -only the last relevant error lines. Never embed image data, base64 payloads, or -complete logs in chat. Start a fresh chat before `Validate` when the current -context approaches 150,000 tokens or already contains large image/tool output; -the loop state in `.git/minipdf-contribution-loop/` supports resuming. - -### Agent Shortcuts - -| Agent | Shortcut | -|---|---| -| GitHub Copilot | `/skill-minipdf-contribution`; asks for .NET or Rust when omitted | -| Claude Code | `/minipdf-contribution`; asks for `dotnet` or `rust` when omitted | -| Cursor | `/minipdf-contribution`; asks for `dotnet` or `rust` when omitted | -| Codex | Ask: `Run the MiniPdf contribution loop`; it asks for .NET or Rust | -| Any terminal agent | Run the vendor-neutral PowerShell commands above | - -Agents must preserve unrelated changes and must not commit, push, fork, or open -a pull request without explicit approval. \ No newline at end of file +`MAINTAINERS.md` for areas that need additional ownership. \ No newline at end of file diff --git a/README.md b/README.md index 271a0819..e463e465 100644 --- a/README.md +++ b/README.md @@ -180,7 +180,7 @@ Open a clean fork or clone in any coding agent, then paste this prompt into the agent chat: ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## Project resources diff --git a/documents/README.fr.md b/documents/README.fr.md index 85d47055..848a4073 100644 --- a/documents/README.fr.md +++ b/documents/README.fr.md @@ -173,7 +173,7 @@ MiniPdf vise une conversion pratique des documents, et non une compatibilité to Ouvrez un fork ou clone propre dans n'importe quel agent de programmation, puis collez cette instruction dans le chat de l'Agent : ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## Ressources du projet diff --git a/documents/README.it.md b/documents/README.it.md index da4e55c8..a1d6bd52 100644 --- a/documents/README.it.md +++ b/documents/README.it.md @@ -173,7 +173,7 @@ MiniPdf punta alla conversione pratica dei documenti, non alla compatibilità co Apri un fork o clone pulito in qualsiasi agente di programmazione, quindi incolla questa istruzione nella chat dell'Agent: ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## Risorse del progetto diff --git a/documents/README.ja.md b/documents/README.ja.md index 8bc9fae5..7e0f4b15 100644 --- a/documents/README.ja.md +++ b/documents/README.ja.md @@ -172,7 +172,7 @@ MiniPdf は実用的な文書変換を目的としており、Microsoft Office 任意のコーディング Agent でクリーンな fork または clone を開き、次の指示を Agent のチャットに貼り付けます。 ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## プロジェクトリソース diff --git a/documents/README.ko.md b/documents/README.ko.md index 9c008e57..6c2320e2 100644 --- a/documents/README.ko.md +++ b/documents/README.ko.md @@ -172,7 +172,7 @@ MiniPdf는 실용적인 문서 변환을 목표로 하며 Microsoft Office 레 원하는 코딩 Agent에서 깨끗한 fork 또는 clone을 연 다음 아래 지시문을 Agent 채팅에 붙여 넣으세요. ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## 프로젝트 리소스 diff --git a/documents/README.zh-CN.md b/documents/README.zh-CN.md index 6b0b0d0b..605ed2d3 100644 --- a/documents/README.zh-CN.md +++ b/documents/README.zh-CN.md @@ -171,7 +171,7 @@ MiniPdf 专注于实用文档转换,不追求完全复制 Microsoft Office 版 在任意编码 Agent 中打开干净的 fork 或 clone,然后将以下指令粘贴到 Agent 聊天中: ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## 项目资源 diff --git a/documents/README.zh-TW.md b/documents/README.zh-TW.md index 2980c0e1..15aba0a5 100644 --- a/documents/README.zh-TW.md +++ b/documents/README.zh-TW.md @@ -171,7 +171,7 @@ MiniPdf 專注於實用文件轉換,不追求完整複製 Microsoft Office 版 在任意程式設計 Agent 中開啟乾淨的 fork 或 clone,然後將以下指令貼到 Agent 聊天中: ```text -Before reading CONTRIBUTING.md or taking any other action, ask me whether to use .NET or Rust and wait for my explicit choice. After I answer, read CONTRIBUTING.md and run the MiniPdf contribution loop from start to finish. Use the selected implementation to diagnose and improve one automatically selected benchmark case, validate all changes, and prepare the pull request. Keep generated images and full logs in artifacts and return only paths and compact summaries. Do not commit, push, fork, or open a pull request without my explicit approval. +Run the minipdf-contribution skill. ``` ## 專案資源 diff --git a/scripts/Invoke-MiniPdfContributionLoop.ps1 b/scripts/Invoke-MiniPdfContributionLoop.ps1 deleted file mode 100644 index 16e0e4cb..00000000 --- a/scripts/Invoke-MiniPdfContributionLoop.ps1 +++ /dev/null @@ -1,38 +0,0 @@ -<# -.SYNOPSIS - Run the MiniPdf contribution loop from any coding agent or terminal. - -.EXAMPLE - .\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Start -Implementation dotnet - -.EXAMPLE - .\scripts\Invoke-MiniPdfContributionLoop.ps1 -Action Start -Implementation rust -CandidateCount 2 -#> - -[CmdletBinding()] -param( - [Parameter(Mandatory)] - [ValidateSet("Start", "Begin", "Evaluate", "Validate", "Pr", "Status")] - [string]$Action, - [ValidateSet("auto", "dotnet", "rust")] - [string]$Implementation = "auto", - [ValidateRange(1, 2)] - [int]$CandidateCount = 1, - [ValidateSet("xlsx", "docx")] - [string]$Format, - [string]$CaseName, - [ValidateRange(0.0, 1.0)] - [double]$MinimumImprovement = 0.0001, - [ValidateRange(0.0, 1.0)] - [double]$RegressionTolerance = 0.002, - [string]$Title, - [switch]$CreatePullRequest, - [switch]$Json -) - -$controller = Join-Path (Split-Path -Parent $PSScriptRoot) ".github\skills\skill-minipdf-contribution\scripts\contribution-loop.ps1" -if (-not (Test-Path -LiteralPath $controller -PathType Leaf)) { - throw "MiniPdf contribution controller was not found: $controller" -} - -& $controller @PSBoundParameters \ No newline at end of file diff --git a/tests/MiniPdf.Benchmark/reports_docx/comparison_report.md b/tests/MiniPdf.Benchmark/reports_docx/comparison_report.md index 34d22c25..adacded8 100644 --- a/tests/MiniPdf.Benchmark/reports_docx/comparison_report.md +++ b/tests/MiniPdf.Benchmark/reports_docx/comparison_report.md @@ -1,6 +1,7 @@ -# MiniPdf vs Reference PDF Comparison Report -Generated: 2026-07-19T19:57:52.759774 +# MiniPdf vs LibreOffice Reference PDF Comparison Report + +Generated: 2026-09-08T23:08:12.931339 ## Summary @@ -42,7 +43,7 @@ Generated: 2026-07-19T19:57:52.759774 | 34 | 🟢 docx_classic32_superscript_subscript | ✅ | 0.9762 | 0.9937 | 1/1 | **0.988** | | 35 | 🟢 docx_classic33_highlighted_text | ✅ | 1.0 | 0.9682 | 1/1 | **0.9873** | | 36 | 🟢 docx_classic33_two_products_side_by_side | ✅ | 1.0 | 0.9768 | 1/1 | **0.9907** | -| 37 | 🔴 docx_classic34_employee_directory_with_photo | ✅ | 1.0 | 0.4879 | 1/2 | **0.6952** | +| 37 | 🟢 docx_classic34_employee_directory_with_photo | ✅ | 1.0 | 0.9969 | 2/2 | **0.9988** | | 38 | 🟢 docx_classic34_paragraph_borders | ✅ | 1.0 | 0.9793 | 1/1 | **0.9917** | | 39 | 🟢 docx_classic35_inventory_with_product_photos | ✅ | 1.0 | 0.9801 | 2/2 | **0.992** | | 40 | 🟡 docx_classic35_tab_stops | ✅ | 0.64 | 0.989 | 1/1 | **0.8516** | @@ -187,19 +188,19 @@ Generated: 2026-07-19T19:57:52.759774 | 179 | 🟢 docx_classic149_merged_section_report | ✅ | 1.0 | 0.9311 | 1/1 | **0.9724** | | 180 | 🟢 docx_classic150_kitchen_sink_styles | ✅ | 1.0 | 0.962 | 2/2 | **0.9848** | -**Average Overall Score: 0.9762** +**Average Overall Score: 0.9779** ## Visual Comparison <table> -<tr><th>MiniPdf</th><th>LibreOffice (Reference)</th></tr> +<tr><th>MiniPdf</th><th>LibreOffice Reference</th></tr> <tr> <td><b>docx_classic01_single_paragraph</b></td> <td colspan="1">docx_classic01_single_paragraph <span style="color:#3fb950">⬤</span> 99.9%</td> </tr> <tr> <td><img src="images/docx_classic01_single_paragraph_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic01_single_paragraph_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic01_single_paragraph_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic02_multiple_paragraphs</b></td> @@ -207,7 +208,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic02_multiple_paragraphs_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic02_multiple_paragraphs_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic02_multiple_paragraphs_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic03_headings</b></td> @@ -215,7 +216,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic03_headings_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic03_headings_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic03_headings_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic04_bold_italic</b></td> @@ -223,7 +224,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic04_bold_italic_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic04_bold_italic_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic04_bold_italic_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic05_font_sizes</b></td> @@ -231,7 +232,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic05_font_sizes_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic05_font_sizes_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic05_font_sizes_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic06_font_colors</b></td> @@ -239,7 +240,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic06_font_colors_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic06_font_colors_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic06_font_colors_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic07_alignment</b></td> @@ -247,7 +248,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic07_alignment_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic07_alignment_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic07_alignment_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic08_bullet_list</b></td> @@ -255,7 +256,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic08_bullet_list_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic08_bullet_list_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic08_bullet_list_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic09_numbered_list</b></td> @@ -263,7 +264,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic09_numbered_list_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic09_numbered_list_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic09_numbered_list_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic10_simple_table</b></td> @@ -271,7 +272,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic10_simple_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic10_simple_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic10_simple_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic11_table_with_shading</b></td> @@ -279,7 +280,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic11_table_with_shading_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic11_table_with_shading_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic11_table_with_shading_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic12_merged_cells_table</b></td> @@ -287,7 +288,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic12_merged_cells_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic12_merged_cells_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic12_merged_cells_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic13_long_document</b></td> @@ -295,19 +296,19 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic13_long_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic13_long_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic13_long_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic13_long_document_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic13_long_document_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic13_long_document_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic13_long_document_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic13_long_document_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic13_long_document_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic13_long_document_p4_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic13_long_document_p4_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic13_long_document_p4_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic14_mixed_content</b></td> @@ -315,7 +316,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic14_mixed_content_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic14_mixed_content_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic14_mixed_content_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic15_indentation</b></td> @@ -323,7 +324,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic15_indentation_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic15_indentation_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic15_indentation_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic16_line_spacing</b></td> @@ -331,7 +332,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic16_line_spacing_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic16_line_spacing_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic16_line_spacing_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic17_page_break</b></td> @@ -339,15 +340,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic17_page_break_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic17_page_break_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic17_page_break_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic17_page_break_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic17_page_break_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic17_page_break_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic17_page_break_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic17_page_break_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic17_page_break_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic18_embedded_image</b></td> @@ -355,7 +356,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic18_embedded_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic18_embedded_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic18_embedded_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic19_multiple_images</b></td> @@ -363,7 +364,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic19_multiple_images_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic19_multiple_images_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic19_multiple_images_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic20_table_with_many_rows</b></td> @@ -371,7 +372,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic20_table_with_many_rows_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic20_table_with_many_rows_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic20_table_with_many_rows_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic21_nested_lists</b></td> @@ -379,7 +380,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic21_nested_lists_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic21_nested_lists_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic21_nested_lists_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic22_horizontal_rule</b></td> @@ -387,7 +388,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic22_horizontal_rule_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic22_horizontal_rule_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic22_horizontal_rule_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic23_mixed_formatting_runs</b></td> @@ -395,7 +396,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic23_mixed_formatting_runs_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic23_mixed_formatting_runs_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic23_mixed_formatting_runs_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic24_two_column_table_layout</b></td> @@ -403,7 +404,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic24_two_column_table_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic24_two_column_table_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic24_two_column_table_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic25_title_and_subtitle</b></td> @@ -411,11 +412,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic25_title_and_subtitle_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic25_title_and_subtitle_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic25_title_and_subtitle_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic25_title_and_subtitle_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic25_title_and_subtitle_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic25_title_and_subtitle_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic26_table_alignment</b></td> @@ -423,7 +424,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic26_table_alignment_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic26_table_alignment_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic26_table_alignment_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic27_long_paragraph_wrapping</b></td> @@ -431,7 +432,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic27_long_paragraph_wrapping_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic27_long_paragraph_wrapping_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic27_long_paragraph_wrapping_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic28_special_characters</b></td> @@ -439,7 +440,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic28_special_characters_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic28_special_characters_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic28_special_characters_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic29_table_with_image</b></td> @@ -447,7 +448,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic29_table_with_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic29_table_with_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic29_table_with_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic30_comprehensive_report</b></td> @@ -455,15 +456,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic30_comprehensive_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic30_comprehensive_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic30_comprehensive_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic30_comprehensive_report_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic30_comprehensive_report_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic30_comprehensive_report_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic30_comprehensive_report_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic30_comprehensive_report_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic30_comprehensive_report_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic31_product_card_with_image</b></td> @@ -471,7 +472,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic31_product_card_with_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic31_product_card_with_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic31_product_card_with_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic31_strikethrough_text</b></td> @@ -479,7 +480,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic31_strikethrough_text_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic31_strikethrough_text_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic31_strikethrough_text_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic32_company_logo_header</b></td> @@ -487,7 +488,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic32_company_logo_header_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic32_company_logo_header_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic32_company_logo_header_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic32_superscript_subscript</b></td> @@ -495,7 +496,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic32_superscript_subscript_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic32_superscript_subscript_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic32_superscript_subscript_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic33_highlighted_text</b></td> @@ -503,7 +504,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic33_highlighted_text_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic33_highlighted_text_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic33_highlighted_text_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic33_two_products_side_by_side</b></td> @@ -511,19 +512,19 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic33_two_products_side_by_side_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic33_two_products_side_by_side_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic33_two_products_side_by_side_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic34_employee_directory_with_photo</b></td> - <td colspan="1">docx_classic34_employee_directory_with_photo <span style="color:#f85149">⬤</span> 69.5%</td> + <td colspan="1">docx_classic34_employee_directory_with_photo <span style="color:#3fb950">⬤</span> 99.9%</td> </tr> <tr> <td><img src="images/docx_classic34_employee_directory_with_photo_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic34_employee_directory_with_photo_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic34_employee_directory_with_photo_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> - <td><i>missing</i></td> - <td><img src="images/docx_classic34_employee_directory_with_photo_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic34_employee_directory_with_photo_p2_minipdf.png" width="340" alt="MiniPdf"></td> + <td><img src="images/docx_classic34_employee_directory_with_photo_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic34_paragraph_borders</b></td> @@ -531,7 +532,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic34_paragraph_borders_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic34_paragraph_borders_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic34_paragraph_borders_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic35_inventory_with_product_photos</b></td> @@ -539,11 +540,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic35_inventory_with_product_photos_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic35_inventory_with_product_photos_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic35_inventory_with_product_photos_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic35_inventory_with_product_photos_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic35_inventory_with_product_photos_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic35_inventory_with_product_photos_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic35_tab_stops</b></td> @@ -551,7 +552,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic35_tab_stops_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic35_tab_stops_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic35_tab_stops_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic36_invoice_with_logo</b></td> @@ -559,7 +560,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic36_invoice_with_logo_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic36_invoice_with_logo_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic36_invoice_with_logo_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic36_wide_table</b></td> @@ -567,7 +568,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic36_wide_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic36_wide_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic36_wide_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic37_nested_table</b></td> @@ -575,7 +576,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic37_nested_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic37_nested_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic37_nested_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic37_real_estate_listing</b></td> @@ -583,7 +584,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic37_real_estate_listing_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic37_real_estate_listing_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic37_real_estate_listing_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic38_restaurant_menu_with_photos</b></td> @@ -591,11 +592,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic38_restaurant_menu_with_photos_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic38_restaurant_menu_with_photos_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic38_restaurant_menu_with_photos_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic38_restaurant_menu_with_photos_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic38_restaurant_menu_with_photos_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic38_restaurant_menu_with_photos_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic38_table_column_widths</b></td> @@ -603,7 +604,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic38_table_column_widths_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic38_table_column_widths_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic38_table_column_widths_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic39_cover_page_with_image</b></td> @@ -611,11 +612,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic39_cover_page_with_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic39_cover_page_with_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic39_cover_page_with_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic39_cover_page_with_image_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic39_cover_page_with_image_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic39_cover_page_with_image_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic39_financial_report</b></td> @@ -623,7 +624,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic39_financial_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic39_financial_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic39_financial_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic40_product_catalog_with_images</b></td> @@ -631,7 +632,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic40_product_catalog_with_images_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic40_product_catalog_with_images_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic40_product_catalog_with_images_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic40_resume</b></td> @@ -639,7 +640,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic40_resume_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic40_resume_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic40_resume_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic41_business_letter</b></td> @@ -647,7 +648,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic41_business_letter_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic41_business_letter_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic41_business_letter_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic41_newsletter_with_hero_image</b></td> @@ -655,7 +656,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic41_newsletter_with_hero_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic41_newsletter_with_hero_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic41_newsletter_with_hero_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic42_chart_image_with_data</b></td> @@ -663,7 +664,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic42_chart_image_with_data_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic42_chart_image_with_data_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic42_chart_image_with_data_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic42_meeting_minutes</b></td> @@ -671,7 +672,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic42_meeting_minutes_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic42_meeting_minutes_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic42_meeting_minutes_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic43_event_flyer_with_banner</b></td> @@ -679,7 +680,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic43_event_flyer_with_banner_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic43_event_flyer_with_banner_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic43_event_flyer_with_banner_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic43_invoice_document</b></td> @@ -687,7 +688,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic43_invoice_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic43_invoice_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic43_invoice_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic44_dashboard_with_kpi_image</b></td> @@ -695,7 +696,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic44_dashboard_with_kpi_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic44_dashboard_with_kpi_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic44_dashboard_with_kpi_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic44_memo</b></td> @@ -703,7 +704,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic44_memo_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic44_memo_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic44_memo_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic45_certificate_with_seal</b></td> @@ -711,7 +712,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic45_certificate_with_seal_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic45_certificate_with_seal_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic45_certificate_with_seal_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic45_project_plan</b></td> @@ -719,7 +720,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic45_project_plan_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic45_project_plan_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic45_project_plan_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic46_comparison_table</b></td> @@ -727,7 +728,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic46_comparison_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic46_comparison_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic46_comparison_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic46_product_image_grid</b></td> @@ -735,7 +736,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic46_product_image_grid_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic46_product_image_grid_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic46_product_image_grid_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic47_data_dictionary</b></td> @@ -743,7 +744,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic47_data_dictionary_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic47_data_dictionary_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic47_data_dictionary_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic47_news_article_with_hero_image</b></td> @@ -751,7 +752,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic47_news_article_with_hero_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic47_news_article_with_hero_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic47_news_article_with_hero_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic48_multi_level_headings</b></td> @@ -759,15 +760,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic48_multi_level_headings_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic48_multi_level_headings_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic48_multi_level_headings_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic48_multi_level_headings_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic48_multi_level_headings_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic48_multi_level_headings_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic48_multi_level_headings_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic48_multi_level_headings_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic48_multi_level_headings_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic48_task_list_with_status_icons</b></td> @@ -775,7 +776,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic48_task_list_with_status_icons_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic48_task_list_with_status_icons_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic48_task_list_with_status_icons_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic49_cjk_document</b></td> @@ -783,7 +784,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic49_cjk_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic49_cjk_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic49_cjk_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic49_wide_panoramic_banner</b></td> @@ -791,7 +792,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic49_wide_panoramic_banner_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic49_wide_panoramic_banner_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic49_wide_panoramic_banner_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic50_long_table_with_formatting</b></td> @@ -799,11 +800,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic50_long_table_with_formatting_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic50_long_table_with_formatting_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic50_long_table_with_formatting_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><i>missing</i></td> - <td><img src="images/docx_classic50_long_table_with_formatting_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic50_long_table_with_formatting_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic50_portrait_tall_image</b></td> @@ -811,7 +812,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic50_portrait_tall_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic50_portrait_tall_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic50_portrait_tall_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic51_step_by_step_with_images</b></td> @@ -819,7 +820,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic51_step_by_step_with_images_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic51_step_by_step_with_images_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic51_step_by_step_with_images_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic51_underline_styles</b></td> @@ -827,7 +828,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic51_underline_styles_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic51_underline_styles_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic51_underline_styles_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic52_before_after_comparison</b></td> @@ -835,7 +836,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic52_before_after_comparison_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic52_before_after_comparison_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic52_before_after_comparison_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic52_spacing_before_after</b></td> @@ -843,7 +844,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic52_spacing_before_after_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic52_spacing_before_after_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic52_spacing_before_after_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic53_color_swatch_palette</b></td> @@ -851,7 +852,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic53_color_swatch_palette_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic53_color_swatch_palette_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic53_color_swatch_palette_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic53_table_merged_complex</b></td> @@ -859,7 +860,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic53_table_merged_complex_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic53_table_merged_complex_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic53_table_merged_complex_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic54_multi_font_family</b></td> @@ -867,7 +868,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic54_multi_font_family_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic54_multi_font_family_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic54_multi_font_family_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic54_travel_destination_cards</b></td> @@ -875,7 +876,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic54_travel_destination_cards_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic54_travel_destination_cards_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic54_travel_destination_cards_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic55_background_shading_paragraph</b></td> @@ -883,7 +884,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic55_background_shading_paragraph_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic55_background_shading_paragraph_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic55_background_shading_paragraph_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic55_lab_results_with_image</b></td> @@ -891,7 +892,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic55_lab_results_with_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic55_lab_results_with_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic55_lab_results_with_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic56_images_and_tables_mixed</b></td> @@ -899,7 +900,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic56_images_and_tables_mixed_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic56_images_and_tables_mixed_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic56_images_and_tables_mixed_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic56_software_screenshot_features</b></td> @@ -907,7 +908,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic56_software_screenshot_features_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic56_software_screenshot_features_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic56_software_screenshot_features_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic57_right_to_left_text</b></td> @@ -915,7 +916,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic57_right_to_left_text_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic57_right_to_left_text_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic57_right_to_left_text_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic57_sports_results_with_logos</b></td> @@ -923,7 +924,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic57_sports_results_with_logos_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic57_sports_results_with_logos_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic57_sports_results_with_logos_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic58_dense_paragraph_document</b></td> @@ -931,31 +932,31 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p4_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p4_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p4_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p5_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p5_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p5_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p6_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p6_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p6_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic58_dense_paragraph_document_p7_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_dense_paragraph_document_p7_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_dense_paragraph_document_p7_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic58_report_with_footer_image</b></td> @@ -963,7 +964,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic58_report_with_footer_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic58_report_with_footer_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic58_report_with_footer_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic59_numbered_and_bullet_mixed</b></td> @@ -971,7 +972,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic59_numbered_and_bullet_mixed_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic59_numbered_and_bullet_mixed_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic59_numbered_and_bullet_mixed_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic59_nutrition_label_with_image</b></td> @@ -979,7 +980,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic59_nutrition_label_with_image_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic59_nutrition_label_with_image_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic59_nutrition_label_with_image_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic60_comprehensive_styled_report</b></td> @@ -987,15 +988,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic60_comprehensive_styled_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic60_comprehensive_styled_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic60_comprehensive_styled_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic60_comprehensive_styled_report_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic60_comprehensive_styled_report_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic60_comprehensive_styled_report_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic60_comprehensive_styled_report_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic60_comprehensive_styled_report_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic60_comprehensive_styled_report_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic60_project_status_with_milestones</b></td> @@ -1003,11 +1004,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic60_project_status_with_milestones_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic60_project_status_with_milestones_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic60_project_status_with_milestones_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><i>missing</i></td> - <td><img src="images/docx_classic60_project_status_with_milestones_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic60_project_status_with_milestones_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic61_header_and_footer</b></td> @@ -1015,7 +1016,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic61_header_and_footer_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic61_header_and_footer_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic61_header_and_footer_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic62_footnote_references</b></td> @@ -1023,7 +1024,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic62_footnote_references_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic62_footnote_references_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic62_footnote_references_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic63_toc_style_headings</b></td> @@ -1031,7 +1032,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic63_toc_style_headings_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic63_toc_style_headings_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic63_toc_style_headings_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic64_multi_column_layout</b></td> @@ -1039,7 +1040,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic64_multi_column_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic64_multi_column_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic64_multi_column_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic65_code_block_styling</b></td> @@ -1047,7 +1048,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic65_code_block_styling_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic65_code_block_styling_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic65_code_block_styling_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic66_colored_title_page</b></td> @@ -1055,7 +1056,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic66_colored_title_page_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic66_colored_title_page_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic66_colored_title_page_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic67_alternating_row_table</b></td> @@ -1063,7 +1064,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic67_alternating_row_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic67_alternating_row_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic67_alternating_row_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic68_sidebar_layout</b></td> @@ -1071,7 +1072,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic68_sidebar_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic68_sidebar_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic68_sidebar_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic69_blockquote_styling</b></td> @@ -1079,7 +1080,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic69_blockquote_styling_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic69_blockquote_styling_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic69_blockquote_styling_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic70_academic_paper</b></td> @@ -1087,7 +1088,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic70_academic_paper_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic70_academic_paper_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic70_academic_paper_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic71_legal_document</b></td> @@ -1095,7 +1096,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic71_legal_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic71_legal_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic71_legal_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic72_technical_specification</b></td> @@ -1103,7 +1104,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic72_technical_specification_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic72_technical_specification_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic72_technical_specification_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic73_calendar_layout</b></td> @@ -1111,7 +1112,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic73_calendar_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic73_calendar_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic73_calendar_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic74_org_chart</b></td> @@ -1119,7 +1120,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic74_org_chart_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic74_org_chart_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic74_org_chart_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic75_newsletter_layout</b></td> @@ -1127,7 +1128,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic75_newsletter_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic75_newsletter_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic75_newsletter_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic76_recipe_card</b></td> @@ -1135,7 +1136,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic76_recipe_card_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic76_recipe_card_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic76_recipe_card_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic77_timeline_layout</b></td> @@ -1143,7 +1144,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic77_timeline_layout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic77_timeline_layout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic77_timeline_layout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic78_faq_document</b></td> @@ -1151,11 +1152,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic78_faq_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic78_faq_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic78_faq_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic78_faq_document_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic78_faq_document_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic78_faq_document_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic79_glossary</b></td> @@ -1163,7 +1164,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic79_glossary_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic79_glossary_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic79_glossary_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic80_matrix_grid</b></td> @@ -1171,7 +1172,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic80_matrix_grid_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic80_matrix_grid_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic80_matrix_grid_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic81_budget_table</b></td> @@ -1179,7 +1180,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic81_budget_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic81_budget_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic81_budget_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic82_survey_questionnaire</b></td> @@ -1187,11 +1188,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic82_survey_questionnaire_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic82_survey_questionnaire_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic82_survey_questionnaire_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic82_survey_questionnaire_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic82_survey_questionnaire_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic82_survey_questionnaire_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic83_medical_form</b></td> @@ -1199,7 +1200,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic83_medical_form_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic83_medical_form_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic83_medical_form_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic84_shipping_label</b></td> @@ -1207,7 +1208,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic84_shipping_label_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic84_shipping_label_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic84_shipping_label_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic85_report_card</b></td> @@ -1215,7 +1216,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic85_report_card_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic85_report_card_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic85_report_card_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic86_checklist_document</b></td> @@ -1223,7 +1224,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic86_checklist_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic86_checklist_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic86_checklist_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic87_bibliography</b></td> @@ -1231,7 +1232,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic87_bibliography_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic87_bibliography_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic87_bibliography_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic88_presentation_handout</b></td> @@ -1239,11 +1240,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic88_presentation_handout_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic88_presentation_handout_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic88_presentation_handout_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic88_presentation_handout_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic88_presentation_handout_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic88_presentation_handout_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic89_multi_image_gallery</b></td> @@ -1251,7 +1252,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic89_multi_image_gallery_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic89_multi_image_gallery_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic89_multi_image_gallery_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic90_comprehensive_annual_report</b></td> @@ -1259,19 +1260,19 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic90_comprehensive_annual_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic90_comprehensive_annual_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic90_comprehensive_annual_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic90_comprehensive_annual_report_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic90_comprehensive_annual_report_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic90_comprehensive_annual_report_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic90_comprehensive_annual_report_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic90_comprehensive_annual_report_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic90_comprehensive_annual_report_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic90_comprehensive_annual_report_p4_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic90_comprehensive_annual_report_p4_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic90_comprehensive_annual_report_p4_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic91_landscape_page</b></td> @@ -1279,7 +1280,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic91_landscape_page_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic91_landscape_page_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic91_landscape_page_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic92_first_line_indent</b></td> @@ -1287,7 +1288,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic92_first_line_indent_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic92_first_line_indent_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic92_first_line_indent_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic93_hanging_indent</b></td> @@ -1295,7 +1296,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic93_hanging_indent_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic93_hanging_indent_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic93_hanging_indent_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic94_custom_bullet_characters</b></td> @@ -1303,7 +1304,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic94_custom_bullet_characters_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic94_custom_bullet_characters_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic94_custom_bullet_characters_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic95_contract_template</b></td> @@ -1311,7 +1312,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic95_contract_template_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic95_contract_template_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic95_contract_template_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic96_dense_data_table</b></td> @@ -1319,7 +1320,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic96_dense_data_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic96_dense_data_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic96_dense_data_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic97_product_catalog</b></td> @@ -1327,7 +1328,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic97_product_catalog_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic97_product_catalog_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic97_product_catalog_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic98_training_manual</b></td> @@ -1335,7 +1336,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic98_training_manual_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic98_training_manual_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic98_training_manual_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic99_policy_document</b></td> @@ -1343,7 +1344,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic99_policy_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic99_policy_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic99_policy_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic100_multi_page_table</b></td> @@ -1351,11 +1352,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic100_multi_page_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic100_multi_page_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic100_multi_page_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic100_multi_page_table_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic100_multi_page_table_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic100_multi_page_table_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic101_warranty_document</b></td> @@ -1363,7 +1364,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic101_warranty_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic101_warranty_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic101_warranty_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic102_curriculum_syllabus</b></td> @@ -1371,7 +1372,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic102_curriculum_syllabus_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic102_curriculum_syllabus_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic102_curriculum_syllabus_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic103_event_program</b></td> @@ -1379,11 +1380,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic103_event_program_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic103_event_program_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic103_event_program_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic103_event_program_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic103_event_program_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic103_event_program_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic104_sop_document</b></td> @@ -1391,11 +1392,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic104_sop_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic104_sop_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic104_sop_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic104_sop_document_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic104_sop_document_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic104_sop_document_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic105_certificate</b></td> @@ -1403,7 +1404,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic105_certificate_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic105_certificate_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic105_certificate_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic106_multi_section_orientation</b></td> @@ -1411,15 +1412,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic106_multi_section_orientation_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic106_multi_section_orientation_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic106_multi_section_orientation_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic106_multi_section_orientation_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic106_multi_section_orientation_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic106_multi_section_orientation_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic106_multi_section_orientation_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic106_multi_section_orientation_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic106_multi_section_orientation_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic107_order_form</b></td> @@ -1427,7 +1428,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic107_order_form_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic107_order_form_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic107_order_form_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic108_comparison_matrix</b></td> @@ -1435,7 +1436,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic108_comparison_matrix_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic108_comparison_matrix_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic108_comparison_matrix_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic109_release_notes</b></td> @@ -1443,7 +1444,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic109_release_notes_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic109_release_notes_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic109_release_notes_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic110_troubleshooting_guide</b></td> @@ -1451,7 +1452,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic110_troubleshooting_guide_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic110_troubleshooting_guide_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic110_troubleshooting_guide_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic111_meeting_agenda</b></td> @@ -1459,7 +1460,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic111_meeting_agenda_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic111_meeting_agenda_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic111_meeting_agenda_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic112_project_status_report</b></td> @@ -1467,7 +1468,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic112_project_status_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic112_project_status_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic112_project_status_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic113_address_labels</b></td> @@ -1475,7 +1476,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic113_address_labels_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic113_address_labels_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic113_address_labels_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic114_test_report</b></td> @@ -1483,7 +1484,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic114_test_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic114_test_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic114_test_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic115_price_list</b></td> @@ -1491,7 +1492,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic115_price_list_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic115_price_list_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic115_price_list_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic116_risk_assessment</b></td> @@ -1499,7 +1500,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic116_risk_assessment_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic116_risk_assessment_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic116_risk_assessment_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic117_employee_handbook_excerpt</b></td> @@ -1507,7 +1508,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic117_employee_handbook_excerpt_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic117_employee_handbook_excerpt_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic117_employee_handbook_excerpt_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic118_data_report_with_summary</b></td> @@ -1515,7 +1516,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic118_data_report_with_summary_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic118_data_report_with_summary_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic118_data_report_with_summary_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic119_multi_language_document</b></td> @@ -1523,7 +1524,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic119_multi_language_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic119_multi_language_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic119_multi_language_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic120_comprehensive_business_proposal</b></td> @@ -1531,15 +1532,15 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic120_comprehensive_business_proposal_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic120_comprehensive_business_proposal_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic120_comprehensive_business_proposal_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic120_comprehensive_business_proposal_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic120_comprehensive_business_proposal_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic120_comprehensive_business_proposal_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic120_comprehensive_business_proposal_p3_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic120_comprehensive_business_proposal_p3_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic120_comprehensive_business_proposal_p3_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic121_thin_border_table</b></td> @@ -1547,7 +1548,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic121_thin_border_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic121_thin_border_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic121_thin_border_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic122_thick_outer_border_table</b></td> @@ -1555,7 +1556,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic122_thick_outer_border_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic122_thick_outer_border_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic122_thick_outer_border_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic123_dashed_border_table</b></td> @@ -1563,7 +1564,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic123_dashed_border_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic123_dashed_border_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic123_dashed_border_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic124_colored_border_table</b></td> @@ -1571,7 +1572,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic124_colored_border_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic124_colored_border_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic124_colored_border_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic125_solid_cell_fills</b></td> @@ -1579,7 +1580,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic125_solid_cell_fills_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic125_solid_cell_fills_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic125_solid_cell_fills_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic126_dark_header_table</b></td> @@ -1587,7 +1588,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic126_dark_header_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic126_dark_header_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic126_dark_header_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic127_font_styles_showcase</b></td> @@ -1595,7 +1596,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic127_font_styles_showcase_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic127_font_styles_showcase_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic127_font_styles_showcase_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic128_font_sizes_showcase</b></td> @@ -1603,7 +1604,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic128_font_sizes_showcase_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic128_font_sizes_showcase_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic128_font_sizes_showcase_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic129_alignment_combinations</b></td> @@ -1611,7 +1612,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic129_alignment_combinations_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic129_alignment_combinations_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic129_alignment_combinations_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic130_wrap_and_indent</b></td> @@ -1619,7 +1620,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic130_wrap_and_indent_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic130_wrap_and_indent_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic130_wrap_and_indent_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic131_number_format_table</b></td> @@ -1627,7 +1628,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic131_number_format_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic131_number_format_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic131_number_format_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic132_striped_table</b></td> @@ -1635,7 +1636,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic132_striped_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic132_striped_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic132_striped_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic133_gradient_rows_table</b></td> @@ -1643,7 +1644,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic133_gradient_rows_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic133_gradient_rows_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic133_gradient_rows_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic134_heatmap_table</b></td> @@ -1651,7 +1652,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic134_heatmap_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic134_heatmap_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic134_heatmap_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic135_bottom_border_paragraphs</b></td> @@ -1659,7 +1660,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic135_bottom_border_paragraphs_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic135_bottom_border_paragraphs_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic135_bottom_border_paragraphs_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic136_financial_statement</b></td> @@ -1667,7 +1668,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic136_financial_statement_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic136_financial_statement_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic136_financial_statement_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic137_checkerboard_table</b></td> @@ -1675,7 +1676,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic137_checkerboard_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic137_checkerboard_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic137_checkerboard_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic138_color_grid_table</b></td> @@ -1683,7 +1684,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic138_color_grid_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic138_color_grid_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic138_color_grid_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic139_paragraph_shading_patterns</b></td> @@ -1691,7 +1692,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic139_paragraph_shading_patterns_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic139_paragraph_shading_patterns_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic139_paragraph_shading_patterns_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic140_rotated_text_table</b></td> @@ -1699,7 +1700,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic140_rotated_text_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic140_rotated_text_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic140_rotated_text_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic141_mixed_border_styles</b></td> @@ -1707,7 +1708,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic141_mixed_border_styles_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic141_mixed_border_styles_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic141_mixed_border_styles_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic142_styled_invoice_document</b></td> @@ -1715,7 +1716,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic142_styled_invoice_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic142_styled_invoice_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic142_styled_invoice_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic143_multi_section_document</b></td> @@ -1723,7 +1724,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic143_multi_section_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic143_multi_section_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic143_multi_section_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic144_note_style_paragraphs</b></td> @@ -1731,7 +1732,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic144_note_style_paragraphs_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic144_note_style_paragraphs_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic144_note_style_paragraphs_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic145_status_badge_table</b></td> @@ -1739,7 +1740,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic145_status_badge_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic145_status_badge_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic145_status_badge_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic146_double_border_table</b></td> @@ -1747,7 +1748,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic146_double_border_table_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic146_double_border_table_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic146_double_border_table_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic147_multi_section_styled_report</b></td> @@ -1755,7 +1756,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic147_multi_section_styled_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic147_multi_section_styled_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic147_multi_section_styled_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic148_data_grid_document</b></td> @@ -1763,7 +1764,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic148_data_grid_document_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic148_data_grid_document_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic148_data_grid_document_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic149_merged_section_report</b></td> @@ -1771,7 +1772,7 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic149_merged_section_report_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic149_merged_section_report_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic149_merged_section_report_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><b>docx_classic150_kitchen_sink_styles</b></td> @@ -1779,11 +1780,11 @@ Generated: 2026-07-19T19:57:52.759774 </tr> <tr> <td><img src="images/docx_classic150_kitchen_sink_styles_p1_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic150_kitchen_sink_styles_p1_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic150_kitchen_sink_styles_p1_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> <tr> <td><img src="images/docx_classic150_kitchen_sink_styles_p2_minipdf.png" width="340" alt="MiniPdf"></td> - <td><img src="images/docx_classic150_kitchen_sink_styles_p2_reference.png" width="340" alt="Reference"></td> + <td><img src="images/docx_classic150_kitchen_sink_styles_p2_reference.png" width="340" alt="LibreOffice Reference"></td> </tr> </table> @@ -1963,7 +1964,6 @@ Text content: ✅ Identical This is section 10 of the report. It contains detailed analysis of the topic at hand. The quick brown fox jumps over the lazy dog. Pack my box with five dozen liquor jugs. How vexingly - ``` </details> @@ -2321,7 +2321,6 @@ Text content: ✅ Identical Footnote reference n n n - ``` </details> @@ -2348,28 +2347,12 @@ Text content: ✅ Identical ### docx_classic34_employee_directory_with_photo - **Text Similarity:** 1.0 -- **Visual Average:** 0.4879 -- **Overall Score:** 0.6952 -- **Pages:** MiniPdf=1, Reference=2 -- **File Size:** MiniPdf=422597 bytes, Reference=99625 bytes - -<details><summary>Text Diff</summary> - -```diff ---- minipdf/docx_classic34_employee_directory_with_photo.pdf -+++ reference/docx_classic34_employee_directory_with_photo.pdf -@@ -10,4 +10,5 @@ - Carol Williams - - UX Designer - - Email: carol@company.com - -+---PAGE--- +- **Visual Average:** 0.9969 +- **Overall Score:** 0.9988 +- **Pages:** MiniPdf=2, Reference=2 +- **File Size:** MiniPdf=975599 bytes, Reference=99625 bytes - Department: Design -``` -</details> +Text content: ✅ Identical ### docx_classic34_paragraph_borders @@ -2806,7 +2789,6 @@ Text content: ✅ Identical Table: orders Column Type Constraints Description - ``` </details> @@ -2853,7 +2835,6 @@ Text content: ✅ Identical Component 3.2 handles specific operations within Module 3. 3.2.1 Sub-component - ``` </details> @@ -3385,7 +3366,6 @@ Text content: ✅ Identical @@ -239,11 +239,11 @@ The rapid evolution of cloud computing and containerization has transformed how teams - ... (671 more characters) ``` @@ -3618,7 +3598,6 @@ Text content: ✅ Identical Chapter 5: Discussion 30 Chapter 6: Conclusion 35 - ``` </details> @@ -3675,7 +3654,6 @@ Text content: ✅ Identical Open source software has become the foundation of modern software development. Projects like Linux, Kubernetes, and .NET have demonstrated how community-driven - ``` </details> @@ -3740,7 +3718,6 @@ Text content: ✅ Identical E008 Henry Wilson Engineering DevOps $110,000 Engineer - ``` </details> @@ -3949,7 +3926,6 @@ Text content: ✅ Identical benchmark pipeline with 60 test cases. - ``` </details> @@ -3984,7 +3960,6 @@ Text content: ✅ Identical alternating row colors in both DOCX and XLSX formats. Q8: How do I report a bug? - ``` </details> @@ -4037,7 +4012,6 @@ Text content: ✅ Identical Documentation A R C C Release R I C A - ``` </details> @@ -4170,7 +4144,6 @@ Text content: ✅ Identical arrangements are available. - ``` </details> @@ -4259,7 +4232,6 @@ Text content: ✅ Identical Proceedings of ICSE 2024, pp. 445-460. [7] Taylor, P. (2025). CJK Font Embedding in Portable Documents. International Journal of - ``` </details> @@ -4300,7 +4272,6 @@ Text content: ✅ Identical Benchmark pipeline and self-evolution cycle. Notes: - ``` </details> @@ -4351,7 +4322,6 @@ Text content: ✅ Identical Revenue Users Test Cases Quality Score $2.4M 12,500 180 97.2% - ``` </details> @@ -4406,7 +4376,6 @@ Text content: ✅ Identical Document Rendering Without Native Dependencies." Software: Practice and Experience, 54(7), 1120-1145. - ``` </details> @@ -4504,7 +4473,6 @@ Text content: ✅ Identical CJK Support: Enable Chinese, Japanese, and Korean font embedding for international documents. - ``` </details> @@ -4555,7 +4523,6 @@ Text content: ✅ Identical EMP-044 Grace Miller Operations Director New York EMP-045 David Williams HR Coordinator Berlin - ``` </details> @@ -4610,7 +4577,6 @@ Text content: ✅ Identical 12:30 - 14:00 Lunch Break Restaurant 14:00 - 14:45 Benchmark-Driven Room A - ``` </details> @@ -4641,7 +4607,6 @@ Text content: ✅ Identical Quality Assurance Testing Procedure 1. Purpose - ``` </details> @@ -4686,7 +4651,6 @@ Text content: ✅ Identical ---PAGE--- Section 3: Conclusions (Portrait) - ``` </details> @@ -4737,7 +4701,6 @@ Text content: ✅ Identical Workshop (Per Seat) - ``` </details> @@ -4894,7 +4857,6 @@ Text content: ✅ Identical Planning 11:20 Open Discussion & All 10 min - ``` </details> @@ -4949,7 +4911,6 @@ Text content: ✅ Identical Release Risks & Issues - ``` </details> @@ -5106,7 +5067,6 @@ Text content: ✅ Identical Currency: £ € ¥ ₩ ₹ ₽ Math: ± × ÷ ≈ ≠ ≤ ≥ ∞ √ π - ``` </details> @@ -5143,7 +5103,6 @@ Text content: ✅ Identical 2. Problem Statement • Heavy dependency on Microsoft Office installations for PDF generation - ``` </details> @@ -5497,7 +5456,6 @@ Text content: ✅ Identical work Double border below this paragraph - ``` </details> @@ -5507,6 +5465,5 @@ Text content: ✅ Identical 1. **docx_classic50_long_table_with_formatting** (score: 0.5607) 1. **docx_classic60_project_status_with_milestones** (score: 0.6927) -1. **docx_classic34_employee_directory_with_photo** (score: 0.6952) Review the text diffs and visual comparisons above to identify specific rendering issues.