mtg_card_maker 0.1.0 → 0.2.1

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +48 -0
  3. data/LICENSE.txt +59 -14
  4. data/README.md +257 -73
  5. data/lib/mtg_card_maker/base_card.rb +24 -3
  6. data/lib/mtg_card_maker/cli.rb +69 -66
  7. data/lib/mtg_card_maker/color_scheme.rb +18 -23
  8. data/lib/mtg_card_maker/css_service.rb +72 -0
  9. data/lib/mtg_card_maker/fonts/GoudyBookletter1911.ttf +0 -0
  10. data/lib/mtg_card_maker/fonts/OFL.txt +92 -0
  11. data/lib/mtg_card_maker/fonts/goudy_bookletter_base64.txt +1 -0
  12. data/lib/mtg_card_maker/icon_filename_service.rb +118 -0
  13. data/lib/mtg_card_maker/icon_service.rb +87 -25
  14. data/lib/mtg_card_maker/icons/black.svg +6 -1
  15. data/lib/mtg_card_maker/icons/blue.svg +6 -1
  16. data/lib/mtg_card_maker/icons/colorless.svg +6 -1
  17. data/lib/mtg_card_maker/icons/double-digit.svg +6 -0
  18. data/lib/mtg_card_maker/icons/energy.svg +26 -0
  19. data/lib/mtg_card_maker/icons/green.svg +6 -1
  20. data/lib/mtg_card_maker/icons/hybrid/2-black.svg +19 -0
  21. data/lib/mtg_card_maker/icons/hybrid/2-blue.svg +19 -0
  22. data/lib/mtg_card_maker/icons/hybrid/2-green.svg +19 -0
  23. data/lib/mtg_card_maker/icons/hybrid/2-red.svg +19 -0
  24. data/lib/mtg_card_maker/icons/hybrid/2-white.svg +19 -0
  25. data/lib/mtg_card_maker/icons/hybrid/black-blue.svg +19 -0
  26. data/lib/mtg_card_maker/icons/hybrid/black-green.svg +19 -0
  27. data/lib/mtg_card_maker/icons/hybrid/black-red.svg +19 -0
  28. data/lib/mtg_card_maker/icons/hybrid/black-white.svg +19 -0
  29. data/lib/mtg_card_maker/icons/hybrid/blue-black.svg +19 -0
  30. data/lib/mtg_card_maker/icons/hybrid/blue-green.svg +19 -0
  31. data/lib/mtg_card_maker/icons/hybrid/blue-red.svg +19 -0
  32. data/lib/mtg_card_maker/icons/hybrid/blue-white.svg +19 -0
  33. data/lib/mtg_card_maker/icons/hybrid/colorless-black.svg +19 -0
  34. data/lib/mtg_card_maker/icons/hybrid/colorless-blue.svg +19 -0
  35. data/lib/mtg_card_maker/icons/hybrid/colorless-green.svg +19 -0
  36. data/lib/mtg_card_maker/icons/hybrid/colorless-red.svg +19 -0
  37. data/lib/mtg_card_maker/icons/hybrid/colorless-white.svg +19 -0
  38. data/lib/mtg_card_maker/icons/hybrid/green-black.svg +19 -0
  39. data/lib/mtg_card_maker/icons/hybrid/green-blue.svg +19 -0
  40. data/lib/mtg_card_maker/icons/hybrid/green-red.svg +19 -0
  41. data/lib/mtg_card_maker/icons/hybrid/green-white.svg +19 -0
  42. data/lib/mtg_card_maker/icons/hybrid/red-black.svg +19 -0
  43. data/lib/mtg_card_maker/icons/hybrid/red-blue.svg +19 -0
  44. data/lib/mtg_card_maker/icons/hybrid/red-green.svg +19 -0
  45. data/lib/mtg_card_maker/icons/hybrid/red-white.svg +19 -0
  46. data/lib/mtg_card_maker/icons/hybrid/white-black.svg +19 -0
  47. data/lib/mtg_card_maker/icons/hybrid/white-blue.svg +19 -0
  48. data/lib/mtg_card_maker/icons/hybrid/white-green.svg +19 -0
  49. data/lib/mtg_card_maker/icons/hybrid/white-red.svg +19 -0
  50. data/lib/mtg_card_maker/icons/phyrexian/black-blue.svg +19 -0
  51. data/lib/mtg_card_maker/icons/phyrexian/black-green.svg +19 -0
  52. data/lib/mtg_card_maker/icons/phyrexian/black-red.svg +19 -0
  53. data/lib/mtg_card_maker/icons/phyrexian/black-white.svg +19 -0
  54. data/lib/mtg_card_maker/icons/phyrexian/black.svg +6 -0
  55. data/lib/mtg_card_maker/icons/phyrexian/blue-black.svg +19 -0
  56. data/lib/mtg_card_maker/icons/phyrexian/blue-green.svg +19 -0
  57. data/lib/mtg_card_maker/icons/phyrexian/blue-red.svg +19 -0
  58. data/lib/mtg_card_maker/icons/phyrexian/blue-white.svg +19 -0
  59. data/lib/mtg_card_maker/icons/phyrexian/blue.svg +6 -0
  60. data/lib/mtg_card_maker/icons/phyrexian/colorless.svg +6 -0
  61. data/lib/mtg_card_maker/icons/phyrexian/green-black.svg +19 -0
  62. data/lib/mtg_card_maker/icons/phyrexian/green-blue.svg +19 -0
  63. data/lib/mtg_card_maker/icons/phyrexian/green-red.svg +19 -0
  64. data/lib/mtg_card_maker/icons/phyrexian/green-white.svg +19 -0
  65. data/lib/mtg_card_maker/icons/phyrexian/green.svg +6 -0
  66. data/lib/mtg_card_maker/icons/phyrexian/red-black.svg +19 -0
  67. data/lib/mtg_card_maker/icons/phyrexian/red-blue.svg +19 -0
  68. data/lib/mtg_card_maker/icons/phyrexian/red-green.svg +19 -0
  69. data/lib/mtg_card_maker/icons/phyrexian/red-white.svg +19 -0
  70. data/lib/mtg_card_maker/icons/phyrexian/red.svg +6 -0
  71. data/lib/mtg_card_maker/icons/phyrexian/white-black.svg +19 -0
  72. data/lib/mtg_card_maker/icons/phyrexian/white-blue.svg +19 -0
  73. data/lib/mtg_card_maker/icons/phyrexian/white-green.svg +19 -0
  74. data/lib/mtg_card_maker/icons/phyrexian/white-red.svg +19 -0
  75. data/lib/mtg_card_maker/icons/phyrexian/white.svg +6 -0
  76. data/lib/mtg_card_maker/icons/red.svg +6 -1
  77. data/lib/mtg_card_maker/icons/single-digit.svg +6 -0
  78. data/lib/mtg_card_maker/icons/snow.svg +24 -0
  79. data/lib/mtg_card_maker/icons/tap.svg +1 -0
  80. data/lib/mtg_card_maker/icons/tap_legacy.svg +45 -0
  81. data/lib/mtg_card_maker/icons/untap.svg +1 -0
  82. data/lib/mtg_card_maker/icons/white.svg +6 -1
  83. data/lib/mtg_card_maker/icons/x.svg +6 -0
  84. data/lib/mtg_card_maker/layer_config.rb +21 -46
  85. data/lib/mtg_card_maker/layers/border_layer.rb +4 -5
  86. data/lib/mtg_card_maker/layers/frame_layer.rb +1 -1
  87. data/lib/mtg_card_maker/layers/name_layer.rb +16 -8
  88. data/lib/mtg_card_maker/layers/power_layer.rb +1 -3
  89. data/lib/mtg_card_maker/layers/text_box_layer.rb +205 -51
  90. data/lib/mtg_card_maker/layers/type_line_layer.rb +2 -4
  91. data/lib/mtg_card_maker/logger.rb +62 -0
  92. data/lib/mtg_card_maker/mana_cost.rb +59 -128
  93. data/lib/mtg_card_maker/sprite_sheet_assets.rb +10 -55
  94. data/lib/mtg_card_maker/sprite_sheet_builder.rb +20 -8
  95. data/lib/mtg_card_maker/sprite_sheet_service.rb +22 -9
  96. data/lib/mtg_card_maker/svg_gradient_service.rb +11 -11
  97. data/lib/mtg_card_maker/symbol_replacement_service.rb +102 -0
  98. data/lib/mtg_card_maker/text_rendering_service.rb +7 -3
  99. data/lib/mtg_card_maker/version.rb +2 -2
  100. data/lib/mtg_card_maker.rb +9 -78
  101. metadata +79 -12
  102. data/lib/mtg_card_maker/fonts/Goudy Mediaeval DemiBold.ttf +0 -0
  103. data/lib/mtg_card_maker/fonts/goudy_base64.txt +0 -1
