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