Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/TaTo30/VuePDF
Browse files Browse the repository at this point in the history
  • Loading branch information
TaTo30 committed May 26, 2024
2 parents 7c4387d + 0365653 commit 92ba602
Show file tree
Hide file tree
Showing 9 changed files with 143 additions and 17 deletions.
24 changes: 12 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@
"vue": "^3.2.33"
},
"dependencies": {
"pdfjs-dist": "3.11.174"
"pdfjs-dist": "4.2.67"
},
"devDependencies": {
"@antfu/eslint-config": "^0.38.5",
Expand Down
73 changes: 73 additions & 0 deletions src/components/usePDF.ts → src/components/composable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import type { PDFDocumentLoadingTask, PDFDocumentProxy } from 'pdfjs-dist'
import type { Ref } from 'vue'
import type { OnPasswordCallback, PDFDestination, PDFInfo, PDFOptions, PDFSrc } from './types'
import { getDestinationArray, getDestinationRef, getLocation, isSpecLike } from './utils/destination'
import { addStylesToIframe, createIframe } from './utils/miscellaneous'

// Could not find a way to make this work with vite, importing the worker entry bundle the whole worker to the the final output
// https://erindoyle.dev/using-pdfjs-with-vite/
Expand Down Expand Up @@ -114,6 +115,76 @@ export function usePDF(src: PDFSrc | Ref<PDFSrc>,
return { pageIndex, location: location ?? { type: 'Fit', spec: [] } }
}

async function download(filename = 'filename') {
if (!pdfDoc.value)
throw new Error('Current PDFDocumentProxy have not loaded yet')
const bytes = await pdfDoc.value?.saveDocument()
const blobBytes = new Blob([bytes], { type: 'application/pdf' })
const blobUrl = URL.createObjectURL(blobBytes)

const anchorDownload = document.createElement('a')
document.body.appendChild(anchorDownload)
anchorDownload.href = blobUrl
anchorDownload.download = filename
anchorDownload.style.display = 'none'
anchorDownload.click()

setTimeout(() => {
URL.revokeObjectURL(blobUrl)
document.body.removeChild(anchorDownload)
}, 10)
}

async function print(dpi = 150, filename = 'filename') {
if (!pdfDoc.value)
throw new Error('Current PDFDocumentProxy have not loaded yet')
const bytes = await pdfDoc.value?.saveDocument()
const savedLoadingTask = PDFJS.getDocument(bytes.buffer)
const savedDocument = await savedLoadingTask.promise

const PRINT_UNITS = dpi / 72
const CSS_UNITS = 96 / 72

const iframe = await createIframe()
const contentWindow = iframe.contentWindow
contentWindow!.document.title = filename

const pagesNumbers = [...Array(savedDocument.numPages).keys()].map(val => val + 1)

for (const pageNumber of pagesNumbers) {
const pageToPrint = await savedDocument.getPage(pageNumber)
const viewport = pageToPrint.getViewport({ scale: 1 })!

if (pageNumber === 1) {
addStylesToIframe(
contentWindow!,
(viewport.width * PRINT_UNITS) / CSS_UNITS,
(viewport.height * PRINT_UNITS) / CSS_UNITS,
)
}

const canvas = document.createElement('canvas')
canvas.width = viewport.width * PRINT_UNITS
canvas.height = viewport.height * PRINT_UNITS

const canvasCloned = canvas.cloneNode() as HTMLCanvasElement
contentWindow?.document.body.appendChild(canvasCloned)

await pageToPrint?.render({
canvasContext: canvas.getContext('2d')!,
intent: 'print',
transform: [PRINT_UNITS, 0, 0, PRINT_UNITS, 0, 0],
viewport,
}).promise

canvasCloned.getContext('2d')?.drawImage(canvas, 0, 0)
}

contentWindow?.focus()
contentWindow?.print()
document.body.removeChild(iframe)
}

if (isRef(src)) {
if (src.value)
processLoadingTask(src.value)
Expand All @@ -131,6 +202,8 @@ export function usePDF(src: PDFSrc | Ref<PDFSrc>,
pdf,
pages,
info,
print,
download,
getPDFDestination,
}
}
4 changes: 2 additions & 2 deletions src/components/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './types'
export * from './usePDF'
export { default as VuePDF } from './VuePDF.vue'
export * from './composable'
export * from './types'
2 changes: 1 addition & 1 deletion src/components/layers/AnnotationLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -98,9 +98,9 @@ async function render() {
accessibilityManager: undefined,
annotationCanvasMap: canvasMap,
div: layer.value!,
l10n: null,
page: page!,
viewport: viewport!.clone({ dontFlip: true }),
annotationEditorUIManager: null,
}
const renderParameters: AnnotationLayerParameters = {
Expand Down
1 change: 0 additions & 1 deletion src/components/layers/TextLayer.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ function render() {
textContentSource: textStream!,
viewport: viewport!,
container: layer.value!,
isOffscreenCanvasSupported: true,
textDivs,
textDivProperties: new WeakMap(),
}
Expand Down
40 changes: 40 additions & 0 deletions src/components/utils/miscellaneous.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
async function createIframe(): Promise<HTMLIFrameElement> {
return new Promise((resolve, reject) => {
const iframe = document.createElement('iframe')

iframe.width = '0px'
iframe.height = '0px'
iframe.style.cssText = 'position: absolute; top:0; left:0'
iframe.style.display = 'none'

iframe.onload = function () {
resolve(iframe)
}
document.body.appendChild(iframe)
})
}

function addStylesToIframe(content: Window, sizeX: number, sizeY: number) {
const style = content.document.createElement('style')
style.textContent = `
@page {
margin: 0;
size: ${sizeX}pt ${sizeY}pt;
}
body {
margin: 0;
width: 100%;
}
canvas {
width: 100%;
page-break-after: always;
page-break-before: avoid;
page-break-inside: avoid;
}
`
content.document.head.appendChild(style)
}

export {
addStylesToIframe, createIframe,
}
7 changes: 7 additions & 0 deletions vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export default defineConfig({
headless: true,
},
},
optimizeDeps: {
esbuildOptions: {
supported: {
'top-level-await': true,
},
},
},
build: {
lib: {
entry: resolve(__dirname, './src/index.ts'),
Expand Down
7 changes: 7 additions & 0 deletions vite.playground.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@ import vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vite'

export default defineConfig({
optimizeDeps: {
esbuildOptions: {
supported: {
'top-level-await': true,
},
},
},
plugins: [
vue(),
],
Expand Down

0 comments on commit 92ba602

Please sign in to comment.