hexapdf 0.3.0 → 0.4.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 +68 -0
- data/CONTRIBUTERS +1 -1
- data/README.md +35 -4
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/data/hexapdf/cmap/83pv-RKSJ-H +314 -0
- data/data/hexapdf/cmap/90ms-RKSJ-H +259 -0
- data/data/hexapdf/cmap/90ms-RKSJ-V +156 -0
- data/data/hexapdf/cmap/90msp-RKSJ-H +257 -0
- data/data/hexapdf/cmap/90msp-RKSJ-V +155 -0
- data/data/hexapdf/cmap/90pv-RKSJ-H +355 -0
- data/data/hexapdf/cmap/Add-RKSJ-H +738 -0
- data/data/hexapdf/cmap/Add-RKSJ-V +135 -0
- data/data/hexapdf/cmap/Adobe-CNS1-UCS2 +18209 -0
- data/data/hexapdf/cmap/Adobe-GB1-UCS2 +14267 -0
- data/data/hexapdf/cmap/Adobe-Japan1-UCS2 +19159 -0
- data/data/hexapdf/cmap/Adobe-Korea1-UCS2 +9267 -0
- data/data/hexapdf/cmap/B5pc-H +337 -0
- data/data/hexapdf/cmap/B5pc-V +90 -0
- data/data/hexapdf/cmap/CNS-EUC-H +490 -0
- data/data/hexapdf/cmap/CNS-EUC-V +538 -0
- data/data/hexapdf/cmap/ETen-B5-H +343 -0
- data/data/hexapdf/cmap/ETen-B5-V +91 -0
- data/data/hexapdf/cmap/ETenms-B5-H +79 -0
- data/data/hexapdf/cmap/ETenms-B5-V +99 -0
- data/data/hexapdf/cmap/EUC-H +207 -0
- data/data/hexapdf/cmap/EUC-V +105 -0
- data/data/hexapdf/cmap/Ext-RKSJ-H +768 -0
- data/data/hexapdf/cmap/Ext-RKSJ-V +117 -0
- data/data/hexapdf/cmap/GB-EUC-H +173 -0
- data/data/hexapdf/cmap/GB-EUC-V +98 -0
- data/data/hexapdf/cmap/GBK-EUC-H +4273 -0
- data/data/hexapdf/cmap/GBK-EUC-V +97 -0
- data/data/hexapdf/cmap/GBK2K-H +5325 -0
- data/data/hexapdf/cmap/GBK2K-V +118 -0
- data/data/hexapdf/cmap/GBKp-EUC-H +4272 -0
- data/data/hexapdf/cmap/GBKp-EUC-V +97 -0
- data/data/hexapdf/cmap/GBpc-EUC-H +175 -0
- data/data/hexapdf/cmap/GBpc-EUC-V +98 -0
- data/data/hexapdf/cmap/H +200 -0
- data/data/hexapdf/cmap/HKscs-B5-H +1331 -0
- data/data/hexapdf/cmap/HKscs-B5-V +90 -0
- data/data/hexapdf/cmap/Identity-H +339 -0
- data/data/hexapdf/cmap/Identity-V +73 -0
- data/data/hexapdf/cmap/KSC-EUC-H +562 -0
- data/data/hexapdf/cmap/KSC-EUC-V +94 -0
- data/data/hexapdf/cmap/KSCms-UHC-H +776 -0
- data/data/hexapdf/cmap/KSCms-UHC-HW-H +775 -0
- data/data/hexapdf/cmap/KSCms-UHC-HW-V +93 -0
- data/data/hexapdf/cmap/KSCms-UHC-V +94 -0
- data/data/hexapdf/cmap/KSCpc-EUC-H +608 -0
- data/data/hexapdf/cmap/LICENSE.txt +26 -0
- data/data/hexapdf/cmap/README.txt +9 -0
- data/data/hexapdf/cmap/UniCNS-UCS2-H +16992 -0
- data/data/hexapdf/cmap/UniCNS-UCS2-V +90 -0
- data/data/hexapdf/cmap/UniCNS-UTF16-H +19117 -0
- data/data/hexapdf/cmap/UniCNS-UTF16-V +94 -0
- data/data/hexapdf/cmap/UniGB-UCS2-H +14321 -0
- data/data/hexapdf/cmap/UniGB-UCS2-V +101 -0
- data/data/hexapdf/cmap/UniGB-UTF16-H +14381 -0
- data/data/hexapdf/cmap/UniGB-UTF16-V +104 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-H +8870 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-HW-H +81 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-HW-V +279 -0
- data/data/hexapdf/cmap/UniJIS-UCS2-V +275 -0
- data/data/hexapdf/cmap/UniJIS-UTF16-H +14450 -0
- data/data/hexapdf/cmap/UniJIS-UTF16-V +299 -0
- data/data/hexapdf/cmap/UniKS-UCS2-H +8725 -0
- data/data/hexapdf/cmap/UniKS-UCS2-V +95 -0
- data/data/hexapdf/cmap/UniKS-UTF16-H +8895 -0
- data/data/hexapdf/cmap/UniKS-UTF16-V +99 -0
- data/data/hexapdf/cmap/V +105 -0
- data/examples/arc.rb +3 -3
- data/examples/merging.rb +4 -1
- data/examples/optimizing.rb +3 -0
- data/examples/show_char_bboxes.rb +2 -2
- data/examples/truetype.rb +2 -2
- data/lib/hexapdf/cli.rb +40 -1
- data/lib/hexapdf/cli/batch.rb +72 -0
- data/lib/hexapdf/cli/command.rb +112 -15
- data/lib/hexapdf/cli/files.rb +2 -2
- data/lib/hexapdf/cli/images.rb +14 -6
- data/lib/hexapdf/cli/info.rb +6 -8
- data/lib/hexapdf/cli/inspect.rb +5 -8
- data/lib/hexapdf/cli/merge.rb +13 -20
- data/lib/hexapdf/cli/modify.rb +4 -7
- data/lib/hexapdf/cli/optimize.rb +2 -5
- data/lib/hexapdf/configuration.rb +32 -3
- data/lib/hexapdf/content/canvas.rb +130 -37
- data/lib/hexapdf/content/parser.rb +40 -6
- data/lib/hexapdf/content/processor.rb +4 -4
- data/lib/hexapdf/document.rb +40 -10
- data/lib/hexapdf/document/fonts.rb +1 -0
- data/lib/hexapdf/encryption/security_handler.rb +8 -12
- data/lib/hexapdf/filter/flate_decode.rb +25 -2
- data/lib/hexapdf/font/cmap.rb +124 -8
- data/lib/hexapdf/font/cmap/parser.rb +65 -15
- data/lib/hexapdf/font/encoding/base.rb +2 -2
- data/lib/hexapdf/font/encoding/glyph_list.rb +2 -4
- data/lib/hexapdf/font/true_type.rb +1 -0
- data/lib/hexapdf/font/true_type/builder.rb +75 -0
- data/lib/hexapdf/font/true_type/optimizer.rb +65 -0
- data/lib/hexapdf/font/true_type/subsetter.rb +9 -22
- data/lib/hexapdf/font/true_type_wrapper.rb +9 -21
- data/lib/hexapdf/font_loader.rb +1 -1
- data/lib/hexapdf/importer.rb +1 -1
- data/lib/hexapdf/serializer.rb +5 -3
- data/lib/hexapdf/type.rb +2 -0
- data/lib/hexapdf/type/cid_font.rb +120 -0
- data/lib/hexapdf/type/font.rb +32 -12
- data/lib/hexapdf/type/font_simple.rb +34 -42
- data/lib/hexapdf/type/font_type0.rb +148 -0
- data/lib/hexapdf/type/form.rb +4 -4
- data/lib/hexapdf/type/page.rb +12 -11
- data/lib/hexapdf/type/resources.rb +14 -0
- data/lib/hexapdf/utils/graphics_helpers.rb +77 -0
- data/lib/hexapdf/version.rb +1 -1
- data/man/man1/hexapdf.1 +43 -1
- data/test/hexapdf/content/test_canvas.rb +76 -0
- data/test/hexapdf/content/test_parser.rb +20 -1
- data/test/hexapdf/content/test_processor.rb +11 -7
- data/test/hexapdf/document/test_fonts.rb +3 -1
- data/test/hexapdf/font/cmap/test_parser.rb +42 -7
- data/test/hexapdf/font/encoding/test_base.rb +1 -1
- data/test/hexapdf/font/encoding/test_glyph_list.rb +3 -3
- data/test/hexapdf/font/test_cmap.rb +104 -0
- data/test/hexapdf/font/test_true_type_wrapper.rb +63 -46
- data/test/hexapdf/font/true_type/test_builder.rb +37 -0
- data/test/hexapdf/font/true_type/test_optimizer.rb +27 -0
- data/test/hexapdf/font/true_type/test_subsetter.rb +6 -13
- data/test/hexapdf/test_configuration.rb +12 -7
- data/test/hexapdf/test_document.rb +24 -0
- data/test/hexapdf/test_importer.rb +9 -1
- data/test/hexapdf/test_writer.rb +2 -2
- data/test/hexapdf/type/test_cid_font.rb +61 -0
- data/test/hexapdf/type/test_font.rb +31 -4
- data/test/hexapdf/type/test_font_simple.rb +6 -21
- data/test/hexapdf/type/test_font_type0.rb +114 -0
- data/test/hexapdf/type/test_resources.rb +17 -1
- data/test/hexapdf/utils/test_graphics_helpers.rb +29 -0
- metadata +82 -3
data/lib/hexapdf/type/font.rb
CHANGED
|
@@ -51,28 +51,48 @@ module HexaPDF
|
|
|
51
51
|
true
|
|
52
52
|
end
|
|
53
53
|
|
|
54
|
-
# Returns the UTF-8 string for the given character code, or
|
|
55
|
-
# found.
|
|
54
|
+
# Returns the UTF-8 string for the given character code, or calls the configuration option
|
|
55
|
+
# 'font.on_missing_unicode_mapping' if no mapping was found.
|
|
56
56
|
def to_utf8(code)
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
to_unicode_cmap && to_unicode_cmap.to_unicode(code) || missing_unicode_mapping(code)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the bounding box of the font or +nil+ if it is not found.
|
|
61
|
+
def bounding_box
|
|
62
|
+
if key?(:FontDescriptor) && self[:FontDescriptor].key?(:FontBBox)
|
|
63
|
+
self[:FontDescriptor][:FontBBox].value
|
|
59
64
|
else
|
|
60
|
-
|
|
65
|
+
nil
|
|
61
66
|
end
|
|
62
67
|
end
|
|
63
68
|
|
|
69
|
+
# Returns +true+ if the font is embedded.
|
|
70
|
+
def embedded?
|
|
71
|
+
dict = self[:FontDescriptor]
|
|
72
|
+
dict && (dict[:FontFile] || dict[:FontFile2] || dict[:FontFile3])
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Returns the embeeded font file object or +nil+ if the font is not embedded.
|
|
76
|
+
def font_file
|
|
77
|
+
embedded?
|
|
78
|
+
end
|
|
79
|
+
|
|
64
80
|
private
|
|
65
81
|
|
|
66
82
|
# Parses and caches the ToUnicode CMap.
|
|
67
83
|
def to_unicode_cmap
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
84
|
+
document.cache(@data, :to_unicode_cmap) do
|
|
85
|
+
if key?(:ToUnicode)
|
|
86
|
+
HexaPDF::Font::CMap.parse(self[:ToUnicode].stream)
|
|
87
|
+
else
|
|
88
|
+
nil
|
|
89
|
+
end
|
|
74
90
|
end
|
|
75
|
-
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Calls the configured proc for handling missing unicode mappings.
|
|
94
|
+
def missing_unicode_mapping(code)
|
|
95
|
+
@document.config['font.on_missing_unicode_mapping'].call(code, self)
|
|
76
96
|
end
|
|
77
97
|
|
|
78
98
|
end
|
|
@@ -54,31 +54,30 @@ module HexaPDF
|
|
|
54
54
|
#
|
|
55
55
|
# Note that the encoding is cached internally when accessed the first time.
|
|
56
56
|
def encoding
|
|
57
|
-
@encoding
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
encoding = HexaPDF::Font::Encoding.for_name(:StandardEncoding)
|
|
72
|
-
end
|
|
57
|
+
document.cache(@data, :encoding) do
|
|
58
|
+
case (val = self[:Encoding])
|
|
59
|
+
when Symbol
|
|
60
|
+
encoding = HexaPDF::Font::Encoding.for_name(val)
|
|
61
|
+
encoding = encoding_from_font if encoding.nil?
|
|
62
|
+
encoding
|
|
63
|
+
when HexaPDF::Dictionary, Hash
|
|
64
|
+
encoding = val[:BaseEncoding]
|
|
65
|
+
encoding = HexaPDF::Font::Encoding.for_name(encoding) if encoding
|
|
66
|
+
unless encoding
|
|
67
|
+
if embedded? || symbolic?
|
|
68
|
+
encoding = encoding_from_font
|
|
69
|
+
else
|
|
70
|
+
encoding = HexaPDF::Font::Encoding.for_name(:StandardEncoding)
|
|
73
71
|
end
|
|
74
|
-
encoding = difference_encoding(encoding, val[:Differences]) if val.key?(:Differences)
|
|
75
|
-
encoding
|
|
76
|
-
when nil
|
|
77
|
-
encoding_from_font
|
|
78
|
-
else
|
|
79
|
-
raise HexaPDF::Error, "Unknown value for font's encoding: #{self[:Encoding]}"
|
|
80
72
|
end
|
|
73
|
+
encoding = difference_encoding(encoding, val[:Differences]) if val.key?(:Differences)
|
|
74
|
+
encoding
|
|
75
|
+
when nil
|
|
76
|
+
encoding_from_font
|
|
77
|
+
else
|
|
78
|
+
raise HexaPDF::Error, "Unknown value for font's encoding: #{self[:Encoding]}"
|
|
81
79
|
end
|
|
80
|
+
end
|
|
82
81
|
end
|
|
83
82
|
|
|
84
83
|
# Decodes the given string into an array of character codes.
|
|
@@ -86,12 +85,11 @@ module HexaPDF
|
|
|
86
85
|
string.bytes
|
|
87
86
|
end
|
|
88
87
|
|
|
89
|
-
# Returns the UTF-8 string for the given character code, or
|
|
90
|
-
# found.
|
|
88
|
+
# Returns the UTF-8 string for the given character code, or calls the configuration option
|
|
89
|
+
# 'font.on_missing_unicode_mapping' if no mapping was found.
|
|
91
90
|
def to_utf8(code)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
str
|
|
91
|
+
(to_unicode_cmap && to_unicode_cmap.to_unicode(code)) ||
|
|
92
|
+
encoding.unicode(code) || missing_unicode_mapping(code)
|
|
95
93
|
end
|
|
96
94
|
|
|
97
95
|
# Returns the unscaled width of the given code point in glyph units, or 0 if the width for
|
|
@@ -110,32 +108,26 @@ module HexaPDF
|
|
|
110
108
|
end
|
|
111
109
|
end
|
|
112
110
|
|
|
113
|
-
# Returns the bounding box of the font or +nil+ if it is not found.
|
|
114
|
-
def bounding_box
|
|
115
|
-
if key?(:FontDescriptor) && self[:FontDescriptor].key?(:FontBBox)
|
|
116
|
-
self[:FontDescriptor][:FontBBox].value
|
|
117
|
-
else
|
|
118
|
-
nil
|
|
119
|
-
end
|
|
120
|
-
end
|
|
121
|
-
|
|
122
111
|
# Returns the writing mode which is always :horizontal for simple fonts like Type1.
|
|
123
112
|
def writing_mode
|
|
124
113
|
:horizontal
|
|
125
114
|
end
|
|
126
115
|
|
|
127
|
-
# Returns +true+ if the font is embedded.
|
|
128
|
-
def embedded?
|
|
129
|
-
dict = self[:FontDescriptor]
|
|
130
|
-
dict && (dict[:FontFile] || dict[:FontFile2] || dict[:FontFile3])
|
|
131
|
-
end
|
|
132
|
-
|
|
133
116
|
# Returns +true+ if the font is a symbolic font, +false+ if it is not, and +nil+ if it is
|
|
134
117
|
# not known.
|
|
135
118
|
def symbolic?
|
|
136
119
|
self[:FontDescriptor] && self[:FontDescriptor].flagged?(:symbolic) || nil
|
|
137
120
|
end
|
|
138
121
|
|
|
122
|
+
# Returns whether word spacing is applicable when using this font.
|
|
123
|
+
#
|
|
124
|
+
# Always returns +true+ for simple fonts.
|
|
125
|
+
#
|
|
126
|
+
# See: PDF1.7 s9.3.3
|
|
127
|
+
def word_spacing_applicable?
|
|
128
|
+
true
|
|
129
|
+
end
|
|
130
|
+
|
|
139
131
|
private
|
|
140
132
|
|
|
141
133
|
# Tries to read the encoding from the embedded font.
|
|
@@ -0,0 +1,148 @@
|
|
|
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/type/font'
|
|
35
|
+
require 'hexapdf/stream'
|
|
36
|
+
require 'hexapdf/font/cmap'
|
|
37
|
+
|
|
38
|
+
module HexaPDF
|
|
39
|
+
module Type
|
|
40
|
+
|
|
41
|
+
# Represents a composite PDF font.
|
|
42
|
+
#
|
|
43
|
+
# Composites fonts wrap a descendant CIDFont and use CIDs to identify glyphs. A CID can be
|
|
44
|
+
# encoded in one or more bytes and an associated CMap specifies how this encoding is done.
|
|
45
|
+
# Composite fonts also allow for vertical writing mode and support TrueType as well as OpenType
|
|
46
|
+
# fonts.
|
|
47
|
+
#
|
|
48
|
+
# See: PDF1.7 s9.7
|
|
49
|
+
class FontType0 < Font
|
|
50
|
+
|
|
51
|
+
define_field :Subtype, type: Symbol, required: true, default: :Type0
|
|
52
|
+
define_field :Encoding, type: [Symbol, Stream], required: true
|
|
53
|
+
define_field :DescendantFonts, type: Array, required: true
|
|
54
|
+
|
|
55
|
+
# Returns the CID font of this type 0 font.
|
|
56
|
+
def descendant_font
|
|
57
|
+
document.cache(@data, :descendant_font) { document.deref(self[:DescendantFonts][0]) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Returns the writing mode which is either :horizontal or :vertical.
|
|
61
|
+
def writing_mode
|
|
62
|
+
cmap.wmode == 0 ? :horizontal : :vertical
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Decodes the given string into an array of CIDs.
|
|
66
|
+
def decode(string)
|
|
67
|
+
cmap.read_codes(string)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Returns the UTF-8 string for the given code, or calls the configuration option
|
|
71
|
+
# 'font.on_missing_unicode_mapping' if no mapping was found.
|
|
72
|
+
def to_utf8(code)
|
|
73
|
+
(to_unicode_cmap && to_unicode_cmap.to_unicode(code)) ||
|
|
74
|
+
(ucs2_cmap && ucs2_cmap.to_unicode(code)) || missing_unicode_mapping(code)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the unscaled width of the given CID in glyph units, or 0 if the width for the code
|
|
78
|
+
# point is missing.
|
|
79
|
+
def width(code)
|
|
80
|
+
descendant_font.width(cmap.to_cid(code))
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Returns the bounding box of the font or +nil+ if it is not found.
|
|
84
|
+
def bounding_box
|
|
85
|
+
descendant_font.bounding_box
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
# Returns +true+ if the font is embedded.
|
|
89
|
+
def embedded?
|
|
90
|
+
descendant_font.embedded?
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Returns the embeeded font file object or +nil+ if the font is not embedded.
|
|
94
|
+
def font_file
|
|
95
|
+
descendant_font.font_file
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Returns whether word spacing is applicable when using this font.
|
|
99
|
+
#
|
|
100
|
+
# Note that the return value is cached when accessed the first time.
|
|
101
|
+
#
|
|
102
|
+
# See: PDF1.7 s9.3.3
|
|
103
|
+
def word_spacing_applicable?
|
|
104
|
+
@word_spacing_applicable ||= ((cmap.read_codes("\x20".freeze) && true) rescue false)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
private
|
|
108
|
+
|
|
109
|
+
# Returns the CMap used for decoding strings for this font.
|
|
110
|
+
#
|
|
111
|
+
# Note that the CMap is cached internally when accessed the first time.
|
|
112
|
+
def cmap
|
|
113
|
+
document.cache(@data, :cmap) do
|
|
114
|
+
val = self[:Encoding]
|
|
115
|
+
if val.kind_of?(Symbol)
|
|
116
|
+
HexaPDF::Font::CMap.for_name(val.to_s)
|
|
117
|
+
elsif val.kind_of?(HexaPDF::Stream)
|
|
118
|
+
HexaPDF::Font::CMap.parse(val.stream)
|
|
119
|
+
else
|
|
120
|
+
raise HexaPDF::Error, "Unknown value for font's encoding: #{self[:Encoding]}"
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns the UCS-2 CMap used for extracting text when no ToUnicode CMap is available, or
|
|
126
|
+
# +nil+ if the UCS-2 CMap could not be determined.
|
|
127
|
+
#
|
|
128
|
+
# Note that the CMap is cached internally when accessed the first time.
|
|
129
|
+
#
|
|
130
|
+
# See: PDF1.7 s9.10.2
|
|
131
|
+
def ucs2_cmap
|
|
132
|
+
document.cache(@data, :ucs2_cmap) do
|
|
133
|
+
encoding = self[:Encoding]
|
|
134
|
+
system_info = descendant_font[:CIDSystemInfo]
|
|
135
|
+
registry = system_info[:Registry]
|
|
136
|
+
ordering = system_info[:Ordering]
|
|
137
|
+
if (encoding.kind_of?(Symbol) && HexaPDF::Font::CMap.predefined?(encoding.to_s) &&
|
|
138
|
+
encoding != :"Identity-H" && encoding != :"Identity-V") ||
|
|
139
|
+
(registry == "Adobe" && ['GB1', 'CNS1', 'Japan1', 'Korea1'].include?(ordering))
|
|
140
|
+
HexaPDF::Font::CMap.for_name("#{registry}-#{ordering}-UCS2")
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/hexapdf/type/form.rb
CHANGED
|
@@ -105,15 +105,15 @@ module HexaPDF
|
|
|
105
105
|
#
|
|
106
106
|
# *Note* that a canvas can only be retrieved for initially empty form XObjects!
|
|
107
107
|
def canvas
|
|
108
|
-
|
|
108
|
+
document.cache(@data, :canvas) do
|
|
109
109
|
unless stream.empty?
|
|
110
110
|
raise HexaPDF::Error, "Cannot create a canvas for a form XObjects with contents"
|
|
111
111
|
end
|
|
112
|
-
|
|
113
|
-
self.stream =
|
|
112
|
+
canvas = Content::Canvas.new(self)
|
|
113
|
+
self.stream = canvas.stream_data
|
|
114
114
|
set_filter(:FlateDecode)
|
|
115
|
+
canvas
|
|
115
116
|
end
|
|
116
|
-
@canvas
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
end
|
data/lib/hexapdf/type/page.rb
CHANGED
|
@@ -135,7 +135,7 @@ module HexaPDF
|
|
|
135
135
|
define_field :TemplateInstantiated, type: Symbol, version: '1.5'
|
|
136
136
|
define_field :PresSteps, type: Dictionary, version: '1.5'
|
|
137
137
|
define_field :UserUnit, type: Numeric, version: '1.6'
|
|
138
|
-
define_field :VP, type:
|
|
138
|
+
define_field :VP, type: Array, version: '1.6'
|
|
139
139
|
|
|
140
140
|
# Returns +true+ since page objects must always be indirect.
|
|
141
141
|
def must_be_indirect?
|
|
@@ -234,7 +234,8 @@ module HexaPDF
|
|
|
234
234
|
end
|
|
235
235
|
end
|
|
236
236
|
|
|
237
|
-
# Returns the resource dictionary which is automatically created if it
|
|
237
|
+
# Returns the possibly inherited resource dictionary which is automatically created if it
|
|
238
|
+
# doesn't exist.
|
|
238
239
|
def resources
|
|
239
240
|
self[:Resources] ||= document.wrap({}, type: :XXResources)
|
|
240
241
|
end
|
|
@@ -277,8 +278,8 @@ module HexaPDF
|
|
|
277
278
|
unless [:page, :overlay, :underlay].include?(type)
|
|
278
279
|
raise ArgumentError, "Invalid value for 'type', expected: :page, :underlay or :overlay"
|
|
279
280
|
end
|
|
280
|
-
|
|
281
|
-
return @
|
|
281
|
+
cache_key = "#{type}_canvas".intern
|
|
282
|
+
return document.cache(@data, cache_key) if document.cached?(@data, cache_key)
|
|
282
283
|
|
|
283
284
|
if type == :page && key?(:Contents)
|
|
284
285
|
raise HexaPDF::Error, "Cannot get the canvas for a page with contents"
|
|
@@ -286,18 +287,18 @@ module HexaPDF
|
|
|
286
287
|
|
|
287
288
|
contents = self[:Contents]
|
|
288
289
|
if contents.nil?
|
|
289
|
-
@
|
|
290
|
+
page_canvas = document.cache(@data, :page_canvas, Content::Canvas.new(self))
|
|
290
291
|
self[:Contents] = document.add({Filter: :FlateDecode},
|
|
291
|
-
stream:
|
|
292
|
+
stream: page_canvas.stream_data)
|
|
292
293
|
end
|
|
293
294
|
|
|
294
295
|
if type == :overlay || type == :underlay
|
|
295
|
-
@
|
|
296
|
-
@
|
|
296
|
+
underlay_canvas = document.cache(@data, :underlay_canvas, Content::Canvas.new(self))
|
|
297
|
+
overlay_canvas = document.cache(@data, :overlay_canvas, Content::Canvas.new(self))
|
|
297
298
|
|
|
298
299
|
stream = HexaPDF::StreamData.new do
|
|
299
300
|
Fiber.yield(" q ")
|
|
300
|
-
fiber =
|
|
301
|
+
fiber = underlay_canvas.stream_data.fiber
|
|
301
302
|
while fiber.alive? && (data = fiber.resume)
|
|
302
303
|
Fiber.yield(data)
|
|
303
304
|
end
|
|
@@ -307,7 +308,7 @@ module HexaPDF
|
|
|
307
308
|
|
|
308
309
|
stream = HexaPDF::StreamData.new do
|
|
309
310
|
Fiber.yield(" Q ")
|
|
310
|
-
fiber =
|
|
311
|
+
fiber = overlay_canvas.stream_data.fiber
|
|
311
312
|
while fiber.alive? && (data = fiber.resume)
|
|
312
313
|
Fiber.yield(data)
|
|
313
314
|
end
|
|
@@ -317,7 +318,7 @@ module HexaPDF
|
|
|
317
318
|
self[:Contents] = [underlay, *self[:Contents], overlay]
|
|
318
319
|
end
|
|
319
320
|
|
|
320
|
-
@
|
|
321
|
+
document.cache(@data, cache_key)
|
|
321
322
|
end
|
|
322
323
|
|
|
323
324
|
# Creates a Form XObject from the page's dictionary and contents for the given PDF document.
|
|
@@ -154,6 +154,20 @@ module HexaPDF
|
|
|
154
154
|
object_setter(:Font, 'F'.freeze, object)
|
|
155
155
|
end
|
|
156
156
|
|
|
157
|
+
# Returns the property list stored under the given name.
|
|
158
|
+
#
|
|
159
|
+
# If the property list is not found, an error is raised.
|
|
160
|
+
def property_list(name)
|
|
161
|
+
object_getter(:Properties, name)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# Adds the property list to the resources and returns the name under which it is stored.
|
|
165
|
+
#
|
|
166
|
+
# If there already exists a name for the given property list, it is just returned.
|
|
167
|
+
def add_property_list(dict)
|
|
168
|
+
object_setter(:Properties, 'P'.freeze, dict)
|
|
169
|
+
end
|
|
170
|
+
|
|
157
171
|
private
|
|
158
172
|
|
|
159
173
|
# Helper method for returning an entry of a subdictionary.
|
|
@@ -0,0 +1,77 @@
|
|
|
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
|
+
module HexaPDF
|
|
35
|
+
module Utils
|
|
36
|
+
|
|
37
|
+
# This module provides some helper functions for graphics.
|
|
38
|
+
module GraphicsHelpers
|
|
39
|
+
|
|
40
|
+
module_function
|
|
41
|
+
|
|
42
|
+
# Calculates and returns the requested dimensions for the rectangular object with the given
|
|
43
|
+
# +width+ and +height+ based on the following: options:
|
|
44
|
+
#
|
|
45
|
+
# +rwidth+::
|
|
46
|
+
# The requested width. If +rheight+ is not specified, it is chosen so that the aspect
|
|
47
|
+
# ratio is maintained
|
|
48
|
+
#
|
|
49
|
+
# +rheight+::
|
|
50
|
+
# The requested height. If +rwidth+ is not specified, it is chosen so that the aspect
|
|
51
|
+
# ratio is maintained
|
|
52
|
+
def calculate_dimensions(width, height, rwidth: nil, rheight: nil)
|
|
53
|
+
if rwidth && rheight
|
|
54
|
+
[rwidth, rheight]
|
|
55
|
+
elsif rwidth
|
|
56
|
+
[rwidth, height * rwidth / width.to_f]
|
|
57
|
+
elsif rheight
|
|
58
|
+
[width * rheight / height.to_f, rheight]
|
|
59
|
+
else
|
|
60
|
+
[width, height]
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Given two points p0 = (x0, y0) and p1 = (x1, y1), returns the point on the line through
|
|
65
|
+
# these points that is +distance+ units away from p0.
|
|
66
|
+
#
|
|
67
|
+
# v = p1 - p0
|
|
68
|
+
# result = p0 + distance * v/norm(v)
|
|
69
|
+
def point_on_line(x0, y0, x1, y1, distance:)
|
|
70
|
+
norm = Math.sqrt((x1 - x0)**2 + (y1 - y0)**2)
|
|
71
|
+
[x0 + distance / norm * (x1 - x0), y0 + distance / norm * (y1 - y0)]
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
end
|
|
77
|
+
end
|