hexapdf 0.5.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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)