prawn 2.3.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (203) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/lib/prawn/document/bounding_box.rb +223 -143
  4. data/lib/prawn/document/column_box.rb +61 -26
  5. data/lib/prawn/document/internals.rb +25 -16
  6. data/lib/prawn/document/span.rb +21 -18
  7. data/lib/prawn/document.rb +273 -182
  8. data/lib/prawn/encoding.rb +2 -5
  9. data/lib/prawn/errors.rb +23 -34
  10. data/lib/prawn/font.rb +254 -139
  11. data/lib/prawn/font_metric_cache.rb +18 -16
  12. data/lib/prawn/fonts/afm.rb +99 -57
  13. data/lib/prawn/fonts/dfont.rb +7 -1
  14. data/lib/prawn/fonts/otf.rb +4 -1
  15. data/lib/prawn/fonts/to_unicode_cmap.rb +151 -0
  16. data/lib/prawn/fonts/ttc.rb +7 -2
  17. data/lib/prawn/fonts/ttf.rb +345 -107
  18. data/lib/prawn/fonts.rb +14 -0
  19. data/lib/prawn/graphics/blend_mode.rb +25 -28
  20. data/lib/prawn/graphics/cap_style.rb +9 -12
  21. data/lib/prawn/graphics/color.rb +75 -50
  22. data/lib/prawn/graphics/dash.rb +45 -42
  23. data/lib/prawn/graphics/join_style.rb +18 -12
  24. data/lib/prawn/graphics/patterns.rb +239 -110
  25. data/lib/prawn/graphics/transformation.rb +51 -44
  26. data/lib/prawn/graphics/transparency.rb +16 -40
  27. data/lib/prawn/graphics.rb +370 -260
  28. data/lib/prawn/grid.rb +219 -57
  29. data/lib/prawn/image_handler.rb +27 -10
  30. data/lib/prawn/images/image.rb +8 -10
  31. data/lib/prawn/images/jpg.rb +46 -20
  32. data/lib/prawn/images/png.rb +94 -42
  33. data/lib/prawn/images.rb +70 -81
  34. data/lib/prawn/measurement_extensions.rb +39 -8
  35. data/lib/prawn/measurements.rb +60 -5
  36. data/lib/prawn/outline.rb +120 -113
  37. data/lib/prawn/repeater.rb +52 -36
  38. data/lib/prawn/security/arcfour.rb +4 -4
  39. data/lib/prawn/security.rb +106 -98
  40. data/lib/prawn/soft_mask.rb +42 -30
  41. data/lib/prawn/stamp.rb +38 -42
  42. data/lib/prawn/text/box.rb +156 -105
  43. data/lib/prawn/text/formatted/arranger.rb +121 -41
  44. data/lib/prawn/text/formatted/box.rb +239 -163
  45. data/lib/prawn/text/formatted/fragment.rb +130 -14
  46. data/lib/prawn/text/formatted/line_wrap.rb +49 -38
  47. data/lib/prawn/text/formatted/parser.rb +116 -74
  48. data/lib/prawn/text/formatted/wrap.rb +25 -26
  49. data/lib/prawn/text/formatted.rb +75 -0
  50. data/lib/prawn/text.rb +456 -211
  51. data/lib/prawn/transformation_stack.rb +29 -10
  52. data/lib/prawn/utilities.rb +13 -13
  53. data/lib/prawn/version.rb +2 -1
  54. data/lib/prawn/view.rb +69 -54
  55. data/lib/prawn.rb +24 -18
  56. data.tar.gz.sig +0 -0
  57. metadata +55 -262
  58. metadata.gz.sig +3 -4
  59. data/.yardopts +0 -10
  60. data/Gemfile +0 -5
  61. data/Rakefile +0 -54
  62. data/manual/absolute_position.pdf +0 -0
  63. data/manual/basic_concepts/adding_pages.rb +0 -26
  64. data/manual/basic_concepts/basic_concepts.rb +0 -43
  65. data/manual/basic_concepts/creation.rb +0 -38
  66. data/manual/basic_concepts/cursor.rb +0 -32
  67. data/manual/basic_concepts/measurement.rb +0 -24
  68. data/manual/basic_concepts/origin.rb +0 -37
  69. data/manual/basic_concepts/other_cursor_helpers.rb +0 -39
  70. data/manual/basic_concepts/view.rb +0 -48
  71. data/manual/bounding_box/bounding_box.rb +0 -41
  72. data/manual/bounding_box/bounds.rb +0 -48
  73. data/manual/bounding_box/canvas.rb +0 -23
  74. data/manual/bounding_box/creation.rb +0 -22
  75. data/manual/bounding_box/indentation.rb +0 -45
  76. data/manual/bounding_box/nesting.rb +0 -52
  77. data/manual/bounding_box/russian_boxes.rb +0 -40
  78. data/manual/bounding_box/stretchy.rb +0 -29
  79. data/manual/contents.rb +0 -35
  80. data/manual/cover.rb +0 -43
  81. data/manual/document_and_page_options/background.rb +0 -25
  82. data/manual/document_and_page_options/document_and_page_options.rb +0 -34
  83. data/manual/document_and_page_options/metadata.rb +0 -25
  84. data/manual/document_and_page_options/page_margins.rb +0 -36
  85. data/manual/document_and_page_options/page_size.rb +0 -34
  86. data/manual/document_and_page_options/print_scaling.rb +0 -22
  87. data/manual/example_helper.rb +0 -8
  88. data/manual/graphics/blend_mode.rb +0 -52
  89. data/manual/graphics/circle_and_ellipse.rb +0 -21
  90. data/manual/graphics/color.rb +0 -22
  91. data/manual/graphics/common_lines.rb +0 -29
  92. data/manual/graphics/fill_and_stroke.rb +0 -41
  93. data/manual/graphics/fill_rules.rb +0 -37
  94. data/manual/graphics/gradients.rb +0 -43
  95. data/manual/graphics/graphics.rb +0 -64
  96. data/manual/graphics/helper.rb +0 -27
  97. data/manual/graphics/line_width.rb +0 -36
  98. data/manual/graphics/lines_and_curves.rb +0 -40
  99. data/manual/graphics/polygon.rb +0 -27
  100. data/manual/graphics/rectangle.rb +0 -20
  101. data/manual/graphics/rotate.rb +0 -25
  102. data/manual/graphics/scale.rb +0 -42
  103. data/manual/graphics/soft_masks.rb +0 -44
  104. data/manual/graphics/stroke_cap.rb +0 -30
  105. data/manual/graphics/stroke_dash.rb +0 -47
  106. data/manual/graphics/stroke_join.rb +0 -29
  107. data/manual/graphics/translate.rb +0 -28
  108. data/manual/graphics/transparency.rb +0 -33
  109. data/manual/how_to_read_this_manual.rb +0 -39
  110. data/manual/images/absolute_position.rb +0 -22
  111. data/manual/images/fit.rb +0 -20
  112. data/manual/images/horizontal.rb +0 -24
  113. data/manual/images/images.rb +0 -41
  114. data/manual/images/plain_image.rb +0 -17
  115. data/manual/images/scale.rb +0 -21
  116. data/manual/images/vertical.rb +0 -27
  117. data/manual/images/width_and_height.rb +0 -24
  118. data/manual/layout/boxes.rb +0 -26
  119. data/manual/layout/content.rb +0 -24
  120. data/manual/layout/layout.rb +0 -27
  121. data/manual/layout/simple_grid.rb +0 -22
  122. data/manual/outline/add_subsection_to.rb +0 -60
  123. data/manual/outline/insert_section_after.rb +0 -46
  124. data/manual/outline/outline.rb +0 -33
  125. data/manual/outline/sections_and_pages.rb +0 -66
  126. data/manual/repeatable_content/alternate_page_numbering.rb +0 -36
  127. data/manual/repeatable_content/page_numbering.rb +0 -55
  128. data/manual/repeatable_content/repeatable_content.rb +0 -35
  129. data/manual/repeatable_content/repeater.rb +0 -54
  130. data/manual/repeatable_content/stamp.rb +0 -40
  131. data/manual/security/encryption.rb +0 -28
  132. data/manual/security/permissions.rb +0 -41
  133. data/manual/security/security.rb +0 -28
  134. data/manual/table.rb +0 -16
  135. data/manual/text/alignment.rb +0 -43
  136. data/manual/text/color.rb +0 -24
  137. data/manual/text/column_box.rb +0 -30
  138. data/manual/text/fallback_fonts.rb +0 -41
  139. data/manual/text/font.rb +0 -40
  140. data/manual/text/font_size.rb +0 -44
  141. data/manual/text/font_style.rb +0 -22
  142. data/manual/text/formatted_callbacks.rb +0 -65
  143. data/manual/text/formatted_text.rb +0 -58
  144. data/manual/text/free_flowing_text.rb +0 -50
  145. data/manual/text/inline.rb +0 -40
  146. data/manual/text/kerning_and_character_spacing.rb +0 -38
  147. data/manual/text/leading.rb +0 -24
  148. data/manual/text/line_wrapping.rb +0 -60
  149. data/manual/text/paragraph_indentation.rb +0 -32
  150. data/manual/text/positioned_text.rb +0 -37
  151. data/manual/text/registering_families.rb +0 -51
  152. data/manual/text/rendering_and_color.rb +0 -36
  153. data/manual/text/right_to_left_text.rb +0 -54
  154. data/manual/text/rotation.rb +0 -47
  155. data/manual/text/single_usage.rb +0 -36
  156. data/manual/text/text.rb +0 -75
  157. data/manual/text/text_box_excess.rb +0 -35
  158. data/manual/text/text_box_extensions.rb +0 -48
  159. data/manual/text/text_box_overflow.rb +0 -49
  160. data/manual/text/utf8.rb +0 -27
  161. data/manual/text/win_ansi_charset.rb +0 -62
  162. data/prawn.gemspec +0 -57
  163. data/spec/data/curves.pdf +0 -66
  164. data/spec/extensions/encoding_helpers.rb +0 -11
  165. data/spec/prawn/document/bounding_box_spec.rb +0 -546
  166. data/spec/prawn/document/column_box_spec.rb +0 -75
  167. data/spec/prawn/document/security_spec.rb +0 -176
  168. data/spec/prawn/document_annotations_spec.rb +0 -76
  169. data/spec/prawn/document_destinations_spec.rb +0 -15
  170. data/spec/prawn/document_grid_spec.rb +0 -99
  171. data/spec/prawn/document_reference_spec.rb +0 -27
  172. data/spec/prawn/document_span_spec.rb +0 -36
  173. data/spec/prawn/document_spec.rb +0 -802
  174. data/spec/prawn/font_metric_cache_spec.rb +0 -54
  175. data/spec/prawn/font_spec.rb +0 -542
  176. data/spec/prawn/graphics/blend_mode_spec.rb +0 -63
  177. data/spec/prawn/graphics/transparency_spec.rb +0 -81
  178. data/spec/prawn/graphics_spec.rb +0 -837
  179. data/spec/prawn/graphics_stroke_styles_spec.rb +0 -229
  180. data/spec/prawn/image_handler_spec.rb +0 -53
  181. data/spec/prawn/images/jpg_spec.rb +0 -20
  182. data/spec/prawn/images/png_spec.rb +0 -283
  183. data/spec/prawn/images_spec.rb +0 -224
  184. data/spec/prawn/measurements_extensions_spec.rb +0 -24
  185. data/spec/prawn/outline_spec.rb +0 -412
  186. data/spec/prawn/repeater_spec.rb +0 -165
  187. data/spec/prawn/soft_mask_spec.rb +0 -74
  188. data/spec/prawn/stamp_spec.rb +0 -172
  189. data/spec/prawn/text/box_spec.rb +0 -1112
  190. data/spec/prawn/text/formatted/arranger_spec.rb +0 -466
  191. data/spec/prawn/text/formatted/box_spec.rb +0 -846
  192. data/spec/prawn/text/formatted/fragment_spec.rb +0 -343
  193. data/spec/prawn/text/formatted/line_wrap_spec.rb +0 -494
  194. data/spec/prawn/text/formatted/parser_spec.rb +0 -697
  195. data/spec/prawn/text_draw_text_spec.rb +0 -149
  196. data/spec/prawn/text_rendering_mode_spec.rb +0 -48
  197. data/spec/prawn/text_spacing_spec.rb +0 -95
  198. data/spec/prawn/text_spec.rb +0 -603
  199. data/spec/prawn/text_with_inline_formatting_spec.rb +0 -35
  200. data/spec/prawn/transformation_stack_spec.rb +0 -66
  201. data/spec/prawn/view_spec.rb +0 -63
  202. data/spec/prawn_manual_spec.rb +0 -35
  203. data/spec/spec_helper.rb +0 -48
