Files
pdfcraft/packaging/windows/pdfcraft.wxs
T
f61a1e5465 M14: "Edit with PdfCraft" on the File Explorer menu of PDF files (#682) (#1180)
* M14: "Edit with PdfCraft" on the File Explorer menu of PDF files (#682)

The Windows MSI adds a classic shell verb under
HKLM\Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit that
runs "pdfcraft.exe" "%1" with the app's icon. It appears whichever app is
the default for .pdf and never changes that default. On Windows 11 it is
under "Show more options": a first-level entry needs an IExplorerCommand
handler and package identity, which an MSI doesn't have.

MultiSelectModel is Document: several selected PDFs (up to 15) start one
launch each, and the existing single-instance hand-off opens them as tabs
of one window.

The verb has its own component, conditioned on a new secure public
property INSTALLPDFCONTEXTMENU (default 1, a welcome-dialog checkbox,
INSTALLPDFCONTEXTMENU=0 for unattended installs), following
INSTALLDESKTOPSHORTCUT. Its menu value is the key path, so uninstall
removes every value. The portable zip still registers nothing.

test-msi.ps1 checks the compiled Registry, Component, Property and
CheckBox rows, and that the .pdf default is never written. Docs: README,
docs/releasing.md, parity notes, ROADMAP log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Leave ROADMAP.md to main (its log moved on); the change is described in the PR

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-11 18:35:02 -04:00

184 lines
16 KiB
XML

<?xml version="1.0" encoding="utf-8"?>
<!--
PdfCraft MSI (WiX Toolset v5: `dotnet tool install -g wix`). Built by package.ps1:
wix build pdfcraft.wxs installer-ui.wxs -arch x64 -d Version=1.2.3 -d BinDir=... -d ModelsDir=... -d IconPath=... -o out.msi
Per-machine install into Program Files, a Start Menu shortcut, a desktop shortcut (on by default,
optional), an App Paths entry, PdfCraft registered as a PDF handler (offered under "Open with"
and in Default apps, never forced as the default), and "Edit with PdfCraft" on PDF files'
right-click menu (on by default, optional). Files opened with PdfCraft arrive on its command
line.
-->
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package
Name="PdfCraft"
Manufacturer="Learning Machines LLC"
Version="$(var.Version)"
UpgradeCode="EA00C367-7270-4C50-9D8B-A7A5C88AD27F"
Scope="perMachine"
Compressed="yes"
Language="1033">
<SummaryInformation Description="PdfCraft PDF workbench" Manufacturer="Learning Machines LLC" />
<!-- HKLM registrations must never point into a per-user installation (#305). Allow removal
of an existing installation even if an older MSI let it install per-user. -->
<Launch Condition="ALLUSERS = 1 OR (Installed AND REMOVE = &quot;ALL&quot;)"
Message="PdfCraft must be installed for all users. Run setup with administrator privileges and ALLUSERS=1; per-user installation is not supported." />
<!-- Same-version upgrades let successive pre-releases of one X.Y.Z replace each other
(MSI versions are numeric, so 1.2.3-rc.1 and 1.2.3-rc.2 are both 1.2.3). -->
<MajorUpgrade AllowSameVersionUpgrades="yes"
DowngradeErrorMessage="A newer version of PdfCraft is already installed." />
<MediaTemplate EmbedCab="yes" CompressionLevel="high" />
<!-- Windows Installer requires an Icon id ending in .ico or .exe, or advertised shortcuts and
Add/Remove Programs show a blank icon (#57). -->
<Icon Id="PdfcraftIcon.ico" SourceFile="$(var.IconPath)" />
<Property Id="ARPPRODUCTICON" Value="PdfcraftIcon.ico" />
<Property Id="ARPURLINFOABOUT" Value="https://getartcraft.com/apps/pdfcraft" />
<Property Id="ARPURLUPDATEINFO" Value="https://getartcraft.com/apps/pdfcraft" />
<Property Id="ARPHELPLINK" Value="https://discord.gg/artcraft" />
<!-- Desktop shortcut (#143): on by default, a checkbox in the full-UI welcome dialog, and
INSTALLDESKTOPSHORTCUT=0 on the msiexec command line for unattended installs. Secure, so the
choice made in the (unelevated) UI reaches the elevated install. -->
<Property Id="INSTALLDESKTOPSHORTCUT" Value="1" Secure="yes" />
<!-- "Edit with PdfCraft" on the right-click menu of PDF files in File Explorer (#682): on by
default, the same way, and INSTALLPDFCONTEXTMENU=0 for unattended installs without it. -->
<Property Id="INSTALLPDFCONTEXTMENU" Value="1" Secure="yes" />
<UIRef Id="PdfcraftInstallerUI" />
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="PdfCraft">
<Directory Id="ModelsFolder" Name="models" />
</Directory>
</StandardDirectory>
<StandardDirectory Id="ProgramMenuFolder" />
<StandardDirectory Id="DesktopFolder" />
<Feature Id="Main" Title="PdfCraft" Level="1">
<ComponentGroupRef Id="PdfcraftFiles" />
<ComponentGroupRef Id="PdfcraftModels" />
</Feature>
</Package>
<Fragment>
<ComponentGroup Id="PdfcraftFiles" Directory="INSTALLFOLDER">
<Component Id="PdfcraftApp" Guid="E724C1C6-091F-4E21-A685-80D089FDBE21">
<File Id="PdfcraftExe" Source="$(var.BinDir)\pdfcraft.exe" KeyPath="yes" />
<RegistryValue Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\App Paths\pdfcraft.exe" Value="[#PdfcraftExe]" Type="string" />
<!-- PDF files (#107): PdfCraft is offered under "Open with" and in Settings ▸ Default
apps, through Windows' Default Programs registration (a ProgID, OpenWithProgids,
Capabilities and RegisteredApplications). It never takes over an existing default:
.pdf's own default value is left alone, and the user chooses. Without this, the only
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" />
<!-- 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" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities" Name="ApplicationDescription" Value="Open-source PDF workbench" Type="string" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities" Name="ApplicationIcon" Value="[#PdfcraftExe],0" Type="string" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Capabilities\FileAssociations" Name=".pdf" Value="PdfCraft.Document" Type="string" />
<RegistryValue Root="HKLM" Key="Software\RegisteredApplications" Name="PdfCraft" Value="Software\PdfCraft\Capabilities" Type="string" />
<!-- Image context menu (#190): convert without changing image file defaults. -->
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.png\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpg\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpeg\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tif\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.tiff\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.gif\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.bmp\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jp2\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.j2k\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Value="Create PDF with PdfCraft…" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf" Name="MultiSelectModel" Value="Single" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.jpx\shell\PdfCraft.CreatePdf\command" Value="&quot;[#PdfcraftExe]&quot; --create-images &quot;%1&quot;" Type="string" />
</Component>
<!-- Shortcuts (#143) are plain links to pdfcraft.exe, not advertised MSI shortcuts: advertised
ones point at a Windows Installer descriptor rather than the program, which Windows can't
use as a file handler (#107), and 0.2.1's advertised one went missing from the Start menu
(#143). Each has its own component with an HKLM key path, so Windows Installer removes it
on uninstall; in this per-machine package both folders are the all-users ones. -->
<Component Id="PdfcraftStartMenuShortcut" Directory="ProgramMenuFolder" Guid="DC028192-C233-4F99-9A77-ED458C60DF26">
<Shortcut Id="StartMenuShortcut" Name="PdfCraft" Description="PdfCraft PDF workbench"
Target="[#PdfcraftExe]" WorkingDirectory="INSTALLFOLDER" Icon="PdfcraftIcon.ico" IconIndex="0" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Shortcuts" Name="StartMenu" Type="integer" Value="1" KeyPath="yes" />
</Component>
<Component Id="PdfcraftDesktopShortcut" Directory="DesktopFolder" Guid="F6948627-32AB-4D78-8865-0FDF417CE442"
Condition="INSTALLDESKTOPSHORTCUT = 1">
<Shortcut Id="DesktopShortcut" Name="PdfCraft" Description="PdfCraft PDF workbench"
Target="[#PdfcraftExe]" WorkingDirectory="INSTALLFOLDER" Icon="PdfcraftIcon.ico" IconIndex="0" />
<RegistryValue Root="HKLM" Key="Software\PdfCraft\Shortcuts" Name="Desktop" Type="integer" Value="1" KeyPath="yes" />
</Component>
<!-- PDF context menu (#682): "Edit with PdfCraft" opens the file in PdfCraft whichever app is
the default for .pdf, which it leaves alone (SystemFileAssociations, like the image
menu). Several PDFs selected (up to 15, Windows' limit for this kind of entry) start one
launch each, and the single-instance hand-off opens them as tabs of one window. On
Windows 11 the entry is under "Show more options": the first-level menu only takes
packaged apps' (IExplorerCommand) entries. Its own component with an HKLM key path, so
INSTALLPDFCONTEXTMENU=0 leaves it out and uninstall removes it. -->
<Component Id="PdfcraftPdfContextMenu" Guid="439F084F-657D-4AC2-A4CC-F099C518D71F"
Condition="INSTALLPDFCONTEXTMENU = 1">
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Value="Edit with PdfCraft" Type="string" KeyPath="yes" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Name="Icon" Value="&quot;[#PdfcraftExe]&quot;,0" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit" Name="MultiSelectModel" Value="Document" Type="string" />
<RegistryValue Root="HKLM" Key="Software\Classes\SystemFileAssociations\.pdf\shell\PdfCraft.Edit\command" Value="&quot;[#PdfcraftExe]&quot; &quot;%1&quot;" Type="string" />
</Component>
<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
where the app looks for them: every file `cargo xtask models` fetched (package.ps1), one
component each, so new models ship without naming them here. -->
<ComponentGroup Id="PdfcraftModels" Directory="ModelsFolder">
<Files Include="$(var.ModelsDir)\*" />
</ComponentGroup>
</Fragment>
</Wix>