squib 0.15.2 → 0.15.3

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 (165) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +41 -41
  3. data/.travis.yml +17 -17
  4. data/CHANGELOG.md +400 -395
  5. data/CONTRIBUTING.md +40 -40
  6. data/Dockerfile +25 -25
  7. data/Gemfile +2 -2
  8. data/LICENSE.txt +22 -22
  9. data/README.md +145 -145
  10. data/RELEASE TODO.md +21 -21
  11. data/Rakefile +48 -48
  12. data/appveyor.yml +25 -25
  13. data/bin/squib +4 -4
  14. data/lib/squib.rb +32 -32
  15. data/lib/squib/api/background.rb +15 -15
  16. data/lib/squib/api/data.rb +137 -137
  17. data/lib/squib/api/groups.rb +54 -54
  18. data/lib/squib/api/image.rb +49 -49
  19. data/lib/squib/api/save.rb +83 -83
  20. data/lib/squib/api/settings.rb +21 -21
  21. data/lib/squib/api/shapes.rb +124 -124
  22. data/lib/squib/api/text.rb +25 -25
  23. data/lib/squib/api/text_embed.rb +71 -71
  24. data/lib/squib/api/units.rb +27 -27
  25. data/lib/squib/args/arg_loader.rb +126 -126
  26. data/lib/squib/args/box.rb +55 -55
  27. data/lib/squib/args/card_range.rb +32 -32
  28. data/lib/squib/args/color_validator.rb +12 -12
  29. data/lib/squib/args/coords.rb +35 -35
  30. data/lib/squib/args/csv_opts.rb +25 -25
  31. data/lib/squib/args/dir_validator.rb +16 -16
  32. data/lib/squib/args/draw.rb +92 -92
  33. data/lib/squib/args/embed_adjust.rb +25 -25
  34. data/lib/squib/args/embed_key.rb +17 -17
  35. data/lib/squib/args/hand_special.rb +37 -37
  36. data/lib/squib/args/import.rb +40 -40
  37. data/lib/squib/args/input_file.rb +37 -37
  38. data/lib/squib/args/paint.rb +44 -44
  39. data/lib/squib/args/paragraph.rb +116 -116
  40. data/lib/squib/args/save_batch.rb +63 -63
  41. data/lib/squib/args/scale_box.rb +53 -53
  42. data/lib/squib/args/sheet.rb +172 -172
  43. data/lib/squib/args/showcase_special.rb +41 -41
  44. data/lib/squib/args/sprue_file.rb +44 -44
  45. data/lib/squib/args/svg_special.rb +37 -37
  46. data/lib/squib/args/transform.rb +61 -61
  47. data/lib/squib/args/typographer.rb +119 -119
  48. data/lib/squib/args/unit_conversion.rb +29 -29
  49. data/lib/squib/builtin/layouts/economy.yml +85 -85
  50. data/lib/squib/builtin/layouts/fantasy.yml +101 -101
  51. data/lib/squib/builtin/layouts/hand.yml +62 -62
  52. data/lib/squib/builtin/layouts/party.yml +94 -94
  53. data/lib/squib/builtin/layouts/playing-card.yml +35 -35
  54. data/lib/squib/builtin/layouts/tuck_box.yml +46 -46
  55. data/lib/squib/builtin/projects/advanced/.gitignore +4 -4
  56. data/lib/squib/builtin/projects/advanced/ABOUT.md +19 -19
  57. data/lib/squib/builtin/projects/advanced/Gemfile +11 -11
  58. data/lib/squib/builtin/projects/advanced/Guardfile +21 -21
  59. data/lib/squib/builtin/projects/advanced/IDEAS.md +22 -22
  60. data/lib/squib/builtin/projects/advanced/PLAYTESTING.md +26 -26
  61. data/lib/squib/builtin/projects/advanced/Rakefile +27 -27
  62. data/lib/squib/builtin/projects/advanced/config.yml +49 -49
  63. data/lib/squib/builtin/projects/advanced/docs/PNP NOTES.md +3 -3
  64. data/lib/squib/builtin/projects/advanced/docs/RULES.md +21 -21
  65. data/lib/squib/builtin/projects/advanced/img/example.svg +60 -60
  66. data/lib/squib/builtin/projects/advanced/layouts/deck.yml +27 -27
  67. data/lib/squib/builtin/projects/advanced/src/deck.rb +34 -34
  68. data/lib/squib/builtin/projects/advanced/src/version.rb +3 -3
  69. data/lib/squib/builtin/projects/basic/.gitignore +4 -4
  70. data/lib/squib/builtin/projects/basic/ABOUT.md +19 -19
  71. data/lib/squib/builtin/projects/basic/Gemfile +3 -3
  72. data/lib/squib/builtin/projects/basic/IDEAS.md +22 -22
  73. data/lib/squib/builtin/projects/basic/PLAYTESTING.md +26 -26
  74. data/lib/squib/builtin/projects/basic/PNP NOTES.md +3 -3
  75. data/lib/squib/builtin/projects/basic/RULES.md +21 -21
  76. data/lib/squib/builtin/projects/basic/Rakefile +7 -7
  77. data/lib/squib/builtin/projects/basic/config.yml +49 -49
  78. data/lib/squib/builtin/projects/basic/deck.rb +6 -6
  79. data/lib/squib/builtin/sprues/a4_euro_card.yml +42 -42
  80. data/lib/squib/builtin/sprues/a4_poker_card_8up.yml +40 -40
  81. data/lib/squib/builtin/sprues/a4_poker_card_9up.yml +42 -42
  82. data/lib/squib/builtin/sprues/a4_usa_card.yml +42 -42
  83. data/lib/squib/builtin/sprues/drivethrucards_1up.yml +10 -10
  84. data/lib/squib/builtin/sprues/letter_poker_card_9up.yml +25 -25
  85. data/lib/squib/builtin/sprues/letter_poker_foldable_8up.yml +52 -52
  86. data/lib/squib/builtin/sprues/printplaygames_18up.yml +68 -68
  87. data/lib/squib/card.rb +75 -75
  88. data/lib/squib/commands/cli.rb +39 -39
  89. data/lib/squib/commands/data/template_option.rb +109 -109
  90. data/lib/squib/commands/make_sprue.rb +277 -277
  91. data/lib/squib/commands/new.rb +77 -77
  92. data/lib/squib/conf.rb +139 -139
  93. data/lib/squib/constants.rb +17 -17
  94. data/lib/squib/deck.rb +116 -116
  95. data/lib/squib/graphics/background.rb +14 -14
  96. data/lib/squib/graphics/cairo_context_wrapper.rb +113 -113
  97. data/lib/squib/graphics/embedding_utils.rb +28 -28
  98. data/lib/squib/graphics/gradient_regex.rb +47 -47
  99. data/lib/squib/graphics/hand.rb +42 -42
  100. data/lib/squib/graphics/image.rb +123 -123
  101. data/lib/squib/graphics/save_doc.rb +61 -61
  102. data/lib/squib/graphics/save_images.rb +52 -52
  103. data/lib/squib/graphics/save_pdf.rb +90 -90
  104. data/lib/squib/graphics/save_sprue.rb +219 -219
  105. data/lib/squib/graphics/shapes.rb +143 -143
  106. data/lib/squib/graphics/showcase.rb +85 -85
  107. data/lib/squib/graphics/text.rb +174 -174
  108. data/lib/squib/import/data_frame.rb +108 -108
  109. data/lib/squib/layout_parser.rb +138 -138
  110. data/lib/squib/progress.rb +38 -38
  111. data/lib/squib/sample_helpers.rb +34 -34
  112. data/lib/squib/sprues/crop_line.rb +28 -28
  113. data/lib/squib/sprues/crop_line_dash.rb +35 -35
  114. data/lib/squib/sprues/invalid_sprue_definition.rb +9 -9
  115. data/lib/squib/sprues/sprue.rb +206 -206
  116. data/lib/squib/sprues/sprue_schema.rb +50 -50
  117. data/lib/squib/version.rb +10 -10
  118. data/samples/autoscale_font/_autoscale_font.rb +29 -29
  119. data/samples/backend/_backend.rb +26 -26
  120. data/samples/basic.rb +19 -19
  121. data/samples/build_groups/build_groups.rb +36 -36
  122. data/samples/colors/_colors.rb +44 -44
  123. data/samples/colors/_gradients.rb +34 -34
  124. data/samples/colors/_switch_color.rb +33 -33
  125. data/samples/config/config_text_markup.rb +20 -20
  126. data/samples/config/custom_config.rb +18 -18
  127. data/samples/data/_csv.rb +33 -33
  128. data/samples/data/_excel.rb +55 -55
  129. data/samples/data/_yaml.rb +12 -12
  130. data/samples/hello_world.rb +6 -6
  131. data/samples/images/_cairo_access.rb +39 -39
  132. data/samples/images/_images.rb +104 -104
  133. data/samples/images/_more_load_images.rb +102 -102
  134. data/samples/intro/01_hello.rb +8 -8
  135. data/samples/intro/02_options.rb +14 -14
  136. data/samples/intro/03_layout.rb +11 -11
  137. data/samples/intro/04_arrays.rb +15 -15
  138. data/samples/intro/05_excel.rb +14 -14
  139. data/samples/layouts/builtin_layouts.rb +97 -97
  140. data/samples/layouts/layouts.rb +71 -71
  141. data/samples/project/src/characters.rb +8 -8
  142. data/samples/project/src/skills.rb +7 -7
  143. data/samples/proofs/_tgc_proofs.rb +16 -16
  144. data/samples/ranges/_ranges.rb +64 -64
  145. data/samples/saves/_hand.rb +23 -23
  146. data/samples/saves/_portrait_landscape.rb +23 -23
  147. data/samples/saves/_save_filenames.rb +24 -24
  148. data/samples/saves/_save_pdf.rb +29 -29
  149. data/samples/saves/_saves.rb +51 -51
  150. data/samples/saves/_showcase.rb +25 -25
  151. data/samples/shapes/_draw_shapes.rb +60 -60
  152. data/samples/shapes/_proofs.rb +22 -22
  153. data/samples/sprues/_advanced_sprues.rb +25 -25
  154. data/samples/sprues/_builtin_sprues.rb +21 -21
  155. data/samples/sprues/_fold_sheet.rb +27 -27
  156. data/samples/sprues/_hex_tiles.rb +15 -15
  157. data/samples/sprues/_mints.rb +11 -11
  158. data/samples/sprues/_sprue_example.rb +11 -11
  159. data/samples/text/_embed_text.rb +128 -128
  160. data/samples/text/_text.rb +47 -47
  161. data/samples/text/_text_options.rb +102 -102
  162. data/samples/text/bug134.rb +14 -14
  163. data/samples/units/_units.rb +32 -32
  164. data/squib.gemspec +52 -52
  165. metadata +62 -63
