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.
Files changed (162) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +41 -41
  3. data/.travis.yml +19 -19
  4. data/CHANGELOG.md +357 -340
  5. data/CONTRIBUTING.md +40 -40
  6. data/Gemfile +2 -2
  7. data/LICENSE.txt +22 -22
  8. data/README.md +121 -121
  9. data/RELEASE TODO.md +21 -21
  10. data/Rakefile +48 -48
  11. data/appveyor.yml +24 -24
  12. data/bin/squib +4 -4
  13. data/lib/squib.rb +32 -32
  14. data/lib/squib/api/background.rb +15 -15
  15. data/lib/squib/api/data.rb +137 -137
  16. data/lib/squib/api/groups.rb +54 -54
  17. data/lib/squib/api/image.rb +49 -49
  18. data/lib/squib/api/save.rb +83 -83
  19. data/lib/squib/api/settings.rb +21 -21
  20. data/lib/squib/api/shapes.rb +124 -124
  21. data/lib/squib/api/text.rb +25 -25
  22. data/lib/squib/api/text_embed.rb +71 -71
  23. data/lib/squib/api/units.rb +27 -27
  24. data/lib/squib/args/arg_loader.rb +126 -126
  25. data/lib/squib/args/box.rb +55 -55
  26. data/lib/squib/args/card_range.rb +32 -32
  27. data/lib/squib/args/color_validator.rb +12 -12
  28. data/lib/squib/args/coords.rb +35 -35
  29. data/lib/squib/args/csv_opts.rb +25 -25
  30. data/lib/squib/args/dir_validator.rb +16 -16
  31. data/lib/squib/args/draw.rb +92 -92
  32. data/lib/squib/args/embed_adjust.rb +25 -25
  33. data/lib/squib/args/embed_key.rb +17 -17
  34. data/lib/squib/args/hand_special.rb +37 -37
  35. data/lib/squib/args/import.rb +40 -40
  36. data/lib/squib/args/input_file.rb +37 -37
  37. data/lib/squib/args/paint.rb +44 -44
  38. data/lib/squib/args/paragraph.rb +116 -116
  39. data/lib/squib/args/save_batch.rb +63 -63
  40. data/lib/squib/args/scale_box.rb +53 -53
  41. data/lib/squib/args/sheet.rb +172 -172
  42. data/lib/squib/args/showcase_special.rb +38 -38
  43. data/lib/squib/args/sprue_file.rb +44 -44
  44. data/lib/squib/args/svg_special.rb +37 -37
  45. data/lib/squib/args/transform.rb +61 -61
  46. data/lib/squib/args/typographer.rb +119 -119
  47. data/lib/squib/args/unit_conversion.rb +29 -29
  48. data/lib/squib/builtin/layouts/economy.yml +85 -85
  49. data/lib/squib/builtin/layouts/fantasy.yml +101 -101
  50. data/lib/squib/builtin/layouts/hand.yml +62 -62
  51. data/lib/squib/builtin/layouts/party.yml +94 -94
  52. data/lib/squib/builtin/layouts/playing-card.yml +35 -35
  53. data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
  54. data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
  55. data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
  56. data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
  57. data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
  58. data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
  59. data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
  60. data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
  61. data/lib/squib/builtin/projects/advanced/config.yml +49 -49
  62. data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
  63. data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
  64. data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
  65. data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
  66. data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
  67. data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
  68. data/lib/squib/builtin/projects/basic/.gitignore +4 -4
  69. data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
  70. data/lib/squib/builtin/projects/basic/Gemfile +3 -3
  71. data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
  72. data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
  73. data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
  74. data/lib/squib/builtin/projects/basic/RULES.md +21 -21
  75. data/lib/squib/builtin/projects/basic/Rakefile +7 -7
  76. data/lib/squib/builtin/projects/basic/config.yml +49 -49
  77. data/lib/squib/builtin/projects/basic/deck.rb +6 -6
  78. data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
  79. data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
  80. data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
  81. data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
  82. data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -0
  83. data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
  84. data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
  85. data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -0
  86. data/lib/squib/card.rb +75 -74
  87. data/lib/squib/commands/cli.rb +39 -39
  88. data/lib/squib/commands/data/template_option.rb +109 -109
  89. data/lib/squib/commands/make_sprue.rb +275 -275
  90. data/lib/squib/commands/new.rb +77 -77
  91. data/lib/squib/conf.rb +139 -139
  92. data/lib/squib/constants.rb +17 -17
  93. data/lib/squib/deck.rb +116 -116
  94. data/lib/squib/graphics/background.rb +14 -14
  95. data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
  96. data/lib/squib/graphics/embedding_utils.rb +28 -28
  97. data/lib/squib/graphics/gradient_regex.rb +47 -47
  98. data/lib/squib/graphics/hand.rb +42 -42
  99. data/lib/squib/graphics/image.rb +108 -108
  100. data/lib/squib/graphics/save_doc.rb +61 -61
  101. data/lib/squib/graphics/save_images.rb +52 -52
  102. data/lib/squib/graphics/save_pdf.rb +90 -90
  103. data/lib/squib/graphics/save_sprue.rb +204 -205
  104. data/lib/squib/graphics/shapes.rb +143 -143
  105. data/lib/squib/graphics/showcase.rb +85 -85
  106. data/lib/squib/graphics/text.rb +174 -174
  107. data/lib/squib/import/data_frame.rb +108 -108
  108. data/lib/squib/layout_parser.rb +138 -126
  109. data/lib/squib/progress.rb +38 -38
  110. data/lib/squib/sample_helpers.rb +34 -34
  111. data/lib/squib/sprues/crop_line.rb +28 -28
  112. data/lib/squib/sprues/crop_line_dash.rb +35 -35
  113. data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
  114. data/lib/squib/sprues/sprue.rb +203 -203
  115. data/lib/squib/sprues/sprue_schema.rb +48 -48
  116. data/lib/squib/version.rb +10 -10
  117. data/samples/autoscale_font/_autoscale_font.rb +29 -29
  118. data/samples/backend/_backend.rb +26 -26
  119. data/samples/basic.rb +19 -19
  120. data/samples/build_groups/build_groups.rb +36 -36
  121. data/samples/colors/_colors.rb +38 -38
  122. data/samples/colors/_gradients.rb +34 -34
  123. data/samples/config/config_text_markup.rb +20 -20
  124. data/samples/config/custom_config.rb +18 -18
  125. data/samples/data/_csv.rb +33 -33
  126. data/samples/data/_excel.rb +55 -55
  127. data/samples/data/_yaml.rb +12 -12
  128. data/samples/hello_world.rb +6 -6
  129. data/samples/images/_cairo_access.rb +39 -39
  130. data/samples/images/_images.rb +104 -104
  131. data/samples/images/_more_load_images.rb +102 -102
  132. data/samples/intro/01_hello.rb +8 -8
  133. data/samples/intro/02_options.rb +14 -14
  134. data/samples/intro/03_layout.rb +11 -11
  135. data/samples/intro/04_arrays.rb +15 -15
  136. data/samples/intro/05_excel.rb +14 -14
  137. data/samples/layouts/builtin_layouts.rb +97 -97
  138. data/samples/layouts/layouts.rb +71 -71
  139. data/samples/project/src/characters.rb +8 -8
  140. data/samples/project/src/skills.rb +7 -7
  141. data/samples/proofs/_tgc_proofs.rb +16 -16
  142. data/samples/ranges/_ranges.rb +64 -64
  143. data/samples/saves/_hand.rb +23 -23
  144. data/samples/saves/_portrait_landscape.rb +23 -23
  145. data/samples/saves/_save_pdf.rb +29 -29
  146. data/samples/saves/_saves.rb +51 -51
  147. data/samples/saves/_showcase.rb +25 -25
  148. data/samples/shapes/_draw_shapes.rb +60 -58
  149. data/samples/shapes/_proofs.rb +22 -22
  150. data/samples/sprues/_advanced_sprues.rb +24 -24
  151. data/samples/sprues/_builtin_sprues.rb +21 -19
  152. data/samples/sprues/_fold_sheet.rb +27 -27
  153. data/samples/sprues/_hex_tiles.rb +15 -15
  154. data/samples/sprues/_mints.rb +11 -11
  155. data/samples/sprues/_sprue_example.rb +11 -11
  156. data/samples/text/_embed_text.rb +128 -128
  157. data/samples/text/_text.rb +47 -47
  158. data/samples/text/_text_options.rb +102 -102
  159. data/samples/text/bug134.rb +14 -14
  160. data/samples/units/_units.rb +32 -32
  161. data/squib.gemspec +52 -51
  162. metadata +66 -50
@@ -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
- block.yield(@cairo_context)
51
- @cairo_context.restore
52
- end
53
-
54
- def finish!
55
- begin
56
- @cairo_surface.finish
57
- rescue Cairo::SurfaceFinishedError
58
- # do nothin - if it's already finished that's fine
59
- end
60
- end
61
-
62
- ########################
63
- ### BACKEND GRAPHICS ###
64
- ########################
65
- require_relative 'graphics/background'
66
- require_relative 'graphics/image'
67
- require_relative 'graphics/save_doc'
68
- require_relative 'graphics/save_images'
69
- require_relative 'graphics/shapes'
70
- require_relative 'graphics/showcase'
71
- require_relative 'graphics/text'
72
-
73
- end
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
@@ -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