prawn 0.13.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (348) hide show
  1. checksums.yaml +5 -5
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +10 -0
  4. data/GPLv2 +20 -21
  5. data/Gemfile +3 -16
  6. data/Rakefile +17 -39
  7. data/lib/prawn/document/bounding_box.rb +85 -42
  8. data/lib/prawn/document/column_box.rb +21 -11
  9. data/lib/prawn/document/internals.rb +40 -147
  10. data/lib/prawn/document/span.rb +25 -17
  11. data/lib/prawn/document.rb +286 -245
  12. data/lib/prawn/encoding.rb +68 -101
  13. data/lib/prawn/errors.rb +47 -43
  14. data/lib/prawn/font.rb +204 -155
  15. data/lib/prawn/font_metric_cache.rb +25 -21
  16. data/lib/prawn/fonts/afm.rb +292 -0
  17. data/lib/prawn/{font → fonts}/dfont.rb +7 -13
  18. data/lib/prawn/fonts/otf.rb +11 -0
  19. data/lib/prawn/fonts/ttc.rb +36 -0
  20. data/lib/prawn/{font → fonts}/ttf.rb +142 -80
  21. data/lib/prawn/graphics/blend_mode.rb +65 -0
  22. data/lib/prawn/graphics/cap_style.rb +6 -5
  23. data/lib/prawn/graphics/color.rb +47 -44
  24. data/lib/prawn/graphics/dash.rb +30 -13
  25. data/lib/prawn/graphics/join_style.rb +13 -6
  26. data/lib/prawn/graphics/patterns.rb +221 -90
  27. data/lib/prawn/graphics/transformation.rb +21 -12
  28. data/lib/prawn/graphics/transparency.rb +21 -17
  29. data/lib/prawn/graphics.rb +155 -128
  30. data/lib/prawn/{layout/grid.rb → grid.rb} +110 -47
  31. data/lib/prawn/image_handler.rb +16 -2
  32. data/lib/prawn/images/image.rb +4 -2
  33. data/lib/prawn/images/jpg.rb +39 -30
  34. data/lib/prawn/images/png.rb +132 -169
  35. data/lib/prawn/images.rb +70 -62
  36. data/lib/prawn/measurement_extensions.rb +15 -10
  37. data/lib/prawn/measurements.rb +22 -23
  38. data/lib/prawn/outline.rb +301 -13
  39. data/lib/prawn/repeater.rb +19 -17
  40. data/lib/prawn/security/arcfour.rb +54 -0
  41. data/lib/prawn/security.rb +108 -86
  42. data/lib/prawn/soft_mask.rb +40 -41
  43. data/lib/prawn/stamp.rb +29 -12
  44. data/lib/prawn/text/box.rb +27 -29
  45. data/lib/prawn/text/formatted/arranger.rb +110 -67
  46. data/lib/prawn/text/formatted/box.rb +233 -165
  47. data/lib/prawn/text/formatted/fragment.rb +27 -27
  48. data/lib/prawn/text/formatted/line_wrap.rb +137 -97
  49. data/lib/prawn/text/formatted/parser.rb +149 -127
  50. data/lib/prawn/text/formatted/wrap.rb +57 -37
  51. data/lib/prawn/text/formatted.rb +6 -4
  52. data/lib/prawn/text.rb +105 -73
  53. data/lib/prawn/transformation_stack.rb +44 -0
  54. data/lib/prawn/utilities.rb +11 -21
  55. data/lib/prawn/version.rb +5 -0
  56. data/lib/prawn/view.rb +101 -0
  57. data/lib/prawn.rb +42 -68
  58. data/{data/images/fractal.jpg → manual/absolute_position.pdf} +0 -0
  59. data/manual/basic_concepts/adding_pages.rb +9 -10
  60. data/manual/basic_concepts/basic_concepts.rb +33 -24
  61. data/manual/basic_concepts/creation.rb +10 -11
  62. data/manual/basic_concepts/cursor.rb +9 -10
  63. data/manual/basic_concepts/measurement.rb +10 -11
  64. data/manual/basic_concepts/origin.rb +8 -9
  65. data/manual/basic_concepts/other_cursor_helpers.rb +17 -18
  66. data/manual/basic_concepts/view.rb +48 -0
  67. data/manual/bounding_box/bounding_box.rb +31 -29
  68. data/manual/bounding_box/bounds.rb +17 -18
  69. data/manual/bounding_box/canvas.rb +8 -9
  70. data/manual/bounding_box/creation.rb +8 -9
  71. data/manual/bounding_box/indentation.rb +22 -23
  72. data/manual/bounding_box/nesting.rb +32 -25
  73. data/manual/bounding_box/russian_boxes.rb +19 -19
  74. data/manual/bounding_box/stretchy.rb +18 -20
  75. data/manual/contents.rb +35 -0
  76. data/manual/cover.rb +43 -0
  77. data/manual/document_and_page_options/background.rb +16 -14
  78. data/manual/document_and_page_options/document_and_page_options.rb +26 -23
  79. data/manual/document_and_page_options/metadata.rb +21 -19
  80. data/manual/document_and_page_options/page_margins.rb +20 -22
  81. data/manual/document_and_page_options/page_size.rb +15 -15
  82. data/manual/document_and_page_options/print_scaling.rb +23 -0
  83. data/manual/example_helper.rb +5 -408
  84. data/manual/graphics/blend_mode.rb +52 -0
  85. data/manual/graphics/circle_and_ellipse.rb +8 -9
  86. data/manual/graphics/color.rb +11 -13
  87. data/manual/graphics/common_lines.rb +13 -12
  88. data/manual/graphics/fill_and_stroke.rb +10 -11
  89. data/manual/graphics/fill_rules.rb +13 -12
  90. data/manual/graphics/gradients.rb +28 -22
  91. data/manual/graphics/graphics.rb +52 -46
  92. data/manual/graphics/helper.rb +20 -10
  93. data/manual/graphics/line_width.rb +13 -12
  94. data/manual/graphics/lines_and_curves.rb +13 -14
  95. data/manual/graphics/polygon.rb +10 -12
  96. data/manual/graphics/rectangle.rb +7 -8
  97. data/manual/graphics/rotate.rb +9 -12
  98. data/manual/graphics/scale.rb +19 -18
  99. data/manual/graphics/soft_masks.rb +5 -7
  100. data/manual/graphics/stroke_cap.rb +10 -11
  101. data/manual/graphics/stroke_dash.rb +16 -17
  102. data/manual/graphics/stroke_join.rb +10 -11
  103. data/manual/graphics/translate.rb +13 -13
  104. data/manual/graphics/transparency.rb +11 -13
  105. data/manual/{manual/how_to_read_this_manual.rb → how_to_read_this_manual.rb} +23 -25
  106. data/manual/images/absolute_position.rb +9 -10
  107. data/manual/images/fit.rb +9 -10
  108. data/manual/images/horizontal.rb +13 -14
  109. data/manual/images/images.rb +31 -30
  110. data/manual/images/plain_image.rb +6 -7
  111. data/manual/images/scale.rb +12 -13
  112. data/manual/images/vertical.rb +19 -17
  113. data/manual/images/width_and_height.rb +13 -14
  114. data/manual/layout/boxes.rb +14 -15
  115. data/manual/layout/content.rb +12 -13
  116. data/manual/layout/layout.rb +19 -20
  117. data/manual/layout/simple_grid.rb +8 -9
  118. data/manual/outline/add_subsection_to.rb +26 -27
  119. data/manual/outline/insert_section_after.rb +19 -20
  120. data/manual/outline/outline.rb +23 -22
  121. data/manual/outline/sections_and_pages.rb +24 -25
  122. data/manual/repeatable_content/alternate_page_numbering.rb +36 -0
  123. data/manual/repeatable_content/page_numbering.rb +20 -19
  124. data/manual/repeatable_content/repeatable_content.rb +26 -22
  125. data/manual/repeatable_content/repeater.rb +18 -19
  126. data/manual/repeatable_content/stamp.rb +18 -19
  127. data/manual/security/encryption.rb +8 -11
  128. data/manual/security/permissions.rb +20 -15
  129. data/manual/security/security.rb +20 -20
  130. data/manual/table.rb +16 -0
  131. data/manual/text/alignment.rb +17 -18
  132. data/manual/text/color.rb +13 -13
  133. data/manual/text/column_box.rb +10 -12
  134. data/manual/text/fallback_fonts.rb +29 -25
  135. data/manual/text/font.rb +17 -18
  136. data/manual/text/font_size.rb +21 -22
  137. data/manual/text/font_style.rb +12 -10
  138. data/manual/text/formatted_callbacks.rb +36 -26
  139. data/manual/text/formatted_text.rb +41 -34
  140. data/manual/text/free_flowing_text.rb +28 -29
  141. data/manual/text/inline.rb +23 -26
  142. data/manual/text/kerning_and_character_spacing.rb +20 -21
  143. data/manual/text/leading.rb +10 -11
  144. data/manual/text/line_wrapping.rb +40 -21
  145. data/manual/text/paragraph_indentation.rb +17 -12
  146. data/manual/text/positioned_text.rb +19 -20
  147. data/manual/text/registering_families.rb +33 -30
  148. data/manual/text/rendering_and_color.rb +11 -12
  149. data/manual/text/right_to_left_text.rb +31 -20
  150. data/manual/text/rotation.rb +36 -27
  151. data/manual/text/single_usage.rb +13 -14
  152. data/manual/text/text.rb +62 -62
  153. data/manual/text/text_box_excess.rb +22 -19
  154. data/manual/text/text_box_extensions.rb +21 -18
  155. data/manual/text/text_box_overflow.rb +28 -21
  156. data/manual/text/utf8.rb +16 -17
  157. data/manual/text/win_ansi_charset.rb +29 -26
  158. data/prawn.gemspec +45 -43
  159. data/spec/extensions/encoding_helpers.rb +4 -3
  160. data/spec/prawn/document/bounding_box_spec.rb +550 -0
  161. data/spec/prawn/document/column_box_spec.rb +75 -0
  162. data/spec/prawn/document/security_spec.rb +176 -0
  163. data/spec/prawn/document_annotations_spec.rb +76 -0
  164. data/spec/prawn/document_destinations_spec.rb +15 -0
  165. data/spec/prawn/document_grid_spec.rb +99 -0
  166. data/spec/prawn/document_reference_spec.rb +27 -0
  167. data/spec/prawn/document_span_spec.rb +44 -0
  168. data/spec/prawn/document_spec.rb +805 -0
  169. data/spec/prawn/font_metric_cache_spec.rb +54 -0
  170. data/spec/prawn/font_spec.rb +544 -0
  171. data/spec/prawn/graphics/blend_mode_spec.rb +63 -0
  172. data/spec/prawn/graphics/transparency_spec.rb +81 -0
  173. data/spec/prawn/graphics_spec.rb +872 -0
  174. data/spec/prawn/graphics_stroke_styles_spec.rb +229 -0
  175. data/spec/prawn/image_handler_spec.rb +53 -0
  176. data/spec/prawn/images/jpg_spec.rb +20 -0
  177. data/spec/prawn/images/png_spec.rb +283 -0
  178. data/spec/prawn/images_spec.rb +229 -0
  179. data/spec/prawn/measurements_extensions_spec.rb +24 -0
  180. data/spec/prawn/outline_spec.rb +512 -0
  181. data/spec/prawn/repeater_spec.rb +166 -0
  182. data/spec/prawn/soft_mask_spec.rb +74 -0
  183. data/spec/prawn/stamp_spec.rb +173 -0
  184. data/spec/prawn/text/box_spec.rb +1110 -0
  185. data/spec/prawn/text/formatted/arranger_spec.rb +466 -0
  186. data/spec/prawn/text/formatted/box_spec.rb +849 -0
  187. data/spec/prawn/text/formatted/fragment_spec.rb +343 -0
  188. data/spec/prawn/text/formatted/line_wrap_spec.rb +495 -0
  189. data/spec/prawn/text/formatted/parser_spec.rb +697 -0
  190. data/spec/prawn/text_draw_text_spec.rb +150 -0
  191. data/spec/prawn/text_rendering_mode_spec.rb +48 -0
  192. data/spec/prawn/text_spacing_spec.rb +95 -0
  193. data/spec/prawn/text_spec.rb +603 -0
  194. data/spec/prawn/text_with_inline_formatting_spec.rb +35 -0
  195. data/spec/prawn/transformation_stack_spec.rb +66 -0
  196. data/spec/prawn/view_spec.rb +63 -0
  197. data/spec/prawn_manual_spec.rb +35 -0
  198. data/spec/spec_helper.rb +22 -21
  199. data.tar.gz.sig +0 -0
  200. metadata +168 -307
  201. metadata.gz.sig +0 -0
  202. data/README.md +0 -109
  203. data/data/encodings/win_ansi.txt +0 -29
  204. data/data/images/16bit.alpha +0 -0
  205. data/data/images/16bit.dat +0 -0
  206. data/data/images/16bit.png +0 -0
  207. data/data/images/arrow.png +0 -0
  208. data/data/images/arrow2.png +0 -0
  209. data/data/images/barcode_issue.png +0 -0
  210. data/data/images/dice.alpha +0 -0
  211. data/data/images/dice.dat +0 -0
  212. data/data/images/dice.png +0 -0
  213. data/data/images/dice_interlaced.png +0 -0
  214. data/data/images/indexed_color.dat +0 -0
  215. data/data/images/indexed_color.png +0 -0
  216. data/data/images/letterhead.jpg +0 -0
  217. data/data/images/page_white_text.alpha +0 -0
  218. data/data/images/page_white_text.dat +0 -0
  219. data/data/images/page_white_text.png +0 -0
  220. data/data/images/pigs.jpg +0 -0
  221. data/data/images/prawn.png +0 -0
  222. data/data/images/ruport.png +0 -0
  223. data/data/images/ruport_data.dat +0 -0
  224. data/data/images/ruport_transparent.png +0 -0
  225. data/data/images/ruport_type0.png +0 -0
  226. data/data/images/stef.jpg +0 -0
  227. data/data/images/tru256.bmp +0 -0
  228. data/data/images/web-links.dat +0 -1
  229. data/data/images/web-links.png +0 -0
  230. data/data/pdfs/complex_template.pdf +0 -0
  231. data/data/pdfs/contains_ttf_font.pdf +0 -0
  232. data/data/pdfs/encrypted.pdf +0 -0
  233. data/data/pdfs/form.pdf +1 -819
  234. data/data/pdfs/hexagon.pdf +0 -61
  235. data/data/pdfs/indirect_reference.pdf +0 -86
  236. data/data/pdfs/multipage_template.pdf +0 -127
  237. data/data/pdfs/nested_pages.pdf +0 -118
  238. data/data/pdfs/page_without_mediabox.pdf +0 -193
  239. data/data/pdfs/resources_as_indirect_object.pdf +0 -83
  240. data/data/pdfs/two_hexagons.pdf +0 -90
  241. data/data/pdfs/version_1_6.pdf +0 -61
  242. data/data/shift_jis_text.txt +0 -1
  243. data/lib/pdf/core/annotations.rb +0 -60
  244. data/lib/pdf/core/byte_string.rb +0 -9
  245. data/lib/pdf/core/destinations.rb +0 -90
  246. data/lib/pdf/core/document_state.rb +0 -78
  247. data/lib/pdf/core/filter_list.rb +0 -51
  248. data/lib/pdf/core/filters.rb +0 -36
  249. data/lib/pdf/core/graphics_state.rb +0 -68
  250. data/lib/pdf/core/literal_string.rb +0 -16
  251. data/lib/pdf/core/name_tree.rb +0 -177
  252. data/lib/pdf/core/object_store.rb +0 -320
  253. data/lib/pdf/core/outline.rb +0 -315
  254. data/lib/pdf/core/page.rb +0 -212
  255. data/lib/pdf/core/page_geometry.rb +0 -126
  256. data/lib/pdf/core/pdf_object.rb +0 -124
  257. data/lib/pdf/core/reference.rb +0 -103
  258. data/lib/pdf/core/stream.rb +0 -98
  259. data/lib/pdf/core/text.rb +0 -275
  260. data/lib/pdf/core.rb +0 -35
  261. data/lib/prawn/compatibility.rb +0 -91
  262. data/lib/prawn/document/graphics_state.rb +0 -73
  263. data/lib/prawn/document/snapshot.rb +0 -89
  264. data/lib/prawn/font/afm.rb +0 -203
  265. data/lib/prawn/layout.rb +0 -20
  266. data/lib/prawn/table/cell/image.rb +0 -70
  267. data/lib/prawn/table/cell/in_table.rb +0 -27
  268. data/lib/prawn/table/cell/span_dummy.rb +0 -92
  269. data/lib/prawn/table/cell/subtable.rb +0 -65
  270. data/lib/prawn/table/cell/text.rb +0 -153
  271. data/lib/prawn/table/cell.rb +0 -770
  272. data/lib/prawn/table/cells.rb +0 -295
  273. data/lib/prawn/table.rb +0 -643
  274. data/manual/example_file.rb +0 -116
  275. data/manual/example_package.rb +0 -53
  276. data/manual/example_section.rb +0 -46
  277. data/manual/manual/cover.rb +0 -35
  278. data/manual/manual/foreword.rb +0 -85
  279. data/manual/manual/manual.rb +0 -35
  280. data/manual/syntax_highlight.rb +0 -52
  281. data/manual/table/basic_block.rb +0 -53
  282. data/manual/table/before_rendering_page.rb +0 -26
  283. data/manual/table/cell_border_lines.rb +0 -24
  284. data/manual/table/cell_borders_and_bg.rb +0 -31
  285. data/manual/table/cell_dimensions.rb +0 -30
  286. data/manual/table/cell_text.rb +0 -38
  287. data/manual/table/column_widths.rb +0 -30
  288. data/manual/table/content_and_subtables.rb +0 -39
  289. data/manual/table/creation.rb +0 -27
  290. data/manual/table/filtering.rb +0 -36
  291. data/manual/table/flow_and_header.rb +0 -17
  292. data/manual/table/image_cells.rb +0 -33
  293. data/manual/table/position.rb +0 -29
  294. data/manual/table/row_colors.rb +0 -20
  295. data/manual/table/span.rb +0 -30
  296. data/manual/table/style.rb +0 -22
  297. data/manual/table/table.rb +0 -52
  298. data/manual/table/width.rb +0 -27
  299. data/manual/templates/full_template.rb +0 -25
  300. data/manual/templates/page_template.rb +0 -48
  301. data/manual/templates/templates.rb +0 -27
  302. data/manual/text/group.rb +0 -29
  303. data/spec/acceptance/png.rb +0 -23
  304. data/spec/annotations_spec.rb +0 -74
  305. data/spec/bounding_box_spec.rb +0 -493
  306. data/spec/cell_spec.rb +0 -628
  307. data/spec/column_box_spec.rb +0 -33
  308. data/spec/destinations_spec.rb +0 -15
  309. data/spec/document_spec.rb +0 -761
  310. data/spec/extensions/mocha.rb +0 -44
  311. data/spec/filters_spec.rb +0 -34
  312. data/spec/font_metric_cache_spec.rb +0 -52
  313. data/spec/font_spec.rb +0 -464
  314. data/spec/formatted_text_arranger_spec.rb +0 -421
  315. data/spec/formatted_text_box_spec.rb +0 -650
  316. data/spec/formatted_text_fragment_spec.rb +0 -298
  317. data/spec/graphics_spec.rb +0 -651
  318. data/spec/grid_spec.rb +0 -85
  319. data/spec/image_handler_spec.rb +0 -42
  320. data/spec/images_spec.rb +0 -157
  321. data/spec/inline_formatted_text_parser_spec.rb +0 -564
  322. data/spec/jpg_spec.rb +0 -25
  323. data/spec/line_wrap_spec.rb +0 -333
  324. data/spec/measurement_units_spec.rb +0 -23
  325. data/spec/name_tree_spec.rb +0 -112
  326. data/spec/object_store_spec.rb +0 -170
  327. data/spec/outline_spec.rb +0 -448
  328. data/spec/pdf_object_spec.rb +0 -172
  329. data/spec/png_spec.rb +0 -240
  330. data/spec/reference_spec.rb +0 -82
  331. data/spec/repeater_spec.rb +0 -158
  332. data/spec/security_spec.rb +0 -158
  333. data/spec/snapshot_spec.rb +0 -186
  334. data/spec/soft_mask_spec.rb +0 -117
  335. data/spec/span_spec.rb +0 -44
  336. data/spec/stamp_spec.rb +0 -158
  337. data/spec/stream_spec.rb +0 -58
  338. data/spec/stroke_styles_spec.rb +0 -211
  339. data/spec/table/span_dummy_spec.rb +0 -17
  340. data/spec/table_spec.rb +0 -1355
  341. data/spec/template_spec.rb +0 -351
  342. data/spec/text_at_spec.rb +0 -130
  343. data/spec/text_box_spec.rb +0 -1030
  344. data/spec/text_rendering_mode_spec.rb +0 -45
  345. data/spec/text_spacing_spec.rb +0 -93
  346. data/spec/text_spec.rb +0 -425
  347. data/spec/text_with_inline_formatting_spec.rb +0 -35
  348. data/spec/transparency_spec.rb +0 -89