@@ -1,28 +1,28 @@
1
- module Squib
2
- class EmbeddingUtils
3
-
4
- # Given a string and a bunch of keys, give us back a mapping of those keys
5
- # to where those keys start, and where they end (in ranges)
6
- #
7
- # See the spec for expected outputs
8
- def self.indices(str, keys)
9
- map = {}
10
- keys.each do |key|
11
- map[key] ||= []
12
- start = 0
13
- while true
14
- idx = str.index(key, start)
15
- if idx.nil?
16
- break; # done searching
17
- else
18
- idx_bytes = str[0..idx].bytesize - 1
19
- map[key] << (idx_bytes..(idx_bytes + key.size))
20
- start = idx + 1
21
- end
22
- end
23
- end
24
- return map
25
- end
26
-
27
- end
28
- end
1
+ module Squib
2
+ class EmbeddingUtils
3
+
4
+ # Given a string and a bunch of keys, give us back a mapping of those keys
5
+ # to where those keys start, and where they end (in ranges)
6
+ #
7
+ # See the spec for expected outputs
8
+ def self.indices(str, keys)
9
+ map = {}
10
+ keys.each do |key|
11
+ map[key] ||= []
12
+ start = 0
13
+ while true
14
+ idx = str.index(key, start)
15
+ if idx.nil?
16
+ break; # done searching
17
+ else
18
+ idx_bytes = str[0..idx].bytesize - 1
19
+ map[key] << (idx_bytes..(idx_bytes + key.size))
20
+ start = idx + 1
21
+ end
22
+ end
23
+ end
24
+ return map
25
+ end
26
+
27
+ end
28
+ end
@@ -1,47 +1,47 @@
1
- module Squib
2
- # @api private
3
- module Graphics
4
- STOPS = / # used to capture the stops
5
- \s* # leading whitespace is ok
6
- (\#?[\w]+) # color
7
- @ # no spaces here
8
- (\d+\.?\d*) # offset number
9
- /x
10
-
11
- LINEAR_GRADIENT = /
12
- \( \s* # coordinate 1
13
- (\d+\.?\d*) \s* # x1 number
14
- ,\s* # whitespace after comma is ok
15
- (\d+\.?\d*) \s* # y1 number
16
- \)
17
- \s* # space between coordinates is ok
18
- \( \s* # coordinate 2
19
- (\d+\.?\d*) \s* # x2 number
20
- ,\s* # whitespace after comma is ok
21
- (\d+\.?\d*) \s* # y2 number
22
- \)
23
- (#{STOPS})+ # stops
24
- \s* # trailing whitespace is ok
25
- /x
26
-
27
- RADIAL_GRADIENT = /
28
- \( \s* # coordinate 1
29
- (\d+\.?\d*) \s* # x1 number
30
- ,\s* # whitespace after comma is ok
31
- (\d+\.?\d*) \s* # y1 number
32
- ,\s* # whitespace after comma is ok
33
- (\d+\.?\d*) \s* # r1 number
34
- \)
35
- \s* # space between coordinates is ok
36
- \( \s* # coordinate 2
37
- (\d+\.?\d*) \s* # x2 number
38
- ,\s* # whitespace after comma is ok
39
- (\d+\.?\d*) \s* # y2 number
40
- ,\s* # whitespace after comma is ok
41
- (\d+\.?\d*) \s* # r2 number
42
- \)
43
- (#{STOPS})+ # stops
44
- \s* # trailing whitespace is ok
45
- /x
46
- end
47
- end
1
+ module Squib
2
+ # @api private
3
+ module Graphics
4
+ STOPS = / # used to capture the stops
5
+ \s* # leading whitespace is ok
6
+ (\#?[\w]+) # color
7
+ @ # no spaces here
8
+ (\d+\.?\d*) # offset number
9
+ /x
10
+
11
+ LINEAR_GRADIENT = /
12
+ \( \s* # coordinate 1
13
+ (\d+\.?\d*) \s* # x1 number
14
+ ,\s* # whitespace after comma is ok
15
+ (\d+\.?\d*) \s* # y1 number
16
+ \)
17
+ \s* # space between coordinates is ok
18
+ \( \s* # coordinate 2
19
+ (\d+\.?\d*) \s* # x2 number
20
+ ,\s* # whitespace after comma is ok
21
+ (\d+\.?\d*) \s* # y2 number
22
+ \)
23
+ (#{STOPS})+ # stops
24
+ \s* # trailing whitespace is ok
25
+ /x
26
+
27
+ RADIAL_GRADIENT = /
28
+ \( \s* # coordinate 1
29
+ (\d+\.?\d*) \s* # x1 number
30
+ ,\s* # whitespace after comma is ok
31
+ (\d+\.?\d*) \s* # y1 number
32
+ ,\s* # whitespace after comma is ok
33
+ (\d+\.?\d*) \s* # r1 number
34
+ \)
35
+ \s* # space between coordinates is ok
36
+ \( \s* # coordinate 2
37
+ (\d+\.?\d*) \s* # x2 number
38
+ ,\s* # whitespace after comma is ok
39
+ (\d+\.?\d*) \s* # y2 number
40
+ ,\s* # whitespace after comma is ok
41
+ (\d+\.?\d*) \s* # r2 number
42
+ \)
43
+ (#{STOPS})+ # stops
44
+ \s* # trailing whitespace is ok
45
+ /x
46
+ end
47
+ end
@@ -1,42 +1,42 @@
1
- require_relative 'cairo_context_wrapper'
2
-
3
- module Squib
4
- class Deck
5
-
6
- # Draw cards in a fan.
7
- # @api private
8
- def render_hand(range, sheet, hand)
9
- cards = range.collect { |i| @cards[i] }
10
- center_x = width / 2.0
11
- center_y = hand.radius + height
12
- out_size = 3.0 * center_y
13
- angle_delta = (hand.angle_range.last - hand.angle_range.first) / cards.size
14
- cxt = Cairo::Context.new(Cairo::RecordingSurface.new(0, 0, out_size, out_size))
15
- cxt.translate(out_size / 2.0, out_size / 2.0)
16
- cxt.rotate(hand.angle_range.first)
17
- cxt.translate(-width, -width)
18
- cards.each_with_index do |card, i|
19
- cxt.translate(center_x, center_y)
20
- cxt.rotate(angle_delta)
21
- cxt.translate(-center_x, -center_y)
22
- card.use_cairo do |card_cxt|
23
- cxt.rounded_rectangle(sheet.trim, sheet.trim,
24
- width - (2 * sheet.trim), height - (2 * sheet.trim),
25
- sheet.trim_radius, sheet.trim_radius)
26
- cxt.clip
27
- cxt.set_source(card_cxt.target)
28
- cxt.paint
29
- cxt.reset_clip
30
- end
31
- end
32
- x, y, w, h = cxt.target.ink_extents # I love Ruby assignment ;)
33
- png_cxt = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(Cairo::ImageSurface.new(w + 2 * sheet.margin, h + 2 * sheet.margin)))
34
- png_cxt.set_source_squibcolor(sheet.fill_color)
35
- png_cxt.paint
36
- png_cxt.translate(-x + sheet.margin, -y + sheet.margin)
37
- png_cxt.set_source(cxt.target)
38
- png_cxt.paint
39
- png_cxt.target.write_to_png sheet.full_filename
40
- end
41
- end
42
- end
1
+ require_relative 'cairo_context_wrapper'
2
+
3
+ module Squib
4
+ class Deck
5
+
6
+ # Draw cards in a fan.
7
+ # @api private
8
+ def render_hand(range, sheet, hand)
9
+ cards = range.collect { |i| @cards[i] }
10
+ center_x = width / 2.0
11
+ center_y = hand.radius + height
12
+ out_size = 3.0 * center_y
13
+ angle_delta = (hand.angle_range.last - hand.angle_range.first) / cards.size
14
+ cxt = Cairo::Context.new(Cairo::RecordingSurface.new(0, 0, out_size, out_size))
15
+ cxt.translate(out_size / 2.0, out_size / 2.0)
16
+ cxt.rotate(hand.angle_range.first)
17
+ cxt.translate(-width, -width)
18
+ cards.each_with_index do |card, i|
19
+ cxt.translate(center_x, center_y)
20
+ cxt.rotate(angle_delta)
21
+ cxt.translate(-center_x, -center_y)
22
+ card.use_cairo do |card_cxt|
23
+ cxt.rounded_rectangle(sheet.trim, sheet.trim,
24
+ width - (2 * sheet.trim), height - (2 * sheet.trim),
25
+ sheet.trim_radius, sheet.trim_radius)
26
+ cxt.clip
27
+ cxt.set_source(card_cxt.target)
28
+ cxt.paint
29
+ cxt.reset_clip
30
+ end
31
+ end
32
+ x, y, w, h = cxt.target.ink_extents # I love Ruby assignment ;)
33
+ png_cxt = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(Cairo::ImageSurface.new(w + 2 * sheet.margin, h + 2 * sheet.margin)))
34
+ png_cxt.set_source_squibcolor(sheet.fill_color)
35
+ png_cxt.paint
36
+ png_cxt.translate(-x + sheet.margin, -y + sheet.margin)
37
+ png_cxt.set_source(cxt.target)
38
+ png_cxt.paint
39
+ png_cxt.target.write_to_png sheet.full_filename
40
+ end
41
+ end
42
+ end
@@ -1,123 +1,123 @@
1
- module Squib
2
-
3
- # Cache all pngs we've already loaded
4
- #
5
- # :nodoc:
6
- # @api private
7
- def cache_load_image(file)
8
- @img_cache ||= {}
9
- @img_cache[file] ||= open_png file
10
- end
11
- module_function :cache_load_image
12
-
13
- # Open a PNG file, checking magic bytes if it's a real PNG
14
- # Magic bytes taken from:
15
- # https://en.wikipedia.org/wiki/List_of_file_signatures
16
- # :nodoc:
17
- # @api private
18
- PNG_MAGIC = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
19
- def open_png(file)
20
- if PNG_MAGIC == File.read(file, 8).bytes
21
- return Cairo::ImageSurface.from_png(file)
22
- else
23
- raise ArgumentError.new("ERROR: #{file} is not a PNG file")
24
- end
25
- end
26
- module_function :open_png
27
-
28
- class Card
29
-
30
- # :nodoc:
31
- # @api private
32
- def png(file, box, paint, trans)
33
- Squib.logger.debug {"RENDERING PNG: \n file: #{file}\n box: #{box}\n paint: #{paint}\n trans: #{trans}"}
34
- return if file.nil? or file.eql? ''
35
- png = Squib.cache_load_image(file)
36
- use_cairo do |cc|
37
- cc.translate(box.x, box.y)
38
- box.width = png.width.to_f if box.width == :native
39
- box.height = png.height.to_f if box.height == :native
40
- box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
41
- box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
42
-
43
- scale_width = box.width.to_f / png.width.to_f
44
- scale_height = box.height.to_f / png.height.to_f
45
- warn_png_scale(file, scale_width, scale_height)
46
- cc.scale(scale_width, scale_height)
47
-
48
- cc.rotate(trans.angle)
49
- cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
50
- cc.translate(-box.x, -box.y)
51
-
52
- trans.crop_width = png.width.to_f if trans.crop_width == :native
53
- trans.crop_height = png.height.to_f if trans.crop_height == :native
54
- cc.rounded_rectangle(box.x, box.y, trans.crop_width, trans.crop_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
55
- cc.clip
56
- cc.translate(-trans.crop_x, -trans.crop_y)
57
-
58
-
59
- cc.set_source(png, box.x, box.y)
60
- cc.operator = paint.blend unless paint.blend == :none
61
- if paint.mask.empty?
62
- cc.paint(paint.alpha)
63
- else
64
- cc.set_source_squibcolor(paint.mask)
65
- cc.mask(png, box.x, box.y)
66
- end
67
- end
68
- end
69
-
70
- # :nodoc:
71
- # @api private
72
- def warn_png_scale(file, scale_width, scale_height)
73
- if @deck.conf.warn_png_scale? && (scale_width > 1.0 || scale_height > 1.0)
74
- Squib.logger.warn "PNG is being upscaled - antialiasing could result: #{file}"
75
- end
76
- end
77
-
78
- # :nodoc:
79
- # @api private
80
- def svg(file, svg_args, box, paint, trans)
81
- Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
82
- Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
83
- return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
84
- svg_args.data = File.read(file) if svg_args.data.to_s.empty?
85
- begin
86
- svg = Rsvg::Handle.new_from_data(svg_args.data)
87
- rescue Rsvg::Error::Failed
88
- Squib.logger.error "Invalid SVG data. Is '#{file}' a valid svg file?"
89
- return
90
- end
91
- box.width = svg.width if box.width == :native
92
- box.height = svg.height if box.height == :native
93
- box.width = svg.width.to_f * box.height.to_f / svg.height.to_f if box.width == :scale
94
- box.height = svg.height.to_f * box.width.to_f / svg.width.to_f if box.height == :scale
95
- scale_width = box.width.to_f / svg.width.to_f
96
- scale_height = box.height.to_f / svg.height.to_f
97
- use_cairo do |cc|
98
- cc.translate(box.x, box.y)
99
- cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
100
- cc.rotate(trans.angle)
101
- cc.scale(scale_width, scale_height)
102
-
103
- trans.crop_width = box.width if trans.crop_width == :native
104
- trans.crop_height = box.height if trans.crop_height == :native
105
- cc.rounded_rectangle(0, 0, trans.crop_width / scale_width, trans.crop_height / scale_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
106
- cc.clip
107
- cc.translate(-trans.crop_x, -trans.crop_y)
108
-
109
- cc.operator = paint.blend unless paint.blend == :none
110
- if paint.mask.to_s.empty?
111
- cc.render_rsvg_handle(svg, id: svg_args.id)
112
- else
113
- tmp = Cairo::ImageSurface.new(box.width / scale_width, box.height / scale_height)
114
- tmp_cc = Cairo::Context.new(tmp)
115
- tmp_cc.render_rsvg_handle(svg, id: svg_args.id)
116
- cc.set_source_squibcolor(paint.mask)
117
- cc.mask(tmp, 0, 0)
118
- end
119
- end
120
- end
121
-
122
- end
123
- end
1
+ module Squib
2
+
3
+ # Cache all pngs we've already loaded
4
+ #
5
+ # :nodoc:
6
+ # @api private
7
+ def cache_load_image(file)
8
+ @img_cache ||= {}
9
+ @img_cache[file] ||= open_png file
10
+ end
11
+ module_function :cache_load_image
12
+
13
+ # Open a PNG file, checking magic bytes if it's a real PNG
14
+ # Magic bytes taken from:
15
+ # https://en.wikipedia.org/wiki/List_of_file_signatures
16
+ # :nodoc:
17
+ # @api private
18
+ PNG_MAGIC = [0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A]
19
+ def open_png(file)
20
+ if PNG_MAGIC == File.read(file, 8).bytes
21
+ return Cairo::ImageSurface.from_png(file)
22
+ else
23
+ raise ArgumentError.new("ERROR: #{file} is not a PNG file")
24
+ end
25
+ end
26
+ module_function :open_png
27
+
28
+ class Card
29
+
30
+ # :nodoc:
31
+ # @api private
32
+ def png(file, box, paint, trans)
33
+ Squib.logger.debug {"RENDERING PNG: \n file: #{file}\n box: #{box}\n paint: #{paint}\n trans: #{trans}"}
34
+ return if file.nil? or file.eql? ''
35
+ png = Squib.cache_load_image(file)
36
+ use_cairo do |cc|
37
+ cc.translate(box.x, box.y)
38
+ box.width = png.width.to_f if box.width == :native
39
+ box.height = png.height.to_f if box.height == :native
40
+ box.width = png.width.to_f * box.height.to_f / png.height.to_f if box.width == :scale
41
+ box.height = png.height.to_f * box.width.to_f / png.width.to_f if box.height == :scale
42
+
43
+ scale_width = box.width.to_f / png.width.to_f
44
+ scale_height = box.height.to_f / png.height.to_f
45
+ warn_png_scale(file, scale_width, scale_height)
46
+ cc.scale(scale_width, scale_height)
47
+
48
+ cc.rotate(trans.angle)
49
+ cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
50
+ cc.translate(-box.x, -box.y)
51
+
52
+ trans.crop_width = png.width.to_f if trans.crop_width == :native
53
+ trans.crop_height = png.height.to_f if trans.crop_height == :native
54
+ cc.rounded_rectangle(box.x, box.y, trans.crop_width, trans.crop_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
55
+ cc.clip
56
+ cc.translate(-trans.crop_x, -trans.crop_y)
57
+
58
+
59
+ cc.set_source(png, box.x, box.y)
60
+ cc.operator = paint.blend unless paint.blend == :none
61
+ if paint.mask.empty?
62
+ cc.paint(paint.alpha)
63
+ else
64
+ cc.set_source_squibcolor(paint.mask)
65
+ cc.mask(png, box.x, box.y)
66
+ end
67
+ end
68
+ end
69
+
70
+ # :nodoc:
71
+ # @api private
72
+ def warn_png_scale(file, scale_width, scale_height)
73
+ if @deck.conf.warn_png_scale? && (scale_width > 1.0 || scale_height > 1.0)
74
+ Squib.logger.warn "PNG is being upscaled - antialiasing could result: #{file}"
75
+ end
76
+ end
77
+
78
+ # :nodoc:
79
+ # @api private
80
+ def svg(file, svg_args, box, paint, trans)
81
+ Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
82
+ Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? || svg_args.data.to_s.empty?
83
+ return if (file.nil? or file.eql? '') and svg_args.data.nil? # nothing specified TODO Move this out to arg validator
84
+ svg_args.data = File.read(file) if svg_args.data.to_s.empty?
85
+ begin
86
+ svg = Rsvg::Handle.new_from_data(svg_args.data)
87
+ rescue Rsvg::Error::Failed
88
+ Squib.logger.error "Invalid SVG data. Is '#{file}' a valid svg file?"
89
+ return
90
+ end
91
+ box.width = svg.width if box.width == :native
92
+ box.height = svg.height if box.height == :native
93
+ box.width = svg.width.to_f * box.height.to_f / svg.height.to_f if box.width == :scale
94
+ box.height = svg.height.to_f * box.width.to_f / svg.width.to_f if box.height == :scale
95
+ scale_width = box.width.to_f / svg.width.to_f
96
+ scale_height = box.height.to_f / svg.height.to_f
97
+ use_cairo do |cc|
98
+ cc.translate(box.x, box.y)
99
+ cc.flip(trans.flip_vertical, trans.flip_horizontal, box.width / 2, box.height / 2)
100
+ cc.rotate(trans.angle)
101
+ cc.scale(scale_width, scale_height)
102
+
103
+ trans.crop_width = box.width if trans.crop_width == :native
104
+ trans.crop_height = box.height if trans.crop_height == :native
105
+ cc.rounded_rectangle(0, 0, trans.crop_width / scale_width, trans.crop_height / scale_height, trans.crop_corner_x_radius, trans.crop_corner_y_radius)
106
+ cc.clip
107
+ cc.translate(-trans.crop_x, -trans.crop_y)
108
+
109
+ cc.operator = paint.blend unless paint.blend == :none
110
+ if paint.mask.to_s.empty?
111
+ cc.render_rsvg_handle(svg, id: svg_args.id)
112
+ else
113
+ tmp = Cairo::ImageSurface.new(box.width / scale_width, box.height / scale_height)
114
+ tmp_cc = Cairo::Context.new(tmp)
115
+ tmp_cc.render_rsvg_handle(svg, id: svg_args.id)
116
+ cc.set_source_squibcolor(paint.mask)
117
+ cc.mask(tmp, 0, 0)
118
+ end
119
+ end
120
+ end
121
+
122
+ end
123
+ end