squib 0.14.1 → 0.14.2
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 +5 -5
- data/.gitignore +41 -41
- data/.travis.yml +19 -19
- data/CHANGELOG.md +357 -340
- data/CONTRIBUTING.md +40 -40
- data/Gemfile +2 -2
- data/LICENSE.txt +22 -22
- data/README.md +121 -121
- data/RELEASE TODO.md +21 -21
- data/Rakefile +48 -48
- data/appveyor.yml +24 -24
- data/bin/squib +4 -4
- data/lib/squib.rb +32 -32
- data/lib/squib/api/background.rb +15 -15
- data/lib/squib/api/data.rb +137 -137
- data/lib/squib/api/groups.rb +54 -54
- data/lib/squib/api/image.rb +49 -49
- data/lib/squib/api/save.rb +83 -83
- data/lib/squib/api/settings.rb +21 -21
- data/lib/squib/api/shapes.rb +124 -124
- data/lib/squib/api/text.rb +25 -25
- data/lib/squib/api/text_embed.rb +71 -71
- data/lib/squib/api/units.rb +27 -27
- data/lib/squib/args/arg_loader.rb +126 -126
- data/lib/squib/args/box.rb +55 -55
- data/lib/squib/args/card_range.rb +32 -32
- data/lib/squib/args/color_validator.rb +12 -12
- data/lib/squib/args/coords.rb +35 -35
- data/lib/squib/args/csv_opts.rb +25 -25
- data/lib/squib/args/dir_validator.rb +16 -16
- data/lib/squib/args/draw.rb +92 -92
- data/lib/squib/args/embed_adjust.rb +25 -25
- data/lib/squib/args/embed_key.rb +17 -17
- data/lib/squib/args/hand_special.rb +37 -37
- data/lib/squib/args/import.rb +40 -40
- data/lib/squib/args/input_file.rb +37 -37
- data/lib/squib/args/paint.rb +44 -44
- data/lib/squib/args/paragraph.rb +116 -116
- data/lib/squib/args/save_batch.rb +63 -63
- data/lib/squib/args/scale_box.rb +53 -53
- data/lib/squib/args/sheet.rb +172 -172
- data/lib/squib/args/showcase_special.rb +38 -38
- data/lib/squib/args/sprue_file.rb +44 -44
- data/lib/squib/args/svg_special.rb +37 -37
- data/lib/squib/args/transform.rb +61 -61
- data/lib/squib/args/typographer.rb +119 -119
- data/lib/squib/args/unit_conversion.rb +29 -29
- data/lib/squib/builtin/layouts/economy.yml +85 -85
- data/lib/squib/builtin/layouts/fantasy.yml +101 -101
- data/lib/squib/builtin/layouts/hand.yml +62 -62
- data/lib/squib/builtin/layouts/party.yml +94 -94
- data/lib/squib/builtin/layouts/playing-card.yml +35 -35
- data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
- data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
- data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
- data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
- data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
- data/lib/squib/builtin/projects/advanced/config.yml +49 -49
- data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
- data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
- data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
- data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
- data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
- data/lib/squib/builtin/projects/basic/.gitignore +4 -4
- data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
- data/lib/squib/builtin/projects/basic/Gemfile +3 -3
- data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
- data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
- data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
- data/lib/squib/builtin/projects/basic/RULES.md +21 -21
- data/lib/squib/builtin/projects/basic/Rakefile +7 -7
- data/lib/squib/builtin/projects/basic/config.yml +49 -49
- data/lib/squib/builtin/projects/basic/deck.rb +6 -6
- data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
- data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
- data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
- data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
- data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -0
- data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
- data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
- data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -0
- data/lib/squib/card.rb +75 -74
- data/lib/squib/commands/cli.rb +39 -39
- data/lib/squib/commands/data/template_option.rb +109 -109
- data/lib/squib/commands/make_sprue.rb +275 -275
- data/lib/squib/commands/new.rb +77 -77
- data/lib/squib/conf.rb +139 -139
- data/lib/squib/constants.rb +17 -17
- data/lib/squib/deck.rb +116 -116
- data/lib/squib/graphics/background.rb +14 -14
- data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
- data/lib/squib/graphics/embedding_utils.rb +28 -28
- data/lib/squib/graphics/gradient_regex.rb +47 -47
- data/lib/squib/graphics/hand.rb +42 -42
- data/lib/squib/graphics/image.rb +108 -108
- data/lib/squib/graphics/save_doc.rb +61 -61
- data/lib/squib/graphics/save_images.rb +52 -52
- data/lib/squib/graphics/save_pdf.rb +90 -90
- data/lib/squib/graphics/save_sprue.rb +204 -205
- data/lib/squib/graphics/shapes.rb +143 -143
- data/lib/squib/graphics/showcase.rb +85 -85
- data/lib/squib/graphics/text.rb +174 -174
- data/lib/squib/import/data_frame.rb +108 -108
- data/lib/squib/layout_parser.rb +138 -126
- data/lib/squib/progress.rb +38 -38
- data/lib/squib/sample_helpers.rb +34 -34
- data/lib/squib/sprues/crop_line.rb +28 -28
- data/lib/squib/sprues/crop_line_dash.rb +35 -35
- data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
- data/lib/squib/sprues/sprue.rb +203 -203
- data/lib/squib/sprues/sprue_schema.rb +48 -48
- data/lib/squib/version.rb +10 -10
- data/samples/autoscale_font/_autoscale_font.rb +29 -29
- data/samples/backend/_backend.rb +26 -26
- data/samples/basic.rb +19 -19
- data/samples/build_groups/build_groups.rb +36 -36
- data/samples/colors/_colors.rb +38 -38
- data/samples/colors/_gradients.rb +34 -34
- data/samples/config/config_text_markup.rb +20 -20
- data/samples/config/custom_config.rb +18 -18
- data/samples/data/_csv.rb +33 -33
- data/samples/data/_excel.rb +55 -55
- data/samples/data/_yaml.rb +12 -12
- data/samples/hello_world.rb +6 -6
- data/samples/images/_cairo_access.rb +39 -39
- data/samples/images/_images.rb +104 -104
- data/samples/images/_more_load_images.rb +102 -102
- data/samples/intro/01_hello.rb +8 -8
- data/samples/intro/02_options.rb +14 -14
- data/samples/intro/03_layout.rb +11 -11
- data/samples/intro/04_arrays.rb +15 -15
- data/samples/intro/05_excel.rb +14 -14
- data/samples/layouts/builtin_layouts.rb +97 -97
- data/samples/layouts/layouts.rb +71 -71
- data/samples/project/src/characters.rb +8 -8
- data/samples/project/src/skills.rb +7 -7
- data/samples/proofs/_tgc_proofs.rb +16 -16
- data/samples/ranges/_ranges.rb +64 -64
- data/samples/saves/_hand.rb +23 -23
- data/samples/saves/_portrait_landscape.rb +23 -23
- data/samples/saves/_save_pdf.rb +29 -29
- data/samples/saves/_saves.rb +51 -51
- data/samples/saves/_showcase.rb +25 -25
- data/samples/shapes/_draw_shapes.rb +60 -58
- data/samples/shapes/_proofs.rb +22 -22
- data/samples/sprues/_advanced_sprues.rb +24 -24
- data/samples/sprues/_builtin_sprues.rb +21 -19
- data/samples/sprues/_fold_sheet.rb +27 -27
- data/samples/sprues/_hex_tiles.rb +15 -15
- data/samples/sprues/_mints.rb +11 -11
- data/samples/sprues/_sprue_example.rb +11 -11
- data/samples/text/_embed_text.rb +128 -128
- data/samples/text/_text.rb +47 -47
- data/samples/text/_text_options.rb +102 -102
- data/samples/text/bug134.rb +14 -14
- data/samples/units/_units.rb +32 -32
- data/squib.gemspec +52 -51
- metadata +66 -50
data/lib/squib/card.rb
CHANGED
|
@@ -1,74 +1,75 @@
|
|
|
1
|
-
require 'cairo'
|
|
2
|
-
require_relative 'graphics/cairo_context_wrapper'
|
|
3
|
-
|
|
4
|
-
module Squib
|
|
5
|
-
# Back end graphics. Private.
|
|
6
|
-
class Card
|
|
7
|
-
|
|
8
|
-
# :nodoc:
|
|
9
|
-
# @api private
|
|
10
|
-
attr_reader :width, :height, :backend, :svgfile, :index
|
|
11
|
-
|
|
12
|
-
# :nodoc:
|
|
13
|
-
# @api private
|
|
14
|
-
attr_accessor :cairo_surface, :cairo_context
|
|
15
|
-
|
|
16
|
-
# :nodoc:
|
|
17
|
-
# @api private
|
|
18
|
-
def initialize(deck, width, height, index=-1)
|
|
19
|
-
@deck = deck
|
|
20
|
-
@width = width
|
|
21
|
-
@height = height
|
|
22
|
-
@backend = deck.backend
|
|
23
|
-
@index = index
|
|
24
|
-
@svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg"
|
|
25
|
-
@cairo_surface = make_surface(@svgfile, @backend)
|
|
26
|
-
@cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface))
|
|
27
|
-
@cairo_context.antialias = deck.antialias
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# :nodoc:
|
|
31
|
-
# @api private
|
|
32
|
-
def make_surface(svgfile, backend)
|
|
33
|
-
case backend.downcase.to_sym
|
|
34
|
-
when :memory
|
|
35
|
-
Cairo::ImageSurface.new(@width, @height)
|
|
36
|
-
when :svg
|
|
37
|
-
FileUtils.mkdir_p @deck.dir unless Dir.exists?(@deck.dir)
|
|
38
|
-
Cairo::SVGSurface.new(svgfile, @width, @height)
|
|
39
|
-
else
|
|
40
|
-
Squib.logger.fatal "Back end not recognized: '#{backend}'"
|
|
41
|
-
abort
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# A save/restore wrapper for using Cairo
|
|
46
|
-
# :nodoc:
|
|
47
|
-
# @api private
|
|
48
|
-
def use_cairo(&block)
|
|
49
|
-
@cairo_context.save
|
|
50
|
-
|
|
51
|
-
@cairo_context
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
require_relative 'graphics/
|
|
67
|
-
require_relative 'graphics/
|
|
68
|
-
require_relative 'graphics/
|
|
69
|
-
require_relative 'graphics/
|
|
70
|
-
require_relative 'graphics/
|
|
71
|
-
require_relative 'graphics/
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
end
|
|
1
|
+
require 'cairo'
|
|
2
|
+
require_relative 'graphics/cairo_context_wrapper'
|
|
3
|
+
|
|
4
|
+
module Squib
|
|
5
|
+
# Back end graphics. Private.
|
|
6
|
+
class Card
|
|
7
|
+
|
|
8
|
+
# :nodoc:
|
|
9
|
+
# @api private
|
|
10
|
+
attr_reader :width, :height, :backend, :svgfile, :index
|
|
11
|
+
|
|
12
|
+
# :nodoc:
|
|
13
|
+
# @api private
|
|
14
|
+
attr_accessor :cairo_surface, :cairo_context
|
|
15
|
+
|
|
16
|
+
# :nodoc:
|
|
17
|
+
# @api private
|
|
18
|
+
def initialize(deck, width, height, index=-1)
|
|
19
|
+
@deck = deck
|
|
20
|
+
@width = width
|
|
21
|
+
@height = height
|
|
22
|
+
@backend = deck.backend
|
|
23
|
+
@index = index
|
|
24
|
+
@svgfile = "#{deck.dir}/#{deck.prefix}#{deck.count_format % index}.svg"
|
|
25
|
+
@cairo_surface = make_surface(@svgfile, @backend)
|
|
26
|
+
@cairo_context = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(@cairo_surface))
|
|
27
|
+
@cairo_context.antialias = deck.antialias
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# :nodoc:
|
|
31
|
+
# @api private
|
|
32
|
+
def make_surface(svgfile, backend)
|
|
33
|
+
case backend.downcase.to_sym
|
|
34
|
+
when :memory
|
|
35
|
+
Cairo::ImageSurface.new(@width, @height)
|
|
36
|
+
when :svg
|
|
37
|
+
FileUtils.mkdir_p @deck.dir unless Dir.exists?(@deck.dir)
|
|
38
|
+
Cairo::SVGSurface.new(svgfile, @width, @height)
|
|
39
|
+
else
|
|
40
|
+
Squib.logger.fatal "Back end not recognized: '#{backend}'"
|
|
41
|
+
abort
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# A save/restore wrapper for using Cairo
|
|
46
|
+
# :nodoc:
|
|
47
|
+
# @api private
|
|
48
|
+
def use_cairo(&block)
|
|
49
|
+
@cairo_context.save
|
|
50
|
+
@cairo_context.new_path # see bug 248
|
|
51
|
+
block.yield(@cairo_context)
|
|
52
|
+
@cairo_context.restore
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def finish!
|
|
56
|
+
begin
|
|
57
|
+
@cairo_surface.finish
|
|
58
|
+
rescue Cairo::SurfaceFinishedError
|
|
59
|
+
# do nothin - if it's already finished that's fine
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
########################
|
|
64
|
+
### BACKEND GRAPHICS ###
|
|
65
|
+
########################
|
|
66
|
+
require_relative 'graphics/background'
|
|
67
|
+
require_relative 'graphics/image'
|
|
68
|
+
require_relative 'graphics/save_doc'
|
|
69
|
+
require_relative 'graphics/save_images'
|
|
70
|
+
require_relative 'graphics/shapes'
|
|
71
|
+
require_relative 'graphics/showcase'
|
|
72
|
+
require_relative 'graphics/text'
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
end
|
data/lib/squib/commands/cli.rb
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
require 'mercenary'
|
|
2
|
-
require_relative 'make_sprue'
|
|
3
|
-
require_relative 'new'
|
|
4
|
-
|
|
5
|
-
module Squib
|
|
6
|
-
class CLI
|
|
7
|
-
|
|
8
|
-
def run
|
|
9
|
-
Mercenary.program(:squib) do |p|
|
|
10
|
-
p.version Squib::VERSION
|
|
11
|
-
p.description 'A Ruby DSL for prototyping card games'
|
|
12
|
-
p.syntax 'squib <subcommand> [options]'
|
|
13
|
-
|
|
14
|
-
p.command(:new) do |c|
|
|
15
|
-
c.syntax 'new PATH'
|
|
16
|
-
c.description 'Creates a new basic Squib project scaffolding in PATH. Must be a new directory or already empty.'
|
|
17
|
-
|
|
18
|
-
c.option 'advanced', '--advanced', 'Create the advanced layout'
|
|
19
|
-
|
|
20
|
-
c.action do |args, options|
|
|
21
|
-
advanced = options.key? 'advanced'
|
|
22
|
-
Squib::Commands::New.new.process(args, advanced)
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
p.command(:make_sprue) do |c|
|
|
27
|
-
c.syntax 'make_sprue'
|
|
28
|
-
c.description 'Creates a sprue definition file.'
|
|
29
|
-
|
|
30
|
-
c.action do |args, options|
|
|
31
|
-
Squib::Commands::MakeSprue.new.process(args)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
end
|
|
39
|
-
end
|
|
1
|
+
require 'mercenary'
|
|
2
|
+
require_relative 'make_sprue'
|
|
3
|
+
require_relative 'new'
|
|
4
|
+
|
|
5
|
+
module Squib
|
|
6
|
+
class CLI
|
|
7
|
+
|
|
8
|
+
def run
|
|
9
|
+
Mercenary.program(:squib) do |p|
|
|
10
|
+
p.version Squib::VERSION
|
|
11
|
+
p.description 'A Ruby DSL for prototyping card games'
|
|
12
|
+
p.syntax 'squib <subcommand> [options]'
|
|
13
|
+
|
|
14
|
+
p.command(:new) do |c|
|
|
15
|
+
c.syntax 'new PATH'
|
|
16
|
+
c.description 'Creates a new basic Squib project scaffolding in PATH. Must be a new directory or already empty.'
|
|
17
|
+
|
|
18
|
+
c.option 'advanced', '--advanced', 'Create the advanced layout'
|
|
19
|
+
|
|
20
|
+
c.action do |args, options|
|
|
21
|
+
advanced = options.key? 'advanced'
|
|
22
|
+
Squib::Commands::New.new.process(args, advanced)
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
p.command(:make_sprue) do |c|
|
|
27
|
+
c.syntax 'make_sprue'
|
|
28
|
+
c.description 'Creates a sprue definition file.'
|
|
29
|
+
|
|
30
|
+
c.action do |args, options|
|
|
31
|
+
Squib::Commands::MakeSprue.new.process(args)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -1,109 +1,109 @@
|
|
|
1
|
-
module Squib
|
|
2
|
-
class Margin
|
|
3
|
-
attr_reader :top
|
|
4
|
-
attr_reader :right
|
|
5
|
-
attr_reader :bottom
|
|
6
|
-
attr_reader :left
|
|
7
|
-
|
|
8
|
-
##
|
|
9
|
-
# Create a new margin definition.
|
|
10
|
-
#
|
|
11
|
-
# Takes +definition+ which can either be a space-separated +String+ or an
|
|
12
|
-
# +Array+ of +Float+ and will translate it to the top, right, bottom and
|
|
13
|
-
# left members.
|
|
14
|
-
#
|
|
15
|
-
# The syntax follows how CSS parses margin shorthand strings.
|
|
16
|
-
def initialize(definition)
|
|
17
|
-
if definition.instance_of? String
|
|
18
|
-
@top, @right, @bottom, @left = expand_shorthand(
|
|
19
|
-
definition.split(/\s+/).map!(&:to_f))
|
|
20
|
-
elsif definition.is_a? Numeric
|
|
21
|
-
@top, @right, @bottom, @left = expand_shorthand [definition]
|
|
22
|
-
elsif definition.instance_of? Array
|
|
23
|
-
@top, @right, @bottom, @left = expand_shorthand definition
|
|
24
|
-
else
|
|
25
|
-
raise ArgumentError, 'Invalid value, must be either string or array'
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
##
|
|
30
|
-
# Map out the margin array.
|
|
31
|
-
#
|
|
32
|
-
# Takes +margin_arr+ and attempt to expand it to a strict [top, right,
|
|
33
|
-
# bottom, left] array.
|
|
34
|
-
private def expand_shorthand(margin_arr)
|
|
35
|
-
if margin_arr.size == 1
|
|
36
|
-
all = margin_arr[0]
|
|
37
|
-
[all, all, all, all]
|
|
38
|
-
elsif margin_arr.size == 2
|
|
39
|
-
margin_arr + margin_arr
|
|
40
|
-
elsif margin_arr.size == 3
|
|
41
|
-
margin_arr + [margin_arr[1]]
|
|
42
|
-
elsif margin_arr.size >= 4
|
|
43
|
-
margin_arr[0..3]
|
|
44
|
-
else
|
|
45
|
-
raise ArgumentError, 'Invalid array'
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
class Gap
|
|
52
|
-
attr_reader :horizontal
|
|
53
|
-
attr_reader :vertical
|
|
54
|
-
|
|
55
|
-
def initialize(definition)
|
|
56
|
-
if definition.instance_of? String
|
|
57
|
-
@horizontal, @vertical = expand_shorthand(
|
|
58
|
-
definition.split(/\s+/).map!(&:to_f))
|
|
59
|
-
elsif definition.instance_of? Array
|
|
60
|
-
@horizontal, @vertical = expand_shorthand definition
|
|
61
|
-
elsif definition.is_a? Numeric
|
|
62
|
-
@horizontal, @vertical = definition, definition
|
|
63
|
-
else
|
|
64
|
-
raise ArgumentError, 'Invalid value, must be either string or array'
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
private def expand_shorthand(gap_arr)
|
|
69
|
-
if gap_arr.size >= 2
|
|
70
|
-
gap_arr[0..1]
|
|
71
|
-
elsif gap_arr.size == 1
|
|
72
|
-
gap_arr + gap_arr
|
|
73
|
-
else
|
|
74
|
-
raise ArgumentError, 'Invalid array'
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
class TemplateOption
|
|
81
|
-
attr_accessor :unit
|
|
82
|
-
attr_accessor :sheet_width
|
|
83
|
-
attr_accessor :sheet_height
|
|
84
|
-
attr_writer :sheet_margin
|
|
85
|
-
attr_accessor :sheet_align
|
|
86
|
-
attr_accessor :card_width
|
|
87
|
-
attr_accessor :card_height
|
|
88
|
-
attr_writer :card_gap
|
|
89
|
-
attr_accessor :card_ordering
|
|
90
|
-
attr_accessor :output_file
|
|
91
|
-
attr_accessor :crop_lines
|
|
92
|
-
|
|
93
|
-
def sheet_margin
|
|
94
|
-
if not @sheet_margin.instance_of? Margin
|
|
95
|
-
@sheet_margin = Margin.new @sheet_margin
|
|
96
|
-
else
|
|
97
|
-
@sheet_margin
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
def card_gap
|
|
102
|
-
if not @card_gap.instance_of? Gap
|
|
103
|
-
@card_gap = Gap.new @card_gap
|
|
104
|
-
else
|
|
105
|
-
@card_gap
|
|
106
|
-
end
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
end
|
|
1
|
+
module Squib
|
|
2
|
+
class Margin
|
|
3
|
+
attr_reader :top
|
|
4
|
+
attr_reader :right
|
|
5
|
+
attr_reader :bottom
|
|
6
|
+
attr_reader :left
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# Create a new margin definition.
|
|
10
|
+
#
|
|
11
|
+
# Takes +definition+ which can either be a space-separated +String+ or an
|
|
12
|
+
# +Array+ of +Float+ and will translate it to the top, right, bottom and
|
|
13
|
+
# left members.
|
|
14
|
+
#
|
|
15
|
+
# The syntax follows how CSS parses margin shorthand strings.
|
|
16
|
+
def initialize(definition)
|
|
17
|
+
if definition.instance_of? String
|
|
18
|
+
@top, @right, @bottom, @left = expand_shorthand(
|
|
19
|
+
definition.split(/\s+/).map!(&:to_f))
|
|
20
|
+
elsif definition.is_a? Numeric
|
|
21
|
+
@top, @right, @bottom, @left = expand_shorthand [definition]
|
|
22
|
+
elsif definition.instance_of? Array
|
|
23
|
+
@top, @right, @bottom, @left = expand_shorthand definition
|
|
24
|
+
else
|
|
25
|
+
raise ArgumentError, 'Invalid value, must be either string or array'
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
##
|
|
30
|
+
# Map out the margin array.
|
|
31
|
+
#
|
|
32
|
+
# Takes +margin_arr+ and attempt to expand it to a strict [top, right,
|
|
33
|
+
# bottom, left] array.
|
|
34
|
+
private def expand_shorthand(margin_arr)
|
|
35
|
+
if margin_arr.size == 1
|
|
36
|
+
all = margin_arr[0]
|
|
37
|
+
[all, all, all, all]
|
|
38
|
+
elsif margin_arr.size == 2
|
|
39
|
+
margin_arr + margin_arr
|
|
40
|
+
elsif margin_arr.size == 3
|
|
41
|
+
margin_arr + [margin_arr[1]]
|
|
42
|
+
elsif margin_arr.size >= 4
|
|
43
|
+
margin_arr[0..3]
|
|
44
|
+
else
|
|
45
|
+
raise ArgumentError, 'Invalid array'
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class Gap
|
|
52
|
+
attr_reader :horizontal
|
|
53
|
+
attr_reader :vertical
|
|
54
|
+
|
|
55
|
+
def initialize(definition)
|
|
56
|
+
if definition.instance_of? String
|
|
57
|
+
@horizontal, @vertical = expand_shorthand(
|
|
58
|
+
definition.split(/\s+/).map!(&:to_f))
|
|
59
|
+
elsif definition.instance_of? Array
|
|
60
|
+
@horizontal, @vertical = expand_shorthand definition
|
|
61
|
+
elsif definition.is_a? Numeric
|
|
62
|
+
@horizontal, @vertical = definition, definition
|
|
63
|
+
else
|
|
64
|
+
raise ArgumentError, 'Invalid value, must be either string or array'
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private def expand_shorthand(gap_arr)
|
|
69
|
+
if gap_arr.size >= 2
|
|
70
|
+
gap_arr[0..1]
|
|
71
|
+
elsif gap_arr.size == 1
|
|
72
|
+
gap_arr + gap_arr
|
|
73
|
+
else
|
|
74
|
+
raise ArgumentError, 'Invalid array'
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class TemplateOption
|
|
81
|
+
attr_accessor :unit
|
|
82
|
+
attr_accessor :sheet_width
|
|
83
|
+
attr_accessor :sheet_height
|
|
84
|
+
attr_writer :sheet_margin
|
|
85
|
+
attr_accessor :sheet_align
|
|
86
|
+
attr_accessor :card_width
|
|
87
|
+
attr_accessor :card_height
|
|
88
|
+
attr_writer :card_gap
|
|
89
|
+
attr_accessor :card_ordering
|
|
90
|
+
attr_accessor :output_file
|
|
91
|
+
attr_accessor :crop_lines
|
|
92
|
+
|
|
93
|
+
def sheet_margin
|
|
94
|
+
if not @sheet_margin.instance_of? Margin
|
|
95
|
+
@sheet_margin = Margin.new @sheet_margin
|
|
96
|
+
else
|
|
97
|
+
@sheet_margin
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def card_gap
|
|
102
|
+
if not @card_gap.instance_of? Gap
|
|
103
|
+
@card_gap = Gap.new @card_gap
|
|
104
|
+
else
|
|
105
|
+
@card_gap
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
@@ -1,275 +1,275 @@
|
|
|
1
|
-
require 'fileutils'
|
|
2
|
-
require 'pathname'
|
|
3
|
-
require 'highline'
|
|
4
|
-
require 'bigdecimal'
|
|
5
|
-
require 'yaml'
|
|
6
|
-
require_relative 'data/template_option'
|
|
7
|
-
|
|
8
|
-
module Squib
|
|
9
|
-
# Squib's command-line option
|
|
10
|
-
module Commands
|
|
11
|
-
# Generate a template definition file that can be used for
|
|
12
|
-
# +save_templated_sheet+
|
|
13
|
-
#
|
|
14
|
-
# @api public
|
|
15
|
-
class MakeSprue
|
|
16
|
-
# :nodoc:
|
|
17
|
-
# @api private
|
|
18
|
-
def process(args, input = $stdin, output = $stdout)
|
|
19
|
-
# Get definitions from the user
|
|
20
|
-
@option = prompt(input, output)
|
|
21
|
-
|
|
22
|
-
@printable_edge_right = (
|
|
23
|
-
@option.sheet_width - @option.sheet_margin.right)
|
|
24
|
-
@printable_edge_bottom = (
|
|
25
|
-
@option.sheet_height - @option.sheet_margin.bottom)
|
|
26
|
-
@card_iter_x = @option.card_width + @option.card_gap.horizontal
|
|
27
|
-
@card_iter_y = @option.card_height + @option.card_gap.vertical
|
|
28
|
-
|
|
29
|
-
# Recalculate the sheet margin if the sheet alignment is in the center
|
|
30
|
-
if @option.sheet_align == :center
|
|
31
|
-
@option.sheet_margin = recalculate_center_align_sheet
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# We would now have to output the file
|
|
35
|
-
YAML.dump generate_template, File.new(@option.output_file, 'w')
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
private
|
|
39
|
-
|
|
40
|
-
# Accept user input that defines the template.
|
|
41
|
-
def prompt(input, output)
|
|
42
|
-
option = TemplateOption.new
|
|
43
|
-
cli = HighLine.new(input, output)
|
|
44
|
-
|
|
45
|
-
option.unit = cli.choose do |menu|
|
|
46
|
-
menu.prompt = 'What measure unit should we use? '
|
|
47
|
-
menu.choice(:in)
|
|
48
|
-
menu.choice(:cm)
|
|
49
|
-
menu.choice(:mm)
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
cli.choose do |menu|
|
|
53
|
-
menu.prompt = 'What paper size you are using? '
|
|
54
|
-
menu.choice('A4, portrait') do
|
|
55
|
-
option.sheet_width = convert_measurement_value(
|
|
56
|
-
210, :mm, option.unit
|
|
57
|
-
)
|
|
58
|
-
option.sheet_height = convert_measurement_value(
|
|
59
|
-
297, :mm, option.unit
|
|
60
|
-
)
|
|
61
|
-
end
|
|
62
|
-
menu.choice('A4, landscape') do
|
|
63
|
-
option.sheet_width = convert_measurement_value(
|
|
64
|
-
297, :mm, option.unit
|
|
65
|
-
)
|
|
66
|
-
option.sheet_height = convert_measurement_value(
|
|
67
|
-
210, :mm, option.unit
|
|
68
|
-
)
|
|
69
|
-
end
|
|
70
|
-
menu.choice('US letter, portrait') do
|
|
71
|
-
option.sheet_width = convert_measurement_value(
|
|
72
|
-
8.5, :in, option.unit
|
|
73
|
-
)
|
|
74
|
-
option.sheet_height = convert_measurement_value(
|
|
75
|
-
11, :in, option.unit
|
|
76
|
-
)
|
|
77
|
-
end
|
|
78
|
-
menu.choice('US letter, landscape') do
|
|
79
|
-
option.sheet_width = convert_measurement_value(
|
|
80
|
-
11, :in, option.unit
|
|
81
|
-
)
|
|
82
|
-
option.sheet_height = convert_measurement_value(
|
|
83
|
-
8.5, :in, option.unit
|
|
84
|
-
)
|
|
85
|
-
end
|
|
86
|
-
menu.choice('Custom size') do
|
|
87
|
-
option.sheet_width = cli.ask(
|
|
88
|
-
"Custom paper width? (#{option.unit}) ", Float
|
|
89
|
-
)
|
|
90
|
-
option.sheet_height = cli.ask(
|
|
91
|
-
"Custom paper height? (#{option.unit}) ", Float
|
|
92
|
-
)
|
|
93
|
-
end
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
option.sheet_margin = cli.ask(
|
|
97
|
-
"Sheet margins? (#{option.unit}) "
|
|
98
|
-
) do |q|
|
|
99
|
-
q.validate = /^((\d+\.\d+|\d+) ){0,3}(\d+\.\d+|\d+)/
|
|
100
|
-
end
|
|
101
|
-
option.sheet_align = cli.choose do |menu|
|
|
102
|
-
menu.prompt = 'How to align cards on sheet? [left] '
|
|
103
|
-
menu.choice(:left)
|
|
104
|
-
menu.choice(:right)
|
|
105
|
-
menu.choice(:center)
|
|
106
|
-
menu.default = :left
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
option.card_width = cli.ask(
|
|
110
|
-
"Card width? (#{option.unit}) ", Float
|
|
111
|
-
) { |q| q.above = 0 }
|
|
112
|
-
option.card_height = cli.ask(
|
|
113
|
-
"Card height? (#{option.unit}) ", Float
|
|
114
|
-
) { |q| q.above = 0 }
|
|
115
|
-
option.card_gap = cli.ask(
|
|
116
|
-
"Gap between cards? (#{option.unit}) "
|
|
117
|
-
) { |q| q.validate = /^((\d+\.\d+|\d+))(\s+(\d+\.\d+|\d+))?/ }
|
|
118
|
-
|
|
119
|
-
option.card_ordering = cli.choose do |menu|
|
|
120
|
-
menu.prompt = 'How to layout your cards? [rows]'
|
|
121
|
-
menu.choice(:rows, text: 'In rows')
|
|
122
|
-
menu.choice(:columns, text: 'In columns')
|
|
123
|
-
menu.default = :rows
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
option.crop_lines = cli.choose do |menu|
|
|
127
|
-
menu.prompt = 'Generate crop lines? [true]'
|
|
128
|
-
menu.choice(:true)
|
|
129
|
-
menu.choice(:false)
|
|
130
|
-
menu.default = :true
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
option.output_file = cli.ask('Output to? ') do |q|
|
|
134
|
-
q.validate = lambda do |path_str|
|
|
135
|
-
path = Pathname.new path_str
|
|
136
|
-
if path.exist?
|
|
137
|
-
path.writable? && !path.directory?
|
|
138
|
-
else
|
|
139
|
-
path.dirname.writable?
|
|
140
|
-
end
|
|
141
|
-
end
|
|
142
|
-
|
|
143
|
-
q.responses[:not_valid] = (
|
|
144
|
-
'The filename specified is not a writable file or is a directory.'
|
|
145
|
-
)
|
|
146
|
-
q.default = 'template.yml'
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
option
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
def convert_measurement_value(val, from_unit, to_unit)
|
|
153
|
-
return val if from_unit == to_unit
|
|
154
|
-
|
|
155
|
-
if from_unit == :in
|
|
156
|
-
val_mm = val * 25.4
|
|
157
|
-
elsif from_unit == :cm
|
|
158
|
-
val_mm = val * 10.0
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
if to_unit == :in
|
|
162
|
-
val_mm / 25.4
|
|
163
|
-
elsif to_unit == :cm
|
|
164
|
-
val_mm / 10.0
|
|
165
|
-
else
|
|
166
|
-
val_mm
|
|
167
|
-
end
|
|
168
|
-
end
|
|
169
|
-
|
|
170
|
-
def generate_template
|
|
171
|
-
x = @option.sheet_margin.left
|
|
172
|
-
y = @option.sheet_margin.top
|
|
173
|
-
cards = []
|
|
174
|
-
horizontal_crop_lines = Set.new
|
|
175
|
-
vertical_crop_lines = Set.new
|
|
176
|
-
|
|
177
|
-
while (
|
|
178
|
-
x + @card_iter_x < @printable_edge_right &&
|
|
179
|
-
y + @card_iter_y < @printable_edge_bottom)
|
|
180
|
-
xpos = x + @option.card_gap.horizontal
|
|
181
|
-
ypos = y + @option.card_gap.vertical
|
|
182
|
-
cards.push(
|
|
183
|
-
'x' => "#{xpos}#{@option.unit}",
|
|
184
|
-
'y' => "#{ypos}#{@option.unit}"
|
|
185
|
-
)
|
|
186
|
-
|
|
187
|
-
# Append the crop lines
|
|
188
|
-
vertical_crop_lines.add xpos
|
|
189
|
-
vertical_crop_lines.add xpos + @option.card_width
|
|
190
|
-
horizontal_crop_lines.add ypos
|
|
191
|
-
horizontal_crop_lines.add ypos + @option.card_height
|
|
192
|
-
|
|
193
|
-
# Calculate the next iterator
|
|
194
|
-
if @option.card_ordering == :rows
|
|
195
|
-
x, y = next_card_pos_row(x, y)
|
|
196
|
-
elsif @option.card_ordering == :columns
|
|
197
|
-
x, y = next_card_pos_col(x, y)
|
|
198
|
-
else
|
|
199
|
-
raise RunTimeException, 'Invalid card ordering value received'
|
|
200
|
-
end
|
|
201
|
-
end
|
|
202
|
-
|
|
203
|
-
output = {
|
|
204
|
-
'sheet_width' => "#{@option.sheet_width}#{@option.unit}",
|
|
205
|
-
'sheet_height' => "#{@option.sheet_height}#{@option.unit}",
|
|
206
|
-
'card_width' => "#{@option.card_width}#{@option.unit}",
|
|
207
|
-
'card_height' => "#{@option.card_height}#{@option.unit}",
|
|
208
|
-
'cards' => cards
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if @option.crop_lines == :true
|
|
212
|
-
lines = []
|
|
213
|
-
vertical_crop_lines.each do |val|
|
|
214
|
-
lines.push(
|
|
215
|
-
'type' => :vertical, 'position' => "#{val}#{@option.unit}"
|
|
216
|
-
)
|
|
217
|
-
end
|
|
218
|
-
horizontal_crop_lines.each do |val|
|
|
219
|
-
lines.push(
|
|
220
|
-
'type' => :horizontal, 'position' => "#{val}#{@option.unit}"
|
|
221
|
-
)
|
|
222
|
-
end
|
|
223
|
-
output['crop_line'] = { 'lines' => lines }
|
|
224
|
-
end
|
|
225
|
-
|
|
226
|
-
# Return the output data
|
|
227
|
-
output
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
def recalculate_center_align_sheet
|
|
231
|
-
# We will still respect the user specified margins
|
|
232
|
-
printable_width = (
|
|
233
|
-
@option.sheet_width - @option.sheet_margin.left -
|
|
234
|
-
@option.sheet_margin.right)
|
|
235
|
-
num_of_cols, remainder = printable_width.divmod(@card_iter_x)
|
|
236
|
-
if (
|
|
237
|
-
@option.card_gap.horizontal > 0 &&
|
|
238
|
-
remainder < @option.card_gap.horizontal)
|
|
239
|
-
num_of_cols -= 1
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
new_hor_margin = (
|
|
243
|
-
(@option.sheet_width - num_of_cols * @card_iter_x -
|
|
244
|
-
@option.card_gap.horizontal) / 2)
|
|
245
|
-
Margin.new [
|
|
246
|
-
@option.sheet_margin.top,
|
|
247
|
-
new_hor_margin,
|
|
248
|
-
@option.sheet_margin.bottom
|
|
249
|
-
]
|
|
250
|
-
end
|
|
251
|
-
|
|
252
|
-
def next_card_pos_row(x, y)
|
|
253
|
-
x += @card_iter_x
|
|
254
|
-
|
|
255
|
-
if (x + @card_iter_x) > @printable_edge_right
|
|
256
|
-
x = @option.sheet_margin.left
|
|
257
|
-
y += @card_iter_y
|
|
258
|
-
end
|
|
259
|
-
|
|
260
|
-
[x, y]
|
|
261
|
-
end
|
|
262
|
-
|
|
263
|
-
def next_card_pos_col(x, y)
|
|
264
|
-
y += @card_iter_y
|
|
265
|
-
|
|
266
|
-
if (y + @card_iter_y) > @printable_edge_bottom
|
|
267
|
-
x += @card_iter_x
|
|
268
|
-
y = @option.sheet_margin.top
|
|
269
|
-
end
|
|
270
|
-
|
|
271
|
-
[x, y]
|
|
272
|
-
end
|
|
273
|
-
end
|
|
274
|
-
end
|
|
275
|
-
end
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
require 'pathname'
|
|
3
|
+
require 'highline'
|
|
4
|
+
require 'bigdecimal'
|
|
5
|
+
require 'yaml'
|
|
6
|
+
require_relative 'data/template_option'
|
|
7
|
+
|
|
8
|
+
module Squib
|
|
9
|
+
# Squib's command-line option
|
|
10
|
+
module Commands
|
|
11
|
+
# Generate a template definition file that can be used for
|
|
12
|
+
# +save_templated_sheet+
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class MakeSprue
|
|
16
|
+
# :nodoc:
|
|
17
|
+
# @api private
|
|
18
|
+
def process(args, input = $stdin, output = $stdout)
|
|
19
|
+
# Get definitions from the user
|
|
20
|
+
@option = prompt(input, output)
|
|
21
|
+
|
|
22
|
+
@printable_edge_right = (
|
|
23
|
+
@option.sheet_width - @option.sheet_margin.right)
|
|
24
|
+
@printable_edge_bottom = (
|
|
25
|
+
@option.sheet_height - @option.sheet_margin.bottom)
|
|
26
|
+
@card_iter_x = @option.card_width + @option.card_gap.horizontal
|
|
27
|
+
@card_iter_y = @option.card_height + @option.card_gap.vertical
|
|
28
|
+
|
|
29
|
+
# Recalculate the sheet margin if the sheet alignment is in the center
|
|
30
|
+
if @option.sheet_align == :center
|
|
31
|
+
@option.sheet_margin = recalculate_center_align_sheet
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# We would now have to output the file
|
|
35
|
+
YAML.dump generate_template, File.new(@option.output_file, 'w')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
# Accept user input that defines the template.
|
|
41
|
+
def prompt(input, output)
|
|
42
|
+
option = TemplateOption.new
|
|
43
|
+
cli = HighLine.new(input, output)
|
|
44
|
+
|
|
45
|
+
option.unit = cli.choose do |menu|
|
|
46
|
+
menu.prompt = 'What measure unit should we use? '
|
|
47
|
+
menu.choice(:in)
|
|
48
|
+
menu.choice(:cm)
|
|
49
|
+
menu.choice(:mm)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
cli.choose do |menu|
|
|
53
|
+
menu.prompt = 'What paper size you are using? '
|
|
54
|
+
menu.choice('A4, portrait') do
|
|
55
|
+
option.sheet_width = convert_measurement_value(
|
|
56
|
+
210, :mm, option.unit
|
|
57
|
+
)
|
|
58
|
+
option.sheet_height = convert_measurement_value(
|
|
59
|
+
297, :mm, option.unit
|
|
60
|
+
)
|
|
61
|
+
end
|
|
62
|
+
menu.choice('A4, landscape') do
|
|
63
|
+
option.sheet_width = convert_measurement_value(
|
|
64
|
+
297, :mm, option.unit
|
|
65
|
+
)
|
|
66
|
+
option.sheet_height = convert_measurement_value(
|
|
67
|
+
210, :mm, option.unit
|
|
68
|
+
)
|
|
69
|
+
end
|
|
70
|
+
menu.choice('US letter, portrait') do
|
|
71
|
+
option.sheet_width = convert_measurement_value(
|
|
72
|
+
8.5, :in, option.unit
|
|
73
|
+
)
|
|
74
|
+
option.sheet_height = convert_measurement_value(
|
|
75
|
+
11, :in, option.unit
|
|
76
|
+
)
|
|
77
|
+
end
|
|
78
|
+
menu.choice('US letter, landscape') do
|
|
79
|
+
option.sheet_width = convert_measurement_value(
|
|
80
|
+
11, :in, option.unit
|
|
81
|
+
)
|
|
82
|
+
option.sheet_height = convert_measurement_value(
|
|
83
|
+
8.5, :in, option.unit
|
|
84
|
+
)
|
|
85
|
+
end
|
|
86
|
+
menu.choice('Custom size') do
|
|
87
|
+
option.sheet_width = cli.ask(
|
|
88
|
+
"Custom paper width? (#{option.unit}) ", Float
|
|
89
|
+
)
|
|
90
|
+
option.sheet_height = cli.ask(
|
|
91
|
+
"Custom paper height? (#{option.unit}) ", Float
|
|
92
|
+
)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
option.sheet_margin = cli.ask(
|
|
97
|
+
"Sheet margins? (#{option.unit}) "
|
|
98
|
+
) do |q|
|
|
99
|
+
q.validate = /^((\d+\.\d+|\d+) ){0,3}(\d+\.\d+|\d+)/
|
|
100
|
+
end
|
|
101
|
+
option.sheet_align = cli.choose do |menu|
|
|
102
|
+
menu.prompt = 'How to align cards on sheet? [left] '
|
|
103
|
+
menu.choice(:left)
|
|
104
|
+
menu.choice(:right)
|
|
105
|
+
menu.choice(:center)
|
|
106
|
+
menu.default = :left
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
option.card_width = cli.ask(
|
|
110
|
+
"Card width? (#{option.unit}) ", Float
|
|
111
|
+
) { |q| q.above = 0 }
|
|
112
|
+
option.card_height = cli.ask(
|
|
113
|
+
"Card height? (#{option.unit}) ", Float
|
|
114
|
+
) { |q| q.above = 0 }
|
|
115
|
+
option.card_gap = cli.ask(
|
|
116
|
+
"Gap between cards? (#{option.unit}) "
|
|
117
|
+
) { |q| q.validate = /^((\d+\.\d+|\d+))(\s+(\d+\.\d+|\d+))?/ }
|
|
118
|
+
|
|
119
|
+
option.card_ordering = cli.choose do |menu|
|
|
120
|
+
menu.prompt = 'How to layout your cards? [rows]'
|
|
121
|
+
menu.choice(:rows, text: 'In rows')
|
|
122
|
+
menu.choice(:columns, text: 'In columns')
|
|
123
|
+
menu.default = :rows
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
option.crop_lines = cli.choose do |menu|
|
|
127
|
+
menu.prompt = 'Generate crop lines? [true]'
|
|
128
|
+
menu.choice(:true)
|
|
129
|
+
menu.choice(:false)
|
|
130
|
+
menu.default = :true
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
option.output_file = cli.ask('Output to? ') do |q|
|
|
134
|
+
q.validate = lambda do |path_str|
|
|
135
|
+
path = Pathname.new path_str
|
|
136
|
+
if path.exist?
|
|
137
|
+
path.writable? && !path.directory?
|
|
138
|
+
else
|
|
139
|
+
path.dirname.writable?
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
q.responses[:not_valid] = (
|
|
144
|
+
'The filename specified is not a writable file or is a directory.'
|
|
145
|
+
)
|
|
146
|
+
q.default = 'template.yml'
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
option
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def convert_measurement_value(val, from_unit, to_unit)
|
|
153
|
+
return val if from_unit == to_unit
|
|
154
|
+
|
|
155
|
+
if from_unit == :in
|
|
156
|
+
val_mm = val * 25.4
|
|
157
|
+
elsif from_unit == :cm
|
|
158
|
+
val_mm = val * 10.0
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
if to_unit == :in
|
|
162
|
+
val_mm / 25.4
|
|
163
|
+
elsif to_unit == :cm
|
|
164
|
+
val_mm / 10.0
|
|
165
|
+
else
|
|
166
|
+
val_mm
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def generate_template
|
|
171
|
+
x = @option.sheet_margin.left
|
|
172
|
+
y = @option.sheet_margin.top
|
|
173
|
+
cards = []
|
|
174
|
+
horizontal_crop_lines = Set.new
|
|
175
|
+
vertical_crop_lines = Set.new
|
|
176
|
+
|
|
177
|
+
while (
|
|
178
|
+
x + @card_iter_x < @printable_edge_right &&
|
|
179
|
+
y + @card_iter_y < @printable_edge_bottom)
|
|
180
|
+
xpos = x + @option.card_gap.horizontal
|
|
181
|
+
ypos = y + @option.card_gap.vertical
|
|
182
|
+
cards.push(
|
|
183
|
+
'x' => "#{xpos}#{@option.unit}",
|
|
184
|
+
'y' => "#{ypos}#{@option.unit}"
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
# Append the crop lines
|
|
188
|
+
vertical_crop_lines.add xpos
|
|
189
|
+
vertical_crop_lines.add xpos + @option.card_width
|
|
190
|
+
horizontal_crop_lines.add ypos
|
|
191
|
+
horizontal_crop_lines.add ypos + @option.card_height
|
|
192
|
+
|
|
193
|
+
# Calculate the next iterator
|
|
194
|
+
if @option.card_ordering == :rows
|
|
195
|
+
x, y = next_card_pos_row(x, y)
|
|
196
|
+
elsif @option.card_ordering == :columns
|
|
197
|
+
x, y = next_card_pos_col(x, y)
|
|
198
|
+
else
|
|
199
|
+
raise RunTimeException, 'Invalid card ordering value received'
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
output = {
|
|
204
|
+
'sheet_width' => "#{@option.sheet_width}#{@option.unit}",
|
|
205
|
+
'sheet_height' => "#{@option.sheet_height}#{@option.unit}",
|
|
206
|
+
'card_width' => "#{@option.card_width}#{@option.unit}",
|
|
207
|
+
'card_height' => "#{@option.card_height}#{@option.unit}",
|
|
208
|
+
'cards' => cards
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if @option.crop_lines == :true
|
|
212
|
+
lines = []
|
|
213
|
+
vertical_crop_lines.each do |val|
|
|
214
|
+
lines.push(
|
|
215
|
+
'type' => :vertical, 'position' => "#{val}#{@option.unit}"
|
|
216
|
+
)
|
|
217
|
+
end
|
|
218
|
+
horizontal_crop_lines.each do |val|
|
|
219
|
+
lines.push(
|
|
220
|
+
'type' => :horizontal, 'position' => "#{val}#{@option.unit}"
|
|
221
|
+
)
|
|
222
|
+
end
|
|
223
|
+
output['crop_line'] = { 'lines' => lines }
|
|
224
|
+
end
|
|
225
|
+
|
|
226
|
+
# Return the output data
|
|
227
|
+
output
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
def recalculate_center_align_sheet
|
|
231
|
+
# We will still respect the user specified margins
|
|
232
|
+
printable_width = (
|
|
233
|
+
@option.sheet_width - @option.sheet_margin.left -
|
|
234
|
+
@option.sheet_margin.right)
|
|
235
|
+
num_of_cols, remainder = printable_width.divmod(@card_iter_x)
|
|
236
|
+
if (
|
|
237
|
+
@option.card_gap.horizontal > 0 &&
|
|
238
|
+
remainder < @option.card_gap.horizontal)
|
|
239
|
+
num_of_cols -= 1
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
new_hor_margin = (
|
|
243
|
+
(@option.sheet_width - num_of_cols * @card_iter_x -
|
|
244
|
+
@option.card_gap.horizontal) / 2)
|
|
245
|
+
Margin.new [
|
|
246
|
+
@option.sheet_margin.top,
|
|
247
|
+
new_hor_margin,
|
|
248
|
+
@option.sheet_margin.bottom
|
|
249
|
+
]
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def next_card_pos_row(x, y)
|
|
253
|
+
x += @card_iter_x
|
|
254
|
+
|
|
255
|
+
if (x + @card_iter_x) > @printable_edge_right
|
|
256
|
+
x = @option.sheet_margin.left
|
|
257
|
+
y += @card_iter_y
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
[x, y]
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def next_card_pos_col(x, y)
|
|
264
|
+
y += @card_iter_y
|
|
265
|
+
|
|
266
|
+
if (y + @card_iter_y) > @printable_edge_bottom
|
|
267
|
+
x += @card_iter_x
|
|
268
|
+
y = @option.sheet_margin.top
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
[x, y]
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|