Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write

steps:
- name: Check out repository
Expand All @@ -63,18 +64,32 @@ jobs:
with:
node-version: 24
cache: npm
registry-url: https://registry.npmjs.org

- name: Install dependencies
run: npm ci

- name: Derive release version
id: version
shell: bash
run: |
BASE_VERSION="$(node -p "require('./package.json').version")"
RELEASE_VERSION="$(node -e "const [major, minor] = process.argv[1].split('.'); process.stdout.write([major, minor, process.argv[2]].join('.'));" "$BASE_VERSION" "${GITHUB_RUN_NUMBER}")"

echo "base_version=${BASE_VERSION}" >> "$GITHUB_OUTPUT"
echo "release_version=${RELEASE_VERSION}" >> "$GITHUB_OUTPUT"

- name: Apply release version
run: npm version "${{ steps.version.outputs.release_version }}" --no-git-tag-version

- name: Build package
run: npm run build

- name: Create npm package
id: package
shell: bash
run: |
VERSION="$(node -p "require('./package.json').version")"
VERSION="${{ steps.version.outputs.release_version }}"
SHORT_SHA="${GITHUB_SHA::7}"
TAG="v${VERSION}-${SHORT_SHA}"
PACKAGE_TGZ="$(npm pack --silent)"
Expand All @@ -84,22 +99,25 @@ jobs:
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "package_tgz=${PACKAGE_TGZ}" >> "$GITHUB_OUTPUT"

- name: Publish to npm
run: npm publish --access public --provenance

