prawn 2.4.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 +213 -141
  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 +20 -18
  7. data/lib/prawn/document.rb +257 -171
  8. data/lib/prawn/encoding.rb +2 -5
  9. data/lib/prawn/errors.rb +23 -34
  10. data/lib/prawn/font.rb +248 -135
  11. data/lib/prawn/font_metric_cache.rb +11 -10
  12. data/lib/prawn/fonts/afm.rb +85 -45
  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 +305 -93
  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 +57 -34
  22. data/lib/prawn/graphics/dash.rb +45 -42
  23. data/lib/prawn/graphics/join_style.rb +17 -11
  24. data/lib/prawn/graphics/patterns.rb +190 -69
  25. data/lib/prawn/graphics/transformation.rb +48 -41
  26. data/lib/prawn/graphics/transparency.rb +16 -40
  27. data/lib/prawn/graphics.rb +363 -253
  28. data/lib/prawn/grid.rb +184 -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 +42 -19
  32. data/lib/prawn/images/png.rb +92 -41
  33. data/lib/prawn/images.rb +44 -57
  34. data/lib/prawn/measurement_extensions.rb +39 -8
  35. data/lib/prawn/measurements.rb +60 -5
  36. data/lib/prawn/outline.rb +114 -108
  37. data/lib/prawn/repeater.rb +51 -35
  38. data/lib/prawn/security/arcfour.rb +4 -4
  39. data/lib/prawn/security.rb +75 -70
  40. data/lib/prawn/soft_mask.rb +42 -30
  41. data/lib/prawn/stamp.rb +38 -42
  42. data/lib/prawn/text/box.rb +146 -96
  43. data/lib/prawn/text/formatted/arranger.rb +87 -26
  44. data/lib/prawn/text/formatted/box.rb +221 -150
  45. data/lib/prawn/text/formatted/fragment.rb +130 -14
  46. data/lib/prawn/text/formatted/line_wrap.rb +33 -24
  47. data/lib/prawn/text/formatted/parser.rb +112 -72
  48. data/lib/prawn/text/formatted/wrap.rb +12 -17
  49. data/lib/prawn/text/formatted.rb +75 -0
  50. data/lib/prawn/text.rb +441 -196
  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 +68 -53
  55. data/lib/prawn.rb +23 -18
  56. data.tar.gz.sig +0 -0
  57. metadata +54 -177
  58. metadata.gz.sig +0 -0
  59. data/.yardopts +0 -10
  60. data/Gemfile +0 -5
  61. data/Rakefile +0 -25
  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 -29
  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 -23
  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 -38
  94. data/manual/graphics/gradients.rb +0 -43
  95. data/manual/graphics/graphics.rb +0 -64
  96. data/manual/graphics/helper.rb +0 -34
  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 -29
  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 -30
  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 -43
  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 -25
  142. data/manual/text/formatted_callbacks.rb +0 -70
  143. data/manual/text/formatted_text.rb +0 -61
  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 -31
  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 -52
  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 -51
  160. data/manual/text/utf8.rb +0 -27
  161. data/manual/text/win_ansi_charset.rb +0 -62
  162. data/prawn.gemspec +0 -51
  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 -550
  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 -44
  173. data/spec/prawn/document_spec.rb +0 -805
  174. data/spec/prawn/font_metric_cache_spec.rb +0 -54
  175. data/spec/prawn/font_spec.rb +0 -544
  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 -872
  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 -229
  184. data/spec/prawn/measurements_extensions_spec.rb +0 -24
  185. data/spec/prawn/outline_spec.rb +0 -512
  186. data/spec/prawn/repeater_spec.rb +0 -166
  187. data/spec/prawn/soft_mask_spec.rb +0 -74
  188. data/spec/prawn/stamp_spec.rb +0 -173
  189. data/spec/prawn/text/box_spec.rb +0 -1110
  190. data/spec/prawn/text/formatted/arranger_spec.rb +0 -466
  191. data/spec/prawn/text/formatted/box_spec.rb +0 -849
  192. data/spec/prawn/text/formatted/fragment_spec.rb +0 -343
  193. data/spec/prawn/text/formatted/line_wrap_spec.rb +0 -495
  194. data/spec/prawn/text/formatted/parser_spec.rb +0 -697
  195. data/spec/prawn/text_draw_text_spec.rb +0 -150
  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,872 +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.52285, 550.0], [100.0, 545.52285],
