hexapdf 0.17.2 → 0.19.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/lib/hexapdf/cli/command.rb +7 -1
  4. data/lib/hexapdf/content/canvas.rb +2 -2
  5. data/lib/hexapdf/content/color_space.rb +19 -0
  6. data/lib/hexapdf/content/graphic_object/arc.rb +2 -2
  7. data/lib/hexapdf/content/graphic_object/endpoint_arc.rb +4 -4
  8. data/lib/hexapdf/content/graphic_object/solid_arc.rb +111 -10
  9. data/lib/hexapdf/content/graphics_state.rb +167 -25
  10. data/lib/hexapdf/dictionary.rb +1 -1
  11. data/lib/hexapdf/dictionary_fields.rb +1 -1
  12. data/lib/hexapdf/document/signatures.rb +221 -0
  13. data/lib/hexapdf/encryption/security_handler.rb +3 -1
  14. data/lib/hexapdf/layout/style.rb +2 -1
  15. data/lib/hexapdf/object.rb +18 -0
  16. data/lib/hexapdf/parser.rb +3 -0
  17. data/lib/hexapdf/serializer.rb +2 -0
  18. data/lib/hexapdf/task/optimize.rb +46 -3
  19. data/lib/hexapdf/type/acro_form/appearance_generator.rb +4 -4
  20. data/lib/hexapdf/type/acro_form/form.rb +39 -28
  21. data/lib/hexapdf/type/acro_form/variable_text_field.rb +56 -18
  22. data/lib/hexapdf/type/annotations/widget.rb +3 -15
  23. data/lib/hexapdf/type/font.rb +5 -0
  24. data/lib/hexapdf/type/font_type3.rb +20 -0
  25. data/lib/hexapdf/type/signature/adbe_pkcs7_detached.rb +125 -0
  26. data/lib/hexapdf/version.rb +1 -1
  27. data/test/hexapdf/content/graphic_object/test_endpoint_arc.rb +16 -8
  28. data/test/hexapdf/content/test_color_space.rb +26 -0
  29. data/test/hexapdf/content/test_graphics_state.rb +9 -1
  30. data/test/hexapdf/content/test_operator.rb +8 -3
  31. data/test/hexapdf/encryption/test_security_handler.rb +3 -1
  32. data/test/hexapdf/layout/test_style.rb +11 -0
  33. data/test/hexapdf/task/test_optimize.rb +26 -0
  34. data/test/hexapdf/test_dictionary.rb +1 -0
  35. data/test/hexapdf/test_dictionary_fields.rb +3 -2
  36. data/test/hexapdf/test_object.rb +28 -0
  37. data/test/hexapdf/test_parser.rb +11 -0
  38. data/test/hexapdf/test_writer.rb +2 -2
  39. data/test/hexapdf/type/acro_form/test_appearance_generator.rb +8 -1
  40. data/test/hexapdf/type/acro_form/test_form.rb +15 -8
  41. data/test/hexapdf/type/acro_form/test_variable_text_field.rb +18 -8
  42. data/test/hexapdf/type/test_font.rb +4 -0
  43. data/test/hexapdf/type/test_font_type3.rb +16 -1
  44. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 71e5d38f8a3d1a6b4371e5f8e2f991d36c0ea8c01ca5dc4ec1e132043fad00aa
4
- data.tar.gz: cf78ef5b6fa915bac30fe75157fc414148c8bd2b8c5c7ee10fe4bb3cbf70076a
3
+ metadata.gz: 496c87d6cffcfde7b155fbfcdd78673fb34931d4d0fd997bcee934decfba175d
4
+ data.tar.gz: ce72598c9d33735c6ea4738c072b1f00202dc7bac24577c9c27a062add38ea8e
5
5
  SHA512:
