hexapdf 1.2.0 → 1.3.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 +45 -0
- data/README.md +1 -1
- data/lib/hexapdf/cli/inspect.rb +13 -4
- data/lib/hexapdf/composer.rb +14 -0
- data/lib/hexapdf/configuration.rb +5 -0
- data/lib/hexapdf/document/annotations.rb +60 -2
- data/lib/hexapdf/document/layout.rb +45 -6
- data/lib/hexapdf/error.rb +11 -3
- data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
- data/lib/hexapdf/layout/style.rb +101 -7
- data/lib/hexapdf/object.rb +2 -2
- data/lib/hexapdf/pdf_array.rb +25 -3
- data/lib/hexapdf/tokenizer.rb +4 -1
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +7 -6
- data/lib/hexapdf/type/annotation.rb +12 -0
- data/lib/hexapdf/type/annotations/appearance_generator.rb +75 -0
- data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
- data/lib/hexapdf/type/annotations/circle.rb +65 -0
- data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
- data/lib/hexapdf/type/annotations/line.rb +4 -35
- data/lib/hexapdf/type/annotations/square.rb +65 -0
- data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
- data/lib/hexapdf/type/annotations/widget.rb +50 -20
- data/lib/hexapdf/type/annotations.rb +5 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/document/test_annotations.rb +22 -0
- data/test/hexapdf/document/test_layout.rb +24 -2
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/layout/test_style.rb +27 -2
- data/test/hexapdf/test_composer.rb +7 -0
- data/test/hexapdf/test_object.rb +1 -1
- data/test/hexapdf/test_pdf_array.rb +36 -3
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
- data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +17 -1
- data/test/hexapdf/type/annotations/test_appearance_generator.rb +84 -0
- data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
- data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
- data/test/hexapdf/type/annotations/test_line.rb +0 -20
- data/test/hexapdf/type/annotations/test_widget.rb +35 -0
- metadata +9 -2
@@ -174,11 +174,59 @@ module HexaPDF
|
|
174
174
|
|
175
175
|
alias create_radio_button_appearances create_check_box_appearances
|
176
176
|
|
177
|
-
# Creates the appropriate appearances for push
|
177
|
+
# Creates the appropriate appearances for push button fields
|
178
178
|
#
|
179
|
-
#
|
179
|
+
# The following describes how the appearance is built:
|
180
|
+
#
|
181
|
+
# * The widget's rectangle /Rect must be defined.
|
182
|
+
#
|
183
|
+
# * If the font size (used for the caption) is zero, a font size of
|
184
|
+
# +acro_form.default_font_size+ is used.
|
185
|
+
#
|
186
|
+
# * The line width, style and color of the rectangle are taken from the widget's border
|
187
|
+
# style. See HexaPDF::Type::Annotations::Widget#border_style.
|
188
|
+
#
|
189
|
+
# * The background color is determined by the widget's background color. See
|
190
|
+
# HexaPDF::Type::Annotations::Widget#background_color.
|
180
191
|
def create_push_button_appearances
|
181
|
-
|
192
|
+
default_resources = @document.acro_form(create: true).default_resources
|
193
|
+
border_style = @widget.border_style
|
194
|
+
padding = border_style.width
|
195
|
+
marker_style = @widget.marker_style
|
196
|
+
font = retrieve_font_information(marker_style.font_name, default_resources)
|
197
|
+
|
198
|
+
@widget[:AS] = :N
|
199
|
+
@widget.flag(:print)
|
200
|
+
@widget.unflag(:hidden)
|
201
|
+
rect = @widget[:Rect]
|
202
|
+
|
203
|
+
width, height, matrix = perform_rotation(rect.width, rect.height)
|
204
|
+
|
205
|
+
form = (@widget[:AP] ||= {})[:N] ||= @document.add({Type: :XObject, Subtype: :Form})
|
206
|
+
# Wrap existing object in Form class in case the PDF writer didn't include the /Subtype
|
207
|
+
# key or the type of the object is wrong; we can do this since we know this has to be a
|
208
|
+
# Form object
|
209
|
+
unless form.type == :XObject && form[:Subtype] == :Form
|
210
|
+
form = @document.wrap(form, type: :XObject, subtype: :Form)
|
211
|
+
end
|
212
|
+
form.value.replace({Type: :XObject, Subtype: :Form, BBox: [0, 0, width, height],
|
213
|
+
Matrix: matrix, Resources: HexaPDF::Object.deep_copy(default_resources)})
|
214
|
+
form.contents = ''
|
215
|
+
|
216
|
+
canvas = form.canvas
|
217
|
+
apply_background_and_border(border_style, canvas)
|
218
|
+
|
219
|
+
style = HexaPDF::Layout::Style.new(font: font, font_size: marker_style.size,
|
220
|
+
fill_color: marker_style.color)
|
221
|
+
if (text = marker_style.style) && text.kind_of?(String)
|
222
|
+
items = @document.layout.text_fragments(marker_style.style, style: style)
|
223
|
+
layouter = Layout::TextLayouter.new(style)
|
224
|
+
layouter.style.text_align(:center).text_valign(:center).line_spacing(:proportional, 1.25)
|
225
|
+
result = layouter.fit(items, width - 2 * padding, height - 2 * padding)
|
226
|
+
unless result.lines.empty?
|
227
|
+
result.draw(canvas, padding, height - padding)
|
228
|
+
end
|
229
|
+
end
|
182
230
|
end
|
183
231
|
|
184
232
|
# Creates the appropriate appearances for text fields, combo box fields and list box fields.
|
@@ -207,7 +255,8 @@ module HexaPDF
|
|
207
255
|
# Note: Rich text fields are currently not supported!
|
208
256
|
def create_text_appearances
|
209
257
|
default_resources = @document.acro_form.default_resources
|
210
|
-
|
258
|
+
font_name, font_size, font_color = @field.parse_default_appearance_string(@widget)
|
259
|
+
font = retrieve_font_information(font_name, default_resources)
|
211
260
|
style = HexaPDF::Layout::Style.new(font: font, font_size: font_size, fill_color: font_color)
|
212
261
|
border_style = @widget.border_style
|
213
262
|
padding = [1, border_style.width].max
|
@@ -475,9 +524,9 @@ module HexaPDF
|
|
475
524
|
end
|
476
525
|
end
|
477
526
|
|
478
|
-
# Returns the font wrapper and font
|
479
|
-
|
480
|
-
|
527
|
+
# Returns the font wrapper, font size and font color to be used for variable text fields and
|
528
|
+
# push button captions.
|
529
|
+
def retrieve_font_information(font_name, resources)
|
481
530
|
font_object = resources.font(font_name) rescue nil
|
482
531
|
font = font_object&.font_wrapper
|
483
532
|
unless font
|
@@ -493,7 +542,7 @@ module HexaPDF
|
|
493
542
|
raise(HexaPDF::Error, "Font #{font_name} of the AcroForm's default resources not usable")
|
494
543
|
end
|
495
544
|
end
|
496
|
-
|
545
|
+
font
|
497
546
|
end
|
498
547
|
|
499
548
|
# Calculates the font size for single line text fields using auto-sizing, based on the font
|
@@ -568,12 +568,12 @@ module HexaPDF
|
|
568
568
|
seen = {} # used for combining field
|
569
569
|
|
570
570
|
validate_array = lambda do |parent, container|
|
571
|
-
container.
|
571
|
+
container.map! do |field|
|
572
572
|
if !field.kind_of?(HexaPDF::Object) || !field.kind_of?(HexaPDF::Dictionary) || field.null?
|
573
573
|
yield("Invalid object in AcroForm field hierarchy", true)
|
574
|
-
next
|
574
|
+
next nil
|
575
575
|
end
|
576
|
-
next
|
576
|
+
next field unless field.key?(:T) # Skip widgets
|
577
577
|
|
578
578
|
field = Field.wrap(document, field)
|
579
579
|
reject = false
|
@@ -597,14 +597,15 @@ module HexaPDF
|
|
597
597
|
widget[:Parent] = other_field
|
598
598
|
kids << widget
|
599
599
|
end
|
600
|
+
document.delete(field)
|
600
601
|
reject = true
|
601
602
|
elsif !reject
|
602
603
|
seen[name] = field
|
603
604
|
end
|
604
605
|
|
605
|
-
validate_array.call(field, field[:Kids]) if field.key?(:Kids)
|
606
|
-
reject
|
607
|
-
end
|
606
|
+
validate_array.call(field, field[:Kids]) if !field.null? && field.key?(:Kids)
|
607
|
+
reject ? nil : field
|
608
|
+
end.compact!
|
608
609
|
end
|
609
610
|
validate_array.call(nil, root_fields)
|
610
611
|
|
@@ -113,6 +113,18 @@ module HexaPDF
|
|
113
113
|
|
114
114
|
end
|
115
115
|
|
116
|
+
# Border effect dictionary used by square, circle and polygon annotation types.
|
117
|
+
#
|
118
|
+
# See: PDF2.0 s12.5.4
|
119
|
+
class BorderEffect < Dictionary
|
120
|
+
|
121
|
+
define_type :XXBorderEffect
|
122
|
+
|
123
|
+
define_field :S, type: Symbol, default: :S, allowed_values: [:C, :S]
|
124
|
+
define_field :I, type: Numeric, default: 0, allowed_values: [0, 1, 2]
|
125
|
+
|
126
|
+
end
|
127
|
+
|
116
128
|
extend Utils::BitField
|
117
129
|
|
118
130
|
define_type :Annot
|
@@ -73,6 +73,8 @@ module HexaPDF
|
|
73
73
|
def create_appearance
|
74
74
|
case @annot[:Subtype]
|
75
75
|
when :Line then create_line_appearance
|
76
|
+
when :Square then create_square_circle_appearance(:square)
|
77
|
+
when :Circle then create_square_circle_appearance(:circle)
|
76
78
|
else
|
77
79
|
raise HexaPDF::Error, "Appearance regeneration for #{@annot[:Subtype]} not yet supported"
|
78
80
|
end
|
@@ -224,6 +226,79 @@ module HexaPDF
|
|
224
226
|
cap_result.draw(canvas, cap_x + 1, cap_y) if captioned
|
225
227
|
end
|
226
228
|
|
229
|
+
# Creates the appropriate appearance for a square or circle annotation depending on the
|
230
|
+
# given +type+ (which can either be +:square+ or +:circle+).
|
231
|
+
#
|
232
|
+
# The cloudy border effect is not supported.
|
233
|
+
#
|
234
|
+
# See: HexaPDF::Type::Annotations::Square, HexaPDF::Type::Annotations::Circle
|
235
|
+
def create_square_circle_appearance(type)
|
236
|
+
# Prepare the annotation
|
237
|
+
form = (@annot[:AP] ||= {})[:N] ||=
|
238
|
+
@document.add({Type: :XObject, Subtype: :Form, BBox: [0, 0, 0, 0]})
|
239
|
+
form.contents = ""
|
240
|
+
@annot.flag(:print)
|
241
|
+
@annot.unflag(:hidden)
|
242
|
+
|
243
|
+
rect = @annot[:Rect]
|
244
|
+
x, y, w, h = rect.left, rect.bottom, rect.width, rect.height
|
245
|
+
border_style = @annot.border_style
|
246
|
+
interior_color = @annot.interior_color
|
247
|
+
opacity = @annot.opacity
|
248
|
+
|
249
|
+
# Take the differences array into account. If it exists, the boundary of the actual
|
250
|
+
# rectangle is the one with the differences applied to /Rect.
|
251
|
+
#
|
252
|
+
# If the differences array doesn't exist, we assume that the /Rect is the rectangle we
|
253
|
+
# want to draw, with the line width split on both side (like with Canvas#rectangle). In
|
254
|
+
# this case we need to update /Rect accordingly so that the line width on the outside is
|
255
|
+
# correctly shown.
|
256
|
+
line_width_adjustment = border_style.width / 2.0
|
257
|
+
if (rd = @annot[:RD])
|
258
|
+
x += rd[0]
|
259
|
+
y += rd[3]
|
260
|
+
w -= rd[0] + rd[2]
|
261
|
+
h -= rd[1] + rd[3]
|
262
|
+
else
|
263
|
+
@annot[:RD] = [0, 0, 0, 0]
|
264
|
+
x = rect.left -= line_width_adjustment
|
265
|
+
y = rect.bottom -= line_width_adjustment
|
266
|
+
w = rect.width += line_width_adjustment
|
267
|
+
h = rect.height += line_width_adjustment
|
268
|
+
end
|
269
|
+
x += line_width_adjustment
|
270
|
+
y += line_width_adjustment
|
271
|
+
w -= 2 * line_width_adjustment
|
272
|
+
h -= 2 * line_width_adjustment
|
273
|
+
|
274
|
+
x -= rect.left
|
275
|
+
y -= rect.bottom
|
276
|
+
form[:BBox] = [0, 0, rect.width, rect.height]
|
277
|
+
|
278
|
+
if border_style.color || interior_color
|
279
|
+
canvas = form.canvas
|
280
|
+
canvas.opacity(**opacity.to_h)
|
281
|
+
canvas.stroke_color(border_style.color) if border_style.color
|
282
|
+
canvas.fill_color(interior_color) if interior_color
|
283
|
+
canvas.line_width(border_style.width)
|
284
|
+
canvas.line_dash_pattern(border_style.style) if border_style.style.kind_of?(Array)
|
285
|
+
|
286
|
+
if type == :square
|
287
|
+
canvas.rectangle(x, y, w, h)
|
288
|
+
else
|
289
|
+
canvas.ellipse(x + w / 2.0, y + h / 2.0, a: w / 2.0, b: h / 2.0)
|
290
|
+
end
|
291
|
+
|
292
|
+
if border_style.color && interior_color
|
293
|
+
canvas.fill_stroke
|
294
|
+
elsif border_style.color
|
295
|
+
canvas.stroke
|
296
|
+
else
|
297
|
+
canvas.fill
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
227
302
|
# Draws the line ending style +type+ at the position (+x+, +y+) and returns +true+ if the
|
228
303
|
# shape needs to be filled.
|
229
304
|
#
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/type/annotations'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Type
|
41
|
+
module Annotations
|
42
|
+
|
43
|
+
# This module provides a convenience method for getting and setting the border effect for
|
44
|
+
# square, circle and polygon annotations.
|
45
|
+
#
|
46
|
+
# See: PDF2.0 s12.5.4
|
47
|
+
module BorderEffect
|
48
|
+
|
49
|
+
# :call-seq:
|
50
|
+
# annot.border_effect => border_effect
|
51
|
+
# annot.border_effect(type) => annot
|
52
|
+
#
|
53
|
+
# Returns the border effect of the annotation when no argument is given. Otherwise sets the
|
54
|
+
# border effect of the annotation and returns self.
|
55
|
+
#
|
56
|
+
# The argument type can have the following values:
|
57
|
+
#
|
58
|
+
# +:none+:: No border effect is used.
|
59
|
+
#
|
60
|
+
# +:cloudy+:: The border appears "cloudy" (as a series of convex curved line segments).
|
61
|
+
#
|
62
|
+
# +:cloudier+:: Like +:cloudy+ but more intense.
|
63
|
+
#
|
64
|
+
# +:cloudiest+:: Like +:cloudier+ but still more intense.
|
65
|
+
def border_effect(type = :UNSET)
|
66
|
+
if type == :UNSET
|
67
|
+
be = self[:BE]
|
68
|
+
if !be || be[:S] != :C
|
69
|
+
:none
|
70
|
+
else
|
71
|
+
case be[:I]
|
72
|
+
when 0 then :cloudy
|
73
|
+
when 1 then :cloudier
|
74
|
+
when 2 then :cloudiest
|
75
|
+
else :cloudy
|
76
|
+
end
|
77
|
+
end
|
78
|
+
else
|
79
|
+
case type
|
80
|
+
when nil, :none
|
81
|
+
delete(:BE)
|
82
|
+
when :cloudy
|
83
|
+
self[:BE] = {S: :C, I: 0}
|
84
|
+
when :cloudier
|
85
|
+
self[:BE] = {S: :C, I: 1}
|
86
|
+
when :cloudiest
|
87
|
+
self[:BE] = {S: :C, I: 2}
|
88
|
+
else
|
89
|
+
raise ArgumentError, "Unknown value #{type} for type argument"
|
90
|
+
end
|
91
|
+
self
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/type/annotations'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Type
|
41
|
+
module Annotations
|
42
|
+
|
43
|
+
# A circle annotation displays an ellipse inside the annotation rectangle (the "circle" name
|
44
|
+
# defined by the PDF specification is a bit misleading).
|
45
|
+
#
|
46
|
+
# Also see SquareCircle for more information.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# #>pdf-small
|
51
|
+
# doc.annotations.create_ellipse(doc.pages[0], 50, 50, a: 30, b: 20).
|
52
|
+
# border_style(color: "hp-blue", width: 2, style: [3, 1]).
|
53
|
+
# interior_color("hp-orange").
|
54
|
+
# regenerate_appearance
|
55
|
+
#
|
56
|
+
# See: PDF2.0 s12.5.6.8, HexaPDF::Type::Annotations::SquareCircle,
|
57
|
+
class Circle < SquareCircle
|
58
|
+
|
59
|
+
define_field :Subtype, type: Symbol, required: true, default: :Circle
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/type/annotations'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Type
|
41
|
+
module Annotations
|
42
|
+
|
43
|
+
# This module provides a convenience method for getting and setting the interior color for
|
44
|
+
# various annotations.
|
45
|
+
#
|
46
|
+
# See: PDF2.0 s12.5
|
47
|
+
module InteriorColor
|
48
|
+
|
49
|
+
# :call-seq:
|
50
|
+
# line.interior_color => color or nil
|
51
|
+
# line.interior_color(*color) => line
|
52
|
+
#
|
53
|
+
# Returns the interior color or +nil+ (in case the interior color should be transparent)
|
54
|
+
# when no argument is given. Otherwise sets the interior color and returns self.
|
55
|
+
#
|
56
|
+
# How the interior color is used depends on the concrete annotation type. For line
|
57
|
+
# annotations, for example, it is the color to fill the line endings
|
58
|
+
#
|
59
|
+
# +color+:: The interior color. See
|
60
|
+
# HexaPDF::Content::ColorSpace.device_color_from_specification for information on
|
61
|
+
# the allowed arguments.
|
62
|
+
#
|
63
|
+
# If the special value +:transparent+ is used when setting the color, no color is
|
64
|
+
# used for filling the line endings.
|
65
|
+
def interior_color(*color)
|
66
|
+
if color.empty?
|
67
|
+
color = self[:IC]
|
68
|
+
color && !color.empty? ? Content::ColorSpace.prenormalized_device_color(color.value) : nil
|
69
|
+
else
|
70
|
+
color = if color.length == 1 && color.first == :transparent
|
71
|
+
[]
|
72
|
+
else
|
73
|
+
Content::ColorSpace.device_color_from_specification(color).components
|
74
|
+
end
|
75
|
+
self[:IC] = color
|
76
|
+
self
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -43,10 +43,10 @@ module HexaPDF
|
|
43
43
|
# A line annotation is a markup annotation that displays a single straight line.
|
44
44
|
#
|
45
45
|
# The style of the line annotation, like adding leader lines, changing the colors and so on,
|
46
|
-
# can be customized using the provided convenience methods
|
46
|
+
# can be customized using the provided convenience methods and those from the included
|
47
|
+
# modules.
|
47
48
|
#
|
48
|
-
# Note that
|
49
|
-
# BorderStyling#border_style. While that method allows special styling of the line (like
|
49
|
+
# Note that while BorderStyling#border_style allows special styling of the line (like
|
50
50
|
# :beveled), only a simple line dash pattern is supported by the line annotation.
|
51
51
|
#
|
52
52
|
# Example:
|
@@ -70,6 +70,7 @@ module HexaPDF
|
|
70
70
|
class Line < MarkupAnnotation
|
71
71
|
|
72
72
|
include BorderStyling
|
73
|
+
include InteriorColor
|
73
74
|
|
74
75
|
define_field :Subtype, type: Symbol, required: true, default: :Line
|
75
76
|
define_field :L, type: PDFArray, required: true
|
@@ -272,38 +273,6 @@ module HexaPDF
|
|
272
273
|
end
|
273
274
|
end
|
274
275
|
|
275
|
-
# :call-seq:
|
276
|
-
# line.interior_color => color or nil
|
277
|
-
# line.interior_color(*color) => line
|
278
|
-
#
|
279
|
-
# Returns the interior color or +nil+ (in case the interior color should be transparent)
|
280
|
-
# when no argument is given. Otherwise sets the interior color and returns self.
|
281
|
-
#
|
282
|
-
# The interior color is used to fill the line endings depending on the line ending styles.
|
283
|
-
#
|
284
|
-
# +color+:: The interior color. See
|
285
|
-
# HexaPDF::Content::ColorSpace.device_color_from_specification for information on
|
286
|
-
# the allowed arguments.
|
287
|
-
#
|
288
|
-
# If the special value +:transparent+ is used when setting the color, no color is
|
289
|
-
# used for filling the line endings.
|
290
|
-
#
|
291
|
-
# Also see: #line_ending_style
|
292
|
-
def interior_color(*color)
|
293
|
-
if color.empty?
|
294
|
-
color = self[:IC]
|
295
|
-
color && !color.empty? ? Content::ColorSpace.prenormalized_device_color(color.value) : nil
|
296
|
-
else
|
297
|
-
color = if color.length == 1 && color.first == :transparent
|
298
|
-
[]
|
299
|
-
else
|
300
|
-
Content::ColorSpace.device_color_from_specification(color).components
|
301
|
-
end
|
302
|
-
self[:IC] = color
|
303
|
-
self
|
304
|
-
end
|
305
|
-
end
|
306
|
-
|
307
276
|
# :call-seq:
|
308
277
|
# line.leader_line_length => leader_line_length
|
309
278
|
# line.leader_line_length(length) => line
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# -*- encoding: utf-8; frozen_string_literal: true -*-
|
2
|
+
#
|
3
|
+
#--
|
4
|
+
# This file is part of HexaPDF.
|
5
|
+
#
|
6
|
+
# HexaPDF - A Versatile PDF Creation and Manipulation Library For Ruby
|
7
|
+
# Copyright (C) 2014-2025 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/type/annotations'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Type
|
41
|
+
module Annotations
|
42
|
+
|
43
|
+
# A square annotation displays a rectangle inside the annotation rectangle (the "square" name
|
44
|
+
# defined by the PDF specification is a bit misleading).
|
45
|
+
#
|
46
|
+
# Also see SquareCircle for more information.
|
47
|
+
#
|
48
|
+
# Example:
|
49
|
+
#
|
50
|
+
# #>pdf-small
|
51
|
+
# doc.annotations.create_rectangle(doc.pages[0], 20, 30, 60, 40).
|
52
|
+
# border_style(color: "hp-blue", width: 2, style: [3, 1]).
|
53
|
+
# interior_color("hp-orange").
|
54
|
+
# regenerate_appearance
|
55
|
+
#
|
56
|
+
# See: PDF2.0 s12.5.6.8, HexaPDF::Type::Annotations::SquareCircle,
|
57
|
+
class Square < SquareCircle
|
58
|
+
|
59
|
+
define_field :Subtype, type: Symbol, required: true, default: :Square
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|