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.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/README.md +1 -1
  4. data/lib/hexapdf/cli/inspect.rb +13 -4
  5. data/lib/hexapdf/composer.rb +14 -0
  6. data/lib/hexapdf/configuration.rb +5 -0
  7. data/lib/hexapdf/document/annotations.rb +60 -2
  8. data/lib/hexapdf/document/layout.rb +45 -6
  9. data/lib/hexapdf/error.rb +11 -3
  10. data/lib/hexapdf/font/true_type/subsetter.rb +15 -2
  11. data/lib/hexapdf/layout/style.rb +101 -7
  12. data/lib/hexapdf/object.rb +2 -2
  13. data/lib/hexapdf/pdf_array.rb +25 -3
  14. data/lib/hexapdf/tokenizer.rb +4 -1
  15. data/lib/hexapdf/type/acro_form/appearance_generator.rb +57 -8
  16. data/lib/hexapdf/type/acro_form/field.rb +1 -0
  17. data/lib/hexapdf/type/acro_form/form.rb +7 -6
  18. data/lib/hexapdf/type/annotation.rb +12 -0
  19. data/lib/hexapdf/type/annotations/appearance_generator.rb +75 -0
  20. data/lib/hexapdf/type/annotations/border_effect.rb +99 -0
  21. data/lib/hexapdf/type/annotations/circle.rb +65 -0
  22. data/lib/hexapdf/type/annotations/interior_color.rb +84 -0
  23. data/lib/hexapdf/type/annotations/line.rb +4 -35
  24. data/lib/hexapdf/type/annotations/square.rb +65 -0
  25. data/lib/hexapdf/type/annotations/square_circle.rb +77 -0
  26. data/lib/hexapdf/type/annotations/widget.rb +50 -20
  27. data/lib/hexapdf/type/annotations.rb +5 -0
  28. data/lib/hexapdf/version.rb +1 -1
  29. data/test/hexapdf/document/test_annotations.rb +22 -0
  30. data/test/hexapdf/document/test_layout.rb +24 -2
  31. data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
  32. data/test/hexapdf/layout/test_style.rb +27 -2
  33. data/test/hexapdf/test_composer.rb +7 -0
  34. data/test/hexapdf/test_object.rb +1 -1
  35. data/test/hexapdf/test_pdf_array.rb +36 -3
  36. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +78 -3
  37. data/test/hexapdf/type/acro_form/test_button_field.rb +7 -6
  38. data/test/hexapdf/type/acro_form/test_field.rb +5 -0
  39. data/test/hexapdf/type/acro_form/test_form.rb +17 -1
  40. data/test/hexapdf/type/annotations/test_appearance_generator.rb +84 -0
  41. data/test/hexapdf/type/annotations/test_border_effect.rb +59 -0
  42. data/test/hexapdf/type/annotations/test_interior_color.rb +37 -0
  43. data/test/hexapdf/type/annotations/test_line.rb +0 -20
  44. data/test/hexapdf/type/annotations/test_widget.rb +35 -0
  45. 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 buttons.
177
+ # Creates the appropriate appearances for push button fields
178
178
  #
179
- # This is currently a dummy implementation raising an error.
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
- raise HexaPDF::Error, "Push button appearance generation not yet supported"
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
- font, font_size, font_color = retrieve_font_information(default_resources)
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 size to be used for a variable text field.
479
- def retrieve_font_information(resources)
480
- font_name, font_size, font_color = @field.parse_default_appearance_string(@widget)
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
- [font, font_size, font_color]
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
@@ -392,6 +392,7 @@ module HexaPDF
392
392
  break # Each annotation dictionary may only appear on one page, see PDF2.0 12.5.2
393
393
  end
394
394
  end
395
+ document.revisions.current.update(self)
395
396
  widget
396
397
  end
397
398
 
@@ -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.reject! do |field|
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 true
574
+ next nil
575
575
  end
576
- next false unless field.key?(:T) # Skip widgets
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 changing the line width and color is done using the included
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