141
- [100.0, 540.0], [100.0, 460.0], [100.0, 454.47715], [95.52285, 450.0],
142
- [90.0, 450.0], [60.0, 450.0], [54.47715, 450.0], [50.0, 454.47715],
143
- [50.0, 460.0], [50.0, 540.0], [50.0, 545.52285], [54.47715, 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.61424,
165
- 113.80712, 150,
166
- 100.0, 150.0,
167
-
168
- 86.19288, 150.0,
169
- 75.0, 127.61424,
170
- 75.0, 100.0,
171
-
172
- 75.0, 72.38576,
173
- 86.19288, 50.0,
174
- 100.0, 50.0,
175
-
176
- 113.80712, 50.0,
177
- 125.0, 72.38576,
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',
289
- '0000FF'
290
- )
291
-
292
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
293
- pattern = grad.patterns.values.first
294
-
295
- expect(pattern).to_not be_nil
296
- expect(pattern[:Shading][:ShadingType]).to eq(2)
297
- expect(pattern[:Shading][:Coords]).to eq([0, 0, pdf.bounds.width, 0])
298
- expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
299
- (x1 - x2).abs < 0.01
300
- end).to eq true
301
- expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
302
- (x1 - x2).abs < 0.01
303
- end).to eq true
304
- end
305
-
306
- it 'creates a unique ID for each pattern resource' do
307
- pdf.fill_gradient(
308
- [256, 512],
309
- [356, 512],
310
- 'ffffff',
311
- 'fe00ff'
312
- )
313
- pdf.fill_gradient(
314
- [256, 256],
315
- [356, 256],
316
- 'ffffff',
317
- '0000ff'
318
- )
319
-
320
- str = pdf.render
321
- pattern_ids = str.scan(/SP\h{40}\s+scn/)
322
- expect(pattern_ids.uniq.length).to eq 2
323
- end
324
-
325
- it 'fill_gradient should set fill color to the pattern' do
326
- pdf.fill_gradient(
327
- [0, pdf.bounds.height],
328
- [pdf.bounds.width, pdf.bounds.height],
329
- 'FF0000',
330
- '0000FF'
331
- )
332
-
333
- str = pdf.render
334
- expect(str).to match(%r{/Pattern\s+cs\s*/SP\h{40}\s+scn})
335
- end
336
-
337
- it 'stroke_gradient should set stroke color to the pattern' do
338
- pdf.stroke_gradient(
339
- [0, pdf.bounds.height],
340
- [pdf.bounds.width, pdf.bounds.height],
341
- 'FF0000',
342
- '0000FF'
343
- )
344
-
345
- str = pdf.render
346
- expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
347
- end
348
-
349
- it 'uses a stitching function to render a gradient with multiple stops' do
350
- pdf.fill_gradient(
351
- from: [0, pdf.bounds.height],
352
- to: [pdf.bounds.width, pdf.bounds.height],
353
- stops: { 0 => 'FF0000', 0.8 => '00FF00', 1 => '0000FF' }
354
- )
355
-
356
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
357
- pattern = grad.patterns.values.first
358
-
359
- expect(pattern).to_not be_nil
360
-
361
- stitching = pattern[:Shading][:Function]
362
- expect(stitching[:FunctionType]).to eq(3)
363
- expect(stitching[:Functions]).to be_an(Array)
364
- expect(stitching[:Functions].map { |f| f[:C0] })
365
- .to eq([[1, 0, 0], [0, 1, 0]])
366
- expect(stitching[:Functions].map { |f| f[:C1] })
367
- .to eq([[0, 1, 0], [0, 0, 1]])
368
- expect(stitching[:Bounds]).to eq([0.8])
369
- expect(stitching[:Encode]).to eq([0, 1, 0, 1])
370
- end
371
-
372
- it 'uses a stitching function to render a gradient with equally spaced '\
373
- 'stops' do
374
- pdf.fill_gradient(
375
- from: [0, pdf.bounds.height],
376
- to: [pdf.bounds.width, pdf.bounds.height],
377
- stops: %w[FF0000 00FF00 0000FF]
378
- )
379
-
380
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
381
- pattern = grad.patterns.values.first
382
-
383
- expect(pattern).to_not be_nil
384
-
385
- stitching = pattern[:Shading][:Function]
386
- expect(stitching[:FunctionType]).to eq(3)
387
- expect(stitching[:Functions]).to be_an(Array)
388
- expect(stitching[:Functions].map { |f| f[:C0] })
389
- .to eq([[1, 0, 0], [0, 1, 0]])
390
- expect(stitching[:Functions].map { |f| f[:C1] })
391
- .to eq([[0, 1, 0], [0, 0, 1]])
392
- expect(stitching[:Bounds]).to eq([0.5])
393
- end
394
- end
395
-
396
- describe 'radial gradients' do
397
- it 'creates a /Pattern resource' do
398
- pdf.fill_gradient(
399
- [0, pdf.bounds.height],
400
- 10,
401
- [pdf.bounds.width, pdf.bounds.height],
402
- 20,
403
- 'FF0000',
404
- '0000FF'
405
- )
406
-
407
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
408
- pattern = grad.patterns.values.first
409
-
410
- expect(pattern).to_not be_nil
411
- expect(pattern[:Shading][:ShadingType]).to eq(3)
412
- expect(pattern[:Shading][:Coords])
413
- .to eq([0, 0, 10, pdf.bounds.width, 0, 20])
414
- expect(pattern[:Shading][:Function][:C0].zip([1, 0, 0]).all? do |x1, x2|
415
- (x1 - x2).abs < 0.01
416
- end).to eq true
417
- expect(pattern[:Shading][:Function][:C1].zip([0, 0, 1]).all? do |x1, x2|
418
- (x1 - x2).abs < 0.01
419
- end).to eq true
420
- end
421
-
422
- it 'fill_gradient should set fill color to the pattern' do
423
- pdf.fill_gradient(
424
- [0, pdf.bounds.height],
425
- 10,
426
- [pdf.bounds.width, pdf.bounds.height],
427
- 20,
428
- 'FF0000',
429
- '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
-
436
- it 'stroke_gradient should set stroke color to the pattern' do
437
- pdf.stroke_gradient(
438
- [0, pdf.bounds.height],
439
- 10,
440
- [pdf.bounds.width, pdf.bounds.height],
441
- 20,
442
- 'FF0000',
443
- '0000FF'
444
- )
445
-
446
- str = pdf.render
447
- expect(str).to match(%r{/Pattern\s+CS\s*/SP\h{40}\s+SCN})
448
- end
449
- end
450
-
451
- describe 'gradient transformations' do
452
- subject(:transformations) do
453
- pdf.scale 2 do
454
- pdf.translate 40, 40 do
455
- pdf.fill_gradient [0, 10], [15, 15], 'FF0000', '0000FF', **opts
456
- pdf.fill_gradient [0, 10], 15, [15, 15], 25, 'FF0000', '0000FF', **opts
457
- end
458
- end
459
-
460
- grad = PDF::Inspector::Graphics::Pattern.analyze(pdf.render)
461
- grad.patterns.values.map { |pattern| pattern[:Matrix] }.uniq
462
- end
463
-
464
- context 'when :apply_transformations is true' do
465
- let(:opts) { { apply_transformations: true } }
466
-
467
- it 'uses the transformation stack to translate user co-ordinates to '\
468
- 'document co-ordinates required by /Pattern' do
469
- expect(transformations).to eq([[2, 0, 0, 2, 80, 100]])
470
- end
471
- end
472
-
473
- context 'when :apply_transformations is false' do
474
- let(:opts) { { apply_transformations: false } }
475
-
476
- it "doesn't transform the gradient" do
477
- expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
478
- end
479
- end
480
-
481
- context 'when :apply_transformations is unset' do
482
- let(:opts) { {} }
483
-
484
- it "doesn't transform the gradient and displays a warning" do
485
- allow(pdf).to receive(:warn).twice
486
- expect(transformations).to eq([[1, 0, 0, 1, 0, 10]])
487
- expect(pdf).to have_received(:warn).twice
488
- end
489
- end
490
- end
491
- end
492
-
493
- describe 'When using painting shortcuts' do
494
- it 'converts stroke_some_method(args) into some_method(args); stroke' do
495
- allow(pdf).to receive(:line_to).with([100, 100])
496
- allow(pdf).to receive(:stroke)
497
-
498
- pdf.stroke_line_to [100, 100]
499
-
500
- expect(pdf).to have_received(:line_to).with([100, 100])
501
- expect(pdf).to have_received(:stroke)
502
- end
503
-
504
- it 'converts fill_some_method(args) into some_method(args); fill' do
505
- allow(pdf).to receive(:line_to).with([100, 100])
506
- allow(pdf).to receive(:fill)
507
-
508
- pdf.fill_line_to [100, 100]
509
-
510
- expect(pdf).to have_received(:line_to).with([100, 100])
511
- expect(pdf).to have_received(:fill)
512
- end
513
-
514
- it 'does not break method_missing' do
515
- expect { pdf.i_have_a_pretty_girlfriend_named_jia }
516
- .to raise_error(NoMethodError)
517
- end
518
- end
519
-
520
- describe 'When using graphics states' do
521
- it 'adds the right content on save_graphics_state' do
522
- allow(pdf.renderer).to receive(:add_content).with('q')
523
-
524
- pdf.save_graphics_state
525
-
526
- expect(pdf.renderer).to have_received(:add_content).with('q')
527
- end
528
-
529
- it 'adds the right content on restore_graphics_state' do
530
- allow(pdf.renderer).to receive(:add_content).with('Q')
531
-
532
- pdf.restore_graphics_state
533
-
534
- expect(pdf.renderer).to have_received(:add_content).with('Q')
535
- end
536
-
537
- it 'saves and restore when save_graphics_state is used with a block' do
538
- allow(pdf.renderer).to receive(:add_content).with('q')
539
- allow(pdf).to receive(:foo)
540
- allow(pdf.renderer).to receive(:add_content).with('Q')
541
-
542
- pdf.save_graphics_state do
543
- pdf.foo
544
- end
545
-
546
- expect(pdf.renderer).to have_received(:add_content).with('q').ordered
547
- expect(pdf).to have_received(:foo).ordered
548
- expect(pdf.renderer).to have_received(:add_content).with('Q').ordered
549
- end
550
-
551
- it 'adds the previous color space when restoring to a graphic state with '\
552
- 'different color space' do
553
- pdf.stroke_color '000000'
554
- pdf.save_graphics_state
555
- pdf.stroke_color 0, 0, 0, 0
556
- pdf.restore_graphics_state
557
- pdf.stroke_color 0, 0, 100, 0
558
- expect(pdf.graphic_state.color_space).to eq(stroke: :DeviceCMYK)
559
- colors = PDF::Inspector::Graphics::Color.analyze(pdf.render)
560
- expect(colors.color_space).to eq(:DeviceCMYK)
561
- expect(colors.stroke_color_space_count[:DeviceCMYK]).to eq(2)
562
- end
563
-
564
- it 'uses the correct dash setting after restoring and starting new page' do
565
- pdf.dash 5
566
- pdf.save_graphics_state
567
- pdf.dash 10
568
- expect(pdf.graphic_state.dash[:dash]).to eq(10)
569
- pdf.restore_graphics_state
570
- pdf.start_new_page
571
- expect(pdf.graphic_state.dash[:dash]).to eq(5)
572
- end
573
-
574
- it 'rounds dash values to four decimal places' do
575
- pdf.dash 5.12345
576
- expect(pdf.graphic_state.dash_setting).to eq('[5.12345 5.12345] 0.0 d')
577
- end
578
-
579
- it 'raises an error when dash is called w. a zero length or space' do
580
- expect { pdf.dash(0) }.to raise_error(ArgumentError)
581
- expect { pdf.dash([0]) }.to raise_error(ArgumentError)
582
- expect { pdf.dash([0, 0]) }.to raise_error(ArgumentError)
583
- end
584
-
585
- it 'raises an error when dash is called w. negative lengths' do
586
- expect { pdf.dash(-1) }.to raise_error(ArgumentError)
587
- expect { pdf.dash([1, -3]) }.to raise_error(ArgumentError)
588
- end
589
-
590
- it 'the current graphic state keeps track of previous unchanged settings' do
591
- pdf.stroke_color '000000'
592
- pdf.save_graphics_state
593
- pdf.dash 5
594
- pdf.save_graphics_state
595
- pdf.cap_style :round
596
- pdf.save_graphics_state
597
- pdf.fill_color 0, 0, 100, 0
598
- pdf.save_graphics_state
599
-
600
- expect(pdf.graphic_state.stroke_color).to eq('000000')
601
- expect(pdf.graphic_state.join_style).to eq(:miter)
602
- expect(pdf.graphic_state.fill_color).to eq([0, 0, 100, 0])
603
- expect(pdf.graphic_state.cap_style).to eq(:round)
604
- expect(pdf.graphic_state.color_space)
605
- .to eq(fill: :DeviceCMYK, stroke: :DeviceRGB)
606
- expect(pdf.graphic_state.dash).to eq(space: 5, phase: 0, dash: 5)
607
- expect(pdf.graphic_state.line_width).to eq(1)
608
- end
609
-
610
- it "doesn't add extra graphic space closings when rendering multiple " \
611
- 'times' do
612
- pdf.render
613
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
614
- expect(state.save_graphics_state_count).to eq(1)
615
- expect(state.restore_graphics_state_count).to eq(1)
616
- end
617
-
618
- it 'adds extra graphic state enclosings when content is added on multiple '\
619
- 'renderings' do
620
- pdf.render
621
- pdf.text 'Adding a bit more content'
622
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
623
- expect(state.save_graphics_state_count).to eq(2)
624
- expect(state.restore_graphics_state_count).to eq(2)
625
- end
626
-
627
- it 'adds extra graphic state enclosings when new settings are applied on '\
628
- 'multiple renderings' do
629
- pdf.render
630
- pdf.stroke_color 0, 0, 0, 0
631
- state = PDF::Inspector::Graphics::State.analyze(pdf.render)
632
- expect(state.save_graphics_state_count).to eq(2)
633
- expect(state.restore_graphics_state_count).to eq(2)
634
- end
635
-
636
- it 'raise_errors error if closing an empty graphic stack' do
637
- expect do
638
- pdf.render
639
- pdf.restore_graphics_state
640
- end.to raise_error(PDF::Core::Errors::EmptyGraphicStateStack)
641
- end
642
-
643
- it 'copies mutable attributes when passing a previous_state to '\
644
- 'the initializer' do
645
- new_state = PDF::Core::GraphicState.new(pdf.graphic_state)
646
-
647
- %i[color_space dash fill_color stroke_color].each do |attr|
648
- expect(new_state.public_send(attr)).to eq(pdf.graphic_state.public_send(attr))
649
- expect(new_state.public_send(attr)).to_not equal(pdf.graphic_state.public_send(attr))
650
- end
651
- end
652
-
653
- it 'copies mutable attributes when duping' do
654
- new_state = pdf.graphic_state.dup
655
-
656
- %i[color_space dash fill_color stroke_color].each do |attr|
657
- expect(new_state.public_send(attr)).to eq(pdf.graphic_state.public_send(attr))
658
- expect(new_state.public_send(attr)).to_not equal(pdf.graphic_state.public_send(attr))
659
- end
660
- end
661
- end
662
-
663
- describe 'When using transformation matrix' do
664
- # NOTE: The (approximate) number of significant decimal digits of precision
665
- # in fractional part is 5 (PDF Reference, Third Edition, p. 706)
666
-
667
- it 'sends the right content on transformation_matrix' do
668
- allow(pdf.renderer).to receive(:add_content)
669
- .with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
670
- pdf.transformation_matrix 1, 0, 0.123456789, -1.0, 5.5, 20
671
- expect(pdf.renderer).to have_received(:add_content)
672
- .with('1.0 0.0 0.12346 -1.0 5.5 20.0 cm')
673
- end
674
-
675
- it 'uses fixed digits with very small number' do
676
- values = Array.new(6, 0.000000000001)
677
- string = Array.new(6, '0.0').join ' '
678
- allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
679
- pdf.transformation_matrix(*values)
680
- expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
681
- end
682
-
683
- it 'is received by the inspector' do
684
- pdf.transformation_matrix 1, 0, 0, -1, 5.5, 20
685
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
686
- expect(matrices.matrices).to eq([[1, 0, 0, -1, 5.5, 20]])
687
- end
688
-
689
- it 'saves the graphics state inside the given block' do
690
- values = Array.new(6, 0.000000000001)
691
- string = Array.new(6, '0.0').join ' '
692
-
693
- allow(pdf).to receive(:save_graphics_state).with(no_args)
694
- allow(pdf.renderer).to receive(:add_content).with(any_args).twice
695
- allow(pdf.renderer).to receive(:add_content).with("#{string} cm")
696
- allow(pdf).to receive(:do_something)
697
- allow(pdf).to receive(:restore_graphics_state).with(no_args)
698
-
699
- pdf.transformation_matrix(*values) do
700
- pdf.do_something
701
- end
702
-
703
- expect(pdf).to have_received(:save_graphics_state).with(no_args).ordered
704
- expect(pdf.renderer).to have_received(:add_content).with("#{string} cm")
705
- .ordered
706
- expect(pdf).to have_received(:do_something).ordered
707
- expect(pdf).to have_received(:restore_graphics_state).with(no_args)
708
- .ordered
709
- end
710
-
711
- it 'properly serializes the matrix', issue: 1178 do
712
- pdf.transformation_matrix 1, 0, 0, 1, 0.0, 1.8977874316715393e-05
713
- rendered_document = pdf.render
714
-
715
- expect(rendered_document).to_not include('2.0e-05')
716
-
717
- matrices = PDF::Inspector::Graphics::Matrix.analyze(rendered_document)
718
- expect(matrices.matrices).to eq([[1, 0, 0, 1, 0.0, 0.00002]])
719
- end
720
- end
721
-
722
- describe 'When using transformations shortcuts' do
723
- describe '#rotate' do
724
- let(:angle) { 12.32 }
725
- let(:cos) { Math.cos(angle * Math::PI / 180) }
726
- let(:sin) { Math.sin(angle * Math::PI / 180) }
727
-
728
- it 'rotates' do
729
- allow(pdf).to receive(:transformation_matrix)
730
- .with(cos, sin, -sin, cos, 0, 0)
731
- pdf.rotate(angle)
732
- expect(pdf).to have_received(:transformation_matrix)
733
- .with(cos, sin, -sin, cos, 0, 0)
734
- end
735
- end
736
-
737
- describe '#rotate with :origin option' do
738
- let(:angle) { 12.32 }
739
- let(:cos) { Math.cos(angle * Math::PI / 180) }
740
- let(:sin) { Math.sin(angle * Math::PI / 180) }
741
-
742
- it 'rotates around the origin' do
743
- x = 12
744
- y = 54.32
745
- x_prime = x * cos - y * sin
746
- y_prime = x * sin + y * cos
747
-
748
- pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
749
-
750
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
751
- expect(matrices.matrices[0]).to eq([
752
- 1, 0, 0, 1,
753
- reduce_precision(x - x_prime),
754
- reduce_precision(y - y_prime)
755
- ])
756
- expect(matrices.matrices[1]).to eq([
757
- reduce_precision(cos),
758
- reduce_precision(sin),
759
- reduce_precision(-sin),
760
- reduce_precision(cos),
761
- 0, 0
762
- ])
763
- end
764
-
765
- it 'rotates around the origin in a document with a margin' do
766
- x = 12
767
- y = 54.32
768
- pdf = Prawn::Document.new
769
-
770
- pdf.rotate(angle, origin: [x, y]) { pdf.text('hello world') }
771
-
772
- x_ = x + pdf.bounds.absolute_left
773
- y_ = y + pdf.bounds.absolute_bottom
774
- x_prime = x_ * cos - y_ * sin
775
- y_prime = x_ * sin + y_ * cos
776
-
777
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
778
- expect(matrices.matrices[0]).to eq([
779
- 1, 0, 0, 1,
780
- reduce_precision(x_ - x_prime),
781
- reduce_precision(y_ - y_prime)
782
- ])
783
- expect(matrices.matrices[1]).to eq([
784
- reduce_precision(cos),
785
- reduce_precision(sin),
786
- reduce_precision(-sin),
787
- reduce_precision(cos),
788
- 0, 0
789
- ])
790
- end
791
-
792
- it 'raise_errors BlockRequired if no block is given' do
793
- expect do
794
- pdf.rotate(angle, origin: [0, 0])
795
- end.to raise_error(Prawn::Errors::BlockRequired)
796
- end
797
- end
798
-
799
- describe '#translate' do
800
- it 'translates' do
801
- x = 12
802
- y = 54.32
803
- allow(pdf).to receive(:transformation_matrix).with(1, 0, 0, 1, x, y)
804
- pdf.translate(x, y)
805
- expect(pdf).to have_received(:transformation_matrix)
806
- .with(1, 0, 0, 1, x, y)
807
- end
808
- end
809
-
810
- describe '#scale' do
811
- it 'scales' do
812
- factor = 0.12
813
- allow(pdf).to receive(:transformation_matrix)
814
- .with(factor, 0, 0, factor, 0, 0)
815
- pdf.scale(factor)
816
- expect(pdf).to have_received(:transformation_matrix)
817
- .with(factor, 0, 0, factor, 0, 0)
818
- end
819
- end
820
-
821
- describe '#scale with :origin option' do
822
- let(:factor) { 0.12 }
823
-
824
- it 'scales from the origin' do
825
- x = 12
826
- y = 54.32
827
- x_prime = factor * x
828
- y_prime = factor * y
829
-
830
- pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
831
-
832
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
833
- expect(matrices.matrices[0]).to eq([
834
- 1, 0, 0, 1,
835
- reduce_precision(x - x_prime),
836
- reduce_precision(y - y_prime)
837
- ])
838
- expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
839
- end
840
-
841
- it 'scales from the origin in a document with a margin' do
842
- x = 12
843
- y = 54.32
844
- pdf = Prawn::Document.new
845
- x_ = x + pdf.bounds.absolute_left
846
- y_ = y + pdf.bounds.absolute_bottom
847
- x_prime = factor * x_
848
- y_prime = factor * y_
849
-
850
- pdf.scale(factor, origin: [x, y]) { pdf.text('hello world') }
851
-
852
- matrices = PDF::Inspector::Graphics::Matrix.analyze(pdf.render)
853
- expect(matrices.matrices[0]).to eq([
854
- 1, 0, 0, 1,
855
- reduce_precision(x_ - x_prime),
856
- reduce_precision(y_ - y_prime)
857
- ])
858
- expect(matrices.matrices[1]).to eq([factor, 0, 0, factor, 0, 0])
859
- end
860
-
861
- it 'raise_errors BlockRequired if no block is given' do
862
- expect do
863
- pdf.scale(factor, origin: [0, 0])
864
- end.to raise_error(Prawn::Errors::BlockRequired)
865
- end
866
- end
867
- end
868
-
869
- def reduce_precision(float)
870
- float.round(5)
871
- end
872
- end