Skip to content

Commit

Permalink
Fix #296
Browse files Browse the repository at this point in the history
  • Loading branch information
hhrutter committed Mar 16, 2021
1 parent 9fa39af commit 53385b4
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 43 deletions.
22 changes: 17 additions & 5 deletions cmd/pdfcpu/usage.go
Expand Up @@ -503,7 +503,7 @@ description ... dimensions, format, orientation
optional entries:
(defaults: "di:595 842, fo:A4, or:rd, bo:on, ma:3")
(defaults: "di:595 842, form:A4, or:rd, bo:on, ma:3")
dimensions: (width,height) in given display unit eg. '400 200'
formsize: The output sheet size, eg. A4, Letter, Legal...
Expand Down Expand Up @@ -536,7 +536,7 @@ Examples: pdfcpu nup out.pdf 4 in.pdf
pdfcpu nup out.pdf 9 logo.jpg
Arrange instances of logo.jpg into a 3x3 grid and write result to out.pdf using the A4 default format.
pdfcpu nup -- "f:Tabloid" out.pdf 4 *.jpg
pdfcpu nup -- "form:Tabloid" out.pdf 4 *.jpg
Rearrange all jpg files into 2x2 grids and write result to out.pdf using the Tabloid format
and the default orientation.
Expand All @@ -548,7 +548,7 @@ Examples: pdfcpu nup out.pdf 4 in.pdf
pages ... for inFile only, please refer to "pdfcpu selectedpages"
description ... dimensions, formsize, border, margin
outFile ... output pdf file
n ... the n-Up value: 2 or 4
n ... booklet style (2 or 4)
inFile ... input pdf file
imageFiles ... input image file(s)
Expand All @@ -557,6 +557,12 @@ There are two styles of booklet, depending on your page/input and sheet/output s
n=2: Two of your pages fit on one side of a sheet (eg statement on letter, A5 on A4)
Assemble by printing on both sides (odd pages on the front and even pages on the back) and folding down the middle.
A variant of n=2 is a technique to bind your own hardback book.
It works best when the source PDF holding your book content has at least 128 pages.
You bind your paper in eight sheet folios each making up 32 pages of your book.
Each sheet is going to make four pages of your book, gets printed on both sides and folded in half.
For such a multi folio booklet set 'multifolio:on' and play around with 'foliosize' which defaults to 8.
n=4: Four of your pages fit on one side of a sheet (eg statement on ledger, A5 on A3, A6 on A4)
Assemble by printing on both sides, then cutting the sheets horizontally.
The sets of pages on the bottom of the sheet are rotated so that the cut side of the
Expand All @@ -578,6 +584,8 @@ set of pages after the top set of pages in the booklet. Then fold the half sheet
Only one of dimensions or format is allowed.
Please refer to "pdfcpu paper" for a comprehensive list of defined paper sizes.
"papersize" is also accepted.
multifolio: Generate multi folio booklet (on/off, true/false, t/f) for n=2 and PDF input only.
foliosize: folio size for multi folio booklets only (default:8)
border: Print border (on/off, true/false, t/f)
guides: Print folding and cutting lines (on/off, true/false, t/f)
margin: Apply content margin (float >= 0 in given display unit)
Expand All @@ -594,6 +602,10 @@ Examples: pdfcpu booklet -- "formsize:Letter" out.pdf 2 in.pdf
pdfcpu booklet -- "formsize:A4" out.pdf 2 in.pdf
Arrange pages of in.pdf 2 per sheet side (4 per sheet, back and front) onto out.pdf
pdfcpu booklet -- "formsize:A4, multifolio:on" hardbackbook.pdf 2 in.pdf
Arrange pages of in.pdf 2 per sheetside as sequence of folios covering 4*foliosize pages each.
See also: https://www.instructables.com/How-to-bind-your-own-Hardback-Book/
`

usageGrid = "usage: pdfcpu grid [-p(ages) selectedPages] -- [description] outFile m n inFile|imageFiles..." + generalFlags
Expand All @@ -614,7 +626,7 @@ description ... dimensions, format, orientation
optional entries:
(defaults: "d:595 842, f:A4, o:rd, bo:on, m:3")
(defaults: "d:595 842, form:A4, o:rd, bo:on, ma:3")
dimensions: (width height) in given display unit eg. '400 200'
formsize: The output sheet size, eg. A4, Letter, Legal...
Expand All @@ -637,7 +649,7 @@ Examples: pdfcpu grid out.pdf 1 10 in.pdf
Rearrange pages of in.pdf into 1x10 grids and write result to out.pdf using the default orientation.
The output page size is the result of a 1(hor)x10(vert) page grid using in.pdf's page size.
pdfcpu grid -- "LegalL" out.pdf 2 2 in.pdf
pdfcpu grid -- "p:LegalL" out.pdf 2 2 in.pdf
Rearrange pages of in.pdf into 2x2 grids and write result to out.pdf using the default orientation.
The output page size is the result of a 2(hor)x2(vert) page grid using page size Legal in landscape mode.
Expand Down
4 changes: 0 additions & 4 deletions pkg/api/booklet.go
Expand Up @@ -62,7 +62,6 @@ func Booklet(rs io.ReadSeeker, w io.Writer, imgFiles, selectedPages []string, nu

log.Info.Printf("%s", nup)

// below is very similar to api.NUp
var (
ctx *pdfcpu.Context
err error
Expand Down Expand Up @@ -111,9 +110,6 @@ func Booklet(rs io.ReadSeeker, w io.Writer, imgFiles, selectedPages []string, nu

// BookletFile rearranges PDF pages or images into a booklet layout and writes the result to outFile.
func BookletFile(inFiles []string, outFile string, selectedPages []string, nup *pdfcpu.NUp, conf *pdfcpu.Configuration) (err error) {
//if nup.ImgInputFile {
// return fmt.Errorf("image file input not yet supported for booklet")
//}

var f1, f2 *os.File

Expand Down
15 changes: 14 additions & 1 deletion pkg/api/test/booklet_test.go
Expand Up @@ -46,7 +46,6 @@ func TestBooklet(t *testing.T) {
n int
isImg bool
}{

// 2-up booklet from images on A4
{"TestBookletFromImagesA42Up",
imageFileNames(t, resDir),
Expand Down Expand Up @@ -126,6 +125,20 @@ func TestBooklet(t *testing.T) {
2,
false,
},

// 2-up multi folio booklet from PDF on A4 using 8 sheets per folio
// using the default foliosize:8
// Here we print 2 complete folios (2 x 8 sheets) + 1 partial folio
// multi folio only makes sense for n = 2
// See also https://www.instructables.com/How-to-bind-your-own-Hardback-Book/
{"TestHardbackBookFromPDF",
[]string{filepath.Join(inDir, "WaldenFull.pdf")},
filepath.Join(outDir, "HardbackBookFromPDF.pdf"),
[]string{"1-70"},
"p:A4, multifolio:on, border:off, g:on, ma:10, bgcol:#beded9",
2,
false,
},
} {
testBooklet(t, tt.msg, tt.inFiles, tt.outFile, tt.selectedPages, tt.desc, tt.n, tt.isImg)
}
Expand Down
6 changes: 3 additions & 3 deletions pkg/api/test/grid_test.go
Expand Up @@ -63,17 +63,17 @@ func TestGrid(t *testing.T) {
{"TestGridFromPDF",
[]string{filepath.Join(inDir, "read.go.pdf")},
filepath.Join("..", "..", "samples", "grid", "GridFromPDF.pdf"),
nil, "f:LegalP, o:dr, border:off", 4, 6, false},
nil, "form:LegalP, o:dr, border:off", 4, 6, false},

{"TestGridFromPDFWithCropBox",
[]string{filepath.Join(inDir, "grid_example.pdf")},
filepath.Join("..", "..", "samples", "grid", "GridFromPDFWithCropBox.pdf"),
nil, "f:A5L, border:on, m:0", 2, 1, false},
nil, "form:A5L, border:on, margin:0", 2, 1, false},

{"TestGridFromImages",
imageFileNames(t, "../../../resources"),
filepath.Join("..", "..", "samples", "grid", "GridFromImages.pdf"),
nil, "d:500 500, m:20, bo:off", 1, 4, true},
nil, "d:500 500, margin:20, bo:off", 1, 4, true},
} {
testGrid(t, tt.msg, tt.inFiles, tt.outFile, tt.selectedPages, tt.desc, tt.rows, tt.cols, tt.isImg)
}
Expand Down
8 changes: 4 additions & 4 deletions pkg/api/test/nup_test.go
Expand Up @@ -67,7 +67,7 @@ func TestNUp(t *testing.T) {
[]string{filepath.Join(inDir, "WaldenFull.pdf")},
filepath.Join(outDir, "NUpFromPDF.pdf"),
nil,
"m:10, bgcol:#f7e6c7",
"margin:10, bgcol:#f7e6c7",
9,
false},

Expand All @@ -76,7 +76,7 @@ func TestNUp(t *testing.T) {
[]string{filepath.Join(inDir, "grid_example.pdf")},
filepath.Join(outDir, "NUpFromPDFWithCropBox.pdf"),
nil,
"f:A5L, border:on, m:10, bgcol:#f7e6c7",
"form:A5L, border:on, margin:10, bgcol:#f7e6c7",
2,
false},

Expand All @@ -85,7 +85,7 @@ func TestNUp(t *testing.T) {
[]string{filepath.Join("..", "..", "..", "resources", "logoSmall.png")},
filepath.Join(outDir, "NUpFromSingleImage.pdf"),
nil,
"f:A3P, m:10, bgcol:#f7e6c7",
"form:A3P, ma:10, bgcol:#f7e6c7",
16,
true},

Expand All @@ -94,7 +94,7 @@ func TestNUp(t *testing.T) {
imageFileNames(t, filepath.Join("..", "..", "..", "resources")),
filepath.Join(outDir, "NUpFromImages.pdf"),
nil,
"f:Tabloid, border:on, m:10, bgcol:#f7e6c7",
"form:Tabloid, border:on, ma:10, bgcol:#f7e6c7",
6,
true},
} {
Expand Down
34 changes: 24 additions & 10 deletions pkg/cli/test/booklet_test.go
@@ -1,17 +1,17 @@
/*
Copyright 2021 The pdfcpu Authors.
Copyright 2021 The pdfcpu Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package test
Expand Down Expand Up @@ -143,6 +143,20 @@ func TestBookletCommand(t *testing.T) {
2,
false,
},

// 2-up multi folio booklet from PDF on A4 using 8 sheets per folio
// using the default foliosize:8
// Here we print 2 complete folios (2 x 8 sheets) + 1 partial folio
// multi folio only makes sense for n = 2
// See also https://www.instructables.com/How-to-bind-your-own-Hardback-Book/
{"TestHardbackBookFromPDF",
[]string{filepath.Join(inDir, "WaldenFull.pdf")},
filepath.Join(outDir, "HardbackBookFromPDF.pdf"),
[]string{"1-70"},
"p:A4, multifolio:on, border:off, g:on, ma:10, bgcol:#beded9",
2,
false,
},
} {
testBooklet(t, tt.msg, tt.inFiles, tt.outFile, tt.selectedPages, tt.desc, tt.n, tt.isImg)
}
Expand Down
2 changes: 1 addition & 1 deletion pkg/cli/test/grid_test.go
Expand Up @@ -66,7 +66,7 @@ func TestGridCommand(t *testing.T) {
{"TestGridFromPDF",
[]string{filepath.Join(inDir, "Acroforms2.pdf")},
filepath.Join(outDir, "testGridFromPDF.pdf"),
nil, "f:LegalL", 1, 3, false},
nil, "form:LegalL", 1, 3, false},

{"TestGridFromImages",
[]string{
Expand Down
4 changes: 2 additions & 2 deletions pkg/cli/test/nup_test.go
Expand Up @@ -75,7 +75,7 @@ func TestNUpCommand(t *testing.T) {
[]string{filepath.Join(resDir, "pdfchip3.png")},
filepath.Join(outDir, "out.pdf"),
nil,
"f:A3L",
"form:A3L",
9,
true},

Expand All @@ -87,7 +87,7 @@ func TestNUpCommand(t *testing.T) {
},
filepath.Join(outDir, "out1.pdf"),
nil,
"f:Tabloid, bo:off, m:0",
"form:Tabloid, bo:off, ma:0",
6,
true},
} {
Expand Down
25 changes: 23 additions & 2 deletions pkg/pdfcpu/booklet.go
Expand Up @@ -31,6 +31,8 @@ func DefaultBookletConfig() *NUp {
nup.Margin = 0
nup.Border = false
nup.BookletGuides = false
nup.MultiFolio = false
nup.FolioSize = 8
return nup
}

Expand Down Expand Up @@ -330,8 +332,27 @@ func (ctx *Context) BookletFromPDF(selectedPages IntSet, nup *NUp) error {

nup.PageDim = &Dim{mb.Width(), mb.Height()}

if err = ctx.bookletPages(selectedPages, nup, pagesDict, pagesIndRef); err != nil {
return err
if nup.MultiFolio {
pages := IntSet{}
for _, i := range sortSelectedPages(selectedPages) {
pages[i] = true
if len(pages) == 4*nup.FolioSize {
if err = ctx.bookletPages(pages, nup, pagesDict, pagesIndRef); err != nil {
return err
}
pages = IntSet{}
}
}
if len(pages) > 0 {
if err = ctx.bookletPages(pages, nup, pagesDict, pagesIndRef); err != nil {
return err
}
}

} else {
if err = ctx.bookletPages(selectedPages, nup, pagesDict, pagesIndRef); err != nil {
return err
}
}

// Replace original pagesDict.
Expand Down
20 changes: 10 additions & 10 deletions pkg/pdfcpu/bookmarks.go
@@ -1,17 +1,17 @@
/*
Copyright 2020 The pdf Authors.
Copyright 2020 The pdfcpu Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package pdfcpu
Expand Down
29 changes: 28 additions & 1 deletion pkg/pdfcpu/nup.go
Expand Up @@ -62,6 +62,8 @@ var nupParamMap = nUpParamMap{
"backgroundcolor": parseSheetBackgroundColor,
"bgcolor": parseSheetBackgroundColor,
"guides": parseBookletGuides,
"multifolio": parseBookletMultifolio,
"foliosize": parseBookletFolioSize,
}

// Handle applies parameter completion and if successful
Expand Down Expand Up @@ -98,7 +100,9 @@ type NUp struct {
ImgInputFile bool // Process image or PDF input files.
Margin int // Cropbox for n-Up content.
Border bool // Draw bounding box.
BookletGuides bool // Draw folding and cutting lines
BookletGuides bool // Draw folding and cutting lines.
MultiFolio bool // Render booklet as sequence of folios.
FolioSize int // Booklet multifolio folio size: default: 8
InpUnit DisplayUnit // input display unit.
BgColor *SimpleColor // background color
}
Expand Down Expand Up @@ -214,6 +218,29 @@ func parseBookletGuides(s string, nup *NUp) error {
return nil
}

func parseBookletMultifolio(s string, nup *NUp) error {
switch strings.ToLower(s) {
case "on", "true", "t":
nup.MultiFolio = true
case "off", "false", "f":
nup.MultiFolio = false
default:
return errors.New("pdfcpu: booklet guides, please provide one of: on/off true/false t/f")
}

return nil
}

func parseBookletFolioSize(s string, nup *NUp) error {
i, err := strconv.Atoi(s)
if err != nil {
return errors.Errorf("pdfcpu: illegal folio size: must be an numeric value, %s\n", s)
}

nup.FolioSize = i
return nil
}

func parseElementMargin(s string, nup *NUp) error {
f, err := strconv.ParseFloat(s, 64)
if err != nil {
Expand Down
Binary file added pkg/samples/booklet/HardbackBookFromPDF.pdf
Binary file not shown.

0 comments on commit 53385b4

Please sign in to comment.