6
- metadata.gz: 2153a1c0eadc0ca940480502b42780717e2777416f1e85e1c9506b879e01df0b2bc4e6d838ecd140af4a62e93bd16aa08ab42bbf522f6bbd234c0d9da82e4115
7
- data.tar.gz: e91b860ece2751633350739852b392719d9d09cb1bdd9e1c1896ef76e5828db9d2d0f9204b93521efbd6e5dcefc84b0451984392c2a4de3703a6060def1f5c75
6
+ metadata.gz: cee5a6f86084490ba5602c40ec48945b9a3c0bdecb22fe03e2d7b8b6ad02c791900f9fd7354557a9ac3dfdebb4a8d95c7335fc329c0054156082853f33717144
7
+ data.tar.gz: eaed67cce68e703eddbbb357e790a9f0bc62083ff8ba0b0856aa360d499f75e0e022b1ccd41ebcdd53e2fe269a93b5f587c47db41311c3f6a0beb9ed293e02cd
data/CHANGELOG.md CHANGED
@@ -1,3 +1,70 @@
1
+ ## 0.19.1 - 2021-12-12
2
+
3
+ ### Added
4
+
5
+ * [HexaPDF::Type::FontType3#bounding_box] to fix content stream processing error
6
+
7
+ ### Fixed
8
+
9
+ * Calculation of scaled font size for [HexaPDF::Content::GraphicsState] and
10
+ [HexaPDF::Layout::Style] when Type3 fonts are used
11
+
12
+
13
+ ## 0.19.0 - 2021-11-24
14
+
15
+ ### Added
16
+
17
+ * Page resource pruning to the optimization task
18
+ * An option for page resources pruning to the optimization options of the
19
+ `hexapdf` command
20
+
21
+ ### Fixed
22
+
23
+ * Handling of invalid date strings with a minute time zone offset greater than
24
+ 59
25
+
26
+
27
+ ## 0.18.0 - 2021-11-04
28
+
29
+ ### Added
30
+
31
+ * [HexaPDF::Content::ColorSpace::serialize_device_color] for serialization of
32
+ device colors in parts other than the canvas
33
+ * [HexaPDF::Type::AcroForm::VariableTextField::create_appearance_string] for
34
+ centralized creation of appearance strings
35
+ * [HexaPDF::Object::make_direct] for making objects and all parts of them direct
36
+ instead of indirect
37
+
38
+ ### Changed
39
+
40
+ * [HexaPDF::Type::AcroForm::VariableTextField::parse_appearance_string] to also
41
+ return the font color
42
+ * [HexaPDF::Type::AcroForm::VariableTextField#set_default_appearance_string] to
43
+ allow specifying the font color
44
+ * [HexaPDF::Type::AcroForm::Form] methods to support new variable text field
45
+ methods
46
+ * [HexaPDF::Type::AcroForm::AppearanceGenerator] to support the set font color
47
+ when creating text field appearances
48
+
49
+ ### Fixed
50
+
51
+ * Writing of existing, encrypted PDF files where parts of the encryption
52
+ dictionary are indirect objects
53
+ * [HexaPDF::Content::GraphicObject::EndpointArc] to correctly determine the
54
+ start and end points
55
+ * HexaPDF::Dictionary#perform_validation to correctly handle objects that
56
+ should not be indirect objects
57
+
58
+
59
+ ## 0.17.3 - 2021-10-31
60
+
61
+ ### Fixed
62
+
63
+ * Reconstruction of invalid PDF files where the PDF header is not at the start
64
+ of the file
65
+ * Reconstruction of invalid PDF files where the first object is invalid
66
+
67
+
1
68
  ## 0.17.2 - 2021-10-26
2
69
 
3
70
  ### Fixed
@@ -66,6 +66,7 @@ module HexaPDF
66
66
  @out_options.xref_streams = :preserve
67
67
  @out_options.streams = :preserve
68
68
  @out_options.optimize_fonts = false
69
+ @out_options.prune_page_resources = false
69
70
 
70
71
  @out_options.encryption = :preserve
71
72
  @out_options.enc_user_pwd = @out_options.enc_owner_pwd = nil
@@ -169,6 +170,10 @@ module HexaPDF
169
170
  "time; default: #{@out_options.compress_pages})") do |c|
170
171
  @out_options.compress_pages = c
171
172
  end
173
+ options.on("--[no-]prune-page-resources", "Prunes unused objects from the page resources " \
174
+ "(may take a long time; default: #{@out_options.prune_page_resources})") do |c|
175
+ @out_options.prune_page_resources = c
176
+ end
172
177
  options.on("--[no-]optimize-fonts", "Optimize embedded font files; " \
173
178
  "default: #{@out_options.optimize_fonts})") do |o|
174
179
  @out_options.optimize_fonts = o
@@ -236,7 +241,8 @@ module HexaPDF
236
241
  doc.task(:optimize, compact: @out_options.compact,
237
242
  object_streams: @out_options.object_streams,
238
243
  xref_streams: @out_options.xref_streams,
239
- compress_pages: @out_options.compress_pages)
244
+ compress_pages: @out_options.compress_pages,
245
+ prune_page_resources: @out_options.prune_page_resources)
240
246
  if @out_options.streams != :preserve || @out_options.optimize_fonts
