squib 0.5.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (125) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rspec +1 -0
  4. data/CHANGELOG.md +62 -14
  5. data/Gemfile +1 -1
  6. data/README.md +16 -10
  7. data/RELEASE TODO.md +2 -1
  8. data/Rakefile +11 -0
  9. data/lib/squib/api/data.rb +3 -3
  10. data/lib/squib/api/image.rb +7 -5
  11. data/lib/squib/api/save.rb +31 -2
  12. data/lib/squib/api/settings.rb +1 -1
  13. data/lib/squib/api/shapes.rb +86 -0
  14. data/lib/squib/api/text.rb +6 -2
  15. data/lib/squib/api/text_embed.rb +7 -5
  16. data/lib/squib/args/typographer.rb +22 -16
  17. data/lib/squib/card.rb +5 -5
  18. data/lib/squib/conf.rb +111 -0
  19. data/lib/squib/constants.rb +11 -31
  20. data/lib/squib/deck.rb +15 -48
  21. data/lib/squib/graphics/cairo_context_wrapper.rb +3 -1
  22. data/lib/squib/graphics/gradient_regex.rb +1 -0
  23. data/lib/squib/graphics/hand.rb +43 -0
  24. data/lib/squib/graphics/image.rb +5 -3
  25. data/lib/squib/graphics/save_doc.rb +1 -1
  26. data/lib/squib/graphics/shapes.rb +74 -0
  27. data/lib/squib/graphics/showcase.rb +5 -2
  28. data/lib/squib/graphics/text.rb +18 -7
  29. data/lib/squib/input_helpers.rb +2 -2
  30. data/lib/squib/version.rb +1 -1
  31. data/samples/csv_import.rb +3 -5
  32. data/samples/custom-config.yml +0 -1
  33. data/samples/custom_config.rb +1 -1
  34. data/samples/draw_shapes.rb +11 -0
  35. data/samples/embed_text.rb +1 -1
  36. data/samples/hand.rb +24 -0
  37. data/samples/load_images.rb +14 -0
  38. data/samples/sample.csv +3 -3
  39. data/samples/text_options.rb +4 -0
  40. data/spec/api/api_data_spec.rb +19 -0
  41. data/spec/api/api_image_spec.rb +1 -1
  42. data/spec/api/api_settings_spec.rb +18 -0
  43. data/spec/api/api_text_spec.rb +3 -3
  44. data/spec/conf_spec.rb +29 -0
  45. data/spec/data/conf/basic.yml +0 -0
  46. data/spec/data/conf/empty.yml +1 -0
  47. data/spec/data/samples/csv_import.rb.txt +4 -38
  48. data/spec/data/samples/custom_config.rb.txt +4 -0
  49. data/spec/data/samples/draw_shapes.rb.txt +57 -0
  50. data/spec/data/samples/hand.rb.txt +514 -0
  51. data/spec/data/samples/load_images.rb.txt +6 -0
  52. data/spec/data/samples/text_options.rb.txt +81 -0
  53. data/spec/data/xlsx/basic.xlsx +0 -0
  54. data/spec/data/xlsx/formulas.xlsx +0 -0
  55. data/spec/graphics/graphics_images_spec.rb +49 -48
  56. data/spec/graphics/graphics_shapes_spec.rb +43 -41
  57. data/spec/graphics/graphics_text_spec.rb +25 -11
  58. data/spec/samples/_diffs/gitkeep.txt +0 -0
  59. data/spec/samples/expected/autoscale_00.png +0 -0
  60. data/spec/samples/expected/autoscale_01.png +0 -0
  61. data/spec/samples/expected/autoscale_02.png +0 -0
  62. data/spec/samples/expected/backend_00.png +0 -0
  63. data/spec/samples/expected/backend_00.svg +78 -0
  64. data/spec/samples/expected/backend_01.png +0 -0
  65. data/spec/samples/expected/backend_01.svg +78 -0
  66. data/spec/samples/expected/basic_00.png +0 -0
  67. data/spec/samples/expected/basic_01.png +0 -0
  68. data/spec/samples/expected/basic_02.png +0 -0
  69. data/spec/samples/expected/cairo_access_00.png +0 -0
  70. data/spec/samples/expected/cairo_access_01.png +0 -0
  71. data/spec/samples/expected/card_00.png +0 -0
  72. data/spec/samples/expected/card_01.png +0 -0
  73. data/spec/samples/expected/colors_00.png +0 -0
  74. data/spec/samples/expected/config_disable_text_00.png +0 -0
  75. data/spec/samples/expected/config_text_00.png +0 -0
  76. data/spec/samples/expected/custom-config_00.png +0 -0
  77. data/spec/samples/expected/embed_00.png +0 -0
  78. data/spec/samples/expected/embed_multi_00.png +0 -0
  79. data/spec/samples/expected/embed_multi_01.png +0 -0
  80. data/spec/samples/expected/embed_multi_02.png +0 -0
  81. data/spec/samples/expected/embed_multisheet_00.png +0 -0
  82. data/spec/samples/expected/gitkeep.txt +0 -0
  83. data/spec/samples/expected/gradient_00.png +0 -0
  84. data/spec/samples/expected/hand.png +0 -0
  85. data/spec/samples/expected/hand_pretty.png +0 -0
  86. data/spec/samples/expected/landscape_00.png +0 -0
  87. data/spec/samples/expected/layout2_00.png +0 -0
  88. data/spec/samples/expected/layout_00.png +0 -0
  89. data/spec/samples/expected/layout_builtin_hand_00.png +0 -0
  90. data/spec/samples/expected/layout_builtin_playing_card_00.png +0 -0
  91. data/spec/samples/expected/load_images_00.png +0 -0
  92. data/spec/samples/expected/portrait_00.png +0 -0
  93. data/spec/samples/expected/ranges_00.png +0 -0
  94. data/spec/samples/expected/ranges_01.png +0 -0
  95. data/spec/samples/expected/ranges_02.png +0 -0
  96. data/spec/samples/expected/sample_csv_00.png +0 -0
  97. data/spec/samples/expected/sample_csv_01.png +0 -0
  98. data/spec/samples/expected/sample_excel_00.png +0 -0
  99. data/spec/samples/expected/sample_excel_01.png +0 -0
  100. data/spec/samples/expected/sample_excel_02.png +0 -0
  101. data/spec/samples/expected/save_sheet_00.png +0 -0
  102. data/spec/samples/expected/save_sheet_01.png +0 -0
  103. data/spec/samples/expected/save_sheet_range_00.png +0 -0
  104. data/spec/samples/expected/save_sheet_range_01.png +0 -0
  105. data/spec/samples/expected/save_single_sheet_00.png +0 -0
  106. data/spec/samples/expected/saves_notrim_01.png +0 -0
  107. data/spec/samples/expected/shape_00.png +0 -0
  108. data/spec/samples/expected/showcase.png +0 -0
  109. data/spec/samples/expected/showcase2.png +0 -0
  110. data/spec/samples/expected/showcase_individual_00.png +0 -0
  111. data/spec/samples/expected/showcase_individual_01.png +0 -0
  112. data/spec/samples/expected/showcase_individual_02.png +0 -0
  113. data/spec/samples/expected/showcase_individual_03.png +0 -0
  114. data/spec/samples/expected/text_00.png +0 -0
  115. data/spec/samples/expected/text_01.png +0 -0
  116. data/spec/samples/expected/text_02.png +0 -0
  117. data/spec/samples/expected/tgc_sample_00.png +0 -0
  118. data/spec/samples/expected/units_00.png +0 -0
  119. data/spec/samples/samples_regression_spec.rb +7 -5
  120. data/spec/samples/sanity.html.erb +28 -0
  121. data/spec/samples/sanity.rb +70 -0
  122. data/spec/spec_helper.rb +15 -1
  123. data/squib.gemspec +3 -1
  124. data/squib.sublime-project +27 -40
  125. metadata +177 -4