data/README.md CHANGED
@@ -1,69 +1,75 @@
1
1
  # MTG Card Maker
2
2
 
3
3
  <p align=center>
4
- <img src="images/mtgcm_card.png" width="300" />
4
+ <img src="images/mtgcm_card.webp"
5
+ alt="A card featuring artwork of a fiery dragon and anvil"
6
+ width="300" />
5
7
  </p>
6
8
 
7
- A Ruby gem for creating fan-made Magic: The Gathering cards. This tool allows you to generate custom MTG cards with proper formatting, mana costs, and card layouts. All you need to supply is the artwork image url/path, or simply overlay on top of an image of your choosing.
9
+ A Ruby gem for creating high quality fan-made Magic: The Gathering cards. This tool generates custom MTG cards with authentic formatting, comprehensive mana cost support, and precise card layouts. Perfect for game designers, content creators, and MTG enthusiasts.
8
10
 
9
- Please note this gem is in early beta, and should not be used in any production application. **Using it commercially is a violation of the license.** Please suggest feature ideas you have after reviewing the open issues here: https://github.com/joe-sharp/mtg_card_maker/issues
11
+
12
+ Please note this gem is for fan content creation only. **Wizards of the Coast's Fan Content Policy does not permit commercial use of cards made with Magic: The Gathering materials.** If you have ideas for improvements, please create a feature request after reviewing the open issues here: https://github.com/joe-sharp/mtg_card_maker/issues
10
13
 
11
14
  ## Features
12
15
  #### 🎨 Card Customization
13
16
 
14
17
  **Core Card Properties:**
15
- - **Card Name**: Customizable card titles with optional font support
16
- - **Mana Cost**: Support for 0-9, X, and all MTG mana symbols (W, U, B, R, G, C), alternate icon sets coming soon!
17
- - **Type Line**: Customizable Card types and subtypes (Creature, Instant, Sorcery, etc.)
18
- - **Description**: Rules text with automatic word wrapping and formatting
19
- - **Flavor Text**: Optional italicized flavor text at the bottom of cards
20
- - **Power/Toughness**: For creature cards, customizable power and toughness values, frame hides when omitted
18
+ - **Card Name**: Customizable card titles with embedded font support
19
+ - **Mana Cost**: Complete MTG symbol support including hybrid, Phyrexian, and numeric costs
20
+ - **Type Line**: Customizable card types and subtypes (Creature, Instant, Sorcery, etc.)
21
+ - **Rules Text**: Rich text with automatic word wrapping and symbol replacement
22
+ - **Flavor Text**: Optional italicized flavor text with proper formatting
23
+ - **Power/Toughness**: Dynamic power/toughness display
21
24
 
