hexapdf 0.7.0 → 0.8.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 +39 -1
- data/CONTRIBUTERS +1 -1
- data/LICENSE +3 -0
- data/README.md +2 -1
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/examples/{hello_world.rb → 001-hello_world.rb} +0 -0
- data/examples/{graphics.rb → 002-graphics.rb} +1 -1
- data/examples/{arc.rb → 003-arcs.rb} +2 -2
- data/examples/{optimizing.rb → 004-optimizing.rb} +0 -0
- data/examples/{merging.rb → 005-merging.rb} +0 -0
- data/examples/{standard_pdf_fonts.rb → 006-standard_pdf_fonts.rb} +0 -0
- data/examples/{truetype.rb → 007-truetype.rb} +0 -0
- data/examples/{show_char_bboxes.rb → 008-show_char_bboxes.rb} +0 -0
- data/examples/{text_layouter_alignment.rb → 009-text_layouter_alignment.rb} +3 -3
- data/examples/{text_layouter_inline_boxes.rb → 010-text_layouter_inline_boxes.rb} +7 -9
- data/examples/{text_layouter_line_wrapping.rb → 011-text_layouter_line_wrapping.rb} +6 -5
- data/examples/{text_layouter_styling.rb → 012-text_layouter_styling.rb} +6 -8
- data/examples/013-text_layouter_shapes.rb +176 -0
- data/examples/014-text_in_polygon.rb +60 -0
- data/examples/{boxes.rb → 015-boxes.rb} +29 -21
- data/examples/016-frame_automatic_box_placement.rb +90 -0
- data/examples/017-frame_text_flow.rb +60 -0
- data/lib/hexapdf/cli/command.rb +4 -3
- data/lib/hexapdf/cli/files.rb +1 -1
- data/lib/hexapdf/cli/inspect.rb +0 -1
- data/lib/hexapdf/cli/merge.rb +1 -1
- data/lib/hexapdf/cli/modify.rb +1 -1
- data/lib/hexapdf/configuration.rb +2 -0
- data/lib/hexapdf/content/canvas.rb +3 -3
- data/lib/hexapdf/content/graphic_object.rb +1 -0
- data/lib/hexapdf/content/graphic_object/geom2d.rb +132 -0
- data/lib/hexapdf/dictionary.rb +7 -1
- data/lib/hexapdf/dictionary_fields.rb +35 -83
- data/lib/hexapdf/document.rb +9 -5
- data/lib/hexapdf/document/fonts.rb +1 -1
- data/lib/hexapdf/encryption/standard_security_handler.rb +1 -1
- data/lib/hexapdf/filter/ascii85_decode.rb +1 -1
- data/lib/hexapdf/filter/ascii_hex_decode.rb +1 -1
- data/lib/hexapdf/font/cmap/writer.rb +2 -2
- data/lib/hexapdf/font/true_type/builder.rb +1 -1
- data/lib/hexapdf/font/true_type/table.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +3 -3
- data/lib/hexapdf/font/true_type/table/kern.rb +1 -1
- data/lib/hexapdf/font/true_type/table/post.rb +1 -1
- data/lib/hexapdf/font/type1/character_metrics.rb +1 -1
- data/lib/hexapdf/font/type1/font_metrics.rb +1 -1
- data/lib/hexapdf/image_loader/jpeg.rb +1 -1
- data/lib/hexapdf/image_loader/png.rb +2 -2
- data/lib/hexapdf/layout.rb +3 -0
- data/lib/hexapdf/layout/box.rb +64 -46
- data/lib/hexapdf/layout/frame.rb +348 -0
- data/lib/hexapdf/layout/inline_box.rb +2 -2
- data/lib/hexapdf/layout/line.rb +3 -3
- data/lib/hexapdf/layout/style.rb +81 -14
- data/lib/hexapdf/layout/text_box.rb +84 -0
- data/lib/hexapdf/layout/text_fragment.rb +8 -8
- data/lib/hexapdf/layout/text_layouter.rb +278 -169
- data/lib/hexapdf/layout/width_from_polygon.rb +246 -0
- data/lib/hexapdf/rectangle.rb +9 -9
- data/lib/hexapdf/stream.rb +2 -2
- data/lib/hexapdf/type.rb +1 -0
- data/lib/hexapdf/type/action.rb +1 -1
- data/lib/hexapdf/type/annotations/markup_annotation.rb +1 -1
- data/lib/hexapdf/type/catalog.rb +1 -1
- data/lib/hexapdf/type/cid_font.rb +2 -1
- data/lib/hexapdf/type/font.rb +0 -1
- data/lib/hexapdf/type/font_descriptor.rb +1 -1
- data/lib/hexapdf/type/font_simple.rb +3 -3
- data/lib/hexapdf/type/font_true_type.rb +8 -0
- data/lib/hexapdf/type/font_type0.rb +2 -1
- data/lib/hexapdf/type/font_type1.rb +7 -1
- data/lib/hexapdf/type/font_type3.rb +61 -0
- data/lib/hexapdf/type/graphics_state_parameter.rb +8 -8
- data/lib/hexapdf/type/image.rb +10 -0
- data/lib/hexapdf/type/page.rb +83 -10
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +2 -2
- data/test/hexapdf/content/graphic_object/test_geom2d.rb +79 -0
- data/test/hexapdf/encryption/test_standard_security_handler.rb +1 -1
- data/test/hexapdf/font/test_true_type_wrapper.rb +1 -1
- data/test/hexapdf/font/test_type1_wrapper.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_cmap.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_directory.rb +1 -1
- data/test/hexapdf/font/true_type/table/test_head.rb +7 -3
- data/test/hexapdf/layout/test_box.rb +57 -15
- data/test/hexapdf/layout/test_frame.rb +313 -0
- data/test/hexapdf/layout/test_inline_box.rb +1 -1
- data/test/hexapdf/layout/test_style.rb +74 -0
- data/test/hexapdf/layout/test_text_box.rb +77 -0
- data/test/hexapdf/layout/test_text_layouter.rb +220 -239
- data/test/hexapdf/layout/test_width_from_polygon.rb +108 -0
- data/test/hexapdf/test_dictionary_fields.rb +22 -26
- data/test/hexapdf/test_document.rb +3 -3
- data/test/hexapdf/test_reference.rb +1 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_true_type.rb +25 -0
- data/test/hexapdf/type/test_font_type1.rb +6 -0
- data/test/hexapdf/type/test_font_type3.rb +26 -0
- data/test/hexapdf/type/test_image.rb +10 -0
- data/test/hexapdf/type/test_page.rb +114 -0
- data/test/test_helper.rb +1 -1
- metadata +65 -17
- data/examples/text_layouter_shapes.rb +0 -170
@@ -0,0 +1,246 @@
|
|
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-2017 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
|
+
|
34
|
+
module HexaPDF
|
35
|
+
module Layout
|
36
|
+
|
37
|
+
# Utility class for generating width specifications for TextLayouter#fit from polygons.
|
38
|
+
class WidthFromPolygon
|
39
|
+
|
40
|
+
# Creates a new object for the given polygon (or polygon set) and immediately prepares it so
|
41
|
+
# that #call can be used.
|
42
|
+
#
|
43
|
+
# The offset argument specifies the vertical offset from the top at which calculations
|
44
|
+
# should start.
|
45
|
+
def initialize(polygon, offset = 0)
|
46
|
+
@polygon = polygon
|
47
|
+
prepare(offset)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Returns the width specification for the given values with respect to the wrapped polygon.
|
51
|
+
def call(height, line_height)
|
52
|
+
width(@max_y - height - line_height, @max_y - height)
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
# Calculates the width specification for the area between the horizontal lines at y1 < y2.
|
58
|
+
#
|
59
|
+
# The following algorithm is used: Given y1 < y2 as the horizontal lines between which text
|
60
|
+
# should be layed out, and a polygon set p that is not self-intersecting but may have
|
61
|
+
# arbitrarily nested holes:
|
62
|
+
#
|
63
|
+
# * Get all segments of the polygon set in sequence, removing the horizontal segments in the
|
64
|
+
# process (done in #prepare).
|
65
|
+
#
|
66
|
+
# * Make sure that the first segment represents a left-most outside-inside transition,
|
67
|
+
# rotate array of segments (separate for each polygon) if necessary. (done in #prepare)
|
68
|
+
#
|
69
|
+
# * For the segments of each polygon do separately:
|
70
|
+
#
|
71
|
+
# * Ignore all segments except those with min_y < y2 and max_y > y1.
|
72
|
+
#
|
73
|
+
# * Determine the min_x and max_x of the segment within y1 <= y2.
|
74
|
+
#
|
75
|
+
# * If the segment crosses both, y1 and y2, store min_x/max_x and this segment is
|
76
|
+
# finished. Otherwise traverse the segments in-order to find the next crossing, updating
|
77
|
+
# min_x/max_x in the process. If it crosses the other line, the result is the same as if
|
78
|
+
# a single segment had crossed both lines. Otherwise the result depends on whether the
|
79
|
+
# segment sequence represents an outside-inside transition (it is ignored) or
|
80
|
+
# inside-outside transition (store two pairs min_x/min_x and max_x/max_x).
|
81
|
+
#
|
82
|
+
# * Order stored x-values.
|
83
|
+
#
|
84
|
+
# * For each pair [a_min, a_max], [b_min, b_max]
|
85
|
+
# - if inside (index is even): calculate width = b_min - a_max
|
86
|
+
# - if outside: calculate offset = b_max - a_min
|
87
|
+
#
|
88
|
+
# * Prepend a0_max for first offset and remove all offset-width pairs where width is zero.
|
89
|
+
def width(y1, y2)
|
90
|
+
result = []
|
91
|
+
|
92
|
+
@polygon_segments.each do |segments|
|
93
|
+
temp_result = []
|
94
|
+
status = if segments.first[0].start_point.y > y2 || segments.first[0].start_point.y < y1
|
95
|
+
:outside
|
96
|
+
else
|
97
|
+
:inside
|
98
|
+
end
|
99
|
+
|
100
|
+
segments.each do |_segment, miny, maxy, minyx, maxyx, vertical, slope, intercept|
|
101
|
+
next unless miny < y2 && maxy > y1
|
102
|
+
|
103
|
+
if vertical
|
104
|
+
min_x = max_x = minyx
|
105
|
+
else
|
106
|
+
min_x = (miny <= y1 ? (y1 - intercept) / slope : (miny <= y2 ? minyx : maxyx))
|
107
|
+
max_x = (maxy >= y2 ? (y2 - intercept) / slope : (miny >= y1 ? minyx : maxyx))
|
108
|
+
min_x, max_x = max_x, min_x if min_x > max_x
|
109
|
+
end
|
110
|
+
|
111
|
+
if miny <= y1 && maxy >= y2 # segment crosses both lines
|
112
|
+
temp_result << [min_x, max_x, :crossed_both]
|
113
|
+
elsif miny <= y1 # segment crosses bottom line
|
114
|
+
if status == :outside
|
115
|
+
temp_result << [min_x, max_x, :crossed_bottom]
|
116
|
+
status = :inside
|
117
|
+
elsif temp_result.last
|
118
|
+
temp_result.last[0] = min_x if temp_result.last[0] > min_x
|
119
|
+
temp_result.last[1] = max_x if temp_result.last[1] < max_x
|
120
|
+
temp_result.last[2] = :crossed_both if temp_result.last[2] == :crossed_top
|
121
|
+
temp_result.last[2] = :crossed_bottom if temp_result.last[2] == :crossed_none
|
122
|
+
status = :outside
|
123
|
+
else
|
124
|
+
temp_result << [min_x, max_x, :crossed_bottom]
|
125
|
+
status = :outside
|
126
|
+
end
|
127
|
+
elsif maxy >= y2 # segment crosses top line
|
128
|
+
if status == :outside
|
129
|
+
temp_result << [min_x, max_x, :crossed_top]
|
130
|
+
status = :inside
|
131
|
+
elsif temp_result.last
|
132
|
+
temp_result.last[0] = min_x if temp_result.last[0] > min_x
|
133
|
+
temp_result.last[1] = max_x if temp_result.last[1] < max_x
|
134
|
+
temp_result.last[2] = :crossed_both if temp_result.last[2] == :crossed_bottom
|
135
|
+
temp_result.last[2] = :crossed_top if temp_result.last[2] == :crossed_none
|
136
|
+
status = :outside
|
137
|
+
else
|
138
|
+
temp_result << [min_x, max_x, :crossed_top]
|
139
|
+
status = :outside
|
140
|
+
end
|
141
|
+
elsif status == :inside && temp_result.last # segment crosses no line
|
142
|
+
temp_result.last[0] = min_x if temp_result.last[0] > min_x
|
143
|
+
temp_result.last[1] = max_x if temp_result.last[1] < max_x
|
144
|
+
else # first segment completely inside
|
145
|
+
temp_result << [min_x, max_x, :crossed_none]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if temp_result.empty? # Ignore degenerate results
|
150
|
+
next
|
151
|
+
elsif temp_result.size == 1
|
152
|
+
# either polygon completely inside or just the top/bottom part, handle the same
|
153
|
+
temp_result[0][2] = :crossed_top
|
154
|
+
elsif temp_result[0][2] != :crossed_both && temp_result[-1][2] != :crossed_both
|
155
|
+
# Handle case where first and last segments only crosses one line
|
156
|
+
temp_result[0][0] = temp_result[-1][0] if temp_result[0][0] > temp_result[-1][0]
|
157
|
+
temp_result[0][1] = temp_result[-1][1] if temp_result[0][1] < temp_result[-1][1]
|
158
|
+
temp_result[0][2] = :crossed_both if temp_result[0][2] != temp_result[-1][2]
|
159
|
+
temp_result.pop
|
160
|
+
end
|
161
|
+
|
162
|
+
result.concat(temp_result)
|
163
|
+
end
|
164
|
+
|
165
|
+
temp_result = result
|
166
|
+
outside = true
|
167
|
+
temp_result.sort_by! {|a| a[0] }.map! do |min, max, stat|
|
168
|
+
if stat == :crossed_both
|
169
|
+
outside = !outside
|
170
|
+
[min, max]
|
171
|
+
elsif outside
|
172
|
+
[]
|
173
|
+
else
|
174
|
+
[min, min, max, max]
|
175
|
+
end
|
176
|
+
end.flatten!
|
177
|
+
temp_result.unshift(0, 0)
|
178
|
+
|
179
|
+
i = 0
|
180
|
+
result = []
|
181
|
+
while i < temp_result.size - 2
|
182
|
+
if i % 4 == 2 # inside the polygon, i.e. width (min2 - max1)
|
183
|
+
if (width = temp_result[i + 2] - temp_result[i + 1]) > 0
|
184
|
+
result << width
|
185
|
+
else
|
186
|
+
result.pop # remove last offset and don't add width
|
187
|
+
end
|
188
|
+
else # outside the polygon, i.e. offset (max2 - min1)
|
189
|
+
result << temp_result[i + 3] - temp_result[i + 0]
|
190
|
+
end
|
191
|
+
i += 2
|
192
|
+
end
|
193
|
+
result.empty? ? [0, 0] : result
|
194
|
+
end
|
195
|
+
|
196
|
+
# Prepare the segments and other data for later use.
|
197
|
+
def prepare(offset)
|
198
|
+
@max_y = @polygon.bbox.max_y - offset
|
199
|
+
@polygon_segments = if @polygon.nr_of_contours > 1
|
200
|
+
@polygon.polygons.map {|polygon| process_polygon(polygon) }
|
201
|
+
else
|
202
|
+
[process_polygon(@polygon)]
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Processes the given polygon segment by segment and returns an array with the following
|
207
|
+
# processing information for each segment of the polygon:
|
208
|
+
#
|
209
|
+
# * the segment itself
|
210
|
+
# * minimum y-value
|
211
|
+
# * maximum y-value
|
212
|
+
# * x-value corresponding to the minimum y-value
|
213
|
+
# * x-value corresponding to the maximum y-value
|
214
|
+
# * whether the segment is vertical
|
215
|
+
# * for non-vertical segments: slope and y-intercept of the segment
|
216
|
+
#
|
217
|
+
# Additionally, the returned array is rotated sothat the data for the segment with the
|
218
|
+
# minimum x-value is the first item (without changing the order).
|
219
|
+
def process_polygon(polygon)
|
220
|
+
rotate_nr = 0
|
221
|
+
min_x = Float::INFINITY
|
222
|
+
segments = polygon.each_segment.reject(&:horizontal?)
|
223
|
+
segments.map!.with_index do |segment, index|
|
224
|
+
(rotate_nr = index; min_x = segment.min.x) if segment.min.x < min_x
|
225
|
+
data = [segment]
|
226
|
+
if segment.start_point.y < segment.end_point.y
|
227
|
+
data.push(segment.start_point.y, segment.end_point.y,
|
228
|
+
segment.start_point.x, segment.end_point.x)
|
229
|
+
else
|
230
|
+
data.push(segment.end_point.y, segment.start_point.y,
|
231
|
+
segment.end_point.x, segment.start_point.x)
|
232
|
+
end
|
233
|
+
data.push(segment.vertical?)
|
234
|
+
unless segment.vertical?
|
235
|
+
data.push(segment.slope)
|
236
|
+
data.push((segment.start_point.y - segment.slope * segment.start_point.x).to_f)
|
237
|
+
end
|
238
|
+
data
|
239
|
+
end
|
240
|
+
segments.rotate!(rotate_nr)
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
end
|
data/lib/hexapdf/rectangle.rb
CHANGED
@@ -43,30 +43,30 @@ module HexaPDF
|
|
43
43
|
# This class simplifies the usage of rectangles by automatically normalizing the coordinates so
|
44
44
|
# that they are in the order:
|
45
45
|
#
|
46
|
-
# [
|
46
|
+
# [left, bottom, right, top]
|
47
47
|
#
|
48
|
-
# where +
|
49
|
-
#
|
48
|
+
# where +left+ is the bottom left x-coordinate, +bottom+ is the bottom left y-coordinate, +right+
|
49
|
+
# is the top right x-coordinate and +top+ is the top right y-coordinate.
|
50
50
|
#
|
51
51
|
# See: PDF1.7 s7.9.5
|
52
52
|
class Rectangle < HexaPDF::Object
|
53
53
|
|
54
|
-
# Returns the x-coordinate of the
|
54
|
+
# Returns the x-coordinate of the bottom-left corner.
|
55
55
|
def left
|
56
56
|
value[0]
|
57
57
|
end
|
58
58
|
|
59
|
-
# Returns the x-coordinate of the
|
59
|
+
# Returns the x-coordinate of the top-right corner.
|
60
60
|
def right
|
61
61
|
value[2]
|
62
62
|
end
|
63
63
|
|
64
|
-
# Returns the y-coordinate of the
|
64
|
+
# Returns the y-coordinate of the bottom-left corner.
|
65
65
|
def bottom
|
66
66
|
value[1]
|
67
67
|
end
|
68
68
|
|
69
|
-
# Returns the y-coordinate of the
|
69
|
+
# Returns the y-coordinate of the top-right corner.
|
70
70
|
def top
|
71
71
|
value[3]
|
72
72
|
end
|
@@ -83,8 +83,8 @@ module HexaPDF
|
|
83
83
|
|
84
84
|
private
|
85
85
|
|
86
|
-
# Ensures that the value is an array containing four numbers that specify the
|
87
|
-
#
|
86
|
+
# Ensures that the value is an array containing four numbers that specify the bottom left and
|
87
|
+
# top right corner.
|
88
88
|
def after_data_change
|
89
89
|
super
|
90
90
|
unless value.kind_of?(Array) && value.size == 4 && value.all? {|i| i.kind_of?(Numeric) }
|
data/lib/hexapdf/stream.rb
CHANGED
@@ -116,10 +116,10 @@ module HexaPDF
|
|
116
116
|
|
117
117
|
define_field :Length, type: Integer # not required, will be auto-filled when writing
|
118
118
|
define_field :Filter, type: [Symbol, Array]
|
119
|
-
define_field :DecodeParms, type: [Dictionary,
|
119
|
+
define_field :DecodeParms, type: [Dictionary, Array]
|
120
120
|
define_field :F, type: :Filespec, version: '1.2'
|
121
121
|
define_field :FFilter, type: [Symbol, Array], version: '1.2'
|
122
|
-
define_field :FDecodeParms, type: [Dictionary,
|
122
|
+
define_field :FDecodeParms, type: [Dictionary, Array], version: '1.2'
|
123
123
|
define_field :DL, type: Integer
|
124
124
|
|
125
125
|
# Stream objects must always be indirect.
|
data/lib/hexapdf/type.rb
CHANGED
data/lib/hexapdf/type/action.rb
CHANGED
@@ -48,7 +48,7 @@ module HexaPDF
|
|
48
48
|
|
49
49
|
define_field :Type, type: Symbol, default: type
|
50
50
|
define_field :S, type: Symbol, required: true
|
51
|
-
define_field :Next, type: [Dictionary,
|
51
|
+
define_field :Next, type: [Dictionary, Array], version: '1.2'
|
52
52
|
|
53
53
|
end
|
54
54
|
|
@@ -46,7 +46,7 @@ module HexaPDF
|
|
46
46
|
define_field :T, type: String, version: '1.1'
|
47
47
|
define_field :Popup, type: :Annotation, version: '1.3'
|
48
48
|
define_field :CA, type: Numeric, default: 1.0, version: '1.4'
|
49
|
-
define_field :RC, type: [
|
49
|
+
define_field :RC, type: [Stream, String], version: '1.5'
|
50
50
|
define_field :CreationDate, type: PDFDate, version: '1.5'
|
51
51
|
define_field :IRT, type: Dictionary, version: '1.5'
|
52
52
|
define_field :Subj, type: String, version: '1.5'
|
data/lib/hexapdf/type/catalog.rb
CHANGED
@@ -61,7 +61,7 @@ module HexaPDF
|
|
61
61
|
define_field :PageMode, type: Symbol, default: :UseNone
|
62
62
|
define_field :Outlines, type: Dictionary, indirect: true
|
63
63
|
define_field :Threads, type: Array, version: '1.1'
|
64
|
-
define_field :OpenAction, type: [
|
64
|
+
define_field :OpenAction, type: [Dictionary, Array], version: '1.1'
|
65
65
|
define_field :AA, type: Dictionary, version: '1.4'
|
66
66
|
define_field :URI, type: Dictionary, version: '1.1'
|
67
67
|
define_field :AcroForm, type: Dictionary, version: '1.2'
|
@@ -44,7 +44,8 @@ module HexaPDF
|
|
44
44
|
|
45
45
|
DEFAULT_WIDTH = 1000 # :nodoc:
|
46
46
|
|
47
|
-
define_field :
|
47
|
+
define_field :BaseFont, type: Symbol, required: true
|
48
|
+
define_field :CIDSystemInfo, type: Dictionary, required: true
|
48
49
|
define_field :FontDescriptor, type: :FontDescriptor, indirect: true, required: true
|
49
50
|
define_field :DW, type: Integer, default: DEFAULT_WIDTH
|
50
51
|
define_field :W, type: Array
|
data/lib/hexapdf/type/font.rb
CHANGED
@@ -45,7 +45,6 @@ module HexaPDF
|
|
45
45
|
define_type :Font
|
46
46
|
|
47
47
|
define_field :Type, type: Symbol, required: true, default: type
|
48
|
-
define_field :BaseFont, type: Symbol, required: true
|
49
48
|
define_field :ToUnicode, type: Stream, version: '1.2'
|
50
49
|
|
51
50
|
# Font objects must always be indirect.
|
@@ -69,7 +69,7 @@ module HexaPDF
|
|
69
69
|
define_field :FontFile, type: Stream
|
70
70
|
define_field :FontFile2, type: Stream, version: '1.1'
|
71
71
|
define_field :FontFile3, type: Stream, version: '1.2'
|
72
|
-
define_field :CharSet, type: [
|
72
|
+
define_field :CharSet, type: [PDFByteString, String], version: '1.1'
|
73
73
|
|
74
74
|
define_field :Style, type: Dictionary
|
75
75
|
define_field :Lang, type: Symbol, version: '1.5'
|
@@ -48,7 +48,7 @@ module HexaPDF
|
|
48
48
|
define_field :LastChar, type: Integer
|
49
49
|
define_field :Widths, type: Array
|
50
50
|
define_field :FontDescriptor, type: :FontDescriptor, indirect: true
|
51
|
-
define_field :Encoding, type: [
|
51
|
+
define_field :Encoding, type: [Dictionary, Symbol]
|
52
52
|
|
53
53
|
# Returns the encoding object used for this font.
|
54
54
|
#
|
@@ -60,7 +60,7 @@ module HexaPDF
|
|
60
60
|
encoding = HexaPDF::Font::Encoding.for_name(val)
|
61
61
|
encoding = encoding_from_font if encoding.nil?
|
62
62
|
encoding
|
63
|
-
when HexaPDF::Dictionary
|
63
|
+
when HexaPDF::Dictionary
|
64
64
|
encoding = val[:BaseEncoding] && HexaPDF::Font::Encoding.for_name(val[:BaseEncoding])
|
65
65
|
encoding ||= if embedded? || symbolic?
|
66
66
|
encoding_from_font
|
@@ -164,7 +164,7 @@ module HexaPDF
|
|
164
164
|
super()
|
165
165
|
return if ignore_missing_font_fields
|
166
166
|
|
167
|
-
[:FirstChar, :LastChar, :Widths
|
167
|
+
[:FirstChar, :LastChar, :Widths].each do |field|
|
168
168
|
yield("Required field #{field} is not set", false) if self[field].nil?
|
169
169
|
end
|
170
170
|
if self[:Widths].length != (self[:LastChar] - self[:FirstChar] + 1)
|
@@ -40,6 +40,14 @@ module HexaPDF
|
|
40
40
|
class FontTrueType < FontSimple
|
41
41
|
|
42
42
|
define_field :Subtype, type: Symbol, required: true, default: :TrueType
|
43
|
+
define_field :BaseFont, type: Symbol, required: true
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def perform_validation
|
48
|
+
super
|
49
|
+
yield("Required field FontDescriptor is not set", false) if self[:FontDescriptor].nil?
|
50
|
+
end
|
43
51
|
|
44
52
|
end
|
45
53
|
|
@@ -49,7 +49,8 @@ module HexaPDF
|
|
49
49
|
class FontType0 < Font
|
50
50
|
|
51
51
|
define_field :Subtype, type: Symbol, required: true, default: :Type0
|
52
|
-
define_field :
|
52
|
+
define_field :BaseFont, type: Symbol, required: true
|
53
|
+
define_field :Encoding, type: [Stream, Symbol], required: true
|
53
54
|
define_field :DescendantFonts, type: Array, required: true
|
54
55
|
|
55
56
|
# Returns the CID font of this type 0 font.
|
@@ -100,6 +100,7 @@ module HexaPDF
|
|
100
100
|
end
|
101
101
|
|
102
102
|
define_field :Subtype, type: Symbol, required: true, default: :Type1
|
103
|
+
define_field :BaseFont, type: Symbol, required: true
|
103
104
|
|
104
105
|
# Returns the unscaled width of the given code point in glyph units, or 0 if the width for the
|
105
106
|
# code point is missing.
|
@@ -153,7 +154,12 @@ module HexaPDF
|
|
153
154
|
|
154
155
|
# Validates the Type1 font dictionary.
|
155
156
|
def perform_validation
|
156
|
-
|
157
|
+
std_font = StandardFonts.standard_font?(self[:BaseFont])
|
158
|
+
super(ignore_missing_font_fields: std_font)
|
159
|
+
|
160
|
+
if !std_font && self[:FontDescriptor].nil?
|
161
|
+
yield("Required field FontDescriptor is not set", false)
|
162
|
+
end
|
157
163
|
end
|
158
164
|
|
159
165
|
end
|