@@ -4,6 +4,7 @@ require 'squib/graphics/gradient_regex'
4
4
  module Squib
5
5
  module Graphics
6
6
  # Wrapper class for the Cairo context. Private.
7
+ # @api private
7
8
  class CairoContextWrapper
8
9
  extend Forwardable
9
10
 
@@ -22,7 +23,8 @@ module Squib
22
23
  :show_pango_layout, :rounded_rectangle, :set_line_width, :stroke, :fill,
23
24
  :set_source, :scale, :render_rsvg_handle, :circle, :triangle, :line_to,
24
25
  :operator=, :show_page, :clip, :transform, :mask, :create_pango_layout,
25
- :antialias=, :curve_to, :matrix, :matrix=, :identity_matrix
26
+ :antialias=, :curve_to, :matrix, :matrix=, :identity_matrix, :pango_layout_path,
27
+ :stroke_preserve, :target, :new_path, :fill_preserve, :close_path
26
28
 
27
29
  # :nodoc:
28
30
  # @api private
@@ -1,4 +1,5 @@
1
1
  module Squib
2
+ # @api private
2
3
  module Graphics
3
4
  STOPS = / # used to capture the stops
4
5
  \s* # leading whitespace is ok
@@ -0,0 +1,43 @@
1
+ require 'squib/graphics/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, radius, angle_range, trim, trim_radius, margin,
9
+ fill_color, dir, file)
10
+ cards = range.collect { |i| @cards[i] }
11
+ center_x = width / 2.0
12
+ center_y = radius + height
13
+ out_size = 3.0 * center_y
14
+ angle_delta = (angle_range.last - angle_range.first) / cards.size
15
+ cxt = Cairo::Context.new(Cairo::RecordingSurface.new(0, 0, out_size, out_size))
16
+ cxt.translate(out_size / 2.0, out_size / 2.0)
17
+ cxt.rotate(angle_range.first)
18
+ cxt.translate(-width, -width)
19
+ cards.each_with_index do |card, i|
20
+ cxt.translate(center_x, center_y)
21
+ cxt.rotate(angle_delta)
22
+ cxt.translate(-center_x, -center_y)
23
+ card.use_cairo do |card_cxt|
24
+ cxt.rounded_rectangle(trim, trim,
25
+ width - (2 * trim), height - (2 * trim),
26
+ trim_radius, trim_radius)
27
+ cxt.clip
28
+ cxt.set_source(card_cxt.target)
29
+ cxt.paint
30
+ cxt.reset_clip
31
+ end
32
+ end
33
+ x, y, w, h = cxt.target.ink_extents # I love Ruby assignment ;)
34
+ png_cxt = Squib::Graphics::CairoContextWrapper.new(Cairo::Context.new(Cairo::ImageSurface.new(w + 2*margin, h + 2*margin)))
35
+ png_cxt.set_source_squibcolor(fill_color)
36
+ png_cxt.paint
37
+ png_cxt.translate(-x + margin, -y + margin)
38
+ png_cxt.set_source(cxt.target)
39
+ png_cxt.paint
40
+ png_cxt.target.write_to_png("#{dir}/#{file}")
41
+ end
42
+ end
43
+ end
@@ -41,10 +41,12 @@ module Squib
41
41
 
