github.com/pdfcpu/pdfcpu@v0.11.1/pkg/pdfcpu/booklet_test.go (about) 1 /* 2 Copyright 2024 The pdfcpu Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 package pdfcpu 17 18 import ( 19 "fmt" 20 "strings" 21 "testing" 22 ) 23 24 type pageOrderResults struct { 25 id string 26 nup int 27 pageCount int 28 expectedPageOrder []int 29 papersize string 30 bookletType string 31 binding string 32 useSignatures bool 33 nPagesPerSignature int 34 } 35 36 var bookletTestCases = []pageOrderResults{ 37 { 38 id: "2up", 39 nup: 2, 40 pageCount: 16, 41 expectedPageOrder: []int{ 42 16, 1, 43 15, 2, 44 14, 3, 45 13, 4, 46 12, 5, 47 11, 6, 48 10, 7, 49 9, 8, 50 }, 51 papersize: "A6", 52 bookletType: "booklet", 53 binding: "long", 54 }, 55 { 56 id: "2up with trailing blank pages", 57 nup: 2, 58 pageCount: 10, 59 expectedPageOrder: []int{ 60 0, 1, 61 0, 2, 62 10, 3, 63 9, 4, 64 8, 5, 65 7, 6, 66 }, 67 papersize: "A6", 68 bookletType: "booklet", 69 binding: "long", 70 }, 71 // basic booklet sidefold test cases 72 { 73 id: "booklet portrait long edge", 74 nup: 4, 75 pageCount: 16, 76 expectedPageOrder: []int{ 77 16, 1, 3, 14, 78 2, 15, 13, 4, 79 12, 5, 7, 10, 80 6, 11, 9, 8, 81 }, 82 papersize: "A5", // portrait, long-edge binding 83 bookletType: "booklet", 84 binding: "long", 85 }, 86 { 87 id: "booklet landscape short edge", 88 nup: 4, 89 pageCount: 8, 90 expectedPageOrder: []int{ 91 8, 1, 3, 6, 92 4, 5, 7, 2, // this is ordered differently from the portrait layout (because of differences in how duplexing works) 93 }, 94 papersize: "A5L", // landscape, short-edge binding 95 bookletType: "booklet", 96 binding: "short", 97 }, 98 // basic booklet topfold test cases 99 { 100 id: "booklet topfold portrait", 101 nup: 4, 102 pageCount: 16, 103 expectedPageOrder: []int{ 104 16, 3, 1, 14, 105 4, 15, 13, 2, 106 12, 7, 5, 10, 107 8, 11, 9, 6, 108 }, 109 papersize: "A5", // portrait, short-edge binding 110 bookletType: "booklet", 111 binding: "short", 112 }, 113 { 114 id: "booklet topfold landscape", 115 nup: 4, 116 pageCount: 8, 117 expectedPageOrder: []int{ 118 8, 3, 1, 6, 119 2, 5, 7, 4, // this is 180degrees flipped from the portrait layout (because of differences in how duplexing works) 120 }, 121 papersize: "A5L", // landscape, long-edge binding 122 bookletType: "booklet", 123 binding: "long", 124 }, 125 // advanced booklet sidefold test cases 126 { 127 id: "advanced portrait long edge", 128 nup: 4, 129 pageCount: 8, 130 expectedPageOrder: []int{ 131 8, 1, 5, 4, 132 2, 7, 3, 6, 133 }, 134 papersize: "A5", // portrait, long-edge binding 135 bookletType: "bookletadvanced", 136 binding: "long", 137 }, 138 { 139 id: "advanced landscape short edge", 140 nup: 4, 141 pageCount: 8, 142 expectedPageOrder: []int{ 143 8, 1, 5, 4, 144 6, 3, 7, 2, // this is ordered differently from the portrait layout (because of differences in how duplexing works) 145 }, 146 papersize: "A5L", // landscape, short-edge binding 147 bookletType: "bookletadvanced", 148 binding: "short", 149 }, 150 // 6up test 151 { 152 id: "6up", 153 nup: 6, 154 pageCount: 12, 155 expectedPageOrder: []int{ 156 12, 1, 10, 3, 8, 5, 157 2, 11, 4, 9, 6, 7, 158 }, 159 papersize: "A6", // portrait, long-edge binding 160 bookletType: "booklet", 161 binding: "long", 162 }, 163 { 164 id: "6up multisheet", 165 nup: 6, 166 pageCount: 24, 167 expectedPageOrder: []int{ 168 24, 1, 22, 3, 20, 5, 169 2, 23, 4, 21, 6, 19, 170 18, 7, 16, 9, 14, 11, 171 8, 17, 10, 15, 12, 13, 172 }, 173 papersize: "A6", // portrait, long-edge binding 174 bookletType: "booklet", 175 binding: "long", 176 }, 177 // 8up test 178 { 179 id: "8up portrait long edge", 180 nup: 8, 181 pageCount: 32, 182 expectedPageOrder: []int{ 183 1, 30, 32, 3, 5, 26, 28, 7, 184 29, 2, 4, 31, 25, 6, 8, 27, 185 9, 22, 24, 11, 13, 18, 20, 15, 186 21, 10, 12, 23, 17, 14, 16, 19, 187 }, 188 papersize: "A6", 189 bookletType: "booklet", 190 binding: "long", 191 }, 192 { 193 id: "8up portrait short edge", 194 nup: 8, 195 pageCount: 16, 196 expectedPageOrder: []int{ 197 16, 1, 14, 3, 12, 5, 10, 7, 198 2, 15, 4, 13, 6, 11, 8, 9, 199 }, 200 papersize: "A6", 201 bookletType: "booklet", 202 binding: "short", 203 }, 204 { 205 id: "8up landscape short edge", 206 nup: 8, 207 pageCount: 16, 208 expectedPageOrder: []int{ 209 16, 1, 14, 3, 12, 5, 10, 7, 210 2, 15, 4, 13, 6, 11, 8, 9, 211 }, 212 papersize: "A6L", 213 bookletType: "booklet", 214 binding: "short", 215 }, 216 { 217 id: "8up landscape long edge", 218 nup: 8, 219 pageCount: 16, 220 expectedPageOrder: []int{ 221 1, 14, 16, 3, 5, 10, 12, 7, 222 13, 2, 4, 15, 9, 6, 8, 11, 223 }, 224 papersize: "A6L", 225 bookletType: "booklet", 226 binding: "long", 227 }, 228 // perfect bound 229 { 230 id: "perfect bound 2up", 231 nup: 2, 232 pageCount: 8, 233 expectedPageOrder: []int{ 234 1, 3, 235 2, 4, 236 5, 7, 237 6, 8, 238 }, 239 papersize: "A6", // portrait, long-edge binding 240 bookletType: "perfectbound", 241 binding: "long", 242 }, 243 { 244 id: "perfect bound 4up", 245 nup: 4, 246 pageCount: 16, 247 expectedPageOrder: []int{ 248 1, 3, 5, 7, 249 4, 2, 8, 6, 250 9, 11, 13, 15, 251 12, 10, 16, 14, 252 }, 253 papersize: "A6", // portrait, long-edge binding 254 bookletType: "perfectbound", 255 binding: "long", 256 }, 257 { 258 id: "perfect bound 4up landscape short-edge", 259 nup: 4, 260 pageCount: 16, 261 expectedPageOrder: []int{ 262 1, 3, 5, 7, 263 6, 8, 2, 4, 264 9, 11, 13, 15, 265 14, 16, 10, 12, 266 }, 267 papersize: "A6L", // landscape, short-edge binding 268 bookletType: "perfectbound", 269 binding: "short", 270 }, 271 { 272 id: "perfect bound 8up", 273 nup: 8, 274 pageCount: 16, 275 expectedPageOrder: []int{ 276 1, 3, 5, 7, 9, 11, 13, 15, 277 4, 2, 8, 6, 12, 10, 16, 14, 278 }, 279 papersize: "A6", // portrait, long-edge binding 280 bookletType: "perfectbound", 281 binding: "long", 282 }, 283 // signatures 284 { 285 id: "signatures 2up", 286 nup: 2, 287 pageCount: 16, 288 expectedPageOrder: []int{ 289 12, 1, // signature 1 290 11, 2, 291 10, 3, 292 9, 4, 293 8, 5, 294 7, 6, 295 16, 13, // signature 2, incomplete 296 15, 14, 297 }, 298 papersize: "A6", 299 bookletType: "booklet", 300 binding: "long", 301 useSignatures: true, 302 nPagesPerSignature: 12, 303 }, 304 { 305 id: "signatures 4up", 306 nup: 4, 307 pageCount: 24, 308 expectedPageOrder: []int{ 309 16, 1, 3, 14, // signature 1 310 2, 15, 13, 4, 311 12, 5, 7, 10, 312 6, 11, 9, 8, 313 24, 17, 19, 22, // signature 2, incomplete 314 18, 23, 21, 20, 315 }, 316 papersize: "A5", 317 bookletType: "booklet", 318 binding: "long", 319 useSignatures: true, 320 nPagesPerSignature: 16, 321 }, 322 { 323 id: "signatures 2up with trailing blank pages", 324 nup: 2, 325 pageCount: 18, 326 expectedPageOrder: []int{ 327 12, 1, // signature 1 328 11, 2, 329 10, 3, 330 9, 4, 331 8, 5, 332 7, 6, 333 0, 13, // signature 2, incomplete, with blanks 334 0, 14, 335 18, 15, 336 17, 16, 337 }, 338 papersize: "A6", 339 bookletType: "booklet", 340 binding: "long", 341 useSignatures: true, 342 nPagesPerSignature: 12, 343 }, 344 } 345 346 func TestBookletPageOrder(t *testing.T) { 347 for _, test := range bookletTestCases { 348 t.Run(test.id, func(tt *testing.T) { 349 desc := fmt.Sprintf("papersize:%s, btype:%s, binding: %s", test.papersize, test.bookletType, test.binding) 350 if test.useSignatures { 351 desc += fmt.Sprintf(", multifolio:on, foliosize:%d", test.nPagesPerSignature/4) 352 } 353 nup, err := PDFBookletConfig(test.nup, desc, nil) 354 if err != nil { 355 tt.Fatal(err) 356 } 357 pageNumbers := make(map[int]bool) 358 for i := 0; i < test.pageCount; i++ { 359 pageNumbers[i+1] = true 360 } 361 pageOrder := make([]int, len(test.expectedPageOrder)) 362 out := GetBookletOrdering(pageNumbers, nup) 363 if len(test.expectedPageOrder) != len(out) { 364 tt.Fatalf("page order output has the wrong length, expected %d but got %d", len(test.expectedPageOrder), len(out)) 365 } 366 for i, p := range out { 367 pageOrder[i] = p.Number 368 } 369 for i, expected := range test.expectedPageOrder { 370 if pageOrder[i] != expected { 371 tt.Fatal("incorrect page order\nexpected:", arrayToString(test.expectedPageOrder), "\n got:", arrayToString(pageOrder)) 372 } 373 } 374 }) 375 } 376 } 377 378 func arrayToString(arr []int) string { 379 out := make([]string, len(arr)) 380 for i, n := range arr { 381 out[i] = fmt.Sprintf("%02d", n) 382 } 383 return fmt.Sprintf("[%s]", strings.Join(out, " ")) 384 }