1
0
Fork 0

add single-page pdf export

This commit is contained in:
Enoch Riese 2023-08-06 18:24:14 +00:00
parent bf417a201f
commit 2867b9c340
6 changed files with 83 additions and 45 deletions

View file

@ -155,13 +155,11 @@ export const handleExport = async ({
let pattern = themedPattern(Design, settings, { layout }, format, t) let pattern = themedPattern(Design, settings, { layout }, format, t)
// a specified size should override the settings one // a specified size should override the settings one
if (format !== 'pdf') { pageSettings.size = format
pageSettings.size = format
}
try { try {
// add pages to pdf exports // add pages to pdf exports
if (format !== 'svg') { if (!exportTypes.exportForEditing.includes(format)) {
pattern.use( pattern.use(
pagesPlugin({ pagesPlugin({
...pageSettings, ...pageSettings,
@ -184,11 +182,13 @@ export const handleExport = async ({
pattern.draft() pattern.draft()
workerArgs.svg = pattern.render() workerArgs.svg = pattern.render()
if (format === 'pdf') pageSettings.size = [pattern.width, pattern.height]
// add the svg and pages data to the worker args // add the svg and pages data to the worker args
workerArgs.pages = pattern.setStores[pattern.activeSet].get('pages') workerArgs.pages = pattern.setStores[pattern.activeSet].get('pages')
// add cutting layouts if requested // add cutting layouts if requested
if (format !== 'svg' && pageSettings.cutlist) { if (!exportTypes.exportForEditing.includes(format) && pageSettings.cutlist) {
workerArgs.cutLayouts = generateCutLayouts(pattern, Design, settings, format, t, ui) workerArgs.cutLayouts = generateCutLayouts(pattern, Design, settings, format, t, ui)
} }
} catch (err) { } catch (err) {

View file

@ -4,6 +4,7 @@
import yaml from 'js-yaml' import yaml from 'js-yaml'
import axios from 'axios' import axios from 'axios'
import { PdfMaker } from './pdf-maker' import { PdfMaker } from './pdf-maker'
import { SinglePdfMaker } from './single-pdf-maker.mjs'
/** when the worker receives data from the page, do the appropriate export */ /** when the worker receives data from the page, do the appropriate export */
addEventListener('message', async (e) => { addEventListener('message', async (e) => {
@ -49,7 +50,7 @@ const exportYaml = (settings) => exportBlob(yaml.dump(settings), 'application/x-
const exportSvg = (svg) => exportBlob(svg, 'image/svg+xml') const exportSvg = (svg) => exportBlob(svg, 'image/svg+xml')
const exportPdf = async (data) => { const exportPdf = async (data) => {
const maker = new PdfMaker(data) const maker = data.format === 'pdf' ? new SinglePdfMaker(data) : new PdfMaker(data)
await maker.makePdf() await maker.makePdf()
postSuccess(await maker.toBlob()) postSuccess(await maker.toBlob())
} }

View file

@ -1,4 +1,4 @@
import PDFDocument from 'pdfkit/js/pdfkit.standalone' import { Pdf, mmToPoints } from './pdf.mjs'
import SVGtoPDF from 'svg-to-pdfkit' import SVGtoPDF from 'svg-to-pdfkit'
import { logoPath } from 'shared/components/logos/freesewing.mjs' import { logoPath } from 'shared/components/logos/freesewing.mjs'
@ -8,11 +8,6 @@ const logoSvg = `<svg viewBox="0 0 25 25">
<path d="${logoPath}" /> <path d="${logoPath}" />
</svg>` </svg>`
/**
* PdfKit, the library we're using for pdf generation, uses points as a unit, so when we tell it things like where to put the svg and how big the svg is, we need those numbers to be in points
* The svg uses mm internally, so when we do spatial reasoning inside the svg, we need to know values in mm
* */
const mmToPoints = 2.834645669291339
const lineStart = 50 const lineStart = 50
/** /**
* Freesewing's first explicit class? * Freesewing's first explicit class?
@ -58,7 +53,10 @@ export class PdfMaker {
this.strings = strings this.strings = strings
this.cutLayouts = cutLayouts this.cutLayouts = cutLayouts
this.initPdf() this.pdf = Pdf({
size: this.pageSettings.size.toUpperCase(),
layout: this.pageSettings.orientation,
})
this.margin = this.pageSettings.margin * mmToPoints // margin is in mm because it comes from us, so we convert it to points this.margin = this.pageSettings.margin * mmToPoints // margin is in mm because it comes from us, so we convert it to points
this.pageHeight = this.pdf.page.height - this.margin * 2 // this is in points because it comes from pdfKit this.pageHeight = this.pdf.page.height - this.margin * 2 // this is in points because it comes from pdfKit
@ -73,22 +71,6 @@ export class PdfMaker {
this.svgHeight = this.rows * this.pageHeight this.svgHeight = this.rows * this.pageHeight
} }
/** create the pdf document */
initPdf() {
// instantiate with the correct size and orientation
this.pdf = new PDFDocument({
size: this.pageSettings.size.toUpperCase(),
layout: this.pageSettings.orientation,
})
// PdfKit wants to flush the buffer on each new page.
// We can't save directly from inside a worker, so we have to manage the buffers ourselves so we can return a blob
this.buffers = []
// use a listener to add new data to our buffer storage
this.pdf.on('data', this.buffers.push.bind(this.buffers))
}
/** make the pdf */ /** make the pdf */
async makePdf() { async makePdf() {
await this.generateCoverPage() await this.generateCoverPage()
@ -97,21 +79,8 @@ export class PdfMaker {
} }
/** convert the pdf to a blob */ /** convert the pdf to a blob */
toBlob() { async toBlob() {
return new Promise((resolve) => { return this.pdf.toBlob()
// have to do it this way so that the document flushes everything to buffers
this.pdf.on('end', () => {
// convert buffers to a blob
resolve(
new Blob(this.buffers, {
type: 'application/pdf',
})
)
})
// end the stream
this.pdf.end()
})
} }
/** generate the cover page for the pdf */ /** generate the cover page for the pdf */

View file

@ -0,0 +1,45 @@
import PDFDocument from 'pdfkit/js/pdfkit.standalone'
/**
* PdfKit, the library we're using for pdf generation, uses points as a unit, so when we tell it things like where to put the svg and how big the svg is, we need those numbers to be in points
* The svg uses mm internally, so when we do spatial reasoning inside the svg, we need to know values in mm
* */
export const mmToPoints = 2.834645669291339
/**
* A PDFKit Pdf with a few of our utilities included
* @returns
*/
export const Pdf = ({ size, layout }) => {
const pdf = new PDFDocument({
size,
layout,
})
// PdfKit wants to flush the buffer on each new page.
// We can't save directly from inside a worker, so we have to manage the buffers ourselves so we can return a blob
const buffers = []
// use a listener to add new data to our buffer storage
pdf.on('data', buffers.push.bind(buffers))
/** convert the pdf to a blob */
pdf.toBlob = function () {
return new Promise((resolve) => {
// have to do it this way so that the document flushes everything to buffers
pdf.on('end', () => {
// convert buffers to a blob
resolve(
new Blob(buffers, {
type: 'application/pdf',
})
)
})
// end the stream
pdf.end()
})
}
return pdf
}

View file

@ -0,0 +1,23 @@
import { Pdf, mmToPoints } from './pdf.mjs'
import SVGtoPDF from 'svg-to-pdfkit'
/**
* Basic exporter for a single-page pdf containing the rendered pattern.
* This generates a PDF that is the size of the pattern and has no additional frills*/
export class SinglePdfMaker {
pdf
svg
constructor({ svg, pageSettings }) {
this.pdf = Pdf({ size: pageSettings.size.map((s) => s * mmToPoints) })
this.svg = svg
}
async makePdf() {
await SVGtoPDF(this.pdf, this.svg)
}
async toBlob() {
return this.pdf.toBlob()
}
}

View file

@ -73,7 +73,7 @@ export const PrintView = ({
const exportIt = () => { const exportIt = () => {
handleExport({ handleExport({
format: 'pdf', format: pageSettings.size,
settings, settings,
design, design,
t, t,