42
42
  # :nodoc:
43
43
  # @api private
44
- def svg(file, id, x, y, width, height, alpha, blend, angle, mask)
44
+ def svg(file, data, id, x, y, width, height, alpha, blend, angle, mask)
45
45
  Squib.logger.debug {"Rendering: #{file}, id: #{id} @#{x},#{y} #{width}x#{height}, alpha: #{alpha}, blend: #{blend}, angle: #{angle}, mask: #{mask}"}
46
- return if file.nil? or file.eql? ''
47
- svg = RSVG::Handle.new_from_file(file)
46
+ Squib.logger.warn 'Both an SVG file and SVG data were specified' unless file.to_s.empty? or data.to_s.empty?
47
+ return if (file.nil? or file.eql? '') and data.nil? # nothing specified
48
+ data = File.read(file) if data.to_s.empty?
49
+ svg = RSVG::Handle.new_from_data(data)
48
50
  width = svg.width if width == :native
49
51
  height = svg.height if height == :native
50
52
  scale_width = width.to_f / svg.width.to_f
@@ -33,7 +33,7 @@ module Squib
33
33
  cc.translate(x,y)
34
34
  cc.rectangle(p[:trim], p[:trim], card_width, card_height)
35
35
  cc.clip
36
- case card.backend
36
+ case card.backend.downcase.to_sym
37
37
  when :memory
38
38
  cc.set_source(card.cairo_surface, 0, 0)
39
39
  cc.paint
@@ -1,4 +1,5 @@
1
1
  module Squib
2
+ # @api private
2
3
  class Card
3
4
 
4
5
  # :nodoc:
@@ -32,6 +33,34 @@ module Squib
32
33
  end
33
34
  end
34
35
 
