hexapdf 0.5.0 → 0.6.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 (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +76 -2
  3. data/CONTRIBUTERS +1 -1
  4. data/Rakefile +1 -1
  5. data/VERSION +1 -1
  6. data/examples/boxes.rb +68 -0
  7. data/examples/graphics.rb +12 -12
  8. data/examples/{text_box_alignment.rb → text_layouter_alignment.rb} +14 -14
  9. data/examples/text_layouter_inline_boxes.rb +66 -0
  10. data/examples/{text_box_line_wrapping.rb → text_layouter_line_wrapping.rb} +9 -10
  11. data/examples/{text_box_shapes.rb → text_layouter_shapes.rb} +58 -54
  12. data/examples/text_layouter_styling.rb +125 -0
  13. data/examples/truetype.rb +5 -7
  14. data/lib/hexapdf/cli/command.rb +1 -0
  15. data/lib/hexapdf/configuration.rb +170 -106
  16. data/lib/hexapdf/content/canvas.rb +41 -36
  17. data/lib/hexapdf/content/graphics_state.rb +15 -0
  18. data/lib/hexapdf/content/operator.rb +1 -1
  19. data/lib/hexapdf/dictionary.rb +20 -8
  20. data/lib/hexapdf/dictionary_fields.rb +8 -6
  21. data/lib/hexapdf/document.rb +25 -26
  22. data/lib/hexapdf/document/fonts.rb +4 -4
  23. data/lib/hexapdf/document/images.rb +2 -2
  24. data/lib/hexapdf/document/pages.rb +16 -16
  25. data/lib/hexapdf/encryption/security_handler.rb +41 -9
  26. data/lib/hexapdf/filter/flate_decode.rb +1 -1
  27. data/lib/hexapdf/filter/lzw_decode.rb +1 -1
  28. data/lib/hexapdf/filter/predictor.rb +7 -1
  29. data/lib/hexapdf/font/true_type/font.rb +20 -0
  30. data/lib/hexapdf/font/type1/font.rb +23 -0
  31. data/lib/hexapdf/font_loader.rb +1 -0
  32. data/lib/hexapdf/font_loader/from_configuration.rb +2 -3
  33. data/lib/hexapdf/font_loader/from_file.rb +65 -0
  34. data/lib/hexapdf/image_loader/png.rb +2 -2
  35. data/lib/hexapdf/layout.rb +3 -2
  36. data/lib/hexapdf/layout/box.rb +146 -0
  37. data/lib/hexapdf/layout/inline_box.rb +40 -31
  38. data/lib/hexapdf/layout/{line_fragment.rb → line.rb} +12 -13
  39. data/lib/hexapdf/layout/style.rb +630 -41
  40. data/lib/hexapdf/layout/text_fragment.rb +80 -12
  41. data/lib/hexapdf/layout/{text_box.rb → text_layouter.rb} +164 -109
  42. data/lib/hexapdf/number_tree_node.rb +1 -1
  43. data/lib/hexapdf/parser.rb +4 -1
  44. data/lib/hexapdf/revisions.rb +11 -4
  45. data/lib/hexapdf/stream.rb +8 -9
  46. data/lib/hexapdf/tokenizer.rb +5 -3
  47. data/lib/hexapdf/type.rb +3 -0
  48. data/lib/hexapdf/type/action.rb +56 -0
  49. data/lib/hexapdf/type/actions.rb +52 -0
  50. data/lib/hexapdf/type/actions/go_to.rb +52 -0
  51. data/lib/hexapdf/type/actions/go_to_r.rb +54 -0
  52. data/lib/hexapdf/type/actions/launch.rb +73 -0
  53. data/lib/hexapdf/type/actions/uri.rb +65 -0
  54. data/lib/hexapdf/type/annotation.rb +85 -0
  55. data/lib/hexapdf/type/annotations.rb +51 -0
  56. data/lib/hexapdf/type/annotations/link.rb +70 -0
  57. data/lib/hexapdf/type/annotations/markup_annotation.rb +70 -0
  58. data/lib/hexapdf/type/annotations/text.rb +81 -0
  59. data/lib/hexapdf/type/catalog.rb +3 -1
  60. data/lib/hexapdf/type/embedded_file.rb +6 -11
  61. data/lib/hexapdf/type/file_specification.rb +4 -6
  62. data/lib/hexapdf/type/font.rb +3 -1
  63. data/lib/hexapdf/type/font_descriptor.rb +18 -16
  64. data/lib/hexapdf/type/form.rb +3 -1
  65. data/lib/hexapdf/type/graphics_state_parameter.rb +3 -1
  66. data/lib/hexapdf/type/image.rb +4 -2
  67. data/lib/hexapdf/type/info.rb +2 -5
  68. data/lib/hexapdf/type/names.rb +2 -5
  69. data/lib/hexapdf/type/object_stream.rb +2 -1
  70. data/lib/hexapdf/type/page.rb +14 -1
  71. data/lib/hexapdf/type/page_tree_node.rb +9 -6
  72. data/lib/hexapdf/type/resources.rb +2 -5
  73. data/lib/hexapdf/type/trailer.rb +2 -5
  74. data/lib/hexapdf/type/viewer_preferences.rb +2 -5
  75. data/lib/hexapdf/type/xref_stream.rb +3 -1
  76. data/lib/hexapdf/version.rb +1 -1
  77. data/test/hexapdf/common_tokenizer_tests.rb +3 -1
  78. data/test/hexapdf/content/test_canvas.rb +29 -3
  79. data/test/hexapdf/content/test_graphics_state.rb +11 -0
  80. data/test/hexapdf/content/test_operator.rb +3 -2
  81. data/test/hexapdf/document/test_fonts.rb +8 -8
  82. data/test/hexapdf/document/test_images.rb +4 -12
  83. data/test/hexapdf/document/test_pages.rb +7 -7
  84. data/test/hexapdf/encryption/test_security_handler.rb +1 -5
  85. data/test/hexapdf/filter/test_predictor.rb +40 -12
  86. data/test/hexapdf/font/true_type/test_font.rb +16 -0
  87. data/test/hexapdf/font/type1/test_font.rb +30 -0
  88. data/test/hexapdf/font_loader/test_from_file.rb +29 -0
  89. data/test/hexapdf/font_loader/test_standard14.rb +4 -3
  90. data/test/hexapdf/layout/test_box.rb +104 -0
  91. data/test/hexapdf/layout/test_inline_box.rb +24 -10
  92. data/test/hexapdf/layout/{test_line_fragment.rb → test_line.rb} +9 -9
  93. data/test/hexapdf/layout/test_style.rb +519 -31
  94. data/test/hexapdf/layout/test_text_fragment.rb +136 -15
  95. data/test/hexapdf/layout/{test_text_box.rb → test_text_layouter.rb} +224 -144
  96. data/test/hexapdf/layout/test_text_shaper.rb +1 -1
  97. data/test/hexapdf/test_configuration.rb +12 -6
  98. data/test/hexapdf/test_dictionary.rb +27 -2
  99. data/test/hexapdf/test_dictionary_fields.rb +10 -1
  100. data/test/hexapdf/test_document.rb +14 -13
  101. data/test/hexapdf/test_parser.rb +12 -0
  102. data/test/hexapdf/test_revisions.rb +34 -0
  103. data/test/hexapdf/test_stream.rb +1 -1
  104. data/test/hexapdf/test_type.rb +18 -0
  105. data/test/hexapdf/test_writer.rb +2 -2
  106. data/test/hexapdf/type/actions/test_launch.rb +24 -0
  107. data/test/hexapdf/type/actions/test_uri.rb +23 -0
  108. data/test/hexapdf/type/annotations/test_link.rb +19 -0
  109. data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
  110. data/test/hexapdf/type/annotations/test_text.rb +38 -0
  111. data/test/hexapdf/type/test_annotation.rb +38 -0
  112. data/test/hexapdf/type/test_file_specification.rb +0 -7
  113. data/test/hexapdf/type/test_info.rb +0 -5
  114. data/test/hexapdf/type/test_page.rb +14 -0
  115. data/test/hexapdf/type/test_page_tree_node.rb +4 -1
  116. data/test/hexapdf/type/test_trailer.rb +0 -4
  117. data/test/test_helper.rb +6 -3
  118. metadata +36 -15
  119. data/examples/text_box_inline_boxes.rb +0 -56
  120. data/examples/text_box_styling.rb +0 -72
  121. data/test/hexapdf/type/test_embedded_file.rb +0 -16
  122. data/test/hexapdf/type/test_names.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3d9818c9cdb9673d2349c392e35b0b1460841a9d
4
- data.tar.gz: cef36589c13850c4b3c1fd6fa976fb605a536bff
3
+ metadata.gz: 401d9b20b7b9305c334bad4951cb403ea6f4c919
4
+ data.tar.gz: 40cd8335bd517c1f772c3a26ebf8bb0c7a257492
5
5
  SHA512:
6
- metadata.gz: e174684958b97a165844e15fdb458172c095cc4c8c1d3a2cfce9a73bd9a3bc1d367e34195d8fbe729cb61b1b71a2ab0dca0fbd91e19cc408510107fafa5c0518
7
- data.tar.gz: 27a927ffbd911851187867ae59afbb31b0edabfb2edb6912b682e5af9236711aa51edc683304b0a4b6cdbfba6dd736fee8b6c11766afd8e1b631dc35c04cce30
6
+ metadata.gz: 1697291f44f1b7882d0b489af252b8b9cdcaae9416c770f88f325833e75f18ac6d1ef0f8fd03a47af76f1b1451475a97993f97381117e3d3c4df6a592b1e4d4d
7
+ data.tar.gz: a52680e4ad7f3a7568115e8d4657a1c934f9273e0ef513b023d48570d887ba4b748be4ac91ed2162a0d88f07ca08e63ae7dba7b1fa8c33692e7d6e8bc63dd09d
@@ -1,9 +1,83 @@
1
+ ## 0.6.0 - 2017-10-27
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::Layout::Box] as base class for all layout boxes
6
+ * More styling properties for [HexaPDF::Layout::Style]
7
+ * Methods for checking whether styling properties in [HexaPDF::Layout::Style]
8
+ have been accessed or set
9
+ * [HexaPDF::FontLoader::FromFile] to allow specifying a font file directly
10
+ * Configuration option 'page.default_media_orientation' for settig the default
11
+ orientation of new pages
12
+ * Convenience methods for getting underline and strikeout properties from fonts
13
+ * Configuration option 'style.layers_map' for pre-defining overlay and underlay
14
+ callback objects for [HexaPDF::Layout::Style]
15
+ * [HexaPDF::Type::Action] as well as specific implementations for the GoTo,
16
+ GoToR, Launch and URI actions
17
+ * [HexaPDF::Type::Annotation] as well as specific implementations for the Text
18
+ Link annotations
19
+ * [HexaPDF::Layout::Style::LinkLayer] for easy adding of in-document, URI and
20
+ file links
21
+
22
+ ### Changed
23
+
24
+ * [HexaPDF::Layout::TextFragment] to support more styling properties
25
+ * Cross-reference subsection parsing can handle missing whitespace
26
+ * Renamed HexaPDF::Layout::LineFragment to [HexaPDF::Layout::Line]
27
+ * Renamed HexaPDF::Layout::TextBox to [HexaPDF::Layout::TextLayouter]
28
+ * [HexaPDF::Layout::TextFragment::new] and
29
+ [HexaPDF::Layout::TextLayouter::new] to either take a Style object or
30
+ style options
31
+ * [HexaPDF::Layout::TextLayouter#fit] method signature
32
+ * [HexaPDF::Layout::InlineBox] to wrap a generic box
33
+ * HexaPDF::Document::Fonts#load to [HexaPDF::Document::Fonts#add] for
34
+ consistency
35
+ * [HexaPDF::Document::Pages#add] to allow setting the paper orientation when
36
+ creating new pages
37
+ * [HexaPDF::Filter::Predictor] to allow correcting some common problems
38
+ depending on the new configuration option 'filter.predictor.strict'
39
+ * Moved configuration options 'encryption.aes', 'encryption.arc4',
40
+ 'encryption.filter_map', 'encryption.sub_filter.map', 'filter.map',
41
+ 'image_loader' and 'task.map' to the document specific configuration object
42
+ * [HexaPDF::Configuration#constantize] can now dig into hierarchical values
43
+ * [HexaPDF::Document#wrap] class resolution and configuration option structure
44
+ of 'object.subtype_map'
45
+
46
+ ### Removed
47
+
48
+ * HexaPDF::Dictionary#to_hash method
49
+
50
+ ### Fixed
51
+
52
+ * [HexaPDF::Layout::TextLayouter#fit] to split text fragment into parts if the
53
+ fragment doesn't fit on an empty line
54
+ * Parsing of PDF files containing a loop with respect to cross-reference tables
55
+ * [HexaPDF::Layout::InlineBox] to act as placeholder if no drawing block is
56
+ given
57
+ * Undefined method error in [HexaPDF::Content::Canvas] by raising a proper error
58
+ * Invalid handling of fonts by [HexaPDF::Content::Canvas] when saving and
59
+ restoring the graphics state
60
+ * [HexaPDF::Layout::TextLayouter] so that text fragments don't pollute the
61
+ graphics state
62
+ * [HexaPDF::Content::Operator::SetTextRenderingMode] to normalize the value
63
+ * [HexaPDF::Stream#stream_source] to always return a decrypted stream
64
+ * [HexaPDF::Layout::TextLayouter] to correctly indent all paragraphs, not just
65
+ the first one
66
+ * One-off error in [HexaPDF::Filter::LZWDecode]
67
+ * [HexaPDF::Configuration#merge] to duplicate array values to avoid unwanted
68
+ modifications
69
+ * [HexaPDF::Dictionary#key?] to return false if the key is present but nil
70
+ * [HexaPDF::DictionaryFields::FileSpecificationConverter] to convert hash and
71
+ dictionaries
72
+ * Field /F definition in [HexaPDF::Stream]
73
+
74
+
1
75
  ## 0.5.0 - 2017-06-24
2
76
 
3
77
  ### Added
4
78
 
5
- * [HexaPDF::Layout::TextBox] for easy positioning and layouting of text
6
- * [HexaPDF::Layout::LineFragment] for single text line layout calculations
79
+ * HexaPDF::Layout::TextBox for easy positioning and layouting of text
80
+ * HexaPDF::Layout::LineFragment for single text line layout calculations
7
81
  * [HexaPDF::Layout::TextShaper] for text shaping functionality
8
82
  * [HexaPDF::Layout::TextFragment] for basic text metrics calculations
9
83
  * [HexaPDF::Layout::InlineBox] for fixed size inline graphics
@@ -1,3 +1,3 @@
1
1
  Count Name
2
2
  ======= ====
3
- 802 Thomas Leitner <t_leitner@gmx.at>
3
+ 882 Thomas Leitner <t_leitner@gmx.at>
data/Rakefile CHANGED
@@ -66,7 +66,7 @@ namespace :dev do
66
66
  s.default_executable = 'hexapdf'
67
67
  s.add_dependency('cmdparse', '~> 3.0', '>= 3.0.3')
68
68
  s.add_development_dependency('kramdown', '~> 1.0', '>= 1.13.0')
69
- s.required_ruby_version = '>= 2.3'
69
+ s.required_ruby_version = '>= 2.4'
70
70
 
71
71
  s.author = 'Thomas Leitner'
72
72
  s.email = 't_leitner@gmx.at'
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.0
1
+ 0.6.0
@@ -0,0 +1,68 @@
1
+ # ## Boxes
2
+ #
3
+ # The [HexaPDF::Layout::Box] class is used as the basis for all document layout
4
+ # features.
5
+ #
6
+ # This example shows the basic properties that are available for all boxes, like
7
+ # paddings, borders and and background color. It is also possible to use the
8
+ # underlay and overlay callbacks with boxes.
9
+ #
10
+ # Usage:
11
+ # : `ruby boxes.rb`
12
+ #
13
+
14
+ require 'hexapdf'
15
+
16
+ doc = HexaPDF::Document.new
17
+
18
+ annotate_box = lambda do |canvas, box|
19
+ text = ""
20
+ canvas.font("Times", size: 6)
21
+
22
+ if (data = box.style.padding)
23
+ text << "Padding (TRBL): #{data.top}, #{data.right}, #{data.bottom}, #{data.left}\n"
24
+ end
25
+ unless box.style.border.none?
26
+ data = box.style.border.width
27
+ text << "Border Width (TRBL): #{data.top}, #{data.right}, #{data.bottom}, #{data.left}\n"
28
+ data = box.style.border.color
29
+ text << "Border Color (TRBL):\n* #{data.top}\n* #{data.right}\n* #{data.bottom}\n* #{data.left}\n"
30
+ data = box.style.border.style
31
+ text << "Border Style (TRBL):\n* #{data.top}\n* #{data.right}\n* #{data.bottom}\n* #{data.left}\n"
32
+ end
33
+
34
+ canvas.line_width(0.1).rectangle(0, 0, box.content_width, box.content_height).stroke
35
+ canvas.text(text, at: [0, box.content_height - 10])
36
+ end
37
+
38
+ canvas = doc.pages.add.canvas
39
+
40
+ [[1, 200], [5, 220], [15, 240]].each_with_index do |(width, red), row|
41
+ [[:solid, 180], [:dashed, 200], [:dashed_round, 220],
42
+ [:dotted, 240]].each_with_index do |(style, green), column|
43
+ box = HexaPDF::Layout::Box.new(content_width: 100, content_height: 100, &annotate_box)
44
+ box.style.border(width: width, style: style)
45
+ box.style.background_color([red, green, 0])
46
+ box.draw(canvas, 20 + 140 * column, 700 - 150 * row)
47
+ end
48
+ end
49
+
50
+ # The whole kitchen sink
51
+ box = HexaPDF::Layout::Box.new(content_width: 470, content_height: 200, &annotate_box)
52
+ box.style.background_color([255, 255, 180])
53
+ box.style.padding([20, 5, 10, 15])
54
+ box.style.border(width: [20, 40, 30, 15],
55
+ color: [[46, 185, 206], [206, 199, 46], [188, 46, 206], [59, 206, 46]],
56
+ style: [:solid, :dashed, :dashed_round, :dotted])
57
+ box.style.underlays.add do |canv, _|
58
+ canv.stroke_color([255, 0, 0]).line_width(10).line_cap_style(:butt).
59
+ line(0, 0, box.width, box.height).line(0, box.height, box.width, 0).
60
+ stroke
61
+ end
62
+ box.style.overlays.add do |canv, _|
63
+ canv.stroke_color([0, 0, 255]).line_width(5).
64
+ rectangle(10, 10, box.width - 20, box.height - 20).stroke
65
+ end
66
+ box.draw(canvas, 20, 100)
67
+
68
+ doc.write("boxes.pdf", optimize: true)
@@ -250,26 +250,26 @@ canvas.translate(0, 80) do
250
250
  end
251
251
  end
252
252
 
253
- # Reusing the already draw graphics for an XObject
254
- # Note that converting the page to a form XObject automatically closes all open
255
- # graphics states, therefore this can't be inside the above Canvas#translate
256
- # call
257
- form = doc.add(page.to_form_xobject(reference: false))
258
- canvas.rectangle(480, 80, form.box.width * (100 / form.box.height.to_f), 100).stroke
259
- canvas.xobject(form, at: [480, 80], height: 100)
260
-
261
253
  # A simple rainbow color band
262
254
  canvas.translate(0, 20) do
263
- canvas.line_width = 10
264
- freq = 0.3
265
- 0.upto(50) do |i|
255
+ canvas.line_width = 6
256
+ freq = 0.1
257
+ 0.upto(100) do |i|
266
258
  r = Math.sin(freq * i) * 127 + 128
267
259
  g = Math.sin(freq * i + 2) * 127 + 128
268
260
  b = Math.sin(freq * i + 4) * 127 + 128
269
261
  canvas.stroke_color(r.to_i, g.to_i, b.to_i)
270
- canvas.line(50 + i * 10, 0, 50 + i * 10, 40)
262
+ canvas.line(50 + i * 5, 0, 50 + i * 5, 40)
271
263
  canvas.stroke
272
264
  end
273
265
  end
274
266
 
267
+ # Reusing the already draw graphics for an XObject
268
+ # Note that converting the page to a form XObject automatically closes all open
269
+ # graphics states, therefore this can't be inside the above Canvas#translate
270
+ # call
271
+ form = doc.add(page.to_form_xobject(reference: false))
272
+ canvas.rectangle(480, 80, form.box.width * (100 / form.box.height.to_f), 100).stroke
273
+ canvas.xobject(form, at: [480, 80], height: 100)
274
+
275
275
  doc.write('graphics.pdf', optimize: true)
@@ -1,15 +1,15 @@
1
- # ## Text Box Alignment
1
+ # ## Text Layouter - Alignment
2
2
  #
3
- # The [HexaPDF::Layout::TextBox] class can be used to easily lay out text inside
4
- # a rectangular area, with various horizontal and vertical alignment options.
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
+ # inside a rectangular area, with various horizontal and vertical alignment
5
+ # options.
5
6
  #
6
- # The text inside the box can be aligned horizontally by setting
7
- # [HexaPDF::Layout::Style#align] and vertically by
8
- # [HexaPDF::Layout::Style#valign]. In this example, a sample text is laid out in
9
- # all possible combinations.
7
+ # The text can be aligned horizontally by setting [HexaPDF::Layout::Style#align]
8
+ # and vertically by [HexaPDF::Layout::Style#valign]. In this example, a sample
9
+ # text is laid out in all possible combinations.
10
10
  #
11
11
  # Usage:
12
- # : `ruby text_box_alignment.rb`
12
+ # : `ruby text_layouter_alignment.rb`
13
13
  #
14
14
 
15
15
  require 'hexapdf'
@@ -26,9 +26,9 @@ canvas.font("Times", size: 10, variant: :bold)
26
26
  width = 100
27
27
  height = 150
28
28
  y_base = 800
29
- box = HexaPDF::Layout::TextBox.create(sample_text, width: width,
30
- height: height,
31
- font: doc.fonts.load("Times"))
29
+ tl = HexaPDF::Layout::TextLayouter.create(sample_text, width: width,
30
+ height: height,
31
+ font: doc.fonts.add("Times"))
32
32
 
33
33
  [:left, :center, :right, :justify].each_with_index do |align, x_index|
34
34
  x = x_index * (width + 20) + 70
@@ -38,10 +38,10 @@ box = HexaPDF::Layout::TextBox.create(sample_text, width: width,
38
38
  y = y_base - (height + 30) * y_index
39
39
  canvas.text(valign.to_s, at: [20, y - height / 2]) if x_index == 0
40
40
 
41
- box.style.align(align).valign(valign)
42
- box.draw(canvas, x, y, fit: true)
41
+ tl.style.align(align).valign(valign)
42
+ tl.draw(canvas, x, y, fit: true)
43
43
  canvas.stroke_color(128, 0, 0).rectangle(x, y, width, -height).stroke
44
44
  end
45
45
  end
46
46
 
47
- doc.write("text_box_alignment.pdf", optimize: true)
47
+ doc.write("text_layouter_alignment.pdf", optimize: true)
@@ -0,0 +1,66 @@
1
+ # ## Text Layouter - Inline Boxes
2
+ #
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text
4
+ # mixed with inline boxes.
5
+ #
6
+ # Inline boxes are used for showing graphics that follow the flow of the text.
7
+ # This means that their horizontal and their general vertical position is
8
+ # determined by the text layout functionality. However, inline boxes may be
9
+ # vertically aligned to various positions, like the baseline, the top/bottom of
10
+ # the text and the top/bottom of the line.
11
+ #
12
+ # This example shows some text containing emoticons that are replaced with their
13
+ # graphical representation, with normal smileys being aligned to the baseline
14
+ # and winking smileys to the top of the line.
15
+ #
16
+ # An inline box is a simple wrapper around a generic box that adheres to the
17
+ # necessary interface. Therefore they don't do any drawing operations themselves
18
+ # but delegate to their wrapped box. This means, for example, that inline boxes
19
+ # can use background colors or borders without doing anything special.
20
+ #
21
+ # Usage:
22
+ # : `ruby text_layouter_inline_boxes.rb`
23
+ #
24
+
25
+ require 'hexapdf'
26
+
27
+ include HexaPDF::Layout
28
+
29
+ sample_text = "Lorem ipsum :-) dolor sit amet, consectetur adipiscing
30
+ ;-) elit, sed do eiusmod tempor incididunt :-) ut labore et dolore magna
31
+ aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
32
+ laboris nisi ut aliquip ex ea commodo consequat ;-). Duis aute irure
33
+ dolor in reprehenderit in voluptate velit esse cillum :-) dolore eu
34
+ fugiat nulla pariatur. ".tr("\n", ' ') * 4
35
+
36
+ doc = HexaPDF::Document.new
37
+ emoji_smile = doc.images.add(File.join(__dir__, "emoji-smile.png"))
38
+ emoji_wink = doc.images.add(File.join(__dir__, "emoji-wink.png"))
39
+ size = 10
40
+
41
+ items = sample_text.split(/(:-\)|;-\))/).map do |part|
42
+ case part
43
+ when ':-)'
44
+ style = {background_color: [162, 234, 247], padding: 2}
45
+ InlineBox.create(content_width: size * 2, content_height: size * 2,
46
+ style: style) do |canvas, box|
47
+ canvas.image(emoji_smile, at: [0, 0], width: box.content_width)
48
+ end
49
+ when ';-)'
50
+ style = {padding: 5, margin: [0, 10],
51
+ border: {width: [1, 2], color: [200, 40]}}
52
+ InlineBox.create(content_width: size, content_height: size,
53
+ valign: :top, style: style) do |canvas, box|
54
+ canvas.image(emoji_wink, at: [0, 0], width: box.content_width)
55
+ end
56
+ else
57
+ TextFragment.create(part, font: doc.fonts.add("Times"), font_size: 18)
58
+ end
59
+ end
60
+
61
+ layouter = TextLayouter.new(items: items, width: 500, height: 700)
62
+ layouter.style.align = :justify
63
+ layouter.style.line_spacing(:proportional, 1.5)
64
+ layouter.draw(doc.pages.add.canvas, 50, 800)
65
+
66
+ doc.write("text_layouter_inline_boxes.pdf")
@@ -1,6 +1,6 @@
1
- # ## Text Box Line Wrapping
1
+ # ## Text Layouter - Line Wrapping
2
2
  #
3
- # The [HexaPDF::Layout::TextBox] class can be used to easily lay out text,
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text,
4
4
  # automatically wrapping it appropriately.
5
5
  #
6
6
  # Text is broken only at certain characters:
@@ -30,7 +30,7 @@
30
30
  # non-breaking spaces in "1 0 1".
31
31
  #
32
32
  # Usage:
33
- # : `ruby text_box_line_wrapping.rb`
33
+ # : `ruby text_layout_line_wrapping.rb`
34
34
  #
35
35
 
36
36
  require 'hexapdf'
@@ -45,13 +45,12 @@ text = "Hello! Fly-fishing\nand wand\u{00AD}ering\taround - fanta\u{200B}stic" \
45
45
  x = 10
46
46
  y = 220
47
47
  [30, 60, 100, 160].each do |width|
48
- box = HexaPDF::Layout::TextBox.create(text, width: width,
49
- font: doc.fonts.load("Times"))
50
- _, height = box.fit
51
- box.draw(canvas, x, y)
48
+ layouter = HexaPDF::Layout::TextLayouter.create(text, width: width,
49
+ font: doc.fonts.add("Times"))
50
+ layouter.draw(canvas, x, y)
52
51
  canvas.stroke_color(255, 0, 0).line_width(0.2)
53
- canvas.rectangle(x, y, width, -height).stroke
54
- y -= height + 5
52
+ canvas.rectangle(x, y, width, -layouter.actual_height).stroke
53
+ y -= layouter.actual_height + 5
55
54
  end
56
55
 
57
- doc.write("text_box_line_wrapping.pdf", optimize: true)
56
+ doc.write("text_layouter_line_wrapping.pdf", optimize: true)
@@ -1,8 +1,9 @@
1
- # ## Text Box Shapes
1
+ # ## Text Layouter - Shapes
2
2
  #
3
- # The [HexaPDF::Layout::TextBox] class can be used to easily lay out text, not
4
- # limiting the area to a rectangle but any shape. There is only one restriction:
5
- # In the case of arbitrary shapes the vertical alignment has to be "top".
3
+ # The [HexaPDF::Layout::TextLayouter] class can be used to easily lay out text,
4
+ # not limiting the area to a rectangle but any shape. There is only one
5
+ # restriction: In the case of arbitrary shapes the vertical alignment has to be
6
+ # "top".
6
7
  #
7
8
  # Arbitrary shapes boil down to varying line widths and horizontal offsets from
8
9
  # left. Imagine a circle: If text is fit in a circle, the line widths start at
@@ -12,24 +13,27 @@
12
13
  #
13
14
  # Both, the line widths and the horizontal offsets can be calculated given a
14
15
  # certain height, and this is exactly what HexaPDF uses. If the `width` argument
15
- # to [HexaPDF::Layout::TextBox::new] is an object responding to #call (e.g. a
16
- # lambda), it is used for determining the line widths. And the `x_offsets`
16
+ # to [HexaPDF::Layout::TextLayouter::new] is an object responding to #call (e.g.
17
+ # a lambda), it is used for determining the line widths. And the `x_offsets`
17
18
  # argument can be used in a similar way for the horizontal offsets.
18
19
  #
19
20
  # This example shows text layed out in various shapes, using the above mentioned
20
21
  # techniques.
21
22
  #
22
23
  # Usage:
23
- # : `ruby text_box_shapes.rb`
24
+ # : `ruby text_layouter_shapes.rb`
24
25
  #
25
26
 
26
27
  require 'hexapdf'
27
28
 
29
+ include HexaPDF::Layout
30
+
28
31
  doc = HexaPDF::Document.new
29
32
  page = doc.pages.add
30
33
  canvas = page.canvas
31
34
  canvas.font("Times", size: 10, variant: :bold)
32
35
  canvas.stroke_color(255, 0, 0).line_width(0.2)
36
+ font = doc.fonts.add("Times")
33
37
 
34
38
  sample_text = "Lorem ipsum dolor sit amet, con\u{00AD}sectetur
35
39
  adipis\u{00AD}cing elit, sed do eiusmod tempor incididunt ut labore et
@@ -58,31 +62,31 @@ left_half_circle_offsets = lambda do |height, line_height|
58
62
  end
59
63
 
60
64
  # Left: right half circle
61
- box = HexaPDF::Layout::TextBox.create(sample_text,
62
- width: half_circle_widths,
63
- height: radius * 2,
64
- font: doc.fonts.load("Times"))
65
- box.draw(canvas, 0, circle_top)
65
+ layouter = TextLayouter.create(sample_text,
66
+ width: half_circle_widths,
67
+ height: radius * 2,
68
+ font: font)
69
+ layouter.draw(canvas, 0, circle_top)
66
70
  canvas.circle(0, circle_top - radius, radius).stroke
67
71
 
68
72
  # Center: full circle
69
- box = HexaPDF::Layout::TextBox.create(sample_text,
70
- width: circle_widths,
71
- x_offsets: left_half_circle_offsets,
72
- height: radius * 2,
73
- font: doc.fonts.load("Times"),
74
- align: :justify)
75
- box.draw(canvas, page.box(:media).width / 2.0 - radius, circle_top)
73
+ layouter = TextLayouter.create(sample_text,
74
+ width: circle_widths,
75
+ x_offsets: left_half_circle_offsets,
76
+ height: radius * 2,
77
+ font: font,
78
+ align: :justify)
79
+ layouter.draw(canvas, page.box(:media).width / 2.0 - radius, circle_top)
76
80
  canvas.circle(page.box(:media).width / 2.0, circle_top - radius, radius).stroke
77
81
 
78
82
  # Right: left half circle
79
- box = HexaPDF::Layout::TextBox.create(sample_text,
80
- width: half_circle_widths,
81
- x_offsets: left_half_circle_offsets,
82
- height: radius * 2,
83
- font: doc.fonts.load("Times"),
84
- align: :right)
85
- box.draw(canvas, page.box(:media).width - radius, circle_top)
83
+ layouter = TextLayouter.create(sample_text,
84
+ width: half_circle_widths,
85
+ x_offsets: left_half_circle_offsets,
86
+ height: radius * 2,
87
+ font: font,
88
+ align: :right)
89
+ layouter.draw(canvas, page.box(:media).width - radius, circle_top)
86
90
  canvas.circle(page.box(:media).width, circle_top - radius, radius).stroke
87
91
 
88
92
 
@@ -107,37 +111,37 @@ left_half_diamond_offsets = lambda do |height, line_height|
107
111
  end
108
112
 
109
113
  # Left: right half diamond
110
- box = HexaPDF::Layout::TextBox.create(sample_text,
111
- width: half_diamond_widths,
112
- height: 2 * diamond_width,
113
- font: doc.fonts.load("Times"))
114
- box.draw(canvas, 0, diamond_top)
114
+ layouter = TextLayouter.create(sample_text,
115
+ width: half_diamond_widths,
116
+ height: 2 * diamond_width,
117
+ font: font)
118
+ layouter.draw(canvas, 0, diamond_top)
115
119
  canvas.polyline(0, diamond_top, diamond_width, diamond_top - diamond_width,
116
- 0, diamond_top - 2 * diamond_width).stroke
120
+ 0, diamond_top - 2 * diamond_width).stroke
117
121
 
118
122
  # Center: full diamond
119
- box = HexaPDF::Layout::TextBox.create(sample_text,
120
- width: full_diamond_widths,
121
- x_offsets: left_half_diamond_offsets,
122
- height: 2 * diamond_width,
123
- font: doc.fonts.load("Times"),
124
- align: :justify)
123
+ layouter = TextLayouter.create(sample_text,
124
+ width: full_diamond_widths,
125
+ x_offsets: left_half_diamond_offsets,
126
+ height: 2 * diamond_width,
127
+ font: font,
128
+ align: :justify)
125
129
  left = page.box(:media).width / 2.0 - diamond_width
126
- box.draw(canvas, left, diamond_top)
130
+ layouter.draw(canvas, left, diamond_top)
127
131
  canvas.polyline(left + diamond_width, diamond_top,
128
132
  left + 2 * diamond_width, diamond_top - diamond_width,
129
133
  left + diamond_width, diamond_top - 2 * diamond_width,
130
134
  left, diamond_top - diamond_width).close_subpath.stroke
131
135
 
132
136
  # Right: left half diamond
133
- box = HexaPDF::Layout::TextBox.create(sample_text,
134
- width: half_diamond_widths,
135
- x_offsets: left_half_diamond_offsets,
136
- height: 2 * diamond_width,
137
- font: doc.fonts.load("Times"),
138
- align: :right)
137
+ layouter = TextLayouter.create(sample_text,
138
+ width: half_diamond_widths,
139
+ x_offsets: left_half_diamond_offsets,
140
+ height: 2 * diamond_width,
141
+ font: font,
142
+ align: :right)
139
143
  middle = page.box(:media).width
140
- box.draw(canvas, middle - diamond_width, diamond_top)
144
+ layouter.draw(canvas, middle - diamond_width, diamond_top)
141
145
  canvas.polyline(middle, diamond_top,
142
146
  middle - diamond_width, diamond_top - diamond_width,
143
147
  middle, diamond_top - 2 * diamond_width).stroke
@@ -154,13 +158,13 @@ end
154
158
  sine_wave_widths = lambda do |height, line_height|
155
159
  sine_wave_height + 100 + sine_wave_offsets.call(height, line_height) * -2
156
160
  end
157
- box = HexaPDF::Layout::TextBox.create(sample_text,
158
- width: sine_wave_widths,
159
- x_offsets: sine_wave_offsets,
160
- height: sine_wave_height,
161
- font: doc.fonts.load("Times"),
162
- align: :justify)
161
+ layouter = TextLayouter.create(sample_text,
162
+ width: sine_wave_widths,
163
+ x_offsets: sine_wave_offsets,
164
+ height: sine_wave_height,
165
+ font: font,
166
+ align: :justify)
163
167
  middle = page.box(:media).width / 2.0
164
- box.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)
168
+ layouter.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)
165
169
 
166
- doc.write("text_box_shapes.pdf", optimize: true)
170
+ doc.write("text_layouter_shapes.pdf", optimize: true)