gruhn / vue-qrcode-reader

A set of Vue.js components for detecting and decoding QR codes.
https://gruhn.github.io/vue-qrcode-reader
MIT License
2.03k stars 330 forks source link

Feature Request: PDF-Support for Dropzone #421

Closed paulwer closed 1 month ago

paulwer commented 4 months ago

Hey everyone, would it be possible to add support to analyze detection of qr-codes within pdf-files. We have protocolls as pdfs, where a qr-code sticker is glued on the bottom of the page and is afterwards scanned into pdf-file.

This would be very amazing, if its possible.

https://stackoverflow.com/questions/12921052/parsing-pdf-pages-as-javascript-images

gruhn commented 4 months ago

Puh, that sounds a bit out-of-scope. I think it's not worth pulling in a PDF parsing dependency for this feature. But you can easily implement this yourself. QrcodeDropZone is not a complicated component. The main complexity of this library comes from QrcodeStream.

Haven't tested (or even type checked) this at all but if I just inline all internal dependencies of QrcodeDropZone I get the following component (and you can probably remove a good chunk of that):

<template>
  <div
    @drop.prevent.stop="onDrop"
    @dragenter.prevent.stop="onDragOver(true)"
    @dragleave.prevent.stop="onDragOver(false)"
    @dragover.prevent.stop
  >
    <slot></slot>
  </div>
</template>

<script setup lang="ts">
import { type DetectedBarcode, type BarcodeFormat, BarcodeDetector } from 'barcode-detector/pure' 
import { type PropType } from 'vue'
import { type BarcodeFormat } from 'barcode-detector/pure'

const props = defineProps({
  formats: {
    type: Array as PropType<BarcodeFormat[]>,
    default: () => ['qr_code'] as BarcodeFormat[]
  }
})

const emit = defineEmits(['detect', 'dragover', 'error'])

// methods
export const eventOn = (
  eventTarget: EventTarget,
  successEvent: string,
  errorEvent = 'error'
): Promise<Event> => {
  let $resolve: (value: Event) => void
  let $reject: (reason?: Event) => void

  const promise = new Promise(
    (resolve: (value: Event) => void, reject: (reason?: Event) => void) => {
      $resolve = resolve
      $reject = reject

      eventTarget.addEventListener(successEvent, $resolve)
      eventTarget.addEventListener(errorEvent, $reject)
    }
  )

  promise.finally(() => {
    eventTarget.removeEventListener(successEvent, $resolve)
    eventTarget.removeEventListener(errorEvent, $reject)
  })

  return promise
}

const imageElementFromUrl = async (url: string) => {
  if (url.startsWith('http') && url.includes(location.host) === false) {
    throw new DropImageFetchError()
  }

  const image = document.createElement('img')
  image.src = url

  await eventOn(image, 'load')

  return image
}

export const processFile = async (
  file: File,
  formats: BarcodeFormat[] = ['qr_code']
): Promise<DetectedBarcode[]> => {
  const barcodeDetector = new BarcodeDetector({
    formats
  })

  return await barcodeDetector.detect(file)
}

export const processUrl = async (
  url: string,
  formats: BarcodeFormat[] = ['qr_code']
): Promise<DetectedBarcode[]> => {
  const barcodeDetector = new BarcodeDetector({
    formats
  })

  const image = await imageElementFromUrl(url)

  return await barcodeDetector.detect(image)
}

const onDetect = async (promise: Promise<any>) => {
  try {
    const detectedCodes = await promise
    emit('detect', detectedCodes)
  } catch (error) {
    emit('error', error)
  }
}

const onDragOver = (isDraggingOver: boolean) => {
  emit('dragover', isDraggingOver)
}

const onDrop = ({ dataTransfer }: DragEvent) => {
  if (!dataTransfer) return

  onDragOver(false)

  const droppedFiles = [...Array.from(dataTransfer.files)]
  const droppedUrl = dataTransfer.getData('text/uri-list')

  droppedFiles.forEach((file: File) => {
    onDetect(processFile(file))
  })

  if (droppedUrl !== '') {
    onDetect(processUrl(droppedUrl, props.formats))
  }
}
</script>
paulwer commented 4 months ago

Thank you for your response. https://www.npmjs.com/package/pdf-img-convert it has Unpacked Size of 12.9 kB, so maybe this could be suiteable, without adding a huge overhead, like pdf.js

would this be an option or do you dont wanna use any other packages? I think such feature would let other users also benefit :)

gruhn commented 4 months ago

Sorry man, I think this is too edge case-y. So I think the dependency is not worth it. We could think of some mechanism to intercept file processing fron the outside, but that would probably be more complicated then implementing the component yourself.

We could leave this issue open for other users to give their two cents but the issue will be auto-closed in a month or so.

github-actions[bot] commented 2 months ago

This issue has been marked as stale. If there is no further activity it will be closed.