pdf-core 0.5.1 → 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/Gemfile +3 -1
- data/Rakefile +24 -6
- data/lib/pdf/core.rb +22 -19
- data/lib/pdf/core/annotations.rb +17 -18
- data/lib/pdf/core/byte_string.rb +3 -2
- data/lib/pdf/core/destinations.rb +18 -15
- data/lib/pdf/core/document_state.rb +30 -22
- data/lib/pdf/core/filter_list.rb +18 -6
- data/lib/pdf/core/filters.rb +5 -5
- data/lib/pdf/core/graphics_state.rb +17 -20
- data/lib/pdf/core/literal_string.rb +2 -1
- data/lib/pdf/core/name_tree.rb +46 -43
- data/lib/pdf/core/object_store.rb +17 -22
- data/lib/pdf/core/outline_item.rb +11 -5
- data/lib/pdf/core/outline_root.rb +3 -1
- data/lib/pdf/core/page.rb +96 -66
- data/lib/pdf/core/page_geometry.rb +53 -55
- data/lib/pdf/core/pdf_object.rb +55 -46
- data/lib/pdf/core/reference.rb +21 -17
- data/lib/pdf/core/renderer.rb +46 -37
- data/lib/pdf/core/stream.rb +14 -8
- data/lib/pdf/core/text.rb +100 -47
- data/lib/pdf/core/utils.rb +13 -0
- data/pdf-core.gemspec +37 -22
- metadata +83 -29
- metadata.gz.sig +2 -0
- data/spec/decimal_rounding_spec.rb +0 -12
- data/spec/filters_spec.rb +0 -34
- data/spec/name_tree_spec.rb +0 -122
- data/spec/object_store_spec.rb +0 -49
- data/spec/pdf_object_spec.rb +0 -172
- data/spec/reference_spec.rb +0 -62
- data/spec/spec_helper.rb +0 -32
- data/spec/stream_spec.rb +0 -59
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Describes PDF page geometries
|
4
4
|
#
|
@@ -8,7 +8,6 @@
|
|
8
8
|
|
9
9
|
module PDF
|
10
10
|
module Core
|
11
|
-
|
12
11
|
# Dimensions pulled from PDF::Writer, rubyforge.org/projects/ruby-pdf
|
13
12
|
#
|
14
13
|
# All of these dimensions are in PDF Points (1/72 inch)
|
@@ -68,59 +67,58 @@ module PDF
|
|
68
67
|
# TABLOID:: => 792.00 x 1224.00
|
69
68
|
#
|
70
69
|
module PageGeometry
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
70
|
+
SIZES = {
|
71
|
+
'4A0' => [4767.87, 6740.79],
|
72
|
+
'2A0' => [3370.39, 4767.87],
|
73
|
+
'A0' => [2383.94, 3370.39],
|
74
|
+
'A1' => [1683.78, 2383.94],
|
75
|
+
'A2' => [1190.55, 1683.78],
|
76
|
+
'A3' => [841.89, 1190.55],
|
77
|
+
'A4' => [595.28, 841.89],
|
78
|
+
'A5' => [419.53, 595.28],
|
79
|
+
'A6' => [297.64, 419.53],
|
80
|
+
'A7' => [209.76, 297.64],
|
81
|
+
'A8' => [147.40, 209.76],
|
82
|
+
'A9' => [104.88, 147.40],
|
83
|
+
'A10' => [73.70, 104.88],
|
84
|
+
'B0' => [2834.65, 4008.19],
|
85
|
+
'B1' => [2004.09, 2834.65],
|
86
|
+
'B2' => [1417.32, 2004.09],
|
87
|
+
'B3' => [1000.63, 1417.32],
|
88
|
+
'B4' => [708.66, 1000.63],
|
89
|
+
'B5' => [498.90, 708.66],
|
90
|
+
'B6' => [354.33, 498.90],
|
91
|
+
'B7' => [249.45, 354.33],
|
92
|
+
'B8' => [175.75, 249.45],
|
93
|
+
'B9' => [124.72, 175.75],
|
94
|
+
'B10' => [87.87, 124.72],
|
95
|
+
'C0' => [2599.37, 3676.54],
|
96
|
+
'C1' => [1836.85, 2599.37],
|
97
|
+
'C2' => [1298.27, 1836.85],
|
98
|
+
'C3' => [918.43, 1298.27],
|
99
|
+
'C4' => [649.13, 918.43],
|
100
|
+
'C5' => [459.21, 649.13],
|
101
|
+
'C6' => [323.15, 459.21],
|
102
|
+
'C7' => [229.61, 323.15],
|
103
|
+
'C8' => [161.57, 229.61],
|
104
|
+
'C9' => [113.39, 161.57],
|
105
|
+
'C10' => [79.37, 113.39],
|
106
|
+
'RA0' => [2437.80, 3458.27],
|
107
|
+
'RA1' => [1729.13, 2437.80],
|
108
|
+
'RA2' => [1218.90, 1729.13],
|
109
|
+
'RA3' => [864.57, 1218.90],
|
110
|
+
'RA4' => [609.45, 864.57],
|
111
|
+
'SRA0' => [2551.18, 3628.35],
|
112
|
+
'SRA1' => [1814.17, 2551.18],
|
113
|
+
'SRA2' => [1275.59, 1814.17],
|
114
|
+
'SRA3' => [907.09, 1275.59],
|
115
|
+
'SRA4' => [637.80, 907.09],
|
116
|
+
'EXECUTIVE' => [521.86, 756.00],
|
117
|
+
'FOLIO' => [612.00, 936.00],
|
118
|
+
'LEGAL' => [612.00, 1008.00],
|
119
|
+
'LETTER' => [612.00, 792.00],
|
120
|
+
'TABLOID' => [792.00, 1224.00]
|
121
|
+
}.freeze
|
123
122
|
end
|
124
123
|
end
|
125
124
|
end
|
126
|
-
|
data/lib/pdf/core/pdf_object.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
3
|
# pdf_object.rb : Handles Ruby to PDF object serialization
|
4
4
|
#
|
5
5
|
# Copyright April 2008, Gregory Brown. All Rights Reserved.
|
@@ -13,89 +13,98 @@ module PDF
|
|
13
13
|
module_function
|
14
14
|
|
15
15
|
def real(num)
|
16
|
-
num.
|
16
|
+
format('%<number>.5f', number: num).sub(/((?<!\.)0)+\z/, '')
|
17
17
|
end
|
18
18
|
|
19
19
|
def real_params(array)
|
20
|
-
array.map { |e| real(e) }.join(
|
20
|
+
array.map { |e| real(e) }.join(' ')
|
21
21
|
end
|
22
22
|
|
23
23
|
def utf8_to_utf16(str)
|
24
|
-
"\xFE\xFF".force_encoding(::Encoding::UTF_16BE) +
|
24
|
+
(+"\xFE\xFF").force_encoding(::Encoding::UTF_16BE) +
|
25
|
+
str.encode(::Encoding::UTF_16BE)
|
25
26
|
end
|
26
27
|
|
27
28
|
# encodes any string into a hex representation. The result is a string
|
28
29
|
# with only 0-9 and a-f characters. That result is valid ASCII so tag
|
29
30
|
# it as such to account for behaviour of different ruby VMs
|
30
31
|
def string_to_hex(str)
|
31
|
-
str.
|
32
|
+
str.unpack1('H*').force_encoding(::Encoding::US_ASCII)
|
32
33
|
end
|
33
34
|
|
35
|
+
ESCAPED_NAME_CHARACTERS = (1..32).to_a + [35, 40, 41, 47, 60, 62] + (127..255).to_a
|
36
|
+
|
34
37
|
# Serializes Ruby objects to their PDF equivalents. Most primitive objects
|
35
38
|
# will work as expected, but please note that Name objects are represented
|
36
|
-
# by Ruby Symbol objects and Dictionary objects are represented by Ruby
|
37
|
-
# (keyed by symbols)
|
39
|
+
# by Ruby Symbol objects and Dictionary objects are represented by Ruby
|
40
|
+
# hashes (keyed by symbols)
|
38
41
|
#
|
39
42
|
# Examples:
|
40
43
|
#
|
41
|
-
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
#
|
44
|
+
# pdf_object(true) #=> "true"
|
45
|
+
# pdf_object(false) #=> "false"
|
46
|
+
# pdf_object(1.2124) #=> "1.2124"
|
47
|
+
# pdf_object('foo bar') #=> "(foo bar)"
|
48
|
+
# pdf_object(:Symbol) #=> "/Symbol"
|
49
|
+
# pdf_object(['foo',:bar, [1,2]]) #=> "[foo /bar [1 2]]"
|
47
50
|
#
|
48
|
-
def
|
49
|
-
case
|
50
|
-
when NilClass then
|
51
|
-
when TrueClass then
|
52
|
-
when FalseClass then
|
51
|
+
def pdf_object(obj, in_content_stream = false)
|
52
|
+
case obj
|
53
|
+
when NilClass then 'null'
|
54
|
+
when TrueClass then 'true'
|
55
|
+
when FalseClass then 'false'
|
53
56
|
when Numeric
|
54
|
-
obj = real(obj) unless obj.
|
57
|
+
obj = real(obj) unless obj.is_a?(Integer)
|
55
58
|
|
56
|
-
|
57
|
-
|
59
|
+
# NOTE: this can fail on huge floating point numbers, but it seems
|
60
|
+
# unlikely to ever happen in practice.
|
61
|
+
num_string = String(obj)
|
62
|
+
|
63
|
+
# Truncate trailing fraction zeroes
|
64
|
+
num_string.sub(/(\d*)((\.0*$)|(\.0*[1-9]*)0*$)/, '\1\4')
|
58
65
|
when Array
|
59
|
-
"[
|
66
|
+
"[#{obj.map { |e| pdf_object(e, in_content_stream) }.join(' ')}]"
|
60
67
|
when PDF::Core::LiteralString
|
61
|
-
obj = obj.gsub(/[\\\n\r\t\b\f
|
68
|
+
obj = obj.gsub(/[\\\n\r\t\b\f()]/) { |m| "\\#{m}" }
|
62
69
|
"(#{obj})"
|
63
70
|
when Time
|
64
|
-
obj = obj.strftime(
|
65
|
-
obj = obj.gsub(/[\\\n\r\t\b\f
|
71
|
+
obj = "#{obj.strftime('D:%Y%m%d%H%M%S%z').chop.chop}'00'"
|
72
|
+
obj = obj.gsub(/[\\\n\r\t\b\f()]/) { |m| "\\#{m}" }
|
66
73
|
"(#{obj})"
|
67
74
|
when PDF::Core::ByteString
|
68
|
-
"
|
75
|
+
"<#{obj.unpack1('H*')}>"
|
69
76
|
when String
|
70
77
|
obj = utf8_to_utf16(obj) unless in_content_stream
|
71
|
-
"
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
"<#{string_to_hex(obj)}>"
|
79
|
+
when Symbol
|
80
|
+
name_string =
|
81
|
+
obj.to_s.unpack('C*').map do |n|
|
82
|
+
if ESCAPED_NAME_CHARACTERS.include?(n)
|
83
|
+
"##{n.to_s(16).upcase}"
|
84
|
+
else
|
85
|
+
[n].pack('C*')
|
86
|
+
end
|
87
|
+
end.join
|
88
|
+
"/#{name_string}"
|
80
89
|
when ::Hash
|
81
|
-
output =
|
82
|
-
obj.each do |k,v|
|
83
|
-
unless String
|
90
|
+
output = +'<< '
|
91
|
+
obj.each do |k, v|
|
92
|
+
unless k.is_a?(String) || k.is_a?(Symbol)
|
84
93
|
raise PDF::Core::Errors::FailedObjectConversion,
|
85
|
-
|
94
|
+
'A PDF Dictionary must be keyed by names'
|
86
95
|
end
|
87
|
-
output <<
|
88
|
-
|
96
|
+
output << pdf_object(k.to_sym, in_content_stream) << ' ' <<
|
97
|
+
pdf_object(v, in_content_stream) << "\n"
|
89
98
|
end
|
90
|
-
output <<
|
99
|
+
output << '>>'
|
91
100
|
when PDF::Core::Reference
|
92
101
|
obj.to_s
|
93
102
|
when PDF::Core::NameTree::Node
|
94
|
-
|
103
|
+
pdf_object(obj.to_hash)
|
95
104
|
when PDF::Core::NameTree::Value
|
96
|
-
|
105
|
+
"#{pdf_object(obj.name)} #{pdf_object(obj.value)}"
|
97
106
|
when PDF::Core::OutlineRoot, PDF::Core::OutlineItem
|
98
|
-
|
107
|
+
pdf_object(obj.to_hash)
|
99
108
|
else
|
100
109
|
raise PDF::Core::Errors::FailedObjectConversion,
|
101
110
|
"This object cannot be serialized to PDF (#{obj.inspect})"
|
data/lib/pdf/core/reference.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# reference.rb : Implementation of PDF indirect objects
|
4
4
|
#
|
@@ -6,13 +6,19 @@
|
|
6
6
|
#
|
7
7
|
# This is free software. Please see the LICENSE and COPYING files for details.
|
8
8
|
|
9
|
+
require 'pdf/core/utils'
|
9
10
|
|
10
11
|
module PDF
|
11
12
|
module Core
|
12
13
|
class Reference #:nodoc:
|
13
|
-
|
14
14
|
attr_accessor :gen, :data, :offset, :stream, :identifier
|
15
15
|
|
16
|
+
class CannotAttachStream < StandardError
|
17
|
+
def initialize(message = 'Cannot attach stream to a non-dictionary object')
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
16
22
|
def initialize(id, data)
|
17
23
|
@identifier = id
|
18
24
|
@gen = 0
|
@@ -21,18 +27,22 @@ module PDF
|
|
21
27
|
end
|
22
28
|
|
23
29
|
def object
|
24
|
-
output = "#{@identifier} #{gen} obj\n"
|
25
|
-
|
26
|
-
output << PDF::Core
|
30
|
+
output = +"#{@identifier} #{gen} obj\n"
|
31
|
+
if @stream.empty?
|
32
|
+
output << PDF::Core.pdf_object(data) << "\n"
|
27
33
|
else
|
28
|
-
output << PDF::Core
|
34
|
+
output << PDF::Core.pdf_object(data.merge(@stream.data)) <<
|
35
|
+
"\n" << @stream.object
|
29
36
|
end
|
30
37
|
|
31
38
|
output << "endobj\n"
|
32
39
|
end
|
33
40
|
|
34
41
|
def <<(io)
|
35
|
-
|
42
|
+
unless @data.is_a?(::Hash)
|
43
|
+
raise CannotAttachStream
|
44
|
+
end
|
45
|
+
|
36
46
|
(@stream ||= Stream.new) << io
|
37
47
|
end
|
38
48
|
|
@@ -43,22 +53,22 @@ module PDF
|
|
43
53
|
# Creates a deep copy of this ref. If +share+ is provided, shares the
|
44
54
|
# given dictionary entries between the old ref and the new.
|
45
55
|
#
|
46
|
-
def deep_copy(share=[])
|
56
|
+
def deep_copy(share = [])
|
47
57
|
r = dup
|
48
58
|
|
49
59
|
case r.data
|
50
60
|
when ::Hash
|
51
61
|
# Copy each entry not in +share+.
|
52
62
|
(r.data.keys - share).each do |k|
|
53
|
-
r.data[k] =
|
63
|
+
r.data[k] = Utils.deep_clone(r.data[k])
|
54
64
|
end
|
55
65
|
when PDF::Core::NameTree::Node
|
56
66
|
r.data = r.data.deep_copy
|
57
67
|
else
|
58
|
-
r.data =
|
68
|
+
r.data = Utils.deep_clone(r.data)
|
59
69
|
end
|
60
70
|
|
61
|
-
r.stream =
|
71
|
+
r.stream = Utils.deep_clone(r.stream)
|
62
72
|
r
|
63
73
|
end
|
64
74
|
|
@@ -68,11 +78,5 @@ module PDF
|
|
68
78
|
@stream = other_ref.stream
|
69
79
|
end
|
70
80
|
end
|
71
|
-
|
72
|
-
module_function
|
73
|
-
|
74
|
-
def Reference(*args, &block) #:nodoc:
|
75
|
-
Reference.new(*args, &block)
|
76
|
-
end
|
77
81
|
end
|
78
82
|
end
|
data/lib/pdf/core/renderer.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'stringio'
|
2
4
|
|
3
5
|
module PDF
|
4
6
|
module Core
|
@@ -6,7 +8,7 @@ module PDF
|
|
6
8
|
def initialize(state)
|
7
9
|
@state = state
|
8
10
|
@state.populate_pages_from_store(self)
|
9
|
-
|
11
|
+
|
10
12
|
min_version(state.store.min_version) if state.store.min_version
|
11
13
|
|
12
14
|
@page_number = 0
|
@@ -14,8 +16,8 @@ module PDF
|
|
14
16
|
|
15
17
|
attr_reader :state
|
16
18
|
|
17
|
-
# Creates a new Reference and adds it to the Document's object
|
18
|
-
#
|
19
|
+
# Creates a new Reference and adds it to the Document's object list. The
|
20
|
+
# +data+ argument is anything that Prawn.pdf_object() can convert.
|
19
21
|
#
|
20
22
|
# Returns the identifier which points to the reference in the ObjectStore
|
21
23
|
#
|
@@ -50,7 +52,7 @@ module PDF
|
|
50
52
|
#
|
51
53
|
# pdf.add_content("#{PDF::Core.real_params([x1, y1])} m") # move
|
52
54
|
# pdf.add_content("#{PDF::Core.real_params([ x2, y2 ])} l") # draw path
|
53
|
-
# pdf.add_content(
|
55
|
+
# pdf.add_content('S') # stroke
|
54
56
|
#
|
55
57
|
def add_content(str)
|
56
58
|
save_graphics_state if graphic_state.nil?
|
@@ -62,7 +64,7 @@ module PDF
|
|
62
64
|
# dictionary do not incur the additional overhead.
|
63
65
|
#
|
64
66
|
def names
|
65
|
-
state.store.root.data[:Names] ||= ref!(:
|
67
|
+
state.store.root.data[:Names] ||= ref!(Type: :Names)
|
66
68
|
end
|
67
69
|
|
68
70
|
# Returns true if the Names dictionary is in use for this document.
|
@@ -80,29 +82,36 @@ module PDF
|
|
80
82
|
# Defines a block to be called just before a new page is started.
|
81
83
|
#
|
82
84
|
def on_page_create(&block)
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
end
|
85
|
+
state.on_page_create_callback =
|
86
|
+
if block_given?
|
87
|
+
block
|
88
|
+
end
|
88
89
|
end
|
89
90
|
|
90
91
|
def start_new_page(options = {})
|
91
|
-
|
92
|
+
last_page = state.page
|
93
|
+
if last_page
|
92
94
|
last_page_size = last_page.size
|
93
95
|
last_page_layout = last_page.layout
|
94
96
|
last_page_margins = last_page.margins
|
95
97
|
end
|
96
98
|
|
97
|
-
page_options = {
|
98
|
-
|
99
|
-
|
99
|
+
page_options = {
|
100
|
+
size: options[:size] || last_page_size,
|
101
|
+
layout: options[:layout] || last_page_layout,
|
102
|
+
margins: last_page_margins
|
103
|
+
}
|
100
104
|
if last_page
|
101
|
-
|
105
|
+
if last_page.graphic_state
|
106
|
+
new_graphic_state = last_page.graphic_state.dup
|
107
|
+
end
|
102
108
|
|
103
|
-
#
|
104
|
-
|
105
|
-
|
109
|
+
# Erase the color space so that it gets reset on new page for fussy
|
110
|
+
# pdf-readers
|
111
|
+
if new_graphic_state
|
112
|
+
new_graphic_state.color_space = {}
|
113
|
+
end
|
114
|
+
page_options[:graphic_state] = new_graphic_state
|
106
115
|
end
|
107
116
|
|
108
117
|
state.page = PDF::Core::Page.new(self, page_options)
|
@@ -121,10 +130,10 @@ module PDF
|
|
121
130
|
# draw on it.
|
122
131
|
#
|
123
132
|
# See Prawn::Document#number_pages for a sample usage of this capability.
|
124
|
-
|
125
|
-
def go_to_page(
|
126
|
-
@page_number =
|
127
|
-
state.page = state.pages[
|
133
|
+
|
134
|
+
def go_to_page(page_number)
|
135
|
+
@page_number = page_number
|
136
|
+
state.page = state.pages[page_number - 1]
|
128
137
|
end
|
129
138
|
|
130
139
|
def finalize_all_page_contents
|
@@ -161,18 +170,16 @@ module PDF
|
|
161
170
|
if output.instance_of?(StringIO)
|
162
171
|
str = output.string
|
163
172
|
str.force_encoding(::Encoding::ASCII_8BIT)
|
164
|
-
|
165
|
-
else
|
166
|
-
return nil
|
173
|
+
str
|
167
174
|
end
|
168
175
|
end
|
169
176
|
|
170
177
|
# Renders the PDF document to file.
|
171
178
|
#
|
172
|
-
# pdf.render_file
|
179
|
+
# pdf.render_file 'foo.pdf'
|
173
180
|
#
|
174
181
|
def render_file(filename)
|
175
|
-
File.open(filename,
|
182
|
+
File.open(filename, 'wb') { |f| render(f) }
|
176
183
|
end
|
177
184
|
|
178
185
|
# Write out the PDF Header, as per spec 3.4.1
|
@@ -201,7 +208,7 @@ module PDF
|
|
201
208
|
output << "0 #{state.store.size + 1}\n"
|
202
209
|
output << "0000000000 65535 f \n"
|
203
210
|
state.store.each do |ref|
|
204
|
-
output.printf(
|
211
|
+
output.printf('%<offset>010d', offset: ref.offset)
|
205
212
|
output << " 00000 n \n"
|
206
213
|
end
|
207
214
|
end
|
@@ -209,24 +216,26 @@ module PDF
|
|
209
216
|
# Write out the PDF Trailer, as per spec 3.4.4
|
210
217
|
#
|
211
218
|
def render_trailer(output)
|
212
|
-
trailer_hash = {
|
213
|
-
|
214
|
-
|
219
|
+
trailer_hash = {
|
220
|
+
Size: state.store.size + 1,
|
221
|
+
Root: state.store.root,
|
222
|
+
Info: state.store.info
|
223
|
+
}
|
215
224
|
trailer_hash.merge!(state.trailer) if state.trailer
|
216
225
|
|
217
226
|
output << "trailer\n"
|
218
|
-
output << PDF::Core
|
227
|
+
output << PDF::Core.pdf_object(trailer_hash) << "\n"
|
219
228
|
output << "startxref\n"
|
220
229
|
output << @xref_offset << "\n"
|
221
|
-
output <<
|
230
|
+
output << '%%EOF' << "\n"
|
222
231
|
end
|
223
232
|
|
224
233
|
def open_graphics_state
|
225
|
-
add_content
|
234
|
+
add_content 'q'
|
226
235
|
end
|
227
236
|
|
228
237
|
def close_graphics_state
|
229
|
-
add_content
|
238
|
+
add_content 'Q'
|
230
239
|
end
|
231
240
|
|
232
241
|
def save_graphics_state(graphic_state = nil)
|
@@ -242,7 +251,7 @@ module PDF
|
|
242
251
|
# false otherwise
|
243
252
|
#
|
244
253
|
def compression_enabled?
|
245
|
-
|
254
|
+
state.compress
|
246
255
|
end
|
247
256
|
|
248
257
|
# Pops the last saved graphics state off the graphics state stack and
|