36
+ # Ellipse drawing taken from looking at the control points in Inkscape
37
+ # Think of it like a rectangle. Curves go from mid-points of the sides
38
+ # of the rectangle. Control points are at 1/4 and 3/4 of the side.
39
+ # :nodoc:
40
+ # @api private
41
+ def ellipse(x, y, w, h, fill_color, stroke_color, stroke_width)
42
+ use_cairo do |cc|
43
+ cc.move_to(x, y + 0.5*h) # start west
44
+ cc.curve_to(x, y + 0.25*h, # west to north
45
+ x + 0.25*w, y,
46
+ x + 0.5*w, y)
47
+ cc.curve_to(x + 0.75*w, y, # north to east
48
+ x + w, y + 0.25*h,
49
+ x + w, y + 0.5*h)
50
+ cc.curve_to(x + w, y + 0.75*h, # east to south
51
+ x + 0.75*w, y + h,
52
+ x + 0.5*w, y + h)
53
+ cc.curve_to(x + 0.25*w, y + h, # south to west
54
+ x, y + 0.75*h,
55
+ x, y + 0.5*h)
56
+ cc.set_source_squibcolor(stroke_color)
57
+ cc.set_line_width(stroke_width)
58
+ cc.stroke_preserve
59
+ cc.set_source_squibcolor(fill_color)
60
+ cc.fill
61
+ end
62
+ end
63
+
35
64
  # :nodoc:
36
65
  # @api private
37
66
  def triangle(x1, y1, x2, y2, x3, y3, fill_color, stroke_color, stroke_width)
@@ -74,5 +103,50 @@ module Squib
74
103
  end
75
104
  end
76
105
 
106
+ # :nodoc:
107
+ # @api private
108
+ def star(x, y, n, angle, inner_radius, outer_radius, fill_color, stroke_color, stroke_width)
109
+ use_cairo do |cc|
110
+ cc.translate(x, y)
111
+ cc.rotate(angle)
112
+ cc.translate(-x, -y)
113
+ cc.move_to(x + outer_radius, y) #i = 0, so cos(0)=1 and sin(0)=0
114
+ theta = Math::PI / n.to_f # i.e. (2*pi) / (2*n)
115
+ 0.upto(2 * n) do |i|
116
+ radius = i.even? ? outer_radius : inner_radius
117
+ cc.line_to(x + radius * Math::cos(i * theta),
118
+ y + radius * Math::sin(i * theta))
119
+ end
120
+ cc.close_path
121
+ cc.set_source_squibcolor(stroke_color)
122
+ cc.set_line_width(stroke_width)
123
+ cc.fill_preserve
124
+ cc.set_source_squibcolor(fill_color)
125
+ cc.stroke
126
+ end
127
+ end
128
+
129
+ # :nodoc:
130
+ # @api private
131
+ def polygon(x, y, n, angle, radius, fill_color, stroke_color, stroke_width)
132
+ use_cairo do |cc|
133
+ cc.translate(x, y)
134
+ cc.rotate(angle)
135
+ cc.translate(-x, -y)
136
+ cc.move_to(x + radius, y) # i = 0, so cos(0)=1 and sin(0)=0
137
+ theta = (2 * Math::PI) / n.to_f
138
+ 0.upto(n) do |i|
139
+ cc.line_to(x + radius * Math::cos(i * theta),
140
+ y + radius * Math::sin(i * theta))
141
+ end
142
+ cc.close_path
143
+ cc.set_source_squibcolor(stroke_color)
144
+ cc.set_line_width(stroke_width)
145
+ cc.fill_preserve
146
+ cc.set_source_squibcolor(fill_color)
147
+ cc.stroke
148
+ end
149
+ end
150
+
77
151
  end
78
152
  end
@@ -1,3 +1,5 @@
1
+ require 'squib/graphics/cairo_context_wrapper'
2
+
1
3
  module Squib
2
4
  class Deck
3
5
 
@@ -16,8 +18,9 @@ module Squib
16
18
  out_width = range.size * ((@width - 2*trim) * scale * offset) + 2*margin
17
19
  out_height = reflect_offset + (1.0 + reflect_percent) * (@height - 2*trim) + 2*margin
18
20
  out_cc = Cairo::Context.new(Cairo::ImageSurface.new(out_width, out_height))
19
- out_cc.set_source_color(fill_color)
20
- out_cc.paint
21
+ wrapper = Squib::Graphics::CairoContextWrapper.new(out_cc)
22
+ wrapper.set_source_squibcolor(fill_color)
23
+ wrapper.paint
21
24
 
