diff --git a/data/handouts/Images/_Export-Asy.ps1 b/data/handouts/Images/_Export-Asy.ps1 index 7233713..b8392df 100644 --- a/data/handouts/Images/_Export-Asy.ps1 +++ b/data/handouts/Images/_Export-Asy.ps1 @@ -5,7 +5,10 @@ # Per .asy file the pipeline is: # 1. Run asy with -noView to render the .asy to a PDF next to the source. asy is run with # -cd so `import _common;` and other relative imports resolve next to the source file. -# 2. Convert the PDF to SVG via Inkscape (matches the conversion path used by _Export-Ggb.ps1 +# 2. Patch the PDF's non-deterministic metadata (timestamps and IDs that the bundled Ghostscript +# stamps with wall-clock values) so re-rendering an unchanged .asy produces a byte-identical +# PDF and stops creating spurious git diffs. +# 3. Convert the PDF to SVG via Inkscape (matches the conversion path used by _Export-Ggb.ps1 # so handout figures coming from either source render identically downstream). param( # One or more directories, .asy files, or PowerShell wildcards (e.g. 'angles-*.asy'). Default @@ -62,7 +65,40 @@ function Convert-OneAsy { throw "asy failed (exit $($proc.ExitCode))" } - # 2. Convert the PDF to plain SVG via Inkscape. The .com launcher on PATH is the console + # 2. Replace the non-deterministic metadata that asy's bundled Ghostscript stamps with wall-clock + # values (/CreationDate, /ModDate, /ID in the trailer plus a second copy of the dates and a + # DocumentID UUID inside the XMP stream). With those derived from the .asy source instead, + # re-rendering an unchanged .asy yields a byte-identical PDF and stops producing spurious git + # diffs. The Windows builds of GS we have access to (asy's bundled 10.x and MiKTeX's 9.25) + # ignore SOURCE_DATE_EPOCH and the -dOmit* flags, so we patch the bytes ourselves. + $mtimeUtc = (Get-Item -LiteralPath $AsyPath).LastWriteTimeUtc + # Date timestamps for the two formats Ghostscript emits: PDF date string in the trailer /Info, + # ISO 8601 in the XMP stream. UTC normalised so the field length doesn't depend on local TZ. + $pdfDate = "D:{0:yyyyMMddHHmmss}+00'00'" -f $mtimeUtc + $xmpDate = "{0:yyyy-MM-ddTHH:mm:ss}+00:00" -f $mtimeUtc + # Identifiers: SHA-256 of the .asy content gives 64 hex chars. First 32 supply the /ID halves + # (PDF spec calls for 16-byte values); next 32 fill the DocumentID UUID positions. + $hash = (Get-FileHash -LiteralPath $AsyPath -Algorithm SHA256).Hash + $idHex = $hash.Substring(0, 32) + $uuidHex = $hash.Substring(32, 32) + $uuid = '{0}-{1}-{2}-{3}-{4}' -f $uuidHex.Substring(0, 8), $uuidHex.Substring(8, 4), $uuidHex.Substring(12, 4), $uuidHex.Substring(16, 4), $uuidHex.Substring(20, 12) + # Read the PDF as Latin1 so each byte round-trips losslessly through a string (PDFs are mostly + # ASCII outside of stream contents, and our regexes never match inside binary streams). + $pdfBytes = [System.IO.File]::ReadAllBytes($finalPdf) + $pdfText = [System.Text.Encoding]::Latin1.GetString($pdfBytes) + # Trailer /Info dict. + $pdfText = [regex]::Replace($pdfText, "/CreationDate\(D:\d{14}[+\-]\d{2}'\d{2}'\)", "/CreationDate($pdfDate)") + $pdfText = [regex]::Replace($pdfText, "/ModDate\(D:\d{14}[+\-]\d{2}'\d{2}'\)", "/ModDate($pdfDate)") + $pdfText = [regex]::Replace($pdfText, "/ID \[<[0-9A-Fa-f]{32}><[0-9A-Fa-f]{32}>\]", "/ID [<$idHex><$idHex>]") + # XMP metadata stream (uncompressed in asy's output). + $pdfText = [regex]::Replace($pdfText, "[^<]+", "$xmpDate") + $pdfText = [regex]::Replace($pdfText, "[^<]+", "$xmpDate") + $pdfText = [regex]::Replace($pdfText, "xapMM:DocumentID='uuid:[0-9a-fA-F-]+'", "xapMM:DocumentID='uuid:$uuid'") + # Every replacement above is the same length as the value it replaced, so the PDF's xref byte + # offsets stay valid without rebuilding the table. + [System.IO.File]::WriteAllBytes($finalPdf, [System.Text.Encoding]::Latin1.GetBytes($pdfText)) + + # 3. Convert the PDF to plain SVG via Inkscape. The .com launcher on PATH is the console # launcher that waits for inkscape.exe, so -Wait is enough. if (Test-Path -LiteralPath $finalSvg) { Remove-Item -LiteralPath $finalSvg -Force } $proc = Start-Process -FilePath $inkscape -ArgumentList @($finalPdf, '--pdf-poppler', '--export-type=svg', '--export-plain-svg', "--export-filename=$finalSvg") -Wait -PassThru diff --git a/data/handouts/Images/ag-proof.pdf b/data/handouts/Images/ag-proof.pdf index ee3ffd8..fb200fe 100644 Binary files a/data/handouts/Images/ag-proof.pdf and b/data/handouts/Images/ag-proof.pdf differ diff --git a/data/handouts/Images/angles-90-60-30.pdf b/data/handouts/Images/angles-90-60-30.pdf index 8112d7f..b343911 100644 Binary files a/data/handouts/Images/angles-90-60-30.pdf and b/data/handouts/Images/angles-90-60-30.pdf differ diff --git a/data/handouts/Images/angles-Ssa-proof-isosceles.pdf b/data/handouts/Images/angles-Ssa-proof-isosceles.pdf index 4a85fa4..de36d79 100644 Binary files a/data/handouts/Images/angles-Ssa-proof-isosceles.pdf and b/data/handouts/Images/angles-Ssa-proof-isosceles.pdf differ diff --git a/data/handouts/Images/angles-Ssa-proof-one-solution.pdf b/data/handouts/Images/angles-Ssa-proof-one-solution.pdf index 7fb3c88..9cc5912 100644 Binary files a/data/handouts/Images/angles-Ssa-proof-one-solution.pdf and b/data/handouts/Images/angles-Ssa-proof-one-solution.pdf differ diff --git a/data/handouts/Images/angles-Ssa-proof-two-solutions.pdf b/data/handouts/Images/angles-Ssa-proof-two-solutions.pdf index d98c078..1544b70 100644 Binary files a/data/handouts/Images/angles-Ssa-proof-two-solutions.pdf and b/data/handouts/Images/angles-Ssa-proof-two-solutions.pdf differ diff --git a/data/handouts/Images/angles-alternate.pdf b/data/handouts/Images/angles-alternate.pdf index 67b9886..d8f23d7 100644 Binary files a/data/handouts/Images/angles-alternate.pdf and b/data/handouts/Images/angles-alternate.pdf differ diff --git a/data/handouts/Images/angles-asa-proof.pdf b/data/handouts/Images/angles-asa-proof.pdf index 3d82106..f0dfa61 100644 Binary files a/data/handouts/Images/angles-asa-proof.pdf and b/data/handouts/Images/angles-asa-proof.pdf differ diff --git a/data/handouts/Images/angles-complementary-in-triangle.pdf b/data/handouts/Images/angles-complementary-in-triangle.pdf index 37380ab..fd2e210 100644 Binary files a/data/handouts/Images/angles-complementary-in-triangle.pdf and b/data/handouts/Images/angles-complementary-in-triangle.pdf differ diff --git a/data/handouts/Images/angles-complementary-parallel-solution.pdf b/data/handouts/Images/angles-complementary-parallel-solution.pdf index cdcfa05..8520117 100644 Binary files a/data/handouts/Images/angles-complementary-parallel-solution.pdf and b/data/handouts/Images/angles-complementary-parallel-solution.pdf differ diff --git a/data/handouts/Images/angles-complementary-parallel-statement.pdf b/data/handouts/Images/angles-complementary-parallel-statement.pdf index cf46a14..275b801 100644 Binary files a/data/handouts/Images/angles-complementary-parallel-statement.pdf and b/data/handouts/Images/angles-complementary-parallel-statement.pdf differ diff --git a/data/handouts/Images/angles-corresponding-alternate-supplementary-connection.pdf b/data/handouts/Images/angles-corresponding-alternate-supplementary-connection.pdf index 0ba660c..4cda796 100644 Binary files a/data/handouts/Images/angles-corresponding-alternate-supplementary-connection.pdf and b/data/handouts/Images/angles-corresponding-alternate-supplementary-connection.pdf differ diff --git a/data/handouts/Images/angles-corresponding.pdf b/data/handouts/Images/angles-corresponding.pdf index e32c8d2..7132d82 100644 Binary files a/data/handouts/Images/angles-corresponding.pdf and b/data/handouts/Images/angles-corresponding.pdf differ diff --git a/data/handouts/Images/angles-equilateral.pdf b/data/handouts/Images/angles-equilateral.pdf index c49b5aa..b8c41c3 100644 Binary files a/data/handouts/Images/angles-equilateral.pdf and b/data/handouts/Images/angles-equilateral.pdf differ diff --git a/data/handouts/Images/angles-isosceles-right-triangle.pdf b/data/handouts/Images/angles-isosceles-right-triangle.pdf index 18db953..d50cb66 100644 Binary files a/data/handouts/Images/angles-isosceles-right-triangle.pdf and b/data/handouts/Images/angles-isosceles-right-triangle.pdf differ diff --git a/data/handouts/Images/angles-isosceles-triangle.pdf b/data/handouts/Images/angles-isosceles-triangle.pdf index 67e9be2..8a3b542 100644 Binary files a/data/handouts/Images/angles-isosceles-triangle.pdf and b/data/handouts/Images/angles-isosceles-triangle.pdf differ diff --git a/data/handouts/Images/angles-polygon-fan.pdf b/data/handouts/Images/angles-polygon-fan.pdf index 564cd92..877c3f3 100644 Binary files a/data/handouts/Images/angles-polygon-fan.pdf and b/data/handouts/Images/angles-polygon-fan.pdf differ diff --git a/data/handouts/Images/angles-polygon-interior.pdf b/data/handouts/Images/angles-polygon-interior.pdf index b5580fa..efcc06a 100644 Binary files a/data/handouts/Images/angles-polygon-interior.pdf and b/data/handouts/Images/angles-polygon-interior.pdf differ diff --git a/data/handouts/Images/angles-polygon.pdf b/data/handouts/Images/angles-polygon.pdf index 7dc7ff9..2fac12f 100644 Binary files a/data/handouts/Images/angles-polygon.pdf and b/data/handouts/Images/angles-polygon.pdf differ diff --git a/data/handouts/Images/angles-sas-proof.pdf b/data/handouts/Images/angles-sas-proof.pdf index 6e7b64f..214ed20 100644 Binary files a/data/handouts/Images/angles-sas-proof.pdf and b/data/handouts/Images/angles-sas-proof.pdf differ diff --git a/data/handouts/Images/angles-sas-warning.pdf b/data/handouts/Images/angles-sas-warning.pdf index 74e0e29..867753b 100644 Binary files a/data/handouts/Images/angles-sas-warning.pdf and b/data/handouts/Images/angles-sas-warning.pdf differ diff --git a/data/handouts/Images/angles-sss-proof.pdf b/data/handouts/Images/angles-sss-proof.pdf index 5d7be23..c7d5a55 100644 Binary files a/data/handouts/Images/angles-sss-proof.pdf and b/data/handouts/Images/angles-sss-proof.pdf differ diff --git a/data/handouts/Images/angles-supplementary-derive-vertical.pdf b/data/handouts/Images/angles-supplementary-derive-vertical.pdf index 7cbbe02..89a006c 100644 Binary files a/data/handouts/Images/angles-supplementary-derive-vertical.pdf and b/data/handouts/Images/angles-supplementary-derive-vertical.pdf differ diff --git a/data/handouts/Images/angles-supplementary.pdf b/data/handouts/Images/angles-supplementary.pdf index a962026..64cebb3 100644 Binary files a/data/handouts/Images/angles-supplementary.pdf and b/data/handouts/Images/angles-supplementary.pdf differ diff --git a/data/handouts/Images/angles-triangle.pdf b/data/handouts/Images/angles-triangle.pdf index d15e708..eed60c5 100644 Binary files a/data/handouts/Images/angles-triangle.pdf and b/data/handouts/Images/angles-triangle.pdf differ diff --git a/data/handouts/Images/angles-vertical-derive-supplementary.pdf b/data/handouts/Images/angles-vertical-derive-supplementary.pdf index 6c25853..1511c41 100644 Binary files a/data/handouts/Images/angles-vertical-derive-supplementary.pdf and b/data/handouts/Images/angles-vertical-derive-supplementary.pdf differ diff --git a/data/handouts/Images/angles-vertical.pdf b/data/handouts/Images/angles-vertical.pdf index 56f468d..3b44013 100644 Binary files a/data/handouts/Images/angles-vertical.pdf and b/data/handouts/Images/angles-vertical.pdf differ diff --git a/data/handouts/Images/box.pdf b/data/handouts/Images/box.pdf index 5e386a6..05dd426 100644 Binary files a/data/handouts/Images/box.pdf and b/data/handouts/Images/box.pdf differ