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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +48 -0
- data/LICENSE.txt +59 -14
- data/README.md +257 -73
- data/lib/mtg_card_maker/base_card.rb +24 -3
- data/lib/mtg_card_maker/cli.rb +69 -66
- data/lib/mtg_card_maker/color_scheme.rb +18 -23
- data/lib/mtg_card_maker/css_service.rb +72 -0
- data/lib/mtg_card_maker/fonts/GoudyBookletter1911.ttf +0 -0
- data/lib/mtg_card_maker/fonts/OFL.txt +92 -0
- data/lib/mtg_card_maker/fonts/goudy_bookletter_base64.txt +1 -0
- data/lib/mtg_card_maker/icon_filename_service.rb +118 -0
- data/lib/mtg_card_maker/icon_service.rb +87 -25
- data/lib/mtg_card_maker/icons/black.svg +6 -1
- data/lib/mtg_card_maker/icons/blue.svg +6 -1
- data/lib/mtg_card_maker/icons/colorless.svg +6 -1
- data/lib/mtg_card_maker/icons/double-digit.svg +6 -0
- data/lib/mtg_card_maker/icons/energy.svg +26 -0
- data/lib/mtg_card_maker/icons/green.svg +6 -1
- data/lib/mtg_card_maker/icons/hybrid/2-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/2-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/2-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/2-red.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/2-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/black-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/black-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/black-red.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/black-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/blue-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/blue-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/blue-red.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/blue-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/colorless-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/colorless-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/colorless-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/colorless-red.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/colorless-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/green-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/green-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/green-red.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/green-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/red-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/red-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/red-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/red-white.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/white-black.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/white-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/white-green.svg +19 -0
- data/lib/mtg_card_maker/icons/hybrid/white-red.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/black-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/black-green.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/black-red.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/black-white.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/black.svg +6 -0
- data/lib/mtg_card_maker/icons/phyrexian/blue-black.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/blue-green.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/blue-red.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/blue-white.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/blue.svg +6 -0
- data/lib/mtg_card_maker/icons/phyrexian/colorless.svg +6 -0
- data/lib/mtg_card_maker/icons/phyrexian/green-black.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/green-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/green-red.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/green-white.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/green.svg +6 -0
- data/lib/mtg_card_maker/icons/phyrexian/red-black.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/red-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/red-green.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/red-white.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/red.svg +6 -0
- data/lib/mtg_card_maker/icons/phyrexian/white-black.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/white-blue.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/white-green.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/white-red.svg +19 -0
- data/lib/mtg_card_maker/icons/phyrexian/white.svg +6 -0
- data/lib/mtg_card_maker/icons/red.svg +6 -1
- data/lib/mtg_card_maker/icons/single-digit.svg +6 -0
- data/lib/mtg_card_maker/icons/snow.svg +24 -0
- data/lib/mtg_card_maker/icons/tap.svg +1 -0
- data/lib/mtg_card_maker/icons/tap_legacy.svg +45 -0
- data/lib/mtg_card_maker/icons/untap.svg +1 -0
- data/lib/mtg_card_maker/icons/white.svg +6 -1
- data/lib/mtg_card_maker/icons/x.svg +6 -0
- data/lib/mtg_card_maker/layer_config.rb +21 -46
- data/lib/mtg_card_maker/layers/border_layer.rb +4 -5
- data/lib/mtg_card_maker/layers/frame_layer.rb +1 -1
- data/lib/mtg_card_maker/layers/name_layer.rb +16 -8
- data/lib/mtg_card_maker/layers/power_layer.rb +1 -3
- data/lib/mtg_card_maker/layers/text_box_layer.rb +205 -51
- data/lib/mtg_card_maker/layers/type_line_layer.rb +2 -4
- data/lib/mtg_card_maker/logger.rb +62 -0
- data/lib/mtg_card_maker/mana_cost.rb +59 -128
- data/lib/mtg_card_maker/sprite_sheet_assets.rb +10 -55
- data/lib/mtg_card_maker/sprite_sheet_builder.rb +20 -8
- data/lib/mtg_card_maker/sprite_sheet_service.rb +22 -9
- data/lib/mtg_card_maker/svg_gradient_service.rb +11 -11
- data/lib/mtg_card_maker/symbol_replacement_service.rb +102 -0
- data/lib/mtg_card_maker/text_rendering_service.rb +7 -3
- data/lib/mtg_card_maker/version.rb +2 -2
- data/lib/mtg_card_maker.rb +9 -78
- metadata +79 -12
- data/lib/mtg_card_maker/fonts/Goudy Mediaeval DemiBold.ttf +0 -0
- 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.
|
|
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
|
|
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
|
-
|
|
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
|
|
16
|
-
- **Mana Cost**:
|
|
17
|
-
- **Type Line**: Customizable
|
|
18
|
-
- **
|
|
19
|
-
- **Flavor Text**: Optional italicized flavor text
|
|
20
|
-
- **Power/Toughness**:
|
|
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
|
|
24
|
-
- **Border
|
|
25
|
-
- **Art Integration**:
|
|
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
|
-
**
|
|
33
|
+
**Performance Optimizations:**
|
|
30
34
|
- **Shared Assets**: Fonts, gradients, and masks defined once per sprite sheet
|
|
31
|
-
- **
|
|
32
|
-
- **Layout Optimization**: Ideal for web applications, printing
|
|
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
|
|
36
|
-
- **Spacing Control**:
|
|
37
|
-
- **
|
|
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**:
|
|
41
|
-
- **CLI Integration**:
|
|
42
|
-
- **Error Handling**: Robust
|
|
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
|
|
49
|
+
#### 🛠️ Technical Excellence
|
|
45
50
|
|
|
46
51
|
**SVG Generation:**
|
|
47
|
-
- **Vector Graphics**: High-quality SVG output suitable for any
|
|
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
|
|
52
|
-
- **Guard Integration**: Automated testing and
|
|
53
|
-
- **Status Summary**: Real-time development
|
|
54
|
-
- **Comprehensive Testing**:
|
|
55
|
-
- **Code Quality**: RuboCop
|
|
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**:
|
|
59
|
-
- **CLI Help**:
|
|
60
|
-
- **Examples**: Multiple
|
|
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.
|
|
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
|
-
|
|
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" #
|
|
81
|
-
type_line: "Creature - Dragon" #
|
|
82
|
-
rules_text: "Card rules text" #
|
|
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
|
|
89
|
-
art: path/to/art.jpg
|
|
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
|
-
|
|
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-
|
|
114
|
-
--
|
|
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-
|
|
124
|
-
--
|
|
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
|
-
###
|
|
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
|
|
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
|
|
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=
|
|
164
|
-
--type-
|
|
165
|
-
--
|
|
166
|
-
--flavor-text='"
|
|
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-
|
|
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-
|
|
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
|
-
##
|
|
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
|
-
|
|
476
|
+
Cards you generate, including their text and artwork, belong to you.
|
|
293
477
|
|
|
294
|
-
|
|
478
|
+
See [LICENSE.txt](LICENSE.txt) for details.
|
|
295
479
|
|
|
296
|
-
## Copyright
|
|
480
|
+
## ©️ Copyright
|
|
297
481
|
|
|
298
|
-
© 2025 Joe Sharp.
|
|
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
|
-
|
|
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
|
-
|
|
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) }
|