macOS DMG: branded window background and icon layout

The DMG opened as a plain Finder window. It now shows the app icon's figure on
the CADCraft colour, with CADCraft.app and the Applications link side by side.
Same approach as PhotoCraft's and VectorCraft's (storytold/photocraft#1201,
storytold/vectorcraft#493).

- packaging/macos/dmg/: background.svg (source; text outlined, no paper grain,
  the figure an <image> of assets/app-icon), background.tiff (1x + 2x HiDPI,
  16-colour palette, Deflate, sRGB; ~21 KB)
  and dmg-layout.DS_Store (Finder view settings: window, icon size 128,
  positions, background).
- generate.py writes the TIFF and the layout on any OS, byte-for-byte
  reproducible, with nothing from the machine that ran it (no paths, users,
  disk names or volume UUIDs).
- package.sh copies them into the staging folder; the image is still built with
  hdiutil makehybrid. Nothing extra runs in the signing job.
- The volume name drops the version: the layout finds the background through
  an alias that includes the volume name. The DMG file name keeps the version.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Xus BadiaandClaude Opus 5.5 committed 2026-10-09 07:43:54 +02:00
1 parent 59631c8d4f
commit 15c35620c4
7 files changed
+244 -1

No files matched your search

+2
View File
@@ -37,6 +37,8 @@ Generated-in-code assets are original and have no file to list:
| `assets/app-icon/cadcraft-256.png` | CADCraft contributors | generated | MIT OR Apache-2.0 | |
| `assets/app-icon/cadcraft-1024.png` | CADCraft contributors | generated | MIT OR Apache-2.0 | |
| `assets/app-icon/cadcraft-macos-512.png` | CADCraft contributors | generated (macOS margin variant) | MIT OR Apache-2.0 | |
| `packaging/macos/dmg/background.svg` | @XusBadia | original work for this repository (2026-10-08), "CADCraft macOS DMG window background"; derived from the CADCraft app icon (`assets/app-icon/`, referenced via `<image>`, not copied); text is outlined Inter / JetBrains Mono glyph paths (OFL fonts the project already uses); no third-party material modified | MIT OR Apache-2.0 (same as the code: `LICENSE-MIT`, `LICENSE-APACHE`) | Finder window background of the macOS DMG; `packaging/macos/dmg/generate.py` renders it to `background.tiff` (see `packaging/macos/dmg/README.md`) |
| `packaging/macos/dmg/background.tiff` | @XusBadia | Rendered from `packaging/macos/dmg/background.svg` by `packaging/macos/dmg/generate.py` | MIT OR Apache-2.0 (same as the code: `LICENSE-MIT`, `LICENSE-APACHE`) | 1x + 2x HiDPI TIFF for the macOS DMG window; includes the app icon (see its row) and LittleCMS's built-in sRGB profile ("No copyright, use freely") |
| `examples/apartment.dxf` | CADCraft contributors | sample drawing made with cadcraft-cli commands | MIT OR Apache-2.0 | Original |
| `docs/images/ui-apartment.png` | CADCraft contributors | screenshot of CADCraft itself (examples/apartment.dxf) | MIT OR Apache-2.0 | Original; no Autodesk UI |
| `docs/images/ui-layout.png` | CADCraft contributors | screenshot of CADCraft itself (a layout of examples/apartment.dxf) | MIT OR Apache-2.0 | Original; no Autodesk UI |
+44
View File
@@ -0,0 +1,44 @@
# macOS DMG window
What Finder shows when the DMG opens: a 660 × 400 pt background with the app icon and the
`Applications` link side by side. `package.sh` copies these files into the image; nothing here is
generated at build time, so the DMG still builds with `hdiutil makehybrid` (no mounted device, no
Finder scripting on CI, nothing extra installed in the signing job).
| File | What |
|---|---|
| `background.svg` | Source of the background: the app icon (`assets/app-icon/cadcraft-1024.png`, linked, not copied) cropped as a cover on the CADCraft colour field (`#14a3c7`), Ink and Paper. Its text (Inter, JetBrains Mono) is outlined, so rendering it needs no fonts. No paper grain: noise doesn't compress, and this keeps `background.tiff` small. |
| `background.tiff` | The background at 1x (660 × 400 px, 72 dpi) and 2x (1320 × 800 px, 144 dpi) in one HiDPI TIFF (16-colour palette, Deflate, sRGB). Goes to `.background/background.tiff`. |
| `dmg-layout.DS_Store` | Finder's view settings for the volume: window size, icon size 128, CADCraft.app at (326, 205), `Applications` at (574, 205), and the background. Goes to `.DS_Store` in the image; named so it isn't mistaken for (or ignored like) a Finder-generated `.DS_Store`. |
| `generate.py` | Writes `background.tiff` and `dmg-layout.DS_Store` from the SVG and the layout above. |
## The volume name has no version
The mounted volume is called `CADCraft`, not `CADCraft <version>`. The layout points at the background
through an alias that includes the volume name, and Finder resolves it by that name: with a
versioned name the window keeps its size and icon positions but shows no background (tested).
The DMG file name (`cadcraft-<version>-macos-<arch>.dmg`) still carries the version.
## Rules
- **Finder draws the icon labels in black in light and dark mode** when a window has a background,
so the area under both icons stays light (Paper).
- **Nothing goes inside the icon boxes:** artwork keeps 10 pt clear of each 128 pt icon box and of
the label strip under it.
## Regenerate
Edit `background.svg` (or the layout constants in `generate.py`), then run, on any OS:
```sh
pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3
python3 packaging/macos/dmg/generate.py # needs resvg on PATH
```
Both outputs are byte-for-byte reproducible and carry nothing from the machine that ran it: the
SVG renders without any fonts, and `dmg-layout.DS_Store` is written from scratch, its background
alias holding only the volume name and `/.background/background.tiff`. If you add text to the SVG,
outline it (`usvg` from resvg converts text to paths). The window is 660 × 432 with Finder's 32 pt
title bar; the content area is 660 × 400.
This follows VectorCraft's `packaging/macos/dmg/` (storytold/vectorcraft#493).
+34
View File
@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="660" height="400" viewBox="0 0 660 400">
<defs><clipPath id="panel"><rect width="232" height="400"/></clipPath><pattern id="hatch" width="2.6" height="2.6" patternUnits="userSpaceOnUse" patternTransform="rotate(-38)">
<rect width="2.6" height="1.05" fill="#0b0b0c"/></pattern><filter id="grain" x="0" y="0" width="100%" height="100%">
<feTurbulence type="fractalNoise" baseFrequency="0.9" numOctaves="2" seed="7"/>
<feColorMatrix values="0 0 0 0 0.04 0 0 0 0 0.04 0 0 0 0 0.05 0.5 0 0 0 -0.225"/>
</filter></defs>
<rect width="660" height="400" fill="#efe9dc"/>
<g clip-path="url(#panel)"><rect width="232" height="400" fill="#14a3c7"/><g transform="translate(-49.6,5.6) scale(0.72)"><image href="../../../assets/app-icon/cadcraft-1024.png" width="512" height="512"/></g></g>
<line x1="232.5" y1="0" x2="232.5" y2="400" stroke="#0b0b0c" stroke-width="1.2"/>
<line x1="236.5" y1="0" x2="236.5" y2="400" stroke="#0b0b0c" stroke-width="0.5"/>
<line x1="267" y1="88.5" x2="624" y2="88.5" stroke="#0b0b0c" stroke-width="0.6"/>
<path d="M271.5,81.5 L276,88.5 L271.5,95.5 L267,88.5 Z" fill="#14a3c7"/>
<line x1="408" y1="191" x2="408" y2="213" stroke="#0b0b0c" stroke-width="0.7"/><line x1="404" y1="205" x2="482" y2="205" stroke="#0b0b0c" stroke-width="1"/><line x1="404" y1="209" x2="412" y2="201" stroke="#0b0b0c" stroke-width="1.8"/><path d="M498.6,205.0 L486.6,210.0 L486.6,200.0 Z" fill="#0b0b0c"/>
<line x1="267" y1="335.5" x2="624" y2="335.5" stroke="#0b0b0c" stroke-width="0.6"/>
<g id="text">
<g>
<path fill="#0b0b0c" stroke="none" d="M 277.49902 72.29297 Q 274.62793 72.29297 272.37207 70.95996 Q 270.1162 69.62695 268.8125 67.12207 Q 267.5088 64.61719 267.5088 61.101563 Q 267.5088 57.571285 268.81982 55.059082 Q 270.13086 52.54688 272.38672 51.213863 Q 274.64258 49.88086 277.49902 49.88086 Q 279.88672 49.88086 281.8423 50.774414 Q 283.79785 51.66797 285.07227 53.359863 Q 286.34668 55.051754 286.69824 57.439453 L 282.7578 57.439453 Q 282.3916 55.50586 280.95605 54.443848 Q 279.5205 53.381836 277.55762 53.381836 Q 274.87695 53.381836 273.15576 55.381344 Q 271.43457 57.38086 271.43457 61.101563 Q 271.43457 64.86621 273.1631 66.8291 Q 274.8916 68.79199 277.54297 68.79199 Q 279.50586 68.79199 280.94873 67.72998 Q 282.3916 66.66797 282.7578 64.734375 L 286.7129 64.734375 Q 286.40527 66.8291 285.2041 68.54297 Q 284.00293 70.256836 282.04004 71.2749 Q 280.07715 72.29297 277.49902 72.29297 Z M 287.58984 72 L 295.20703 50.17383 L 300.17285 50.17383 L 307.96582 72 L 303.67383 72 L 301.81348 66.59473 L 293.6543 66.59473 L 291.8672 72 Z M 294.69434 63.445316 L 300.74414 63.445316 L 299.792 60.66211 Q 299.3086 59.15332 298.78857 57.322266 Q 298.26855 55.49121 297.65332 53.220707 Q 297.06738 55.50586 296.56934 57.35889 Q 296.0713 59.211914 295.6172 60.66211 Z M 317.76367 72 L 310.38086 72 L 310.38086 50.17383 L 317.91016 50.17383 Q 321.1621 50.17383 323.49854 51.484863 Q 325.83496 52.7959 327.08008 55.234863 Q 328.3252 57.673832 328.3252 61.057617 Q 328.3252 64.4707 327.07275 66.92432 Q 325.8203 69.37793 323.4546 70.688965 Q 321.08887 72 317.76367 72 Z M 314.292 68.61621 L 317.57324 68.61621 Q 324.47266 68.61621 324.47266 61.057617 Q 324.47266 53.557617 317.67578 53.557617 L 314.292 53.557617 Z M 340.84766 72.29297 Q 337.97656 72.29297 335.7207 70.95996 Q 333.46484 69.62695 332.16113 67.12207 Q 330.85742 64.61719 330.85742 61.101563 Q 330.85742 57.571285 332.16846 55.059082 Q 333.4795 52.54688 335.73535 51.213863 Q 337.9912 49.88086 340.84766 49.88086 Q 343.23535 49.88086 345.19092 50.774414 Q 347.14648 51.66797 348.42093 53.359863 Q 349.6953 55.051754 350.04688 57.439453 L 346.1064 57.439453 Q 345.74023 55.50586 344.3047 54.443848 Q 342.86914 53.381836 340.90625 53.381836 Q 338.2256 53.381836 336.5044 55.381344 Q 334.7832 57.38086 334.7832 61.101563 Q 334.7832 64.86621 336.51172 66.8291 Q 338.24023 68.79199 340.8916 68.79199 Q 342.8545 68.79199 344.29736 67.72998 Q 345.74023 66.66797 346.1064 64.734375 L 350.06152 64.734375 Q 349.7539 66.8291 348.55273 68.54297 Q 347.35156 70.256836 345.3887 71.2749 Q 343.42578 72.29297 340.84766 72.29297 Z M 352.9746 72 L 352.9746 55.623047 L 356.666 55.623047 L 356.666 58.362305 L 356.8418 58.362305 Q 357.28125 56.941402 358.37988 56.172363 Q 359.47852 55.40332 360.88477 55.40332 Q 361.61722 55.40332 362.27637 55.50586 L 362.27637 59.006836 Q 362.042 58.948246 361.51465 58.882324 Q 360.98734 58.816406 360.4746 58.816406 Q 358.87793 58.816406 357.83057 59.805172 Q 356.78317 60.79394 356.78317 62.36133 L 356.78317 72 Z M 368.6758 72.336914 Q 367.12305 72.336914 365.87793 71.77295 Q 364.6328 71.208984 363.91507 70.11035 Q 363.19727 69.01172 363.19727 67.40039 Q 363.19727 65.55469 364.09814 64.53662 Q 364.99902 63.518555 366.42725 63.057125 Q 367.85544 62.595703 369.4375 62.43457 Q 371.57617 62.200195 372.52832 62.00244 Q 373.4805 61.804688 373.4805 60.95508 L 373.4805 60.88184 Q 373.4805 59.680664 372.74805 59.01416 Q 372.01563 58.34766 370.62402 58.34766 Q 369.1885 58.34766 368.33887 58.977543 Q 367.48926 59.607426 367.1963 60.413086 L 363.66602 59.695313 Q 364.38382 57.527344 366.2661 56.472656 Q 368.14844 55.41797 370.60934 55.41797 Q 371.7666 55.41797 372.9458 55.688965 Q 374.125 55.95996 375.10645 56.597168 Q 376.0879 57.234375 376.68848 58.325684 Q 377.28906 59.416992 377.28906 61.057617 L 377.28906 72 L 373.64157 72 L 373.64157 69.74414 L 373.49512 69.74414 Q 372.95313 70.79883 371.7666 71.56787 Q 370.58008 72.336914 368.6758 72.336914 Z M 369.65723 69.509766 Q 371.41504 69.509766 372.4624 68.49902 Q 373.50977 67.48828 373.50977 66.12598 L 373.50977 64.19238 Q 373.24606 64.39746 372.5723 64.55127 Q 371.89844 64.70508 371.14404 64.81494 Q 370.38965 64.924805 369.8916 64.99805 Q 368.6025 65.17383 367.7456 65.70117 Q 366.88867 66.228516 366.88867 67.356445 Q 366.88867 68.41113 367.66504 68.96045 Q 368.44144 69.509766 369.65723 69.509766 Z M 389.4453 55.623047 L 389.4453 58.61133 L 385.9004 58.61133 L 385.9004 72 L 382.10645 72 L 382.10645 58.61133 L 379.10352 58.61133 L 379.10352 55.623047 L 382.10645 55.623047 L 382.10645 54.055664 Q 382.10645 52.429688 382.79492 51.33838 Q 383.4834 50.24707 384.63327 49.697758 Q 385.7832 49.148438 387.18948 49.148438 Q 388.2002 49.148438 388.97656 49.309566 Q 389.7529 49.470703 390.11914 49.60254 L 389.34277 52.561527 Q 389.09375 52.488277 388.73486 52.407715 Q 388.37598 52.32715 387.89258 52.32715 Q 386.82324 52.32715 386.36185 52.854492 Q 385.9004 53.381836 385.9004 54.348633 L 385.9004 55.623047 Z M 398.4375 55.623047 L 398.4375 5Line truncated
</g>
<g>
<path fill="#0b0b0c" fill-opacity="0.7" stroke="none" d="M 267.36 112 L 269.25 108.670006 L 267.459 105.42999 L 268.395 105.42999 L 269.412 107.374 Q 269.493 107.527 269.5785 107.7025 Q 269.664 107.878 269.718 107.977 Q 269.763 107.878 269.8485 107.7025 Q 269.934 107.527 270.015 107.374 L 271.05 105.42999 L 271.941 105.42999 L 270.15 108.616 L 272.04 112 L 271.113 112 L 269.988 109.912 Q 269.907 109.75901 269.8215 109.5835 Q 269.736 109.408 269.691 109.299995 Q 269.646 109.408 269.5605 109.578995 Q 269.475 109.74999 269.394 109.903 L 268.26 112 Z M 280.44598 112.09 Q 279.537 112.09 279.00598 111.581505 Q 278.47498 111.07299 278.47498 110.19999 L 279.28497 110.19999 Q 279.28497 110.74 279.59998 111.055 Q 279.91498 111.37 280.455 111.37 Q 280.995 111.37 281.31 111.055 Q 281.625 110.74 281.625 110.19999 L 281.625 109.795 Q 281.625 109.255 281.319 108.94 Q 281.013 108.625 280.491 108.625 L 278.69998 108.625 L 278.69998 105.42999 L 282.201 105.42999 L 282.201 106.168 L 279.456 106.168 L 279.474 107.887 L 280.53598 107.887 Q 281.44498 107.887 281.94 108.3865 Q 282.435 108.88601 282.435 109.795 L 282.435 110.19999 Q 282.435 111.07299 281.89948 111.581505 Q 281.36398 112.09 280.44598 112.09 Z M 284.91 112 L 287.25 106.168 L 284.74802 106.168 L 284.74802 107.23 L 283.93802 107.23 L 283.93802 105.42999 L 288.114 105.42999 L 288.114 106.186 L 285.79202 112 Z M 292.2 112 L 292.2 110.56 L 289.23 110.56 L 289.23 109.291 L 291.624 105.42999 L 292.542 105.42999 L 290.04 109.47099 L 290.04 109.82201 L 292.2 109.82201 L 292.2 108.21999 L 293.01 108.21999 L 293.01 112 Z M 296.7 112.09 Q 296.367 112.09 296.1645 111.892 Q 295.962 111.694 295.962 111.379 Q 295.962 111.046 296.1645 110.839 Q 296.367 110.63201 296.7 110.63201 Q 297.033 110.63201 297.2355 110.839 Q 297.438 111.046 297.438 111.379 Q 297.438 111.694 297.2355 111.892 Q 297.033 112.09 296.7 112.09 Z M 302.1 112.09 Q 301.19098 112.09 300.6555 111.5635 Q 300.12 111.037 300.12 110.155 L 300.12 107.275 Q 300.12 106.393 300.6555 105.86649 Q 301.19098 105.34 302.1 105.34 Q 303.009 105.34 303.5445 105.86649 Q 304.08 106.393 304.08 107.275 L 304.08 110.155 Q 304.08 110.74 303.837 111.1765 Q 303.594 111.613 303.1485 111.8515 Q 302.703 112.09 302.1 112.09 Z M 302.1 111.39701 Q 302.63998 111.39701 302.9685 111.0505 Q 303.297 110.704 303.297 110.155 L 303.297 107.275 Q 303.297 106.726 302.9685 106.3795 Q 302.63998 106.033 302.1 106.033 Q 301.56 106.033 301.2315 106.3795 Q 300.90298 106.726 300.90298 107.275 L 300.90298 110.155 Q 300.90298 110.704 301.2315 111.0505 Q 301.56 111.39701 302.1 111.39701 Z M 302.1 109.21 Q 301.857 109.21 301.7085 109.075 Q 301.56 108.94 301.56 108.688 Q 301.56 108.445 301.7085 108.3145 Q 301.857 108.184006 302.1 108.184006 Q 302.343 108.184006 302.4915 108.3145 Q 302.63998 108.445 302.63998 108.688 Q 302.63998 108.94 302.4915 109.075 Q 302.343 109.21 302.1 109.21 Z M 307.5 112.09 Q 306.59097 112.09 306.05548 111.5635 Q 305.52 111.037 305.52 110.155 L 305.52 107.275 Q 305.52 106.393 306.05548 105.86649 Q 306.59097 105.34 307.5 105.34 Q 308.409 105.34 308.9445 105.86649 Q 309.47998 106.393 309.47998 107.275 L 309.47998 110.155 Q 309.47998 110.74 309.237 111.1765 Q 308.994 111.613 308.5485 111.8515 Q 308.103 112.09 307.5 112.09 Z M 307.5 111.39701 Q 308.03998 111.39701 308.3685 111.0505 Q 308.697 110.704 308.697 110.155 L 308.697 107.275 Q 308.697 106.726 308.3685 106.3795 Q 308.03998 106.033 307.5 106.033 Q 306.96 106.033 306.6315 106.3795 Q 306.30298 106.726 306.30298 107.275 L 306.30298 110.155 Q 306.30298 110.704 306.6315 111.0505 Q 306.96 111.39701 307.5 111.39701 Z M 307.5 109.21 Q 307.257 109.21 307.1085 109.075 Q 306.96 108.94 306.96 108.688 Q 306.96 108.445 307.1085 108.3145 Q 307.257 108.184006 307.5 108.184006 Q 307.74298 108.184006 307.89148 108.3145 Q 308.03998 108.445 308.03998 108.688 Q 308.03998 108.94 307.89148 109.075 Q 307.74298 109.21 307.5 109.21 Z M 317.89502 112 L 317.89502 109.54301 L 315.915 105.42999 L 316.779 105.42999 L 318.057 108.13 Q 318.165 108.355 318.228 108.512505 Q 318.29102 108.670006 318.30902 108.742004 Q 318.327 108.670006 318.3945 108.512505 Q 318.462 108.355 318.57 108.13 L 319.848 105.42999 L 320.685 105.42999 L 318.70502 109.54301 L 318.70502 112 Z M 327.17398 112 L 327.17398 111.253006 L 329.361 108.913 Q 329.83798 108.409 330.03598 108.01301 Q 330.234 107.617 330.234 107.23899 Q 330.234 106.699 329.9235 106.3795 Q 329.613 106.06 329.082 106.06 Q 328.533 106.06 328.2135 106.388504 Q 327.89398 106.717 327.89398 107.275 L 327.08398 107.275 Q 327.111 106.384 327.651 105.862 Q 328.19098 105.34 329.082 105.34 Q 329.991 105.34 330.5265 105.853 Q 331.06198 106.36599 331.06198 107.24801 Q 331.06198 107.716 330.80548 108.238 Q 330.54898 108.76 329.92798 109.408 L 328.155 111.262 L 331.143 111.262 L 331.143 112 Z M 334.5 112.09 Q 333.59097 112.09 333.05548 111.5635 Q 332.52 111.037 332.52 110.155 L 332.52 107.275 Q 332.52 106.393 333.05548 105.86649 Q 333.59097 105.34 334.5 105.34 Q 335.409 105.34 335.9445 105.86Line truncated
</g>
<g>
<path fill="#14a3c7" stroke="none" d="M 430.57397 199 L 430.57397 198.25302 L 432.761 195.913 Q 433.23798 195.409 433.43597 195.013 Q 433.634 194.61699 433.634 194.239 Q 433.634 193.699 433.3235 193.37949 Q 433.01297 193.06 432.48196 193.06 Q 431.93298 193.06 431.6135 193.3885 Q 431.29398 193.717 431.29398 194.275 L 430.48398 194.275 Q 430.511 193.384 431.051 192.862 Q 431.59097 192.33998 432.48196 192.33998 Q 433.391 192.33998 433.92648 192.853 Q 434.46198 193.366 434.46198 194.248 Q 434.46198 194.716 434.2055 195.238 Q 433.94894 195.75998 433.328 196.40802 L 431.55502 198.26198 L 434.543 198.26198 L 434.543 199 Z M 438.80002 199 L 438.80002 197.56 L 435.83002 197.56 L 435.83002 196.29102 L 438.224 192.43 L 439.142 192.43 L 436.64005 196.471 L 436.64005 196.82202 L 438.80002 196.82202 L 438.80002 195.22002 L 439.61 195.22002 L 439.61 199 Z M 443.30002 199.09 Q 442.67902 199.09 442.202 198.847 Q 441.725 198.604 441.4595 198.1675 Q 441.194 197.731 441.194 197.16402 Q 441.194 196.543 441.5495 196.093 Q 441.905 195.643 442.499 195.544 L 442.499 195.51698 Q 441.986 195.4 441.68 194.9995 Q 441.374 194.599 441.374 194.077 Q 441.374 193.564 441.617 193.1725 Q 441.86002 192.781 442.29654 192.5605 Q 442.733 192.33998 443.30002 192.33998 Q 443.87604 192.33998 444.30804 192.5605 Q 444.74002 192.781 444.983 193.1725 Q 445.226 193.564 445.226 194.077 Q 445.226 194.599 444.92 194.9995 Q 444.614 195.4 444.101 195.51698 L 444.101 195.544 Q 444.695 195.643 445.0505 196.093 Q 445.406 196.543 445.406 197.16402 Q 445.406 197.731 445.1405 198.1675 Q 444.875 198.604 444.4025 198.847 Q 443.93002 199.09 443.30002 199.09 Z M 443.30002 195.20198 Q 443.80402 195.20198 444.11002 194.905 Q 444.416 194.608 444.416 194.13101 Q 444.416 193.654 444.11002 193.35701 Q 443.80402 193.06 443.30002 193.06 Q 442.805 193.06 442.49454 193.35701 Q 442.18402 193.654 442.18402 194.13101 Q 442.18402 194.608 442.49454 194.905 Q 442.805 195.20198 443.30002 195.20198 Z M 443.30002 198.361 Q 443.87604 198.361 444.236 198.0235 Q 444.596 197.686 444.596 197.146 Q 444.596 196.597 444.236 196.2595 Q 443.87604 195.922 443.30002 195.922 Q 442.72397 195.922 442.364 196.2595 Q 442.004 196.597 442.004 197.146 Q 442.004 197.686 442.364 198.0235 Q 442.72397 198.361 443.30002 198.361 Z M 448.7 199.09 Q 448.367 199.09 448.1645 198.892 Q 447.96198 198.69398 447.96198 198.37901 Q 447.96198 198.046 448.1645 197.839 Q 448.367 197.632 448.7 197.632 Q 449.03296 197.632 449.2355 197.839 Q 449.438 198.046 449.438 198.37901 Q 449.438 198.69398 449.2355 198.892 Q 449.03296 199.09 448.7 199.09 Z M 454.1 199.09 Q 453.19098 199.09 452.6555 198.5635 Q 452.12 198.03702 452.12 197.155 L 452.12 194.275 Q 452.12 193.393 452.6555 192.86652 Q 453.19098 192.33998 454.1 192.33998 Q 455.009 192.33998 455.5445 192.86652 Q 456.08 193.393 456.08 194.275 L 456.08 197.155 Q 456.08 197.74 455.837 198.1765 Q 455.594 198.613 455.1485 198.8515 Q 454.70303 199.09 454.1 199.09 Z M 454.1 198.397 Q 454.63998 198.397 454.96854 198.0505 Q 455.297 197.704 455.297 197.155 L 455.297 194.275 Q 455.297 193.726 454.96854 193.37949 Q 454.63998 193.033 454.1 193.033 Q 453.56 193.033 453.2315 193.37949 Q 452.90298 193.726 452.90298 194.275 L 452.90298 197.155 Q 452.90298 197.704 453.2315 198.0505 Q 453.56 198.397 454.1 198.397 Z M 454.1 196.21 Q 453.857 196.21 453.70853 196.07498 Q 453.56 195.94 453.56 195.688 Q 453.56 195.445 453.70853 195.3145 Q 453.857 195.184 454.1 195.184 Q 454.343 195.184 454.49152 195.3145 Q 454.63998 195.445 454.63998 195.688 Q 454.63998 195.94 454.49152 196.07498 Q 454.343 196.21 454.1 196.21 Z M 459.5 199.09 Q 458.59097 199.09 458.05548 198.5635 Q 457.52002 198.03702 457.52002 197.155 L 457.52002 194.275 Q 457.52002 193.393 458.05548 192.86652 Q 458.59097 192.33998 459.5 192.33998 Q 460.409 192.33998 460.94452 192.86652 Q 461.47998 193.393 461.47998 194.275 L 461.47998 197.155 Q 461.47998 197.74 461.237 198.1765 Q 460.994 198.613 460.5485 198.8515 Q 460.103 199.09 459.5 199.09 Z M 459.5 198.397 Q 460.03998 198.397 460.3685 198.0505 Q 460.697 197.704 460.697 197.155 L 460.697 194.275 Q 460.697 193.726 460.3685 193.37949 Q 460.03998 193.033 459.5 193.033 Q 458.96 193.033 458.6315 193.37949 Q 458.303 193.726 458.303 194.275 L 458.303 197.155 Q 458.303 197.704 458.6315 198.0505 Q 458.96 198.397 459.5 198.397 Z M 459.5 196.21 Q 459.257 196.21 459.1085 196.07498 Q 458.96 195.94 458.96 195.688 Q 458.96 195.445 459.1085 195.3145 Q 459.257 195.184 459.5 195.184 Q 459.74298 195.184 459.89148 195.3145 Q 460.03998 195.445 460.03998 195.688 Q 460.03998 195.94 459.89148 196.07498 Q 459.74298 196.21 459.5 196.21 Z"/>
</g>
<g>
<path fill="#0b0b0c" fill-opacity="0.65" stroke="none" d="M 270.4912 358 L 267.88623 358 L 267.88623 349.99707 L 270.5664 349.99707 Q 271.74805 349.99707 272.5913 350.4778 Q 273.43457 350.9585 273.88574 351.8501 Q 274.3369 352.7417 274.3369 353.9878 Q 274.3369 355.23923 273.88306 356.13623 Q 273.4292 357.0332 272.56714 357.5166 Q 271.70508 358 270.4912 358 Z M 269.11084 356.92578 L 270.4214 356.92578 Q 271.78027 356.92578 272.45703 356.16574 Q 273.1338 355.40573 273.1338 353.9878 Q 273.1338 352.5752 272.4651 351.82327 Q 271.7964 351.07126 270.48584 351.07126 L 269.11084 351.07126 Z M 277.3247 358 L 277.3247 349.99707 L 280.19287 349.99707 Q 281.1167 349.99707 281.73438 350.31934 Q 282.35205 350.6416 282.6582 351.20825 Q 282.96436 351.77487 282.96436 352.51074 Q 282.96436 353.3164 282.59912 353.90457 Q 282.2339 354.49268 281.50342 354.7666 L 283.26514 358 L 281.87402 358 L 280.25195 354.97607 Q 280.21973 354.97607 280.18213 354.97607 L 278.54932 354.97607 L 278.54932 358 Z M 278.54932 353.9287 L 280.06396 353.9287 Q 280.94482 353.9287 281.33423 353.55542 Q 281.72363 353.18216 281.72363 352.51074 Q 281.72363 351.83398 281.33154 351.4392 Q 280.93945 351.04443 280.05322 351.04443 L 278.54932 351.04443 Z M 285.33984 358 L 288.18652 349.99707 L 289.69043 349.99707 L 292.59082 358 L 291.26416 358 L 290.52832 355.88916 L 287.38086 355.88916 L 286.6665 358 Z M 287.72998 354.86862 L 290.16846 354.86862 L 289.70654 353.54733 Q 289.5454 353.06934 289.35742 352.43823 Q 289.16943 351.80713 288.9331 350.9585 Q 288.69678 351.81784 288.51147 352.4543 Q 288.32617 353.09082 288.17578 353.54733 Z M 298.21045 358.10742 Q 297.1255 358.10742 296.3037 357.6079 Q 295.48193 357.1084 295.0254 356.1899 Q 294.56885 355.27148 294.56885 354.00388 Q 294.56885 352.72562 295.03345 351.80444 Q 295.49805 350.8833 296.30908 350.38647 Q 297.12012 349.88965 298.15674 349.88965 Q 299.02148 349.88965 299.7251 350.2253 Q 300.4287 350.56104 300.88794 351.15454 Q 301.34717 351.74805 301.48145 352.52686 L 300.2246 352.52686 Q 300.0044 351.82327 299.4807 351.41504 Q 298.95703 351.00684 298.16748 351.00684 Q 297.49072 351.00684 296.95093 351.3506 Q 296.41113 351.6943 296.09692 352.36304 Q 295.7827 353.03174 295.7827 353.99854 Q 295.7827 354.96533 296.09692 355.63135 Q 296.41113 356.29736 296.95898 356.6438 Q 297.50684 356.99023 298.21582 356.99023 Q 299.1665 356.99023 299.74927 356.42896 Q 300.33203 355.86768 300.35352 354.90625 L 298.39307 354.90625 L 298.39307 353.89648 L 301.55127 353.89648 L 301.55127 354.81494 Q 301.55127 355.8247 301.1189 356.56323 Q 300.68652 357.30173 299.93457 357.7046 Q 299.18262 358.10742 298.21045 358.10742 Z M 312.33936 358.10742 Q 311.30273 358.10742 310.48364 357.61328 Q 309.66455 357.11914 309.19458 356.20068 Q 308.7246 355.2822 308.7246 354.00388 Q 308.7246 352.7202 309.19458 351.7991 Q 309.66455 350.87793 310.48364 350.3838 Q 311.30273 349.88965 312.33936 349.88965 Q 313.37598 349.88965 314.1897 350.3838 Q 315.00342 350.87793 315.4734 351.7991 Q 315.94336 352.7202 315.94336 354.00388 Q 315.94336 355.2822 315.4734 356.20337 Q 315.00342 357.1245 314.1897 357.616 Q 313.37598 358.10742 312.33936 358.10742 Z M 312.33936 356.99023 Q 313.02148 356.99023 313.56128 356.6465 Q 314.10107 356.30273 314.41528 355.63672 Q 314.7295 354.9707 314.7295 354.00388 Q 314.7295 353.03174 314.41528 352.36304 Q 314.10107 351.6943 313.56128 351.3506 Q 313.02148 351.00684 312.33936 351.00684 Q 311.65186 351.00684 311.10938 351.3506 Q 310.5669 351.6943 310.2527 352.36304 Q 309.93848 353.03174 309.93848 354.00388 Q 309.93848 354.9707 310.2527 355.63672 Q 310.5669 356.30273 311.10938 356.6465 Q 311.65186 356.99023 312.33936 356.99023 Z M 318.93652 358 L 318.93652 349.99707 L 320.34912 349.99707 L 323.3623 354.76123 Q 323.5503 355.062 323.80273 355.51053 Q 324.05518 355.95898 324.32373 356.5337 Q 324.28076 355.9536 324.26196 355.42453 Q 324.24316 354.8955 324.24316 354.55176 L 324.24316 349.99707 L 325.4839 349.99707 L 325.4839 358 L 324.06592 358 L 321.36426 353.74072 Q 321.16553 353.4238 320.9856 353.1123 Q 320.80566 352.80078 320.59082 352.41135 Q 320.37598 352.02194 320.08594 351.48483 Q 320.12354 352.23682 320.1504 352.82764 Q 320.17725 353.41846 320.17725 353.72998 L 320.17725 358 Z M 328.35352 351.0498 L 328.35352 349.99707 L 334.56787 349.99707 L 334.56787 351.0498 L 332.08105 351.0498 L 332.08105 358 L 330.85107 358 L 330.85107 351.0498 Z M 340.43994 358.10742 Q 339.40332 358.10742 338.58423 357.61328 Q 337.76514 357.11914 337.29517 356.20068 Q 336.8252 355.2822 336.8252 354.00388 Q 336.8252 352.7202 337.29517 351.7991 Q 337.76514 350.87793 338.58423 350.3838 Q 339.40332 349.88965 340.43994 349.88965 Q 341.47656 349.88965 342.29028 350.3838 Q 343.104 350.87793 343.57397 351.7991 Q 344.0439 352.7202 344.0439 354.00388 Q 344.0439 355.2822 343.57397 356.20337 Q 343.104 357.1245 342.29028 357.616 Q 341.47656 358.10742 340.43994 358.10742 Z M 340.43994 356.99023 Q 341.12207 356.99023 341.66187 356.6465 Q 342.20166 356.30273 342.51587 35Line truncated
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.
Binary file not shown.
+158
View File
@@ -0,0 +1,158 @@
#!/usr/bin/env python3
"""Regenerate background.tiff and dmg-layout.DS_Store for the macOS DMG window (see README.md here).
Needs resvg on PATH and:
pip install 'pillow>=12' ds_store==1.3.3 mac_alias==2.2.3
Runs on any OS, no Mac needed. Both outputs come from this script alone, so they carry nothing
from the machine that made them: no local paths, user or disk names, dates or volume UUIDs.
python3 packaging/macos/dmg/generate.py
"""
import datetime
import os
import shutil
import subprocess
import sys
import tempfile
from ds_store import DSStore
from mac_alias import Alias, TargetInfo, VolumeInfo
from PIL import Image, ImageCms
HERE = os.path.dirname(os.path.abspath(__file__))
# Keep in sync with package.sh (volume name) and README.md (layout).
VOLUME = "CADCraft"
WIDTH, HEIGHT = 660, 400 # window content, pt; the background's 1x size
TITLE_BAR = 32
ICON_SIZE = 128
PALETTE = 16 # colours in background.tiff
ICONS = {"CADCraft.app": (326, 205), "Applications": (574, 205)}
# A fixed date for the alias and the colour profile. Finder never matches it against the image
# (each build is a new volume): it finds the background by volume name and path.
FIXED_DATE = datetime.datetime(2026, 10, 8, tzinfo=datetime.timezone.utc)
def render(svg, width, out):
# The SVG's text is already outlined and no fonts are loaded, so every machine draws the
# same pixels.
subprocess.run(
["resvg", "--skip-system-fonts", "-w", str(width), svg, out],
check=True,
)
with Image.open(out) as im:
return im.convert("RGB")
def quantize(im):
# No dithering: dither noise compresses badly and the artwork has flat fills.
return im.quantize(PALETTE, method=Image.Quantize.FASTOCTREE, dither=Image.Dither.NONE)
def srgb_profile():
# LittleCMS's built-in sRGB ("No copyright, use freely"), with its creation date fixed.
icc = bytearray(ImageCms.ImageCmsProfile(ImageCms.createProfile("sRGB")).tobytes())
d = FIXED_DATE
icc[24:36] = b"".join(v.to_bytes(2, "big") for v in (d.year, d.month, d.day, 0, 0, 0))
if icc[84:100] != bytes(16):
sys.exit("error: the sRGB profile has an ID; recompute it after fixing the date")
return bytes(icc)
def write_tiff(svg, out):
"""1x (72 dpi) and 2x (144 dpi) in one TIFF, as `tiffutil -cathidpicheck` writes them."""
with tempfile.TemporaryDirectory() as tmp:
one, two = (
quantize(render(svg, WIDTH * s, os.path.join(tmp, f"{s}x.png"))) for s in (1, 2)
)
two.encoderinfo = {"dpi": (144, 144)} # the rest as the first page
# Opaque 16-colour palette, Deflate: the artwork is three inks plus antialiasing, and a palette
# keeps the file under ~100 KB (repository assets must stay small). As PhotoCraft's.
one.save(
out,
"TIFF",
save_all=True,
append_images=[two],
compression="tiff_adobe_deflate", # brand-ok: Pillow's name for TIFF compression 8 (Deflate)
dpi=(72, 72),
icc_profile=srgb_profile(),
)
with Image.open(out) as im:
im.seek(1)
if im.info.get("dpi") != (144, 144):
sys.exit("error: the 2x page isn't 144 dpi (needs Pillow 12 or later)")
strips = []
for page in (0, 1):
im.seek(page)
strips += zip(im.tag_v2[273], im.tag_v2[279]) # StripOffsets, StripByteCounts
# TIFF keeps directories and tag data on even offsets. When a strip ends on an odd one, libtiff
# leaves the padding byte after it uninitialised, so the file changed from run to run with
# identical pixels. Zero it: nothing refers to that byte.
starts = {o for o, _ in strips}
with open(out, "r+b") as f:
for o, n in strips:
if (o + n) % 2 and o + n not in starts:
f.seek(o + n)
f.write(bytes(1))
def background_alias():
# What Finder stores for /.background/background.tiff on the volume, minus anything about the
# machine (dmgbuild writes the same kind of alias from a mounted image). Disk type 5 (ejectable),
# the volume flags and the folder/file ids (25, 26) are what Finder wrote on a test image; the ids
# are only hints, as the volume's creation date.
volume = VolumeInfo(VOLUME, FIXED_DATE, b"H+", 5, 0x0D02, b"\0\0")
volume.posix_path = f"/Volumes/{VOLUME}"
target = TargetInfo(0, "background.tiff", 25, 26, FIXED_DATE, b"\0\0\0\0", b"\0\0\0\0")
target.folder_name = ".background"
target.cnid_path = [25]
target.carbon_path = f"{VOLUME}:.background:\0background.tiff"
target.posix_path = "/.background/background.tiff"
return Alias(volume=volume, target=target).to_bytes()
def write_ds_store(out):
if os.path.exists(out):
os.remove(out)
with DSStore.open(out, "w+") as d:
d["."]["bwsp"] = {
"ShowStatusBar": False,
"ShowToolbar": False,
"ShowTabView": False,
"ContainerShowSidebar": False,
"ShowSidebar": False,
"WindowBounds": f"{{{{200, 528}}, {{{WIDTH}, {HEIGHT + TITLE_BAR}}}}}",
}
d["."]["icvp"] = {
"viewOptionsVersion": 1,
"backgroundType": 2,
"backgroundImageAlias": background_alias(),
"backgroundColorRed": 1.0,
"backgroundColorGreen": 1.0,
"backgroundColorBlue": 1.0,
"gridOffsetX": 0.0,
"gridOffsetY": 0.0,
"gridSpacing": 100.0,
"arrangeBy": "none",
"showIconPreview": True,
"showItemInfo": False,
"labelOnBottom": True,
"textSize": 16.0,
"iconSize": float(ICON_SIZE),
}
d["."]["vSrn"] = ("long", 1)
for name, pos in ICONS.items():
d[name]["Iloc"] = pos
def main():
if not shutil.which("resvg"):
sys.exit("error: resvg not found (brew install resvg / cargo install resvg --locked)")
write_tiff(os.path.join(HERE, "background.svg"), os.path.join(HERE, "background.tiff"))
write_ds_store(os.path.join(HERE, "dmg-layout.DS_Store"))
if __name__ == "__main__":
main()
+6 -1
View File
@@ -131,10 +131,15 @@ STAGE="$WORK/dmg"
mkdir -p "$STAGE"
ditto "$APP" "$STAGE/CADCraft.app"
ln -s /Applications "$STAGE/Applications"
# Finder window layout: background, icon size and positions (packaging/macos/dmg/README.md).
mkdir -p "$STAGE/.background"
cp "$HERE/dmg/background.tiff" "$STAGE/.background/background.tiff"
cp "$HERE/dmg/dmg-layout.DS_Store" "$STAGE/.DS_Store"
rm -f "$DMG" "$WORK/raw.dmg"
# makehybrid + convert builds the image without attaching a device, unlike `create -srcfolder`,
# which is flaky on CI runners ("Resource busy") and hangs in sandboxed sessions.
hdiutil makehybrid -hfs -hfs-volume-name "CADCraft $VERSION" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE"
# The volume name has no version: .DS_Store finds the background through an alias that includes it.
hdiutil makehybrid -hfs -hfs-volume-name "CADCraft" -hfs-openfolder "$STAGE" -o "$WORK/raw.dmg" "$STAGE"
hdiutil convert "$WORK/raw.dmg" -format UDZO -imagekey zlib-level=9 -o "$DMG"
rm -f "$WORK/raw.dmg"
sign "$DMG"