241
247
  doc.each(only_current: false) do |obj|
242
248
  optimize_stream(obj)
@@ -589,7 +589,7 @@ module HexaPDF
589
589
  #
590
590
  # The line cap style specifies how the ends of stroked open paths should look like.
591
591
  #
592
- # The +style+ parameter can be one of:
592
+ # The +style+ parameter can be one of (also see LineCapStyle):
593
593
  #
594
594
  # :butt or 0::
595
595
  # Stroke is squared off at the endpoint of a path.
@@ -641,7 +641,7 @@ module HexaPDF
641
641
  #
642
642
  # The line join style specifies the shape that is used at the corners of stroked paths.
643
643
  #
644
- # The +style+ parameter can be one of:
644
+ # The +style+ parameter can be one of (also see LineJoinStyle):
645
645
  #
646
646
  # :miter or 0::
647
647
  # The outer lines of the two segments continue until the meet at an angle.
@@ -35,7 +35,9 @@
35
35
  #++
36
36
 
37
37
  require 'hexapdf/error'
38
+ require 'hexapdf/content'
38
39
  require 'hexapdf/configuration'
40
+ require 'hexapdf/serializer'
39
41
 
40
42
  module HexaPDF
41
43
  module Content
@@ -304,6 +306,23 @@ module HexaPDF
304
306
  GlobalConfiguration.constantize('color_space.map', for_components(spec)).new.color(*spec)
305
307
  end
306
308
 
309
+ # Serializes the given device color into the form expected by PDF content streams.
310
+ #
311
+ # The +type+ argument can either be :stroke to serialize as stroke color operator or :fill as
312
+ # fill color operator.
313
+ def self.serialize_device_color(color, type: :fill)
314
+ operator = case color.color_space.family
315
+ when :DeviceRGB then :rg
316
+ when :DeviceGray then :g
317
+ when :DeviceCMYK then :k
318
+ else
319
+ raise ArgumentError, "Device color object expected, got #{color.class}"
320
+ end
321
+ operator = operator.upcase if type == :stroke
322
+ Content::Operator::DEFAULT_OPERATORS[operator].
323
+ serialize(HexaPDF::Serializer.new, *color.components)
324
+ end
325
+
307
326
  # Returns a device color object for the given components array without applying value
308
327
  # normalization.
309
328
  def self.prenormalized_device_color(components)
@@ -51,8 +51,8 @@ module HexaPDF
51
51
  # Examples:
52
52
  #
53
53
  # #>pdf-center
54
- # arc = canvas.graphic_object(:arc, a: 100, b: 50).stroke
55
- # arc.draw(canvas).stroke # or: canvas.draw(arc).stroke
54
+ # arc = canvas.graphic_object(:arc, a: 100, b: 50, end_angle: 150)
55
+ # canvas.draw(arc).stroke
56
56
  #
57
57
  # See: ELL - https://spaceroots.org/documents/ellipse/elliptical-arc.pdf
58
58
  class Arc
@@ -49,8 +49,8 @@ module HexaPDF
49
49
  # Examples:
50
50
  #
51
51
  # #>pdf-center
52
- # arc = canvas.graphic_object(:endpoint_arc, x: 50, y: 20, a: 30, b: 10).stroke
53
- # arc.move_to(0, 0).draw(canvas).stroke # or: canvas.draw(arc).stroke
52
+ # arc = canvas.graphic_object(:endpoint_arc, x: 50, y: 20, a: 30, b: 10)
53
+ # canvas.move_to(0, 0).draw(arc).stroke
54
54
  #
55
55
  # See: GraphicObject::Arc, ARC - https://www.w3.org/TR/SVG/implnote.html#ArcImplementationNotes
56
56
  class EndpointArc
@@ -251,10 +251,10 @@ module HexaPDF
251
251
  cy = sin_theta * cxp + cos_theta * cyp + (y1 + y2) / 2.0
252
252
 
253
253
  # F.6.5.5
