Skip to content

Commit

Permalink
support for logo in jpeg pormat
Browse files Browse the repository at this point in the history
  • Loading branch information
gugu committed Sep 11, 2023
1 parent b9a4f5e commit 7ddbb7d
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 52 deletions.
96 changes: 49 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,80 @@
@shortcm/qr-image
========
# @shortcm/qr-image

[![npm version](https://badge.fury.io/js/%40shortcm%2Fqr-image.svg)](https://badge.fury.io/js/%40shortcm%2Fqr-image)

QR Code generator for browser and node.js with tree shaking and logo support (based on [alexeyten/qr-image](https://github.com/alexeyten/qr-image)).

![image](https://github.com/Short-io/qr-image/assets/75169/02b84738-56f2-44d8-8d11-f40e263302ed)

## Overview

Overview
--------

* generate image in `png`, `svg` and `pdf` formats
* numeric and alphanumeric modes
* support UTF-8
* supports color customization
* supports logos
* tree shaking support
* browser / node.js
- generate image in `png`, `svg` and `pdf` formats
- numeric and alphanumeric modes
- support UTF-8
- supports color customization
- supports logos
- tree shaking support
- browser / node.js

[Releases](https://github.com/Short-io/qr-image/releases)

Installing
-----
## Installing

```shell
npm install @shortcm/qr-image
# or
yarn add @shortcm/qr-image
```

Usage
-----
## Usage

Example:

```javascript
import { getSVG } from '@shortcm/qr-image/lib/svg';
import { getPNG } from '@shortcm/qr-image/lib/png'; // imports canvas implementation in browser and sharp module in node.js
import { getPDF } from '@shortcm/qr-image/lib/pdf'; // this import is large, consider async import
const svgString = await getSVG('I love QR', { logo: fs.openFileSync('my-logo.svg'), color: '#000000', bgColor: '#FFFFFF'})
const pngBuffer = await getPNG('I love QR', { logo: fs.openFileSync('my-logo.svg'), color: 'rgb(0, 0, 0)', bgColor: 'rgb(255, 255, 255)'})
import { getSVG } from "@shortcm/qr-image/lib/svg";
import { getPNG } from "@shortcm/qr-image/lib/png"; // imports canvas implementation in browser and sharp module in node.js
import { getPDF } from "@shortcm/qr-image/lib/pdf"; // this import is large, consider async import
const svgString = await getSVG("I love QR", {
logo: fs.openFileSync("my-logo.svg"),
color: "#000000",
bgColor: "#FFFFFF",
});
const pngBuffer = await getPNG("I love QR", {
logo: fs.openFileSync("my-logo.svg"),
color: "rgb(0, 0, 0)",
bgColor: "rgb(255, 255, 255)",
});
```

[More examples](./examples)

### Syntax

* `getPNG(text, [options])`: Readable stream with image data.
* `getSVG(text, [options])`: Readable stream with image data.
* `getPDF(text, [options])`: Readable stream with image data.
- `getPNG(text, [options])`: Readable stream with image data.
- `getSVG(text, [options])`: Readable stream with image data.
- `getPDF(text, [options])`: Readable stream with image data.

### Options

* `text`: text to encode
* `options`: additional image options object
- `text`: text to encode
- `options`: additional image options object

#### Additional Options ####
#### Additional Options

| Name | Description | Type | Possible Values | Default |
| :---: | :---: | :---: | :---: | :---: |
| `ec_level` | error correction level | string | `L`, `M`, `Q`, `H` | `M` |
| `type` | image type | string | `png`, `svg`, `pdf` | `png` |
| `size` | png and svg only<br />size of one module in pixels | number | `0` - n | `5` (png)<br />`0` (others) |
| `margin` | white space around QR image in modules | number | `0` - n | `4` (png)<br />`1` (others) |
| `parse_url` | EXPERIMENTAL<br />try to optimize QR-code for URLs | boolean | `true`, `false` | `false` |
| `logo` | buffer with png image | Buffer | - | `undefined` |
| `logoWidth` | height of logo in percent | number | `0` - `100` | `20` |
| `logoHeight` | width of logo in percent | number | `0` - `100` | `20` |
| `color` | module color in rgba or hex format | number | `#000000` - `#000000` | `#000000`<br />(black with 100% opacity) |
| `bgColor` | background color in rgba or hex format | number | `#000000` - `#FFFFFF` | `#FFFFFF`<br />(white with 100% opacity) |
| Name | Description | Type | Possible Values | Default |
| :----------: | :------------------------------------------------: | :---------: | :-------------------: | :--------------------------------------: |
| `ec_level` | error correction level | string | `L`, `M`, `Q`, `H` | `M` |
| `type` | image type | string | `png`, `svg`, `pdf` | `png` |
| `size` | png and svg only<br />size of one module in pixels | number | `0` - n | `5` (png)<br />`0` (others) |
| `margin` | white space around QR image in modules | number | `0` - n | `4` (png)<br />`1` (others) |
| `parse_url` | EXPERIMENTAL<br />try to optimize QR-code for URLs | boolean | `true`, `false` | `false` |
| `logo` | buffer with png/jpeg image | ArrayBuffer | - | `undefined` |
| `logoWidth` | height of logo in percent | number | `0` - `100` | `20` |
| `logoHeight` | width of logo in percent | number | `0` - `100` | `20` |
| `color` | module color in rgba or hex format | number | `#000000` - `#000000` | `#000000`<br />(black with 100% opacity) |
| `bgColor` | background color in rgba or hex format | number | `#000000` - `#FFFFFF` | `#FFFFFF`<br />(white with 100% opacity) |

Benchmarks
----------
## Benchmarks

```
getPNG x 98.36 ops/sec ±0.61% (69 runs sampled)
Expand All @@ -82,10 +85,9 @@ getPDF with logo x 39.29 ops/sec ±8.17% (68 runs sampled)
getSVG with logo x 2,817 ops/sec ±0.17% (89 runs sampled)
```

TODO
----
## TODO

* Use lighter versions of PDF library
* Round corners
* Transparency
* Background
- Use lighter versions of PDF library
- Round corners
- Transparency
- Background
12 changes: 10 additions & 2 deletions src/pdf.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import { PDFDocument, rgb } from "pdf-lib";
import { PDFDocument, PDFImage, rgb } from "pdf-lib";
import { QR } from "./qr-base.js";
import { ImageOptions, Matrix } from "./typing/types";
import { getOptions } from "./utils.js";
import colorString from "color-string";

const textDec = new TextDecoder();

export async function getPDF(text: string, inOptions: ImageOptions) {
const options = getOptions(inOptions);
const matrix = QR(text, options.ec_level, options.parse_url);
Expand Down Expand Up @@ -59,7 +61,13 @@ async function PDF({
page.moveRight(size);
}
if (logo) {
const logoData = await document.embedPng(logo);
let logoData: PDFImage;
const header = new Uint8Array(logo.slice(0, 4));
if (textDec.decode(header.slice(1, 4)) === "PNG" && header.at(0) === 0x89) {
logoData = await document.embedPng(logo);
} else {
logoData = await document.embedJpg(logo);
}
page.drawImage(logoData, {
x: page.getWidth() / 2 - (logoWidth / 100) * (page.getWidth() / 2),
y:
Expand Down
26 changes: 24 additions & 2 deletions src/tests/browser.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,17 @@ const defaultParams = {
},
},
{
name: "PNG with logo",
name: "PNG with logo (PNG)",
type: "png",
filename: "qr_with_logo.png",
params: { logo: readFileSync(`${goldenDir}/logo.png`).buffer },
},
{
name: "PNG with logo (JPG)",
type: "png",
filename: "qr_with_logo_jpg.png",
params: { logo: readFileSync(`${goldenDir}/logo.jpg`).buffer },
},
{
name: "PNG with colors (rgba)",
type: "png",
Expand Down Expand Up @@ -120,13 +126,21 @@ const defaultParams = {
},
},
{
name: "SVG with logo as arraybuffer",
name: "SVG with logo as arraybuffer (PNG)",
type: "svg",
filename: "qr_with_logo_as_arraybuffer.svg",
params: {
logo: readFileSync(`${goldenDir}/logo.png`).buffer,
},
},
{
name: "SVG with logo as arraybuffer (JPG)",
type: "svg",
filename: "qr_with_logo_as_arraybuffer_jpg.svg",
params: {
logo: readFileSync(`${goldenDir}/logo.jpg`).buffer,
},
},
{
name: "PDF",
type: "pdf",
Expand All @@ -146,6 +160,14 @@ const defaultParams = {
logo: new window.Uint8Array(readFileSync(`${goldenDir}/logo.png`).buffer),
},
},
{
name: "PDF with arraybuffer (JPG)",
type: "pdf",
filename: "qr_logo_arraybuffer.pdf",
params: {
logo: new window.Uint8Array(readFileSync(`${goldenDir}/logo.jpg`).buffer),
},
},
] as TestParams[]) .forEach((testData) => {
test(`browser > ${testData.name}`, async (t) => {
const image = await functions[testData.type](text, {
Expand Down
24 changes: 23 additions & 1 deletion src/tests/test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,12 @@ interface TestParams {
filename: "qr_with_logo.png",
params: { logo: await readFile(`${goldenDir}/logo.png`) },
},
{
name: "PNG with logo (JPG)",
fn: getPNG,
filename: "qr_with_logo_jpg.png",
params: { logo: await readFile(`${goldenDir}/logo.jpg`) },
},
{
name: "PNG with logo (arraybuffer)",
fn: getPNG,
Expand Down Expand Up @@ -154,13 +160,21 @@ interface TestParams {
params: { logo: await readFile(`${goldenDir}/logo.png`) },
},
{
name: "SVG with logo as arraybuffer",
name: "SVG with logo as arraybuffer (PNG)",
fn: getSVG,
filename: "qr_with_logo_as_arraybuffer.svg",
params: {
logo: (await readFile(`${goldenDir}/logo.png`)).buffer,
},
},
{
name: "SVG with logo as arraybuffer (JPG)",
fn: getSVG,
filename: "qr_with_logo_as_arraybuffer_jpg.svg",
params: {
logo: (await readFile(`${goldenDir}/logo.jpg`)).buffer,
},
},
{
name: "PDF",
fn: getPDF,
Expand All @@ -186,6 +200,14 @@ interface TestParams {
logo: (await readFile(`${goldenDir}/logo.png`)).buffer,
},
},
{
name: "PDF with arraybuffer (JPG)",
fn: getPDF,
filename: "qr_logo_arraybuffer_jpg.pdf",
params: {
logo: (await readFile(`${goldenDir}/logo.jpg`)).buffer,
},
},
{
name: "PDF with logo",
fn: getPDF,
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test_data/golden/browser_qr_with_logo_jpg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test_data/golden/logo.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions test_data/golden/qr_with_logo_as_arraybuffer_jpg.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added test_data/golden/qr_with_logo_jpg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit 7ddbb7d

Please sign in to comment.