diff --git a/index.html b/index.html index b059e65..47153f6 100644 --- a/index.html +++ b/index.html @@ -279,8 +279,8 @@

Wacky Small Layouts

- - + +
diff --git a/src/book.js b/src/book.js index 3c9fc41..18bd3f8 100644 --- a/src/book.js +++ b/src/book.js @@ -114,35 +114,32 @@ export class Book { } /** - * Populates [this.orderedpages] (array [0, 1, ... this.page_sheets * # of sheets]) + * Populates `this.orderedpages` array with page numbers and 'b' placeholders to represent flyleafs. + * Flyleafs are added to the beginning and end of the ordered pages array. + * + * @returns {void} */ createpagelist() { this.pagecount = this.currentdoc.getPageCount(); - this.orderedpages = Array.from({ length: this.pagecount }, (x, i) => i); - - for (let i = 0; i < this.flyleafs; i++) { - this.orderedpages.unshift('b'); - this.orderedpages.unshift('b'); - - this.orderedpages.push('b'); - this.orderedpages.push('b'); - } - - // padding calculations if needed - let pagetotal = this.orderedpages.length; - - // calculate how many sheets of paper the output document needs + this.orderedpages = Array.from({ length: this.pagecount }, (_, i) => i); + + // Add flyleafs to the beginning and end of the ordered pages array + const flyleafPages = Array.from({ length: this.flyleafs * 2 }, () => 'b'); + this.orderedpages = [...flyleafPages, ...this.orderedpages, ...flyleafPages]; + + // Calculate total number of pages after adding flyleafs + const pagetotal = this.orderedpages.length; + + // Calculate the number of sheets needed for the output document let sheets = Math.floor(pagetotal / this.per_sheet); - - // pad out end of document if necessary - if (pagetotal % this.per_sheet > 0) { - sheets += 1; - let padding = (sheets * this.per_sheet) - pagetotal; - for (let i = 0; i < padding; i++) { - this.orderedpages.push('b'); - } + + // Pad out the end of the document if necessary + const padding = sheets * this.per_sheet - pagetotal; + if (padding > 0) { + this.orderedpages.push(...Array(padding).fill('b')); } - console.log("Calculated pagecount [",this.pagecount,"] and ordered pages: ", this.orderedpages); + + console.log("Calculated pagecount [", this.pagecount, "] and ordered pages: ", this.orderedpages); } /** @@ -154,40 +151,50 @@ export class Book { let pages; [this.managedDoc, pages] = await this.embedPagesInNewPdf(this.currentdoc); - for (var i = 0; i < pages.length; ++i) { - var page = pages[i]; - var newPage = this.managedDoc.addPage(); - var rotate90cw = this.source_rotation == '90cw' - || (this.source_rotation == 'out_binding' && i % 2 == 0) - || (this.source_rotation == 'in_binding' && i % 2 == 1); - var rotate90ccw = this.source_rotation == '90ccw' - || (this.source_rotation == 'out_binding' && i % 2 == 1) - || (this.source_rotation == 'in_binding' && i % 2 == 0); - if (this.source_rotation == 'none') { - newPage.setSize(page.width, page.height); - newPage.drawPage(page); - } else if (rotate90ccw) { + pages.forEach((page, i) => { + const newPage = this.managedDoc.addPage(); + let rotateAngle = -1; + let evenPage = i % 2 === 0; + + // Determine rotation angle + switch (this.source_rotation) { + case 'none': + rotateAngle = 0; + break; + case '90cw': + rotateAngle = -90; + break; + case '90ccw': + rotateAngle = 90; + break; + case 'out_binding': + rotateAngle = evenPage ? -90 : 90; + break; + case 'in_binding': + rotateAngle = evenPage ? 90 : -90; + break; + default: + const e = new Error("Invalid source rotation"); + console.error(e); + throw e; + } + + // Apply rotation to the new page + if (rotateAngle !== 0) { newPage.setSize(page.height, page.width); newPage.drawPage(page, { - x: page.height, - y: 0, - rotate: degrees(90), - }); - } else if (rotate90cw) { - newPage.setSize(page.height, page.width); - newPage.drawPage(page, { - x: 0, - y: page.width, - rotate: degrees(-90), + x: (rotateAngle === 90 ? page.height : 0), + y: (rotateAngle === -90 ? page.width : 0), + rotate: degrees(rotateAngle), }); } else { - var e = new Error("??? what sorta' layout you think you're going to get?"); - console.error(e); - throw e; + newPage.setSize(page.width, page.height); + newPage.drawPage(page); } + page.embed(); this.cropbox = newPage.getCropBox(); - } + }); console.log("The updatedDoc doc has : ", this.managedDoc.getPages(), " vs --- ", this.managedDoc.getPageCount()); @@ -213,7 +220,7 @@ export class Book { break; case 'a9_3_3_4': case 'a10_6_10s': - case 'A7_2_16s': + case 'a7_2_16s': case '1_3rd': case '8_zine': case 'a_3_6s': @@ -237,81 +244,56 @@ export class Book { } /** - * Calls the appropriate builder based on [this.format] - * to generate PDF & populate Previewer - * @param isPreview - if it's true we only generate preview content, if it's not true... we still - * generate preview content AND a downloadable zip + * Calls the appropriate builder based on `this.format` to generate PDF & populate Previewer. + * @param {boolean} isPreview - If true, generates only preview content; otherwise, generates both preview content and a downloadable zip. + * @returns {Promise} - A promise resolving to 1 if it's a preview or a file blob if it's not. */ async createoutputfiles(isPreview) { - let previewFrame = document.getElementById('pdf'); + const previewFrame = document.getElementById('pdf'); previewFrame.style.display = 'none'; let resultPDF = null; - // create a directory named after the input pdf and fill it with - // the signatures this.zip = new JSZip(); - var origFileName = this.inputpdf.replace(/\s|,|\.pdf/, ''); + const origFileName = this.inputpdf.replace(/\s|,|\.pdf/, ''); this.filename = origFileName; - if (this.format == 'perfect' || this.format == 'booklet' || this.format == 'standardsig' || this.format == 'customsig') { - const generateAggregate = this.print_file != "signatures"; - const generateSignatures = this.print_file != "aggregated"; - const filename = (this.format == 'standardsig' || this.format == 'customsig') ? 'signatures' : this.format; - const side1PageNumbers = new Set(this.rearrangedpages.reduce((accumulator, currentValue) => { return accumulator.concat(currentValue[0]); },[])); + if (['perfect', 'booklet', 'standardsig', 'customsig'].includes(this.format)) { + const generateAggregate = this.print_file !== "signatures"; + const generateSignatures = this.print_file !== "aggregated"; + const side1PageNumbers = new Set(this.rearrangedpages.reduce((accumulator, currentValue) => accumulator.concat(currentValue[0]), [])); const [pdf0PageNumbers, pdf1PageNumbers] = (!generateAggregate || this.duplex) ? [null, null] - : [ - Array.from(Array(this.managedDoc.getPageCount()).keys()).map( p => { return (side1PageNumbers.has(p) ? p : 'b');}), - Array.from(Array(this.managedDoc.getPageCount()).keys()).map( p => { return (!side1PageNumbers.has(p) ? p : 'b');}) - ]; - const [aggregatePdf0, embeddedPages0] = (generateAggregate) ? await this.embedPagesInNewPdf(this.managedDoc, pdf0PageNumbers) : [null, null]; + : [ + Array.from(Array(this.managedDoc.getPageCount()).keys()).map(p => side1PageNumbers.has(p) ? p : 'b'), + Array.from(Array(this.managedDoc.getPageCount()).keys()).map(p => !side1PageNumbers.has(p) ? p : 'b') + ]; + const [aggregatePdf0, embeddedPages0] = generateAggregate ? await this.embedPagesInNewPdf(this.managedDoc, pdf0PageNumbers) : [null, null]; const [aggregatePdf1, embeddedPages1] = (generateAggregate && !this.duplex) ? await this.embedPagesInNewPdf(this.managedDoc, pdf1PageNumbers) : [null, null]; - const forLoop = async _ => { - for (let i = 0; i < this.rearrangedpages.length; i++) { - let signature = this.rearrangedpages[i]; - await this.createsignatures({ - embeddedPages: (generateAggregate) ? [embeddedPages0, embeddedPages1] : null, - aggregatePdfs: (generateAggregate) ? [aggregatePdf0, aggregatePdf1] : null, - pageIndexDetails: signature, - id: (generateSignatures) ? `signature${i}` : null, - isDuplex: this.duplex, - fileList: this.filelist - }); - } - }; - await forLoop(); - - if (aggregatePdf1 != null) { - await aggregatePdf1.save().then(pdfBytes => { - if (!isPreview) - this.zip.file('aggregate_side2.pdf', pdfBytes); - }); - } - if (aggregatePdf0 != null) { - await aggregatePdf0.save().then(pdfBytes => { - if (!isPreview) - this.zip.file((this.duplex) ? 'aggregate_book.pdf' : 'aggregate_side1.pdf', pdfBytes); + + for (let i = 0; i < this.rearrangedpages.length; i++) { + const signature = this.rearrangedpages[i]; + await this.createsignatures({ + embeddedPages: generateAggregate ? [embeddedPages0, embeddedPages1] : null, + aggregatePdfs: generateAggregate ? [aggregatePdf0, aggregatePdf1] : null, + pageIndexDetails: signature, + id: generateSignatures ? `signature${i}` : null, + isDuplex: this.duplex, + fileList: this.filelist }); } - var rotationMetaInfo = ((this.paper_rotation_90) ? "_paperRotated" : "") - + ((this.source_rotation == 'none') ? "" : `_${this.source_rotation}`); + + if (aggregatePdf1 != null && !isPreview) + this.zip.file('aggregate_side2.pdf', await aggregatePdf1.save()); + if (aggregatePdf0 != null && !isPreview) + this.zip.file(this.duplex ? 'aggregate_book.pdf' : 'aggregate_side1.pdf', await aggregatePdf0.save()); + + const rotationMetaInfo = (this.paper_rotation_90 ? "_paperRotated" : "") + (this.source_rotation === 'none' ? "" : `_${this.source_rotation}`); this.filename = `${origFileName}${rotationMetaInfo}`; resultPDF = aggregatePdf0; - } else if (this.format == 'a9_3_3_4') { - resultPDF = await this.buildSheets(this.filename, this.book.a9_3_3_4_builder()); - } else if (this.format == 'a10_6_10s') { - resultPDF = await this.buildSheets(this.filename, this.book.a10_6_10s_builder()); - } else if (this.format == 'a_4_8s') { - resultPDF = await this.buildSheets(this.filename, this.book.a_4_8s_builder()); - } else if (this.format == 'a_3_6s') { - resultPDF = await this.buildSheets(this.filename, this.book.a_3_6s_builder()); - } else if (this.format == 'A7_2_16s') { - resultPDF = await this.buildSheets(this.filename, this.book.a7_2_16s_builder()); - } else if (this.format == '1_3rd') { - resultPDF = await this.buildSheets(this.filename, this.book.page_1_3rd_builder()); - } else if (this.format == '8_zine') { - resultPDF = await this.buildSheets(this.filename, this.book.page_8_zine_builder()); + } else if (['a9_3_3_4', 'a10_6_10s', 'a_4_8s', 'a_3_6s', 'a7_2_16s', '1_3rd', '8_zine'].includes(this.format)) { + resultPDF = await this.buildSheets(this.filename, this.book[`${this.format}_builder`]()); } - console.log("Attempting to generate preview for ",resultPDF); + + console.log("Attempting to generate preview for ", resultPDF); if (resultPDF != null) { const pdfDataUri = await resultPDF.saveAsBase64({ dataUri: true }); @@ -324,23 +306,13 @@ export class Book { viewerPrefs.setDisplayDocTitle(true); previewFrame.style.width = `450px`; - let height = this.papersize[1] / this.papersize[0] * 500; + const height = this.papersize[1] / this.papersize[0] * 500; previewFrame.style.height = `${height}px`; previewFrame.style.display = ''; previewFrame.src = pdfDataUri; } - if (!isPreview) - return this.saveZip(); - else - return Promise.resolve(1); - } - - /** - * @return the aggregate file w/ all the original pages embedded, but nothing placed - */ - async create_base_aggregate_files() { - return aggregatePdf; + return isPreview ? Promise.resolve(1) : this.saveZip(); } /** @@ -515,117 +487,99 @@ export class Book { */ draw_spine_marks(curPage, sigDetails, position) { let w = 5; + let startX, startY, endX, endY; + + if (sigDetails.isSigStart) { + startX = position.spineMarkTop[0]; + startY = position.spineMarkTop[1]; + endX = position.spineMarkTop[0]; + endY = position.spineMarkTop[1]; + } else { + startX = position.spineMarkBottom[0]; + startY = position.spineMarkBottom[1]; + endX = position.spineMarkBottom[0]; + endY = position.spineMarkBottom[1]; + } + if (position.rotation == 0) { - console.log(" --> draw this: ",{ - start: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] - w/2: position.spineMarkBottom[0] - w/2, - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] : position.spineMarkBottom[1] ) - }, - end: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] + w/2: position.spineMarkBottom[0]+ w/2, - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] : position.spineMarkBottom[1] ) - }, - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1, - }) - curPage.drawLine({ - start: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] - w/2: position.spineMarkBottom[0] - w/2, - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] : position.spineMarkBottom[1] ) - }, - end: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] + w/2: position.spineMarkBottom[0]+ w/2, - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] : position.spineMarkBottom[1] ) - }, - thickness: 0.5, - color: rgb(0,0,0), - opacity: 1, - }) + startX -= w / 2; + endX += w / 2; } else { - curPage.drawLine({ - start: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] : position.spineMarkBottom[0], - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] - w/2: position.spineMarkBottom[1] ) - w/2 - }, - end: { - x: (sigDetails.isSigStart) ? position.spineMarkTop[0] : position.spineMarkBottom[0], - y: ((sigDetails.isSigStart) ? position.spineMarkTop[1] + w/2: position.spineMarkBottom[1] ) + w/2 - }, - thickness: 0.25, - color: rgb(0,0,0), - opacity: 1, - }) + startY -= w / 2; + endY += w / 2; + } + + let drawLineArgs = { + start: { x: startX, y: startY }, + end: { x: endX, y: endY }, + thickness: position.rotation == 0 ? 0.5 : 0.25, + color: rgb(0, 0, 0), + opacity: 1, } + + console.log(" --> draw this: ", drawLineArgs); + curPage.drawLine(drawLineArgs); } + draw_cropmarks(currPage, side2flag) { - switch(this.per_sheet){ + const lineSettings = { + opacity: 0.4, + dashArray: [1, 5] + }; + let start, end; + + switch (this.per_sheet) { case 32: if (side2flag) { - if (this.duplexrotate){ - currPage.drawLine({ - start: {x: this.papersize[0] * 0.75, y: this.papersize[1] * 0.75 }, - end: {x: this.papersize[0] * 0.75, y: this.papersize[1] * 0.5 }, - opacity: 0.4, - dashArray: [1, 5] - });} else { - currPage.drawLine({ - start: {x: this.papersize[0] * 0.25, y: this.papersize[1] * 0.5 }, - end: {x: this.papersize[0] * 0.25, y: this.papersize[1] * 0.25 }, - opacity: 0.4, - dashArray: [1, 5] - }); + lineSettings.dashArray = [1, 5]; + if (this.duplexrotate) { + start = { x: this.papersize[0] * 0.75, y: this.papersize[1] * 0.75 }; + end = { x: this.papersize[0] * 0.75, y: this.papersize[1] * 0.5 }; + } else { + start = { x: this.papersize[0] * 0.25, y: this.papersize[1] * 0.5 }; + end = { x: this.papersize[0] * 0.25, y: this.papersize[1] * 0.25 }; + } + currPage.drawLine({ start, end, ...lineSettings }); } - } - /* falls through */ + /* falls through */ case 16: if (side2flag) { - if (this.duplexrotate){ - currPage.drawLine({ - start: {x: 0, y: this.papersize[1] * 0.75 }, - end: {x: this.papersize[0] * 0.5, y: this.papersize[1] * 0.75 }, - opacity: 0.4, - dashArray: [3, 5] - });} else { - currPage.drawLine({ - start: {x: this.papersize[0] * 0.5, y: this.papersize[1] * 0.25 }, - end: {x: this.papersize[0], y: this.papersize[1] * 0.25 }, - opacity: 0.4, - dashArray: [3, 5] - }); + lineSettings.dashArray = [3, 5]; + if (this.duplexrotate) { + start = { x: 0, y: this.papersize[1] * 0.75 }; + end = { x: this.papersize[0] * 0.5, y: this.papersize[1] * 0.75 }; + } else { + start = { x: this.papersize[0] * 0.5, y: this.papersize[1] * 0.25 }; + end = { x: this.papersize[0], y: this.papersize[1] * 0.25 }; + } + currPage.drawLine({ start, end, ...lineSettings }); } - } - /* falls through */ + /* falls through */ case 8: if (side2flag) { - if (this.duplexrotate){ - currPage.drawLine({ - start: {x: this.papersize[0] * 0.5, y: 0 }, - end: { y: this.papersize[1] * 0.5, x: this.papersize[0] * 0.5 }, - opacity: 0.4, - dashArray: [5, 5] - });} else { - currPage.drawLine({ - start: {x: this.papersize[0] * 0.5, y: this.papersize[1] }, - end: { y: this.papersize[1] * 0.5, x: this.papersize[0] * 0.5 }, - opacity: 0.4, - dashArray: [5, 5] - }); + lineSettings.dashArray = [5, 5]; + if (this.duplexrotate) { + start = { x: this.papersize[0] * 0.5, y: 0 }; + end = { y: this.papersize[1] * 0.5, x: this.papersize[0] * 0.5 }; + } else { + start = { x: this.papersize[0] * 0.5, y: this.papersize[1] }; + end = { y: this.papersize[1] * 0.5, x: this.papersize[0] * 0.5 }; + } + currPage.drawLine({ start, end, ...lineSettings }); } - } - /* falls through */ + /* falls through */ case 4: if (!side2flag) { - currPage.drawLine({ - start: { x: 0, y: this.papersize[1] * 0.5 }, - end: { x: this.papersize[0], y: this.papersize[1] * 0.5 }, - opacity: 0.4, - dashArray: [10, 5] - }); - } - } + lineSettings.dashArray = [10, 5]; + start = { x: 0, y: this.papersize[1] * 0.5 }; + end = { x: this.papersize[0], y: this.papersize[1] * 0.5 }; + currPage.drawLine({ start, end, ...lineSettings }); + } + break; + } } + draw_cutmarks(currPage) { let lines = []; diff --git a/src/models/configuration.js b/src/models/configuration.js index d96465f..ed604e1 100644 --- a/src/models/configuration.js +++ b/src/models/configuration.js @@ -48,7 +48,7 @@ const pageScaling = urlSafe(z.enum(["centered", "lockratio", "stretch"])).defaul const pagePositioning = urlSafe(z.enum(["centered", "binding_aligned"])).default("centered"); -const sigFormat = urlSafe(z.enum(["booklet", "perfect", "standardsig", "customsig", "1_3rd", "A7_2_16s", "8_zine", "a_3_6s", "a9_3_3_4", "a_4_8s", "a10_6_10s"])).default("standardsig"); +const sigFormat = urlSafe(z.enum(["booklet", "perfect", "standardsig", "customsig", "1_3rd", "a7_2_16s", "8_zine", "a_3_6s", "a9_3_3_4", "a_4_8s", "a10_6_10s"])).default("standardsig"); const wackySpacing = urlSafe(z.enum(["wacky_pack", "wacky_gap"])).default("wacky_pack"); diff --git a/src/utils/renderUtils.js b/src/utils/renderUtils.js index efebef4..6816dea 100644 --- a/src/utils/renderUtils.js +++ b/src/utils/renderUtils.js @@ -136,7 +136,7 @@ export function renderWacky() { document.getElementById('a10_6_10s').checked || document.getElementById('a_3_6s').checked || document.getElementById('a_4_8s').checked || - document.getElementById('A7_2_16s').checked || + document.getElementById('a7_2_16s').checked || document.getElementById('1_3rd').checked; console.log('Is a wacky layout? ', isWacky); document diff --git a/src/wacky_imposition.js b/src/wacky_imposition.js index cc21632..e448575 100644 --- a/src/wacky_imposition.js +++ b/src/wacky_imposition.js @@ -29,7 +29,7 @@ export class WackyImposition{ } else if (format == "a_3_6s") { this.sheets = Math.ceil(pages.length/36.0); this.sigconfig = Array(Math.ceil(pages.length/12)) - } else if (format == "A7_2_16s") { + } else if (format == "a7_2_16s") { this.sheets = Math.ceil(pages.length/32.0); this.sigconfig = Array(this.sheets * 2) } else if (format == "8_zine") {