acro_that 0.1.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 +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +8 -0
- data/.rubocop.yml +78 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +86 -0
- data/README.md +360 -0
- data/Rakefile +18 -0
- data/acro_that.gemspec +34 -0
- data/docs/README.md +99 -0
- data/docs/dict_scan_explained.md +341 -0
- data/docs/object_streams.md +311 -0
- data/docs/pdf_structure.md +251 -0
- data/lib/acro_that/actions/add_field.rb +278 -0
- data/lib/acro_that/actions/add_signature_appearance.rb +422 -0
- data/lib/acro_that/actions/base.rb +44 -0
- data/lib/acro_that/actions/remove_field.rb +158 -0
- data/lib/acro_that/actions/update_field.rb +301 -0
- data/lib/acro_that/dict_scan.rb +413 -0
- data/lib/acro_that/document.rb +331 -0
- data/lib/acro_that/field.rb +143 -0
- data/lib/acro_that/incremental_writer.rb +244 -0
- data/lib/acro_that/object_resolver.rb +376 -0
- data/lib/acro_that/objstm.rb +75 -0
- data/lib/acro_that/pdf_writer.rb +97 -0
- data/lib/acro_that/version.rb +5 -0
- data/lib/acro_that.rb +24 -0
- metadata +143 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module AcroThat
|
|
4
|
+
# PDFWriter - Clean PDF writer for flattening documents
|
|
5
|
+
# Writes a complete PDF from parsed objects, consolidating incremental updates
|
|
6
|
+
class PDFWriter
|
|
7
|
+
def initialize
|
|
8
|
+
# Work entirely in binary encoding to avoid UTF-8/ASCII-8BIT conflicts
|
|
9
|
+
@buffer = "".b
|
|
10
|
+
@offsets = [] # Track [obj_num, gen, offset] for xref table
|
|
11
|
+
@xref_offset = 0
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def write_header
|
|
15
|
+
@buffer << "%PDF-1.6\n".b
|
|
16
|
+
# Binary marker (helps PDF readers identify binary content)
|
|
17
|
+
@buffer << "%\xE2\xE3\xCF\xD3\n".b
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def write_object(ref, body)
|
|
21
|
+
obj_num, gen = ref
|
|
22
|
+
offset = @buffer.bytesize
|
|
23
|
+
@offsets << [obj_num, gen, offset]
|
|
24
|
+
|
|
25
|
+
# Write object with proper PDF syntax
|
|
26
|
+
# Use ASCII-8BIT encoding throughout to avoid conflicts
|
|
27
|
+
@buffer << "#{obj_num} #{gen} obj\n".b
|
|
28
|
+
|
|
29
|
+
# Body is already in binary from ObjectResolver
|
|
30
|
+
@buffer << body.b
|
|
31
|
+
|
|
32
|
+
# Ensure proper spacing before endobj
|
|
33
|
+
@buffer << "\n".b unless body.end_with?("\n")
|
|
34
|
+
@buffer << "endobj\n".b
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def write_xref
|
|
38
|
+
@xref_offset = @buffer.bytesize
|
|
39
|
+
|
|
40
|
+
# Build xref table
|
|
41
|
+
xref = "xref\n".b
|
|
42
|
+
|
|
43
|
+
# Object 0 (free list head)
|
|
44
|
+
xref << "0 1\n".b
|
|
45
|
+
xref << "0000000000 65535 f \n".b
|
|
46
|
+
|
|
47
|
+
# Sort offsets and group consecutive objects into subsections
|
|
48
|
+
sorted = @offsets.sort_by { |num, gen, _offset| [num, gen] }
|
|
49
|
+
|
|
50
|
+
i = 0
|
|
51
|
+
while i < sorted.length
|
|
52
|
+
first_num = sorted[i][0]
|
|
53
|
+
|
|
54
|
+
# Find consecutive run
|
|
55
|
+
run_length = 1
|
|
56
|
+
while (i + run_length) < sorted.length &&
|
|
57
|
+
sorted[i + run_length][0] == first_num + run_length &&
|
|
58
|
+
sorted[i + run_length][1] == sorted[i][1]
|
|
59
|
+
run_length += 1
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Write subsection header
|
|
63
|
+
xref << "#{first_num} #{run_length}\n".b
|
|
64
|
+
|
|
65
|
+
# Write entries in this subsection
|
|
66
|
+
run_length.times do |j|
|
|
67
|
+
offset = sorted[i + j][2]
|
|
68
|
+
gen = sorted[i + j][1]
|
|
69
|
+
xref << format("%010d %05d n \n", offset, gen).b
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
i += run_length
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
@buffer << xref
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def write_trailer(size, root_ref, info_ref = nil)
|
|
79
|
+
trailer = "trailer\n".b
|
|
80
|
+
trailer << "<<".b
|
|
81
|
+
trailer << " /Size #{size}".b
|
|
82
|
+
trailer << " /Root #{root_ref[0]} #{root_ref[1]} R".b
|
|
83
|
+
trailer << " /Info #{info_ref[0]} #{info_ref[1]} R".b if info_ref
|
|
84
|
+
trailer << " >>".b
|
|
85
|
+
trailer << "\n".b
|
|
86
|
+
trailer << "startxref\n".b
|
|
87
|
+
trailer << "#{@xref_offset}\n".b
|
|
88
|
+
trailer << "%%EOF\n".b
|
|
89
|
+
|
|
90
|
+
@buffer << trailer
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def output
|
|
94
|
+
@buffer
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
data/lib/acro_that.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "strscan"
|
|
4
|
+
require "stringio"
|
|
5
|
+
require "zlib"
|
|
6
|
+
require "base64"
|
|
7
|
+
|
|
8
|
+
require_relative "acro_that/dict_scan"
|
|
9
|
+
require_relative "acro_that/object_resolver"
|
|
10
|
+
require_relative "acro_that/objstm"
|
|
11
|
+
require_relative "acro_that/pdf_writer"
|
|
12
|
+
require_relative "acro_that/incremental_writer"
|
|
13
|
+
require_relative "acro_that/field"
|
|
14
|
+
require_relative "acro_that/document"
|
|
15
|
+
|
|
16
|
+
# Load actions
|
|
17
|
+
require_relative "acro_that/actions/base"
|
|
18
|
+
require_relative "acro_that/actions/add_field"
|
|
19
|
+
require_relative "acro_that/actions/update_field"
|
|
20
|
+
require_relative "acro_that/actions/remove_field"
|
|
21
|
+
require_relative "acro_that/actions/add_signature_appearance"
|
|
22
|
+
|
|
23
|
+
module AcroThat
|
|
24
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: acro_that
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michael Wynkoop
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2025-11-01 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: chunky_png
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.4'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.4'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '3.0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '3.0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: pry
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - "~>"
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0.14'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - "~>"
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0.14'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rubocop
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - "~>"
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '1.50'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - "~>"
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '1.50'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: rubocop-rspec
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '2.20'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '2.20'
|
|
83
|
+
description: A minimal pure Ruby library for parsing and editing PDF AcroForm fields
|
|
84
|
+
using only stdlib
|
|
85
|
+
email:
|
|
86
|
+
- michaelwynkoop@corporatetools.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- ".DS_Store"
|
|
92
|
+
- ".gitignore"
|
|
93
|
+
- ".rubocop.yml"
|
|
94
|
+
- Gemfile
|
|
95
|
+
- Gemfile.lock
|
|
96
|
+
- README.md
|
|
97
|
+
- Rakefile
|
|
98
|
+
- acro_that.gemspec
|
|
99
|
+
- docs/README.md
|
|
100
|
+
- docs/dict_scan_explained.md
|
|
101
|
+
- docs/object_streams.md
|
|
102
|
+
- docs/pdf_structure.md
|
|
103
|
+
- lib/acro_that.rb
|
|
104
|
+
- lib/acro_that/actions/add_field.rb
|
|
105
|
+
- lib/acro_that/actions/add_signature_appearance.rb
|
|
106
|
+
- lib/acro_that/actions/base.rb
|
|
107
|
+
- lib/acro_that/actions/remove_field.rb
|
|
108
|
+
- lib/acro_that/actions/update_field.rb
|
|
109
|
+
- lib/acro_that/dict_scan.rb
|
|
110
|
+
- lib/acro_that/document.rb
|
|
111
|
+
- lib/acro_that/field.rb
|
|
112
|
+
- lib/acro_that/incremental_writer.rb
|
|
113
|
+
- lib/acro_that/object_resolver.rb
|
|
114
|
+
- lib/acro_that/objstm.rb
|
|
115
|
+
- lib/acro_that/pdf_writer.rb
|
|
116
|
+
- lib/acro_that/version.rb
|
|
117
|
+
homepage: https://github.com/corporatetools/acro_that
|
|
118
|
+
licenses:
|
|
119
|
+
- MIT
|
|
120
|
+
metadata:
|
|
121
|
+
homepage_uri: https://github.com/corporatetools/acro_that
|
|
122
|
+
source_code_uri: https://github.com/corporatetools/acro_that
|
|
123
|
+
changelog_uri: https://github.com/corporatetools/acro_that/blob/main/CHANGELOG.md
|
|
124
|
+
post_install_message:
|
|
125
|
+
rdoc_options: []
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: 3.1.0
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubygems_version: 3.4.12
|
|
140
|
+
signing_key:
|
|
141
|
+
specification_version: 4
|
|
142
|
+
summary: Pure Ruby PDF AcroForm editing library
|
|
143
|
+
test_files: []
|