22
25
  **Visual Customization:**
23
- - **Color Schemes**: 8 built-in colors (white, blue, black, red, green, colorless, gold, artifact), split colors coming soon!
24
- - **Border Color**: Customizable border colors (white, black, gold, silver)
25
- - **Art Integration**: Support for custom artwork via image URLs, or simply layer the svg over your art of choice
26
+ - **Color Schemes**: 8 card colors (white, blue, black, red, green, colorless, gold, artifact)
27
+ - **Border Colors**: Customizable borders (white, black, gold, silver)
28
+ - **Art Integration**: Flexible artwork support via URLs or local paths
29
+ - **Font Embedding**: Base64 font embedding for portability
26
30
 
27
- #### 🖼️ Sprite Sheet Features
31
+ #### 🖼️ Advanced Sprite Sheet Features
28
32
 
29
- **Efficiency Optimizations:**
33
+ **Performance Optimizations:**
30
34
  - **Shared Assets**: Fonts, gradients, and masks defined once per sprite sheet
31
- - **Reduced File Size**: Significantly smaller file sizes compared to individual cards
32
- - **Layout Optimization**: Ideal for web applications, printing and digital displays
35
+ - **File Size Reduction**: Significantly smaller than individual card files
36
+ - **Layout Optimization**: Ideal for web applications, printing
37
+ and digital displays
33
38
 
34
39
  **Layout Control:**
35
- - **Cards Per Row**: Configurable number of cards per row (default: 4)
36
- - **Spacing Control**: Adjustable spacing between cards in pixels (default: 30)
37
- - **Automatic Sizing**: Dynamic sprite sheet dimensions based on card count
40
+ - **Cards Per Row**: Configurable grid layout (default: 4)
41
+ - **Spacing Control**: Pixel-perfect spacing between cards (default: 30px)
42
+ - **Dynamic Sizing**: Automatic sprite sheet dimensions based on card count
38
43
 
39
44
  **Batch Processing:**
40
- - **YAML Configuration**: Define multiple cards in structured YAML files
41
- - **CLI Integration**: Command-line tools for adding cards to YAML files
42
- - **Error Handling**: Robust error handling for invalid configurations
45
+ - **YAML Configuration**: Structured card definitions with validation
46
+ - **CLI Integration**: Streamlined command-line workflow
47
+ - **Error Handling**: Robust validation and helpful error messages
43
48
 
44
- #### 🛠️ Technical Features
49
+ #### 🛠️ Technical Excellence
45
50
 
46
51
  **SVG Generation:**
47
- - **Vector Graphics**: High-quality SVG output suitable for any size
52
+ - **Vector Graphics**: High-quality SVG output suitable for any scale
48
53
  - **Print Ready**: Optimized for both digital and print applications
49
54
  - **Cross-Platform**: Compatible with all modern browsers and design software
55
+ - NOTE: inline symbols in rule text may not render in some design software
50
56
 
51
- **Development Tools:**
52
- - **Guard Integration**: Automated testing and code quality monitoring
53
- - **Status Summary**: Real-time development status with integrity checks
54
- - **Comprehensive Testing**: 95%+ test coverage with RSpec
55
- - **Code Quality**: RuboCop integration for consistent code style
57
+ **Development Infrastructure:**
58
+ - **Guard Integration**: Automated testing and quality monitoring
59
+ - **Status Summary**: Real-time development feedback with integrity checks
60
+ - **Comprehensive Testing**: 480+ tests with fixtures and helpers
61
+ - **Code Quality**: Zero RuboCop violations, consistent style
56
62
 
57
63
  **Documentation:**
58
- - **YARD Documentation**: Comprehensive API documentation (95.96% documented)
59
- - **CLI Help**: Detailed command-line help and examples
60
- - **Examples**: Multiple example configurations and use cases
64
+ - **YARD Documentation**: 96.30% documented with local server
65
+ - **CLI Help**: Comprehensive command-line help and examples
66
+ - **Examples**: Multiple configuration examples and use cases
61
67
 
62
- ## Installation
68
+ ## 💽 Installation
63
69
 
64
70
  ### Prerequisites
65
71
 
66
- This gem requires Ruby 3.2 or higher. The version of Ruby included with macOS is typically outdated and won't work. You can install a newer version of Ruby using tools like [asdf](https://asdf-vm.com/) (with the [Ruby plugin](https://github.com/asdf-vm/asdf-ruby)), [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/). Note: This gem seems to work just fine on Ruby 3.5.0 but cannot be tested in CI currently.
72
+ This gem requires Ruby 3.3 or higher. The version of Ruby included with macOS is typically outdated and won't work. You can install a newer version of Ruby using tools like [asdf](https://asdf-vm.com/) (with the [Ruby plugin](https://github.com/asdf-vm/asdf-ruby)), [rbenv](https://github.com/rbenv/rbenv) or [RVM](https://rvm.io/). Note: This gem seems to work just fine on Ruby 3.5.0 but cannot be tested in CI currently.
67
73
 
68
74
  Install the gem:
