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,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'pathname'
5
+
6
+ describe Prawn::FontMetricCache do
7
+ let(:document) { Prawn::Document.new }
8
+ let(:font_metric_cache) { described_class.new(document) }
9
+
10
+ it 'starts with an empty cache' do
11
+ expect(font_metric_cache.instance_variable_get(:@cache)).to be_empty
12
+ end
13
+
14
+ it 'caches the width of the provided string' do
15
+ font_metric_cache.width_of('M', {})
16
+
17
+ expect(font_metric_cache.instance_variable_get(:@cache).size).to eq(1)
18
+ end
19
+
20
+ it 'onlies cache a single copy of the same string' do
21
+ font_metric_cache.width_of('M', {})
22
+ font_metric_cache.width_of('M', {})
23
+
24
+ expect(font_metric_cache.instance_variable_get(:@cache).size).to eq(1)
25
+ end
26
+
27
+ it 'caches different copies for different strings' do
28
+ font_metric_cache.width_of('M', {})
29
+ font_metric_cache.width_of('W', {})
30
+
31
+ expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
32
+ .to eq 2
33
+ end
34
+
35
+ it 'caches different copies of the same string with different font sizes' do
36
+ font_metric_cache.width_of('M', {})
37
+
38
+ document.font_size 24
39
+ font_metric_cache.width_of('M', {})
40
+
41
+ expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
42
+ .to eq 2
43
+ end
44
+
45
+ it 'caches different copies of the same string with different fonts' do
46
+ font_metric_cache.width_of('M', {})
47
+
48
+ document.font 'Courier'
49
+ font_metric_cache.width_of('M', {})
50
+
51
+ expect(font_metric_cache.instance_variable_get(:@cache).entries.size)
52
+ .to eq 2
53
+ end
54
+ end
@@ -0,0 +1,544 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+ require 'pathname'
5
+
6
+ describe Prawn::Font do
7
+ let(:pdf) { create_pdf }
8
+
9
+ describe 'Font behavior' do
10
+ it 'defaults to Helvetica if no font is specified' do
11
+ pdf = Prawn::Document.new
12
+ expect(pdf.font.name).to eq('Helvetica')
13
+ end
14
+ end
15
+
16
+ describe 'Font objects' do
17
+ it 'is equal' do
18
+ font1 = Prawn::Document.new.font
19
+ font2 = Prawn::Document.new.font
20
+
21
+ expect(font1).to eql(font2)
22
+ end
23
+
24
+ it 'alwayses be the same key' do
25
+ font1 = Prawn::Document.new.font
26
+ font2 = Prawn::Document.new.font
27
+
28
+ hash = {}
29
+
30
+ hash[font1] = 'Original'
31
+ hash[font2] = 'Overwritten'
32
+
33
+ expect(hash.size).to eq(1)
34
+
35
+ expect(hash[font1]).to eq('Overwritten')
36
+ expect(hash[font2]).to eq('Overwritten')
37
+ end
38
+ end
39
+
40
+ describe '#width_of' do
41
+ it 'takes character spacing into account' do
42
+ original_width = pdf.width_of('hello world')
43
+ pdf.character_spacing(7) do
44
+ expect(pdf.width_of('hello world')).to eq(original_width + 10 * 7)
45
+ end
46
+ end
47
+
48
+ it 'excludes newlines' do
49
+ # Use a TTF font that has a non-zero width for \n
50
+ pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
51
+
52
+ expect(pdf.width_of("\nhello world\n")).to eq(
53
+ pdf.width_of('hello world')
54
+ )
55
+ end
56
+
57
+ it 'takes formatting into account' do
58
+ normal_hello = pdf.width_of('hello')
59
+ inline_bold_hello = pdf.width_of('<b>hello</b>', inline_format: true)
60
+ expect(inline_bold_hello).to be > normal_hello
61
+
62
+ pdf.font('Helvetica', style: :bold) do
63
+ expect(inline_bold_hello).to eq(pdf.width_of('hello'))
64
+ end
65
+ end
66
+
67
+ it 'accepts :style as an argument' do
68
+ styled_bold_hello = pdf.width_of('hello', style: :bold)
69
+ pdf.font('Helvetica', style: :bold) do
70
+ expect(styled_bold_hello).to eq(pdf.width_of('hello'))
71
+ end
72
+ end
73
+
74
+ it 'reports missing font with style' do
75
+ expect do
76
+ pdf.font('Nada', style: :bold) do
77
+ pdf.width_of('hello')
78
+ end
79
+ end.to raise_error(Prawn::Errors::UnknownFont, /Nada \(bold\)/)
80
+ end
81
+
82
+ it 'calculates styled widths correctly using TTFs' do
83
+ pdf.font_families.update(
84
+ 'DejaVu Sans' => {
85
+ normal: "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf",
86
+ bold: "#{Prawn::DATADIR}/fonts/DejaVuSans-Bold.ttf"
87
+ }
88
+ )
89
+ styled_bold_hello = nil
90
+ bold_hello = nil
91
+ plain_hello = nil
92
+
93
+ pdf.font('DejaVu Sans') do
94
+ styled_bold_hello = pdf.width_of('hello', style: :bold)
95
+ end
96
+ pdf.font('DejaVu Sans', style: :bold) do
97
+ bold_hello = pdf.width_of('hello')
98
+ end
99
+
100
+ pdf.font('DejaVu Sans') do
101
+ plain_hello = pdf.width_of('hello')
102
+ end
103
+
104
+ expect(plain_hello).to_not eq(bold_hello)
105
+
106
+ expect(styled_bold_hello).to eq(bold_hello)
107
+ end
108
+
109
+ it 'does not treat minus as if it were a hyphen', issue: 578 do
110
+ expect(pdf.width_of('-0.75')).to be < pdf.width_of('25.00')
111
+ end
112
+ end
113
+
114
+ describe '#font_size' do
115
+ it 'allows setting font size in DSL style' do
116
+ pdf.font_size 20
117
+ expect(pdf.font_size).to eq(20)
118
+ end
119
+ end
120
+
121
+ describe 'font style support' do
122
+ it 'complains if there is no @current_page' do
123
+ pdf_without_page = Prawn::Document.new(skip_page_creation: true)
124
+
125
+ expect { pdf_without_page.font 'Helvetica' }
126
+ .to raise_error(Prawn::Errors::NotOnPage)
127
+ end
128
+
129
+ it 'allows specifying font style by style name and font family' do
130
+ pdf.font 'Courier', style: :bold
131
+ pdf.text 'In Courier bold'
132
+
133
+ pdf.font 'Courier', style: :bold_italic
134
+ pdf.text 'In Courier bold-italic'
135
+
136
+ pdf.font 'Courier', style: :italic
137
+ pdf.text 'In Courier italic'
138
+
139
+ pdf.font 'Courier', style: :normal
140
+ pdf.text 'In Normal Courier'
141
+
142
+ pdf.font 'Helvetica'
143
+ pdf.text 'In Normal Helvetica'
144
+
145
+ text = PDF::Inspector::Text.analyze(pdf.render)
146
+ expect(text.font_settings.map { |e| e[:name] }).to eq(
147
+ %i[
148
+ Courier-Bold Courier-BoldOblique Courier-Oblique
149
+ Courier Helvetica
150
+ ]
151
+ )
152
+ end
153
+
154
+ it 'allows font familes to be defined in a single dfont' do
155
+ file = "#{Prawn::DATADIR}/fonts/Panic+Sans.dfont"
156
+ pdf.font_families['Panic Sans'] = {
157
+ normal: { file: file, font: 'PanicSans' },
158
+ italic: { file: file, font: 'PanicSans-Italic' },
159
+ bold: { file: file, font: 'PanicSans-Bold' },
160
+ bold_italic: { file: file, font: 'PanicSans-BoldItalic' }
161
+ }
162
+
163
+ pdf.font 'Panic Sans', style: :italic
164
+ pdf.text 'In PanicSans-Italic'
165
+
166
+ text = PDF::Inspector::Text.analyze(pdf.render)
167
+ name = text.font_settings.map { |e| e[:name] }.first.to_s
168
+ name = name.sub(/\w+\+/, 'subset+')
169
+ expect(name).to eq('subset+PanicSans-Italic')
170
+ end
171
+
172
+ it 'allows font familes to be defined in a single ttc' do
173
+ file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttc"
174
+ pdf.font_families['DejaVu Sans'] = {
175
+ normal: { file: file, font: 0 },
176
+ bold: { file: file, font: 1 }
177
+ }
178
+
179
+ pdf.font 'DejaVu Sans', style: :bold
180
+ pdf.text 'In PanicSans-Bold'
181
+
182
+ text = PDF::Inspector::Text.analyze(pdf.render)
183
+ name = text.font_settings.map { |e| e[:name] }.first.to_s
184
+ name = name.sub(/\w+\+/, 'subset+')
185
+ expect(name).to eq('subset+DejaVuSans-Bold')
186
+ end
187
+
188
+ it 'allows fonts to be indexed by name in a ttc file' do
189
+ file = "#{Prawn::DATADIR}/fonts/DejaVuSans.ttc"
190
+ pdf.font_families['DejaVu Sans'] = {
191
+ normal: { file: file, font: 'DejaVu Sans' },
192
+ bold: { file: file, font: 'DejaVu Sans Bold' }
193
+ }
194
+
195
+ pdf.font 'DejaVu Sans', style: :bold
196
+ pdf.text 'In PanicSans-Bold'
197
+
198
+ text = PDF::Inspector::Text.analyze(pdf.render)
199
+ name = text.font_settings.map { |e| e[:name] }.first.to_s
200
+ name = name.sub(/\w+\+/, 'subset+')
201
+ expect(name).to eq('subset+DejaVuSans-Bold')
202
+ end
203
+
204
+ it 'accepts Pathname objects for font files' do
205
+ file = Pathname.new("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
206
+ pdf.font_families['DejaVu Sans'] = {
207
+ normal: file
208
+ }
209
+
210
+ pdf.font 'DejaVu Sans'
211
+ pdf.text 'In DejaVu Sans'
212
+
213
+ text = PDF::Inspector::Text.analyze(pdf.render)
214
+ name = text.font_settings.map { |e| e[:name] }.first.to_s
215
+ name = name.sub(/\w+\+/, 'subset+')
216
+ expect(name).to eq('subset+DejaVuSans')
217
+ end
218
+
219
+ it 'accepts IO objects for font files' do
220
+ File.open "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf" do |io|
221
+ pdf.font_families['DejaVu Sans'] = {
222
+ normal: described_class.load(pdf, io)
223
+ }
224
+
225
+ pdf.font 'DejaVu Sans'
226
+ pdf.text 'In DejaVu Sans'
227
+
228
+ text = PDF::Inspector::Text.analyze(pdf.render)
229
+ name = text.font_settings.map { |e| e[:name] }.first.to_s
230
+ name = name.sub(/\w+\+/, 'subset+')
231
+ expect(name).to eq('subset+DejaVuSans')
232
+ end
233
+ end
234
+ end
235
+
236
+ describe 'Transactional font handling' do
237
+ it 'allows setting of size directly when font is created' do
238
+ pdf.font 'Courier', size: 16
239
+ expect(pdf.font_size).to eq(16)
240
+ end
241
+
242
+ it 'allows temporary setting of a new font using a transaction' do
243
+ pdf.font 'Helvetica', size: 12
244
+
245
+ pdf.font 'Courier', size: 16 do
246
+ expect(pdf.font.name).to eq('Courier')
247
+ expect(pdf.font_size).to eq(16)
248
+ end
249
+
250
+ expect(pdf.font.name).to eq('Helvetica')
251
+ expect(pdf.font_size).to eq(12)
252
+ end
253
+
254
+ it 'masks font size when using a transacation' do
255
+ pdf.font 'Courier', size: 16 do
256
+ expect(pdf.font_size).to eq(16)
257
+ end
258
+
259
+ pdf.font 'Times-Roman'
260
+ pdf.font 'Courier'
261
+
262
+ expect(pdf.font_size).to eq(12)
263
+ end
264
+ end
265
+
266
+ describe 'Document#page_fonts' do
267
+ it 'registers fonts properly by page' do
268
+ pdf.font 'Courier'
269
+ pdf.text('hello')
270
+
271
+ pdf.font 'Helvetica'
272
+ pdf.text('hello')
273
+
274
+ pdf.font 'Times-Roman'
275
+ pdf.text('hello')
276
+
277
+ %w[Courier Helvetica Times-Roman].each do |f|
278
+ page_should_include_font(f)
279
+ end
280
+
281
+ pdf.start_new_page
282
+
283
+ pdf.font 'Helvetica'
284
+ pdf.text('hello')
285
+
286
+ page_should_include_font('Helvetica')
287
+ page_should_not_include_font('Courier')
288
+ page_should_not_include_font('Times-Roman')
289
+ end
290
+
291
+ def page_includes_font?(font)
292
+ pdf.page.fonts.values.map { |e| e.data[:BaseFont] }.include?(font.to_sym)
293
+ end
294
+
295
+ def page_should_include_font(font)
296
+ expect(page_includes_font?(font)).to eq true
297
+ end
298
+
299
+ def page_should_not_include_font(font)
300
+ expect(page_includes_font?(font)).to eq false
301
+ end
302
+ end
303
+
304
+ describe 'AFM fonts' do
305
+ let(:times) { pdf.find_font 'Times-Roman' }
306
+
307
+ it 'calculates string width taking into account accented characters' do
308
+ input = win1252_string("\xE9") # é in win-1252
309
+ expect(times.compute_width_of(input, size: 12))
310
+ .to eq(times.compute_width_of('e', size: 12))
311
+ end
312
+
313
+ it 'calculates string width taking into account kerning pairs' do
314
+ expect(times.compute_width_of(win1252_string('To'), size: 12))
315
+ .to eq(13.332)
316
+ expect(times.compute_width_of(
317
+ win1252_string('To'),
318
+ size: 12,
319
+ kerning: true
320
+ )).to eq(12.372)
321
+
322
+ input = win1252_string("T\xF6") # Tö in win-1252
323
+ expect(times.compute_width_of(input, size: 12, kerning: true))
324
+ .to eq(12.372)
325
+ end
326
+
327
+ it 'encodes text without kerning by default' do
328
+ expect(times.encode_text(win1252_string('To'))).to eq([[0, 'To']])
329
+ input = win1252_string("T\xE9l\xE9") # Télé in win-1252
330
+ expect(times.encode_text(input)).to eq([[0, input]])
331
+ expect(times.encode_text(win1252_string('Technology')))
332
+ .to eq([[0, 'Technology']])
333
+ expect(times.encode_text(win1252_string('Technology...')))
334
+ .to eq([[0, 'Technology...']])
335
+ end
336
+
337
+ it 'encodes text with kerning if requested' do
338
+ expect(times.encode_text(win1252_string('To'), kerning: true))
339
+ .to eq([[0, ['T', 80, 'o']]])
340
+ input = win1252_string("T\xE9l\xE9") # Télé in win-1252
341
+ output = win1252_string("\xE9l\xE9") # élé in win-1252
342
+ expect(times.encode_text(input, kerning: true))
343
+ .to eq([[0, ['T', 70, output]]])
344
+ expect(times.encode_text(win1252_string('Technology'), kerning: true))
345
+ .to eq([[0, ['T', 70, 'echnology']]])
346
+ expect(times.encode_text(win1252_string('Technology...'), kerning: true))
347
+ .to eq([[0, ['T', 70, 'echnology', 65, '...']]])
348
+ end
349
+
350
+ describe 'when normalizing encoding' do
351
+ it 'does not modify the original string when normalize_encoding() '\
352
+ 'is used' do
353
+ original = 'Foo'
354
+ normalized = times.normalize_encoding(original)
355
+ expect(original.equal?(normalized)).to eq false
356
+ end
357
+ end
358
+
359
+ it 'omits /Encoding for symbolic fonts' do
360
+ zapf = pdf.find_font 'ZapfDingbats'
361
+ font_dict = zapf.__send__(:register, nil)
362
+ expect(font_dict.data[:Encoding]).to be_nil
363
+ end
364
+ end
365
+
366
+ describe '#glyph_present' do
367
+ it 'returns true when present in an AFM font' do
368
+ font = pdf.find_font('Helvetica')
369
+ expect(font.glyph_present?('H')).to eq true
370
+ end
371
+
372
+ it 'returns false when absent in an AFM font' do
373
+ font = pdf.find_font('Helvetica')
374
+ expect(font.glyph_present?('再')).to eq false
375
+ end
376
+
377
+ it 'returns true when present in a TTF font' do
378
+ font = pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
379
+ expect(font.glyph_present?('H')).to eq true
380
+ end
381
+
382
+ it 'returns false when absent in a TTF font' do
383
+ font = pdf.find_font("#{Prawn::DATADIR}/fonts/DejaVuSans.ttf")
384
+ expect(font.glyph_present?('再')).to eq false
385
+
386
+ font = pdf.find_font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
387
+ expect(font.glyph_present?('€')).to eq false
388
+ end
389
+ end
390
+
391
+ describe 'TTF fonts' do
392
+ let(:font) { pdf.find_font "#{Prawn::DATADIR}/fonts/DejaVuSans.ttf" }
393
+
394
+ it 'calculates string width taking into account accented characters' do
395
+ expect(font.compute_width_of('é', size: 12)).to eq(
396
+ font.compute_width_of('e', size: 12)
397
+ )
398
+ end
399
+
400
+ it 'calculates string width taking into account kerning pairs' do
401
+ expect(font.compute_width_of('To', size: 12)).to be_within(0.01).of(14.65)
402
+ expect(font.compute_width_of('To', size: 12, kerning: true))
403
+ .to be_within(0.01).of(12.61)
404
+ end
405
+
406
+ it 'encodes text without kerning by default' do
407
+ expect(font.encode_text('To')).to eq([[0, 'To']])
408
+
409
+ tele = "T\216l\216"
410
+ result = font.encode_text('Télé')
411
+ expect(result.length).to eq(1)
412
+ expect(result[0][0]).to eq(0)
413
+ expect(result[0][1].bytes.to_a).to eq(tele.bytes.to_a)
414
+
415
+ expect(font.encode_text('Technology')).to eq([[0, 'Technology']])
416
+ expect(font.encode_text('Technology...')).to eq([[0, 'Technology...']])
417
+ expect(font.encode_text('Teχnology...')).to eq([
418
+ [0, 'Te'],
419
+ [1, '!'], [0, 'nology...']
420
+ ])
421
+ end
422
+
423
+ it 'encodes text with kerning if requested' do
424
+ expect(font.encode_text('To', kerning: true)).to eq([
425
+ [0, ['T', 169.921875, 'o']]
426
+ ])
427
+ expect(font.encode_text('Technology', kerning: true)).to eq([
428
+ [0, ['T', 169.921875, 'echnology']]
429
+ ])
430
+ expect(font.encode_text('Technology...', kerning: true)).to eq([
431
+ [0, ['T', 169.921875, 'echnology', 142.578125, '...']]
432
+ ])
433
+ expect(font.encode_text('Teχnology...', kerning: true)).to eq([
434
+ [0, ['T', 169.921875, 'e']],
435
+ [1, '!'],
436
+ [0, ['nology', 142.578125, '...']]
437
+ ])
438
+ end
439
+
440
+ it 'uses the ascender, descender, and cap height from the TTF verbatim' do
441
+ # These metrics are relative to the font's own bbox. They should not be
442
+ # scaled with font size.
443
+ ref = pdf.ref!({})
444
+ font.__send__(:embed, ref, 0)
445
+
446
+ # Pull out the embedded font descriptor
447
+ descriptor = ref.data[:FontDescriptor].data
448
+ expect(descriptor[:Ascent]).to eq(759)
449
+ expect(descriptor[:Descent]).to eq(-240)
450
+ expect(descriptor[:CapHeight]).to eq(759)
451
+ end
452
+
453
+ describe 'when normalizing encoding' do
454
+ it 'does not modify the original string with normalize_encoding()' do
455
+ original = 'Foo'
456
+ normalized = font.normalize_encoding(original)
457
+ expect(original.equal?(normalized)).to eq false
458
+ end
459
+ end
460
+ end
461
+
462
+ describe 'OTF fonts' do
463
+ let(:font) { pdf.find_font "#{Prawn::DATADIR}/fonts/Bodoni-Book.otf" }
464
+
465
+ it 'calculates string width taking into account accented characters' do
466
+ expect(font.compute_width_of('é', size: 12)).to eq(
467
+ font.compute_width_of('e', size: 12)
468
+ )
469
+ end
470
+
471
+ it 'uses the ascender, descender, and cap height from the OTF verbatim' do
472
+ # These metrics are relative to the font's own bbox. They should not be
473
+ # scaled with font size.
474
+ ref = pdf.ref!({})
475
+ font.__send__(:embed, ref, 0)
476
+
477
+ # Pull out the embedded font descriptor
478
+ descriptor = ref.data[:FontDescriptor].data
479
+ expect(descriptor[:Ascent]).to eq(1023)
480
+ expect(descriptor[:Descent]).to eq(-200)
481
+ expect(descriptor[:CapHeight]).to eq(3072)
482
+ end
483
+
484
+ describe 'when normalizing encoding' do
485
+ it 'does not modify the original string with normalize_encoding()' do
486
+ original = 'Foo'
487
+ normalized = font.normalize_encoding(original)
488
+ expect(original).to_not be_equal(normalized)
489
+ end
490
+ end
491
+ end
492
+
493
+ describe 'DFont fonts' do
494
+ let(:file) { "#{Prawn::DATADIR}/fonts/Panic+Sans.dfont" }
495
+
496
+ it 'lists all named fonts' do
497
+ list = Prawn::Fonts::DFont.named_fonts(file)
498
+ expect(list).to match_array(%w[
499
+ PanicSans PanicSans-Bold PanicSans-BoldItalic PanicSans-Italic
500
+ ])
501
+ end
502
+
503
+ it 'counts the number of fonts in the file' do
504
+ expect(Prawn::Fonts::DFont.font_count(file)).to eq(4)
505
+ end
506
+
507
+ it 'defaults selected font to the first one if not specified' do
508
+ font = pdf.find_font(file)
509
+ expect(font.basename).to eq('PanicSans')
510
+ end
511
+
512
+ it 'allows font to be selected by index' do
513
+ font = pdf.find_font(file, font: 2)
514
+ expect(font.basename).to eq('PanicSans-Italic')
515
+ end
516
+
517
+ it 'allows font to be selected by name' do
518
+ font = pdf.find_font(file, font: 'PanicSans-BoldItalic')
519
+ expect(font.basename).to eq('PanicSans-BoldItalic')
520
+ end
521
+
522
+ it 'caches font object based on selected font' do
523
+ f1 = pdf.find_font(file, font: 'PanicSans')
524
+ f2 = pdf.find_font(file, font: 'PanicSans-Bold')
525
+ expect(f2.object_id).to_not eq(f1.object_id)
526
+ expect(pdf.find_font(file, font: 'PanicSans').object_id)
527
+ .to eq(f1.object_id)
528
+ expect(pdf.find_font(file, font: 'PanicSans-Bold').object_id)
529
+ .to eq(f2.object_id)
530
+ end
531
+ end
532
+
533
+ describe '#character_count(text)' do
534
+ it 'works on TTF fonts' do
535
+ pdf.font("#{Prawn::DATADIR}/fonts/gkai00mp.ttf")
536
+ expect(pdf.font.character_count('こんにちは世界')).to eq(7)
537
+ expect(pdf.font.character_count('Hello, world!')).to eq(13)
538
+ end
539
+
540
+ it 'works on AFM fonts' do
541
+ expect(pdf.font.character_count('Hello, world!')).to eq(13)
542
+ end
543
+ end
544
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Prawn::Graphics::BlendMode do
6
+ def make_blend_mode(blend_mode)
7
+ pdf.blend_mode(blend_mode) do
8
+ yield if block_given?
9
+ end
10
+ end
11
+
12
+ let(:pdf) { create_pdf }
13
+
14
+ it 'the PDF version should be at least 1.4' do
15
+ make_blend_mode(:Multiply)
16
+ str = pdf.render
17
+ expect(str[0, 8]).to eq('%PDF-1.4')
18
+ end
19
+
20
+ it 'a new extended graphics state should be created for ' \
21
+ 'each unique blend mode setting' do
22
+ make_blend_mode(:Multiply) do
23
+ make_blend_mode(:Screen)
24
+ end
25
+ extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
26
+ expect(extgstates.length).to eq(2)
27
+ end
28
+
29
+ it 'a new extended graphics state should not be created for ' \
30
+ 'each duplicate blend mode setting' do
31
+ make_blend_mode(:Multiply) do
32
+ make_blend_mode(:Multiply)
33
+ end
34
+ extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
35
+ expect(extgstates.length).to eq(1)
36
+ end
37
+
38
+ it 'setting the blend mode with only one parameter sets a single '\
39
+ 'blend mode value' do
40
+ make_blend_mode(:Multiply)
41
+ extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
42
+ expect(extgstate[:blend_mode]).to eq(:Multiply)
43
+ end
44
+
45
+ it 'setting the blend mode with multiple parameters sets an array of '\
46
+ 'blend modes' do
47
+ make_blend_mode(%i[Multiply Screen Overlay])
48
+ extgstate = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates.first
49
+ expect(extgstate[:blend_mode]).to eq(%i[Multiply Screen Overlay])
50
+ end
51
+
52
+ describe 'with more than one page' do
53
+ it 'the extended graphic state resource should be added to both pages' do
54
+ make_blend_mode(:Multiply)
55
+ pdf.start_new_page
56
+ make_blend_mode(:Multiply)
57
+ extgstates = PDF::Inspector::ExtGState.analyze(pdf.render).extgstates
58
+ extgstate = extgstates[0]
59
+ expect(extgstates.length).to eq(2)
60
+ expect(extgstate[:blend_mode]).to eq(:Multiply)
61
+ end
62
+ end
63
+ end