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,61 @@
|
|
|
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-2018 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_simple'
|
|
35
|
+
|
|
36
|
+
module HexaPDF
|
|
37
|
+
module Type
|
|
38
|
+
|
|
39
|
+
# Represents a Type 3 font.
|
|
40
|
+
#
|
|
41
|
+
# See: PDF1.7 s9.6.5
|
|
42
|
+
class FontType3 < FontSimple
|
|
43
|
+
|
|
44
|
+
define_field :Subtype, type: Symbol, required: true, default: :Type3
|
|
45
|
+
define_field :Name, type: Symbol
|
|
46
|
+
define_field :FontBBox, type: Rectangle, required: true
|
|
47
|
+
define_field :FontMatrix, type: Array, required: true
|
|
48
|
+
define_field :CharProcs, type: Dictionary, required: true
|
|
49
|
+
define_field :Resources, type: Dictionary, version: '1.2'
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def perform_validation
|
|
54
|
+
super
|
|
55
|
+
yield("Required field Encoding is not set", false) if self[:Encoding].nil?
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -58,18 +58,18 @@ module HexaPDF
|
|
|
58
58
|
define_field :op, type: Boolean, version: "1.3"
|
|
59
59
|
define_field :OPM, type: Integer, version: "1.3"
|
|
60
60
|
define_field :Font, type: Array, version: "1.3"
|
|
61
|
-
define_field :BG, type: [Dictionary,
|
|
62
|
-
define_field :BG2, type: [Dictionary,
|
|
63
|
-
define_field :UCR, type: [Dictionary,
|
|
64
|
-
define_field :UCR2, type: [Dictionary,
|
|
65
|
-
define_field :TR, type: [Dictionary,
|
|
66
|
-
define_field :TR2, type: [Dictionary,
|
|
67
|
-
define_field :HT, type: [Dictionary,
|
|
61
|
+
define_field :BG, type: [Dictionary, Stream]
|
|
62
|
+
define_field :BG2, type: [Dictionary, Stream, Symbol], version: "1.3"
|
|
63
|
+
define_field :UCR, type: [Dictionary, Stream]
|
|
64
|
+
define_field :UCR2, type: [Dictionary, Stream, Symbol], version: "1.3"
|
|
65
|
+
define_field :TR, type: [Dictionary, Stream, Array, Symbol]
|
|
66
|
+
define_field :TR2, type: [Dictionary, Stream, Array, Symbol], version: "1.3"
|
|
67
|
+
define_field :HT, type: [Dictionary, Stream, Symbol]
|
|
68
68
|
define_field :FL, type: Numeric, version: "1.3"
|
|
69
69
|
define_field :SM, type: Numeric, version: "1.3"
|
|
70
70
|
define_field :SA, type: Boolean
|
|
71
71
|
define_field :BM, type: [Symbol, Array], version: "1.4"
|
|
72
|
-
define_field :SMask, type: [Dictionary,
|
|
72
|
+
define_field :SMask, type: [Dictionary, Symbol], version: "1.4"
|
|
73
73
|
define_field :CA, type: Numeric, version: "1.4"
|
|
74
74
|
define_field :ca, type: Numeric, version: "1.4"
|
|
75
75
|
define_field :AIS, type: Boolean, version: "1.4"
|
data/lib/hexapdf/type/image.rb
CHANGED
|
@@ -76,6 +76,16 @@ module HexaPDF
|
|
|
76
76
|
# facility and not when the image is part of a loaded PDF file.
|
|
77
77
|
attr_accessor :source_path
|
|
78
78
|
|
|
79
|
+
# Returns the width of the image.
|
|
80
|
+
def width
|
|
81
|
+
self[:Width]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Returns the height of the image.
|
|
85
|
+
def height
|
|
86
|
+
self[:Height]
|
|
87
|
+
end
|
|
88
|
+
|
|
79
89
|
# Returns an Info structure with information about the image.
|
|
80
90
|
#
|
|
81
91
|
# Available accessors:
|
data/lib/hexapdf/type/page.rb
CHANGED
|
@@ -36,6 +36,7 @@ require 'hexapdf/dictionary'
|
|
|
36
36
|
require 'hexapdf/stream'
|
|
37
37
|
require 'hexapdf/type/page_tree_node'
|
|
38
38
|
require 'hexapdf/content'
|
|
39
|
+
require 'hexapdf/content/transformation_matrix'
|
|
39
40
|
|
|
40
41
|
module HexaPDF
|
|
41
42
|
module Type
|
|
@@ -129,7 +130,7 @@ module HexaPDF
|
|
|
129
130
|
define_field :TrimBox, type: Rectangle, version: '1.3'
|
|
130
131
|
define_field :ArtBox, type: Rectangle, version: '1.3'
|
|
131
132
|
define_field :BoxColorInfo, type: Dictionary, version: '1.4'
|
|
132
|
-
define_field :Contents, type: [
|
|
133
|
+
define_field :Contents, type: [Stream, Array]
|
|
133
134
|
define_field :Rotate, type: Integer, default: 0
|
|
134
135
|
define_field :Group, type: Dictionary, version: '1.4'
|
|
135
136
|
define_field :Thumb, type: Stream
|
|
@@ -182,7 +183,13 @@ module HexaPDF
|
|
|
182
183
|
end
|
|
183
184
|
end
|
|
184
185
|
|
|
185
|
-
#
|
|
186
|
+
# :call-seq:
|
|
187
|
+
# page.box(type = :media) -> box
|
|
188
|
+
# page.box(type = :media, rectangle) -> rectangle
|
|
189
|
+
#
|
|
190
|
+
# If no +rectangle+ is given, returns the rectangle defining a certain kind of box for the
|
|
191
|
+
# page. Otherwise sets the value for the given box type to +rectangle+ (an array with four
|
|
192
|
+
# values or a HexaPDF::Rectangle).
|
|
186
193
|
#
|
|
187
194
|
# This method should be used instead of directly accessing any of /MediaBox, /CropBox,
|
|
188
195
|
# /BleedBox, /ArtBox or /TrimBox because it also takes the fallback values into account!
|
|
@@ -209,15 +216,81 @@ module HexaPDF
|
|
|
209
216
|
# author. The default is the crop box.
|
|
210
217
|
#
|
|
211
218
|
# See: PDF1.7 s14.11.2
|
|
212
|
-
def box(type = :media)
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
+
def box(type = :media, rectangle = nil)
|
|
220
|
+
if rectangle
|
|
221
|
+
case type
|
|
222
|
+
when :media, :crop, :bleed, :trim, :art
|
|
223
|
+
self["#{type.capitalize}Box".to_sym] = rectangle
|
|
224
|
+
else
|
|
225
|
+
raise ArgumentError, "Unsupported page box type provided: #{type}"
|
|
226
|
+
end
|
|
227
|
+
else
|
|
228
|
+
case type
|
|
229
|
+
when :media then self[:MediaBox]
|
|
230
|
+
when :crop then self[:CropBox] || self[:MediaBox]
|
|
231
|
+
when :bleed then self[:BleedBox] || self[:CropBox] || self[:MediaBox]
|
|
232
|
+
when :trim then self[:TrimBox] || self[:CropBox] || self[:MediaBox]
|
|
233
|
+
when :art then self[:ArtBox] || self[:CropBox] || self[:MediaBox]
|
|
234
|
+
else
|
|
235
|
+
raise ArgumentError, "Unsupported page box type provided: #{type}"
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Returns the orientation of the media box, either :portrait or :landscape.
|
|
241
|
+
def orientation
|
|
242
|
+
box = self[:MediaBox]
|
|
243
|
+
rotation = self[:Rotate]
|
|
244
|
+
if (box.height > box.width && (rotation == 0 || rotation == 180)) ||
|
|
245
|
+
(box.height < box.width && (rotation == 90 || rotation == 270))
|
|
246
|
+
:portrait
|
|
247
|
+
else
|
|
248
|
+
:landscape
|
|
249
|
+
end
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Rotates the page +angle+ degrees counterclockwise where +angle+ has to be a multiple of 90.
|
|
253
|
+
#
|
|
254
|
+
# Positive values rotate the page to the left, negative values to the right. If +flatten+ is
|
|
255
|
+
# +true+, the rotation is not done via the page's meta data but by "rotating" the canvas
|
|
256
|
+
# itself.
|
|
257
|
+
#
|
|
258
|
+
# Note that the :Rotate key of a page object describes the angle in a clockwise orientation
|
|
259
|
+
# but this method uses counterclockwise rotation to be consistent with other rotation methods
|
|
260
|
+
# (e.g. HexaPDF::Content::Canvas#rotate).
|
|
261
|
+
def rotate(angle, flatten: false)
|
|
262
|
+
if angle % 90 != 0
|
|
263
|
+
raise ArgumentError, "Page rotation has to be multiple of 90 degrees"
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
cw_angle = (self[:Rotate] - angle) % 360
|
|
267
|
+
|
|
268
|
+
if flatten
|
|
269
|
+
delete(:Rotate)
|
|
270
|
+
return if cw_angle == 0
|
|
271
|
+
|
|
272
|
+
matrix, llx, lly, urx, ury = \
|
|
273
|
+
case cw_angle
|
|
274
|
+
when 90
|
|
275
|
+
[HexaPDF::Content::TransformationMatrix.new(0, -1, 1, 0),
|
|
276
|
+
box.right, box.bottom, box.left, box.top]
|
|
277
|
+
when 180
|
|
278
|
+
[HexaPDF::Content::TransformationMatrix.new(-1, 0, 0, -1),
|
|
279
|
+
box.right, box.top, box.left, box.bottom]
|
|
280
|
+
when 270
|
|
281
|
+
[HexaPDF::Content::TransformationMatrix.new(0, 1, -1, 0),
|
|
282
|
+
box.left, box.top, box.right, box.bottom]
|
|
283
|
+
end
|
|
284
|
+
[:MediaBox, :CropBox, :BleedBox, :TrimBox, :ArtBox].each do |box|
|
|
285
|
+
next unless key?(box)
|
|
286
|
+
self[box].value = matrix.evaluate(llx, lly).concat(matrix.evaluate(urx, ury))
|
|
287
|
+
end
|
|
288
|
+
|
|
289
|
+
before_contents = document.add({}, stream: " q #{matrix.to_a.join(' ')} cm ")
|
|
290
|
+
after_contents = document.add({}, stream: " Q ")
|
|
291
|
+
self[:Contents] = [before_contents, *self[:Contents], after_contents]
|
|
219
292
|
else
|
|
220
|
-
|
|
293
|
+
self[:Rotate] = cw_angle
|
|
221
294
|
end
|
|
222
295
|
end
|
|
223
296
|
|
data/lib/hexapdf/version.rb
CHANGED
|
@@ -51,7 +51,7 @@ module CommonTokenizerTests
|
|
|
51
51
|
<</Name 5>>
|
|
52
52
|
|
|
53
53
|
% Test
|
|
54
|
-
|
|
54
|
+
EOF
|
|
55
55
|
|
|
56
56
|
expected_tokens = [
|
|
57
57
|
true, false,
|
|
@@ -125,7 +125,7 @@ module CommonTokenizerTests
|
|
|
125
125
|
create_tokenizer(<<-EOF.chomp.gsub(/^ {8}/, ''))
|
|
126
126
|
true false null 123 34.5 (string) <4E6F76> /Name
|
|
127
127
|
[5 6 /Name] <</Name 5/Null null>>
|
|
128
|
-
|
|
128
|
+
EOF
|
|
129
129
|
assert_equal(true, @tokenizer.next_object)
|
|
130
130
|
assert_equal(false, @tokenizer.next_object)
|
|
131
131
|
assert_nil(@tokenizer.next_object)
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
require 'test_helper'
|
|
4
|
+
require_relative '../common'
|
|
5
|
+
require 'hexapdf/document'
|
|
6
|
+
require 'hexapdf/content'
|
|
7
|
+
require 'hexapdf/content/graphic_object'
|
|
8
|
+
|
|
9
|
+
describe HexaPDF::Content::GraphicObject::Geom2D do
|
|
10
|
+
before do
|
|
11
|
+
@obj = HexaPDF::Content::GraphicObject::Geom2D.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "allows creation via the ::configure method" do
|
|
15
|
+
obj = HexaPDF::Content::GraphicObject::Geom2D.configure(object: Geom2D::Point(5, 5))
|
|
16
|
+
assert_equal(Geom2D::Point(5, 5), obj.object)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "creates a default Geom2D drawing support object" do
|
|
20
|
+
obj = HexaPDF::Content::GraphicObject::Geom2D.new
|
|
21
|
+
assert_nil(obj.object)
|
|
22
|
+
assert_equal(1, obj.point_radius)
|
|
23
|
+
assert_equal(false, obj.path_only)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
it "allows configuration of the object" do
|
|
27
|
+
@obj.configure(object: Geom2D::Point(5, 5), point_radius: 3, path_only: true)
|
|
28
|
+
assert_equal(Geom2D::Point(5, 5), @obj.object)
|
|
29
|
+
assert_equal(3, @obj.point_radius)
|
|
30
|
+
assert_equal(true, @obj.path_only)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
describe "draw" do
|
|
34
|
+
before do
|
|
35
|
+
doc = HexaPDF::Document.new
|
|
36
|
+
@canvas = doc.pages.add.canvas
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
it "draws a Geom2D::Point onto the canvas" do
|
|
40
|
+
@obj.object = Geom2D::Point(5, 5)
|
|
41
|
+
@obj.draw(@canvas)
|
|
42
|
+
assert_operators(@canvas.contents,
|
|
43
|
+
[:move_to, :curve_to, :curve_to, :curve_to, :curve_to, :curve_to, :curve_to,
|
|
44
|
+
:close_subpath, :fill_path_non_zero],
|
|
45
|
+
only_names: true)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
it "draws a Geom2D::Segment onto the canvas" do
|
|
49
|
+
@obj.object = Geom2D::Segment([5, 6], [10, 11])
|
|
50
|
+
@obj.draw(@canvas)
|
|
51
|
+
assert_operators(@canvas.contents,
|
|
52
|
+
[[:move_to, [5, 6]], [:line_to, [10, 11]], [:stroke_path]])
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
it "draws a Geom2D::Polygon onto the canvas" do
|
|
56
|
+
@obj.object = Geom2D::Polygon([5, 6], [10, 11], [7, 9])
|
|
57
|
+
@obj.draw(@canvas)
|
|
58
|
+
assert_operators(@canvas.contents,
|
|
59
|
+
[[:move_to, [5, 6]], [:line_to, [10, 11]], [:line_to, [7, 9]],
|
|
60
|
+
[:stroke_path]])
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
it "draws a Geom2D::PolygonSet onto the canvas" do
|
|
64
|
+
@obj.object = Geom2D::PolygonSet(Geom2D::Polygon([5, 6], [10, 11], [7, 9]),
|
|
65
|
+
Geom2D::Polygon([0, 0], [4, 0], [2, 3]))
|
|
66
|
+
@obj.draw(@canvas)
|
|
67
|
+
assert_operators(@canvas.contents,
|
|
68
|
+
[[:move_to, [5, 6]], [:line_to, [10, 11]], [:line_to, [7, 9]],
|
|
69
|
+
[:close_subpath],
|
|
70
|
+
[:move_to, [0, 0]], [:line_to, [4, 0]], [:line_to, [2, 3]],
|
|
71
|
+
[:close_subpath], [:stroke_path]])
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
it "fails for unkown classes" do
|
|
75
|
+
@obj.object = 5
|
|
76
|
+
assert_raises(HexaPDF::Error) { @obj.draw(@canvas) }
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
@@ -76,7 +76,7 @@ describe HexaPDF::Encryption::StandardSecurityHandler do
|
|
|
76
76
|
end
|
|
77
77
|
end
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
unless basename.start_with?("userpwd")
|
|
80
80
|
it "can decrypt the encrypted file #{basename} with the owner password" do
|
|
81
81
|
begin
|
|
82
82
|
doc = HexaPDF::Document.new(io: StringIO.new(File.binread(file)),
|
|
@@ -111,7 +111,7 @@ describe HexaPDF::Font::TrueTypeWrapper do
|
|
|
111
111
|
assert_equal(:Font, cidfont[:Type])
|
|
112
112
|
assert_equal(:CIDFontType2, cidfont[:Subtype])
|
|
113
113
|
assert_equal({Registry: "Adobe", Ordering: "Identity", Supplement: 0},
|
|
114
|
-
cidfont[:CIDSystemInfo])
|
|
114
|
+
cidfont[:CIDSystemInfo].value)
|
|
115
115
|
assert_equal(:Identity, cidfont[:CIDToGIDMap])
|
|
116
116
|
assert_equal(@font_wrapper.glyph(3).width, cidfont[:DW])
|
|
117
117
|
assert_equal([2, [glyph.width]], cidfont[:W])
|
|
@@ -75,7 +75,7 @@ describe HexaPDF::Font::Type1Wrapper do
|
|
|
75
75
|
code = @symbol_wrapper.encode(@symbol_wrapper.glyph(:plus))
|
|
76
76
|
@doc.dispatch_message(:complete_objects)
|
|
77
77
|
assert_equal("\x21", code)
|
|
78
|
-
assert_equal({Differences: [32, :space, :plus]}, @symbol_wrapper.dict[:Encoding])
|
|
78
|
+
assert_equal({Differences: [32, :space, :plus]}, @symbol_wrapper.dict[:Encoding].value)
|
|
79
79
|
end
|
|
80
80
|
end
|
|
81
81
|
end
|
|
@@ -11,7 +11,7 @@ describe HexaPDF::Font::TrueType::Table::Cmap do
|
|
|
11
11
|
[0, 1, 28],
|
|
12
12
|
[3, 1, 28 + f0.length],
|
|
13
13
|
[1, 0, 28],
|
|
14
|
-
].map {|a| a.pack('n2N') }.join('') << f0 <<
|
|
14
|
+
].map {|a| a.pack('n2N') }.join('') << f0 << [10, 22, 0, 0, 2, 10, 13].pack('nN2N2n2')
|
|
15
15
|
set_up_stub_true_type_font(data)
|
|
16
16
|
end
|
|
17
17
|
|
|
@@ -22,7 +22,7 @@ describe HexaPDF::Font::TrueType::Table::Directory do
|
|
|
22
22
|
dir = HexaPDF::Font::TrueType::Table::Directory.new(@file, @self_entry)
|
|
23
23
|
entry = dir.entry('CUST')
|
|
24
24
|
assert_equal('CUST', entry.tag)
|
|
25
|
-
assert_equal('----'.
|
|
25
|
+
assert_equal('----'.unpack1('N'), entry.checksum)
|
|
26
26
|
assert_equal(28, entry.offset)
|
|
27
27
|
assert_equal(5, entry.length)
|
|
28
28
|
end
|
|
@@ -8,7 +8,7 @@ describe HexaPDF::Font::TrueType::Table::Head do
|
|
|
8
8
|
before do
|
|
9
9
|
data = [1, 0, 2, 6554, 0, 42, 0x5f0f, 0x3CF5, 3, 64].pack('n*')
|
|
10
10
|
@time = Time.new(2016, 05, 01)
|
|
11
|
-
data << ([(@time - HexaPDF::Font::TrueType::Table::TIME_EPOCH).to_i] * 2).pack('
|
|
11
|
+
data << ([(@time - HexaPDF::Font::TrueType::Table::TIME_EPOCH).to_i] * 2).pack('q>*')
|
|
12
12
|
data << [-132, -152, 3423, 4231, 3, 9, -2, 0, 0].pack('s>4n2s>3')
|
|
13
13
|
set_up_stub_true_type_font(data)
|
|
14
14
|
end
|
|
@@ -43,8 +43,12 @@ describe HexaPDF::Font::TrueType::Table::Head do
|
|
|
43
43
|
|
|
44
44
|
describe "checksum_valid?" do
|
|
45
45
|
it "checks whether an entry's checksum is valid" do
|
|
46
|
-
data = 254.chr * 12 +
|
|
47
|
-
|
|
46
|
+
data = 254.chr * 12 + # fields before checksum field
|
|
47
|
+
[0x5F0F3CF5].pack('N') + # checksum field
|
|
48
|
+
254.chr * 4 +
|
|
49
|
+
0.chr * 4 + 254.chr * 4 + 0.chr * 4 + 254.chr * 4 + # date fields
|
|
50
|
+
254.chr * 16 + 0.chr * 4
|
|
51
|
+
@entry.checksum = (0xfefefefe * 9 + 0x5F0F3CF5) % 2**32
|
|
48
52
|
table = create_table(:Head, data)
|
|
49
53
|
assert(table.checksum_valid?)
|
|
50
54
|
end
|
|
@@ -10,21 +10,31 @@ describe HexaPDF::Layout::Box do
|
|
|
10
10
|
HexaPDF::Layout::Box.new(*args, &block)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
describe "
|
|
13
|
+
describe "::create" do
|
|
14
|
+
it "passes the block on to #initialize" do
|
|
15
|
+
block = proc {}
|
|
16
|
+
box = HexaPDF::Layout::Box.create(&block)
|
|
17
|
+
assert_same(block, box.instance_eval { @draw_block })
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
it "allows specifying style options" do
|
|
21
|
+
box = HexaPDF::Layout::Box.create(background_color: 20)
|
|
22
|
+
assert_equal(20, box.style.background_color)
|
|
23
|
+
end
|
|
24
|
+
|
|
14
25
|
it "takes content width and height" do
|
|
15
|
-
box =
|
|
26
|
+
box = HexaPDF::Layout::Box.create(width: 100, height: 200, content_box: true,
|
|
27
|
+
padding: 10, border: {width: 10})
|
|
16
28
|
assert_equal(100, box.content_width)
|
|
17
29
|
assert_equal(200, box.content_height)
|
|
18
30
|
end
|
|
31
|
+
end
|
|
19
32
|
|
|
33
|
+
describe "initialize" do
|
|
20
34
|
it "takes box width and height" do
|
|
21
35
|
box = create_box(width: 100, height: 200)
|
|
22
|
-
assert_equal(100, box.
|
|
23
|
-
assert_equal(200, box.
|
|
24
|
-
|
|
25
|
-
box = create_box(width: 100, height: 200, style: {padding: [20, 10], border: {width: [10, 5]}})
|
|
26
|
-
assert_equal(70, box.content_width)
|
|
27
|
-
assert_equal(140, box.content_height)
|
|
36
|
+
assert_equal(100, box.width)
|
|
37
|
+
assert_equal(200, box.height)
|
|
28
38
|
end
|
|
29
39
|
|
|
30
40
|
it "allows passing a Style object or a hash" do
|
|
@@ -36,16 +46,41 @@ describe HexaPDF::Layout::Box do
|
|
|
36
46
|
end
|
|
37
47
|
end
|
|
38
48
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
49
|
+
describe "fit" do
|
|
50
|
+
before do
|
|
51
|
+
@frame = Object.new
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
it "fits a fixed sized box" do
|
|
55
|
+
box = create_box(width: 50, height: 50)
|
|
56
|
+
assert(box.fit(100, 100, @frame))
|
|
57
|
+
assert_equal(50, box.width)
|
|
58
|
+
assert_equal(50, box.height)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "uses the maximum available width" do
|
|
62
|
+
box = create_box(height: 50)
|
|
63
|
+
assert(box.fit(100, 100, @frame))
|
|
64
|
+
assert_equal(100, box.width)
|
|
65
|
+
assert_equal(50, box.height)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
it "uses the maximum available height" do
|
|
69
|
+
box = create_box(width: 50)
|
|
70
|
+
assert(box.fit(100, 100, @frame))
|
|
71
|
+
assert_equal(50, box.width)
|
|
72
|
+
assert_equal(100, box.height)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
it "returns false if the box doesn't fit" do
|
|
76
|
+
box = create_box(width: 101)
|
|
77
|
+
refute(box.fit(100, 100, @frame))
|
|
78
|
+
end
|
|
44
79
|
end
|
|
45
80
|
|
|
46
81
|
describe "draw" do
|
|
47
82
|
it "draws the box onto the canvas" do
|
|
48
|
-
box = create_box(
|
|
83
|
+
box = create_box(width: 150, height: 130) do |canvas, _|
|
|
49
84
|
canvas.line_width(15)
|
|
50
85
|
end
|
|
51
86
|
box.style.background_color = 0.5
|
|
@@ -69,6 +104,8 @@ describe HexaPDF::Layout::Box do
|
|
|
69
104
|
[:restore_graphics_state],
|
|
70
105
|
[:save_graphics_state],
|
|
71
106
|
[:set_line_width, [5]],
|
|
107
|
+
[:append_rectangle, [5, 5, 150, 130]],
|
|
108
|
+
[:clip_path_non_zero], [:end_path],
|
|
72
109
|
[:append_rectangle, [7.5, 7.5, 145, 125]],
|
|
73
110
|
[:stroke_path],
|
|
74
111
|
[:restore_graphics_state],
|
|
@@ -86,8 +123,13 @@ describe HexaPDF::Layout::Box do
|
|
|
86
123
|
|
|
87
124
|
it "draws nothing onto the canvas if the box is empty" do
|
|
88
125
|
@canvas = HexaPDF::Document.new.pages.add.canvas
|
|
89
|
-
|
|
126
|
+
box = create_box
|
|
127
|
+
box.draw(@canvas, 5, 5)
|
|
90
128
|
assert_operators(@canvas.contents, [])
|
|
129
|
+
refute(box.style.background_color?)
|
|
130
|
+
refute(box.style.underlays?)
|
|
131
|
+
refute(box.style.border?)
|
|
132
|
+
refute(box.style.overlays?)
|
|
91
133
|
end
|
|
92
134
|
end
|
|
93
135
|
|