@@ -0,0 +1,512 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Prawn::Outline do
6
+ let(:pdf) do
7
+ Prawn::Document.new do
8
+ text 'Page 1. This is the first Chapter. '
9
+ start_new_page
10
+ text 'Page 2. More in the first Chapter. '
11
+ start_new_page
12
+ outline.define do
13
+ section 'Chapter 1', destination: 1, closed: true do
14
+ page destination: 1, title: 'Page 1'
15
+ page destination: 2, title: 'Page 2'
16
+ end
17
+ end
18
+ end
19
+ end
20
+
21
+ let(:hash) do
22
+ output = StringIO.new(pdf.render, 'r+')
23
+ PDF::Reader::ObjectHash.new(output)
24
+ end
25
+ let(:outline_root) do
26
+ hash.values.find do |obj|
27
+ obj.is_a?(Hash) && obj[:Type] == :Outlines
28
+ end
29
+ end
30
+
31
+ describe 'outline encoding' do
32
+ it 'stores all outline titles as UTF-16' do
33
+ hash.each_value do |obj|
34
+ next unless obj.is_a?(Hash) && obj[:Title]
35
+
36
+ title = obj[:Title].dup
37
+ title.force_encoding(Encoding::UTF_16LE)
38
+ expect(title.valid_encoding?).to eq(true)
39
+ end
40
+ end
41
+ end
42
+
43
+ describe '#generate_outline' do
44
+ it 'creates a root outline dictionary item' do
45
+ expect(outline_root).to_not be_nil
46
+ end
47
+
48
+ it 'sets the first and last top items of the root outline dictionary '\
49
+ 'item' do
50
+ section1 = find_by_title('Chapter 1')
51
+
52
+ expect(referenced_object(outline_root[:First])).to eq(section1)
53
+ expect(referenced_object(outline_root[:Last])).to eq(section1)
54
+ end
55
+
56
+ describe '#create_outline_item' do
57
+ it 'creates outline items for each section and page' do
58
+ section1 = find_by_title('Chapter 1')
59
+ page1 = find_by_title('Page 1')
60
+ page2 = find_by_title('Page 2')
61
+
62
+ expect(section1).to_not be_nil
63
+ expect(page1).to_not be_nil
64
+ expect(page2).to_not be_nil
65
+ end
66
+ end
67
+
68
+ describe '#set_relations, #set_variables_for_block, and #reset_parent' do
69
+ it 'links sibling items' do
70
+ page1 = find_by_title('Page 1')
71
+ page2 = find_by_title('Page 2')
72
+
73
+ expect(referenced_object(page1[:Next])).to eq(page2)
74
+ expect(referenced_object(page2[:Prev])).to eq(page1)
75
+ end
76
+
77
+ it 'links child items to parent item' do
78
+ section1 = find_by_title('Chapter 1')
79
+ page1 = find_by_title('Page 1')
80
+ page2 = find_by_title('Page 2')
81
+
82
+ expect(referenced_object(page1[:Parent])).to eq(section1)
83
+ expect(referenced_object(page2[:Parent])).to eq(section1)
84
+ end
85
+
86
+ it 'sets the first and last child items for parent item' do
87
+ section1 = find_by_title('Chapter 1')
88
+ page1 = find_by_title('Page 1')
89
+ page2 = find_by_title('Page 2')
90
+
91
+ expect(referenced_object(section1[:First])).to eq(page1)
92
+ expect(referenced_object(section1[:Last])).to eq(page2)
93
+ end
94
+ end
95
+
96
+ describe '#increase_count' do
97
+ it 'adds the count of all descendant items' do
98
+ section1 = find_by_title('Chapter 1')
99
+ page1 = find_by_title('Page 1')
100
+ page2 = find_by_title('Page 2')
101
+
102
+ expect(outline_root[:Count]).to eq(3)
103
+ expect(section1[:Count].abs).to eq(2)
104
+ expect(page1[:Count]).to eq(0)
105
+ expect(page2[:Count]).to eq(0)
106
+ end
107
+ end
108
+
109
+ describe 'closed option' do
110
+ it "sets the item's integer count to negative" do
111
+ section1 = find_by_title('Chapter 1')
112
+
113
+ expect(section1[:Count]).to eq(-2)
114
+ end
115
+ end
116
+ end
117
+
118
+ describe 'adding a custom destination' do
119
+ before do
120
+ pdf.start_new_page
121
+ pdf.text 'Page 3 with a destination'
122
+ pdf.add_dest('customdest', pdf.dest_xyz(200, 200))
123
+ destination = pdf.dest_xyz(200, 200)
124
+ pdf.outline.update do
125
+ page destination: destination, title: 'Custom Destination'
126
+ end
127
+ end
128
+
129
+ it 'creates an outline item' do
130
+ custom_dest = find_by_title('Custom Destination')
131
+
132
+ expect(custom_dest).to_not be_nil
133
+ end
134
+
135
+ it 'references the custom destination' do
136
+ custom_dest = find_by_title('Custom Destination')
137
+ last_page =
138
+ hash.values.find do |obj|
139
+ obj.is_a?(Hash) && obj[:Type] == :Pages
140
+ end[:Kids]
141
+ .last
142
+
143
+ expect(referenced_object(custom_dest[:Dest].first))
144
+ .to eq(referenced_object(last_page))
145
+ end
146
+ end
147
+
148
+ describe 'addding a section later with outline#section' do
149
+ before do
150
+ pdf.start_new_page
151
+ pdf.text 'Page 3. An added section '
152
+ pdf.outline.update do
153
+ section 'Added Section', destination: 3 do
154
+ page destination: 3, title: 'Page 3'
155
+ end
156
+ end
157
+ end
158
+
159
+ it 'adds new outline items to document' do
160
+ section2 = find_by_title('Added Section')
161
+ page3 = find_by_title('Page 3')
162
+
163
+ expect(section2).to_not be_nil
164
+ expect(page3).to_not be_nil
165
+ end
166
+
167
+ it 'resets the last items for root outline dictionary' do
168
+ section1 = find_by_title('Chapter 1')
169
+ section2 = find_by_title('Added Section')
170
+
171
+ expect(referenced_object(outline_root[:First])).to eq(section1)
172
+ expect(referenced_object(outline_root[:Last])).to eq(section2)
173
+ end
174
+
175
+ it 'resets the next relation for the previous last top level item' do
176
+ section1 = find_by_title('Chapter 1')
177
+ section2 = find_by_title('Added Section')
178
+
179
+ expect(referenced_object(section1[:Next])).to eq(section2)
180
+ end
181
+
182
+ it 'sets the previous relation of the addded to section' do
183
+ section1 = find_by_title('Chapter 1')
184
+ section2 = find_by_title('Added Section')
185
+
186
+ expect(referenced_object(section2[:Prev])).to eq(section1)
187
+ end
188
+
189
+ it 'increases the count of root outline dictionary' do
190
+ expect(outline_root[:Count]).to eq(5)
191
+ end
192
+ end
193
+
194
+ describe '#outline.add_subsection_to' do
195
+ context 'when positioned last' do
196
+ before do
197
+ pdf.start_new_page
198
+ pdf.text 'Page 3. An added subsection '
199
+ pdf.outline.update do
200
+ add_subsection_to 'Chapter 1' do
201
+ section 'Added SubSection', destination: 3 do
202
+ page destination: 3, title: 'Added Page 3'
203
+ end
204
+ end
205
+ end
206
+ end
207
+
208
+ it 'adds new outline items to document' do
209
+ subsection = find_by_title('Added SubSection')
210
+ added_page = find_by_title('Added Page 3')
211
+
212
+ expect(subsection).to_not be_nil
213
+ expect(added_page).to_not be_nil
214
+ end
215
+
216
+ it 'resets the last item for parent item dictionary' do
217
+ section1 = find_by_title('Chapter 1')
218
+ page1 = find_by_title('Page 1')
219
+ subsection = find_by_title('Added SubSection')
220
+
221
+ expect(referenced_object(section1[:First])).to eq(page1)
222
+ expect(referenced_object(section1[:Last])).to eq(subsection)
223
+ end
224
+
225
+ it "sets the prev relation for the new subsection to its parent's old "\
226
+ 'last item' do
227
+ subsection = find_by_title('Added SubSection')
228
+ page2 = find_by_title('Page 2')
229
+
230
+ expect(referenced_object(subsection[:Prev])).to eq(page2)
231
+ end
232
+
233
+ it "the subsection should become the next relation for its parent's old "\
234
+ 'last item' do
235
+ subsection = find_by_title('Added SubSection')
236
+ page2 = find_by_title('Page 2')
237
+
238
+ expect(referenced_object(page2[:Next])).to eq(subsection)
239
+ end
240
+
241
+ it 'sets the first relation for the new subsection' do
242
+ subsection = find_by_title('Added SubSection')
243
+ added_page = find_by_title('Added Page 3')
244
+
245
+ expect(referenced_object(subsection[:First])).to eq(added_page)
246
+ end
247
+
248
+ it 'sets the correct last relation of the added to section' do
249
+ subsection = find_by_title('Added SubSection')
250
+ added_page = find_by_title('Added Page 3')
251
+
252
+ expect(referenced_object(subsection[:Last])).to eq(added_page)
253
+ end
254
+
255
+ it 'increases the count of root outline dictionary' do
256
+ expect(outline_root[:Count]).to eq(5)
257
+ end
258
+ end
259
+
260
+ context 'when positioned first' do
261
+ before do
262
+ pdf.start_new_page
263
+ pdf.text 'Page 3. An added subsection '
264
+ pdf.outline.update do
265
+ add_subsection_to 'Chapter 1', :first do
266
+ section 'Added SubSection', destination: 3 do
267
+ page destination: 3, title: 'Added Page 3'
268
+ end
269
+ end
270
+ end
271
+ end
272
+
273
+ it 'adds new outline items to document' do
274
+ subsection = find_by_title('Added SubSection')
275
+ added_page = find_by_title('Added Page 3')
276
+
277
+ expect(subsection).to_not be_nil
278
+ expect(added_page).to_not be_nil
279
+ end
280
+
281
+ it 'resets the first item for parent item dictionary' do
282
+ section1 = find_by_title('Chapter 1')
283
+ subsection = find_by_title('Added SubSection')
284
+ page2 = find_by_title('Page 2')
285
+
286
+ expect(referenced_object(section1[:First])).to eq(subsection)
287
+ expect(referenced_object(section1[:Last])).to eq(page2)
288
+ end
289
+
290
+ it "sets the next relation for the new subsection to its parent's old "\
291
+ 'first item' do
292
+ subsection = find_by_title('Added SubSection')
293
+ page1 = find_by_title('Page 1')
294
+
295
+ expect(referenced_object(subsection[:Next])).to eq(page1)
296
+ end
297
+
298
+ it "the subsection should become the prev relation for its parent's old "\
299
+ 'first item' do
300
+ subsection = find_by_title('Added SubSection')
301
+ page1 = find_by_title('Page 1')
302
+
303
+ expect(referenced_object(page1[:Prev])).to eq(subsection)
304
+ end
305
+
306
+ it 'sets the first relation for the new subsection' do
307
+ subsection = find_by_title('Added SubSection')
308
+ added_page = find_by_title('Added Page 3')
309
+
310
+ expect(referenced_object(subsection[:First])).to eq(added_page)
311
+ end
312
+
313
+ it 'sets the correct last relation of the added to section' do
314
+ subsection = find_by_title('Added SubSection')
315
+ added_page = find_by_title('Added Page 3')
316
+
317
+ expect(referenced_object(subsection[:Last])).to eq(added_page)
318
+ end
319
+
320
+ it 'increases the count of root outline dictionary' do
321
+ expect(outline_root[:Count]).to eq(5)
322
+ end
323
+ end
324
+
325
+ it 'requires an existing title' do
326
+ expect do
327
+ pdf.go_to_page 1
328
+ pdf.start_new_page
329
+ pdf.text 'Inserted Page'
330
+ pdf.outline.update do
331
+ add_subsection_to 'Wrong page' do
332
+ page page_number, title: 'Inserted Page'
333
+ end
334
+ end
335
+ render_and_find_objects
336
+ end.to raise_error(Prawn::Errors::UnknownOutlineTitle)
337
+ end
338
+ end
339
+
340
+ describe '#outline.insert_section_after' do
341
+ describe 'inserting in the middle of another section' do
342
+ before do
343
+ pdf.go_to_page 1
344
+ pdf.start_new_page
345
+ pdf.text 'Inserted Page'
346
+ pdf.outline.update do
347
+ insert_section_after 'Page 1' do
348
+ page destination: page_number, title: 'Inserted Page'
349
+ end
350
+ end
351
+ end
352
+
353
+ it 'inserts new outline items to document' do
354
+ inserted_page = find_by_title('Inserted Page')
355
+
356
+ expect(inserted_page).to_not be_nil
357
+ end
358
+
359
+ it 'adjusts the count of all ancestors' do
360
+ section1 = find_by_title('Chapter 1')
361
+
362
+ expect(outline_root[:Count]).to eq(4)
363
+ expect(section1[:Count].abs).to eq(3)
364
+ end
365
+
366
+ describe '#adjust_relations' do
367
+ it 'resets the sibling relations of adjoining items to inserted item' do
368
+ inserted_page = find_by_title('Inserted Page')
369
+ page1 = find_by_title('Page 1')
370
+ page2 = find_by_title('Page 2')
371
+
372
+ expect(referenced_object(page1[:Next])).to eq(inserted_page)
373
+ expect(referenced_object(page2[:Prev])).to eq(inserted_page)
374
+ end
375
+
376
+ it 'sets the sibling relation of added item to adjoining items' do
377
+ inserted_page = find_by_title('Inserted Page')
378
+ page1 = find_by_title('Page 1')
379
+ page2 = find_by_title('Page 2')
380
+
381
+ expect(referenced_object(inserted_page[:Next])).to eq(page2)
382
+ expect(referenced_object(inserted_page[:Prev])).to eq(page1)
383
+ end
384
+
385
+ it 'does not affect the first and last relations of parent item' do
386
+ section1 = find_by_title('Chapter 1')
387
+ page1 = find_by_title('Page 1')
388
+ page2 = find_by_title('Page 2')
389
+
390
+ expect(referenced_object(section1[:First])).to eq(page1)
391
+ expect(referenced_object(section1[:Last])).to eq(page2)
392
+ end
393
+ end
394
+
395
+ context 'when adding another section afterwards' do
396
+ it 'has reset the root position so that a new section is added at '\
397
+ 'the end of root sections' do
398
+ pdf.start_new_page
399
+ pdf.text 'Another Inserted Page'
400
+ pdf.outline.update do
401
+ section 'Added Section' do
402
+ page destination: page_number, title: 'Inserted Page'
403
+ end
404
+ end
405
+
406
+ section1 = find_by_title('Chapter 1')
407
+ section2 = find_by_title('Added Section')
408
+
409
+ expect(referenced_object(outline_root[:Last])).to eq(section2)
410
+ expect(referenced_object(section1[:Next])).to eq(section2)
411
+ end
412
+ end
413
+ end
414
+
415
+ describe 'inserting at the end of another section' do
416
+ before do
417
+ pdf.go_to_page 2
418
+ pdf.start_new_page
419
+ pdf.text 'Inserted Page'
420
+ pdf.outline.update do
421
+ insert_section_after 'Page 2' do
422
+ page destination: page_number, title: 'Inserted Page'
423
+ end
424
+ end
425
+ end
426
+
427
+ describe '#adjust_relations' do
428
+ it 'resets the sibling relations of adjoining item to inserted item' do
429
+ page2 = find_by_title('Page 2')
430
+ inserted_page = find_by_title('Inserted Page')
431
+
432
+ expect(referenced_object(page2[:Next])).to eq(inserted_page)
433
+ end
434
+
435
+ it 'sets the sibling relation of added item to adjoining items' do
436
+ page2 = find_by_title('Page 2')
437
+ inserted_page = find_by_title('Inserted Page')
438
+
439
+ expect(referenced_object(inserted_page[:Next])).to be_nil
440
+ expect(referenced_object(inserted_page[:Prev])).to eq(page2)
441
+ end
442
+
443
+ it 'adjusts the last relation of parent item' do
444
+ section1 = find_by_title('Chapter 1')
445
+ inserted_page = find_by_title('Inserted Page')
446
+
447
+ expect(referenced_object(section1[:Last])).to eq(inserted_page)
448
+ end
449
+ end
450
+ end
451
+
452
+ it 'requires an existing title' do
453
+ expect do
454
+ pdf.go_to_page 1
455
+ pdf.start_new_page
456
+ pdf.text 'Inserted Page'
457
+ pdf.outline.update do
458
+ insert_section_after 'Wrong page' do
459
+ page destination: page_number, title: 'Inserted Page'
460
+ end
461
+ end
462
+ end.to raise_error(Prawn::Errors::UnknownOutlineTitle)
463
+ end
464
+ end
465
+
466
+ describe '#page' do
467
+ it 'requires a title option to be set' do
468
+ expect do
469
+ Prawn::Document.new do
470
+ text 'Page 1. This is the first Chapter. '
471
+ outline.define do
472
+ page destination: 1, title: nil
473
+ end
474
+ end
475
+ end.to raise_error(Prawn::Errors::RequiredOption)
476
+ end
477
+ end
478
+
479
+ describe 'foreign character encoding' do
480
+ let(:hash) do
481
+ pdf =
482
+ Prawn::Document.new do
483
+ outline.define do
484
+ section 'La pomme croquée', destination: 1, closed: true
485
+ end
486
+ end
487
+ PDF::Reader::ObjectHash.new(StringIO.new(pdf.render, 'r+'))
488
+ end
489
+
490
+ it 'handles other encodings for the title' do
491
+ object = find_by_title('La pomme croquée')
492
+ expect(object).to_not be_nil
493
+ end
494
+ end
495
+
496
+ # Outline titles are stored as UTF-16. This method accepts a UTF-8 outline
497
+ # title and returns the PDF Object that contains an outline with that name
498
+ def find_by_title(title)
499
+ hash.values.find do |obj|
500
+ next unless obj.is_a?(Hash) && obj[:Title]
501
+
502
+ title_codepoints = obj[:Title].unpack('n*')
503
+ title_codepoints.shift
504
+ utf8_title = title_codepoints.pack('U*')
505
+ utf8_title == title ? obj : nil
506
+ end
507
+ end
508
+
509
+ def referenced_object(reference)
510
+ hash[reference]
511
+ end
512
+ end
@@ -0,0 +1,166 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Prawn::Repeater do
6
+ it 'creates a stamp and increments Prawn::Repeater.count on initialize' do
7
+ orig_count = described_class.count
8
+
9
+ doc = sample_document
10
+ allow(doc).to receive(:create_stamp).with("prawn_repeater(#{orig_count})")
11
+
12
+ repeater(doc, :all) { :do_nothing }
13
+
14
+ expect(doc).to have_received(:create_stamp)
15
+ .with("prawn_repeater(#{orig_count})")
16
+
17
+ expect(described_class.count).to eq(orig_count + 1)
18
+ end
19
+
20
+ it 'must provide an :all filter' do
21
+ doc = sample_document
22
+ r = repeater(doc, :all) { :do_nothing }
23
+
24
+ expect((1..doc.page_count).all? { |i| r.match?(i) }).to eq true
25
+ end
26
+
27
+ it 'must provide an :odd filter' do
28
+ doc = sample_document
29
+ r = repeater(doc, :odd) { :do_nothing }
30
+
31
+ odd, even = (1..doc.page_count).partition(&:odd?)
32
+
33
+ expect(odd.all? { |i| r.match?(i) }).to eq true
34
+ expect(even.any? { |i| r.match?(i) }).to eq false
35
+ end
36
+
37
+ it 'must be able to filter by an array of page numbers' do
38
+ doc = sample_document
39
+ r = repeater(doc, [1, 2, 7]) { :do_nothing }
40
+
41
+ expect((1..10).select { |i| r.match?(i) }).to eq([1, 2, 7])
42
+ end
43
+
44
+ it 'must be able to filter by a range of page numbers' do
45
+ doc = sample_document
46
+ r = repeater(doc, 2..4) { :do_nothing }
47
+
48
+ expect((1..10).select { |i| r.match?(i) }).to eq([2, 3, 4])
49
+ end
50
+
51
+ it 'must be able to filter by an arbitrary proc' do
52
+ doc = sample_document
53
+ r = repeater(doc, ->(x) { x == 1 || x % 3 == 0 })
54
+
55
+ expect((1..10).select { |i| r.match?(i) }).to eq([1, 3, 6, 9])
56
+ end
57
+
58
+ it 'must try to run a stamp if the page number matches' do
59
+ doc = sample_document
60
+ allow(doc).to receive(:stamp)
61
+
62
+ repeater(doc, :odd).run(3)
63
+ expect(doc).to have_received(:stamp)
64
+ end
65
+
66
+ it 'must not try to run a stamp unless the page number matches' do
67
+ doc = sample_document
68
+
69
+ allow(doc).to receive(:stamp)
70
+ repeater(doc, :odd).run(2)
71
+ expect(doc).to_not have_received(:stamp)
72
+ end
73
+
74
+ it 'must not try to run a stamp if dynamic is selected' do
75
+ doc = sample_document
76
+
77
+ allow(doc).to receive(:stamp)
78
+ (1..10).each { |p| repeater(doc, :all, true) { :do_nothing }.run(p) }
79
+ expect(doc).to_not have_received(:stamp)
80
+ end
81
+
82
+ it 'must try to run a block if the page number matches' do
83
+ doc = sample_document
84
+
85
+ allow(doc).to receive(:draw_text)
86
+ (1..10).each do |p|
87
+ repeater(doc, [1, 2], true) { doc.draw_text 'foo' }.run(p)
88
+ end
89
+ expect(doc).to have_received(:draw_text).twice
90
+ end
91
+
92
+ it 'must not try to run a block unless the page number matches' do
93
+ doc = sample_document
94
+
95
+ allow(doc).to receive(:draw_text)
96
+ repeater(doc, :odd, true) { doc.draw_text 'foo' }.run(2)
97
+ expect(doc).to_not have_received(:draw_text)
98
+ end
99
+
100
+ it 'must treat any block as a closure' do
101
+ doc = sample_document
102
+
103
+ page = 'Page' # ensure access to ivars
104
+ doc.repeat(:all, dynamic: true) do
105
+ doc.draw_text "#{page} #{doc.page_number}", at: [500, 0]
106
+ end
107
+
108
+ text = PDF::Inspector::Text.analyze(doc.render)
109
+ expect(text.strings).to eq((1..10).to_a.map { |p| "Page #{p}" })
110
+ end
111
+
112
+ it 'must treat any block as a closure (Document.new instance_eval form)' do
113
+ doc =
114
+ Prawn::Document.new(skip_page_creation: true) do
115
+ 10.times { start_new_page }
116
+
117
+ page = 'Page'
118
+ repeat(:all, dynamic: true) do
119
+ # ensure self is accessible here
120
+ draw_text "#{page} #{page_number}", at: [500, 0]
121
+ end
122
+ end
123
+
124
+ text = PDF::Inspector::Text.analyze(doc.render)
125
+ expect(text.strings).to eq((1..10).to_a.map { |p| "Page #{p}" })
126
+ end
127
+
128
+ def sample_document
129
+ doc = Prawn::Document.new(skip_page_creation: true)
130
+ 10.times { |_e| doc.start_new_page }
131
+ doc
132
+ end
133
+
134
+ def repeater(*args, &block)
135
+ Prawn::Repeater.new(*args, &block)
136
+ end
137
+
138
+ describe 'graphic state' do
139
+ let(:pdf) { create_pdf }
140
+
141
+ it 'does not alter the graphic state stack color space' do
142
+ starting_color_space = pdf.state.page.graphic_state.color_space.dup
143
+ pdf.repeat :all do
144
+ pdf.text 'Testing', size: 24, style: :bold
145
+ end
146
+ expect(pdf.state.page.graphic_state.color_space)
147
+ .to eq(starting_color_space)
148
+ end
149
+
150
+ context 'with dynamic repeaters' do
151
+ it 'preserves the graphic state at creation time' do
152
+ pdf.repeat :all, dynamic: true do
153
+ pdf.text "fill_color: #{pdf.graphic_state.fill_color}"
154
+ pdf.text "cap_style: #{pdf.graphic_state.cap_style}"
155
+ end
156
+ pdf.fill_color '666666'
157
+ pdf.cap_style :round
158
+ text = PDF::Inspector::Text.analyze(pdf.render)
159
+ expect(text.strings.include?('fill_color: 666666')).to eq(false)
160
+ expect(text.strings.include?('fill_color: 000000')).to eq(true)
161
+ expect(text.strings.include?('cap_style: round')).to eq(false)
162
+ expect(text.strings.include?('cap_style: butt')).to eq(true)
163
+ end
164
+ end
165
+ end
166
+ end