254
- start_angle = compute_angle_to_x_axis((x1p - cxp) / rx, (y1p - cyp) / ry)
254
+ start_angle = compute_angle_to_x_axis((x1p - cxp), (y1p - cyp)) % 360
255
255
 
256
256
  # F.6.5.6 (modified bc we just need the end angle)
257
- end_angle = compute_angle_to_x_axis((-x1p - cxp) / rx, (-y1p - cyp) / ry)
257
+ end_angle = compute_angle_to_x_axis((-x1p - cxp), (-y1p - cyp)) % 360
258
258
 
259
259
  {cx: cx, cy: cy, a: rx, b: ry, start_angle: start_angle, end_angle: end_angle,
260
260
  inclination: @inclination, clockwise: @clockwise}
@@ -41,20 +41,45 @@ module HexaPDF
41
41
  # This graphic object represents a solid elliptical arc, i.e. an arc that has an inner and
42
42
  # an outer set of a/b values.
43
43
  #
44
+ # This graphic object is registered under the :solid_arc key for use with the
45
+ # HexaPDF::Content::Canvas class.
46
+ #
44
47
  # Thus it can be used to create
45
48
  #
46
- # * an (elliptical) disk (when the inner a/b are zero and the difference between start and
49
+ # * an (*elliptical*) *disk* (when the inner a/b are zero and the difference between start and
47
50
  # end angles is greater than or equal to 360),
48
51
  #
49
- # * an (elliptical) sector (when the inner a/b are zero and the difference between start
52
+ # #>pdf-center
53
+ # canvas.fill_color("red").
54
+ # draw(:solid_arc, outer_a: 80, outer_b: 50, end_angle: 360).
55
+ # fill_stroke
56
+ #
57
+ # * an (*elliptical*) *sector* (when the inner a/b are zero and the difference between start
50
58
  # and end angles is less than 360),
51
59
  #
52
- # * an (elliptical) annulus (when the inner a/b are nonzero and the difference between
60
+ # #>pdf-center
61
+ # canvas.fill_color("red").
62
+ # draw(:solid_arc, outer_a: 80, outer_b: 50, start_angle: 20, end_angle: 230).
63
+ # fill_stroke
64
+ #
65
+ # * an (*elliptical*) *annulus* (when the inner a/b are nonzero and the difference between
53
66
  # start and end angles is greater than or equal to 360), and
54
67
  #
55
- # * an (elliptical) annular sector (when the inner a/b are nonzero and the difference
68
+ # #>pdf-center
69
+ # canvas.fill_color("red").
70
+ # draw(:solid_arc, outer_a: 80, outer_b: 50, inner_a: 70, inner_b: 30,
71
+ # end_angle: 360).
72
+ # fill_stroke
73
+ #
74
+ # * an (*elliptical*) *annular sector* (when the inner a/b are nonzero and the difference
56
75
  # between start and end angles is less than 360)
57
76
  #
77
+ # #>pdf-center
78
+ # canvas.fill_color("red").
79
+ # draw(:solid_arc, outer_a: 80, outer_b: 50, inner_a: 70, inner_b: 30,
80
+ # start_angle: 20, end_angle: 230).
81
+ # fill_stroke
82
+ #
58
83
  # See: Arc
59
84
  class SolidArc
60
85
 
@@ -66,30 +91,106 @@ module HexaPDF
66
91
  end
67
92
 
68
93
  # x-coordinate of center point
94
+ #
95
+ # Examples:
96
+ #
97
+ # #>pdf-center
98
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
99
+ # inner_a: 20, inner_b: 10)
100
+ # canvas.draw(solid_arc).stroke
101
+ # canvas.stroke_color("red").draw(solid_arc, cx: 50).stroke
69
102
  attr_reader :cx
70
103
 
71
104
  # y-coordinate of center point
105
+ #
106
+ # Examples:
107
+ #
108
+ # #>pdf-center
109
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
110
+ # inner_a: 20, inner_b: 10)
111
+ # canvas.draw(solid_arc).stroke
112
+ # canvas.stroke_color("red").draw(solid_arc, cy: 50).stroke
72
113
  attr_reader :cy
73
114
 
74
- # Length of inner semi-major axis
115
+ # Length of inner semi-major axis which (without altering the #inclination) is parallel to
116
+ # the x-axis
117
+ #
118
+ # Examples:
119
+ #
120
+ # #>pdf-center
121
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
122
+ # inner_a: 20, inner_b: 10)
123
+ # canvas.draw(solid_arc).stroke
124
+ # canvas.stroke_color("red").draw(solid_arc, inner_a: 5).stroke
75
125
  attr_reader :inner_a