69
75
  ```bash
@@ -75,18 +81,19 @@ gem install mtg_card_maker
75
81
 
76
82
  **Card Properties:**
77
83
 
78
- Optionals without a default hide when omitted.
84
+ Every property is optional and hides when omitted. A warning is shown if `name` or `type_line` is missing.
79
85
  ```yaml
80
- name: "Card Name" # Required
81
- type_line: "Creature - Dragon" # Required
82
- rules_text: "Card rules text" # Required
86
+ name: "Card Name" # Optional (warns when omitted)
87
+ type_line: "Creature - Dragon" # Optional (warns when omitted)
88
+ rules_text: "Card rules text" # Optional
83
89
  mana_cost: 2RR # Optional
84
90
  flavor_text: "Flavor text" # Optional
85
91
  power: 4 # Optional
86
92
  toughness: 4 # Optional
87
93
  color: red # Optional (default: colorless)
88
- border_color: gold # Optional (default: white)
89
- art: path/to/art.jpg # Optional
94
+ border_color: gold # Optional (default: white)
95
+ art: path/to/art.jpg # Optional
96
+ embed_font: false # Optional (default: true)
90
97
  ```
91
98
 
92
99
  **Color Schemes Available:**
@@ -101,7 +108,116 @@ art: path/to/art.jpg # Optional
101
108
  `white` => White cards
102
109
  ```
103
110
 
