dynimg 0.1.14__tar.gz → 0.1.15__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. {dynimg-0.1.14 → dynimg-0.1.15}/.claude/settings.local.json +3 -1
  2. dynimg-0.1.15/CLAUDE.md +4 -0
  3. {dynimg-0.1.14 → dynimg-0.1.15}/Cargo.lock +200 -2
  4. {dynimg-0.1.14 → dynimg-0.1.15}/Cargo.toml +2 -2
  5. {dynimg-0.1.14 → dynimg-0.1.15}/Makefile +1 -1
  6. {dynimg-0.1.14 → dynimg-0.1.15}/PKG-INFO +128 -30
  7. {dynimg-0.1.14 → dynimg-0.1.15}/README.md +127 -29
  8. dynimg-0.1.15/examples/transparent.html +39 -0
  9. {dynimg-0.1.14 → dynimg-0.1.15}/pyproject.toml +1 -1
  10. {dynimg-0.1.14 → dynimg-0.1.15}/python/dynimg/__init__.pyi +14 -1
  11. {dynimg-0.1.14 → dynimg-0.1.15}/scripts/snapshot-tests.sh +6 -0
  12. {dynimg-0.1.14 → dynimg-0.1.15}/src/lib.rs +70 -13
  13. {dynimg-0.1.14 → dynimg-0.1.15}/src/main.rs +7 -0
  14. {dynimg-0.1.14 → dynimg-0.1.15}/src/python.rs +13 -6
  15. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/.gitignore +2 -0
  16. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/test_dynimg.py +33 -27
  17. dynimg-0.1.15/tests/snapshots/inline-only-jpg.jpg +0 -0
  18. dynimg-0.1.15/tests/snapshots/transparent-jpg.jpg +0 -0
  19. dynimg-0.1.15/tests/snapshots/transparent-webp.webp +0 -0
  20. dynimg-0.1.15/tests/snapshots/transparent.png +0 -0
  21. dynimg-0.1.14/CLAUDE.md +0 -2
  22. dynimg-0.1.14/tests/snapshots/inline-only-jpg.jpg +0 -0
  23. {dynimg-0.1.14 → dynimg-0.1.15}/.github/workflows/build-wheels.yml +0 -0
  24. {dynimg-0.1.14 → dynimg-0.1.15}/.github/workflows/ci.yml +0 -0
  25. {dynimg-0.1.14 → dynimg-0.1.15}/.github/workflows/release.yml +0 -0
  26. {dynimg-0.1.14 → dynimg-0.1.15}/.gitignore +0 -0
  27. {dynimg-0.1.14 → dynimg-0.1.15}/.pearls/CLAUDE.md +0 -0
  28. {dynimg-0.1.14 → dynimg-0.1.15}/.pearls/issues.jsonl +0 -0
  29. {dynimg-0.1.14 → dynimg-0.1.15}/LICENSE +0 -0
  30. {dynimg-0.1.14 → dynimg-0.1.15}/codebook.toml +0 -0
  31. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/PlaywriteINGuides-Regular.ttf +0 -0
  32. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/RobotoMono-Bold.ttf +0 -0
  33. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/RobotoMono-Bold.woff2 +0 -0
  34. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/logo.svg +0 -0
  35. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/servo.css +0 -0
  36. {dynimg-0.1.14 → dynimg-0.1.15}/examples/assets/style.css +0 -0
  37. {dynimg-0.1.14 → dynimg-0.1.15}/examples/google-fonts.html +0 -0
  38. {dynimg-0.1.14 → dynimg-0.1.15}/examples/inline-only.html +0 -0
  39. {dynimg-0.1.14 → dynimg-0.1.15}/examples/local-assets.html +0 -0
  40. {dynimg-0.1.14 → dynimg-0.1.15}/examples/local-font-woff2.html +0 -0
  41. {dynimg-0.1.14 → dynimg-0.1.15}/examples/local-font.html +0 -0
  42. {dynimg-0.1.14 → dynimg-0.1.15}/examples/mixed-assets.html +0 -0
  43. {dynimg-0.1.14 → dynimg-0.1.15}/examples/og-image.html +0 -0
  44. {dynimg-0.1.14 → dynimg-0.1.15}/examples/quote.html +0 -0
  45. {dynimg-0.1.14 → dynimg-0.1.15}/examples/remote-image.html +0 -0
  46. {dynimg-0.1.14 → dynimg-0.1.15}/examples/servo.html +0 -0
  47. {dynimg-0.1.14 → dynimg-0.1.15}/examples/social-card.html +0 -0
  48. {dynimg-0.1.14 → dynimg-0.1.15}/python/dynimg/__init__.py +0 -0
  49. {dynimg-0.1.14 → dynimg-0.1.15}/python/dynimg/py.typed +0 -0
  50. {dynimg-0.1.14 → dynimg-0.1.15}/scripts/release.sh +0 -0
  51. {dynimg-0.1.14 → dynimg-0.1.15}/scripts/render-examples.sh +0 -0
  52. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/README.md +0 -0
  53. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/debug_parser.py +0 -0
  54. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/debug_verbose.py +0 -0
  55. {dynimg-0.1.14 → dynimg-0.1.15}/test_wheels/test_from_ci.sh +0 -0
  56. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/inline-custom-size.png +0 -0
  57. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/inline-only-webp.webp +0 -0
  58. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/inline-only.png +0 -0
  59. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/og-image.png +0 -0
  60. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/quote.png +0 -0
  61. {dynimg-0.1.14 → dynimg-0.1.15}/tests/snapshots/social-card.png +0 -0