76
126
 
77
- # Length of inner semi-minor axis
127
+ # Length of inner semi-minor axis which (without altering the #inclination) is parallel to the
128
+ # y-axis
129
+ #
130
+ # Examples:
131
+ #
132
+ # #>pdf-center
133
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
134
+ # inner_a: 20, inner_b: 10)
135
+ # canvas.draw(solid_arc).stroke
136
+ # canvas.stroke_color("red").draw(solid_arc, inner_b: 20).stroke
78
137
  attr_reader :inner_b
79
138
 
80
- # Length of outer semi-major axis
139
+ # Length of outer semi-major axis which (without altering the #inclination) is parallel to
140
+ # the x-axis
141
+ #
142
+ # Examples:
143
+ #
144
+ # #>pdf-center
145
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
146
+ # inner_a: 20, inner_b: 10)
147
+ # canvas.draw(solid_arc).stroke
148
+ # canvas.stroke_color("red").draw(solid_arc, outer_a: 45).stroke
81
149
  attr_reader :outer_a
82
150
 
83
- # Length of outer semi-minor axis
151
+ # Length of outer semi-minor axis which (without altering the #inclination) is parallel to the
152
+ # y-axis
153
+ #
154
+ # Examples:
155
+ #
156
+ # #>pdf-center
157
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
158
+ # inner_a: 20, inner_b: 10)
159
+ # canvas.draw(solid_arc).stroke
160
+ # canvas.stroke_color("red").draw(solid_arc, outer_b: 40).stroke
84
161
  attr_reader :outer_b
85
162
 
86
- # Start angle in degrees
163
+ # Start angle of the solid arc in degrees
164
+ #
165
+ # Examples:
166
+ #
167
+ # #>pdf-center
168
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
169
+ # inner_a: 20, inner_b: 10)
170
+ # canvas.draw(solid_arc).stroke
171
+ # canvas.stroke_color("red").draw(solid_arc, start_angle: 60).stroke
87
172
  attr_reader :start_angle
88
173
 
89
- # End angle in degrees
174
+ # End angle of the solid arc in degrees
175
+ #
176
+ # Examples:
177
+ #
178
+ # #>pdf-center
179
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
180
+ # inner_a: 20, inner_b: 10)
181
+ # canvas.draw(solid_arc).stroke
182
+ # canvas.stroke_color("red").draw(solid_arc, end_angle: 120).stroke
90
183
  attr_reader :end_angle
91
184
 
92
185
  # Inclination in degrees of semi-major axis in respect to x-axis
186
+ #
187
+ # Examples:
188
+ #
189
+ # #>pdf-center
190
+ # solid_arc = canvas.graphic_object(:solid_arc, outer_a: 30, outer_b: 20,
191
+ # inner_a: 20, inner_b: 10)
192
+ # canvas.draw(solid_arc).stroke
193
+ # canvas.stroke_color("red").draw(solid_arc, inclination: 40).stroke
93
194
  attr_reader :inclination
94
195
 
95
196
  # Creates a solid arc with default values (a unit disk at the origin).
@@ -73,7 +73,7 @@ module HexaPDF
73
73
  end
74
74
 
75
75
  # Defines all available line cap styles as constants. Each line cap style is an instance of
76
- # NamedValue. For use with Content::GraphicsState#line_cap_style.
76
+ # NamedValue, see ::normalize. For use with e.g. Content::Canvas#line_cap_style.
77
77
  #
78
78
  # See: PDF1.7 s8.4.3.3
79
79
  module LineCapStyle
@@ -95,18 +95,39 @@ module HexaPDF
95
95
  end
96
96
 
97
97
  # Stroke is squared off at the endpoint of a path.
98
+ #
99
+ # Specify as 0 or :butt.
100
+ #
101
+ # #>pdf-small-hide
102
+ # canvas.line_cap_style(:butt)
103
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
104
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
98
105
  BUTT_CAP = NamedValue.new(:butt, 0)
99
106
 
100
107
  # A semicircular arc is drawn at the endpoint of a path.
108
+ #
109
+ # Specify as 1 or :round.
110
+ #
111
+ # #>pdf-small-hide
112
+ # canvas.line_cap_style(:round)
113
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
114
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
101
115
  ROUND_CAP = NamedValue.new(:round, 1)