104
- ### 🚀 Usage Examples
111
+ **Symbols Available for Mana Cost:**
112
+
113
+ **Mana Symbols:**
114
+ - `W` - White mana <img src="images/icons/white.svg" width="16" valign="middle" />
115
+ - `B` - Black mana <img src="images/icons/black.svg" width="16" valign="middle" />
116
+ - `R` - Red mana <img src="images/icons/red.svg" width="16" valign="middle" />
117
+ - `G` - Green mana <img src="images/icons/green.svg" width="16" valign="middle" />
118
+ - `U` - Blue mana <img src="images/icons/blue.svg" width="16" valign="middle" />
119
+ - `C` - Colorless mana <img src="images/icons/colorless.svg" width="16" valign="middle" />
120
+ - `S` - Snow mana <img src="images/icons/snow.svg" width="16" valign="middle" />
121
+ - `{C/P}` - Colorless Phyrexian mana <img src="images/icons/phyrexian/colorless.svg" width="20" valign="middle" />
122
+ - `{R/P}` - Red Phyrexian mana <img src="images/icons/phyrexian/red.svg" width="20" valign="middle" />
123
+ - `{G/P}` - Green Phyrexian mana <img src="images/icons/phyrexian/green.svg" width="20" valign="middle" />
124
+ - `{U/P}` - Blue Phyrexian mana <img src="images/icons/phyrexian/blue.svg" width="20" valign="middle" />
125
+ - `{B/P}` - Black Phyrexian mana <img src="images/icons/phyrexian/black.svg" width="20" valign="middle" />
126
+ - `{W/P}` - White Phyrexian mana <img src="images/icons/phyrexian/white.svg" width="20" valign="middle" />
127
+
128
+ **Advanced Mana Support:**
129
+ Hybrid and Phyrexian mana are now fully supported! See all the
130
+ notations below in the rule text section. The notation uses curly braces for complex symbols:
131
+ - `{R/G}` Red / Green Mana <img src="images/icons/hybrid/red-green.svg" width="20" valign="middle" />
132
+ - `{G/W/P}` Green / White Phyrexian Mana <img src="images/icons/phyrexian/green-white.svg" width="20" valign="middle" />
133
+
134
+ **Numeric Symbols:**
135
+ - `0` through `99` - Generic mana costs (0-99) <img src="images/icons/single-digit.svg" width="16" valign="middle" />
136
+ - `X` - X symbol <img src="images/icons/x.svg" width="16" valign="middle" />
137
+
138
+ **Combination Examples:**
139
+ - `16US` - 16 generic + 1 blue + 1 snow <img src="images/icons/double-digit.svg" width="16" valign="middle" /> <img src="images/icons/blue.svg" width="16" valign="middle" /> <img src="images/icons/snow.svg" width="16" valign="middle" />
140
+ - `XG` - X generic + 1 green <img src="images/icons/x.svg" width="16" valign="middle" /> <img src="images/icons/green.svg" width="16" valign="middle" />
141
+ - `X3R` - X generic + 3 generic + 1 red <img src="images/icons/x.svg" width="16" valign="middle" /> <img src="images/icons/single-digit.svg" width="16" valign="middle" /> <img src="images/icons/red.svg" width="16" valign="middle" />
142
+ - `3WU` - 3 generic + 1 white + 1 blue <img src="images/icons/single-digit.svg" width="16" valign="middle" /> <img src="images/icons/white.svg" width="16" valign="middle" /> <img src="images/icons/blue.svg" width="16" valign="middle" />
143
+ - `WU{B/P}RG` White, Blue, Phyrexian Black, Red, Green <img src="images/icons/white.svg" width="16" valign="middle" /> <img src="images/icons/blue.svg" width="16" valign="middle" /> <img src="images/icons/phyrexian/black.svg" width="20" valign="middle" /> <img src="images/icons/red.svg" width="16" valign="middle" /> <img src="images/icons/green.svg" width="16" valign="middle" />
144
+ - `X` - X generic only <img src="images/icons/x.svg" width="16" valign="middle" />
145
+ - `3` - 3 generic only <img src="images/icons/single-digit.svg" width="16" valign="middle" />
146
+ - `B` - black only <img src="images/icons/black.svg" width="16" valign="middle" />
147
+
148
+ **Symbols Available in Rules Text:**
149
+
150
+ You can use MTG symbols in your rules text by wrapping them in curly braces "{C}". The following symbols are supported:
151
+
152
+ **Mana Symbols:**
153
+ - `{W}` - White mana <img src="images/icons/white.svg" width="16" valign="middle" />
154
+ - `{B}` - Black mana <img src="images/icons/black.svg" width="16" valign="middle" />
155
+ - `{R}` - Red mana <img src="images/icons/red.svg" width="16" valign="middle" />
156
+ - `{G}` - Green mana <img src="images/icons/green.svg" width="16" valign="middle" />
157
+ - `{U}` - Blue mana <img src="images/icons/blue.svg" width="16" valign="middle" />
158
+ - `{C}` - Colorless mana <img src="images/icons/colorless.svg" width="16" valign="middle" />
159
+ - `{S}` - Snow mana symbol <img src="images/icons/snow.svg" width="16" valign="middle" />
160
+ - `{C/P}` - Colorless Phyrexian mana <img src="images/icons/phyrexian/colorless.svg" width="20" valign="middle" />
161
+ - `{R/P}` - Red Phyrexian mana <img src="images/icons/phyrexian/red.svg" width="20" valign="middle" />
162
+ - `{G/P}` - Green Phyrexian mana <img src="images/icons/phyrexian/green.svg" width="20" valign="middle" />
163
+ - `{U/P}` - Blue Phyrexian mana <img src="images/icons/phyrexian/blue.svg" width="20" valign="middle" />
164
+ - `{B/P}` - Black Phyrexian mana <img src="images/icons/phyrexian/black.svg" width="20" valign="middle" />
165
+ - `{W/P}` - White Phyrexian mana <img src="images/icons/phyrexian/white.svg" width="20" valign="middle" />
166
+
167
+ **Hybrid Mana Symbols:**
168
+
169
+ **White hybrid:** `{W/B}` `{W/R}` `{W/G}` `{W/U}` - <img src="images/icons/hybrid/white-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/white-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/white-green.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/white-blue.svg" width="20" valign="middle" />
170
+
171
+ **Black hybrid:** `{B/W}` `{B/R}` `{B/G}` `{B/U}` - <img src="images/icons/hybrid/black-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/black-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/black-green.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/black-blue.svg" width="20" valign="middle" />
172
+
173
+ **Red hybrid:** `{R/W}` `{R/B}` `{R/G}` `{R/U}` - <img src="images/icons/hybrid/red-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/red-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/red-green.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/red-blue.svg" width="20" valign="middle" />
174
+
175
+ **Green hybrid:** `{G/W}` `{G/B}` `{G/R}` `{G/U}` - <img src="images/icons/hybrid/green-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/green-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/green-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/green-blue.svg" width="20" valign="middle" />
176
+
177
+ **Blue hybrid:** `{U/W}` `{U/B}` `{U/R}` `{U/G}` - <img src="images/icons/hybrid/blue-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/blue-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/blue-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/blue-green.svg" width="20" valign="middle" />
178
+
179
+ **"Two-brids":** `{2/W}` `{2/B}` `{2/R}` `{2/G}` `{2/U}` - <img src="images/icons/hybrid/2-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/2-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/2-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/2-green.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/2-blue.svg" width="20" valign="middle" />
180
+
181
+ **Colorless hybrid:** `{C/W}` `{C/B}` `{C/R}` `{C/G}` `{C/U}` - <img src="images/icons/hybrid/colorless-white.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/colorless-black.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/colorless-red.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/colorless-green.svg" width="20" valign="middle" /> <img src="images/icons/hybrid/colorless-blue.svg" width="20" valign="middle" />
182
+
183
+ **Phyrexian Hybrid Mana Symbols:**
184
+
185
+ **White Phyrexian hybrid:** `{W/B/P}` `{W/R/P}` `{W/G/P}` `{W/U/P}` - <img src="images/icons/phyrexian/white-black.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/white-red.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/white-green.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/white-blue.svg" width="20" valign="middle" />
186
+
187
+ **Black Phyrexian hybrid:** `{B/W/P}` `{B/R/P}` `{B/G/P}` `{B/U/P}` - <img src="images/icons/phyrexian/black-white.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/black-red.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/black-green.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/black-blue.svg" width="20" valign="middle" />
188
+
189
+ **Red Phyrexian hybrid:** `{R/W/P}` `{R/B/P}` `{R/G/P}` `{R/U/P}` - <img src="images/icons/phyrexian/red-white.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/red-black.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/red-green.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/red-blue.svg" width="20" valign="middle" />
190
+
191
+ **Green Phyrexian hybrid:** `{G/W/P}` `{G/B/P}` `{G/R/P}` `{G/U/P}` - <img src="images/icons/phyrexian/green-white.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/green-black.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/green-red.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/green-blue.svg" width="20" valign="middle" />
192
+
193
+ **Blue Phyrexian hybrid:** `{U/W/P}` `{U/B/P}` `{U/R/P}` `{U/G/P}` - <img src="images/icons/phyrexian/blue-white.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/blue-black.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/blue-red.svg" width="20" valign="middle" /> <img src="images/icons/phyrexian/blue-green.svg" width="20" valign="middle" />
194
+
195
+ **Numeric Symbols:**
196
+ - `{0}` through `{99}` - Generic mana costs (0-99) <img src="images/icons/single-digit.svg" width="16" valign="middle" />
197
+ - `{X}` - X symbol <img src="images/icons/x.svg" width="16" valign="middle" />
198
+
199
+ **Special Symbols:**
200
+ - `{T}` - Tap symbol <img src="images/icons/tap.svg" width="16" valign="middle" />
201
+ - `{Q}` - Untap symbol <img src="images/icons/untap.svg" width="16" valign="middle" />
202
+ - `{E}` - Energy symbol <img src="images/icons/energy.svg" width="16" valign="middle" />
203
+
204
+ **Example Usage:**
205
+ ```yaml
206
+ rules_text: "{T}: Add {W} or {U} to your mana pool."
207
+ ```
208
+ <p align=center>
209
+ <img src="images/icons/tap.svg"
210
+ alt="{T}" width="16" valign="middle" />
211
+ : Add
212
+ <img src="images/icons/white.svg"
213
+ alt="{W}" width="16" valign="middle" />
214
+ or
215
+ <img src="images/icons/blue.svg"
216
+ alt="{U}" width="16" valign="middle" />
217
+ to your mana pool
218
+ </p>
219
+
220
+ ## 🧪 Basic Examples
105
221
 