@@ -33,7 +33,9 @@
33
33
  "WebSearch",
34
34
  "Bash(.venv/bin/python:*)",
35
35
  "Bash(./scripts/snapshot-tests.sh:*)",
36
- "Bash(bash -x scripts/snapshot-tests.sh:*)"
36
+ "Bash(bash -x scripts/snapshot-tests.sh:*)",
37
+ "Bash(head:*)",
38
+ "Bash(identify:*)"
37
39
  ]
38
40
  }
39
41
  }
@@ -0,0 +1,4 @@
1
+
2
+ @.pearls/CLAUDE.md
3
+
4
+ Use the ./output folder here to make test images and as an output location for all test images.
@@ -26,6 +26,15 @@ dependencies = [
26
26
  "memchr",
27
27
  ]
28
28
 
29
+ [[package]]
30
+ name = "aligned-vec"
31
+ version = "0.6.4"
32
+ source = "registry+https://github.com/rust-lang/crates.io-index"
33
+ checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
34
+ dependencies = [
35
+ "equator",
36
+ ]
37
+
29
38
  [[package]]
30
39
  name = "alloc-no-stdlib"
31
40
  version = "2.0.4"
@@ -151,6 +160,27 @@ dependencies = [
151
160
  "serde",
152
161
  ]
153
162
 
163
+ [[package]]
164
+ name = "archmage"
165
+ version = "0.3.0"
166
+ source = "registry+https://github.com/rust-lang/crates.io-index"
167
+ checksum = "e5778a98b0714014089227472fd4342aacbd581d58ab673cbba6f6bf9fb5b5cd"
168
+ dependencies = [
169
+ "archmage-macros",
170
+ "bytemuck",
171
+ ]
172
+
173
+ [[package]]
174
+ name = "archmage-macros"
175
+ version = "0.3.0"
176
+ source = "registry+https://github.com/rust-lang/crates.io-index"
177
+ checksum = "80979662181a630a1e7a4f9f25174e990f44fc0c4e534e11c563fee10c9b8a1e"
178
+ dependencies = [
179
+ "proc-macro2",
180
+ "quote",
181
+ "syn",
182
+ ]
183
+
154
184
  [[package]]
155
185
  name = "arrayref"
156
186
  version = "0.3.9"