102
116
 
103
117
  # The stroke continues half the line width beyond the endpoint of a path.
118
+ #
119
+ # Specify as 2 or :projecting_square.
120
+ #
121
+ # #>pdf-small-hide
122
+ # canvas.line_cap_style(:projecting_square)
123
+ # canvas.line_width(10).line(50, 20, 50, 80).stroke
124
+ # canvas.stroke_color("white").line_width(1).line(50, 20, 50, 80).stroke
104
125
  PROJECTING_SQUARE_CAP = NamedValue.new(:projecting_square, 2)
105
126
 
106
127
  end
107
128
 
108
129
  # Defines all available line join styles as constants. Each line join style is an instance of
109
- # NamedValue. For use with Content::GraphicsState#line_join_style.
130
+ # NamedValue, see ::normalize For use with e.g. Content::Canvas#line_join_style.
110
131
  #
111
132
  # See: PDF1.7 s8.4.3.4
112
133
  module LineJoinStyle
@@ -127,20 +148,47 @@ module HexaPDF
127
148
  end
128
149
  end
129
150
 
130
- # The outer lines of the two segments continue until the meet at an angle.
151
+ # The outer lines of the two segments continue until they meet at an angle.
152
+ #
153
+ # Specify as 0 or :miter.
154
+ #
155
+ # #>pdf-small-hide
156
+ # canvas.line_join_style(:miter)
157
+ # canvas.line_width(10).
158
+ # polyline(20, 20, 50, 80, 80, 20).stroke
159
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
160
+ # polyline(20, 20, 50, 80, 80, 20).stroke
131
161
  MITER_JOIN = NamedValue.new(:miter, 0)
132
162
 
133
163
  # An arc of a circle is drawn around the point where the segments meet.
164
+ #
165
+ # Specify as 1 or :round.
166
+ #
167
+ # #>pdf-small-hide
168
+ # canvas.line_join_style(:round)
169
+ # canvas.line_width(10).
170
+ # polyline(20, 20, 50, 80, 80, 20).stroke
171
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
172
+ # polyline(20, 20, 50, 80, 80, 20).stroke
134
173
  ROUND_JOIN = NamedValue.new(:round, 1)
135
174
 
136
- # The two segments are finished with butt caps and the space between the ends is filled with
137
- # a triangle.
175
+ # The two segments are finished with butt caps and the space between the ends is filled with a
176
+ # triangle.
177
+ #
178
+ # Specify as 2 or :bevel.
179
+ #
180
+ # #>pdf-small-hide
181
+ # canvas.line_join_style(:bevel)
182
+ # canvas.line_width(10).
183
+ # polyline(20, 20, 50, 80, 80, 20).stroke
184
+ # canvas.stroke_color("white").line_width(1).line_join_style(:bevel).
185
+ # polyline(20, 20, 50, 80, 80, 20).stroke
138
186
  BEVEL_JOIN = NamedValue.new(:bevel, 2)
139
187
 
140
188
  end
141
189
 
142
- # The line dash pattern defines how a line should be dashed. For use with
143
- # Content::GraphicsState#line_dash_pattern.
190
+ # The line dash pattern defines how a line should be dashed. For use with e.g.
191
+ # Content::Canvas#line_dash_pattern.
144
192
  #
145
193
  # A dash pattern consists of two parts: the dash array and the dash phase. The dash array
146
194
  # defines the length of alternating dashes and gaps (important: starting with dashes). And the
@@ -159,6 +207,12 @@ module HexaPDF
159
207
  # See: PDF1.7 s8.4.3.6
160
208
  class LineDashPattern
161
209
 
210
+ # :call-seq:
211
+ # LineDashPattern.normalize(line_dash_pattern) -> line_dash_pattern
212
+ # LineDashPattern.normalize(array, phase = 0) -> LineDashPattern.new(array, phase)
213
+ # LineDashPattern.normalize(number, phase = 0) -> LineDashPattern.new([number], phase)
214
+ # LineDashPattern.normalize(0) -> LineDashPattern.new
215
+ #
162
216
  # Returns the arguments normalized to a valid LineDashPattern instance.
163
217
  #
164
218
  # If +array+ is 0, the default line dash pattern representing a solid line will be used. If it
@@ -206,8 +260,8 @@ module HexaPDF
206
260
 
