hexapdf 0.14.0 → 0.15.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 +106 -0
- data/lib/hexapdf/cli/form.rb +30 -8
- data/lib/hexapdf/configuration.rb +19 -4
- data/lib/hexapdf/dictionary.rb +8 -2
- data/lib/hexapdf/dictionary_fields.rb +1 -1
- data/lib/hexapdf/encryption/security_handler.rb +7 -2
- data/lib/hexapdf/error.rb +4 -3
- data/lib/hexapdf/filter.rb +1 -0
- data/lib/hexapdf/filter/crypt.rb +60 -0
- data/lib/hexapdf/font/true_type/subsetter.rb +16 -3
- data/lib/hexapdf/font/true_type/table/post.rb +15 -10
- data/lib/hexapdf/font/type1/afm_parser.rb +2 -1
- data/lib/hexapdf/font_loader/from_configuration.rb +2 -2
- data/lib/hexapdf/font_loader/from_file.rb +18 -8
- data/lib/hexapdf/importer.rb +3 -2
- data/lib/hexapdf/parser.rb +55 -15
- data/lib/hexapdf/pdf_array.rb +4 -1
- data/lib/hexapdf/revision.rb +16 -0
- data/lib/hexapdf/serializer.rb +43 -10
- data/lib/hexapdf/tokenizer.rb +44 -3
- data/lib/hexapdf/type/acro_form.rb +1 -0
- data/lib/hexapdf/type/acro_form/appearance_generator.rb +32 -17
- data/lib/hexapdf/type/acro_form/button_field.rb +8 -4
- data/lib/hexapdf/type/acro_form/field.rb +1 -0
- data/lib/hexapdf/type/acro_form/form.rb +37 -0
- data/lib/hexapdf/type/acro_form/signature_field.rb +223 -0
- data/lib/hexapdf/type/annotation.rb +13 -9
- data/lib/hexapdf/type/annotations/widget.rb +3 -1
- data/lib/hexapdf/type/font_descriptor.rb +9 -2
- data/lib/hexapdf/type/page.rb +81 -0
- data/lib/hexapdf/type/resources.rb +4 -0
- data/lib/hexapdf/type/xref_stream.rb +7 -0
- data/lib/hexapdf/version.rb +1 -1
- data/test/hexapdf/encryption/test_security_handler.rb +15 -0
- data/test/hexapdf/filter/test_crypt.rb +21 -0
- data/test/hexapdf/font/true_type/table/test_post.rb +1 -1
- data/test/hexapdf/font/true_type/test_subsetter.rb +10 -0
- data/test/hexapdf/font/type1/test_afm_parser.rb +5 -0
- data/test/hexapdf/font_loader/test_from_configuration.rb +7 -3
- data/test/hexapdf/font_loader/test_from_file.rb +7 -0
- data/test/hexapdf/test_dictionary.rb +5 -0
- data/test/hexapdf/test_dictionary_fields.rb +7 -0
- data/test/hexapdf/test_parser.rb +94 -2
- data/test/hexapdf/test_revision.rb +21 -0
- data/test/hexapdf/test_serializer.rb +10 -0
- data/test/hexapdf/test_tokenizer.rb +50 -0
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/acro_form/test_appearance_generator.rb +24 -3
- data/test/hexapdf/type/acro_form/test_button_field.rb +13 -7
- data/test/hexapdf/type/acro_form/test_field.rb +5 -0
- data/test/hexapdf/type/acro_form/test_form.rb +46 -2
- data/test/hexapdf/type/acro_form/test_signature_field.rb +38 -0
- data/test/hexapdf/type/annotations/test_widget.rb +2 -0
- data/test/hexapdf/type/test_annotation.rb +20 -10
- data/test/hexapdf/type/test_font_descriptor.rb +7 -0
- data/test/hexapdf/type/test_page.rb +187 -49
- data/test/hexapdf/type/test_resources.rb +6 -0
- data/test/hexapdf/type/test_xref_stream.rb +7 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bcd3bc77b70872416b1377b4fdf97804de083cf4d7213dfd200738fd8b2adae7
|
4
|
+
data.tar.gz: 53a8a850610a744570999cf56c656d6bd65c8ab691a5658b81172111bdd44804
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 54c99dbd44c4ae146496912f295982d47bb5ca297d4d2b76475c1f3151670068cd3be0c2dedee413b0a52e493383229bbd36d819ff2db2a9c04b377731cc107e
|
7
|
+
data.tar.gz: 8426e942709633b921f7a644e01b645d555cfcdecbdbd25bae03d0154cf07df3a3cd1f108adb16a8b449801c203014698a6b382133bbb63033a1d590351b61f7
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,109 @@
|
|
1
|
+
## 0.15.0 - 2021-04-12
|
2
|
+
|
3
|
+
### Added
|
4
|
+
|
5
|
+
* [HexaPDF::Type::Page#flatten_annotations] for flattening the annotations of a
|
6
|
+
page
|
7
|
+
* [HexaPDF::Type::AcroForm::Form#flatten] for flattening interactive forms
|
8
|
+
* [HexaPDF::Revision#update] for updating the stored wrapper class of a PDF
|
9
|
+
object
|
10
|
+
* [HexaPDF::Type::AcroForm::SignatureField] for working with AcroForm signature
|
11
|
+
fields
|
12
|
+
* Support for form field flattening to the `hexapdf form` CLI command
|
13
|
+
|
14
|
+
### Changed
|
15
|
+
|
16
|
+
* **Breaking change**: Overhauled the interface for accessing appearances of
|
17
|
+
annotations to make it more convenient
|
18
|
+
* Validation of [HexaPDF::Type::FontDescriptor] to delete invalid `/FontWeight`
|
19
|
+
value
|
20
|
+
* [HexaPDF::MalformedPDFError#pos] an accessor instead of a reader and update
|
21
|
+
the exception message
|
22
|
+
* Configuration option 'acro_form.fallback_font' to allow a callable object for
|
23
|
+
more advanced fallback font handling
|
24
|
+
|
25
|
+
### Fixed
|
26
|
+
|
27
|
+
* [HexaPDF::Type::Annotations::Widget#background_color] to correctly handle
|
28
|
+
empty background color arrays
|
29
|
+
* [HexaPDF::Type::AcroForm::Field#delete_widget] to update the wrapper object
|
30
|
+
stored in the document in case the widget is embedded
|
31
|
+
* Processing of invalid PDF files containing a space,CR,LF combination after
|
32
|
+
the 'stream' keyword
|
33
|
+
* Cross-reference stream reconstruction with respect to detection of linearized
|
34
|
+
files
|
35
|
+
* Detection of existing appearances for AcroForm push button fields when
|
36
|
+
creating appearances
|
37
|
+
|
38
|
+
|
39
|
+
## 0.14.4 - 2021-02-27
|
40
|
+
|
41
|
+
### Added
|
42
|
+
|
43
|
+
* Support for the Crypt filters
|
44
|
+
|
45
|
+
### Changed
|
46
|
+
|
47
|
+
* [HexaPDF::MalformedPDFError] to make the `pos` argument optional
|
48
|
+
|
49
|
+
### Fixed
|
50
|
+
|
51
|
+
* Handling of invalid floating point numbers NaN, Inf and -Inf when serializing
|
52
|
+
* Processing of invalid PDF files containing NaN and Inf instead of numbers
|
53
|
+
* Bug in Type1 font AFM parser that occured if the file doesn't end with a new
|
54
|
+
line character
|
55
|
+
* Cross-reference table reconstruction to handle the case of an entry specifying
|
56
|
+
a non-existent indirect object
|
57
|
+
* Cross-reference table reconstruction to handle trailers specified by cross-
|
58
|
+
reference streams
|
59
|
+
* Cross-reference table reconstruction to use the set security handle for
|
60
|
+
decrypting indirect objects
|
61
|
+
* Parsing of cross-reference streams where data is missing
|
62
|
+
|
63
|
+
|
64
|
+
## 0.14.3 - 2021-02-16
|
65
|
+
|
66
|
+
### Fixed
|
67
|
+
|
68
|
+
* Bug in [HexaPDF::Font::TrueType::Subsetter#use_glyph] which lead to corrupt
|
69
|
+
text output
|
70
|
+
* [HexaPDF::Serializer] to handle infinite recursion problem
|
71
|
+
* Cross-reference table reconstruction to avoid an O(n^2) performance problem
|
72
|
+
* [HexaPDF::Type::Resources] validation to handle an invalid `/ProcSet` entry
|
73
|
+
containing a single value instead of an array
|
74
|
+
* Processing of invalid PDF files missing a required value in appearance streams
|
75
|
+
* Processing of invalid empty arrays that should be rectangles by converting
|
76
|
+
them to PDF null objects
|
77
|
+
* Processing of invalid PDF files containing indirect objects with offset 0
|
78
|
+
* Processing of invalid PDF files containing a space/CR or space/LF combination
|
79
|
+
after the 'stream' keyword
|
80
|
+
|
81
|
+
|
82
|
+
## 0.14.2 - 2021-01-22
|
83
|
+
|
84
|
+
### Fixed
|
85
|
+
|
86
|
+
* [HexaPDF::Font::TrueType::Subsetter#use_glyph] to really avoid using subset
|
87
|
+
glyph ID 41 (`)`)
|
88
|
+
|
89
|
+
|
90
|
+
## 0.14.1 - 2021-01-21
|
91
|
+
|
92
|
+
### Changed
|
93
|
+
|
94
|
+
* Validation message when checking for allowed values to include the invalid
|
95
|
+
object
|
96
|
+
* [HexaPDF::FontLoader::FromFile] to allow (re)using an existing font object
|
97
|
+
* [HexaPDF::Importer] internals to avoid problems with retained memory
|
98
|
+
|
99
|
+
### Fixed
|
100
|
+
|
101
|
+
* Parsing of invalid PDF files where whitespace is missing after the integer
|
102
|
+
value of an indirect object
|
103
|
+
* [HexaPDF::Dictionary] so that adding new key-value pairs during validation is
|
104
|
+
possible
|
105
|
+
|
106
|
+
|
1
107
|
## 0.14.0 - 2020-12-30
|
2
108
|
|
3
109
|
### Added
|
data/lib/hexapdf/cli/form.rb
CHANGED
@@ -52,18 +52,26 @@ module HexaPDF
|
|
52
52
|
If the the output file name is not given, all form fields are listed in page order. Use
|
53
53
|
the global --verbose option to show additional information like field type and location.
|
54
54
|
|
55
|
-
If the output file name is given, the fields can be
|
56
|
-
|
57
|
-
|
55
|
+
If the output file name is given, the fields can be filled out interactively, via a
|
56
|
+
template or just flattened by using the respective options. Form field flattening can also
|
57
|
+
be activated in addition to filling out the form. If neither --fill, --template nor
|
58
|
+
--flatten is specified, --fill is implied.
|
58
59
|
EOF
|
59
60
|
|
60
61
|
options.on("--password PASSWORD", "-p", String,
|
61
62
|
"The password for decryption. Use - for reading from standard input.") do |pwd|
|
62
63
|
@password = (pwd == '-' ? read_password : pwd)
|
63
64
|
end
|
65
|
+
options.on("--fill", "Fill out the form") do
|
66
|
+
@fill = true
|
67
|
+
end
|
64
68
|
options.on("--template TEMPLATE_FILE", "-t TEMPLATE_FILE",
|
65
|
-
"Use the template file for the field values") do |template|
|
69
|
+
"Use the template file for the field values (implies --fill)") do |template|
|
66
70
|
@template = template
|
71
|
+
@fill = true
|
72
|
+
end
|
73
|
+
options.on('--flatten', 'Flatten the form fields') do
|
74
|
+
@flatten = true
|
67
75
|
end
|
68
76
|
options.on("--[no-]viewer-override", "Let the PDF viewer override the visual " \
|
69
77
|
"appearance. Default: use setting from input PDF") do |need_appearances|
|
@@ -75,6 +83,8 @@ module HexaPDF
|
|
75
83
|
end
|
76
84
|
|
77
85
|
@password = nil
|
86
|
+
@fill = false
|
87
|
+
@flatten = false
|
78
88
|
@template = nil
|
79
89
|
@need_appearances = nil
|
80
90
|
@incremental = true
|
@@ -82,16 +92,28 @@ module HexaPDF
|
|
82
92
|
|
83
93
|
def execute(in_file, out_file = nil) #:nodoc:
|
84
94
|
maybe_raise_on_existing_file(out_file) if out_file
|
95
|
+
if (@fill || @flatten) && !out_file
|
96
|
+
raise "Output file missing"
|
97
|
+
end
|
85
98
|
with_document(in_file, password: @password, out_file: out_file,
|
86
99
|
incremental: @incremental) do |doc|
|
87
100
|
if !doc.acro_form
|
88
101
|
raise "This PDF doesn't contain an interactive form"
|
89
102
|
elsif out_file
|
90
103
|
doc.acro_form[:NeedAppearances] = @need_appearances unless @need_appearances.nil?
|
91
|
-
if @
|
92
|
-
|
93
|
-
|
94
|
-
|
104
|
+
if @fill || !@flatten
|
105
|
+
if @template
|
106
|
+
fill_form_with_template(doc)
|
107
|
+
else
|
108
|
+
fill_form(doc)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
if @flatten
|
112
|
+
unless doc.acro_form.flatten.empty?
|
113
|
+
$stderr.puts "Warning: Not all form fields could be flattened"
|
114
|
+
doc.catalog.delete(:AcroForm)
|
115
|
+
doc.delete(doc.acro_form)
|
116
|
+
end
|
95
117
|
end
|
96
118
|
else
|
97
119
|
list_form_fields(doc)
|
@@ -164,9 +164,20 @@ module HexaPDF
|
|
164
164
|
# acro_form.fallback_font::
|
165
165
|
# The font that should be used when a variable text field references a font that cannot be used.
|
166
166
|
#
|
167
|
-
# Can
|
168
|
-
#
|
169
|
-
#
|
167
|
+
# Can be one of the following:
|
168
|
+
#
|
169
|
+
# * The name of a font, like 'Helvetica'.
|
170
|
+
#
|
171
|
+
# * An array consisting of the font name and a hash of font options, like ['Helvetica',
|
172
|
+
# variant: :italic].
|
173
|
+
#
|
174
|
+
# * A callable object receiving the field and the font object (or +nil+ if no valid font object
|
175
|
+
# was found) and which has to return either a font name or an array consisting of the font
|
176
|
+
# name and a hash of font options. This way the response can be different depending on the
|
177
|
+
# original font and it would also allow e.g. modifying the configured fonts to add custom
|
178
|
+
# ones.
|
179
|
+
#
|
180
|
+
# If set to +nil+, the use of the fallback font is disabled.
|
170
181
|
#
|
171
182
|
# Default is 'Helvetica'.
|
172
183
|
#
|
@@ -393,7 +404,7 @@ module HexaPDF
|
|
393
404
|
DCTDecode: 'HexaPDF::Filter::PassThrough',
|
394
405
|
DCT: 'HexaPDF::Filter::PassThrough',
|
395
406
|
JPXDecode: 'HexaPDF::Filter::PassThrough',
|
396
|
-
Crypt:
|
407
|
+
Crypt: 'HexaPDF::Filter::Crypt',
|
397
408
|
Encryption: 'HexaPDF::Filter::Encryption',
|
398
409
|
},
|
399
410
|
'font.map' => {},
|
@@ -516,6 +527,9 @@ module HexaPDF
|
|
516
527
|
XXAcroFormField: 'HexaPDF::Type::AcroForm::Field',
|
517
528
|
XXAppearanceDictionary: 'HexaPDF::Type::Annotation::AppearanceDictionary',
|
518
529
|
Border: 'HexaPDF::Type::Annotation::Border',
|
530
|
+
SigFieldLock: 'HexaPDF::Type::AcroForm::SignatureField::LockDictionary',
|
531
|
+
SV: 'HexaPDF::Type::AcroForm::SignatureField::SeedValueDictionary',
|
532
|
+
SVCert: 'HexaPDF::Type::AcroForm::SignatureField::CertificateSeedValueDictionary',
|
519
533
|
},
|
520
534
|
'object.subtype_map' => {
|
521
535
|
nil => {
|
@@ -561,6 +575,7 @@ module HexaPDF
|
|
561
575
|
Tx: 'HexaPDF::Type::AcroForm::TextField',
|
562
576
|
Btn: 'HexaPDF::Type::AcroForm::ButtonField',
|
563
577
|
Ch: 'HexaPDF::Type::AcroForm::ChoiceField',
|
578
|
+
Sig: 'HexaPDF::Type::AcroForm::SignatureField',
|
564
579
|
},
|
565
580
|
})
|
566
581
|
|
data/lib/hexapdf/dictionary.rb
CHANGED
@@ -155,6 +155,12 @@ module HexaPDF
|
|
155
155
|
# available (see ::define_field).
|
156
156
|
#
|
157
157
|
# * Returns the default value if one is specified and no value is available.
|
158
|
+
#
|
159
|
+
# Note: If field information is available for the entry, a Hash or Array value will always be
|
160
|
+
# wrapped by Dictionary or PDFArray. Otherwise, the value will be returned as-is.
|
161
|
+
#
|
162
|
+
# Note: This method may throw a "can't add a new key into hash during iteration" error in
|
163
|
+
# certain cases because it potentially modifies the underlying hash!
|
158
164
|
def [](name)
|
159
165
|
field = self.class.field(name)
|
160
166
|
data = if key?(name)
|
@@ -255,7 +261,7 @@ module HexaPDF
|
|
255
261
|
|
256
262
|
# Iterates over all currently set fields and those that are required.
|
257
263
|
def each_set_key_or_required_field #:yields: name, field
|
258
|
-
value.
|
264
|
+
value.keys.each {|name| yield(name, self.class.field(name)) }
|
259
265
|
self.class.each_field do |name, field|
|
260
266
|
yield(name, field) if field.required? && !value.key?(name)
|
261
267
|
end
|
@@ -301,7 +307,7 @@ module HexaPDF
|
|
301
307
|
|
302
308
|
# Check the value of the field against the allowed values.
|
303
309
|
if field.allowed_values && !field.allowed_values.include?(obj)
|
304
|
-
yield("Field #{name} does not contain an allowed value")
|
310
|
+
yield("Field #{name} does not contain an allowed value: #{obj.inspect}")
|
305
311
|
end
|
306
312
|
|
307
313
|
# Check if field value needs to be (in)direct
|
@@ -344,7 +344,7 @@ module HexaPDF
|
|
344
344
|
# Wraps a given array in the Rectangle class. Otherwise returns +nil+.
|
345
345
|
def self.convert(data, _type, document)
|
346
346
|
return unless data.kind_of?(Array) || data.kind_of?(HexaPDF::PDFArray)
|
347
|
-
document.wrap(data, type: Rectangle)
|
347
|
+
data.empty? ? document.wrap(nil) : document.wrap(data, type: Rectangle)
|
348
348
|
end
|
349
349
|
|
350
350
|
end
|
@@ -268,7 +268,7 @@ module HexaPDF
|
|
268
268
|
str.replace(string_algorithm.decrypt(key, str))
|
269
269
|
end
|
270
270
|
|
271
|
-
if obj.kind_of?(HexaPDF::Stream)
|
271
|
+
if obj.kind_of?(HexaPDF::Stream) && obj.raw_stream.filter[0] != :Crypt
|
272
272
|
unless string_algorithm == stream_algorithm
|
273
273
|
key = object_key(obj.oid, obj.gen, stream_algorithm)
|
274
274
|
end
|
@@ -300,7 +300,12 @@ module HexaPDF
|
|
300
300
|
obj.raw_stream.key == key && obj.raw_stream.algorithm == stream_algorithm
|
301
301
|
obj.raw_stream.undecrypted_fiber
|
302
302
|
else
|
303
|
-
|
303
|
+
filter = obj[:Filter]
|
304
|
+
if filter == :Crypt || (filter.kind_of?(PDFArray) && filter[0] == :Crypt)
|
305
|
+
result
|
306
|
+
else
|
307
|
+
stream_algorithm.encryption_fiber(key, result)
|
308
|
+
end
|
304
309
|
end
|
305
310
|
end
|
306
311
|
|
data/lib/hexapdf/error.rb
CHANGED
@@ -43,12 +43,13 @@ module HexaPDF
|
|
43
43
|
class MalformedPDFError < Error
|
44
44
|
|
45
45
|
# The byte position in the PDF file where the error occured.
|
46
|
-
|
46
|
+
attr_accessor :pos
|
47
47
|
|
48
48
|
# Creates a new malformed PDF error object for the given exception message.
|
49
49
|
#
|
50
|
-
# The byte position where the error occured
|
51
|
-
|
50
|
+
# The byte position where the error occured can either be given via the +pos+ argument or later
|
51
|
+
# via the #pos accessor but must be set before the exception message is retrieved.
|
52
|
+
def initialize(message, pos: nil)
|
52
53
|
super(message)
|
53
54
|
@pos = pos
|
54
55
|
end
|
data/lib/hexapdf/filter.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
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-2020 Thomas Leitner
|
8
|
+
#
|
9
|
+
# HexaPDF is free software: you can redistribute it and/or modify it
|
10
|
+
# under the terms of the GNU Affero General Public License version 3 as
|
11
|
+
# published by the Free Software Foundation with the addition of the
|
12
|
+
# following permission added to Section 15 as permitted in Section 7(a):
|
13
|
+
# FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
|
14
|
+
# THOMAS LEITNER, THOMAS LEITNER DISCLAIMS THE WARRANTY OF NON
|
15
|
+
# INFRINGEMENT OF THIRD PARTY RIGHTS.
|
16
|
+
#
|
17
|
+
# HexaPDF is distributed in the hope that it will be useful, but WITHOUT
|
18
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
19
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
20
|
+
# License for more details.
|
21
|
+
#
|
22
|
+
# You should have received a copy of the GNU Affero General Public License
|
23
|
+
# along with HexaPDF. If not, see <http://www.gnu.org/licenses/>.
|
24
|
+
#
|
25
|
+
# The interactive user interfaces in modified source and object code
|
26
|
+
# versions of HexaPDF must display Appropriate Legal Notices, as required
|
27
|
+
# under Section 5 of the GNU Affero General Public License version 3.
|
28
|
+
#
|
29
|
+
# In accordance with Section 7(b) of the GNU Affero General Public
|
30
|
+
# License, a covered work must retain the producer line in every PDF that
|
31
|
+
# is created or manipulated using HexaPDF.
|
32
|
+
#
|
33
|
+
# If the GNU Affero General Public License doesn't fit your need,
|
34
|
+
# commercial licenses are available at <https://gettalong.at/hexapdf/>.
|
35
|
+
#++
|
36
|
+
|
37
|
+
require 'hexapdf/error'
|
38
|
+
|
39
|
+
module HexaPDF
|
40
|
+
module Filter
|
41
|
+
|
42
|
+
# This filter module implements the Crypt filter. The only supported part is using the Identity
|
43
|
+
# filter.
|
44
|
+
module Crypt
|
45
|
+
|
46
|
+
# See HexaPDF::Filter
|
47
|
+
def self.decoder(source, options)
|
48
|
+
if !options || !options.key?(:Name) || options[:Name] == :Identity
|
49
|
+
source
|
50
|
+
else
|
51
|
+
raise FilterError, "Handling of Crypt filters besides Identity is not implemented"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
singleton_class.send(:alias_method, :encoder, :decoder)
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -63,6 +63,16 @@ module HexaPDF
|
|
63
63
|
def use_glyph(glyph_id)
|
64
64
|
return @glyph_map[glyph_id] if @glyph_map.key?(glyph_id)
|
65
65
|
@last_id += 1
|
66
|
+
# Handle codes for ASCII characters \r (13), (, ) (40, 41) and \ (92) specially so that
|
67
|
+
# they never appear in the output (PDF serialization would need to escape them)
|
68
|
+
if @last_id == 13 || @last_id == 40 || @last_id == 92
|
69
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
70
|
+
if @last_id == 40
|
71
|
+
@last_id += 1
|
72
|
+
@glyph_map[:"s#{@last_id}"] = @last_id
|
73
|
+
end
|
74
|
+
@last_id += 1
|
75
|
+
end
|
66
76
|
@glyph_map[glyph_id] = @last_id
|
67
77
|
end
|
68
78
|
|
@@ -107,7 +117,7 @@ module HexaPDF
|
|
107
117
|
locations = []
|
108
118
|
|
109
119
|
@glyph_map.each_key do |old_gid|
|
110
|
-
glyph = orig_glyf[old_gid]
|
120
|
+
glyph = orig_glyf[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
111
121
|
locations << table.size
|
112
122
|
data = glyph.raw_data
|
113
123
|
if glyph.compound?
|
@@ -134,7 +144,7 @@ module HexaPDF
|
|
134
144
|
hmtx = @font[:hmtx]
|
135
145
|
data = ''.b
|
136
146
|
@glyph_map.each_key do |old_gid|
|
137
|
-
metric = hmtx[old_gid]
|
147
|
+
metric = hmtx[old_gid.kind_of?(Symbol) ? 0 : old_gid]
|
138
148
|
data << [metric.advance_width, metric.left_side_bearing].pack('n2')
|
139
149
|
end
|
140
150
|
data
|
@@ -166,7 +176,10 @@ module HexaPDF
|
|
166
176
|
# Adds the components of compound glyphs to the subset.
|
167
177
|
def add_glyph_components
|
168
178
|
glyf = @font[:glyf]
|
169
|
-
@glyph_map.keys.each
|
179
|
+
@glyph_map.keys.each do |gid|
|
180
|
+
next if gid.kind_of?(Symbol)
|
181
|
+
glyf[gid].components&.each {|cgid| use_glyph(cgid) }
|
182
|
+
end
|
170
183
|
end
|
171
184
|
|
172
185
|
end
|
@@ -99,18 +99,23 @@ module HexaPDF
|
|
99
99
|
@max_mem_type42, @min_mem_type1, @max_mem_type1 = read_formatted(24, 's>2N5')
|
100
100
|
|
101
101
|
sub_table_length = directory_entry.length - 32
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
102
|
+
cur_pos = io.pos
|
103
|
+
@glyph_names = lambda do |glyph_id|
|
104
|
+
io.pos = cur_pos
|
105
|
+
@glyph_names = case @format
|
106
|
+
when 1 then Format1.parse(io, sub_table_length)
|
107
|
+
when 2 then Format2.parse(io, sub_table_length)
|
108
|
+
when 3 then Format3.parse(io, sub_table_length)
|
109
|
+
when 4 then Format4.parse(io, sub_table_length)
|
110
110
|
else
|
111
|
-
[]
|
111
|
+
if font.config['font.true_type.unknown_format'] == :raise
|
112
|
+
raise HexaPDF::Error, "Unsupported post table format: #{@format}"
|
113
|
+
else
|
114
|
+
[]
|
115
|
+
end
|
112
116
|
end
|
113
|
-
|
117
|
+
@glyph_names[glyph_id]
|
118
|
+
end
|
114
119
|
end
|
115
120
|
|
116
121
|
# 'post' table format 1
|