22
25
  cards = range.collect { |i| @cards[i] }
23
26
  cards.each_with_index do |card, i|
@@ -6,8 +6,8 @@ module Squib
6
6
 
7
7
  # :nodoc:
8
8
  # @api private
9
- def draw_text_hint(cc,x,y,layout, color,angle)
10
- color = @deck.text_hint if color.eql? 'off' and not @deck.text_hint.to_s.eql? 'off'
9
+ def draw_text_hint(cc, x, y, layout, color)
10
+ color = @deck.text_hint if color.to_s.eql? 'off' and not @deck.text_hint.to_s.eql? 'off'
11
11
  return if color.to_s.eql? 'off' or color.nil?
12
12
  # when w,h < 0, it was never set. extents[1] are ink extents
13
13
  w = layout.width / Pango::SCALE
@@ -78,7 +78,7 @@ module Squib
78
78
 
79
79
  def set_font_rendering_opts!(layout)
80
80
  font_options = Cairo::FontOptions.new
81
- font_options.antialias = ANTIALIAS_OPTS[(@deck.antialias.downcase)] || 'gray'
81
+ font_options.antialias = Conf::ANTIALIAS_OPTS[(@deck.antialias || 'gray').downcase]
82
82
  font_options.hint_metrics = 'on' # TODO make this configurable
83
83
  font_options.hint_style = 'full' # TODO make this configurable
84
84
  layout.context.font_options = font_options
@@ -127,7 +127,7 @@ module Squib
127
127
  while (key = next_embed(embed.rules.keys, clean_str)) != nil
128
128
  rule = embed.rules[key]
129
129
  spacing = rule[:width] * Pango::SCALE
130
- index = clean_str.index(key)
130
+ index = clean_str.index(key)
131
131
  index = clean_str[0..index].bytesize #convert to byte index (bug #57)
132
132
  str = str.sub(key, "<span size=\"#{ZERO_WIDTH_CHAR_SIZE}\">a<span letter_spacing=\"#{spacing.to_i}\">a</span>a</span>")
133
133
  layout.markup = str
@@ -143,12 +143,22 @@ module Squib
143
143
  return draw_calls
144
144
  end
145
145
 
146
+ def stroke_outline!(cc, layout, stroke_width, stroke_color)
147
+ if stroke_width > 0
148
+ cc.set_source_squibcolor(stroke_color)
149
+ cc.set_line_width(stroke_width)
150
+ cc.pango_layout_path(layout)
151
+ cc.stroke
152
+ end
153
+ end
154
+
146
155
  # :nodoc:
147
156
  # @api private
148
157
  def text(embed,str, font, font_size, color,
149
158
  x, y, width, height,
150
159
  markup, justify, wrap, ellipsize,
151
- spacing, align, valign, hint, angle)
160
+ spacing, align, valign, hint, angle,
161
+ stroke_color, stroke_width)
152
162
  Squib.logger.debug {"Placing '#{str}'' with font '#{font}' @ #{x}, #{y}, color: #{color}, angle: #{angle} etc."}
153
163
  extents = nil
154
164
  str = str.to_s
@@ -164,7 +174,7 @@ module Squib
164
174
  layout.font_description = font_desc
165
175
  layout.text = str
166
176
  if markup
167
- str = Args::Typographer.new(@deck.quote_chars).process(layout.text)
177
+ str = @deck.typographer.process(layout.text)
168
178
  layout.markup = str
169
179
  end
170
180
 
@@ -185,6 +195,7 @@ module Squib
185
195
 
186
196
  cc.update_pango_layout(layout)
187
197
  cc.show_pango_layout(layout)
198
+ stroke_outline!(cc, layout, stroke_width, stroke_color)
188
199
  begin
189
200
  embed_draws.each { |ed| ed[:draw].call(self, ed[:x], ed[:y] + vertical_start) }
190
201
  rescue Exception => e
@@ -194,7 +205,7 @@ module Squib
194
205
  puts "=================="
195
206
  raise e
196
207
  end
197
- draw_text_hint(cc, x, y, layout, hint, angle)
208
+ draw_text_hint(cc, x, y, layout, hint)
198
209
  extents = { width: layout.extents[1].width / Pango::SCALE,
199
210
  height: layout.extents[1].height / Pango::SCALE }
200
211
  end
@@ -130,8 +130,8 @@ module Squib
130
130
  def colorify(opts, nillable=false, key=:color)
