hexapdf 0.43.0 → 0.45.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 +36 -0
- data/examples/027-composer_optional_content.rb +6 -4
- data/examples/030-pdfa.rb +13 -11
- data/lib/hexapdf/composer.rb +23 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/canvas_composer.rb +1 -0
- data/lib/hexapdf/document/files.rb +7 -2
- data/lib/hexapdf/document/layout.rb +15 -3
- data/lib/hexapdf/document/metadata.rb +12 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/layout/box.rb +180 -66
- data/lib/hexapdf/layout/box_fitter.rb +1 -0
- data/lib/hexapdf/layout/column_box.rb +18 -28
- data/lib/hexapdf/layout/container_box.rb +6 -6
- data/lib/hexapdf/layout/frame.rb +13 -94
- data/lib/hexapdf/layout/image_box.rb +4 -4
- data/lib/hexapdf/layout/list_box.rb +13 -31
- data/lib/hexapdf/layout/style.rb +8 -4
- data/lib/hexapdf/layout/table_box.rb +55 -58
- data/lib/hexapdf/layout/text_box.rb +84 -71
- data/lib/hexapdf/layout/text_fragment.rb +1 -1
- data/lib/hexapdf/layout/text_layouter.rb +7 -8
- data/lib/hexapdf/parser.rb +5 -2
- data/lib/hexapdf/rectangle.rb +4 -4
- data/lib/hexapdf/type/file_specification.rb +9 -5
- data/lib/hexapdf/type/form.rb +2 -2
- data/lib/hexapdf/type/graphics_state_parameter.rb +1 -1
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/content/test_canvas_composer.rb +13 -8
- data/test/hexapdf/document/test_files.rb +5 -0
- data/test/hexapdf/document/test_layout.rb +16 -0
- data/test/hexapdf/document/test_metadata.rb +21 -0
- data/test/hexapdf/layout/test_box.rb +93 -37
- data/test/hexapdf/layout/test_box_fitter.rb +7 -0
- data/test/hexapdf/layout/test_column_box.rb +7 -13
- data/test/hexapdf/layout/test_container_box.rb +1 -1
- data/test/hexapdf/layout/test_frame.rb +7 -46
- data/test/hexapdf/layout/test_image_box.rb +14 -6
- data/test/hexapdf/layout/test_list_box.rb +26 -27
- data/test/hexapdf/layout/test_table_box.rb +47 -54
- data/test/hexapdf/layout/test_text_box.rb +83 -83
- data/test/hexapdf/test_composer.rb +20 -5
- data/test/hexapdf/test_parser.rb +8 -0
- data/test/hexapdf/test_serializer.rb +1 -0
- data/test/hexapdf/type/test_file_specification.rb +2 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cd31e769d2a906198da2a4194085cb2a884fa3879442b75add168d7f81d67d8b
|
4
|
+
data.tar.gz: 43c2b4ba4df2f997d470835995f2581aa306c639c63ddc892e648d94605f3b22
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f17a303dba8104974564a213c72c0f0862c520964a3255b575544489ccb5a17a468d093d3970817903c4eb798e888592410db18447d3264d535f3a01a4a94c82
|
7
|
+
data.tar.gz: d5727e2f2bfb5ed827ac92eecc8cd9e0ba73044150ba07d03623dccde43dc29db1cddc2fbce24bfd63268522e7965519e188271c2bc9c00162b1a660fc468b74
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,39 @@
|
|
1
|
+
## 0.45.0 - 2024-06-18
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::Document::Layout#styles] and [HexaPDF::Composer#styles] for defining
|
6
|
+
multiple styles at once
|
7
|
+
|
8
|
+
### Changed
|
9
|
+
|
10
|
+
* [HexaPDF::Layout::Box#fit] to set width/height correctly for boxes with
|
11
|
+
position `:flow`
|
12
|
+
|
13
|
+
### Fixed
|
14
|
+
|
15
|
+
* Regression in [HexaPDF::Layout::ListBox] that leads to missing markers
|
16
|
+
* [HexaPDF::Content::CanvasComposer#draw_box] to handle truncated boxes
|
17
|
+
* [HexaPDF::Layout::TableBox::Cell] to handle too-big content in all cases
|
18
|
+
|
19
|
+
|
20
|
+
## 0.44.0 - 2024-06-05
|
21
|
+
|
22
|
+
### Added
|
23
|
+
|
24
|
+
* Support for specifying the MIME type when embedding files
|
25
|
+
* Support for adding custom XMP metadata
|
26
|
+
|
27
|
+
### Changed
|
28
|
+
|
29
|
+
* **Breaking change**: Refactored the box implementation of the document layout
|
30
|
+
system
|
31
|
+
|
32
|
+
### Fixed
|
33
|
+
|
34
|
+
* Parsing of invalid files with garbage bytes at the end
|
35
|
+
|
36
|
+
|
1
37
|
## 0.43.0 - 2024-05-26
|
2
38
|
|
3
39
|
### Added
|
@@ -16,8 +16,10 @@
|
|
16
16
|
require 'hexapdf'
|
17
17
|
|
18
18
|
HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
|
19
|
-
composer.
|
20
|
-
|
19
|
+
composer.styles(
|
20
|
+
question: {font_size: 16, margin: [0, 0, 16], fill_color: 'hp-blue'},
|
21
|
+
answer: {font: 'ZapfDingbats', fill_color: "green"},
|
22
|
+
)
|
21
23
|
|
22
24
|
all = composer.document.optional_content.ocg('All answers')
|
23
25
|
a1 = composer.document.optional_content.ocg('Answer 1')
|
@@ -38,7 +40,7 @@ HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
|
|
38
40
|
answers.text('Guido van Rossum')
|
39
41
|
answers.multiple do |answer|
|
40
42
|
answer.text('Yukihiro “Matz” Matsumoto', position: :float)
|
41
|
-
answer.text("\u{a0}\u{a0}
|
43
|
+
answer.text("\u{a0}\u{a0}✔", style: :answer,
|
42
44
|
properties: {'optional_content' => a1})
|
43
45
|
end
|
44
46
|
answers.text('Rob Pike')
|
@@ -54,7 +56,7 @@ HexaPDF::Composer.create('composer_optional_content.pdf') do |composer|
|
|
54
56
|
answers.text('1992')
|
55
57
|
answers.multiple do |answer|
|
56
58
|
answer.text('1993', position: :float)
|
57
|
-
answer.text("\u{a0}\u{a0}
|
59
|
+
answer.text("\u{a0}\u{a0}✔", style: :answer,
|
58
60
|
properties: {'optional_content' => a2})
|
59
61
|
end
|
60
62
|
end
|
data/examples/030-pdfa.rb
CHANGED
@@ -8,6 +8,7 @@
|
|
8
8
|
# Usage:
|
9
9
|
# : `ruby pdfa.rb`
|
10
10
|
#
|
11
|
+
|
11
12
|
require 'hexapdf'
|
12
13
|
|
13
14
|
HexaPDF::Composer.create('pdfa.pdf') do |composer|
|
@@ -27,17 +28,18 @@ HexaPDF::Composer.create('pdfa.pdf') do |composer|
|
|
27
28
|
}
|
28
29
|
|
29
30
|
# Define all styles
|
30
|
-
composer.
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
31
|
+
composer.styles(
|
32
|
+
base: {font: 'Lato', font_size: 10, line_spacing: 1.3},
|
33
|
+
top: {font_size: 8},
|
34
|
+
top_box: {padding: [100, 0, 0], margin: [0, 0, 10], border: {width: [0, 0, 1]}},
|
35
|
+
header: {font: 'Lato bold', font_size: 20, margin: [50, 0, 20]},
|
36
|
+
line_items: {border: {width: 1, color: "eee"}, margin: [20, 0]},
|
37
|
+
line_item_cell: {font_size: 8},
|
38
|
+
footer: {border: {width: [1, 0, 0], color: "darkgrey"}, padding: [5, 0, 0],
|
39
|
+
valign: :bottom},
|
40
|
+
footer_heading: {font: 'Lato bold', font_size: 8, padding: [0, 0, 8]},
|
41
|
+
footer_text: {font_size: 8, fill_color: "darkgrey"},
|
42
|
+
)
|
41
43
|
|
42
44
|
# Top part
|
43
45
|
composer.box(:container, style: :top_box) do |container|
|
data/lib/hexapdf/composer.rb
CHANGED
@@ -254,6 +254,28 @@ module HexaPDF
|
|
254
254
|
@document.layout.style(name, base: base, **properties)
|
255
255
|
end
|
256
256
|
|
257
|
+
# :call-seq:
|
258
|
+
# composer.styles -> styles
|
259
|
+
# composer.styles(**mapping) -> styles
|
260
|
+
#
|
261
|
+
# Creates multiple named styles at once if +mapping+ is provided, and returns the style mapping.
|
262
|
+
#
|
263
|
+
# See HexaPDF::Document::Layout#styles for details; this method is just a thin wrapper around
|
264
|
+
# that method.
|
265
|
+
#
|
266
|
+
# Example:
|
267
|
+
#
|
268
|
+
# composer.styles(
|
269
|
+
# base: {font_size: 12, leading: 1.2},
|
270
|
+
# header: {font: 'Helvetica', fill_color: "008"},
|
271
|
+
# header1: {base: :header, font_size: 30}
|
272
|
+
# )
|
273
|
+
#
|
274
|
+
# See: HexaPDF::Layout::Style
|
275
|
+
def styles(**mapping)
|
276
|
+
@document.layout.styles(**mapping)
|
277
|
+
end
|
278
|
+
|
257
279
|
# :call-seq:
|
258
280
|
# composer.page_style(name) -> page_style
|
259
281
|
# composer.page_style(name, **attributes, &template_block) -> page_style
|
@@ -425,6 +447,7 @@ module HexaPDF
|
|
425
447
|
if draw_box
|
426
448
|
@frame.draw(@canvas, result)
|
427
449
|
drawn_on_page = true
|
450
|
+
(box = draw_box; break) unless box
|
428
451
|
elsif !@frame.find_next_region
|
429
452
|
unless drawn_on_page
|
430
453
|
raise HexaPDF::Error, "Box doesn't fit on empty page"
|
@@ -1127,7 +1127,7 @@ module HexaPDF
|
|
1127
1127
|
# canvas.rectangle(x, y, width, height, radius: 0) => canvas
|
1128
1128
|
#
|
1129
1129
|
# Appends a rectangle to the current path as a complete subpath (drawn in counterclockwise
|
1130
|
-
# direction), with the bottom
|
1130
|
+
# direction), with the bottom-left corner specified by +x+ and +y+ and the given +width+ and
|
1131
1131
|
# +height+. Returns +self+.
|
1132
1132
|
#
|
1133
1133
|
# If +radius+ is greater than 0, the corners are rounded with the given radius.
|
@@ -1137,7 +1137,7 @@ module HexaPDF
|
|
1137
1137
|
#
|
1138
1138
|
# If there is no current path when the method is invoked, a new path is automatically begun.
|
1139
1139
|
#
|
1140
|
-
# The current point is set to the bottom
|
1140
|
+
# The current point is set to the bottom-left corner if +radius+ is zero, otherwise it is set
|
1141
1141
|
# to (x, y + radius).
|
1142
1142
|
#
|
1143
1143
|
# Examples:
|
@@ -1720,7 +1720,7 @@ module HexaPDF
|
|
1720
1720
|
# If the filename or the IO specifies a PDF file, the first page of this file is used to
|
1721
1721
|
# create a form XObject which is then drawn.
|
1722
1722
|
#
|
1723
|
-
# The +at+ argument has to be an array containing two numbers specifying the bottom
|
1723
|
+
# The +at+ argument has to be an array containing two numbers specifying the bottom-left
|
1724
1724
|
# corner at which to draw the XObject.
|
1725
1725
|
#
|
1726
1726
|
# If +width+ and +height+ are specified, the drawn XObject will have exactly these
|
@@ -96,6 +96,7 @@ module HexaPDF
|
|
96
96
|
draw_box, box = @frame.split(result)
|
97
97
|
if draw_box
|
98
98
|
@frame.draw(@canvas, result)
|
99
|
+
(box = draw_box; break) unless box
|
99
100
|
elsif !@frame.find_next_region
|
100
101
|
raise HexaPDF::Error, "Frame for canvas composer is full and box doesn't fit anymore"
|
101
102
|
end
|
@@ -70,6 +70,9 @@ module HexaPDF
|
|
70
70
|
# description::
|
71
71
|
# A description of the file.
|
72
72
|
#
|
73
|
+
# mime_type::
|
74
|
+
# The MIME type that should be set for embedded files (so only used if +embed+ is +true+).
|
75
|
+
#
|
73
76
|
# embed::
|
74
77
|
# When an IO object is given, it is always embedded and this option is ignored.
|
75
78
|
#
|
@@ -77,7 +80,7 @@ module HexaPDF
|
|
77
80
|
# only a reference to it is stored.
|
78
81
|
#
|
79
82
|
# See: HexaPDF::Type::FileSpecification
|
80
|
-
def add(file_or_io, name: nil, description: nil, embed: true)
|
83
|
+
def add(file_or_io, name: nil, description: nil, mime_type: nil, embed: true)
|
81
84
|
name ||= File.basename(file_or_io) if file_or_io.kind_of?(String)
|
82
85
|
if name.nil?
|
83
86
|
raise ArgumentError, "The name argument is mandatory when given an IO object"
|
@@ -86,7 +89,9 @@ module HexaPDF
|
|
86
89
|
spec = @document.add({Type: :Filespec})
|
87
90
|
spec.path = name
|
88
91
|
spec[:Desc] = description if description
|
89
|
-
|
92
|
+
if embed || !file_or_io.kind_of?(String)
|
93
|
+
spec.embed(file_or_io, name: name, mime_type: mime_type, register: true)
|
94
|
+
end
|
90
95
|
spec
|
91
96
|
end
|
92
97
|
|
@@ -175,9 +175,6 @@ module HexaPDF
|
|
175
175
|
|
176
176
|
end
|
177
177
|
|
178
|
-
# The mapping of style name (a Symbol) to Layout::Style instance.
|
179
|
-
attr_reader :styles
|
180
|
-
|
181
178
|
# Creates a new Layout object for the given PDF document.
|
182
179
|
def initialize(document)
|
183
180
|
@document = document
|
@@ -219,6 +216,21 @@ module HexaPDF
|
|
219
216
|
style
|
220
217
|
end
|
221
218
|
|
219
|
+
# :call-seq:
|
220
|
+
# layout.styles -> styles
|
221
|
+
# layout.styles(**mapping) -> styles
|
222
|
+
#
|
223
|
+
# Returns the mapping of style names to Layout::Style instances. If +mapping+ is provided,
|
224
|
+
# also defines the given styles using #style.
|
225
|
+
#
|
226
|
+
# The argument +mapping+ needs to be a hash mapping a style name (a Symbol) to style
|
227
|
+
# properties. The special key +:base+ can be used to define the base style. For details see
|
228
|
+
# #style.
|
229
|
+
def styles(**mapping)
|
230
|
+
mapping.each {|name, properties| style(name, **properties) } unless mapping.empty?
|
231
|
+
@styles
|
232
|
+
end
|
233
|
+
|
222
234
|
# Creates an inline box for use together with text fragments.
|
223
235
|
#
|
224
236
|
# The +valign+ argument ist used to specify the vertical alignment of the box within the text
|
@@ -161,6 +161,7 @@ module HexaPDF
|
|
161
161
|
@properties = PREDEFINED_PROPERTIES.transform_values(&:dup)
|
162
162
|
@default_language = document.catalog[:Lang] || 'x-default'
|
163
163
|
@metadata = Hash.new {|h, k| h[k] = {} }
|
164
|
+
@custom_metadata = []
|
164
165
|
write_info_dict(true)
|
165
166
|
write_metadata_stream(true)
|
166
167
|
@document.register_listener(:complete_objects, &method(:write_metadata))
|
@@ -248,6 +249,16 @@ module HexaPDF
|
|
248
249
|
end
|
249
250
|
end
|
250
251
|
|
252
|
+
# Adds the given +data+ string as custom metadata to the XMP document.
|
253
|
+
#
|
254
|
+
# The +data+ string must contain a fully valid 'rdf:Description' element.
|
255
|
+
#
|
256
|
+
# Using this method allows adding metadata like PDF/A schema definitions for which there is no
|
257
|
+
# direct support by HexaPDF.
|
258
|
+
def custom_metadata(data)
|
259
|
+
@custom_metadata << data
|
260
|
+
end
|
261
|
+
|
251
262
|
# :call-seq:
|
252
263
|
# metadata.delete
|
253
264
|
# metadata.delete(ns_prefix)
|
@@ -469,7 +480,7 @@ module HexaPDF
|
|
469
480
|
<?xpacket begin="\u{FEFF}" id="#{SecureRandom.uuid.tr('-', '')}"?>
|
470
481
|
<x:xmpmeta xmlns:x="adobe:ns:meta/">
|
471
482
|
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">
|
472
|
-
#{data}
|
483
|
+
#{data}#{@custom_metadata.empty? ? '' : "\n#{@custom_metadata.join("\n")}"}
|
473
484
|
</rdf:RDF>
|
474
485
|
</x:xmpmeta>
|
475
486
|
<?xpacket end="r"?>
|
@@ -51,7 +51,7 @@ module HexaPDF
|
|
51
51
|
attr_accessor :name
|
52
52
|
|
53
53
|
# Character bounding box as array of four numbers, specifying the x- and y-coordinates of
|
54
|
-
# the bottom
|
54
|
+
# the bottom-left corner and the x- and y-coordinates of the top-right corner.
|
55
55
|
attr_accessor :bbox
|
56
56
|
|
57
57
|
end
|
@@ -63,7 +63,7 @@ module HexaPDF
|
|
63
63
|
attr_accessor :weight
|
64
64
|
|
65
65
|
# The font bounding box as array of four numbers, specifying the x- and y-coordinates of the
|
66
|
-
# bottom
|
66
|
+
# bottom-left corner and the x- and y-coordinates of the top-right corner.
|
67
67
|
attr_accessor :bounding_box
|
68
68
|
|
69
69
|
# The y-value of the top of the capital H (or 0 or nil if the font doesn't contain a capital
|