106
222
  ### Single Card
107
223
 
@@ -110,8 +226,8 @@ art: path/to/art.jpg # Optional
110
226
  mtg_card_maker generate_card \
111
227
  --name="Lightning Bolt" \
112
228
  --mana-cost=R \
113
- --type-text=Instant \
114
- --description="Deal 3 damage to any target." \
229
+ --type-line=Instant \
230
+ --rules-text="Deal 3 damage to any target." \
115
231
  --color=red
116
232
  ```
117
233
 
@@ -120,14 +236,16 @@ mtg_card_maker generate_card \
120
236
  mtg_card_maker add_card deck.yml \
121
237
  --name="Counterspell" \
122
238
  --mana-cost="UU" \
123
- --type-text="Instant" \
124
- --description="Counter target spell." \
239
+ --type-line="Instant" \
240
+ --rules-text="Counter target spell." \
125
241
  --color="blue"
126
242
  ```
127
243
  *Shortcuts:*
128
244
  - `g` or `gc` for `generate_card`
129
245
  - `a` or `ac` for `add_card`
130
246
 
247
+ *Misspelled options are rejected* (for example `--nmae` fails with `Unknown switches "--nmae"` and suggests `--name`) rather than silently producing a blank card.
248
+
131
249
  ### Generate a Sprite Sheet
132
250
 
133
251
  For printing or displaying multiple cards on a webpage, using a sprite sheet is recommended. This approach embeds fonts and gradients once per sheet instead of repeating them for each individual card, improving file size and loading efficiency.
@@ -149,32 +267,76 @@ mtg_card_maker generate_sprite deck.yml sprite_sheet.svg \
149
267
  *Shortcuts:*
150
268
  - `gs` or `gcs` for `generate_sprite`
151
269
 
152
- ### Examples
270
+ ### 🖼️ Experimental WebP Conversion
271
+
272
+ **⚠️ Experimental Feature - Mac Only**
273
+
274
+ The source repository includes an experimental script, `bin/svg_to_webp`, for converting SVG cards to WebP format using Chrome's headless mode. It is a development script and is **not** part of the published gem, so clone the repository to use it. This feature is currently **Mac-only** and requires specific dependencies.
275
+
276
+ **Why?:**
277
+ It seems a lot more user-friendly for use in things like this README.md file. The file size is bigger and it can't scale so I wouldn't use this option unless something in the SVG isn't working with whatever you are trying to do. You should also report that at https://github.com/joe-sharp/mtg_card_maker/issues . Thanks in advance!
278
+
279
+ **Prerequisites:**
280
+ - **macOS**: Chrome must be installed at `/Applications/Google Chrome.app/Contents/MacOS/Google Chrome`
281
+ - **cwebp**: Google's WebP encoder must be installed (install via Homebrew: `brew install webp`)
282
+
283
+ **Usage** (from a clone of the repository):
284
+ ```bash
285
+ # Convert a single SVG card to WebP
286
+ bin/svg_to_webp output_card.svg
287
+
288
+ # Convert any SVG file to WebP
289
+ bin/svg_to_webp path/to/your/card.svg
290
+ ```
291
+
292
+ **Features:**
293
+ - **Lossless Conversion**: High-quality WebP output with transparency support
294
+ - **Automatic Cropping**: Removes Chrome's window chrome for clean card output
295
+ - **Optimized Dimensions**: Outputs at 630x880 pixels (standard MTG card ratio)
296
+ - **Transparent Background**: Preserves transparency for web and design use
297
+
298
+ **Caveats:**
299
+ - **Platform Limitation**: Currently only works on macOS with Chrome installed
300
+ - **Dependency Required**: Requires `cwebp` command-line tool for final processing
301
+ - **Experimental Status**: May have issues with complex SVG content or Chrome updates
302
+ - **File Size**: WebP files may be larger than optimized SVGs for simple cards
303
+
304
+ **Future Plans:**
305
+ - Cross-platform support (Windows, Linux)
306
+ - Integration with main CLI commands
307
+
308
+ ## 🔮 Examples
153
309
 
154
310
  <p>
155
- <img align=right src="images/joe-sharp_card.png" alt="a sample card generated by the application" width="300" />
311
+ <img align=right
312
+ src="images/joe-sharp_card.webp"
313
+ alt="A card featuring artwork of a bearded man writing on a magical scroll with a feather pen"
314
+ width="300" />
156
315
  </p>
157
316
 
158
- **Generate a simple creature:**
317
+ **Generate a creature card:**
159
318
 
