hexapdf 0.7.0 → 0.8.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 +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
|
|