hexapdf 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -0
- data/CONTRIBUTERS +1 -1
- data/README.md +5 -5
- data/VERSION +1 -1
- data/examples/emoji-smile.png +0 -0
- data/examples/emoji-wink.png +0 -0
- data/examples/graphics.rb +9 -8
- data/examples/standard_pdf_fonts.rb +2 -1
- data/examples/text_box_alignment.rb +47 -0
- data/examples/text_box_inline_boxes.rb +56 -0
- data/examples/text_box_line_wrapping.rb +57 -0
- data/examples/text_box_shapes.rb +166 -0
- data/examples/text_box_styling.rb +72 -0
- data/examples/truetype.rb +3 -4
- data/lib/hexapdf/cli/optimize.rb +2 -2
- data/lib/hexapdf/configuration.rb +8 -6
- data/lib/hexapdf/content/canvas.rb +8 -5
- data/lib/hexapdf/content/parser.rb +3 -2
- data/lib/hexapdf/content/processor.rb +14 -3
- data/lib/hexapdf/document.rb +1 -0
- data/lib/hexapdf/document/fonts.rb +2 -1
- data/lib/hexapdf/document/pages.rb +23 -0
- data/lib/hexapdf/font/invalid_glyph.rb +78 -0
- data/lib/hexapdf/font/true_type/font.rb +14 -3
- data/lib/hexapdf/font/true_type/table.rb +1 -0
- data/lib/hexapdf/font/true_type/table/cmap.rb +1 -1
- data/lib/hexapdf/font/true_type/table/cmap_subtable.rb +1 -0
- data/lib/hexapdf/font/true_type/table/glyf.rb +4 -0
- data/lib/hexapdf/font/true_type/table/kern.rb +170 -0
- data/lib/hexapdf/font/true_type/table/post.rb +5 -1
- data/lib/hexapdf/font/true_type_wrapper.rb +71 -24
- data/lib/hexapdf/font/type1/afm_parser.rb +3 -2
- data/lib/hexapdf/font/type1/character_metrics.rb +0 -9
- data/lib/hexapdf/font/type1/font.rb +11 -0
- data/lib/hexapdf/font/type1/font_metrics.rb +6 -1
- data/lib/hexapdf/font/type1_wrapper.rb +51 -7
- data/lib/hexapdf/font_loader/standard14.rb +1 -1
- data/lib/hexapdf/layout.rb +51 -0
- data/lib/hexapdf/layout/inline_box.rb +95 -0
- data/lib/hexapdf/layout/line_fragment.rb +333 -0
- data/lib/hexapdf/layout/numeric_refinements.rb +56 -0
- data/lib/hexapdf/layout/style.rb +365 -0
- data/lib/hexapdf/layout/text_box.rb +727 -0
- data/lib/hexapdf/layout/text_fragment.rb +206 -0
- data/lib/hexapdf/layout/text_shaper.rb +155 -0
- data/lib/hexapdf/task.rb +0 -1
- data/lib/hexapdf/task/dereference.rb +1 -1
- data/lib/hexapdf/tokenizer.rb +3 -2
- data/lib/hexapdf/type/font_descriptor.rb +2 -1
- data/lib/hexapdf/type/font_type0.rb +3 -1
- data/lib/hexapdf/type/form.rb +12 -4
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/common_tokenizer_tests.rb +7 -0
- data/test/hexapdf/content/common.rb +8 -0
- data/test/hexapdf/content/test_canvas.rb +10 -22
- data/test/hexapdf/content/test_processor.rb +4 -1
- data/test/hexapdf/document/test_pages.rb +16 -0
- data/test/hexapdf/font/test_invalid_glyph.rb +34 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +25 -11
- data/test/hexapdf/font/test_type1_wrapper.rb +26 -10
- data/test/hexapdf/font/true_type/table/common.rb +27 -0
- data/test/hexapdf/font/true_type/table/test_cmap.rb +14 -20
- data/test/hexapdf/font/true_type/table/test_cmap_subtable.rb +7 -0
- data/test/hexapdf/font/true_type/table/test_glyf.rb +8 -6
- data/test/hexapdf/font/true_type/table/test_head.rb +9 -13
- data/test/hexapdf/font/true_type/table/test_hhea.rb +16 -23
- data/test/hexapdf/font/true_type/table/test_hmtx.rb +4 -7
- data/test/hexapdf/font/true_type/table/test_kern.rb +61 -0
- data/test/hexapdf/font/true_type/table/test_loca.rb +7 -13
- data/test/hexapdf/font/true_type/table/test_maxp.rb +4 -9
- data/test/hexapdf/font/true_type/table/test_name.rb +14 -17
- data/test/hexapdf/font/true_type/table/test_os2.rb +3 -5
- data/test/hexapdf/font/true_type/table/test_post.rb +21 -19
- data/test/hexapdf/font/true_type/test_font.rb +4 -0
- data/test/hexapdf/font/type1/common.rb +6 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +9 -0
- data/test/hexapdf/font/type1/test_font.rb +6 -0
- data/test/hexapdf/layout/test_inline_box.rb +40 -0
- data/test/hexapdf/layout/test_line_fragment.rb +206 -0
- data/test/hexapdf/layout/test_style.rb +143 -0
- data/test/hexapdf/layout/test_text_box.rb +640 -0
- data/test/hexapdf/layout/test_text_fragment.rb +208 -0
- data/test/hexapdf/layout/test_text_shaper.rb +64 -0
- data/test/hexapdf/task/test_dereference.rb +1 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_font_descriptor.rb +4 -2
- data/test/hexapdf/type/test_font_type0.rb +7 -0
- data/test/hexapdf/type/test_form.rb +12 -0
- metadata +29 -2
@@ -0,0 +1,206 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
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
|
+
require 'hexapdf/layout/style'
|
35
|
+
require 'hexapdf/layout/text_shaper'
|
36
|
+
require 'hexapdf/layout/numeric_refinements'
|
37
|
+
|
38
|
+
module HexaPDF
|
39
|
+
module Layout
|
40
|
+
|
41
|
+
# A TextFragment describes an optionally kerned piece of text that shares the same font, font
|
42
|
+
# size and other properties.
|
43
|
+
#
|
44
|
+
# Its items are either glyph objects of the font or numeric values describing kerning
|
45
|
+
# information. All returned measurement values are in text space units. If the items or the
|
46
|
+
# style are changed, the #clear_cache has to be called. Otherwise the measurements may not be
|
47
|
+
# correct!
|
48
|
+
#
|
49
|
+
# The items of a text fragment may be frozen to indicate that the fragment is potentially used
|
50
|
+
# multiple times.
|
51
|
+
#
|
52
|
+
# The rectangle with the lower-left corner (#x_min, #y_min) and the upper right corner (#x_max,
|
53
|
+
# #y_max) describes the minimum bounding box of the whole text fragment and is usually *not*
|
54
|
+
# equal to the box (0, 0)-(#width, #height).
|
55
|
+
class TextFragment
|
56
|
+
|
57
|
+
using NumericRefinements
|
58
|
+
|
59
|
+
# Creates a new TextFragment object for the given text, shapes it and returns it.
|
60
|
+
#
|
61
|
+
# The style of the text fragment can be specified using additional options, of which font is
|
62
|
+
# mandatory.
|
63
|
+
def self.create(text, font:, **options)
|
64
|
+
fragment = new(items: font.decode_utf8(text), style: Style.new(font: font, **options))
|
65
|
+
TextShaper.new.shape_text(fragment)
|
66
|
+
end
|
67
|
+
|
68
|
+
# The items (glyphs and kerning values) of the text fragment.
|
69
|
+
attr_accessor :items
|
70
|
+
|
71
|
+
# The style to be applied.
|
72
|
+
#
|
73
|
+
# Only the following properties are used: Style#font, Style#font_size,
|
74
|
+
# Style#horizontal_scaling, Style#character_spacing, Style#word_spacing and Style#text_rise.
|
75
|
+
attr_reader :style
|
76
|
+
|
77
|
+
# Creates a new TextFragment object with the given items and style.
|
78
|
+
def initialize(items:, style: Style.new)
|
79
|
+
@items = items || []
|
80
|
+
@style = style
|
81
|
+
end
|
82
|
+
|
83
|
+
# Draws the text onto the canvas at the given position.
|
84
|
+
#
|
85
|
+
# Before the text is drawn using HexaPDF::Content;:Canvas#show_glyphs, the text properties
|
86
|
+
# mentioned in the description of #style are set.
|
87
|
+
def draw(canvas, x, y)
|
88
|
+
canvas.move_text_cursor(offset: [x, y])
|
89
|
+
canvas.font(style.font, size: style.font_size).
|
90
|
+
horizontal_scaling(style.horizontal_scaling).
|
91
|
+
character_spacing(style.character_spacing).
|
92
|
+
word_spacing(style.word_spacing).
|
93
|
+
text_rise(style.text_rise)
|
94
|
+
canvas.show_glyphs_only(items)
|
95
|
+
end
|
96
|
+
|
97
|
+
# The minimum x-coordinate of the first glyph.
|
98
|
+
def x_min
|
99
|
+
@x_min ||= calculate_x_min
|
100
|
+
end
|
101
|
+
|
102
|
+
# The maximum x-coordinate of the last glyph.
|
103
|
+
def x_max
|
104
|
+
@x_max ||= calculate_x_max
|
105
|
+
end
|
106
|
+
|
107
|
+
# The minimum y-coordinate, calculated using the scaled descender of the font.
|
108
|
+
def y_min
|
109
|
+
@y_min ||= style.scaled_font_descender + style.text_rise
|
110
|
+
end
|
111
|
+
|
112
|
+
# The maximum y-coordinate, calculated using the scaled ascender of the font.
|
113
|
+
def y_max
|
114
|
+
@y_max ||= style.scaled_font_ascender + style.text_rise
|
115
|
+
end
|
116
|
+
|
117
|
+
# The minimum y-coordinate of any item.
|
118
|
+
def exact_y_min
|
119
|
+
@exact_y_min ||= (@items.min_by(&:y_min)&.y_min || 0) * style.font_size / 1000.0 +
|
120
|
+
style.text_rise
|
121
|
+
end
|
122
|
+
|
123
|
+
# The maximum y-coordinate of any item.
|
124
|
+
def exact_y_max
|
125
|
+
@exact_y_max ||= (@items.max_by(&:y_max)&.y_max || 0) * style.font_size / 1000.0 +
|
126
|
+
style.text_rise
|
127
|
+
end
|
128
|
+
|
129
|
+
# The width of the text fragment.
|
130
|
+
#
|
131
|
+
# It is the sum of the widths of its items and is calculated by using the algorithm presented
|
132
|
+
# in PDF1.7 s9.4.4. By using kerning values as the first and/or last items, the text contained
|
133
|
+
# in the fragment may spill over the left and/or right boundary.
|
134
|
+
def width
|
135
|
+
@width ||= @items.sum {|item| style.scaled_item_width(item)}
|
136
|
+
end
|
137
|
+
|
138
|
+
# The height of the text fragment.
|
139
|
+
#
|
140
|
+
# It is calculated as the difference of the maximum of the +y_max+ values and the minimum of
|
141
|
+
# the +y_min+ values of the items. However, the text rise value is also taken into account so
|
142
|
+
# that the baseline is always *inside* the bounds. For example, if a large negative text rise
|
143
|
+
# value is used, the baseline will be equal to the top boundary; if a large positive value is
|
144
|
+
# used, it will be equal to the bottom boundary.
|
145
|
+
def height
|
146
|
+
@height ||= [y_max, 0].max - [y_min, 0].min
|
147
|
+
end
|
148
|
+
|
149
|
+
# Returns the vertical alignment inside a line which is always :text for text fragments.
|
150
|
+
#
|
151
|
+
# See LineFragment for details.
|
152
|
+
def valign
|
153
|
+
:text
|
154
|
+
end
|
155
|
+
|
156
|
+
# Clears all cached values.
|
157
|
+
#
|
158
|
+
# This method needs to be called if the fragment's items or attributes are changed!
|
159
|
+
def clear_cache
|
160
|
+
@x_min = @x_max = @y_min = @y_max = @exact_y_min = @exact_y_max = @width = @height = nil
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
# :nodoc:
|
165
|
+
def inspect
|
166
|
+
"#<#{self.class.name} #{items.inspect}>"
|
167
|
+
end
|
168
|
+
|
169
|
+
private
|
170
|
+
|
171
|
+
def calculate_x_min
|
172
|
+
if !@items.empty? && !@items[0].kind_of?(Numeric)
|
173
|
+
@items[0].x_min * style.scaled_font_size
|
174
|
+
else
|
175
|
+
@items.inject(0) do |sum, item|
|
176
|
+
sum += item.x_min * style.scaled_font_size
|
177
|
+
break sum unless item.kind_of?(Numeric)
|
178
|
+
sum
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def calculate_x_max
|
184
|
+
if !@items.empty? && !@items[0].kind_of?(Numeric)
|
185
|
+
width - scaled_glyph_right_side_bearing(@items[-1])
|
186
|
+
else
|
187
|
+
@items.reverse_each.inject(width) do |sum, item|
|
188
|
+
if item.kind_of?(Numeric)
|
189
|
+
sum + item * style.scaled_font_size
|
190
|
+
else
|
191
|
+
break sum - scaled_glyph_right_side_bearing(item)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def scaled_glyph_right_side_bearing(glyph)
|
198
|
+
(glyph.x_max <= 0 ? 0 : glyph.width - glyph.x_max) * style.scaled_font_size +
|
199
|
+
style.scaled_character_spacing +
|
200
|
+
(glyph.apply_word_spacing? ? style.scaled_word_spacing : 0)
|
201
|
+
end
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
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
|
+
require 'hexapdf/layout/numeric_refinements'
|
35
|
+
|
36
|
+
module HexaPDF
|
37
|
+
module Layout
|
38
|
+
|
39
|
+
using NumericRefinements
|
40
|
+
|
41
|
+
# This class is used to perform text shaping, i.e. changing the position of glyphs (e.g. for
|
42
|
+
# kerning) or substituting one or more glyphs for other glyphs (e.g. for ligatures).
|
43
|
+
#
|
44
|
+
# Status of the implementation:
|
45
|
+
#
|
46
|
+
# * All text shaping functionality possible for Type1 fonts is implemented, i.e. kerning and
|
47
|
+
# ligature substitution.
|
48
|
+
#
|
49
|
+
# * For TrueType fonts only kerning via the 'kern' table is implemented.
|
50
|
+
class TextShaper
|
51
|
+
|
52
|
+
# Shapes the given text fragment in-place.
|
53
|
+
#
|
54
|
+
# The following shaping options, retrieved from the text fragment's Style#font_features, are
|
55
|
+
# supported:
|
56
|
+
#
|
57
|
+
# :kern:: Pair-wise kerning.
|
58
|
+
# :liga:: Ligature substitution.
|
59
|
+
def shape_text(text_fragment)
|
60
|
+
font = text_fragment.style.font
|
61
|
+
if text_fragment.style.font_features[:liga] && font.wrapped_font.features.include?(:liga)
|
62
|
+
if font.font_type == :Type1
|
63
|
+
process_type1_ligatures(text_fragment)
|
64
|
+
end
|
65
|
+
text_fragment.clear_cache
|
66
|
+
end
|
67
|
+
if text_fragment.style.font_features[:kern] && font.wrapped_font.features.include?(:kern)
|
68
|
+
if font.font_type == :TrueType
|
69
|
+
process_true_type_kerning(text_fragment)
|
70
|
+
elsif font.font_type == :Type1
|
71
|
+
process_type1_kerning(text_fragment)
|
72
|
+
end
|
73
|
+
text_fragment.clear_cache
|
74
|
+
end
|
75
|
+
text_fragment
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
# Processes the text fragment and substitutes ligatures.
|
81
|
+
def process_type1_ligatures(text_fragment)
|
82
|
+
items = text_fragment.items
|
83
|
+
font = text_fragment.style.font
|
84
|
+
pairs = font.wrapped_font.metrics.ligature_pairs
|
85
|
+
each_glyph_pair(items) do |left_item, right_item, left, right|
|
86
|
+
if (ligature = pairs.dig(left_item.id, right_item.id))
|
87
|
+
items[left..right] = font.glyph(ligature)
|
88
|
+
left
|
89
|
+
else
|
90
|
+
right
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Processes the text fragment and does pair-wise kerning.
|
96
|
+
def process_type1_kerning(text_fragment)
|
97
|
+
pairs = text_fragment.style.font.wrapped_font.metrics.kerning_pairs
|
98
|
+
items = text_fragment.items
|
99
|
+
each_glyph_pair(items) do |left_item, right_item, left, right|
|
100
|
+
if (left + 1 == right) && (kerning = pairs.dig(left_item.id, right_item.id))
|
101
|
+
items.insert(right, -kerning)
|
102
|
+
right + 1
|
103
|
+
else
|
104
|
+
right
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# Processes the text fragment and does pair-wise kerning.
|
110
|
+
def process_true_type_kerning(text_fragment)
|
111
|
+
font = text_fragment.style.font
|
112
|
+
table = font.wrapped_font[:kern].horizontal_kerning_subtable
|
113
|
+
items = text_fragment.items
|
114
|
+
each_glyph_pair(items) do |left_item, right_item, left, right|
|
115
|
+
if (left + 1 == right) && (kerning = table.kern(left_item.id, right_item.id))
|
116
|
+
items.insert(right, -kerning * font.scaling_factor)
|
117
|
+
right + 1
|
118
|
+
else
|
119
|
+
right
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# :call-seq:
|
125
|
+
# each_glyph_pair(items) {|left_item, right_item, left, right}
|
126
|
+
#
|
127
|
+
# Yields each pair of glyphs of the items array (so left must not be right + 1 if between two
|
128
|
+
# glyphs are one or more kerning values).
|
129
|
+
#
|
130
|
+
# The return value of the block is taken as the next *left* item position.
|
131
|
+
def each_glyph_pair(items)
|
132
|
+
left = 0
|
133
|
+
left_item = items[left]
|
134
|
+
right = 1
|
135
|
+
right_item = items[right]
|
136
|
+
while left_item && right_item
|
137
|
+
if left_item.kind_of?(Numeric)
|
138
|
+
left += 1
|
139
|
+
left_item = items[left]
|
140
|
+
right = left + 1
|
141
|
+
elsif right_item.kind_of?(Numeric)
|
142
|
+
right += 1
|
143
|
+
else
|
144
|
+
left = yield(left_item, right_item, left, right)
|
145
|
+
left_item = items[left]
|
146
|
+
right = left + 1
|
147
|
+
end
|
148
|
+
right_item = items[right]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|
155
|
+
end
|
data/lib/hexapdf/task.rb
CHANGED
data/lib/hexapdf/tokenizer.rb
CHANGED
@@ -251,10 +251,11 @@ module HexaPDF
|
|
251
251
|
prepare_string_scanner(10)
|
252
252
|
tmp = Reference.new(tmp, @ss[1].to_i) if @ss.scan(REFERENCE_RE)
|
253
253
|
tmp
|
254
|
-
|
255
|
-
val = @ss.scan(/[+-]?(?:\d+\.\d*|\.\d+)/)
|
254
|
+
elsif (val = @ss.scan(/[+-]?(?:\d+\.\d*|\.\d+)/))
|
256
255
|
val << '0'.freeze if val.getbyte(-1) == 46 # dot '.'
|
257
256
|
Float(val)
|
257
|
+
else
|
258
|
+
parse_keyword
|
258
259
|
end
|
259
260
|
end
|
260
261
|
|
@@ -99,7 +99,8 @@ module HexaPDF
|
|
99
99
|
|
100
100
|
descent = self[:Descent]
|
101
101
|
if descent && descent > 0
|
102
|
-
yield("The /Descent value needs to be a negative number",
|
102
|
+
yield("The /Descent value needs to be a negative number", true)
|
103
|
+
self[:Descent] = -descent
|
103
104
|
end
|
104
105
|
end
|
105
106
|
|
@@ -54,7 +54,9 @@ module HexaPDF
|
|
54
54
|
|
55
55
|
# Returns the CID font of this type 0 font.
|
56
56
|
def descendant_font
|
57
|
-
document.cache(@data, :descendant_font)
|
57
|
+
document.cache(@data, :descendant_font) do
|
58
|
+
document.wrap(document.deref(self[:DescendantFonts][0]))
|
59
|
+
end
|
58
60
|
end
|
59
61
|
|
60
62
|
# Returns the writing mode which is either :horizontal or :vertical.
|
data/lib/hexapdf/type/form.rb
CHANGED
@@ -89,12 +89,20 @@ module HexaPDF
|
|
89
89
|
self[:Resources] ||= document.wrap({}, type: :XXResources)
|
90
90
|
end
|
91
91
|
|
92
|
-
# Processes the content
|
92
|
+
# Processes the content stream of the form XObject with the given processor object.
|
93
|
+
#
|
94
|
+
# The +original_resources+ argument has to be set to a page's resources if this form XObject
|
95
|
+
# is processed as part of this page.
|
93
96
|
#
|
94
97
|
# See: HexaPDF::Content::Processor
|
95
|
-
def process_contents(processor)
|
96
|
-
|
97
|
-
|
98
|
+
def process_contents(processor, original_resources: nil)
|
99
|
+
processor.resources = if self[:Resources]
|
100
|
+
self[:Resources]
|
101
|
+
elsif original_resources
|
102
|
+
original_resources
|
103
|
+
else
|
104
|
+
document.wrap({}, type: :XXResources)
|
105
|
+
end
|
98
106
|
Content::Parser.parse(contents, processor)
|
99
107
|
end
|
100
108
|
|