207
261
  end
208
262
 
209
- # Defines all available rendering intents as constants. For use with
210
- # Content::GraphicsState#rendering_intent.
263
+ # Defines all available rendering intents as constants. For use with e.g.
264
+ # Content::Canvas#rendering_intent.
211
265
  #
212
266
  # See: PDF1.7 s8.6.5.8
213
267
  module RenderingIntent
@@ -241,7 +295,7 @@ module HexaPDF
241
295
  end
242
296
 
243
297
  # Defines all available text rendering modes as constants. Each text rendering mode is an
244
- # instance of NamedValue. For use with Content::GraphicsState#text_rendering_mode.
298
+ # instance of NamedValue. For use with e.g. Content::Canvas#text_rendering_mode.
245
299
  #
246
300
  # See: PDF1.7 s9.3.6
247
301
  module TextRenderingMode
@@ -272,28 +326,97 @@ module HexaPDF
272
326
  end
273
327
  end
274
328
 
275
- # Fill text
329
+ # Fill text.
330
+ #
331
+ # Specify as 0 or :fill.
332
+ #
333
+ # #>pdf-small-hide
334
+ # canvas.font("Helvetica", size: 13)
335
+ # canvas.stroke_color("green").line_width(0.5)
336
+ # canvas.text_rendering_mode(:fill)
337
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
276
338
  FILL = NamedValue.new(:fill, 0)
277
339
 
278
- # Stroke text
340
+ # Stroke text.
341
+ #
342
+ # Specify as 1 or :stroke.
343
+ #
344
+ # #>pdf-small-hide
345
+ # canvas.font("Helvetica", size: 13)
346
+ # canvas.stroke_color("green").line_width(0.5)
347
+ # canvas.text_rendering_mode(:stroke)
348
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
279
349
  STROKE = NamedValue.new(:stroke, 1)
280
350
 
281
- # Fill, then stroke text
351
+ # Fill, then stroke text.
352
+ #
353
+ # Specify as 2 or :fill_stroke.
354
+ #
355
+ # #>pdf-small-hide
356
+ # canvas.font("Helvetica", size: 13)
357
+ # canvas.stroke_color("green").line_width(0.5)
358
+ # canvas.text_rendering_mode(:fill_stroke)
359
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
282
360
  FILL_STROKE = NamedValue.new(:fill_stroke, 2)
283
361
 
284
- # Neither fill nor stroke text (invisible)
362
+ # Neither fill nor stroke text (invisible).
363
+ #
364
+ # Specify as 3 or :invisible.
365
+ #
366
+ # #>pdf-small-hide
367
+ # canvas.font("Helvetica", size: 13)
368
+ # canvas.stroke_color("green").line_width(0.5)
369
+ # canvas.text_rendering_mode(:invisible)
370
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
371
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
285
372
  INVISIBLE = NamedValue.new(:invisible, 3)
286
373
 
287
- # Fill text and add to path for clipping
374
+ # Fill text and add to path for clipping.
375
+ #
376
+ # Specify as 4 or :fill_clip.
377
+ #
378
+ # #>pdf-small-hide
379
+ # canvas.font("Helvetica", size: 13)
380
+ # canvas.stroke_color("green").line_width(0.5)
381
+ # canvas.text_rendering_mode(:fill_clip)
382
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
383
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
288
384
  FILL_CLIP = NamedValue.new(:fill_clip, 4)
289
385
 
290
- # Stroke text and add to path for clipping
386
+ # Stroke text and add to path for clipping.
387
+ #
388
+ # Specify as 5 or :stroke_clip.
389
+ #
390
+ # #>pdf-small-hide
391
+ # canvas.font("Helvetica", size: 13)
392
+ # canvas.stroke_color("green").line_width(0.5)
393
+ # canvas.text_rendering_mode(:stroke_clip)
394
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
395
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
291
396
  STROKE_CLIP = NamedValue.new(:stroke_clip, 5)
292
397
 
293
- # Fill, then stroke text and add to path for clipping
398
+ # Fill, then stroke text and add to path for clipping.
399
+ #
400
+ # Specify as 6 or :fill_stroke_clip.
401
+ #
402
+ # #>pdf-small-hide
403
+ # canvas.font("Helvetica", size: 13)
404
+ # canvas.stroke_color("green").line_width(0.5)
405
+ # canvas.text_rendering_mode(:fill_stroke_clip)
406
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
407
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
294
408
  FILL_STROKE_CLIP = NamedValue.new(:fill_stroke_clip, 6)
