11<template >
22 <div class =" text-xs" >
3- <div v-if =" pdfData" >
4- <!--
5- We use props.resource.url instead of props.resource.latest
6- because the PDF component raises an error otherwise.
7- See https://github.com/datagouv/cdata/pull/611
8- -->
9- <PDF
10- :src =" props.resource.url"
11- :show-progress =" true"
12- progress-color =" #0063cb"
13- :show-page-tooltip =" true"
14- :show-back-to-top-btn =" true"
15- :scroll-threshold =" 300"
16- pdf-width =" 100%"
17- :row-gap =" 12"
18- :use-system-fonts =" true"
19- :disable-range =" false"
20- :disable-stream =" false"
21- :disable-auto-fetch =" false"
3+ <div
4+ v-if =" pdfReady"
5+ ref =" containerRef"
6+ class =" w-full overflow-y-auto max-h-[80vh] space-y-3"
7+ >
8+ <canvas
9+ v-for =" page in totalPages"
10+ :key =" page"
11+ :ref =" (el) => setCanvasRef(el as HTMLCanvasElement, page)"
2212 class =" w-full"
23- @on-progress =" handleProgress"
24- @on-complete =" handleComplete"
25- @on-page-change =" handlePageChange"
26- @on-pdf-init =" handlePdfInit"
27- @on-error =" handleError"
2813 />
2914 </div >
3015 <div
6449</template >
6550
6651<script setup lang="ts">
67- import { computed , defineAsyncComponent , onMounted , ref } from ' vue'
52+ import { computed , nextTick , onBeforeUnmount , onMounted , ref } from ' vue'
6853import { RiErrorWarningLine } from ' @remixicon/vue'
54+ import * as pdfjsLib from ' pdfjs-dist'
55+ import pdfjsWorker from ' pdfjs-dist/build/pdf.worker.min.mjs?url'
56+ import type { PDFDocumentProxy } from ' pdfjs-dist'
6957import SimpleBanner from ' ../SimpleBanner.vue'
7058import { useComponentsConfig } from ' ../../config'
7159import type { Resource } from ' ../../types/resources'
7260import { useTranslation } from ' ../../composables/useTranslation'
7361import { getResourceFilesize } from ' ../../functions/datasets'
7462
75- const PDF = defineAsyncComponent (() =>
76- import (' pdf-vue3' ).then ((module ) => {
77- return module .default
78- }),
79- )
63+ pdfjsLib .GlobalWorkerOptions .workerSrc = pdfjsWorker
8064
8165const props = defineProps <{
8266 resource: Resource
@@ -85,10 +69,47 @@ const props = defineProps<{
8569const config = useComponentsConfig ()
8670const { t } = useTranslation ()
8771
88- const pdfData = ref <boolean >(false )
72+ const containerRef = ref <HTMLElement | null >(null )
73+ const pdfReady = ref (false )
8974const loading = ref (false )
9075const error = ref <string | null >(null )
9176const fileTooLarge = ref (false )
77+ const totalPages = ref (0 )
78+
79+ let pdfDoc: PDFDocumentProxy | null = null
80+ const canvasRefs = new Map <number , HTMLCanvasElement >()
81+
82+ function setCanvasRef(el : HTMLCanvasElement | null , pageNum : number ) {
83+ if (el ) canvasRefs .set (pageNum , el )
84+ }
85+
86+ async function renderPage(pageNum : number ) {
87+ if (! pdfDoc ) return
88+
89+ const canvas = canvasRefs .get (pageNum )
90+ if (! canvas ) return
91+
92+ const page = await pdfDoc .getPage (pageNum )
93+
94+ const containerWidth = containerRef .value ?.clientWidth ?? 800
95+ const unscaledViewport = page .getViewport ({ scale: 1 })
96+ const scale = containerWidth / unscaledViewport .width
97+ const viewport = page .getViewport ({ scale })
98+
99+ const dpr = window .devicePixelRatio || 1
100+ canvas .width = viewport .width * dpr
101+ canvas .height = viewport .height * dpr
102+ canvas .style .width = ` ${viewport .width }px `
103+ canvas .style .height = ` ${viewport .height }px `
104+
105+ const context = canvas .getContext (' 2d' )!
106+ context .scale (dpr , dpr )
107+
108+ await page .render ({
109+ canvasContext: context ,
110+ viewport ,
111+ }).promise
112+ }
92113
93114const fileSizeBytes = computed (() => getResourceFilesize (props .resource ))
94115
@@ -105,7 +126,6 @@ const shouldLoadPdf = computed(() => {
105126})
106127
107128const loadPdf = async () => {
108- // Check if file is too large or size is unknown before loading
109129 if (! shouldLoadPdf .value ) {
110130 fileTooLarge .value = true
111131 return
@@ -115,65 +135,41 @@ const loadPdf = async () => {
115135 error .value = null
116136
117137 try {
118- // Test if the PDF URL is accessible
119- const response = await fetch (props .resource .url , { method: ' HEAD' })
120- // const response = await fetch('/test-data.pdf') // For testing locally without CORS issues
121- if (! response .ok ) {
122- throw new Error (` HTTP error! status: ${response .status } ` )
123- }
138+ const loadingTask = pdfjsLib .getDocument ({
139+ url: props .resource .url ,
140+ isEvalSupported: false ,
141+ })
142+
143+ pdfDoc = await loadingTask .promise
144+ totalPages .value = pdfDoc .numPages
145+ pdfReady .value = true
124146
125- // If the URL is accessible, set pdfData to true
126- // The PDF component will handle the actual loading
127- pdfData .value = true
147+ await nextTick ()
148+
149+ for (let i = 1 ; i <= pdfDoc .numPages ; i ++ ) {
150+ await renderPage (i )
151+ }
128152 }
129153 catch (err ) {
130- console .error (' Error testing PDF URL :' , err )
154+ console .error (' Error loading PDF:' , err )
131155
132156 if (err instanceof TypeError ) {
133157 error .value = ' network'
134158 }
135159 else {
136160 error .value = ' generic'
137161 }
138-
139- pdfData .value = false
140162 }
141163 finally {
142164 loading .value = false
143165 }
144166}
145167
146- // Event handlers for PDF component
147- const handleProgress = (loadRatio : number ) => {
148- console .log (` PDF loading progress: ${loadRatio }% ` )
149- }
150-
151- const handleComplete = () => {
152- console .log (' PDF download completed' )
153- }
154-
155- const handlePageChange = (page : number ) => {
156- console .log (` PDF page changed to: ${page } ` )
157- }
158-
159- const handlePdfInit = (pdf : unknown ) => {
160- console .log (' PDF initialized:' , pdf )
161- }
162-
163- const handleError = (err : unknown ) => {
164- console .error (' PDF loading error:' , err )
165-
166- if (err instanceof TypeError ) {
167- error .value = ' network'
168- }
169- else {
170- error .value = ' generic'
171- }
172-
173- pdfData .value = false
174- }
175-
176168onMounted (() => {
177169 loadPdf ()
178170})
171+
172+ onBeforeUnmount (() => {
173+ pdfDoc ?.destroy ()
174+ })
179175 </script >
0 commit comments