131
131
  opts[key].each_with_index do |color, i|
132
132
  unless nillable && color.nil?
133
- if @custom_colors.key? color.to_s
134
- color = @custom_colors[color.to_s]
133
+ if custom_colors.key? color.to_s
134
+ color = custom_colors[color.to_s]
135
135
  end
136
136
  opts[key][i] = color
137
137
  end
@@ -6,5 +6,5 @@ module Squib
6
6
  # Most of the time this is in the alpha of the next release.
7
7
  # e.g. v0.0.5a is on its way to becoming v0.0.5
8
8
  #
9
- VERSION = '0.5.1'
9
+ VERSION = '0.6.0'
10
10
  end
@@ -5,15 +5,13 @@ Squib::Deck.new(cards: 2) do
5
5
 
6
6
  # Outputs a hash of arrays with the header names as keys
7
7
  data = csv file: 'sample.csv'
8
-
9
- text str: data['Name'], x: 250, y: 55, font: 'Arial 54'
8
+ text str: data['Type'], x: 250, y: 55, font: 'Arial 54'
10
9
  text str: data['Level'], x: 65, y: 65, font: 'Arial 72'
11
- text str: data['Description'], x: 65, y: 600, font: 'Arial 36'
10
+
11
+ save format: :png, prefix: 'sample_csv_'
12
12
 
13
13
  # You can also specify the sheet, starting at 0
14
14
  data = xlsx file: 'sample.xlsx', sheet: 2
15
-
16
- save format: :png, prefix: 'sample_csv_'
17
15
  end
18
16
 
19
17
  # CSV is also a Squib-module-level function, so this also works:
@@ -1,4 +1,3 @@
1
- dpi: 300
2
1
  progress_bars: true
3
2
  text_hint: '#FF0000'
4
3
  custom_colors:
@@ -4,7 +4,7 @@ Squib::Deck.new(config: 'custom-config.yml') do
4
4
  # Custom color defined in our config
5
5
  background color: :foo
6
6
 
7
- # Hints are turned on in the config file
7
+ # Hints can be turned on in the config file
8
8
  text str: 'The Title', x: 0, y: 78, width: 825,
9
9
  font: 'Arial 72', align: :center
10
10
 
@@ -1,6 +1,7 @@
1
1
  require 'squib'
2
2
 
3
3
  Squib::Deck.new do
4
+ background color: :white
4
5
  rect x: 300, y: 300, width: 400, height: 400,
5
6
  fill_color: :blue, stroke_color: :red, stroke_width: 50.0
6
7
 
@@ -20,5 +21,15 @@ Squib::Deck.new do
20
21
  stroke_width: 12.0, stroke_color: :cyan,
21
22
  fill_color: :burgundy
22
23
 
24
+ ellipse x: 50, y: 925, width: 200, height: 100,
25
+ stroke_width: 5.0, stroke_color: :cyan,
26
+ fill_color: :burgundy
27
+
28
+ star x: 300, y: 1000, n: 5, inner_radius: 10, outer_radius: 25,
29
+ fill_color: :burgundy, stroke_color: :cyan, stroke_width: 3
30
+
31
+ polygon x: 500, y: 1000, n: 5, radius: 25, angle: Math::PI / 2,
32
+ fill_color: :burgundy, stroke_color: :cyan, stroke_width: 2
33
+
23
34
  save_png prefix: 'shape_'
24
35
  end
@@ -61,7 +61,7 @@ Squib::Deck.new do
61
61
  text(str: embed_text, font: 'Sans 21',
62
62
  x: 400, y: 320, width: 180, height: 300,
63
63
  align: :center, ellipsize: false, justify: false, hint: :magenta) do |embed|
64
- embed.svg key: ':tool:', width: 28, height: 28, file: 'spanner.svg'
64
+ embed.svg key: ':tool:', width: 28, height: 28, data: File.read('spanner.svg')
65
65
  embed.svg key: ':health:', width: 28, height: 28, file: 'glass-heart.svg'
66
66
  embed.png key: ':purse:', width: 28, height: 28, file: 'shiny-purse.png'
67
67
  end
