Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Vue Example #134

Merged
merged 7 commits into from
Jan 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions examples/vue/.eslintrc.cjs

This file was deleted.

2 changes: 1 addition & 1 deletion examples/vue/.prettierrc.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"semi": true,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
Expand Down
7 changes: 5 additions & 2 deletions examples/vue/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
# Vue
# MuPDF with Vue, TypeScript, Vite

This project was generated with [Vite](https://vitejs.dev/).
## Notable files
- `src/workers/mupdf.worker.ts`: imports the worker script, exposes functionality from it.
- `src/composables/useMupdf.ts`: enables and initializes the worker, exposes functions to interact with it.
- `src/App.vue`: calls useMupdf, and uses some demo functionality.

## Setup

Expand Down
27 changes: 27 additions & 0 deletions examples/vue/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import eslint from '@eslint/js';
import eslintConfigPrettier from 'eslint-config-prettier';
import eslintPluginVue from 'eslint-plugin-vue';
import globals from 'globals';
import typescriptEslint from 'typescript-eslint';

export default typescriptEslint.config(
{ ignores: ['*.d.ts', '**/dist'] },
{
extends: [
eslint.configs.recommended,
...typescriptEslint.configs.recommended,
...eslintPluginVue.configs['flat/recommended'],
],
files: ['**/*.{ts,vue}'],
languageOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
globals: globals.browser,
parserOptions: {
parser: typescriptEslint.parser,
},
},
rules: {},
},
eslintConfigPrettier
);
35 changes: 17 additions & 18 deletions examples/vue/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,28 +9,27 @@
"preview": "vite preview",
"build-only": "vite build",
"type-check": "vue-tsc --build --force",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
"lint": "eslint --fix",
"format": "prettier --write src/"
},
"dependencies": {
"comlink": "^4.4.1",
"mupdf": "^0.2.2",
"vue": "^3.4.29"
"comlink": "^4.4.2",
"mupdf": "^1.2.0",
"vue": "^3.5.13"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.8.0",
"@eslint/eslintrc": "^3.2.0",
"@eslint/js": "^9.18.0",
"@tsconfig/node20": "^20.1.4",
"@types/node": "^20.14.5",
"@vitejs/plugin-vue": "^5.0.5",
"@vue/eslint-config-prettier": "^9.0.0",
"@vue/eslint-config-typescript": "^13.0.0",
"@vue/tsconfig": "^0.5.1",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.23.0",
"npm-run-all2": "^6.2.0",
"prettier": "^3.2.5",
"typescript": "~5.4.0",
"vite": "^5.3.1",
"vue-tsc": "^2.0.21"
"@types/node": "^22.10.7",
"@vitejs/plugin-vue": "^5.2.1",
"@vue/tsconfig": "^0.7.0",
"eslint": "^9.18.0",
"eslint-plugin-vue": "^9.32.0",
"npm-run-all2": "^7.0.2",
"prettier": "^3.4.2",
"typescript": "^5.7.3",
"vite": "^6.0.11",
"vue-tsc": "^2.2.0"
}
}
}
23 changes: 13 additions & 10 deletions examples/vue/src/App.vue
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
<script setup lang="ts">
import { useMupdf } from '@/composables/useMupdf'
import { watch, ref } from 'vue'
import { useMupdf } from '@/composables/useMupdf';
import { ref, watch } from 'vue';

const { workerInitialized, loadDocument, renderPage, currentPage } = useMupdf()
const pdfUrl = ref<string | null>(null)
const { workerInitialized, loadDocument, renderPage } = useMupdf();
const pdfUrl = ref<string | null>(null);

// ===> This is a demo callback which uses functions <===
// ===> from useMupdf to load and display the first page <===
// ===> of the pdf as an image. <===
watch(workerInitialized, async (isInitialized) => {
if (isInitialized) {
const response = await fetch('/test.pdf')
const arrayBuffer = await response.arrayBuffer()
await loadDocument(arrayBuffer)
const pngData = await renderPage(0)
pdfUrl.value = URL.createObjectURL(new Blob([pngData], { type: 'image/png' }))
const response = await fetch('/test.pdf');
const arrayBuffer = await response.arrayBuffer();
await loadDocument(arrayBuffer);
const pngData = await renderPage(0);
pdfUrl.value = URL.createObjectURL(new Blob([pngData], { type: 'image/png' }));
}
})
});
</script>

<template>
Expand Down
15 changes: 9 additions & 6 deletions examples/vue/src/composables/useMupdf.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { MupdfWorker } from '@/workers/mupdf.worker';
import { MUPDF_LOADED, type MupdfWorker } from '@/workers/mupdf.worker';
import * as Comlink from 'comlink';
import { ref, shallowRef } from 'vue';

Expand All @@ -7,7 +7,7 @@ const mupdfWorker = Comlink.wrap<MupdfWorker>(worker);
const workerInitialized = ref(false);