@@ -1,837 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'spec_helper'
4
-
5
- describe Prawn::Graphics do
6
- let(:pdf) { create_pdf }
7
-
8
- describe 'When drawing a line' do
9
- it 'draws a line from (100,600) to (100,500)' do
10
- pdf.line([100, 600], [100, 500])
11
-
12
- line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
13
-
14
- expect(line_drawing.points).to eq([[100, 600], [100, 500]])
15
- end
16
-
17
- it 'draws two lines at (100,600) to (100,500) and (75,100) to (50,125)' do
18
- pdf.line(100, 600, 100, 500)
19
- pdf.line(75, 100, 50, 125)
20
-
21
- line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
22
-
23
- expect(line_drawing.points).to eq(
24
- [[100.0, 600.0], [100.0, 500.0], [75.0, 100.0], [50.0, 125.0]]
25
- )
26
- end
27
-
28
- it 'properlies set line width via line_width=' do
29
- pdf.line_width = 10
30
- line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
31
- expect(line.widths.first).to eq(10)
32
- end
33
-
34
- it 'properlies set line width via line_width(width)' do
35
- pdf.line_width(10)
36
- line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
37
- expect(line.widths.first).to eq(10)
38
- end
39
-
40
- it 'carries the current line width settings over to new pages' do
41
- pdf.line_width(10)
42
- pdf.start_new_page
43
- line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
44
- expect(line.widths.length).to eq(2)
45
- expect(line.widths[1]).to eq(10)
46
- end
47
-
48
- describe '(Horizontally)' do
49
- it 'draws from [x1,pdf.y],[x2,pdf.y]' do
50
- pdf.horizontal_line(100, 150)
51
- line = PDF::Inspector::Graphics::Line.analyze(pdf.render)
52
- expect(line.points).to eq([
53
- [100.0 + pdf.bounds.absolute_left, pdf.y],
54
- [150.0 + pdf.bounds.absolute_left, pdf.y]
55
- ])
56
- end
57
-
58
- it 'draws a line from (200, 250) to (300, 250)' do
59
- pdf.horizontal_line(200, 300, at: 250)
60
- line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
61
- expect(line_drawing.points).to eq([[200, 250], [300, 250]])
62
- end
63
- end
64
-
65
- describe '(Vertically)' do
66
- it 'draws a line from (350, 300) to (350, 400)' do
67
- pdf.vertical_line(300, 400, at: 350)
68
- line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
69
- expect(line_drawing.points).to eq([[350, 300], [350, 400]])
70
- end
71
-
72
- it 'requires a y coordinate' do
73
- expect { pdf.vertical_line(400, 500) }
74
- .to raise_error(ArgumentError)
75
- end
76
- end
77
- end
78
-
79
- describe 'When drawing a polygon' do
80
- it 'draws each line passed to polygon()' do
81
- pdf.polygon([100, 500], [100, 400], [200, 400])
82
-
83
- line_drawing = PDF::Inspector::Graphics::Line.analyze(pdf.render)
84
- expect(line_drawing.points)
85
- .to eq([[100, 500], [100, 400], [200, 400], [100, 500]])
86
- end
87
- end
88
-
89
- describe 'When drawing a rectangle' do
90
- it 'uses a point, width, and height for coords' do
91
- pdf.rectangle [200, 200], 50, 100
92
-
93
- rectangles = PDF::Inspector::Graphics::Rectangle.analyze(pdf.render)
94
- .rectangles
95
- # PDF uses bottom left corner
96
- expect(rectangles[0][:point]).to eq([200, 100])
97
- expect(rectangles[0][:width]).to eq(50)
98
- expect(rectangles[0][:height]).to eq(100)
99
- end
100
- end
101
-
102
- describe 'When drawing a curve' do
103
- it 'draws a bezier curve from 50,50 to 100,100' do
104
- pdf.move_to [50, 50]
105
- pdf.curve_to [100, 100], bounds: [[20, 90], [90, 70]]
106
- curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
107
- expect(curve.coords)
108
- .to eq([50.0, 50.0, 20.0, 90.0, 90.0, 70.0, 100.0, 100.0])
109
- end
110
-
111
- it 'draws a bezier curve from 100,100 to 50,50' do
112
- pdf.curve [100, 100], [50, 50], bounds: [[20, 90], [90, 75]]
113
- curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
114
- expect(curve.coords)
115
- .to eq([100.0, 100.0, 20.0, 90.0, 90.0, 75.0, 50.0, 50.0])
116
- end
117
- end
118
-
119
- describe 'When drawing a rounded rectangle' do
120
- before { pdf.rounded_rectangle([50, 550], 50, 100, 10) }
121
-
122
- let(:original_point) do
123
- curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
124
- curve_points = curve.coords.each_slice(2).to_a
125
- curve_points.shift
126
- end
127
- let(:all_coords) do
128
- curve = PDF::Inspector::Graphics::Curve.analyze(pdf.render)
129
- curve_points = curve.coords.each_slice(2).to_a
130
- curve_points.shift
131
- curves = curve_points.each_slice(3).to_a
132
- line_points = PDF::Inspector::Graphics::Line.analyze(pdf.render).points
133
- line_points.shift
134
- line_points.zip(curves).flatten.each_slice(2).to_a.unshift original_point
135
- end
136
-
137
- it 'draws a rectangle by connecting lines with rounded bezier curves' do
138
- expect(all_coords).to eq(
139
- [
140
- [60.0, 550.0], [90.0, 550.0], [95.5228, 550.0], [100.0, 545.5228],
141
- [100.0, 540.0], [100.0, 460.0], [100.0, 454.4772], [95.5228, 450.0],
142
- [90.0, 450.0], [60.0, 450.0], [54.4772, 450.0], [50.0, 454.4772],
143
- [50.0, 460.0], [50.0, 540.0], [50.0, 545.5228], [54.4772, 550.0],
144
- [60.0, 550.0]
145
- ]
146
- )
147
- end
148
-
149
- it 'starts and end with the same point' do
150
- expect(original_point).to eq(all_coords.last)
151
- end
152
- end
153
-
154
- describe 'When drawing an ellipse' do
155
- let(:curve) do
156
- pdf.ellipse [100, 100], 25, 50
157
- PDF::Inspector::Graphics::Curve.analyze(pdf.render)
158
- end
159
-
160
- it 'uses a Bézier approximation' do
161
- expect(curve.coords).to eq([
162
- 125.0, 100.0,
163
-
164
- 125.0, 127.6142,
165
- 113.8071, 150,
166
- 100.0, 150.0,
167
-
168
- 86.1929, 150.0,
169
- 75.0, 127.6142,
170
- 75.0, 100.0,
171
-
172
- 75.0, 72.3858,
173
- 86.1929, 50.0,
174
- 100.0, 50.0,
175
-
176
- 113.8071, 50.0,
177
- 125.0, 72.3858,
178
- 125.0, 100.0,
179
-
180
- 100.0, 100.0
181
- ])
182
- end
183
-
184
- it 'moves the pointer to the center of the ellipse after drawing' do
185
- expect(curve.coords[-2..-1]).to eq([100, 100])
186
- end
187
- end
188
-
189
- describe 'When drawing a circle' do
190
- let(:curve) do
191
- pdf.circle [100, 100], 25
192
- pdf.ellipse [100, 100], 25, 25
193
- PDF::Inspector::Graphics::Curve.analyze(pdf.render)
194
- end
195
-
196
- it 'strokes the same path as the equivalent ellipse' do
197
- middle = curve.coords.length / 2
198
- expect(curve.coords[0...middle]).to eq(curve.coords[middle..-1])
199
- end
200
- end
201
-
202
- describe 'When filling' do
203
- it 'defaults to the f operator (nonzero winding number rule)' do
204
- allow(pdf.renderer).to receive(:add_content).with('f')
205
- pdf.fill
206
- expect(pdf.renderer).to have_received(:add_content).with('f')
207
- end
208
-
209
- it 'uses f* for :fill_rule => :even_odd' do
210
- allow(pdf.renderer).to receive(:add_content).with('f*')
211
- pdf.fill(fill_rule: :even_odd)
212
- expect(pdf.renderer).to have_received(:add_content).with('f*')
213
- end
214
-
215
- it 'uses b by default for fill_and_stroke (nonzero winding number)' do
216
- allow(pdf.renderer).to receive(:add_content).with('b')
217
- pdf.fill_and_stroke
218
- expect(pdf.renderer).to have_received(:add_content).with('b')
219
- end
220
-
221
- it 'uses b* for fill_and_stroke(:fill_rule => :even_odd)' do
222
- allow(pdf.renderer).to receive(:add_content).with('b*')
223
- pdf.fill_and_stroke(fill_rule: :even_odd)
224
- expect(pdf.renderer).to have_received(:add_content).with('b*')
225
- end
226
- end
227
-
228
- describe 'When setting colors' do
229
- it 'sets stroke colors' do
230
- pdf.stroke_color 'ffcccc'
231
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
232
- # 100% red, 80% green, 80% blue
233
- expect(colors.stroke_color).to eq([1.0, 0.8, 0.8])
234
- end
235
-
236
- it 'sets fill colors' do
237
- pdf.fill_color 'ccff00'
238
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
239
- # 80% red, 100% green, 0% blue
240
- expect(colors.fill_color).to eq([0.8, 1.0, 0])
241
- end
242
-
243
- it 'raises an error for a color with a leading #' do
244
- expect { pdf.fill_color '#ccff00' }.to raise_error(ArgumentError)
245
- end
246
-
247
- it 'raises an error for a color string that is not a hex' do
248
- expect { pdf.fill_color 'zcff00' }.to raise_error(ArgumentError)
249
- end
250
-
251
- it 'raises an error for a color string with invalid characters' do
252
- expect { pdf.fill_color 'f0f0f?' }.to raise_error(ArgumentError)
253
- end
254
-
255
- it 'resets the colors on each new page if they have been defined' do
256
- pdf.fill_color 'ccff00'
257
-
258
- pdf.start_new_page
259
- pdf.stroke_color 'ff00cc'
260
-
261
- pdf.start_new_page
262
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
263
- expect(colors.fill_color_count).to eq(3)
264
- expect(colors.stroke_color_count).to eq(2)
265
-
266
- expect(colors.fill_color).to eq([0.8, 1.0, 0.0])
267
- expect(colors.stroke_color).to eq([1.0, 0.0, 0.8])
268
- end
269
-
270
- it 'sets the color space when setting colors on new pages to please fussy '\
271
- 'readers' do
272
- pdf.stroke_color '000000'
273
- pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
274
- pdf.start_new_page
275
- pdf.stroke_color '000000'
276
- pdf.stroke { pdf.rectangle([10, 10], 10, 10) }
277
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
278
- expect(colors.stroke_color_space_count[:DeviceRGB]).to eq(2)
279
- end
280
- end
281
-
282
- describe 'Patterns' do
283
- describe 'linear gradients' do
284
- it 'creates a /Pattern resource' do
285
- pdf.fill_gradient(
286
- [0, pdf.bounds.height],
287
- [pdf.bounds.width, pdf.bounds.height],
288
- 'FF0000', '0000FF'
289
- )
290
-
291
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
292
- pattern = grad.patterns.values.first
293
-
294
- expect(pattern).to_not be_nil
295
- expect(pattern[:Shading][:ShadingType]).to eq(2)
296
- expect(pattern[:Shading][:Coords]).to eq([0, 0, pdf.bounds.width, 0])
297
- expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
298
- (x1 - x2).abs < 0.01
299
- end).to eq true
300
- expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
301
- (x1 - x2).abs < 0.01
302
- end).to eq true
303
- end
304
-
305
- it 'creates a unique ID for each pattern resource' do
306
- pdf.fill_gradient(
307
- [256, 512],
308
- [356, 512],
309
- 'ffffff', 'fe00ff'
310
- )
311
- pdf.fill_gradient(
312
- [256, 256],
313
- [356, 256],
314
- 'ffffff', '0000ff'
315
- )
316
-
317
- str = pdf.render
318
- pattern_ids = str.scan(/SP\h{40}\s+scn/)
319
- expect(pattern_ids.uniq.length).to eq 2
320
- end
321
-
322
- it 'fill_gradient should set fill color to the pattern' do
323
- pdf.fill_gradient(
324
- [0, pdf.bounds.height],
325
- [pdf.bounds.width, pdf.bounds.height],
326
- 'FF0000', '0000FF'
327
- )
328
-
329
- str = pdf.render
330
- expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
331
- end
332
-
333
- it 'stroke_gradient should set stroke color to the pattern' do
334
- pdf.stroke_gradient(
335
- [0, pdf.bounds.height],
336
- [pdf.bounds.width, pdf.bounds.height],
337
- 'FF0000', '0000FF'
338
- )
339
-
340
- str = pdf.render
341
- expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
342
- end
343
-
344
- it 'uses a stitching function to render a gradient with multiple stops' do
345
- pdf.fill_gradient(
346
- from: [0, pdf.bounds.height],
347
- to: [pdf.bounds.width, pdf.bounds.height],
348
- stops: { 0 => 'FF0000', 0.8 => '00FF00', 1 => '0000FF' }
349
- )
350
-
351
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
352
- pattern = grad.patterns.values.first
353
-
354
- expect(pattern).to_not be_nil
355
-
356
- stitching = pattern[:Shading][:Function]
357
- expect(stitching[:FunctionType]).to eq(3)
358
- expect(stitching[:Functions]).to be_an(Array)
359
- expect(stitching[:Functions].map { |f| f[:C0] })
360
- .to eq([[1, 0, 0], [0, 1, 0]])
361
- expect(stitching[:Functions].map { |f| f[:C1] })
362
- .to eq([[0, 1, 0], [0, 0, 1]])
363
- expect(stitching[:Bounds]).to eq([0.8])
364
- expect(stitching[:Encode]).to eq([0, 1, 0, 1])
365
- end
366
-
367
- it 'uses a stitching function to render a gradient with equally spaced '\
368
- 'stops' do
369
- pdf.fill_gradient(
370
- from: [0, pdf.bounds.height],
371
- to: [pdf.bounds.width, pdf.bounds.height],
372
- stops: %w[FF0000 00FF00 0000FF]
373
- )
374
-
375
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
376
- pattern = grad.patterns.values.first
377
-
378
- expect(pattern).to_not be_nil
379
-
380
- stitching = pattern[:Shading][:Function]
381
- expect(stitching[:FunctionType]).to eq(3)
382
- expect(stitching[:Functions]).to be_an(Array)
383
- expect(stitching[:Functions].map { |f| f[:C0] })
384
- .to eq([[1, 0, 0], [0, 1, 0]])
385
- expect(stitching[:Functions].map { |f| f[:C1] })
386
- .to eq([[0, 1, 0], [0, 0, 1]])
387
- expect(stitching[:Bounds]).to eq([0.5])
388
- end
389
- end
390
-
391
- describe 'radial gradients' do
392
- it 'creates a /Pattern resource' do
393
- pdf.fill_gradient(
394
- [0, pdf.bounds.height], 10,
395
- [pdf.bounds.width, pdf.bounds.height], 20,
396
- 'FF0000', '0000FF'
397
- )
398
-
399
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
400
- pattern = grad.patterns.values.first
401
-
402
- expect(pattern).to_not be_nil
403
- expect(pattern[:Shading][:ShadingType]).to eq(3)
404
- expect(pattern[:Shading][:Coords])
405
- .to eq([0, 0, 10, pdf.bounds.width, 0, 20])
406
- expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
407
- (x1 - x2).abs < 0.01
408
- end).to eq true
409
- expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
410
- (x1 - x2).abs < 0.01
411
- end).to eq true
412
- end
413
-
414
- it 'fill_gradient should set fill color to the pattern' do
415
- pdf.fill_gradient(
416
- [0, pdf.bounds.height], 10,
417
- [pdf.bounds.width, pdf.bounds.height], 20,
418
- 'FF0000', '0000FF'
419
- )
420
-
421
- str = pdf.render
422
- expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
423
- end
424
-
425
- it 'stroke_gradient should set stroke color to the pattern' do
426
- pdf.stroke_gradient(
427
- [0, pdf.bounds.height], 10,
428
- [pdf.bounds.width, pdf.bounds.height], 20,
429
- 'FF0000', '0000FF'
430
- )
431
-
432
- str = pdf.render
433
- expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
434
- end
435
- end
436
-
437
- describe 'gradient transformations' do
438
- subject(:transformations) do
439
- pdf.scale 2 do
440
- pdf.translate 40, 40 do
441
- pdf.fill_gradient [0, 10], [15, 15], 'FF0000', '0000FF', opts
442
- pdf.fill_gradient [0, 10], 15, [15, 15], 25, 'FF0000', '0000FF',
443
- opts
444
- end
445
- end
446
-
447
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
448
- grad.patterns.values.map { |pattern| pattern[:Matrix] }.uniq
449
- end
450
-
451
- context 'when :apply_transformations is true' do
452
- let(:opts) { { apply_transformations: true } }
453
-
454
- it 'uses the transformation stack to translate user co-ordinates to '\
455
- 'document co-ordinates required by /Pattern' do
456
- expect(transformations).to eq([[2, 0, 0, 2, 80, 100]])
457
- end
458
- end
459
-
460
- context 'when :apply_transformations is false' do
461
- let(:opts) { { apply_transformations: false } }
462
-
463
- it "doesn't transform the gradient" do
464
- expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
465
- end
466
- end
467
-
468
- context 'when :apply_transformations is unset' do
469
- let(:opts) { {} }
470
-
471
- it "doesn't transform the gradient and displays a warning" do
472
- allow(pdf).to receive(:warn).twice
473
- expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
474
- expect(pdf).to have_received(:warn).twice
475
- end
476
- end
477
- end
478
- end
479
-
480
- describe 'When using painting shortcuts' do
481
- it 'converts stroke_some_method(args) into some_method(args); stroke' do
482
- allow(pdf).to receive(:line_to).with([100, 100])
483
- allow(pdf).to receive(:stroke)
484
-
485
- pdf.stroke_line_to [100, 100]
486
-
487
- expect(pdf).to have_received(:line_to).with([100, 100])
488
- expect(pdf).to have_received(:stroke)
489
- end
490
-
491
- it 'converts fill_some_method(args) into some_method(args); fill' do
492
- allow(pdf).to receive(:line_to).with([100, 100])
493
- allow(pdf).to receive(:fill)
494
-
495
- pdf.fill_line_to [100, 100]
496
-
497
- expect(pdf).to have_received(:line_to).with([100, 100])
498
- expect(pdf).to have_received(:fill)
499
- end
500
-
501
- it 'does not break method_missing' do
502
- expect { pdf.i_have_a_pretty_girlfriend_named_jia }
503
- .to raise_error(NoMethodError)
504
- end
505
- end
506
-
507
- describe 'When using graphics states' do
508
- it 'adds the right content on save_graphics_state' do
509
- allow(pdf.renderer).to receive(:add_content).with('q')
510
-
511
- pdf.save_graphics_state
512
-
513
- expect(pdf.renderer).to have_received(:add_content).with('q')
514
- end
515
-
516
- it 'adds the right content on restore_graphics_state' do
517
- allow(pdf.renderer).to receive(:add_content).with('Q')
518
-
519
- pdf.restore_graphics_state
520
-
521
- expect(pdf.renderer).to have_received(:add_content).with('Q')
522
- end
523
-
524
- it 'saves and restore when save_graphics_state is used with a block' do
525
- allow(pdf.renderer).to receive(:add_content).with('q')
526
- allow(pdf).to receive(:foo)
527
- allow(pdf.renderer).to receive(:add_content).with('Q')
528
-
529
- pdf.save_graphics_state do
530
- pdf.foo
531
- end
532
-
533
- expect(pdf.renderer).to have_received(:add_content).with('q').ordered
534
- expect(pdf).to have_received(:foo).ordered
535
- expect(pdf.renderer).to have_received(:add_content).with('Q').ordered
536
- end
537
-
538
- it 'adds the previous color space when restoring to a graphic state with '\
539
- 'different color space' do
540
- pdf.stroke_color '000000'
541
- pdf.save_graphics_state
542
- pdf.stroke_color 0, 0, 0, 0
543
- pdf.restore_graphics_state
544
- pdf.stroke_color 0, 0, 100, 0
545
- expect(pdf.graphic_state.color_space).to eq(stroke: :DeviceCMYK)
546
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
547
- expect(colors.color_space).to eq(:DeviceCMYK)
548
- expect(colors.stroke_color_space_count[:DeviceCMYK]).to eq(2)
549
- end
550
-
551
- it 'uses the correct dash setting after restoring and starting new page' do
552
- pdf.dash 5
553
- pdf.save_graphics_state
554
- pdf.dash 10
555
- expect(pdf.graphic_state.dash[:dash]).to eq(10)
556
- pdf.restore_graphics_state
557
- pdf.start_new_page
558
- expect(pdf.graphic_state.dash[:dash]).to eq(5)
559
- end
560
-
561
- it 'rounds dash values to four decimal places' do
562
- pdf.dash 5.12345
563
- expect(pdf.graphic_state.dash_setting).to eq('[5.1235 5.1235] 0.0 d')
564
- end
565
-
566
- it 'raises an error when dash is called w. a zero length or space' do
567
- expect { pdf.dash(0) }.to raise_error(ArgumentError)
568
- expect { pdf.dash([0]) }.to raise_error(ArgumentError)
569
- expect { pdf.dash([0, 0]) }.to raise_error(ArgumentError)
570
- end
571
-
572
- it 'raises an error when dash is called w. negative lengths' do
573
- expect { pdf.dash(-1) }.to raise_error(ArgumentError)
574
- expect { pdf.dash([1, -3]) }.to raise_error(ArgumentError)
575
- end
576
-
577
- it 'the current graphic state keeps track of previous unchanged settings' do
578
- pdf.stroke_color '000000'
579
- pdf.save_graphics_state
580
- pdf.dash 5
581
- pdf.save_graphics_state
582
- pdf.cap_style :round
583
- pdf.save_graphics_state
584
- pdf.fill_color 0, 0, 100, 0
585
- pdf.save_graphics_state
586
-
587
- expect(pdf.graphic_state.stroke_color).to eq('000000')
588
- expect(pdf.graphic_state.join_style).to eq(:miter)
589
- expect(pdf.graphic_state.fill_color).to eq([0, 0, 100, 0])
590
- expect(pdf.graphic_state.cap_style).to eq(:round)
591
- expect(pdf.graphic_state.color_space)
592
- .to eq(fill: :DeviceCMYK, stroke: :DeviceRGB)
593
- expect(pdf.graphic_state.dash).to eq(space: 5, phase: 0, dash: 5)
594
- expect(pdf.graphic_state.line_width).to eq(1)
595
- end
596
-
597
- it "doesn't add extra graphic space closings when rendering multiple " \
598
- 'times' do
599
- pdf.render
600
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
601
- expect(state.save_graphics_state_count).to eq(1)
602
- expect(state.restore_graphics_state_count).to eq(1)
603
- end
604
-
605
- it 'adds extra graphic state enclosings when content is added on multiple '\
606
- 'renderings' do
607
- pdf.render
608
- pdf.text 'Adding a bit more content'
609
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
610
- expect(state.save_graphics_state_count).to eq(2)
611
- expect(state.restore_graphics_state_count).to eq(2)
612
- end
613
-
614
- it 'adds extra graphic state enclosings when new settings are applied on '\
615
- 'multiple renderings' do
616
- pdf.render
617
- pdf.stroke_color 0, 0, 0, 0
618
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
619
- expect(state.save_graphics_state_count).to eq(2)
620
- expect(state.restore_graphics_state_count).to eq(2)
621
- end
622
-
623
- it 'raise_errors error if closing an empty graphic stack' do
624
- expect do
625
- pdf.render
626
- pdf.restore_graphics_state
627
- end.to raise_error(PDF::Core::Errors::EmptyGraphicStateStack)
628
- end
629
-
630
- it 'copies mutable attributes when passing a previous_state to '\
631
- 'the initializer' do
632
- new_state = PDF::Core::GraphicState.new(pdf.graphic_state)
633
-
634
- %i[color_space dash fill_color stroke_color].each do |attr|
635
- expect(new_state.send(attr)).to eq(pdf.graphic_state.send(attr))
636
- expect(new_state.send(attr)).to_not equal(pdf.graphic_state.send(attr))
637
- end
638
- end
639
-
640
- it 'copies mutable attributes when duping' do
641
- new_state = pdf.graphic_state.dup
642
-
643
- %i[color_space dash fill_color stroke_color].each do |attr|
644
- expect(new_state.send(attr)).to eq(pdf.graphic_state.send(attr))
645
- expect(new_state.send(attr)).to_not equal(pdf.graphic_state.send(attr))
646
- end
647
- end
648
- end
649
-
650
- describe 'When using transformation matrix' do
651
- # Note: The (approximate) number of significant decimal digits of precision
652
- # in fractional part is 5 (PDF Reference, Third Edition, p. 706)
653
-
654
- it 'sends the right content on transformation_matrix' do
655
- allow(pdf.renderer).to receive(:add_content)
656
- .with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
657
- pdf.transformation_matrix 1, 0, 0.123456789, -1.0, 5.5, 20
658
- expect(pdf.renderer).to have_received(:add_content)
659
- .with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
660
- end
661
-
662
- it 'uses fixed digits with very small number' do
663
- values = Array.new(6, 0.000000000001)
664
- string = Array.new(6, '0.0').join ' '
665
- allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
666
- pdf.transformation_matrix(*values)
667
- expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
668
- end
669
-
670
- it 'is received by the inspector' do
671
- pdf.transformation_matrix 1, 0, 0, -1, 5.5, 20
672
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
673
- expect(matrices.matrices).to eq([[1, 0, 0, -1, 5.5, 20]])
674
- end
675
-
676
- it 'saves the graphics state inside the given block' do
677
- values = Array.new(6, 0.000000000001)
678
- string = Array.new(6, '0.0').join ' '
679
-
680
- allow(pdf).to receive(:save_graphics_state).with(no_args)
681
- allow(pdf.renderer).to receive(:add_content).with(any_args).twice
682
- allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
683
- allow(pdf).to receive(:do_something)
684
- allow(pdf).to receive(:restore_graphics_state).with(no_args)
685
-
686
- pdf.transformation_matrix(*values) do
687
- pdf.do_something
688
- end
689
-
690
- expect(pdf).to have_received(:save_graphics_state).with(no_args).ordered
691
- expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
692
- .ordered
693
- expect(pdf).to have_received(:do_something).ordered
694
- expect(pdf).to have_received(:restore_graphics_state).with(no_args)
695
- .ordered
696
- end
697
- end
698
-
699
- describe 'When using transformations shortcuts' do
700
- let(:x) { 12 }
701
- let(:y) { 54.32 }
702
- let(:angle) { 12.32 }
703
- let(:cos) { Math.cos(angle * Math::PI / 180) }
704
- let(:sin) { Math.sin(angle * Math::PI / 180) }
705
- let(:factor) { 0.12 }
706
-
707
- describe '#rotate' do
708
- it 'rotates' do
709
- allow(pdf).to receive(:transformation_matrix)
710
- .with(cos, sin, -sin, cos, 0, 0)
711
- pdf.rotate(angle)
712
- expect(pdf).to have_received(:transformation_matrix)
713
- .with(cos, sin, -sin, cos, 0, 0)
714
- end
715
- end
716
-
717
- describe '#rotate with :origin option' do
718
- it 'rotates around the origin' do
719
- x_prime = x * cos - y * sin
720
- y_prime = x * sin + y * cos
721
-
722
- pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
723
-
724
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
725
- expect(matrices.matrices[0]).to eq([
726
- 1, 0, 0, 1,
727
- reduce_precision(x - x_prime),
728
- reduce_precision(y - y_prime)
729
- ])
730
- expect(matrices.matrices[1]).to eq([
731
- reduce_precision(cos),
732
- reduce_precision(sin),
733
- reduce_precision(-sin),
734
- reduce_precision(cos),
735
- 0, 0
736
- ])
737
- end
738
-
739
- it 'rotates around the origin in a document with a margin' do
740
- pdf = Prawn::Document.new
741
-
742
- pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
743
-
744
- x_ = x + pdf.bounds.absolute_left
745
- y_ = y + pdf.bounds.absolute_bottom
746
- x_prime = x_ * cos - y_ * sin
747
- y_prime = x_ * sin + y_ * cos
748
-
749
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
750
- expect(matrices.matrices[0]).to eq([
751
- 1, 0, 0, 1,
752
- reduce_precision(x_ - x_prime),
753
- reduce_precision(y_ - y_prime)
754
- ])
755
- expect(matrices.matrices[1]).to eq([
756
- reduce_precision(cos),
757
- reduce_precision(sin),
758
- reduce_precision(-sin),
759
- reduce_precision(cos),
760
- 0, 0
761
- ])
762
- end
763
-
764
- it 'raise_errors BlockRequired if no block is given' do
765
- expect do
766
- pdf.rotate(angle, origin: [x, y])
767
- end.to raise_error(Prawn::Errors::BlockRequired)
768
- end
769
- end
770
-
771
- describe '#translate' do
772
- it 'translates' do
773
- x = 12
774
- y = 54.32
775
- allow(pdf).to receive(:transformation_matrix).with(1, 0, 0, 1, x, y)
776
- pdf.translate(x, y)
777
- expect(pdf).to have_received(:transformation_matrix)
778
- .with(1, 0, 0, 1, x, y)
779
- end
780
- end
781
-
782
- describe '#scale' do
783
- it 'scales' do
784
- allow(pdf).to receive(:transformation_matrix)
785
- .with(factor, 0, 0, factor, 0, 0)
786
- pdf.scale(factor)
787
- expect(pdf).to have_received(:transformation_matrix)
788
- .with(factor, 0, 0, factor, 0, 0)
789
- end
790
- end
791
-
792
- describe '#scale with :origin option' do
793
- it 'scales from the origin' do
794
- x_prime = factor * x
795
- y_prime = factor * y
796
-
797
- pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
798
-
799
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
800
- expect(matrices.matrices[0]).to eq([
801
- 1, 0, 0, 1,
802
- reduce_precision(x - x_prime),
803
- reduce_precision(y - y_prime)
804
- ])
805
- expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
806
- end
807
-
808
- it 'scales from the origin in a document with a margin' do
809
- pdf = Prawn::Document.new
810
- x_ = x + pdf.bounds.absolute_left
811
- y_ = y + pdf.bounds.absolute_bottom
812
- x_prime = factor * x_
813
- y_prime = factor * y_
814
-
815
- pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
816
-
817
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
818
- expect(matrices.matrices[0]).to eq([
819
- 1, 0, 0, 1,
820
- reduce_precision(x_ - x_prime),
821
- reduce_precision(y_ - y_prime)
822
- ])
823
- expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
824
- end
825
-
826
- it 'raise_errors BlockRequired if no block is given' do
827
- expect do
828
- pdf.scale(factor, origin: [x, y])
829
- end.to raise_error(Prawn::Errors::BlockRequired)
830
- end
831
- end
832
- end
833
-
834
- def reduce_precision(float)
835
- float.round(5)
836
- end
837
- end