@@ -754,7 +784,7 @@ dependencies = [
754
784
 
755
785
  [[package]]
756
786
  name = "dynimg"
757
- version = "0.1.14"
787
+ version = "0.1.15"
758
788
  dependencies = [
759
789
  "anyhow",
760
790
  "anyrender",
@@ -766,7 +796,6 @@ dependencies = [
766
796
  "blitz-traits",
767
797
  "bytes",
768
798
  "clap",
769
- "image",
770
799
  "kurbo",
771
800
  "libc",
772
801
  "peniko",
@@ -776,6 +805,7 @@ dependencies = [
776
805
  "tokio",
777
806
  "tracing-subscriber",
778
807
  "webp",
808
+ "zenjpeg",
779
809
  ]
780
810
 
781
811
  [[package]]
@@ -793,6 +823,32 @@ dependencies = [
793
823
  "cfg-if",
794
824
  ]
795
825
 
826
+ [[package]]
827
+ name = "enough"
828
+ version = "0.3.1"
829
+ source = "registry+https://github.com/rust-lang/crates.io-index"
830
+ checksum = "65641ff0923e45cf34a4cb0115080cb7fc8bf4fd419c2207670d5722bab4c581"
831
+
832
+ [[package]]
833
+ name = "equator"
834
+ version = "0.4.2"
835
+ source = "registry+https://github.com/rust-lang/crates.io-index"
836
+ checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
837
+ dependencies = [
838
+ "equator-macro",
839
+ ]
840
+
841
+ [[package]]
842
+ name = "equator-macro"
843
+ version = "0.4.2"
844
+ source = "registry+https://github.com/rust-lang/crates.io-index"
845
+ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
846
+ dependencies = [
847
+ "proc-macro2",
848
+ "quote",
849
+ "syn",
850
+ ]
851
+
796
852
  [[package]]
797
853
  name = "equivalent"
798
854
  version = "1.0.2"
@@ -1522,6 +1578,12 @@ version = "0.14.0"
1522
1578
  source = "registry+https://github.com/rust-lang/crates.io-index"
1523
1579
  checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c"
1524
1580
 
1581
+ [[package]]
1582
+ name = "imgref"
1583
+ version = "1.12.0"
1584
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1585
+ checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
1586
+
1525
1587
  [[package]]
1526
1588
  name = "indexmap"
1527
1589
  version = "2.13.0"
@@ -1659,6 +1721,19 @@ dependencies = [
1659
1721
  "glob",
1660
1722
  ]
1661
1723
 
1724
+ [[package]]
1725
+ name = "linear-srgb"
1726
+ version = "0.3.2"
1727
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1728
+ checksum = "153732436b90b689934ad2d2997d5f0d2416163017ac76e6c0475dce9e7999c3"
1729
+ dependencies = [
1730
+ "bytemuck",
1731
+ "multiversed",
1732
+ "multiversion",
1733
+ "num-traits",
1734
+ "wide",
1735
+ ]
1736
+
1662
1737
  [[package]]
1663
1738
  name = "linebender_resource_handle"
1664
1739
  version = "0.1.1"
@@ -1698,6 +1773,16 @@ version = "0.1.2"
1698
1773
  source = "registry+https://github.com/rust-lang/crates.io-index"
1699
1774
  checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
1700
1775
 
1776
+ [[package]]
1777
+ name = "magetypes"
1778
+ version = "0.3.0"
1779
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1780
+ checksum = "79bcaaebc5420c1dfb6a2db979cf86c7f852fea4013de3920d0f0395ac9ea5a7"
1781
+ dependencies = [
1782
+ "archmage",
1783
+ "bytemuck",
1784
+ ]
1785
+
1701
1786
  [[package]]
1702
1787
  name = "malloc_size_of_derive"
1703
1788
  version = "0.1.3"
@@ -1803,6 +1888,39 @@ dependencies = [
1803
1888
  "windows-sys 0.61.2",
1804
1889
  ]
1805
1890
 
1891
+ [[package]]
1892
+ name = "multiversed"
1893
+ version = "0.1.0"
1894
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1895
+ checksum = "31eede710ee60ecb1708685a9d948d25a67ee3940ee8a1096431590cb21eef7d"
1896
+ dependencies = [
1897
+ "proc-macro2",
1898
+ "quote",
1899
+ "syn",
1900
+ ]
1901
+
1902
+ [[package]]
1903
+ name = "multiversion"
1904
+ version = "0.8.0"
1905
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1906
+ checksum = "7edb7f0ff51249dfda9ab96b5823695e15a052dc15074c9dbf3d118afaf2c201"
1907
+ dependencies = [
1908
+ "multiversion-macros",
1909
+ "target-features",
1910
+ ]
1911
+
1912
+ [[package]]
1913
+ name = "multiversion-macros"
1914
+ version = "0.8.0"
1915
+ source = "registry+https://github.com/rust-lang/crates.io-index"
1916
+ checksum = "b093064383341eb3271f42e381cb8f10a01459478446953953c75d24bd339fc0"
1917
+ dependencies = [
1918
+ "proc-macro2",
1919
+ "quote",
1920
+ "syn",
1921
+ "target-features",
1922
+ ]
1923
+
1806
1924
  [[package]]
1807
1925
  name = "new_debug_unreachable"
1808
1926
  version = "1.0.6"
@@ -1851,6 +1969,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
1851
1969
  checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
1852
1970
  dependencies = [
1853
1971
  "autocfg",
1972
+ "libm",
1854
1973
  ]
1855
1974
 
1856
1975
  [[package]]
@@ -2444,6 +2563,15 @@ dependencies = [
2444
2563
  "tower-service",
2445
2564
  ]
2446
2565
 
2566
+ [[package]]
2567
+ name = "rgb"
2568
+ version = "0.8.52"
2569
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2570
+ checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
2571
+ dependencies = [
2572
+ "bytemuck",
2573
+ ]
2574
+
2447
2575
  [[package]]
2448
2576
  name = "ring"
2449
2577
  version = "0.17.14"
@@ -2566,6 +2694,21 @@ version = "1.0.22"
2566
2694
  source = "registry+https://github.com/rust-lang/crates.io-index"
2567
2695
  checksum = "a50f4cf475b65d88e057964e0e9bb1f0aa9bbb2036dc65c64596b42932536984"
2568
2696
 
2697
+ [[package]]
2698
+ name = "safe_arch"
2699
+ version = "1.0.0"
2700
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2701
+ checksum = "1f7caad094bd561859bcd467734a720c3c1f5d1f338995351fefe2190c45efed"
2702
+ dependencies = [
2703
+ "bytemuck",
2704
+ ]
2705
+
2706
+ [[package]]
2707
+ name = "safe_unaligned_simd"
2708
+ version = "0.2.4"
2709
+ source = "registry+https://github.com/rust-lang/crates.io-index"
2710
+ checksum = "c1d612ce01a584cfab72164fb3f689112249fafeab7b4181c14601d19b34cb0a"
2711
+
2569
2712
  [[package]]
2570
2713
  name = "same-file"
2571
2714
  version = "1.0.6"
@@ -3098,6 +3241,12 @@ dependencies = [
3098
3241
  "slotmap",
3099
3242
  ]
3100
3243
 
3244
+ [[package]]
3245
+ name = "target-features"
3246
+ version = "0.1.6"
3247
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3248
+ checksum = "c1bbb9f3c5c463a01705937a24fdabc5047929ac764b2d5b9cf681c1f5041ed5"
3249
+
3101
3250
  [[package]]
3102
3251
  name = "target-lexicon"
3103
3252
  version = "0.13.4"
@@ -3784,6 +3933,22 @@ version = "0.1.12"
3784
3933
  source = "registry+https://github.com/rust-lang/crates.io-index"
3785
3934
  checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
3786
3935
 
3936
+ [[package]]
3937
+ name = "whereat"
3938
+ version = "0.1.3"
3939
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3940
+ checksum = "3ec598a1ade4224cfe3c5dace0d5d7deec9ed670503ff99d4946973c8c094534"
3941
+
3942
+ [[package]]
3943
+ name = "wide"
3944
+ version = "1.1.1"
3945
+ source = "registry+https://github.com/rust-lang/crates.io-index"
3946
+ checksum = "ac11b009ebeae802ed758530b6496784ebfee7a87b9abfbcaf3bbe25b814eb25"
3947
+ dependencies = [
3948
+ "bytemuck",
3949
+ "safe_arch",
3950
+ ]
3951
+
3787
3952
  [[package]]
3788
3953
  name = "winapi-util"
3789
3954
  version = "0.1.11"
@@ -4210,6 +4375,39 @@ dependencies = [
4210
4375
  "synstructure",
4211
4376
  ]
4212
4377
 
4378
+ [[package]]
4379
+ name = "yuv"
4380
+ version = "0.8.10"
4381
+ source = "registry+https://github.com/rust-lang/crates.io-index"
4382
+ checksum = "447d8234fba933a5198401c673607dd75758bab32136766da61711cc362294e9"
4383
+ dependencies = [
4384
+ "num-traits",
4385
+ ]
4386
+
4387
+ [[package]]
4388
+ name = "zenjpeg"
4389
+ version = "0.4.0"
4390
+ source = "registry+https://github.com/rust-lang/crates.io-index"
4391
+ checksum = "7bbccbe8dcf2cc21579981070905b6120ec82a45aa92b1715d28e05941390c50"
4392
+ dependencies = [
4393
+ "aligned-vec",
4394
+ "archmage",
4395
+ "bytemuck",
4396
+ "enough",
4397
+ "imgref",
4398
+ "linear-srgb",
4399
+ "magetypes",
4400
+ "multiversed",
4401
+ "multiversion",
4402
+ "rgb",
4403
+ "safe_unaligned_simd",
4404
+ "thiserror 2.0.18",
4405
+ "tinyvec",
4406
+ "whereat",
4407
+ "wide",
4408
+ "yuv",
4409
+ ]
4410
+
4213
4411
  [[package]]
4214
4412
  name = "zeno"
4215
4413
  version = "0.3.3"
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "dynimg"
3
- version = "0.1.14"
3
+ version = "0.1.15"
4
4
  edition = "2024"
5
5
  description = "A fast library and CLI for rendering HTML/CSS to images"
6
6
  license = "MIT"
@@ -48,7 +48,7 @@ clap = { version = "4", features = ["derive"] }
48
48
 
49
49
  # Image encoding
50
50
  png = "0.18"
51
- image = { version = "0.25", default-features = false, features = ["jpeg"] }
51
+ zenjpeg = "0.4"
52
52
  webp = "0.3"
53
53
 
54
54
  # Async runtime (needed for blitz-net)
@@ -11,7 +11,7 @@ dev:
11
11
  # Run tests
12
12
  test: install
13
13
  cargo test
14
- python test_wheels/test_dynimg.py
14
+ .venv/bin/python test_wheels/test_dynimg.py
15
15
  ./scripts/snapshot-tests.sh
16
16
 
17
17
  # Run lints
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dynimg
3
- Version: 0.1.14
3
+ Version: 0.1.15
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Intended Audience :: Developers
6
6
  Classifier: License :: OSI Approved :: MIT License
@@ -28,10 +28,20 @@ A fast library and CLI for rendering HTML/CSS to images. Use from Python, Rust,
28
28
 
29
29
  Perfect for generating dynamic images like Open Graph (OG) images, social media cards, email headers, and more.
30
30
 
31
+ ## Example Output
32
+
33
+ <table>
34
+ <tr>
35
+ <td><img src="tests/snapshots/og-image.png" width="400" alt="OG Image Example"></td>
36
+ <td><img src="tests/snapshots/social-card.png" width="400" alt="Social Card Example"></td>
37
+ </tr>
38
+ </table>
39
+
31
40
  ## Features
32
41
 
33
42
  - **Python + Rust + CLI**: Use from Python, as a Rust library, or command-line tool
34
43
  - **Multiple output formats**: PNG, WebP (lossless), and JPEG
44
+ - **Transparent backgrounds**: PNG and WebP support transparency (JPEG uses white)
35
45
  - **High-quality rendering**: Configurable scale factor for retina displays
36
46
  - **Fast**: Native Rust performance with no browser overhead
37
47
  - **Secure by default**: Network and filesystem access disabled unless explicitly enabled
@@ -103,7 +113,8 @@ let options = RenderOptions::default()
103
113
  .height(630)
104
114
  .scale(2.0)
105
115
  .allow_net()
106
- .assets_dir("./assets");
116
+ .assets_dir("./assets")
117
+ .background("#ffffff"); // CSS hex color (default: transparent)
107
118
 
108
119
  // Or struct initialization
109
120
  let options = RenderOptions {
@@ -113,6 +124,7 @@ let options = RenderOptions {
113
124
  allow_net: true,
114
125
  assets_dir: Some("./assets".into()),
115
126
  base_url: None,
127
+ background: Some("#ffffff".into()), // None = transparent
116
128
  };
117
129
  ```
118
130
 
@@ -138,16 +150,32 @@ dynimg input.html -o output.png
138
150
  ### Output Formats
139
151
 
140
152
  ```bash
141
- # PNG (lossless)
153
+ # PNG (lossless, supports transparency)
142
154
  dynimg input.html -o image.png
143
155
 
144
- # WebP (lossless)
156
+ # WebP (lossless, supports transparency)
145
157
  dynimg input.html -o image.webp
146
158
 
147
- # JPEG (lossy)
159
+ # JPEG (lossy, no transparency - uses white background)
148
160
  dynimg input.html -o image.jpg --quality 90
149
161
  ```
150
162
 
163
+ ### Transparent Backgrounds
164
+
165
+ PNG and WebP output supports transparent backgrounds. Simply don't set a background on your HTML body:
166
+
167
+ ```html
168
+ <body style="padding: 20px;">
169
+ <div style="background: white; border-radius: 8px; padding: 20px;">
170
+ Content with transparent surrounding area
171
+ </div>
172
+ </body>
173
+ ```
174
+
175
+ <img src="tests/snapshots/transparent.png" width="300" alt="Transparent background example">
176
+
177
+ JPEG doesn't support transparency, so it automatically uses a white background.
178
+
151
179
  ### Image Dimensions
152
180
 
153
181
  The `--width` and `--height` options set the **viewport size** (CSS layout dimensions). The actual output image is scaled by the `--scale` factor (default: 2x for high-DPI/retina displays).
@@ -279,6 +307,7 @@ options = dynimg.RenderOptions(
279
307
  allow_net=True, # Allow network requests (default: False)
280
308
  assets_dir="./assets", # Local assets directory (default: None)
281
309
  base_url="https://example.com", # Base URL for relative URLs (default: None)
310
+ background="#ffffff", # CSS hex color (default: transparent)
282
311
  )
283
312
 
284
313
  image = dynimg.render(html, options)
@@ -318,7 +347,14 @@ You can configure rendering options directly in your HTML using meta tags. CLI f
318
347
 
319
348
  This is useful for templates that should always render at specific dimensions. Remember: the output image size is viewport × scale.
320
349
 
321
- ## Example HTML Template
350
+ ## Example Templates
351
+
352
+ ### OG Image
353
+
354
+ <img src="tests/snapshots/og-image.png" width="500" alt="OG Image Example">
355
+
356
+ <details>
357
+ <summary>View HTML</summary>
322
358
 
323
359
  ```html
324
360
  <!DOCTYPE html>
@@ -327,40 +363,99 @@ This is useful for templates that should always render at specific dimensions. R
327
363
  <meta name="dynimg:width" content="1200">
328
364
  <meta name="dynimg:height" content="630">
329
365
  <style>
330
- * {
331
- margin: 0;
332
- padding: 0;
333
- box-sizing: border-box;
334
- }
366
+ * { margin: 0; padding: 0; box-sizing: border-box; }
367
+ body { font-family: system-ui, -apple-system, sans-serif; }
335
368
  .container {
336
369
  width: 1200px;
337
370
  height: 630px;
338
371
  display: flex;
339
372
  flex-direction: column;
340
373
  justify-content: center;
341
- align-items: center;
374
+ padding: 80px;
342
375
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
343
- font-family: system-ui, sans-serif;
344
376
  }
345
- h1 {
377
+ h1 { color: white; font-size: 72px; font-weight: 700; line-height: 1.1; margin-bottom: 24px; }
378
+ p { color: rgba(255, 255, 255, 0.85); font-size: 32px; }
379
+ .footer { position: absolute; bottom: 40px; left: 80px; color: rgba(255, 255, 255, 0.6); font-size: 24px; }
380
+ </style>
381
+ </head>
382
+ <body>
383
+ <div class="container">
384
+ <h1>Your Blog Post Title Here</h1>
385
+ <p>A compelling subtitle that captures attention</p>
386
+ <div class="footer">yoursite.com</div>
387
+ </div>
388
+ </body>
389
+ </html>
390
+ ```
391
+ </details>
392
+
393
+ ### Social Card
394
+
395
+ <img src="tests/snapshots/social-card.png" width="500" alt="Social Card Example">
396
+
397
+ <details>
398
+ <summary>View HTML</summary>
399
+
400
+ ```html
401
+ <!DOCTYPE html>
402
+ <html>
403
+ <head>
404
+ <meta name="dynimg:width" content="1200">
405
+ <meta name="dynimg:height" content="675">
406
+ <style>
407
+ * { margin: 0; padding: 0; box-sizing: border-box; }
408
+ body { font-family: system-ui, -apple-system, sans-serif; }
409
+ .card {
410
+ width: 1200px;
411
+ height: 675px;
412
+ background: #0f172a;
413
+ display: flex;
414
+ padding: 60px;
415
+ }
416
+ .content { flex: 1; display: flex; flex-direction: column; justify-content: center; }
417
+ .tag {
418
+ display: inline-block;
419
+ background: #3b82f6;
346
420
  color: white;
347
- font-size: 64px;
348
- margin: 0;
421
+ padding: 8px 16px;
422
+ border-radius: 20px;
423
+ font-size: 18px;
424
+ font-weight: 600;
425
+ margin-bottom: 24px;
426
+ width: fit-content;
349
427
  }
350
- p {
351
- color: rgba(255,255,255,0.8);
352
- font-size: 32px;
428
+ h1 { color: white; font-size: 56px; font-weight: 700; line-height: 1.2; margin-bottom: 20px; }
429
+ p { color: #94a3b8; font-size: 24px; line-height: 1.5; }
430
+ .avatar {
431
+ width: 200px;
432
+ height: 200px;
433
+ background: linear-gradient(135deg, #06b6d4, #3b82f6);
434
+ border-radius: 100px;
435
+ display: flex;
436
+ align-items: center;
437
+ justify-content: center;
438
+ align-self: center;
439
+ margin-left: 60px;
353
440
  }
441
+ .avatar-text { color: white; font-size: 72px; font-weight: 700; }
354
442
  </style>
355
443
  </head>
356
444
  <body>
357
- <div class="container">
358
- <h1>Hello World</h1>
359
- <p>Welcome to my site</p>
445
+ <div class="card">
446
+ <div class="content">
447
+ <span class="tag">Tutorial</span>
448
+ <h1>Getting Started with Rust</h1>
449
+ <p>Learn the fundamentals of Rust programming language with practical examples.</p>
450
+ </div>
451
+ <div class="avatar">
452
+ <span class="avatar-text">RS</span>
453
+ </div>
360
454
  </div>
361
455
  </body>
362
456
  </html>
363
457
  ```
458
+ </details>
364
459
 
365
460
  ## Supported CSS Features
366
461
 
@@ -410,20 +505,23 @@ cargo fmt -- --check
410
505
 
411
506
  ## Releasing
412
507
 
413
- Releases are automated via GitHub Actions. To create a new release:
414
-
415
- 1. Update the version in `Cargo.toml`
416
- 2. Create and push a git tag:
508
+ Releases are automated via a release script and GitHub Actions. To create a new release:
417
509
 
418
510
  ```bash
419
- git tag v0.1.0
420
- git push origin v0.1.0
511
+ ./scripts/release.sh
421
512
  ```
422
513
 
423
- This triggers the release workflow which:
514
+ The script will:
515
+ 1. Verify you're on the main branch with a clean working directory
516
+ 2. Check that you're up to date with the remote
517
+ 3. Bump the patch version in `Cargo.toml` and `pyproject.toml`
518
+ 4. Commit the version changes and create an annotated tag
519
+ 5. Push the commit and tag to origin
520
+
521
+ This triggers the GitHub Actions release workflow which:
424
522
  - Builds wheels for Linux (x86_64, aarch64) and macOS (x86_64, aarch64)
425
523
  - Creates a GitHub Release with all artifacts
426
- - (Optional) Publishes to PyPI (when enabled)
524
+ - Publishes to PyPI
427
525
 
428
526
  ## License
429
527