worker.addEventListener('message', (event) => {
if (event.data === 'MUPDF_LOADED') {
if (event.data === MUPDF_LOADED) {
workerInitialized.value = true;
}
});
Expand All @@ -16,12 +16,15 @@ export function useMupdf() {
const document = shallowRef<ArrayBuffer | null>(null);
const currentPage = ref(0);

const loadDocument = async (arrayBuffer: ArrayBuffer) => {
// ===> Here you can create functions <===
// ===> that use the methods of the worker. <===

const loadDocument = (arrayBuffer: ArrayBuffer) => {
document.value = arrayBuffer;
return mupdfWorker.loadDocument(arrayBuffer);
};

const renderPage = async (pageIndex: number) => {
const renderPage = (pageIndex: number) => {
if (!document.value) throw new Error('Document not loaded');
currentPage.value = pageIndex;
return mupdfWorker.renderPageAsImage(pageIndex, (window.devicePixelRatio * 96) / 72);
Expand All @@ -31,6 +34,6 @@ export function useMupdf() {
workerInitialized,
loadDocument,
renderPage,
currentPage,
currentPage
};
}
}
8 changes: 4 additions & 4 deletions examples/vue/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import './assets/main.css'
import './assets/main.css';

import { createApp } from 'vue'
import App from './App.vue'
import { createApp } from 'vue';
import App from './App.vue';

createApp(App).mount('#app')
createApp(App).mount('#app');
29 changes: 13 additions & 16 deletions examples/vue/src/workers/mupdf.worker.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,40 @@
/// <reference lib="webworker" />
import * as Comlink from 'comlink';
import * as mupdfjs from 'mupdf/mupdfjs';
import { PDFDocument } from 'mupdf/mupdfjs';

export const MUPDF_LOADED = 'MUPDF_LOADED';

const mupdfScript = import.meta.env.PROD ? '/assets/mupdf.js' : '/node_modules/mupdf/dist/mupdf.js';

export class MupdfWorker {
private mupdf?: any;
private document?: any;
private document?: PDFDocument;

constructor() {
this.initializeMupdf();
}

private async initializeMupdf() {
try {
const mupdfModule = await import(/* @vite-ignore */ mupdfScript);
this.mupdf = mupdfModule;
postMessage(MUPDF_LOADED);
} catch (error) {
console.error('Failed to initialize MuPDF:', error);
}
}

async loadDocument(document: ArrayBuffer): Promise<boolean> {
if (!this.mupdf) throw new Error('MuPDF not initialized');
this.document = this.mupdf.Document.openDocument(
document,
'application/pdf'
);
// ===> Here you can create methods <===
// ===> that call statics and methods <===
// ===> from mupdfjs which wraps ./node_modules/mupdf/dist/mupdf.js <===

loadDocument(document: ArrayBuffer): boolean {
this.document = mupdfjs.PDFDocument.openDocument(document, 'application/pdf');
return true;
}

async renderPageAsImage(pageIndex: number = 0, scale: number = 1): Promise<Uint8Array> {
if (!this.mupdf || !this.document) throw new Error('Document not loaded');
renderPageAsImage(pageIndex: number = 0, scale: number = 1): Uint8Array {
if (!this.document) throw new Error('Document not loaded');
const page = this.document.loadPage(pageIndex);
const pixmap = page.toPixmap([scale, 0, 0, scale, 0, 0], this.mupdf.ColorSpace.DeviceRGB);
const pixmap = page.toPixmap([scale, 0, 0, scale, 0, 0], mupdfjs.ColorSpace.DeviceRGB);
return pixmap.asPNG();
}
}

Comlink.expose(new MupdfWorker());
Comlink.expose(new MupdfWorker());
57 changes: 13 additions & 44 deletions examples/vue/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,45 +1,17 @@
import vue from '@vitejs/plugin-vue'
import fs from 'fs'
import path, { resolve } from 'path'
import { fileURLToPath } from 'url'
import { defineConfig } from 'vite'

const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)

function copyMupdfFiles() {
return {
name: 'copy-mupdf-files',
writeBundle() {
const srcDir = resolve(__dirname, 'node_modules/mupdf/dist')
const destDir = resolve(__dirname, 'dist/assets')
const filesToCopy = [
'mupdf-wasm.js',
'mupdf-wasm.wasm',
'mupdf.js',
'tasks.js'
]

if (!fs.existsSync(destDir)) {
fs.mkdirSync(destDir, { recursive: true })
}

filesToCopy.forEach(file => {
const src = path.join(srcDir, file)
const dest = path.join(destDir, file)
if (fs.existsSync(src)) {
fs.copyFileSync(src, dest)
console.log(`Copied ${file} to ${destDir}`)
} else {
console.warn(`Warning: ${file} not found in ${srcDir}`)
}
})
}
}
}
import vue from '@vitejs/plugin-vue';
import { resolve } from 'path';
import { fileURLToPath } from 'url';
import { defineConfig } from 'vite';

// https://vite.dev/config/
export default defineConfig({
plugins: [vue(), copyMupdfFiles()],
plugins: [vue()],
worker: {
format: 'es'
},
optimizeDeps: {
exclude: ['mupdf'] // Exclude mupdf from pre-bundling
},
build: {
target: 'esnext',
rollupOptions: {
Expand All @@ -49,12 +21,9 @@ export default defineConfig({
}
}
},
worker: {
format: 'es'
},
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
}
})
});
Loading