295
409
 
296
- # Add text to path for clipping
410
+ # Add text to path for clipping.
411
+ #
412
+ # Specify as 7 or :clip.
413
+ #
414
+ # #>pdf-small-hide
415
+ # canvas.font("Helvetica", size: 13)
416
+ # canvas.stroke_color("green").line_width(0.5)
417
+ # canvas.text_rendering_mode(:clip)
418
+ # canvas.text("#{canvas.text_rendering_mode.name}", at: [10, 50])
419
+ # canvas.stroke_color("red").line_width(20).line(30, 20, 30, 80).stroke
297
420
  CLIP = NamedValue.new(:clip, 7)
298
421
 
299
422
  end
@@ -389,7 +512,7 @@ module HexaPDF
389
512
  attr_accessor :leading
390
513
 
391
514
  # The font for the text.
392
- attr_accessor :font
515
+ attr_reader :font
393
516
 
394
517
  # The font size.
395
518
  attr_reader :font_size
@@ -415,23 +538,25 @@ module HexaPDF
415
538
 
416
539
  # The scaled character spacing used in glyph displacement calculations.
417
540
  #
418
- # This returns the value T_c multiplied by #scaled_horizontal_scaling.
541
+ # This returns the character spacing multiplied by #scaled_horizontal_scaling.
419
542
  #
420
543
  # See PDF1.7 s9.4.4
421
544
  attr_reader :scaled_character_spacing
422
545
 
423
546
  # The scaled word spacing used in glyph displacement calculations.
424
547
  #
425
- # This returns the value T_w multiplied by #scaled_horizontal_scaling.
548
+ # This returns the word spacing multiplied by #scaled_horizontal_scaling.
426
549
  #
427
550
  # See PDF1.7 s9.4.4
428
551
  attr_reader :scaled_word_spacing
429
552
 
430
553
  # The scaled font size used in glyph displacement calculations.
431
554
  #
432
- # This returns the value T_fs / 1000 multiplied by #scaled_horizontal_scaling.
555
+ # This returns the font size multiplied by the scaling factor from glyph space to text space
556
+ # (0.001 for all fonts except Type3 fonts or the scaling specified in /FontMatrix for Type3
557
+ # fonts) and multiplied by #scaled_horizontal_scaling.
433
558
  #
434
- # See PDF1.7 s9.4.4
559
+ # See PDF1.7 s9.4.4, HexaPDF::Type::FontType3
435
560
  attr_reader :scaled_font_size
436
561
 
437
562
  # The scaled horizontal scaling used in glyph displacement calculations.
@@ -542,6 +667,15 @@ module HexaPDF
542
667
  self.fill_color = color_space.default_color
543
668
  end
544
669
 
670
+ ##
671
+ # :attr_writer: font
672
+ #
673
+ # Sets the font and updates the glyph space to text space scaling.
674
+ def font=(font)
675
+ @font = font
676
+ update_scaled_font_size
677
+ end
678
+
545
679
  ##
546
680
  # :attr_writer: character_spacing
547
681
  #
@@ -566,7 +700,7 @@ module HexaPDF
566
700
  # Sets the font size and updates the scaled font size.
567
701
  def font_size=(size)
568
702
  @font_size = size
569
- @scaled_font_size = size / 1000.0 * @scaled_horizontal_scaling
703
+ update_scaled_font_size
570
704
  end
571
705
 
572
706
  ##
@@ -579,7 +713,15 @@ module HexaPDF
579
713
  @scaled_horizontal_scaling = scaling / 100.0
580
714
  @scaled_character_spacing = @character_spacing * @scaled_horizontal_scaling
581
715
  @scaled_word_spacing = @word_spacing * @scaled_horizontal_scaling
582
- @scaled_font_size = @font_size / 1000.0 * @scaled_horizontal_scaling
716
+ update_scaled_font_size
717
+ end
718
+
719
+ private
720
+
721
+ # Updates the cached value for the scaled font size.
722
+ def update_scaled_font_size
723
+ @scaled_font_size = @font_size * (@font&.glyph_scaling_factor || 0.001) *
724
+ @scaled_horizontal_scaling
583
725
  end
584
726
 
585
727
  end