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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 429f3bfae75301dabdb339edccbdd816026f605dc84d1902f07d6494277d6360
|
4
|
+
data.tar.gz: 13fa187ef7307bc6de531a06d9d97272ff2ba0c2a9e21744b52efe1c387094d5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 76f74e81e59e382b4ab8c0bfe0cd58eb6b8624afd4b6b92fee6ed56da432205ad682cd3bd99b364d29be38747727a198f796975cb7718c7f62ad682f33f4953f
|
7
|
+
data.tar.gz: b0b08cdd020ba23a4b3aa135d795eb35089cf0c6fbe34dd5457a3302f78969062347952b2f0f532b9066578a81537059afed889edd7d102817f26439b7f0848e
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,11 +1,29 @@
|
|
1
|
-
|
2
|
-
Bundler.setup
|
1
|
+
# frozen_string_literal: true
|
3
2
|
|
4
3
|
require 'rake'
|
5
4
|
require 'rspec/core/rake_task'
|
6
|
-
task :default => [:spec]
|
7
5
|
|
8
|
-
|
9
|
-
|
10
|
-
|
6
|
+
task default: %i[spec rubocop]
|
7
|
+
|
8
|
+
desc 'Run all rspec files'
|
9
|
+
RSpec::Core::RakeTask.new('spec') do |c|
|
10
|
+
c.rspec_opts = '-t ~unresolved'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rubocop/rake_task'
|
14
|
+
RuboCop::RakeTask.new
|
15
|
+
|
16
|
+
require 'rubygems/package_task'
|
17
|
+
spec = Gem::Specification.load 'pdf-core.gemspec'
|
18
|
+
Gem::PackageTask.new(spec) do |pkg|
|
19
|
+
pkg.need_zip = true
|
20
|
+
pkg.need_tar = true
|
21
|
+
end
|
22
|
+
|
23
|
+
task :checksum do
|
24
|
+
require 'digest/sha2'
|
25
|
+
built_gem_path = "pkg/pdf-core-#{Prawn::VERSION}.gem"
|
26
|
+
checksum = Digest::SHA512.new.hexdigest(File.read(built_gem_path))
|
27
|
+
checksum_path = "checksums/#{built_gem_path}.sha512"
|
28
|
+
File.write(checksum_path, checksum)
|
11
29
|
end
|
data/lib/pdf/core.rb
CHANGED
@@ -1,26 +1,29 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require_relative
|
4
|
-
require_relative
|
5
|
-
require_relative
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
11
|
-
require_relative
|
12
|
-
require_relative
|
13
|
-
require_relative
|
14
|
-
require_relative
|
15
|
-
require_relative
|
16
|
-
require_relative
|
17
|
-
require_relative
|
18
|
-
require_relative
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'core/pdf_object'
|
4
|
+
require_relative 'core/annotations'
|
5
|
+
require_relative 'core/byte_string'
|
6
|
+
require_relative 'core/destinations'
|
7
|
+
require_relative 'core/filters'
|
8
|
+
require_relative 'core/stream'
|
9
|
+
require_relative 'core/reference'
|
10
|
+
require_relative 'core/literal_string'
|
11
|
+
require_relative 'core/filter_list'
|
12
|
+
require_relative 'core/page'
|
13
|
+
require_relative 'core/object_store'
|
14
|
+
require_relative 'core/document_state'
|
15
|
+
require_relative 'core/name_tree'
|
16
|
+
require_relative 'core/graphics_state'
|
17
|
+
require_relative 'core/page_geometry'
|
18
|
+
require_relative 'core/outline_root'
|
19
|
+
require_relative 'core/outline_item'
|
20
|
+
require_relative 'core/renderer'
|
21
|
+
require_relative 'core/text'
|
19
22
|
|
20
23
|
module PDF
|
21
24
|
module Core
|
22
25
|
module Errors
|
23
|
-
# This error is raised when
|
26
|
+
# This error is raised when pdf_object() fails
|
24
27
|
FailedObjectConversion = Class.new(StandardError)
|
25
28
|
|
26
29
|
# This error is raise when trying to restore a graphic state that
|
data/lib/pdf/core/annotations.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# annotations.rb : Implements low-level annotation support for PDF
|
4
4
|
#
|
@@ -11,7 +11,6 @@ module PDF
|
|
11
11
|
# Provides very low-level support for annotations.
|
12
12
|
#
|
13
13
|
module Annotations #:nodoc:
|
14
|
-
|
15
14
|
# Adds a new annotation (section 8.4 in PDF spec) to the current page.
|
16
15
|
# +options+ must be a Hash describing the annotation.
|
17
16
|
#
|
@@ -19,34 +18,35 @@ module PDF
|
|
19
18
|
state.page.dictionary.data[:Annots] ||= []
|
20
19
|
options = sanitize_annotation_hash(options)
|
21
20
|
state.page.dictionary.data[:Annots] << ref!(options)
|
22
|
-
|
21
|
+
options
|
23
22
|
end
|
24
23
|
|
25
|
-
# A convenience method for creating Text annotations. +rect+ must be an
|
26
|
-
# of four numbers, describing the bounds of the annotation.
|
27
|
-
# be a string, to be shown when the annotation is
|
24
|
+
# A convenience method for creating Text annotations. +rect+ must be an
|
25
|
+
# array of four numbers, describing the bounds of the annotation.
|
26
|
+
# +contents+ should be a string, to be shown when the annotation is
|
27
|
+
# activated.
|
28
28
|
#
|
29
|
-
def text_annotation(rect, contents, options={})
|
30
|
-
options = options.merge(:
|
29
|
+
def text_annotation(rect, contents, options = {})
|
30
|
+
options = options.merge(Subtype: :Text, Rect: rect, Contents: contents)
|
31
31
|
annotate(options)
|
32
32
|
end
|
33
33
|
|
34
|
-
# A convenience method for creating Link annotations. +rect+ must be an
|
35
|
-
# of four numbers, describing the bounds of the annotation. The
|
36
|
-
# should include either :Dest (describing the target
|
37
|
-
# string that has been recorded in the
|
38
|
-
#
|
39
|
-
# link to).
|
34
|
+
# A convenience method for creating Link annotations. +rect+ must be an
|
35
|
+
# array of four numbers, describing the bounds of the annotation. The
|
36
|
+
# +options+ hash should include either :Dest (describing the target
|
37
|
+
# destination, usually as a string that has been recorded in the
|
38
|
+
# document's Dests tree), or :A (describing an action to perform on
|
39
|
+
# clicking the link), or :PA (for describing a URL to link to).
|
40
40
|
#
|
41
|
-
def link_annotation(rect, options={})
|
42
|
-
options = options.merge(:
|
41
|
+
def link_annotation(rect, options = {})
|
42
|
+
options = options.merge(Subtype: :Link, Rect: rect)
|
43
43
|
annotate(options)
|
44
44
|
end
|
45
45
|
|
46
46
|
private
|
47
47
|
|
48
48
|
def sanitize_annotation_hash(options)
|
49
|
-
options = options.merge(:
|
49
|
+
options = options.merge(Type: :Annot)
|
50
50
|
|
51
51
|
if options[:Dest].is_a?(String)
|
52
52
|
options[:Dest] = PDF::Core::LiteralString.new(options[:Dest])
|
@@ -54,7 +54,6 @@ module PDF
|
|
54
54
|
|
55
55
|
options
|
56
56
|
end
|
57
|
-
|
58
57
|
end
|
59
58
|
end
|
60
59
|
end
|
data/lib/pdf/core/byte_string.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Implements destination support for PDF
|
4
4
|
#
|
@@ -9,16 +9,19 @@
|
|
9
9
|
module PDF
|
10
10
|
module Core
|
11
11
|
module Destinations #:nodoc:
|
12
|
-
|
13
|
-
#
|
12
|
+
# The maximum number of children to fit into a single node in the Dests
|
13
|
+
# tree.
|
14
14
|
NAME_TREE_CHILDREN_LIMIT = 20 #:nodoc:
|
15
15
|
|
16
|
-
# The Dests name tree in the Name dictionary (see
|
17
|
-
# This name tree is used to store named
|
18
|
-
# (For more on name trees, see section
|
16
|
+
# The Dests name tree in the Name dictionary (see
|
17
|
+
# Prawn::Document::Internal#names). This name tree is used to store named
|
18
|
+
# destinations (PDF spec 8.2.1). (For more on name trees, see section
|
19
|
+
# 3.8.4 in the PDF spec.)
|
19
20
|
#
|
20
21
|
def dests
|
21
|
-
names.data[:Dests] ||= ref!(
|
22
|
+
names.data[:Dests] ||= ref!(
|
23
|
+
PDF::Core::NameTree::Node.new(self, NAME_TREE_CHILDREN_LIMIT)
|
24
|
+
)
|
22
25
|
end
|
23
26
|
|
24
27
|
# Adds a new destination to the dests name tree (see #dests). The
|
@@ -33,56 +36,56 @@ module PDF
|
|
33
36
|
# Return a Dest specification for a specific location (and optional zoom
|
34
37
|
# level).
|
35
38
|
#
|
36
|
-
def dest_xyz(left, top, zoom=nil, dest_page=page)
|
39
|
+
def dest_xyz(left, top, zoom = nil, dest_page = page)
|
37
40
|
[dest_page.dictionary, :XYZ, left, top, zoom]
|
38
41
|
end
|
39
42
|
|
40
43
|
# Return a Dest specification that will fit the given page into the
|
41
44
|
# viewport.
|
42
45
|
#
|
43
|
-
def dest_fit(dest_page=page)
|
46
|
+
def dest_fit(dest_page = page)
|
44
47
|
[dest_page.dictionary, :Fit]
|
45
48
|
end
|
46
49
|
|
47
50
|
# Return a Dest specification that will fit the given page horizontally
|
48
51
|
# into the viewport, aligned vertically at the given top coordinate.
|
49
52
|
#
|
50
|
-
def dest_fit_horizontally(top, dest_page=page)
|
53
|
+
def dest_fit_horizontally(top, dest_page = page)
|
51
54
|
[dest_page.dictionary, :FitH, top]
|
52
55
|
end
|
53
56
|
|
54
57
|
# Return a Dest specification that will fit the given page vertically
|
55
58
|
# into the viewport, aligned horizontally at the given left coordinate.
|
56
59
|
#
|
57
|
-
def dest_fit_vertically(left, dest_page=page)
|
60
|
+
def dest_fit_vertically(left, dest_page = page)
|
58
61
|
[dest_page.dictionary, :FitV, left]
|
59
62
|
end
|
60
63
|
|
61
64
|
# Return a Dest specification that will fit the given rectangle into the
|
62
65
|
# viewport, for the given page.
|
63
66
|
#
|
64
|
-
def dest_fit_rect(left, bottom, right, top, dest_page=page)
|
67
|
+
def dest_fit_rect(left, bottom, right, top, dest_page = page)
|
65
68
|
[dest_page.dictionary, :FitR, left, bottom, right, top]
|
66
69
|
end
|
67
70
|
|
68
71
|
# Return a Dest specfication that will fit the given page's bounding box
|
69
72
|
# into the viewport.
|
70
73
|
#
|
71
|
-
def dest_fit_bounds(dest_page=page)
|
74
|
+
def dest_fit_bounds(dest_page = page)
|
72
75
|
[dest_page.dictionary, :FitB]
|
73
76
|
end
|
74
77
|
|
75
78
|
# Same as #dest_fit_horizontally, but works on the page's bounding box
|
76
79
|
# instead of the entire page.
|
77
80
|
#
|
78
|
-
def dest_fit_bounds_horizontally(top, dest_page=page)
|
81
|
+
def dest_fit_bounds_horizontally(top, dest_page = page)
|
79
82
|
[dest_page.dictionary, :FitBH, top]
|
80
83
|
end
|
81
84
|
|
82
85
|
# Same as #dest_fit_vertically, but works on the page's bounding box
|
83
86
|
# instead of the entire page.
|
84
87
|
#
|
85
|
-
def dest_fit_bounds_vertically(left, dest_page=page)
|
88
|
+
def dest_fit_bounds_vertically(left, dest_page = page)
|
86
89
|
[dest_page.dictionary, :FitBV, left]
|
87
90
|
end
|
88
91
|
end
|
@@ -1,20 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module PDF
|
2
4
|
module Core
|
3
5
|
class DocumentState #:nodoc:
|
4
6
|
def initialize(options)
|
5
7
|
normalize_metadata(options)
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
@store =
|
10
|
+
if options[:print_scaling]
|
11
|
+
PDF::Core::ObjectStore.new(
|
12
|
+
info: options[:info],
|
13
|
+
print_scaling: options[:print_scaling]
|
14
|
+
)
|
15
|
+
else
|
16
|
+
PDF::Core::ObjectStore.new(info: options[:info])
|
17
|
+
end
|
13
18
|
|
14
19
|
@version = 1.3
|
15
20
|
@pages = []
|
16
21
|
@page = nil
|
17
|
-
@trailer = {}
|
22
|
+
@trailer = options.fetch(:trailer, {})
|
18
23
|
@compress = options.fetch(:compress, false)
|
19
24
|
@encrypt = options.fetch(:encrypt, false)
|
20
25
|
@encryption_key = options[:encryption_key]
|
@@ -23,25 +28,24 @@ module PDF
|
|
23
28
|
@on_page_create_callback = nil
|
24
29
|
end
|
25
30
|
|
26
|
-
attr_accessor :store, :version, :pages, :page, :trailer, :compress,
|
27
|
-
|
28
|
-
:before_render_callbacks, :on_page_create_callback
|
31
|
+
attr_accessor :store, :version, :pages, :page, :trailer, :compress, :encrypt, :encryption_key, :skip_encoding
|
32
|
+
attr_accessor :before_render_callbacks, :on_page_create_callback
|
29
33
|
|
30
34
|
def populate_pages_from_store(document)
|
31
|
-
return 0 if @store.page_count <= 0 ||
|
35
|
+
return 0 if @store.page_count <= 0 || !@pages.empty?
|
32
36
|
|
33
37
|
count = (1..@store.page_count)
|
34
|
-
@pages =
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
38
|
+
@pages =
|
39
|
+
count.map do |index|
|
40
|
+
orig_dict_id = @store.object_id_for_page(index)
|
41
|
+
PDF::Core::Page.new(document, object_id: orig_dict_id)
|
42
|
+
end
|
39
43
|
end
|
40
44
|
|
41
45
|
def normalize_metadata(options)
|
42
46
|
options[:info] ||= {}
|
43
|
-
options[:info][:Creator] ||=
|
44
|
-
options[:info][:Producer] ||=
|
47
|
+
options[:info][:Creator] ||= 'Prawn'
|
48
|
+
options[:info][:Producer] ||= 'Prawn'
|
45
49
|
|
46
50
|
options[:info]
|
47
51
|
end
|
@@ -56,8 +60,8 @@ module PDF
|
|
56
60
|
on_page_create_callback[doc] if on_page_create_callback
|
57
61
|
end
|
58
62
|
|
59
|
-
def before_render_actions(
|
60
|
-
before_render_callbacks.each{ |c| c.call(self) }
|
63
|
+
def before_render_actions(_doc)
|
64
|
+
before_render_callbacks.each { |c| c.call(self) }
|
61
65
|
end
|
62
66
|
|
63
67
|
def page_count
|
@@ -67,8 +71,12 @@ module PDF
|
|
67
71
|
def render_body(output)
|
68
72
|
store.each do |ref|
|
69
73
|
ref.offset = output.size
|
70
|
-
output <<
|
71
|
-
|
74
|
+
output <<
|
75
|
+
if @encrypt
|
76
|
+
ref.encrypted_object(@encryption_key)
|
77
|
+
else
|
78
|
+
ref.object
|
79
|
+
end
|
72
80
|
end
|
73
81
|
end
|
74
82
|
end
|
data/lib/pdf/core/filter_list.rb
CHANGED
@@ -1,6 +1,20 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module PDF
|
2
4
|
module Core
|
3
5
|
class FilterList
|
6
|
+
class NotFilter < StandardError
|
7
|
+
DEFAULT_MESSAGE = 'Can not interpret input as a filter'
|
8
|
+
MESSAGE_WITH_FILTER = 'Can not interpret input as a filter: %<filter>s'
|
9
|
+
|
10
|
+
def initialize(message = DEFAULT_MESSAGE, filter: nil)
|
11
|
+
if filter
|
12
|
+
super format(MESSAGE_WITH_FILTER, filter: filter)
|
13
|
+
else
|
14
|
+
super(message)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
4
18
|
def initialize
|
5
19
|
@list = []
|
6
20
|
end
|
@@ -14,7 +28,7 @@ module PDF
|
|
14
28
|
@list << [name, params]
|
15
29
|
end
|
16
30
|
else
|
17
|
-
raise
|
31
|
+
raise NotFilter.new(filter: filter)
|
18
32
|
end
|
19
33
|
|
20
34
|
self
|
@@ -23,7 +37,7 @@ module PDF
|
|
23
37
|
def normalized
|
24
38
|
@list
|
25
39
|
end
|
26
|
-
|
40
|
+
alias to_a normalized
|
27
41
|
|
28
42
|
def names
|
29
43
|
@list.map do |(name, _)|
|
@@ -42,9 +56,7 @@ module PDF
|
|
42
56
|
end
|
43
57
|
|
44
58
|
def each(&block)
|
45
|
-
@list.each
|
46
|
-
block.call(filter)
|
47
|
-
end
|
59
|
+
@list.each(&block)
|
48
60
|
end
|
49
61
|
end
|
50
62
|
end
|
data/lib/pdf/core/filters.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# prawn/core/filters.rb : Implements stream filters
|
4
4
|
#
|
@@ -12,22 +12,22 @@ module PDF
|
|
12
12
|
module Core
|
13
13
|
module Filters
|
14
14
|
module FlateDecode
|
15
|
-
def self.encode(stream,
|
15
|
+
def self.encode(stream, _params = nil)
|
16
16
|
Zlib::Deflate.deflate(stream)
|
17
17
|
end
|
18
18
|
|
19
|
-
def self.decode(stream,
|
19
|
+
def self.decode(stream, _params = nil)
|
20
20
|
Zlib::Inflate.inflate(stream)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
24
|
# Pass through stub
|
25
25
|
module DCTDecode
|
26
|
-
def self.encode(stream,
|
26
|
+
def self.encode(stream, _params = nil)
|
27
27
|
stream
|
28
28
|
end
|
29
29
|
|
30
|
-
def self.decode(stream,
|
30
|
+
def self.decode(stream, _params = nil)
|
31
31
|
stream
|
32
32
|
end
|
33
33
|
end
|