@@ -0,0 +1,24 @@
1
+ require 'squib'
2
+
3
+ Squib::Deck.new(cards: 8, layout: 'playing-card.yml') do
4
+ background color: :cyan
5
+ rect x: 37, y: 37, width: 750, height: 1050, fill_color: :black, radius: 25
6
+ rect x: 75, y: 75, width: 675, height: 975, fill_color: :white, radius: 20
7
+ text str: ('A'..'Z').to_a, layout: :bonus_ul, font: 'Sans bold 100'
8
+
9
+ # Defaults are sensible
10
+ hand #saves to _output/hand.png
11
+
12
+ # Here's a prettier version:
13
+ # - Each card is trimmed with rounded corners
14
+ # - Zero radius means cards rotate about the bottom of the card
15
+ # - Cards are shown in reverse order
16
+ hand trim: 37.5, trim_radius: 25,
17
+ radius: 0,
18
+ range: 7.downto(0),
19
+ file: 'hand_pretty.png'
20
+
21
+ # Tip: you can have the top card be on the left by reversing the range
22
+ # angle_range: (Math::PI / 4)..(Math::PI / -4)
23
+ end
24
+
@@ -17,6 +17,20 @@ Squib::Deck.new(width: 825, height: 1125, cards: 1) do
17
17
  # Squib prepends a #-sign if one is not specified
18
18
  svg file: 'spanner.svg', id: 'backdrop', x: 50, y: 450, width: 125, height: 125
19
19
 
20
+ # We can also load SVGs as a string of XML
21
+ svg data: File.read('spanner.svg'), x: 50, y: 600, width: 75, height: 75
22
+
23
+ # The svg data field works nicely with modifying the SVG XML on-the-fly.
24
+ # To run this one, do `gem install game_icons` and uncomment the following
25
+ #
26
+ # require 'game_icons'
27
+ # svg data: GameIcons.get('angler-fish').recolor(fg: '#ccc', bg: '#333').string,
28
+ # x: 150, y: 600, width: 75, height: 75
29
+ #
30
+ # More examples at https://github.com/andymeneely/game_icons
31
+ # (or `gem install game_icons`) to get & manipulate art from game-icons.net
32
+ # Nokogiri (already included in Squib) is also great for XML manipulation.
33
+
20
34
  # WARNING! If you choose to use the SVG ID, the x-y coordinates are still
21
35
  # relative to the SVG page. See this example in an SVG editor
22
36
  svg file: 'offset.svg', id: 'thing', x: 0, y: 0, width: 600, height: 600
@@ -1,3 +1,3 @@
1
- Type,"Quantity"
2
- Thief,2
3
- Mastermind,1
1
+ Type,"Level"
2
+ Thief,1
3
+ Mastermind,2
@@ -78,6 +78,10 @@ Squib::Deck.new(width: 825, height: 1125, cards: 3) do
78
78
  embed.svg key: ':health:', width: 28, height: 28, file: 'glass-heart.svg'
79
79
  end
80
80
 
81
+ text str: "Stroke n <span fgcolor=\"\#ff0000\">fill</span>",
82
+ color: :green, stroke_width: 2.0, stroke_color: :blue,
83
+ x: '1.8in', y: '3in', width: '0.85in', font: 'Sans Bold 26', markup: true
84
+
81
85
  text str: "<b>Markup</b> is <i>quite</i> <s>'easy'</s> <span fgcolor=\"\#ff0000\">awesome</span>. Can't beat those \"smart\" 'quotes', now with 10--20% more en-dashes --- and em-dashes --- with explicit ellipses too...",
82
86
  markup: true,
83
87
  x: 50, y: 1000,
@@ -28,4 +28,23 @@ describe Squib::Deck do
28
28
  })
29
29
  end
30
30
  end
31
+
32
+ context '#xlsx' do
33
+ it 'loads basic xlsx data' do
34
+ expect(Squib.xlsx(file: xlsx_file('basic.xlsx'))).to eq({
35
+ 'Name' => %w(Larry Curly Mo),
36
+ 'General Number' => %w(1 2 3), #general types always get loaded as strings with no conversion
37
+ 'Actual Number' => [4.0, 5.0, 6.0], #numbers get auto-converted to integers
38
+ })
39
+ end
40
+
41
+ it 'loads xlsx with formulas' do
42
+ expect(Squib.xlsx(file: xlsx_file('formulas.xlsx'))).to eq({
43
+ 'A' => %w(1 2),
44
+ 'B' => %w(3 4),
45
+ 'Sum' => %w(4 6),
46
+ })
47
+ end
48
+
49
+ end
31
50
  end