pdf-impose 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +255 -0
- data/bin/impose +138 -0
- data/lib/pdf/impose/builder.rb +221 -0
- data/lib/pdf/impose/ext.rb +92 -0
- data/lib/pdf/impose/form.rb +149 -0
- data/lib/pdf/impose/forms/card_fold.rb +25 -0
- data/lib/pdf/impose/forms/duodecimo.rb +67 -0
- data/lib/pdf/impose/forms/folio.rb +14 -0
- data/lib/pdf/impose/forms/octavo.rb +26 -0
- data/lib/pdf/impose/forms/quarto.rb +22 -0
- data/lib/pdf/impose/forms/sexto.rb +26 -0
- data/lib/pdf/impose/forms/sextodecimo.rb +43 -0
- data/lib/pdf/impose/page.rb +22 -0
- data/lib/pdf/impose/signature.rb +21 -0
- data/lib/pdf/impose/version.rb +5 -0
- metadata +107 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d1c61ffc5f76f744484e97641eb3a42934e3517d
|
4
|
+
data.tar.gz: 30e272a0ec02dd406fa6fabad2e1ca7c3539c6fe
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 98ec292d86c84b64742c5b76502cde1d67136118b108ddeb5a27d201cab34f0b9dbd9adecf216a5e5e5073fd418ad951c573ebf63e17c0830fb48e5c5450dbc1
|
7
|
+
data.tar.gz: 73f462fafcaab365289ed4f3d7439addaf44cc70322a20535607e33270a169a2c9b2cd9da6c91f5c3212617b6edb192607ea207d29f6d7103d1edd9bbb6c5600
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2017 Jamis Buck
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,255 @@
|
|
1
|
+
# PDF::Impose
|
2
|
+
|
3
|
+
`PDF::Impose` is a utility and library for reformatting PDF files, in order to lay out multiple pages of the original document on a single page. The original pages are arranged in such a way that the new page may be folded and cut to produce a _signature_--a small booklet in which the pages are in the expected order. In this way, an existing PDF can be printed, folded, cut, and bound into a handmade book or booklet.
|
4
|
+
|
5
|
+
This process of laying out pages in this way is called [_imposition_](https://en.wikipedia.org/wiki/Imposition).
|
6
|
+
|
7
|
+
|
8
|
+
## Installation
|
9
|
+
|
10
|
+
`PDF::Impose` and its dependencies may be installed via RubyGems:
|
11
|
+
|
12
|
+
$ gem install pdf-impose
|
13
|
+
|
14
|
+
## Caveats
|
15
|
+
|
16
|
+
This has been tested on a variety of PDFs, and while it works great for most, there are some that it does not work correctly on. Pull requests would be welcome, to increase the number of PDFs that can correctly be imposed.
|
17
|
+
|
18
|
+
## Usage
|
19
|
+
|
20
|
+
The easiest way to use `PDF::Impose` is via the command-line tool:
|
21
|
+
|
22
|
+
$ impose -h
|
23
|
+
Usage: impose [options] <input.pdf>
|
24
|
+
-l, --layout LAYOUT The form to use when laying out pages for imposition.
|
25
|
+
Default is "quarto".
|
26
|
+
(Specify "list" to see all available forms.)
|
27
|
+
-o, --orient ORIENT How each sheet should be oriented.
|
28
|
+
Possible options are "portrait" or "landscape".
|
29
|
+
Default is "portrait".
|
30
|
+
-f, --forms COUNT The number of forms to use for each signature.
|
31
|
+
Default is dependent on the form used.
|
32
|
+
-d, --dimensions DIM Either the name of a paper size, or a WxH (width/height)
|
33
|
+
specification. Measurements must be in points.
|
34
|
+
Default is "LETTER".
|
35
|
+
(Specify "list" to see all named paper sizes.)
|
36
|
+
-m, --margin SIZE The minimum margin (in points) for the chosen form.
|
37
|
+
Default is 36 points.
|
38
|
+
-s, --start PAGE The page at which to start imposing.
|
39
|
+
Default is 1.
|
40
|
+
-e, --end PAGE The page at which to stop imposing.
|
41
|
+
Default is the last page of the source document.
|
42
|
+
-M, --[no-]marks Whether or not to include registration marks.
|
43
|
+
Default is to include registration marks.
|
44
|
+
-O, --output FILENAME The name of the file to which to write the resulting PDF.
|
45
|
+
Default is the original filename with "imposed" appended.
|
46
|
+
-h, --help This help screen.
|
47
|
+
|
48
|
+
To impose an existing PDF in quarto on A4 sheets, the following would suffice:
|
49
|
+
|
50
|
+
$ impose -l quarto -d A4 my-document.pdf
|
51
|
+
|
52
|
+
This would produce a new PDF called `my-document-imposed.pdf`.
|
53
|
+
|
54
|
+
|
55
|
+
## Default Layouts and Forms
|
56
|
+
|
57
|
+
The process of imposition takes a source document and lays out its pages in a particular form. The form used depends on how many pages you want to fit on a single sheet, and how many times you want to fold the paper to produce a signature.
|
58
|
+
|
59
|
+
`PDF::Impose` supports several common imposition forms, which should satisfy most needs. If you need a specific layout, though, it is not hard to define a custom imposition form. (See the "minibook" example in this repository.)
|
60
|
+
|
61
|
+
The following forms are supported by default.
|
62
|
+
|
63
|
+
|
64
|
+
### Four-page card-fold (`card-fold4`)
|
65
|
+
|
66
|
+
The four-page card fold takes a four-page document and lays out the pages on a single sheet in the following order:
|
67
|
+
|
68
|
+
<img src="layouts/card-fold4.png" width="200" />
|
69
|
+
|
70
|
+
When printed, the page can be folded in half twice to make a simple pamphlet of the original four pages. This form works best with the sheet in portrait orientation.
|
71
|
+
|
72
|
+
$ impose -o portrait -l card-fold4 document.pdf
|
73
|
+
# produces document-imposed.pdf
|
74
|
+
|
75
|
+
|
76
|
+
### Eight-page card-fold (`card-fold8`)
|
77
|
+
|
78
|
+
The eight-page card fold takes an eight-page document and lays out the pages on a single sheet in the following order:
|
79
|
+
|
80
|
+
<img src="layouts/card-fold8.png" width="400" />
|
81
|
+
|
82
|
+
When printed, the page can be folded in half three times to make a simple pamphlet of the original eight pages. This form works best with the sheet in landscape orientation.
|
83
|
+
|
84
|
+
$ impose -o landscape -l card-fold8 document.pdf
|
85
|
+
# produces document-imposed.pdf
|
86
|
+
|
87
|
+
|
88
|
+
### Folio (`folio`)
|
89
|
+
|
90
|
+
This form applies two pages to each side of a sheet of paper, in the following order:
|
91
|
+
|
92
|
+
Recto (front):
|
93
|
+
|
94
|
+
<img src="layouts/folio - recto.png" width="200" />
|
95
|
+
|
96
|
+
Verso (back):
|
97
|
+
|
98
|
+
<img src="layouts/folio - verso.png" width="200" />
|
99
|
+
|
100
|
+
When printed, the page can be folded in half to produce a simple pamphlet of four pages. For documents of more than four pages, multiple sheets can be folded and nested inside each other to form a signature. By default, up to eight forms (four sheets, one form for each side of the sheet) will be treated as a single signature. For documents of more than eight pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
101
|
+
|
102
|
+
This form works best with landscape orientation.
|
103
|
+
|
104
|
+
$ impose -o landscape -l folio document.pdf
|
105
|
+
# produces document-imposed.pdf
|
106
|
+
|
107
|
+
|
108
|
+
### Quarto (`quarto`)
|
109
|
+
|
110
|
+
This form applies four pages to each side of a sheet of paper, in the following order:
|
111
|
+
|
112
|
+
Recto (front):
|
113
|
+
|
114
|
+
<img src="layouts/quarto - recto.png" width="200" />
|
115
|
+
|
116
|
+
Verso (back):
|
117
|
+
|
118
|
+
<img src="layouts/quarto - verso.png" width="200" />
|
119
|
+
|
120
|
+
When printed, the page can be folded in half twice to produce a simple pamphlet of eight pages. For documents of more than eight pages, multiple sheets can be folded and nested inside each other to form a signature. By default, up to four forms (two sheets, one form for each side of the sheet) will be treated as a single signature. For documents of more than sixteen pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
121
|
+
|
122
|
+
This form works best with portrait orientation.
|
123
|
+
|
124
|
+
$ impose -o portrait -l quarto document.pdf
|
125
|
+
# produces document-imposed.pdf
|
126
|
+
|
127
|
+
|
128
|
+
### Sexto (`sexto`)
|
129
|
+
|
130
|
+
This form applies six pages to each side of a sheet of paper, in the following order:
|
131
|
+
|
132
|
+
Recto (front):
|
133
|
+
|
134
|
+
<img src="layouts/sexto - recto.png" width="200" />
|
135
|
+
|
136
|
+
Verso (back):
|
137
|
+
|
138
|
+
<img src="layouts/sexto - verso.png" width="200" />
|
139
|
+
|
140
|
+
When printed, the page can be folded three times to produce a simple pamphlet of twelve pages. For documents of more than twelve pages, multiple sheets can be folded and nested inside each other to form a signature. By default, up to four forms (two sheets, one form for each side of the sheet) will be treated as a single signature. For documents of more than twenty-four pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
141
|
+
|
142
|
+
This form is awkward to apply when dealing with standard letter-sized pages, but for non-standard page sizes it may work very well.
|
143
|
+
|
144
|
+
$ impose -l sexto document.pdf
|
145
|
+
# produces document-imposed.pdf
|
146
|
+
|
147
|
+
|
148
|
+
### Octavo (`octavo`)
|
149
|
+
|
150
|
+
This form applies eight pages to each side of a sheet of paper, in the following order:
|
151
|
+
|
152
|
+
Recto (front):
|
153
|
+
|
154
|
+
<img src="layouts/octavo - recto.png" width="400" />
|
155
|
+
|
156
|
+
Verso (back):
|
157
|
+
|
158
|
+
<img src="layouts/octavo - verso.png" width="400" />
|
159
|
+
|
160
|
+
When printed, the page can be folded three times to produce a simple pamphlet of sixteen pages. For documents of more than sixteen pages, multiple sheets can be folded and nested inside each other to form a signature. By default, two forms (one sheet, one form for each side of the sheet) will be treated as a single signature. For documents of more than sixteen pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
161
|
+
|
162
|
+
This form works well in landscape orientation.
|
163
|
+
|
164
|
+
$ impose -o landscape -l octavo document.pdf
|
165
|
+
# produces document-imposed.pdf
|
166
|
+
|
167
|
+
|
168
|
+
### Duodecimo (`duodecimo`)
|
169
|
+
|
170
|
+
This form applies twelve pages to each side of a sheet of paper, in the following order:
|
171
|
+
|
172
|
+
Recto (front):
|
173
|
+
|
174
|
+
<img src="layouts/duodecimo - recto.png" width="200" />
|
175
|
+
|
176
|
+
Verso (back):
|
177
|
+
|
178
|
+
<img src="layouts/duodecimo - verso.png" width="200" />
|
179
|
+
|
180
|
+
When printed, the bottommost strip can be separated and folded in quarto, while the remaining octavo is folded per octavo. The octavo is then nested inside the quarto to form the signature. For documents of more than twenty-four pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
181
|
+
|
182
|
+
This form is awkward to apply when dealing with standard letter-sized pages, but for non-standard page sizes it may work very well.
|
183
|
+
|
184
|
+
$ impose -l duodecimo document.pdf
|
185
|
+
# produces document-imposed.pdf
|
186
|
+
|
187
|
+
|
188
|
+
### Duodecimo (Quarto-inside) (`duodecimo-i`)
|
189
|
+
|
190
|
+
This form applies twelve pages to each side of a sheet of paper, in the following order:
|
191
|
+
|
192
|
+
Recto (front):
|
193
|
+
|
194
|
+
<img src="layouts/duodecimo-i - recto.png" width="200" />
|
195
|
+
|
196
|
+
Verso (back):
|
197
|
+
|
198
|
+
<img src="layouts/duodecimo-i - verso.png" width="200" />
|
199
|
+
|
200
|
+
When printed, the bottommost strip can be separated and folded in quarto, while the remaining octavo is folded per octavo. The quarto is then nested inside the octavo to form the signature. (Note that this is slightly different than how the `duodecimo` form works!) For documents of more than twenty-four pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
201
|
+
|
202
|
+
This form is awkward to apply when dealing with standard letter-sized pages, but for non-standard page sizes it may work very well.
|
203
|
+
|
204
|
+
$ impose -l duodecimo-i document.pdf
|
205
|
+
# produces document-imposed.pdf
|
206
|
+
|
207
|
+
|
208
|
+
### Duodecimo (Two-Cut) (`duodecimo-2c`)
|
209
|
+
|
210
|
+
This form applies twelve pages to each side of a sheet of paper, in the following order:
|
211
|
+
|
212
|
+
Recto (front):
|
213
|
+
|
214
|
+
<img src="layouts/duodecimo-2c - recto.png" width="200" />
|
215
|
+
|
216
|
+
Verso (back):
|
217
|
+
|
218
|
+
<img src="layouts/duodecimo-2c - verso.png" width="200" />
|
219
|
+
|
220
|
+
When printed, each strip of four can be separated and folded in quarto, with the quartos nested to form the signature. (Note that this is different than how the `duodecimo` form works!) For documents of more than twenty-four pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
221
|
+
|
222
|
+
This form is awkward to apply when dealing with standard letter-sized pages, but for non-standard page sizes it may work very well.
|
223
|
+
|
224
|
+
$ impose -l duodecimo-2c document.pdf
|
225
|
+
# produces document-imposed.pdf
|
226
|
+
|
227
|
+
|
228
|
+
### Sextodecimo (`sextodecimo`)
|
229
|
+
|
230
|
+
This form applies sixteen pages to each side of a sheet of paper, in the following order:
|
231
|
+
|
232
|
+
Recto (front):
|
233
|
+
|
234
|
+
<img src="layouts/sextodecimo - recto.png" width="200" />
|
235
|
+
|
236
|
+
Verso (back):
|
237
|
+
|
238
|
+
<img src="layouts/sextodecimo - verso.png" width="200" />
|
239
|
+
|
240
|
+
When printed, the sheet is cut in half to form two octavos which, when folded, are nested to form the signature. For documents of more than thirty-two pages, multiple signatures will be produced, though you can control how many forms to include in a signature with the `--forms` switch.
|
241
|
+
|
242
|
+
This form works best in portrait mode when used with standard paper sizes.
|
243
|
+
|
244
|
+
$ impose -o portrait -l sextodecimo document.pdf
|
245
|
+
# produces document-imposed.pdf
|
246
|
+
|
247
|
+
|
248
|
+
## Author
|
249
|
+
|
250
|
+
Jamis Buck <jamis@jamisbuck.org>
|
251
|
+
|
252
|
+
|
253
|
+
## License
|
254
|
+
|
255
|
+
This code is released and distributed under the terms of the MIT license. See the associated `MIT-LICENSE` file for details.
|
data/bin/impose
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/bin/sh ruby
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'pdf/impose/builder'
|
5
|
+
|
6
|
+
def parse_page_size(dim)
|
7
|
+
match = dim.match(/^(\d+)x(\d+)$/)
|
8
|
+
if match
|
9
|
+
[match[1].to_i, match[2].to_i]
|
10
|
+
else
|
11
|
+
dim
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def show_available_forms!
|
16
|
+
puts 'Available forms (with aliases, if any):'
|
17
|
+
PDF::Impose::Builder::PRIMARY_LAYOUTS.keys.each do |key|
|
18
|
+
aliases = if PDF::Impose::Builder::ALIASES[key].any?
|
19
|
+
' (' + PDF::Impose::Builder::ALIASES[key].join(', ') + ')'
|
20
|
+
else
|
21
|
+
''
|
22
|
+
end
|
23
|
+
|
24
|
+
puts " * #{key}#{aliases}"
|
25
|
+
end
|
26
|
+
|
27
|
+
exit(-1)
|
28
|
+
end
|
29
|
+
|
30
|
+
def show_available_paper_sizes!
|
31
|
+
puts 'Named paper sizes (with dimensions in points):'
|
32
|
+
PDF::Core::PageGeometry::SIZES.each do |name, (width, height)|
|
33
|
+
puts " * #{name} - #{width}x#{height}"
|
34
|
+
end
|
35
|
+
|
36
|
+
exit(-1)
|
37
|
+
end
|
38
|
+
|
39
|
+
options = {
|
40
|
+
layout: 'quarto',
|
41
|
+
orientation: :portrait,
|
42
|
+
forms_per_signature: nil,
|
43
|
+
page_size: 'LETTER',
|
44
|
+
margin: 36,
|
45
|
+
start_page: 1,
|
46
|
+
end_page: nil,
|
47
|
+
marks: true
|
48
|
+
}
|
49
|
+
|
50
|
+
output_filename = nil
|
51
|
+
|
52
|
+
OptionParser.new do |parser|
|
53
|
+
parser.banner = "Usage: #{File.basename($PROGRAM_NAME)} [options] <input.pdf>"
|
54
|
+
parser.summary_indent = " "
|
55
|
+
parser.summary_width = 22
|
56
|
+
|
57
|
+
parser.on('-l', '--layout LAYOUT',
|
58
|
+
'The form to use when laying out pages for imposition.',
|
59
|
+
"Default is \"#{options[:layout]}\".",
|
60
|
+
'(Specify "list" to see all available forms.)'
|
61
|
+
) do |layout|
|
62
|
+
show_available_forms! if layout == 'list'
|
63
|
+
options[:layout] = layout
|
64
|
+
end
|
65
|
+
|
66
|
+
parser.on('-o', '--orient ORIENT',
|
67
|
+
'How each sheet should be oriented.',
|
68
|
+
'Possible options are "portrait" or "landscape".',
|
69
|
+
"Default is \"#{options[:orientation]}\"."
|
70
|
+
) do |orientation|
|
71
|
+
options[:orientation] = orientation.to_sym
|
72
|
+
end
|
73
|
+
|
74
|
+
parser.on('-f', '--forms COUNT', Integer,
|
75
|
+
'The number of forms to use for each signature.',
|
76
|
+
'Default is dependent on the form used.'
|
77
|
+
) do |count|
|
78
|
+
options[:forms_per_signature] = count
|
79
|
+
end
|
80
|
+
|
81
|
+
parser.on('-d', '--dimensions DIM',
|
82
|
+
'Either the name of a paper size, or a WxH (width/height)',
|
83
|
+
'specification. Measurements must be in points.',
|
84
|
+
"Default is \"#{options[:page_size]}\".",
|
85
|
+
'(Specify "list" to see all named paper sizes.)'
|
86
|
+
) do |dim|
|
87
|
+
show_available_paper_sizes! if dim == 'list'
|
88
|
+
options[:page_size] = parse_page_size(dim)
|
89
|
+
end
|
90
|
+
|
91
|
+
parser.on('-m', '--margin SIZE', Integer,
|
92
|
+
'The minimum margin (in points) for the chosen form.',
|
93
|
+
"Default is #{options[:margin]} points."
|
94
|
+
) do |size|
|
95
|
+
options[:margin] = size
|
96
|
+
end
|
97
|
+
|
98
|
+
parser.on('-s', '--start PAGE', Integer,
|
99
|
+
'The page at which to start imposing.',
|
100
|
+
"Default is #{options[:start_page]}."
|
101
|
+
) do |page|
|
102
|
+
options[:start_page] = page
|
103
|
+
end
|
104
|
+
|
105
|
+
parser.on('-e', '--end PAGE', Integer,
|
106
|
+
'The page at which to stop imposing.',
|
107
|
+
"Default is the last page of the source document."
|
108
|
+
) do |page|
|
109
|
+
options[:end_page] = page
|
110
|
+
end
|
111
|
+
|
112
|
+
parser.on('-M', '--[no-]marks',
|
113
|
+
'Whether or not to include registration marks.',
|
114
|
+
'Default is to include registration marks.'
|
115
|
+
) do |marks|
|
116
|
+
options[:marks] = marks
|
117
|
+
end
|
118
|
+
|
119
|
+
parser.on('-O', '--output FILENAME',
|
120
|
+
'The name of the file to which to write the resulting PDF.',
|
121
|
+
'Default is the original filename with "imposed" appended.'
|
122
|
+
) do |filename|
|
123
|
+
output_filename = filename
|
124
|
+
end
|
125
|
+
|
126
|
+
parser.on('-h', '--help',
|
127
|
+
'This help screen.'
|
128
|
+
) do
|
129
|
+
puts parser.help
|
130
|
+
exit(-1)
|
131
|
+
end
|
132
|
+
end.parse!
|
133
|
+
|
134
|
+
filename = ARGV.first or abort 'please specify a PDF file to impose'
|
135
|
+
imposer = PDF::Impose::Builder.new(filename, options)
|
136
|
+
|
137
|
+
output_filename ||= File.basename(filename, '.pdf') + '-imposed.pdf'
|
138
|
+
imposer.emit output_filename
|
@@ -0,0 +1,221 @@
|
|
1
|
+
require 'pdf/reader'
|
2
|
+
require 'prawn'
|
3
|
+
require 'prawn/templates'
|
4
|
+
|
5
|
+
require 'pdf/impose/ext'
|
6
|
+
require 'pdf/impose/forms/card_fold'
|
7
|
+
require 'pdf/impose/forms/duodecimo'
|
8
|
+
require 'pdf/impose/forms/folio'
|
9
|
+
require 'pdf/impose/forms/quarto'
|
10
|
+
require 'pdf/impose/forms/octavo'
|
11
|
+
require 'pdf/impose/forms/sexto'
|
12
|
+
require 'pdf/impose/forms/sextodecimo'
|
13
|
+
require 'pdf/impose/signature'
|
14
|
+
|
15
|
+
module PDF
|
16
|
+
module Impose
|
17
|
+
class Builder
|
18
|
+
PRIMARY_LAYOUTS = {
|
19
|
+
'card-fold4' => PDF::Impose::Forms::CardFold::Quarto,
|
20
|
+
'card-fold8' => PDF::Impose::Forms::CardFold::Octavo,
|
21
|
+
'folio' => PDF::Impose::Forms::Folio,
|
22
|
+
'quarto' => PDF::Impose::Forms::Quarto,
|
23
|
+
'sexto' => PDF::Impose::Forms::Sexto,
|
24
|
+
'octavo' => PDF::Impose::Forms::Octavo,
|
25
|
+
'duodecimo' => PDF::Impose::Forms::Duodecimo::OneCutOutside,
|
26
|
+
'duodecimo-i' => PDF::Impose::Forms::Duodecimo::OneCutInside,
|
27
|
+
'duodecimo-2c' => PDF::Impose::Forms::Duodecimo::TwoCut,
|
28
|
+
'sextodecimo' => PDF::Impose::Forms::Sextodecimo::Nested
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
ALIASES = {
|
32
|
+
'card-fold4' => %w( card-fold ),
|
33
|
+
'card-fold8' => %w( ),
|
34
|
+
'folio' => %w( f fo ),
|
35
|
+
'quarto' => %w( 4to ),
|
36
|
+
'sexto' => %w( 6to 6mo ),
|
37
|
+
'octavo' => %w( 8vo octavo ),
|
38
|
+
'duodecimo' => %w( twelvemo 12mo ),
|
39
|
+
'duodecimo-i' => %w( twelvemo-i 12mo-i ),
|
40
|
+
'duodecimo-2c' => %w( twelvemo-2c 12mo-2c ),
|
41
|
+
'sextodecimo' => %w( sixteenmo 16mo )
|
42
|
+
}.freeze
|
43
|
+
|
44
|
+
LAYOUTS = ALIASES.keys.each_with_object({}) do |key, hash|
|
45
|
+
hash[key] = PRIMARY_LAYOUTS[key]
|
46
|
+
ALIASES[key].each { |name| hash[name] = hash[key] }
|
47
|
+
end.freeze
|
48
|
+
|
49
|
+
# source: the name of a PDF document to lay out
|
50
|
+
# options:
|
51
|
+
# layout: quarto, octavo, etc.
|
52
|
+
# page_size: passed through to Prawn
|
53
|
+
# orientation: passed through to Prawn as :page_layout
|
54
|
+
# forms_per_signature: defaults to layout.per_signature
|
55
|
+
# margin: point size of margin of page (default = 32 points)
|
56
|
+
# start_page: defaults to 1
|
57
|
+
# end_page: defaults to last page of source document
|
58
|
+
# marks: true or false, whether to include registration marks (default true)
|
59
|
+
def initialize(source, options={})
|
60
|
+
layout_arg = options[:layout]
|
61
|
+
|
62
|
+
@layout = if layout_arg.respond_to?(:recto)
|
63
|
+
layout_arg
|
64
|
+
else
|
65
|
+
LAYOUTS[options[:layout].to_s.downcase]
|
66
|
+
end
|
67
|
+
|
68
|
+
raise "`#{options[:layout]}' is not a supported layout" if @layout.nil?
|
69
|
+
|
70
|
+
@margin = options[:margin] || 36 # half inch
|
71
|
+
@marks = options.fetch(:marks, true)
|
72
|
+
|
73
|
+
@source = PDF::Reader.new(source)
|
74
|
+
@destination = Prawn::Document.new(
|
75
|
+
skip_page_creation: true, margin: 0,
|
76
|
+
page_size: options[:page_size], page_layout: options[:orientation])
|
77
|
+
|
78
|
+
@start_page = options[:start_page] || 1
|
79
|
+
@end_page = [options[:end_page] || 1e6, @source.page_count].min
|
80
|
+
|
81
|
+
@page_count = @end_page - @start_page + 1
|
82
|
+
|
83
|
+
@forms_per_signature = options[:forms_per_signature] ||
|
84
|
+
@layout.per_signature
|
85
|
+
@pages_per_signature = @forms_per_signature * @layout.pages_per_form
|
86
|
+
|
87
|
+
@signature_count = (@page_count + @pages_per_signature - 1) /
|
88
|
+
@pages_per_signature
|
89
|
+
|
90
|
+
@signatures = (1..@signature_count).map do |s|
|
91
|
+
first = (s - 1) * @pages_per_signature + @start_page
|
92
|
+
last = first + @pages_per_signature - 1
|
93
|
+
Signature.new(first, last)
|
94
|
+
end
|
95
|
+
|
96
|
+
_apply
|
97
|
+
end
|
98
|
+
|
99
|
+
def emit(filename)
|
100
|
+
@destination.render_file filename
|
101
|
+
end
|
102
|
+
|
103
|
+
def _apply
|
104
|
+
source_width = @source.page(1).page_object[:MediaBox][2]
|
105
|
+
source_height = @source.page(1).page_object[:MediaBox][3]
|
106
|
+
|
107
|
+
height = @destination.margin_box.height
|
108
|
+
|
109
|
+
sheet_width = @destination.margin_box.width - @margin * 2
|
110
|
+
sheet_height = height - @margin * 2
|
111
|
+
|
112
|
+
max_cell_width = sheet_width / @layout.columns_per_form
|
113
|
+
max_cell_height = sheet_height / @layout.rows_per_form
|
114
|
+
|
115
|
+
width_ratio = max_cell_width.to_f / source_width
|
116
|
+
height_ratio = max_cell_height.to_f / source_height
|
117
|
+
scale = [width_ratio, height_ratio].min
|
118
|
+
|
119
|
+
cell_width = source_width * scale
|
120
|
+
cell_height = source_height * scale
|
121
|
+
|
122
|
+
form_width = cell_width * @layout.columns_per_form
|
123
|
+
form_height = cell_height * @layout.rows_per_form
|
124
|
+
|
125
|
+
recto = true
|
126
|
+
@layout.layout_signatures(@signatures) do |form|
|
127
|
+
left = if recto
|
128
|
+
@destination.margin_box.width - @margin - form_width
|
129
|
+
else
|
130
|
+
@margin
|
131
|
+
end
|
132
|
+
|
133
|
+
@destination.start_new_page
|
134
|
+
|
135
|
+
_draw_guides(recto, form_width, form_height) if @marks
|
136
|
+
|
137
|
+
form.each_page do |page|
|
138
|
+
x = page.column * cell_width + left
|
139
|
+
y = height - (page.row + 1) * cell_height - @margin
|
140
|
+
@destination.page.import_page @source, page.number - 1,
|
141
|
+
x, y, cell_width, cell_height,
|
142
|
+
page.mirror?
|
143
|
+
end
|
144
|
+
|
145
|
+
recto = !recto
|
146
|
+
end
|
147
|
+
|
148
|
+
self
|
149
|
+
end
|
150
|
+
|
151
|
+
def _draw_guides(recto, width, height)
|
152
|
+
if recto
|
153
|
+
x2 = @destination.margin_box.width - @margin
|
154
|
+
x1 = x2 - width
|
155
|
+
else
|
156
|
+
x1 = @margin
|
157
|
+
x2 = x1 + width
|
158
|
+
end
|
159
|
+
|
160
|
+
y2 = @destination.margin_box.height - @margin
|
161
|
+
y1 = y2 - height
|
162
|
+
|
163
|
+
l1 = x1 - @margin / 2.0
|
164
|
+
l2 = l1 + @margin / 4.0
|
165
|
+
r1 = x2 + @margin / 4.0
|
166
|
+
r2 = x2 + @margin / 2.0
|
167
|
+
|
168
|
+
b1 = y1 - @margin / 2.0
|
169
|
+
b2 = y1 - @margin / 4.0
|
170
|
+
t1 = y2 + @margin / 4.0
|
171
|
+
t2 = y2 + @margin / 2.0
|
172
|
+
|
173
|
+
cx = recto ? (r1 + r2) / 2 : (l1 + l2) / 2
|
174
|
+
cy = (t1 + t2) / 2
|
175
|
+
radius = (r2 - r1) / 2
|
176
|
+
|
177
|
+
@destination.stroke do
|
178
|
+
@destination.line([l1, y1], [l2, y1])
|
179
|
+
@destination.line([l1, y2], [l2, y2])
|
180
|
+
@destination.line([r1, y1], [r2, y1])
|
181
|
+
@destination.line([r1, y2], [r2, y2])
|
182
|
+
|
183
|
+
@destination.line([x1, t1], [x1, t2])
|
184
|
+
@destination.line([x2, t1], [x2, t2])
|
185
|
+
@destination.line([x1, b1], [x1, b2])
|
186
|
+
@destination.line([x2, b1], [x2, b2])
|
187
|
+
|
188
|
+
@destination.circle([cx, cy], radius)
|
189
|
+
@destination.line([cx - 1.5 * radius, cy], [cx + 1.5 * radius, cy])
|
190
|
+
@destination.line([cx, cy - 1.5 * radius], [cx, cy + 1.5 * radius])
|
191
|
+
end
|
192
|
+
|
193
|
+
cut_rows = @layout.cut_row || []
|
194
|
+
if cut_rows.any?
|
195
|
+
row_height = height.to_f / @layout.rows_per_form
|
196
|
+
|
197
|
+
@destination.stroke do
|
198
|
+
cut_rows.each do |row|
|
199
|
+
y = y2 - row_height * row
|
200
|
+
@destination.line([l1, y], [l2, y])
|
201
|
+
@destination.line([r1, y], [r2, y])
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
cut_cols = @layout.cut_col || []
|
207
|
+
if cut_cols.any?
|
208
|
+
col_width = width.to_f / @layout.columns_per_form
|
209
|
+
|
210
|
+
@destination.stroke do
|
211
|
+
cut_cols.each do |col|
|
212
|
+
x = x1 + col_width * col
|
213
|
+
@destination.line([x, t1], [x, t2])
|
214
|
+
@destination.line([x, b1], [x, b2])
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module PDF
|
2
|
+
module Impose
|
3
|
+
module Ext
|
4
|
+
module Reference
|
5
|
+
def extract_content_stream
|
6
|
+
content = stream
|
7
|
+
content = content.filtered_stream if content.respond_to?(:filtered_stream)
|
8
|
+
|
9
|
+
if data[:Filter]
|
10
|
+
options = []
|
11
|
+
|
12
|
+
if data[:DecodeParams].is_a?(Hash)
|
13
|
+
options = [data[:DecodeParams]]
|
14
|
+
elsif data[:DecodeParams]
|
15
|
+
options = data[:DecodeParams]
|
16
|
+
end
|
17
|
+
|
18
|
+
Array(data[:Filter]).each_with_index do |filter, index|
|
19
|
+
content = PDF::Reader::Filter.
|
20
|
+
with(filter, options[index]).filter(content)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
content
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Page
|
29
|
+
def merge_object_resources(object)
|
30
|
+
object_resources = document.deref(object.data[:Resources])
|
31
|
+
|
32
|
+
object_resources.keys.each do |resource|
|
33
|
+
case object_resources[resource]
|
34
|
+
when ::Hash then
|
35
|
+
resources[resource] ||= {}
|
36
|
+
resources[resource].update(object_resources[resource])
|
37
|
+
when ::Array then
|
38
|
+
resources[resource] ||= []
|
39
|
+
resources[resource] |= object_resources[resource]
|
40
|
+
when PDF::Core::Reference then
|
41
|
+
resources[resource] = object_resources[resource]
|
42
|
+
else
|
43
|
+
klass = object_resources[resource].class
|
44
|
+
abort "unknown resource type #{klass} for #{resource}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def import_page(reader, page_number, dx, dy, width, height, mirror)
|
50
|
+
ref = reader.objects.page_references[page_number]
|
51
|
+
return unless ref
|
52
|
+
|
53
|
+
object = document.state.store.
|
54
|
+
send(:load_object_graph, reader.objects, ref)
|
55
|
+
|
56
|
+
merge_object_resources object
|
57
|
+
|
58
|
+
contents = object.data[:Contents]
|
59
|
+
contents = [contents] unless contents.is_a?(Array)
|
60
|
+
|
61
|
+
contents.each do |content|
|
62
|
+
content = content.extract_content_stream
|
63
|
+
box = object.data[:MediaBox]
|
64
|
+
|
65
|
+
scale = 1.0
|
66
|
+
width_ratio = width.to_f / box[2]
|
67
|
+
height_ratio = height.to_f / box[3]
|
68
|
+
|
69
|
+
scale = [width_ratio, height_ratio].min
|
70
|
+
|
71
|
+
document.save_graphics_state do
|
72
|
+
document.translate dx, dy
|
73
|
+
document.scale scale
|
74
|
+
|
75
|
+
if mirror
|
76
|
+
document.translate box[2], box[3]
|
77
|
+
document.rotate 180
|
78
|
+
end
|
79
|
+
|
80
|
+
document.add_content content
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
require 'pdf/core'
|
90
|
+
|
91
|
+
PDF::Core::Reference.include PDF::Impose::Ext::Reference
|
92
|
+
PDF::Core::Page.include PDF::Impose::Ext::Page
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require 'pdf/impose/page'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
# A Form represents a collection of pages that will be printed together on a
|
6
|
+
# single sheet of paper, in a particular (grid) layout.
|
7
|
+
#
|
8
|
+
# Form subclasses define the layout of individual pages via the `recto` and
|
9
|
+
# `verso` methods (`recto` == front, `verso` == back).
|
10
|
+
#
|
11
|
+
# class Quarto < Form
|
12
|
+
# per_signature 4
|
13
|
+
#
|
14
|
+
# recto %w(3* *3),
|
15
|
+
# %w(0. .0)
|
16
|
+
#
|
17
|
+
# verso %w(1* *1),
|
18
|
+
# %w(2. .2)
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Each row of the recto/verso configuration is a series of cells, representing
|
22
|
+
# individual pages. They take the following format:
|
23
|
+
#
|
24
|
+
# *n OR .n OR n* OR n.
|
25
|
+
#
|
26
|
+
# 'n' is which pair from the signature is to be set in this position. If the
|
27
|
+
# dot or asterisk comes before the number, then the first element of the pair
|
28
|
+
# is used (the lower page number). If the dot or asterisk comes after, then
|
29
|
+
# the last element of the pair is used (the higher page number).
|
30
|
+
#
|
31
|
+
# A '.' means the page is set without rotation. A '*' means the page is
|
32
|
+
# rotated 180 degrees to turn it upside down.
|
33
|
+
|
34
|
+
class Form
|
35
|
+
class <<self
|
36
|
+
attr_reader :rows_per_form, :columns_per_form
|
37
|
+
|
38
|
+
def per_signature(n = nil)
|
39
|
+
@per_signature = n || @per_signature
|
40
|
+
end
|
41
|
+
|
42
|
+
def pages_per_form
|
43
|
+
rows_per_form * columns_per_form
|
44
|
+
end
|
45
|
+
|
46
|
+
def cut_row(*rows)
|
47
|
+
@cut_row = rows.any? ? rows : @cut_row
|
48
|
+
end
|
49
|
+
|
50
|
+
def cut_col(*cols)
|
51
|
+
@cut_col = cols.any? ? cols : @cut_col
|
52
|
+
end
|
53
|
+
|
54
|
+
# method can be :pages or :signatures (the default), and determines what
|
55
|
+
# the numbers in the recto/verso layouts represent.
|
56
|
+
def layout(method = nil)
|
57
|
+
@layout = method || @layout
|
58
|
+
end
|
59
|
+
|
60
|
+
def recto(*rows)
|
61
|
+
if rows.any?
|
62
|
+
@recto = _parse_configuration(rows)
|
63
|
+
else
|
64
|
+
@recto
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def verso(*rows)
|
69
|
+
if rows.any?
|
70
|
+
@verso = _parse_configuration(rows)
|
71
|
+
else
|
72
|
+
@verso
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def layout_signatures(signatures)
|
77
|
+
l = layout || :signatures
|
78
|
+
signatures.each do |signature|
|
79
|
+
offset = 0
|
80
|
+
while offset < signature.pairs.length
|
81
|
+
[recto, verso].compact.each do |side|
|
82
|
+
pages = []
|
83
|
+
|
84
|
+
side.each do |element|
|
85
|
+
n = if l == :signatures
|
86
|
+
idx = offset + element.offset
|
87
|
+
signature.pairs[idx].send(element.which)
|
88
|
+
else
|
89
|
+
signature.pairs[offset].first + element.offset - 1
|
90
|
+
end
|
91
|
+
|
92
|
+
pages << PDF::Impose::Page.new(element.column, element.row,
|
93
|
+
n, element.flip)
|
94
|
+
end
|
95
|
+
|
96
|
+
yield self.new(pages)
|
97
|
+
end
|
98
|
+
|
99
|
+
offset += pages_per_form
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
self
|
104
|
+
end
|
105
|
+
|
106
|
+
class LayoutElement < Struct.new(:column, :row, :offset, :which, :flip)
|
107
|
+
end
|
108
|
+
|
109
|
+
def _parse_configuration(rows)
|
110
|
+
@rows_per_form = rows.length
|
111
|
+
@columns_per_form = rows[0].length
|
112
|
+
|
113
|
+
[].tap do |config|
|
114
|
+
rows.each.with_index do |columns, row|
|
115
|
+
columns.each.with_index do |cell, column|
|
116
|
+
if cell !~ /^\s*([*.])?(\d+)([*.])?\s*$/
|
117
|
+
raise "invalid form specification: #{cell.inspect}"
|
118
|
+
end
|
119
|
+
|
120
|
+
first = $1
|
121
|
+
ofs = $2.to_i
|
122
|
+
last = $3
|
123
|
+
|
124
|
+
if first.nil? && last.nil?
|
125
|
+
raise "invalid form specification: #{cell.inspect}"
|
126
|
+
end
|
127
|
+
|
128
|
+
flip = (first || last) == '*'
|
129
|
+
config << LayoutElement.new(column, row, ofs,
|
130
|
+
first ? :first : :last, flip)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
attr_reader :pages
|
138
|
+
|
139
|
+
def initialize(pages)
|
140
|
+
@pages = pages
|
141
|
+
end
|
142
|
+
|
143
|
+
def each_page
|
144
|
+
@pages.each { |page| yield page }
|
145
|
+
self
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
module CardFold
|
7
|
+
class Octavo < Form
|
8
|
+
per_signature 1
|
9
|
+
|
10
|
+
layout :page_numbers
|
11
|
+
|
12
|
+
recto %w(*3 *2 *1 *8),
|
13
|
+
%w(.4 .5 .6 .7)
|
14
|
+
end
|
15
|
+
|
16
|
+
class Quarto < Form
|
17
|
+
per_signature 1
|
18
|
+
|
19
|
+
recto %w(*0 0*),
|
20
|
+
%w(.1 1.)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
module Duodecimo
|
7
|
+
# cut into three 4to strips
|
8
|
+
class TwoCut < Form
|
9
|
+
per_signature 2
|
10
|
+
|
11
|
+
cut_row 1, 2
|
12
|
+
|
13
|
+
recto %w(.3 3. 0. .0),
|
14
|
+
%w(.7 7. 4. .4),
|
15
|
+
%w(.11 11. 8. .8)
|
16
|
+
|
17
|
+
verso %w(.1 1. 2. .2),
|
18
|
+
%w(.5 5. 6. .6),
|
19
|
+
%w(.9 9. 10. .10)
|
20
|
+
end
|
21
|
+
|
22
|
+
# one-cut, 4to on outside
|
23
|
+
class OneCutOutside < Form
|
24
|
+
per_signature 2
|
25
|
+
|
26
|
+
cut_row 2
|
27
|
+
|
28
|
+
recto %w(*4 4* 11* *11),
|
29
|
+
%w(.7 7. 8. .8),
|
30
|
+
%w(.3 3. 0. .0)
|
31
|
+
|
32
|
+
verso %w(*10 10* 5* *5),
|
33
|
+
%w(.9 9. 6. .6),
|
34
|
+
%w(.1 1. 2. .2)
|
35
|
+
end
|
36
|
+
|
37
|
+
# one-cut, 4to on inside
|
38
|
+
class OneCutInside < Form
|
39
|
+
per_signature 2
|
40
|
+
|
41
|
+
cut_row 2
|
42
|
+
|
43
|
+
recto %w(*0 0* 7* *7),
|
44
|
+
%w(.3 3. 4. .4),
|
45
|
+
%w(.11 11. 8. .8)
|
46
|
+
|
47
|
+
verso %w(*6 6* 1* *1),
|
48
|
+
%w(.5 5. 2. .2),
|
49
|
+
%w(.9 9. 10. .10)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# 0 | 1 . 24
|
57
|
+
# 1 | 2 . 23
|
58
|
+
# 2 | 3 . 22
|
59
|
+
# 3 | 4 . 21
|
60
|
+
# 4 | 5 . 20
|
61
|
+
# 5 | 6 . 19
|
62
|
+
# 6 | 7 . 18
|
63
|
+
# 7 | 8 . 17
|
64
|
+
# 8 | 9 . 16
|
65
|
+
# 9 | 10 . 15
|
66
|
+
# 10 | 11 . 14
|
67
|
+
# 11 | 12 . 13
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
class Octavo < Form
|
7
|
+
per_signature 2
|
8
|
+
|
9
|
+
recto %w(3* *3 *0 0*),
|
10
|
+
%w(4. .4 .7 7.)
|
11
|
+
|
12
|
+
verso %w(1* *1 *2 2*),
|
13
|
+
%w(6. .6 .5 5.)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# 0 | 1 . 16
|
20
|
+
# 1 | 2 . 15
|
21
|
+
# 2 | 3 . 14
|
22
|
+
# 3 | 4 . 13
|
23
|
+
# 4 | 5 . 12
|
24
|
+
# 5 | 6 . 11
|
25
|
+
# 6 | 7 . 10
|
26
|
+
# 7 | 8 . 9
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
class Quarto < Form
|
7
|
+
per_signature 4
|
8
|
+
|
9
|
+
recto %w(3* *3),
|
10
|
+
%w(0. .0)
|
11
|
+
|
12
|
+
verso %w(*2 2*),
|
13
|
+
%w(.1 1.)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# 0 | 1 . 8
|
20
|
+
# 1 | 2 . 7
|
21
|
+
# 2 | 3 . 6
|
22
|
+
# 3 | 4 . 5
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
class Sexto < Form
|
7
|
+
per_signature 4
|
8
|
+
|
9
|
+
recto %w(0. .0),
|
10
|
+
%w(3* *3),
|
11
|
+
%w(4. .4)
|
12
|
+
|
13
|
+
verso %w(.1 1.),
|
14
|
+
%w(*2 2*),
|
15
|
+
%w(.5 5.)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# 0 | 1 . 12
|
22
|
+
# 1 | 2 . 11
|
23
|
+
# 2 | 3 . 10
|
24
|
+
# 3 | 4 . 9
|
25
|
+
# 4 | 5 . 8
|
26
|
+
# 5 | 6 . 7
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'pdf/impose/form'
|
2
|
+
|
3
|
+
module PDF
|
4
|
+
module Impose
|
5
|
+
module Forms
|
6
|
+
module Sextodecimo
|
7
|
+
# two 8vo forms that nest one inside the other
|
8
|
+
class Nested < Form
|
9
|
+
per_signature 2
|
10
|
+
|
11
|
+
cut_row 2
|
12
|
+
|
13
|
+
recto %w(7* *7 *0 0*),
|
14
|
+
%w(4. .4 .3 3.),
|
15
|
+
%w(15* *15 *8 8*),
|
16
|
+
%w(12. .12 .11 11.)
|
17
|
+
|
18
|
+
verso %w(1* *1 *6 6*),
|
19
|
+
%w(2. .2 .5 5.),
|
20
|
+
%w(9* *9 *14 14*),
|
21
|
+
%w(10. .10 .13 13.)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# 0 | 1 . 32
|
29
|
+
# 1 | 2 . 31
|
30
|
+
# 2 | 3 . 30
|
31
|
+
# 3 | 4 . 29
|
32
|
+
# 4 | 5 . 28
|
33
|
+
# 5 | 6 . 27
|
34
|
+
# 6 | 7 . 26
|
35
|
+
# 7 | 8 . 25
|
36
|
+
# 8 | 9 . 24
|
37
|
+
# 9 | 10 . 23
|
38
|
+
# 10 | 11 . 22
|
39
|
+
# 11 | 12 . 21
|
40
|
+
# 12 | 13 . 20
|
41
|
+
# 13 | 14 . 19
|
42
|
+
# 14 | 15 . 18
|
43
|
+
# 15 | 16 . 17
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module PDF
|
2
|
+
module Impose
|
3
|
+
# A Page represents a single page from the source document. It
|
4
|
+
# indicates the page (by number), as well as where on the form (column/row)
|
5
|
+
# it should go, and whether it should be mirrored (inverted, by rotation)
|
6
|
+
# on the form.
|
7
|
+
class Page
|
8
|
+
attr_reader :column, :row
|
9
|
+
attr_reader :number
|
10
|
+
attr_reader :mirror
|
11
|
+
|
12
|
+
alias mirror? mirror
|
13
|
+
|
14
|
+
def initialize(column, row, number, mirror = false)
|
15
|
+
@column = column
|
16
|
+
@row = row
|
17
|
+
@number = number
|
18
|
+
@mirror = mirror
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module PDF
|
2
|
+
module Impose
|
3
|
+
# A signature is composed of one or more forms.
|
4
|
+
class Signature
|
5
|
+
attr_reader :first, :last, :pairs
|
6
|
+
|
7
|
+
def initialize(first, last)
|
8
|
+
@first = first
|
9
|
+
@last = last
|
10
|
+
|
11
|
+
@pairs = []
|
12
|
+
f, l = first, last
|
13
|
+
while f < l
|
14
|
+
@pairs << [f, l]
|
15
|
+
f += 1
|
16
|
+
l -= 1
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
metadata
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pdf-impose
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jamis Buck
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-02 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: prawn
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: prawn-templates
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: pdf-reader
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
55
|
+
description: |2
|
56
|
+
Arrange pages of existing PDF documents so they fit on a single page, and
|
57
|
+
so they can be folded and cut to produce signatures that may then be assembled
|
58
|
+
to form a bound book.
|
59
|
+
email:
|
60
|
+
- jamis@jamisbuck.org
|
61
|
+
executables:
|
62
|
+
- impose
|
63
|
+
extensions: []
|
64
|
+
extra_rdoc_files: []
|
65
|
+
files:
|
66
|
+
- MIT-LICENSE
|
67
|
+
- README.md
|
68
|
+
- bin/impose
|
69
|
+
- lib/pdf/impose/builder.rb
|
70
|
+
- lib/pdf/impose/ext.rb
|
71
|
+
- lib/pdf/impose/form.rb
|
72
|
+
- lib/pdf/impose/forms/card_fold.rb
|
73
|
+
- lib/pdf/impose/forms/duodecimo.rb
|
74
|
+
- lib/pdf/impose/forms/folio.rb
|
75
|
+
- lib/pdf/impose/forms/octavo.rb
|
76
|
+
- lib/pdf/impose/forms/quarto.rb
|
77
|
+
- lib/pdf/impose/forms/sexto.rb
|
78
|
+
- lib/pdf/impose/forms/sextodecimo.rb
|
79
|
+
- lib/pdf/impose/page.rb
|
80
|
+
- lib/pdf/impose/signature.rb
|
81
|
+
- lib/pdf/impose/version.rb
|
82
|
+
homepage: http://github.com/jamis/impose
|
83
|
+
licenses:
|
84
|
+
- MIT
|
85
|
+
metadata: {}
|
86
|
+
post_install_message:
|
87
|
+
rdoc_options: []
|
88
|
+
require_paths:
|
89
|
+
- lib
|
90
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
requirements: []
|
101
|
+
rubyforge_project:
|
102
|
+
rubygems_version: 2.5.1
|
103
|
+
signing_key:
|
104
|
+
specification_version: 4
|
105
|
+
summary: A utility and library for imposition -- arranging pages on a sheet of paper
|
106
|
+
for optimal printing
|
107
|
+
test_files: []
|