diff --git a/src/book.js b/src/book.js index d91c328..2e3c499 100644 --- a/src/book.js +++ b/src/book.js @@ -2,14 +2,15 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -import { PDFDocument, degrees, rgb } from 'pdf-lib'; +import { PDFDocument, degrees } from 'pdf-lib'; import { saveAs } from 'file-saver'; import { Signatures } from './signatures.js'; import { WackyImposition } from './wacky_imposition.js'; -import { PAGE_LAYOUTS, PAGE_SIZES, LINE_LEN } from './constants.js'; +import { PAGE_LAYOUTS, PAGE_SIZES } from './constants.js'; import { updatePageLayoutInfo } from './utils/renderUtils.js'; import JSZip from 'jszip'; import { loadConfiguration } from './utils/formUtils.js'; +import { drawFoldlines, drawCropmarks, drawSpineMarks } from './utils/drawing.js'; // Some JSDoc typedefs we use multiple places /** @@ -23,7 +24,6 @@ import { loadConfiguration } from './utils/formUtils.js'; /** * @typedef Position * @type {object} - * {rotation (degrees), sx, sy, x, y} * @property {number} rotation - Rotation in degrees * @property {number} sx - x scale factor (where 1.0 is 100%) * @property {number} sy - y scale factor (where 1.0 is 100%) @@ -553,201 +553,49 @@ export class Book { const papersize = config.papersize; const outPDF = config.outPDF; const positions = config.positions; - const cropmarks = config.cropmarks; + const foldmarks = config.cropmarks; const pdfEdgeMarks = config.pdfEdgeMarks; const cutmarks = config.cutmarks; const alt = config.alt; let side2flag = config.side2flag; const block = config.embeddedPages.slice(block_start, block_end); - const currPage = outPDF.addPage([papersize[0], papersize[1]]); + const currPage = outPDF.addPage(papersize); + const cropLines = cutmarks ? drawCropmarks(papersize, this.per_sheet) : []; + const foldLines = foldmarks + ? drawFoldlines(side2flag, this.duplexrotate, papersize, this.per_sheet) + : []; + const drawLines = [...cropLines, ...foldLines]; block.forEach((page, i) => { if (page == 'b' || page === undefined) { // blank page, move on. } else { - const pos = positions[i]; - const rot = pos.rotation; + const { y, x, sx, sy, rotation } = positions[i]; currPage.drawPage(page, { - y: pos.y, - x: pos.x, - xScale: pos.sx, - yScale: pos.sy, - rotate: degrees(rot), + y, + x, + xScale: sx, + yScale: sy, + rotate: degrees(rotation), }); } - }); - block.forEach((page, i) => { - if (sigDetails[i].isSigStart || sigDetails[i].isSigEnd) { - if (pdfEdgeMarks) { - this.draw_spine_marks(currPage, sigDetails[i], positions[i]); - } + + if (pdfEdgeMarks && (sigDetails[i].isSigStart || sigDetails[i].isSigEnd)) { + drawLines.push(drawSpineMarks(sigDetails[i], positions[i])); } }); - if (cropmarks) { - this.draw_cropmarks(currPage, side2flag); - } - if (cutmarks) { - this.draw_cutmarks(currPage); - } + + drawLines.forEach((line) => { + currPage.drawLine(line); + }); + if (alt) { side2flag = !side2flag; } return side2flag; } - /** - * @param curPage - PDFPage - * @param {PageInfo} sigDetails - page info object - * @param {Position} position - position info object - */ - draw_spine_marks(curPage, sigDetails, position) { - const w = 5; - let startX, startY, endX, endY; - if (sigDetails.isSigStart) { - [startX, startY] = position.spineMarkTop; - [endX, endY] = position.spineMarkTop; - } else { - [startX, startY] = position.spineMarkBottom; - [endX, endY] = position.spineMarkBottom; - } - - if (position.rotation == 0) { - startX -= w / 2; - endX += w / 2; - } else if (sigDetails.isSigStart) { - startY -= w; - endY += w; - } else { - startY -= w / 2; - endY += w / 2; - } - - const 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); - } - - /** - * @param curPage - PDFPage - * @param {boolean} side2flag - whether we're on the back side or not - */ - draw_cropmarks(currPage, side2flag) { - const lineSettings = { - opacity: 0.4, - dashArray: [1, 5], - }; - let start, end; - - switch (this.per_sheet) { - case 32: - if (side2flag) { - 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 */ - case 16: - if (side2flag) { - 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 */ - case 8: - if (side2flag) { - 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 */ - case 4: - if (!side2flag) { - 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 = []; - switch (this.per_sheet) { - case 32: - lines = [ - ...lines, - ...this.draw_hline(this.papersize[1] * 0.75, 0, this.papersize[0]), - ...this.draw_hline(this.papersize[1] * 0.25, 0, this.papersize[0]), - ...this.draw_cross(this.papersize[0] * 0.5, this.papersize[1] * 0.75), - ...this.draw_cross(this.papersize[0] * 0.5, this.papersize[1] * 0.25), - ]; - /* falls through */ - case 16: - lines = [ - ...lines, - ...this.draw_vline(this.papersize[0] * 0.5, 0, this.papersize[1]), - ...this.draw_cross(this.papersize[0] * 0.5, this.papersize[1] * 0.5), - ]; - /* falls through */ - case 8: - lines = [...lines, ...this.draw_hline(this.papersize[1] * 0.5, 0, this.papersize[0])]; - /* falls through */ - case 4: - } - - lines.forEach((line) => { - currPage.drawLine({ ...line, opacity: 0.4 }); - }); - } - - draw_vline(x, ystart, yend) { - return [ - { start: { x: x, y: ystart }, end: { x: x, y: ystart + LINE_LEN } }, - { start: { x: x, y: yend - LINE_LEN }, end: { x: x, y: yend } }, - ]; - } - - draw_hline(y, xstart, xend) { - return [ - { start: { x: xstart, y: y }, end: { x: xstart + LINE_LEN, y: y } }, - { start: { x: xend - LINE_LEN, y: y }, end: { x: xend, y: y } }, - ]; - } - - draw_cross(x, y) { - return [ - { start: { x: x - LINE_LEN, y: y }, end: { x: x + LINE_LEN, y: y } }, - { start: { x: x, y: y - LINE_LEN }, end: { x: x, y: y + LINE_LEN } }, - ]; - } - /** * Looks at [this.cropbox] and [this.padding_pt] and [this.papersize] and [this.page_layout] and [this.page_scaling] * in order to calculate the information needed to render a PDF page within a layout cell. It provides several functions diff --git a/src/utils/drawing.js b/src/utils/drawing.js new file mode 100644 index 0000000..c2f2670 --- /dev/null +++ b/src/utils/drawing.js @@ -0,0 +1,207 @@ +import { LINE_LEN } from '../constants'; +import { rgb } from 'pdf-lib'; + +/** + * @typedef Point + * @type {object} + * @property {number} x - horizontal position + * @property {number} y - vertical position + */ + +/** + * @typedef Line + * @type {object} + * @property {Point} start - start position + * @property {Point} end - end position + * @property {number} [opacity] - line opacity + * @property {number[]} [dashArray] - sequence of dash and gap lengths to be repeated for a dashed line + */ + +/** + * @param {boolean} side2flag - whether we're on the back or not. + * @param {boolean} duplexrotate - if alternate sides are rotated or not + * @param {number[]} papersize - paper dimensions + * @param {number} per_sheet - pages per sheet of paper + * @returns {Line[]} + */ +export function drawFoldlines(side2flag, duplexrotate, papersize, per_sheet) { + const lineSettings = { + opacity: 0.4, + dashArray: [1, 5], + }; + let x, xStart, xEnd; + let y, yStart, yEnd; + const [width, height] = papersize; + const lines = []; + + switch (per_sheet) { + case 32: + if (side2flag) { + lineSettings.dashArray = [1, 5]; + + x = duplexrotate ? width * 0.75 : width * 0.25; + yStart = height * 0.5; + yEnd = duplexrotate ? height * 0.75 : height * 0.25; + + lines.push({ ...drawVLine(x, yStart, yEnd), ...lineSettings }); + } + /* falls through */ + case 16: + if (side2flag) { + lineSettings.dashArray = [3, 5]; + + y = duplexrotate ? height * 0.75 : height * 0.25; + xStart = width * 0.5; + xEnd = duplexrotate ? 0 : height; + + lines.push({ ...drawHLine(y, xStart, xEnd), ...lineSettings }); + } + /* falls through */ + case 8: + if (side2flag) { + lineSettings.dashArray = [5, 5]; + + x = width * 0.5; + yStart = height * 0.5; + yEnd = duplexrotate ? 0 : height; + + lines.push({ ...drawVLine(x, yStart, yEnd), ...lineSettings }); + } + /* falls through */ + case 4: + if (!side2flag) { + lineSettings.dashArray = [10, 5]; + lines.push({ ...drawHLine(height * 0.5, 0, width), ...lineSettings }); + } + break; + } + return lines; +} + +/** + * @param {number[]} papersize - paper dimensions + * @param {number} per_sheet - number of pages per sheet of paper + * @returns {Line[]} + */ +export function drawCropmarks(papersize, per_sheet) { + let lines = []; + const [width, height] = papersize; + switch (per_sheet) { + case 32: + lines = [ + ...lines, + ...drawHCrop(height * 0.75, 0, width), + ...drawHCrop(height * 0.25, 0, width), + ...drawCross(width * 0.5, height * 0.75), + ...drawCross(width * 0.5, height * 0.25), + ]; + /* falls through */ + case 16: + lines = [ + ...lines, + ...drawVCrop(width * 0.5, 0, height), + ...drawCross(width * 0.5, height * 0.5), + ]; + /* falls through */ + case 8: + lines = [...lines, ...drawHCrop(height * 0.5, 0, width)]; + /* falls through */ + case 4: + } + + return lines; +} + +/** + * @param {PageInfo} sigDetails - page info object + * @param {Position} position - position info object + * @returns {Line[]} + */ +export function drawSpineMarks(sigDetails, position) { + const w = 5; + let startX, startY, endX, endY; + if (sigDetails.isSigStart) { + [startX, startY] = position.spineMarkTop; + [endX, endY] = position.spineMarkTop; + } else { + [startX, startY] = position.spineMarkBottom; + [endX, endY] = position.spineMarkBottom; + } + + if (position.rotation == 0) { + startX -= w / 2; + endX += w / 2; + } else { + startY -= w / 2; + endY += w / 2; + } + + const 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); + return drawLineArgs; +} + +/** + * @param {number} x + * @param {number} ystart + * @param {number} yend + * @returns {Line} + */ +function drawVLine(x, ystart, yend) { + return { start: { x: x, y: ystart }, end: { x: x, y: yend } }; +} + +/** + * @param {number} y + * @param {number} xstart + * @param {number} xend + * @returns {Line} + */ +function drawHLine(y, xstart, xend) { + return { start: { x: xstart, y: y }, end: { x: xend, y: y } }; +} + +/** + * @param {number} x + * @param {number} ystart + * @param {number} yend + * @returns {Line[]} + */ +function drawVCrop(x, ystart, yend) { + return [ + { start: { x: x, y: ystart }, end: { x: x, y: ystart + LINE_LEN }, opacity: 0.4 }, + { start: { x: x, y: yend - LINE_LEN }, end: { x: x, y: yend }, opacity: 0.4 }, + ]; +} + +/** + * @param {number} y + * @param {number} xstart + * @param {number} xend + * @returns {Line[]} + */ +function drawHCrop(y, xstart, xend) { + return [ + { start: { x: xstart, y: y }, end: { x: xstart + LINE_LEN, y: y }, opacity: 0.4 }, + { start: { x: xend - LINE_LEN, y: y }, end: { x: xend, y: y }, opacity: 0.4 }, + ]; +} + +/** + * @param {number} y + * @param {number} x + * @returns {Line[]} + */ +function drawCross(x, y) { + return [ + { start: { x: x - LINE_LEN, y: y }, end: { x: x + LINE_LEN, y: y }, opacity: 0.4 }, + { start: { x: x, y: y - LINE_LEN }, end: { x: x, y: y + LINE_LEN }, opacity: 0.4 }, + ]; +}