160
319
  ```bash
161
320
  mtg_card_maker generate_card \
162
321
  --name="Joe Sharp" \
163
- --mana-cost=2UR \
164
- --type-text="Engineer - Fullstack" \
165
- --description='When Joe Sharp enters the battlefield, create a 1/1 Code token with "This token can debug any program."\n\nWhenever you cast a Red spell, draw a card.' \
166
- --flavor-text='"I write code everyday." - Joe Sharp' \
322
+ --mana-cost=3UR \
323
+ --type-line="Engineer - Fullstack" \
324
+ --rules-text='When Joe Sharp enters the battlefield, all malfunctioning computers begin working.\nWhenever you cast a Red spell, draw a card.\n\n{E}: Debug program. Ignore this cost, if a Coffee token is in play.\n\n' \
325
+ --flavor-text='\"Nothing is true, everything is a string.\"\n\t\t\t\t- The ENV Creed' \
167
326
  --power=3 \
168
327
  --toughness=3 \
169
328
  --border-color=gold \
170
- --color=blue
329
+ --color=blue \
330
+ --art=images/joe.webp
171
331
  ```
172
332
 
333
+
334
+
173
335
  **Add multiple cards to a YAML file:**
174
336
  ```bash
175
- mtg_card_maker add_card deck.yml --name="Lightning Bolt" --mana-cost="R" --type-text="Instant" --description="Deal 3 damage to any target." --color="red"
337
+ mtg_card_maker add_card deck.yml --name="Lightning Bolt" --mana-cost="R" --type-line="Instant" --rules-text="Deal 3 damage to any target." --color="red"
176
338
 
177
- mtg_card_maker add_card deck.yml --name="Counterspell" --mana-cost="UU" --type-text="Instant" --description="Counter target spell." --color="blue"
339
+ mtg_card_maker add_card deck.yml --name="Counterspell" --mana-cost="UU" --type-line="Instant" --rules-text="Counter target spell." --color="blue"
178
340
  ```
179
341
 
180
342
  **Generate a sprite sheet from YAML:**
@@ -182,7 +344,7 @@ mtg_card_maker add_card deck.yml --name="Counterspell" --mana-cost="UU" --type-t
182
344
  mtg_card_maker generate_sprite deck.yml sprite_sheet.svg --cards-per-row=3 --spacing=20
183
345
  ```
184
346
 
185
- ## Development
347
+ ## 🚧 Development
186
348
 
187
349
  ### Setup
188
350
 
@@ -207,6 +369,12 @@ bundle exec guard
207
369
  - RSpec results are saved to `tmp/rspec_status.txt`
208
370
  - Status summary provides real-time feedback on code quality and card generation
209
371
 
372
+ **Development Benefits:**
373
+ - **Zero Configuration**: Just run `bundle exec guard` and start coding
374
+ - **Instant Feedback**: See test results and code quality status immediately
375
+ - **Regression Prevention**: Automatic integrity checks prevent breaking changes
376
+ - **Productivity Boost**: Focus on coding while Guard handles quality assurance
377
+
210
378
  ### Status Summary Script
211
379
 
212
380
  The `bin/status_summary` script provides a comprehensive development status overview, it is meant to only be run by guard.
@@ -252,7 +420,23 @@ bundle exec rubocop
252
420
  # Or run `bundle exec guard` and monitor output_card.svg and color_cards_sprite.svg for changes, it is far better, really.
253
421
  ```
254
422
 
255
- ## Documentation
423
+ ## 🏗️ Technical Architecture
424
+
425
+ **Design Patterns & Best Practices:**
426
+ - **Layered Architecture**: Clean separation between card components (art, text, frame, border)
427
+ - **Factory Pattern**: `LayerFactory` provides flexible layer creation
428
+ - **Service-Oriented Design**: Dedicated services for icons, sprites, gradients, and text rendering
429
+ - **Single Responsibility**: Each class has a focused, well-defined purpose
430
+ - **Dependency Injection**: Clean interfaces and testable components
431
+
432
+ **Advanced Features:**
433
+ - **Symbol Replacement Engine**: Sophisticated parsing of MTG notation with regex optimization
434
+ - **Dynamic Gradient Generation**: Real-time SVG gradient creation for card colors
435
+ - **Font Embedding System**: Base64 font embedding for portable SVG files
436
+ - **Sprite Sheet Optimization**: Shared asset management reducing file sizes considerably
437
+ - **Comprehensive Validation**: Robust error handling and input validation throughout
438
+
439
+ ## 📰 Documentation
256
440
 
257
441
  ### API Documentation
258
442
 
@@ -269,37 +453,37 @@ bundle exec yard server
269
453
  ```
270
454
  Then visit `http://localhost:8808` to browse the documentation.
271
455
 
272
- ### Key Classes
273
456
 
274
- - **MtgCardMaker**: Main module with card generation examples
275
- - **BaseCard**: High-level card creation interface
276
- - **Template**: SVG canvas and layer management
277
- - **BaseLayer**: Abstract base class for all card layers
278
- - **ColorScheme**: Unified color system for all MTG colors
279
- - **LayerFactory**: Factory pattern for creating card layers
280
- - **CLI**: Command-line interface with Thor integration
281
457
 
282
458
  ### Installation and Release
283
459
 
284
460
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
285
461
 
286
- ## Contributing
462
+ ## 🤝🏻 Contributing
287
463
 
288
464
  Bug reports and pull requests are welcome on GitHub at https://github.com/joe-sharp/mtg_card_maker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/joe-sharp/mtg_card_maker/blob/main/CODE_OF_CONDUCT.md).