- name: Create GitHub release
env:
GH_TOKEN: ${{ github.token }}
shell: bash
run: |
gh release create "${{ steps.package.outputs.tag }}" \
"${{ steps.package.outputs.package_tgz }}" \
qr-scanner-worker.min.js \
qr-scanner-worker.min.js.map \
qr-scanner.legacy.min.js \
qr-scanner.legacy.min.js.map \
qr-scanner.min.js \
qr-scanner.min.js.map \
qr-scanner.umd.min.js \
qr-scanner.umd.min.js.map \
types/qr-scanner.d.ts \
artifacts/qr-scanner-worker.min.js \
artifacts/qr-scanner-worker.min.js.map \
artifacts/qr-scanner.legacy.min.js \
artifacts/qr-scanner.legacy.min.js.map \
artifacts/qr-scanner.min.js \
artifacts/qr-scanner.min.js.map \
artifacts/qr-scanner.umd.min.js \
artifacts/qr-scanner.umd.min.js.map \
artifacts/types/qr-scanner.d.ts \
--target "${GITHUB_SHA}" \
--title "qr-scanner v${{ steps.package.outputs.version }} (${{ steps.package.outputs.short_sha }})" \
--generate-notes
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
node_modules
.DS_Store
tmp
tmp
artifacts/*
!artifacts/.gitkeep
54 changes: 26 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# QR Scanner

Javascript QR Code Scanner based on [Cosmo Wolfe's javascript port](https://github.com/cozmo/jsqr) of [Google's ZXing library](https://github.com/zxing/zxing).
JavaScript QR Code Scanner based on [Cosmo Wolfe's javascript port](https://github.com/cozmo/jsqr) of [Google's ZXing library](https://github.com/zxing/zxing); see also the orphaned upstream npm package [`qr-scanner`](https://www.npmjs.com/package/qr-scanner).

In this library, several improvements have been applied over the original port:

Expand All @@ -15,63 +15,61 @@ According to [our benchmarking](https://github.com/danimoh/qr-scanner-benchmark)

The library supports scanning a continuous video stream from a web cam as well as scanning of single images.

The development of this library is sponsored by [nimiq](https://www.nimiq.com), world's first browser based blockchain.

[<img src="https://nimiq.github.io/qr-scanner/nimiq_logo_rgb_horizontal.svg" alt="nimiq.com" width="250">](https://nimiq.com)


## Demo
See [https://nimiq.github.io/qr-scanner/demo/](https://nimiq.github.io/qr-scanner/demo/)

## Installation

To install via npm:
Install from npm if you bundle your application:
```bash
npm install --save qr-scanner
npm install @codeuctivity/qr-scanner
```
Or simply copy `qr-scanner.min.js` and `qr-scanner-worker.min.js` to your project.

Then import it from your application code:
```js
import QrScanner from '@codeuctivity/qr-scanner';
```

If you are not using a bundler, copy `artifacts/qr-scanner.min.js` and `artifacts/qr-scanner-worker.min.js` from the published package (or a GitHub release) into your project and keep both files next to each other.

## Setup

The QR Scanner consists of two main files. `qr-scanner.min.js` is the main API file which loads the worker script `qr-scanner-worker.min.js` via a dynamic import, only if needed. If you are not using a bundler like Rollup or Webpack that handles dynamic imports automatically, you might have to copy `qr-scanner-worker.min.js` over to your dist, next to `qr-scanner.min.js` or next to the script into which you're bundling `qr-scanner.min.js`.
The QR Scanner consists of two main files. `artifacts/qr-scanner.min.js` is the main API file which loads the worker script `artifacts/qr-scanner-worker.min.js` via a dynamic import, only if needed. If you are not using a bundler like Rollup or Webpack that handles dynamic imports automatically, you might have to copy `artifacts/qr-scanner-worker.min.js` over to your dist, next to `artifacts/qr-scanner.min.js` or next to the script into which you're bundling `artifacts/qr-scanner.min.js`.

`qr-scanner.min.js` is an es6 module and can be imported as follows:
`artifacts/qr-scanner.min.js` is an es6 module and can be imported as follows:
```js
import QrScanner from 'path/to/qr-scanner.min.js'; // if using plain es6 import
import QrScanner from 'qr-scanner'; // if installed via package and bundling with a module bundler like webpack or rollup
import QrScanner from 'path/to/artifacts/qr-scanner.min.js'; // if using plain es6 import
import QrScanner from '@codeuctivity/qr-scanner'; // if installed via package and bundling with a module bundler like webpack or rollup
```
This requires the importing script to also be an es6 module or a module script tag, e.g.:
```html
<script type="module">
import QrScanner from 'path/to/qr-scanner.min.js';
import QrScanner from 'path/to/artifacts/qr-scanner.min.js';
// do something with QrScanner
</script>
```

If your project is not based on es6 modules you can
- use a [dynamic import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Dynamic_Imports) to import the es6 module:
```js
import('path/to/qr-scanner.min.js').then((module) => {
import('path/to/artifacts/qr-scanner.min.js').then((module) => {
const QrScanner = module.default;
// do something with QrScanner
});
```
- use the [UMD build](https://github.com/umdjs/umd) `qr-scanner.umd.min.js` for direct usage as non-module script
- use the [UMD build](https://github.com/umdjs/umd) `artifacts/qr-scanner.umd.min.js` for direct usage as non-module script
```html
<script src="path/to/qr-scanner.umd.min.js"></script>
<script src="path/to/artifacts/qr-scanner.umd.min.js"></script>
<script>
// do something with QrScanner
</script>
```
- bundle `qr-scanner.umd.min.js` directly with your non-module code with tools like [gulp](https://gulpjs.com/) or [grunt](https://gruntjs.com/).
- bundle `artifacts/qr-scanner.umd.min.js` directly with your non-module code with tools like [gulp](https://gulpjs.com/) or [grunt](https://gruntjs.com/).
- bundle the lib with `require` based bundlers like [browserify](https://browserify.org/):
```js
const QrScanner = require('qr-scanner'); // if installed via package
const QrScanner = require('path/to/qr-scanner.umd.min.js'); // if not installed via package
const QrScanner = require('@codeuctivity/qr-scanner'); // if installed via package
const QrScanner = require('path/to/artifacts/qr-scanner.umd.min.js'); // if not installed via package
// do something with QrScanner
```

This library uses ECMAScript 2017 features like `async` functions. If you need to support old browsers, you can use `qr-scanner.legacy.min.js`, which is ECMAScript 2015 (ES6) compatible. It's a UMD build and can be used as a replacement for `qr-scanner.umd.min.js`, see above. Note, that the legacy build is larger as it includes some polyfills and, to support browsers that don't support dynamic imports, inlines the worker script which however would be needed to be loaded in legacy browsers anyway. You will likely not need to use the legacy build though, as general browser support is already very good for the regular build. Especially if you want to scan from the device's camera, camera support by the browser is the stricter restriction.
This library uses ECMAScript 2017 features like `async` functions. If you need to support old browsers, you can use `artifacts/qr-scanner.legacy.min.js`, which is ECMAScript 2015 (ES6) compatible. It's a UMD build and can be used as a replacement for `artifacts/qr-scanner.umd.min.js`, see above. Note that the legacy build is larger because it includes polyfills and, to support browsers that don't support dynamic imports, inlines the worker script which would have to be loaded separately in legacy browsers anyway. You will likely not need to use the legacy build though, as general browser support is already very good for the regular build. Especially if you want to scan from the device's camera, camera support by the browser is the stricter restriction.

## Usage

Expand Down Expand Up @@ -111,8 +109,8 @@ Supported options are:
| `preferredCamera` | Preference for the camera to be used. The preference can be either a device id as returned by `listCameras` or a facing mode specified as `'environment'` or `'user'`. The default is `'environment'`. Note that there is no guarantee that the preference can actually be fulfilled. |
| `maxScansPerSecond` | This option can be used to throttle the scans for less battery consumption. The default is 25. [If supported by the browser](https://caniuse.com/mdn-api_htmlvideoelement_requestvideoframecallback), the scan rate is never higher than the camera's frame rate to avoid unnecessary duplicate scans on the same frame. |
| `calculateScanRegion` | A method that determines a region to which scanning should be restricted as a performance improvement. This region can optionally also be scaled down before performing the scan as an additional performance improvement. The region is specified as `x`, `y`, `width` and `height`; the dimensions for the downscaled region as `downScaledWidth` and `downScaledHeight`. Note that the aspect ratio between `width` and `height` and `downScaledWidth` and `downScaledHeight` should remain the same. By default, the scan region is restricted to a centered square of two thirds of the video width or height, whichever is smaller, and scaled down to a 400x400 square. |
| `highlightScanRegion` | Set this option to `true` for rendering an outline around the scan region on the video stream. This uses an absolutely positioned `div` that covers the scan region. This `div` can either be supplied as option `overlay`, see below, or automatically created and then accessed via `qrScanner.$overlay`. It can be freely styled via CSS, e.g. by setting an outline, border, background color, etc. See the [demo](https://nimiq.github.io/qr-scanner/demo/) for examples. |
| `highlightCodeOutline` | Set this option to `true` for rendering an outline around detected QR codes. This uses an absolutely positioned `div` on which an SVG for rendering the outline will be placed. This `div` can either be supplied as option `overlay`, see below, or be accessed via `qrScanner.$overlay`. The SVG can be freely styled via CSS, e.g. by setting the fill color, stroke color, stroke width, etc. See the [demo](https://nimiq.github.io/qr-scanner/demo/) for examples. For more special needs, you can also use the `cornerPoints` directly, see below, for rendering an outline or the points yourself. |
| `highlightScanRegion` | Set this option to `true` for rendering an outline around the scan region on the video stream. This uses an absolutely positioned `div` that covers the scan region. This `div` can either be supplied as option `overlay`, see below, or automatically created and then accessed via `qrScanner.$overlay`. It can be freely styled via CSS, e.g. by setting an outline, border, background color, etc. |
| `highlightCodeOutline` | Set this option to `true` for rendering an outline around detected QR codes. This uses an absolutely positioned `div` on which an SVG for rendering the outline will be placed. This `div` can either be supplied as option `overlay`, see below, or be accessed via `qrScanner.$overlay`. The SVG can be freely styled via CSS, e.g. by setting the fill color, stroke color, stroke width, etc. For more special needs, you can also use the `cornerPoints` directly, see below, for rendering an outline or the points yourself. |
| `overlay` | A custom `div` that can be supplied for use for `highlightScanRegion` and `highlightCodeOutline`. The `div` should be a sibling of `videoElem` in the DOM. If this option is supplied, the default styles for `highlightCodeOutline` are not applied as the expectation is that the element already has some custom style applied to it. |
| `returnDetailedScanResult` | Enforce reporting detailed scan results, see below. |

Expand Down Expand Up @@ -245,8 +243,8 @@ This will stop the camera stream and web worker and cleans up event listeners.
The QR scanner will be dysfunctional after it has been destroyed.

## Build the project
The project is prebuild in qr-scanner.min.js in combination with qr-scanner-worker.min.js. Building yourself is only necessary if you want to change the code in
the /src folder. NodeJs is required for building.
The published npm package (and GitHub releases) include the prebuilt artifacts under `artifacts/` (e.g. `artifacts/qr-scanner.min.js` and `artifacts/qr-scanner-worker.min.js`).
To generate them locally from this repo checkout (required for the demo and integration tests), run the build steps below. NodeJs is required for building.

Install required build packages:
```batch
Expand Down
Empty file added artifacts/.gitkeep
Empty file.
6 changes: 3 additions & 3 deletions demo/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,10 @@ <h1>Scan from File:</h1>
<b>Detected QR code: </b>
<span id="file-qr-result">None</span>

<!--<script src="../qr-scanner.umd.min.js"></script>-->
<!--<script src="../qr-scanner.legacy.min.js"></script>-->
<!--<script src="../artifacts/qr-scanner.umd.min.js"></script>-->
<!--<script src="../artifacts/qr-scanner.legacy.min.js"></script>-->
<script type="module">
import QrScanner from "../qr-scanner.min.js";
import QrScanner from "../artifacts/qr-scanner.min.js";

Comment on lines 67 to 69
const video = document.getElementById('qr-video');
const videoContainer = document.getElementById('video-container');
Expand Down
1 change: 0 additions & 1 deletion nimiq_logo_rgb_horizontal.svg

This file was deleted.

8 changes: 4 additions & 4 deletions package-lock.json

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

42 changes: 24 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,32 @@
{
"name": "qr-scanner",
"version": "1.4.2",
"name": "@codeuctivity/qr-scanner",
"version": "1.5.0",
"description": "A javascript QR scanner library",
"packageManager": "npm@11.6.2",
"main": "qr-scanner.umd.min.js",
"module": "qr-scanner.min.js",
"types": "types/qr-scanner.d.ts",
"main": "artifacts/qr-scanner.umd.min.js",
"module": "artifacts/qr-scanner.min.js",
"types": "artifacts/types/qr-scanner.d.ts",
"files": [
"LICENSE",
"qr-scanner.min.js",
"qr-scanner.min.js.map",
"qr-scanner.umd.min.js",
"qr-scanner.umd.min.js.map",
"qr-scanner.legacy.min.js",
"qr-scanner.legacy.min.js.map",
"qr-scanner-worker.min.js",
"qr-scanner-worker.min.js.map",
"types/qr-scanner.d.ts"
"artifacts/qr-scanner.min.js",
"artifacts/qr-scanner.min.js.map",
"artifacts/qr-scanner.umd.min.js",
"artifacts/qr-scanner.umd.min.js.map",
"artifacts/qr-scanner.legacy.min.js",
"artifacts/qr-scanner.legacy.min.js.map",
"artifacts/qr-scanner-worker.min.js",
"artifacts/qr-scanner-worker.min.js.map",
"artifacts/types/qr-scanner.d.ts"
],
"scripts": {
"build": "rollup --config && tsc src/qr-scanner.ts --target esnext --module esnext --declaration --declarationDir types --emitDeclarationOnly",
"build": "rollup --config && tsc src/qr-scanner.ts --target esnext --module esnext --declaration --declarationDir artifacts/types --emitDeclarationOnly",
"prepack": "npm run build",
"pack:local": "npm pack --pack-destination artifacts",
"test:integration": "playwright test"
Comment on lines +22 to 25
},
"repository": {
"type": "git",
"url": "git+https://github.com/nimiq/qr-scanner.git"
"url": "git+https://github.com/Codeuctivity/qr-scanner.git"
},
"keywords": [
"qr",
Expand All @@ -40,9 +42,13 @@
"author": "Nimiq (https://nimiq.com)",
"license": "MIT",
"bugs": {
"url": "https://github.com/nimiq/qr-scanner/issues"
"url": "https://github.com/Codeuctivity/qr-scanner/issues"
},
"homepage": "https://github.com/Codeuctivity/qr-scanner#readme",
"publishConfig": {
"access": "public",
"provenance": true
},
"homepage": "https://github.com/nimiq/qr-scanner#readme",
"dependencies": {
"@types/offscreencanvas": "^2019.6.4"
},
Expand Down
2 changes: 1 addition & 1 deletion playwright/fixtures/qr-camera-harness.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<video id="video" muted playsinline autoplay></video>

<script type="module">
import QrScanner from '/qr-scanner.min.js';
import QrScanner from '/artifacts/qr-scanner.min.js';

QrScanner._disableBarcodeDetector = true;

Expand Down
Loading
Loading