M14: give Windows PDF files a dedicated document icon (#890)

* M14: give Windows PDF files a dedicated document icon

Embed the document icon as resource 2 through the existing Windows resource
build and select it in both PDF association registrations. Keep the asset
reproducible and carry its required licence notices in Windows packages.

Formatting, strict Clippy, 1,842 existing workspace tests, all 30 WASM crate
checks, assets, layering and parity pass. The Windows x64 release package
checks pass, and the contributor confirmed the icon in Explorer.

Co-authored-by: Codex <noreply@openai.com>

* Refresh catalog hashes after merging main

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
Joe Abou Serhal
2026-10-10 17:21:30 -04:00
committed by GitHub
co-authored by Codex Brandon Thomas
parent 9ccf77bdf6
commit 892f0b78f0
12 changed files with 127 additions and 12 deletions
+12 -8
View File
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
# Regenerate every app icon from assets/app-icon/pdfcraft.svg (the master vector).
# Regenerate the app icons from assets/app-icon/pdfcraft.svg and the Windows PDF file icon
# from assets/app-icon/pdfcraft-document.svg. Both are master vectors.
#
# Needs: resvg (brew install resvg / cargo install resvg) and python3 (stdlib only, for the .ico).
# On macOS, iconutil also writes the .icns. The outputs are committed, so building and packaging
@@ -39,13 +40,15 @@ done
mkdir -p "$DIR/hicolor/scalable/apps"
cp "$SVG" "$DIR/hicolor/scalable/apps/$ID.svg"
# Windows .ico: PNG-compressed entries, 16-256 px.
ICO_PNGS=()
for s in 16 20 24 32 40 48 64 128 256; do
render "$SVG" "$s" "$TMP/ico-$s.png"
ICO_PNGS+=("$TMP/ico-$s.png")
done
python3 - "$DIR/pdfcraft.ico" "${ICO_PNGS[@]}" <<'PY'
# Windows .ico: PNG-compressed entries, 16-256 px. The document icon has its own resource
# in pdfcraft.exe; the app icon remains the first/default icon.
for name in pdfcraft pdfcraft-document; do
ICO_PNGS=()
for s in 16 20 24 32 40 48 64 128 256; do
render "$DIR/$name.svg" "$s" "$TMP/$name-$s.png"
ICO_PNGS+=("$TMP/$name-$s.png")
done
python3 - "$DIR/$name.ico" "${ICO_PNGS[@]}" <<'PY'
import struct, sys
out, pngs = sys.argv[1], sys.argv[2:]
blobs = [open(p, "rb").read() for p in pngs]
@@ -58,6 +61,7 @@ for b in blobs:
offset += len(b)
open(out, "wb").write(head + entries + data)
PY
done
# macOS .icns.
if command -v iconutil >/dev/null; then
+9
View File
@@ -87,6 +87,12 @@ $Stage = Join-Path $TargetDir "windows-package\$Arch"
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $Stage | Out-Null
Copy-Item (Join-Path $Bin 'pdfcraft.exe'), (Join-Path $Bin 'pdfcraft-cli.exe') $Stage
# The document icon embeds Lucide artwork: carry its ISC notice and the project licences
# beside the binaries in both distributions.
foreach ($f in 'LICENSE-MIT', 'LICENSE-APACHE', 'NOTICE') {
Copy-Item (Join-Path $Root $f) $Stage
}
Copy-Item (Join-Path $Root 'assets\app-icon\LICENSE-document.txt') $Stage
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'pdfcraft.exe') (Join-Path $Stage 'pdfcraft-cli.exe')
@@ -122,6 +128,9 @@ $Portable = Join-Path $TargetDir "windows-package\pdfcraft-$Version-windows-$Arc
Remove-Item -Recurse -Force $Portable -ErrorAction SilentlyContinue
New-Item -ItemType Directory -Force -Path $Portable | Out-Null
Copy-Item (Join-Path $Stage '*.exe') $Portable
foreach ($f in 'NOTICE', 'LICENSE-document.txt') {
Copy-Item (Join-Path $Stage $f) $Portable
}
Copy-Item -Recurse $Models (Join-Path $Portable 'models')
foreach ($f in 'README.md', 'LICENSE', 'LICENSE-MIT', 'LICENSE-APACHE') {
$p = Join-Path $Root $f
+17 -1
View File
@@ -70,10 +70,13 @@
PdfCraft entry to pick was the advertised Start Menu shortcut, which Windows can't
run as a file handler ("This app can't run on your PC"). -->
<RegistryValue Root="HKLM" Key="Software\Classes\PdfCraft.Document" Value="PDF Document" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\PdfCraft.Document\DefaultIcon" Value="[#PdfcraftExe],0" Type="string" />
<!-- A negative index selects the stable icon resource ID, rather than its position.
Resource 1 is the app's lion; resource 2 is the PDF document (build.rs). -->
<RegistryValue Root="HKLM" Key="Software\Classes\PdfCraft.Document\DefaultIcon" Value="&quot;[#PdfcraftExe]&quot;,-2" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\PdfCraft.Document\shell\open\command" Value="&quot;[#PdfcraftExe]&quot; &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\.pdf\OpenWithProgids" Name="PdfCraft.Document" Value="" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\pdfcraft.exe" Name="FriendlyAppName" Value="PdfCraft" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\pdfcraft.exe\DefaultIcon" Value="&quot;[#PdfcraftExe]&quot;,-2" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\pdfcraft.exe\SupportedTypes" Name=".pdf" Value="" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\Applications\pdfcraft.exe\shell\open\command" Value="&quot;[#PdfcraftExe]&quot; &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities" Name="ApplicationName" Value="PdfCraft" Type="string" />
@@ -136,6 +139,19 @@
<Component Id="PdfcraftCli">
<File Id="PdfcraftCliExe" Source="$(var.BinDir)\pdfcraft-cli.exe" KeyPath="yes" />
</Component>
<!-- The embedded PDF icon's ISC notice and project licences travel with the binaries. -->
<Component Id="PdfcraftDocumentIconLicense">
<File Id="PdfcraftDocumentIconLicenseFile" Source="$(var.BinDir)\LICENSE-document.txt" KeyPath="yes" />
</Component>
<Component Id="PdfcraftMitLicense">
<File Id="PdfcraftMitLicenseFile" Source="$(var.BinDir)\LICENSE-MIT" KeyPath="yes" />
</Component>
<Component Id="PdfcraftApacheLicense">
<File Id="PdfcraftApacheLicenseFile" Source="$(var.BinDir)\LICENSE-APACHE" KeyPath="yes" />
</Component>
<Component Id="PdfcraftNotice">
<File Id="PdfcraftNoticeFile" Source="$(var.BinDir)\NOTICE" KeyPath="yes" />
</Component>
</ComponentGroup>
<!-- OCR models (#103), with their licences and ATTRIBUTION.txt, in models\ beside pdfcraft.exe