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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +76 -2
- data/CONTRIBUTERS +1 -1
- data/Rakefile +1 -1
- data/VERSION +1 -1
- data/examples/boxes.rb +68 -0
- data/examples/graphics.rb +12 -12
- data/examples/{text_box_alignment.rb → text_layouter_alignment.rb} +14 -14
- data/examples/text_layouter_inline_boxes.rb +66 -0
- data/examples/{text_box_line_wrapping.rb → text_layouter_line_wrapping.rb} +9 -10
- data/examples/{text_box_shapes.rb → text_layouter_shapes.rb} +58 -54
- data/examples/text_layouter_styling.rb +125 -0
- data/examples/truetype.rb +5 -7
- data/lib/hexapdf/cli/command.rb +1 -0
- data/lib/hexapdf/configuration.rb +170 -106
- data/lib/hexapdf/content/canvas.rb +41 -36
- data/lib/hexapdf/content/graphics_state.rb +15 -0
- data/lib/hexapdf/content/operator.rb +1 -1
- data/lib/hexapdf/dictionary.rb +20 -8
- data/lib/hexapdf/dictionary_fields.rb +8 -6
- data/lib/hexapdf/document.rb +25 -26
- data/lib/hexapdf/document/fonts.rb +4 -4
- data/lib/hexapdf/document/images.rb +2 -2
- data/lib/hexapdf/document/pages.rb +16 -16
- data/lib/hexapdf/encryption/security_handler.rb +41 -9
- data/lib/hexapdf/filter/flate_decode.rb +1 -1
- data/lib/hexapdf/filter/lzw_decode.rb +1 -1
- data/lib/hexapdf/filter/predictor.rb +7 -1
- data/lib/hexapdf/font/true_type/font.rb +20 -0
- data/lib/hexapdf/font/type1/font.rb +23 -0
- data/lib/hexapdf/font_loader.rb +1 -0
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -3
- data/lib/hexapdf/font_loader/from_file.rb +65 -0
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -2
- data/lib/hexapdf/layout/box.rb +146 -0
- data/lib/hexapdf/layout/inline_box.rb +40 -31
- data/lib/hexapdf/layout/{line_fragment.rb → line.rb} +12 -13
- data/lib/hexapdf/layout/style.rb +630 -41
- data/lib/hexapdf/layout/text_fragment.rb +80 -12
- data/lib/hexapdf/layout/{text_box.rb → text_layouter.rb} +164 -109
- data/lib/hexapdf/number_tree_node.rb +1 -1
- data/lib/hexapdf/parser.rb +4 -1
- data/lib/hexapdf/revisions.rb +11 -4
- data/lib/hexapdf/stream.rb +8 -9
- data/lib/hexapdf/tokenizer.rb +5 -3
- data/lib/hexapdf/type.rb +3 -0
- data/lib/hexapdf/type/action.rb +56 -0
- data/lib/hexapdf/type/actions.rb +52 -0
- data/lib/hexapdf/type/actions/go_to.rb +52 -0
- data/lib/hexapdf/type/actions/go_to_r.rb +54 -0
- data/lib/hexapdf/type/actions/launch.rb +73 -0
- data/lib/hexapdf/type/actions/uri.rb +65 -0
- data/lib/hexapdf/type/annotation.rb +85 -0
- data/lib/hexapdf/type/annotations.rb +51 -0
- data/lib/hexapdf/type/annotations/link.rb +70 -0
- data/lib/hexapdf/type/annotations/markup_annotation.rb +70 -0
- data/lib/hexapdf/type/annotations/text.rb +81 -0
- data/lib/hexapdf/type/catalog.rb +3 -1
- data/lib/hexapdf/type/embedded_file.rb +6 -11
- data/lib/hexapdf/type/file_specification.rb +4 -6
- data/lib/hexapdf/type/font.rb +3 -1
- data/lib/hexapdf/type/font_descriptor.rb +18 -16
- data/lib/hexapdf/type/form.rb +3 -1
- data/lib/hexapdf/type/graphics_state_parameter.rb +3 -1
- data/lib/hexapdf/type/image.rb +4 -2
- data/lib/hexapdf/type/info.rb +2 -5
- data/lib/hexapdf/type/names.rb +2 -5
- data/lib/hexapdf/type/object_stream.rb +2 -1
- data/lib/hexapdf/type/page.rb +14 -1
- data/lib/hexapdf/type/page_tree_node.rb +9 -6
- data/lib/hexapdf/type/resources.rb +2 -5
- data/lib/hexapdf/type/trailer.rb +2 -5
- data/lib/hexapdf/type/viewer_preferences.rb +2 -5
- data/lib/hexapdf/type/xref_stream.rb +3 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +3 -1
- data/test/hexapdf/content/test_canvas.rb +29 -3
- data/test/hexapdf/content/test_graphics_state.rb +11 -0
- data/test/hexapdf/content/test_operator.rb +3 -2
- data/test/hexapdf/document/test_fonts.rb +8 -8
- data/test/hexapdf/document/test_images.rb +4 -12
- data/test/hexapdf/document/test_pages.rb +7 -7
- data/test/hexapdf/encryption/test_security_handler.rb +1 -5
- data/test/hexapdf/filter/test_predictor.rb +40 -12
- data/test/hexapdf/font/true_type/test_font.rb +16 -0
- data/test/hexapdf/font/type1/test_font.rb +30 -0
- data/test/hexapdf/font_loader/test_from_file.rb +29 -0
- data/test/hexapdf/font_loader/test_standard14.rb +4 -3
- data/test/hexapdf/layout/test_box.rb +104 -0
- data/test/hexapdf/layout/test_inline_box.rb +24 -10
- data/test/hexapdf/layout/{test_line_fragment.rb → test_line.rb} +9 -9
- data/test/hexapdf/layout/test_style.rb +519 -31
- data/test/hexapdf/layout/test_text_fragment.rb +136 -15
- data/test/hexapdf/layout/{test_text_box.rb → test_text_layouter.rb} +224 -144
- data/test/hexapdf/layout/test_text_shaper.rb +1 -1
- data/test/hexapdf/test_configuration.rb +12 -6
- data/test/hexapdf/test_dictionary.rb +27 -2
- data/test/hexapdf/test_dictionary_fields.rb +10 -1
- data/test/hexapdf/test_document.rb +14 -13
- data/test/hexapdf/test_parser.rb +12 -0
- data/test/hexapdf/test_revisions.rb +34 -0
- data/test/hexapdf/test_stream.rb +1 -1
- data/test/hexapdf/test_type.rb +18 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/actions/test_launch.rb +24 -0
- data/test/hexapdf/type/actions/test_uri.rb +23 -0
- data/test/hexapdf/type/annotations/test_link.rb +19 -0
- data/test/hexapdf/type/annotations/test_markup_annotation.rb +22 -0
- data/test/hexapdf/type/annotations/test_text.rb +38 -0
- data/test/hexapdf/type/test_annotation.rb +38 -0
- data/test/hexapdf/type/test_file_specification.rb +0 -7
- data/test/hexapdf/type/test_info.rb +0 -5
- data/test/hexapdf/type/test_page.rb +14 -0
- data/test/hexapdf/type/test_page_tree_node.rb +4 -1
- data/test/hexapdf/type/test_trailer.rb +0 -4
- data/test/test_helper.rb +6 -3
- metadata +36 -15
- data/examples/text_box_inline_boxes.rb +0 -56
- data/examples/text_box_styling.rb +0 -72
- data/test/hexapdf/type/test_embedded_file.rb +0 -16
- data/test/hexapdf/type/test_names.rb +0 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 401d9b20b7b9305c334bad4951cb403ea6f4c919
|
|
4
|
+
data.tar.gz: 40cd8335bd517c1f772c3a26ebf8bb0c7a257492
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1697291f44f1b7882d0b489af252b8b9cdcaae9416c770f88f325833e75f18ac6d1ef0f8fd03a47af76f1b1451475a97993f97381117e3d3c4df6a592b1e4d4d
|
|
7
|
+
data.tar.gz: a52680e4ad7f3a7568115e8d4657a1c934f9273e0ef513b023d48570d887ba4b748be4ac91ed2162a0d88f07ca08e63ae7dba7b1fa8c33692e7d6e8bc63dd09d
|
data/CHANGELOG.md
CHANGED
|
@@ -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
|
-
*
|
|
6
|
-
*
|
|
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
|
data/CONTRIBUTERS
CHANGED
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.
|
|
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.
|
|
1
|
+
0.6.0
|
data/examples/boxes.rb
ADDED
|
@@ -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)
|
data/examples/graphics.rb
CHANGED
|
@@ -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 =
|
|
264
|
-
freq = 0.
|
|
265
|
-
0.upto(
|
|
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 *
|
|
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
|
|
1
|
+
# ## Text Layouter - Alignment
|
|
2
2
|
#
|
|
3
|
-
# The [HexaPDF::Layout::
|
|
4
|
-
# a rectangular area, with various horizontal and vertical alignment
|
|
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
|
|
7
|
-
# [HexaPDF::Layout::Style#
|
|
8
|
-
#
|
|
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
|
|
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
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
42
|
-
|
|
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("
|
|
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
|
|
1
|
+
# ## Text Layouter - Line Wrapping
|
|
2
2
|
#
|
|
3
|
-
# The [HexaPDF::Layout::
|
|
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
|
|
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
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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, -
|
|
54
|
-
y -=
|
|
52
|
+
canvas.rectangle(x, y, width, -layouter.actual_height).stroke
|
|
53
|
+
y -= layouter.actual_height + 5
|
|
55
54
|
end
|
|
56
55
|
|
|
57
|
-
doc.write("
|
|
56
|
+
doc.write("text_layouter_line_wrapping.pdf", optimize: true)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
# ## Text
|
|
1
|
+
# ## Text Layouter - Shapes
|
|
2
2
|
#
|
|
3
|
-
# The [HexaPDF::Layout::
|
|
4
|
-
# limiting the area to a rectangle but any shape. There is only one
|
|
5
|
-
# In the case of arbitrary shapes the vertical alignment has to be
|
|
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::
|
|
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
|
|
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
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
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
|
-
|
|
120
|
+
0, diamond_top - 2 * diamond_width).stroke
|
|
117
121
|
|
|
118
122
|
# Center: full diamond
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
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
|
-
|
|
168
|
+
layouter.draw(canvas, middle - (sine_wave_height + 100) / 2, sine_wave_top)
|
|
165
169
|
|
|
166
|
-
doc.write("
|
|
170
|
+
doc.write("text_layouter_shapes.pdf", optimize: true)
|