hexapdf 0.17.2 → 0.19.1

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