289
465
 
290
- ## License
466
+ ## 🪪 License
467
+
468
+ The code in this gem is available as open source under the terms of the [MIT License](LICENSE.txt).
469
+
470
+ The MIT License does **not** cover:
471
+
472
+ - **Wizards of the Coast materials** (the Magic: The Gathering name, mana and set symbols, card frame and layout designs). These are property of Wizards of the Coast and are used under the Fan Content Policy, which does not permit commercial use.
473
+ - **The Goudy Bookletter 1911 font**, © 2010 Barry Schwartz, licensed under the [SIL Open Font License 1.1](lib/mtg_card_maker/fonts/OFL.txt).
474
+ - **The sample artwork and sample cards in `images/`**, which are AI-generated, are for illustration only and are not licensed for reuse. They are not part of the gem.
291
475
 
292
- <a href="https://projects.joe-sharp.com/mtg_card_maker">MTG Card Maker</a> © 2025 by <a href="https://github.com/joe-sharp/mtg_card_maker">Joe Sharp</a> is licensed under <a href="https://creativecommons.org/licenses/by-nc-nd/4.0/">CC BY-NC-ND 4.0</a> <img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg" width="20" valign=bottom><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg" width="20" valign=bottom><img src="https://mirrors.creativecommons.org/presskit/icons/nc.svg" width="20" valign=bottom><img src="https://mirrors.creativecommons.org/presskit/icons/nd.svg" width="20" valign=bottom>
476
+ Cards you generate, including their text and artwork, belong to you.
293
477
 
294
- The gem is available as open source under the terms of this license.
478
+ See [LICENSE.txt](LICENSE.txt) for details.
295
479
 
296
- ## Copyright
480
+ ## ©️ Copyright
297
481
 
298
- © 2025 Joe Sharp. Some rights reserved.
482
+ © 2025 Joe Sharp. Released under the MIT License.
299
483
  MTG Card Maker is unofficial Fan Content permitted under the [Fan Content Policy.](https://company.wizards.com/en/legal/fancontentpolicy) Not approved/endorsed by Wizards. Portions of the materials used are property of Wizards of the Coast. © Wizards of the Coast LLC.
300
484
 
301
- The removal or alteration of copyright notices, attributions, and/or QR codes from generated images constitutes a material breach of the license agreement. All copyright notices and identifying marks must remain intact and unmodified in accordance with the terms of use.
485
+ Please keep the copyright notices, attributions, and QR code on generated cards intact.
302
486
 
303
- ## Code of Conduct
487
+ ## 🧙🏻 Code of Conduct
304
488
 
305
489
  Everyone interacting in the MTG Card Maker project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/joe-sharp/mtg_card_maker/blob/main/CODE_OF_CONDUCT.md).
@@ -111,19 +111,21 @@ module MtgCardMaker
111
111
  # Initialize a new card with the given configuration
112
112
  #
113
113
  # @param config [Hash] the card configuration
114
- # @option config [String] :name the card name
114
+ # @option config [String] :name the card name (optional, warns when omitted)
115
115
  # @option config [String] :mana_cost the mana cost in MTG notation
116
116
  # @option config [String] :type_line the card type
117
- # @option config [String] :rules_text the card rules text
117
+ # @option config [String] :rules_text the card rules text (optional)
118
118
  # @option config [String] :flavor_text the flavor text
119
119
  # @option config [String] :power the power value for creatures
120
120
  # @option config [String] :toughness the toughness value for creatures
121
121
  # @option config [String] :border_color the border color
122
122
  # @option config [Symbol, String] :color the color scheme
123
123
  # @option config [String] :art the URL or path for card artwork
124
+ # @option config [Boolean] :embed_font whether to embed the font as base64 (default: true)
124
125
  def initialize(config)
125
126
  assign_attributes(config)
126
- @template = Template.new
127
+ warn_about_missing_fields
128
+ @template = Template.new(embed_font: config.fetch(:embed_font, true))
127
129
  add_layers
128
130
  end
129
131
 
@@ -185,6 +187,13 @@ module MtgCardMaker
185
187
  @art = config[:art]
186
188
  end
187
189
 
190
+ def warn_about_missing_fields
191
+ missing = { name: 'name', type_line: 'type line' }.select { |field, _label| public_send(field).to_s.strip.empty? }
192
+ return if missing.empty?
193
+
194
+ Logger.new.warn("Card has no #{missing.values.join(' or ')}")
195
+ end
196
+
188
197
  def define_art_window_mask
189
198
  svg = @template.instance_variable_get(:@svg)
190
199
  svg.defs do
@@ -200,8 +209,20 @@ module MtgCardMaker
200
209
  end
201
210
  end
202
211
 
212
+ def define_gradients_for_card
213
+ svg = @template.instance_variable_get(:@svg)
214
+ SvgGradientService.define_all_gradients(svg, @color_scheme)
215
+
216
+ # Also define gradients for border layer
217
+ return unless @border_color
218
+
219
+ border_color_scheme = ColorScheme.new(@border_color.to_sym)
220
+ SvgGradientService.define_all_gradients(svg, border_color_scheme)
221
+ end
222
+
203
223
  def add_layers
204
224
  define_art_window_mask
225
+ define_gradients_for_card
205
226
  # Use LayerFactory to create layers in order
206
227
  layers = LayerFactory.create_layers_for_card(self, DEFAULTS[:mask_id], self)
207
228
  layers.each { |layer| @template.add_layer(layer) }