Skip to content

Commit

Permalink
feat(alpha): implements alpha channel support #35
Browse files Browse the repository at this point in the history
Implements ability to pass in an 8-digit hexadecimal color for
foreground and background to make colors opaque or transparent.
  • Loading branch information
jshor committed Mar 29, 2021
1 parent 9390a49 commit 993699b
Show file tree
Hide file tree
Showing 84 changed files with 220 additions and 86 deletions.
38 changes: 19 additions & 19 deletions docs/docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,25 @@

The following symbology options are available:

| Enumerated type | Type | Meaning | Required? | Default value |
|-------------------------|-----------|----------------------------------------------------------------------------------------|-----------|---------------|
| `symbology` | `Symbol` | The enumerated type of the symbology (see [Reference](../guide/README.md) for a list). | **Yes** | |
| `height` | `Number` | The height of the image. If specified, this will maintain the aspect ratio. | No | `50` |
| `whitespaceWidth` | `Number` | Width of whitespace, for barcodes which have this option. | No | `0` |
| `borderWidth` | `Number` | Width of border. | No | `0` |
| `outputOptions` | `Number` | [Symbology-specific output options.](#output-options) | No | `0` |
| `foregroundColor` | `String` | Barcode foreground color. | No | `FFFFFF` |
| `backgroundColor` | `String` | Barcode background color. | No | `000000` |
| `fileName` | `String` | Full path to the file to render. | **Yes*** | |
| `scale` | `Number` | Scale of the barcode image. Applies only to PNG. | No | `1.0` |
| `option1` | `Number` | Symbology-type-specific option value. | No | `null` |
| `option2` | `Number` | Symbology-type-specific option value. | No | `null` |
| `option3` | `Number` | Symbology-type-specific option value. | No | `null` |
| `showHumanReadableText` | `Boolean` | Show or hide the symbology data as human-readable text (if applicable). | No | `true` |
| `encoding` | `Encoding`| [The enumerated encoding type of input data.](encoding.md#encoding-modes) | No | `DATA_MODE` |
| `eci` | `Number` | [ECI encoding mode.](encoding.md#extended-channel-interpolation-eci) | No | `0` |
| `primary` | `String` | Primary message data for more complex symbols. | No | `null` |
| `text` | `String` | Human-readable text to display. Defaults to the input data value. | No | (Data value) |
| Enumerated type | Type | Meaning | Required? | Default value |
|-------------------------|------------|--------------------------------------------------------------------------------------------------|-----------|---------------|
| `symbology` | `Symbol` | The enumerated type of the symbology (see [Reference](../guide/README.md) for a list). | **Yes** | |
| `height` | `Number` | The height of the image. If specified, this will maintain the aspect ratio. | No | `50` |
| `whitespaceWidth` | `Number` | Width of whitespace, for barcodes which have this option. | No | `0` |
| `borderWidth` | `Number` | Width of border. | No | `0` |
| `outputOptions` | `Number` | [Symbology-specific output options.](#output-options) | No | `0` |
| `foregroundColor` | `String` | Barcode foreground color. Supports [alpha channels](https://css-tricks.com/8-digit-hex-codes/). | No | `FFFFFFFF` |
| `backgroundColor` | `String` | Barcode background color. Supports [alpha channels](https://css-tricks.com/8-digit-hex-codes/). | No | `000000FF` |
| `fileName` | `String` | Full path to the file to render. | **Yes*** | |
| `scale` | `Number` | Scale of the barcode image. Applies only to PNG. | No | `1.0` |
| `option1` | `Number` | Symbology-type-specific option value. | No | `null` |
| `option2` | `Number` | Symbology-type-specific option value. | No | `null` |
| `option3` | `Number` | Symbology-type-specific option value. | No | `null` |
| `showHumanReadableText` | `Boolean` | Show or hide the symbology data as human-readable text (if applicable). | No | `true` |
| `encoding` | `Encoding` | [The enumerated encoding type of input data.](encoding.md#encoding-modes) | No | `DATA_MODE` |
| `eci` | `Number` | [ECI encoding mode.](encoding.md#extended-channel-interpolation-eci) | No | `0` |
| `primary` | `String` | Primary message data for more complex symbols. | No | `null` |
| `text` | `String` | Human-readable text to display. Defaults to the input data value. | No | (Data value) |

\* required only if using [`createFile()`](api.md#createfile).

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
},
"dependencies": {
"git-clone": "^0.1.0",
"lodash": "^4.17.21",
"nan": "^2.14.2",
"node-pre-gyp": "^0.14.0",
"pngjs": "3.3.0",
Expand Down
6 changes: 3 additions & 3 deletions src/__tests__/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ describe('Symbology Library', () => {
}, '12345', 'png')

expect(png.render).toHaveBeenCalledTimes(1)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height, 'ffffffff', '000000ff')
expect(png.blobToBase64).toHaveBeenCalledTimes(1)
expect(png.blobToBase64).toHaveBeenCalledWith(mockPngImage)

Expand Down Expand Up @@ -81,7 +81,7 @@ describe('Symbology Library', () => {
}, '12345')

expect(png.render).toHaveBeenCalledTimes(1)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height, 'ffffffff', '000000ff')
expect(png.blobToBase64).toHaveBeenCalledTimes(1)
expect(png.blobToBase64).toHaveBeenCalledWith(mockPngImage)

Expand Down Expand Up @@ -115,7 +115,7 @@ describe('Symbology Library', () => {
}, '12345')

expect(png.render).toHaveBeenCalledTimes(1)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height)
expect(png.render).toHaveBeenCalledWith(mockPngRes.data, mockPngRes.width, mockPngRes.height, 'ffffffff', '000000ff')
expect(png.writeFile).toHaveBeenCalledTimes(1)
expect(png.writeFile).toHaveBeenCalledWith(mockPngImage, fileName)
expect(fs.writeFileSync).not.toHaveBeenCalled()
Expand Down
42 changes: 36 additions & 6 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,28 @@ const exp = {
createFile
}

/**
* Default Symbology config, populated with default values
*/
const defaultConfig = {
symbology: 20,
height: 50,
whitespaceWidth: 0,
borderWidth: 0,
outputOptions: -1,
foregroundColor: '000000ff',
backgroundColor: 'ffffffff',
fileName: 'out.bmp',
scale: 1.0,
option1: -1,
option2: -1,
option3: -1,
showHumanReadableText: true,
encoding: exp.Encoding.DATA_MODE,
eci: 0,
primary: ''
}

/**
* Creates an in-memory buffer of a PNG, SVG or EPS image.
*
Expand All @@ -23,12 +45,16 @@ const exp = {
* @param {String} outputType - `png`, `eps`, or `svg`.
* @returns {Promise<Object>} object with resulting props (see docs)
*/
async function createStream (symbol, barcodeData, outputType = exp.Output.PNG) {
const res = await binary.invoke(symbol, barcodeData, outputType, true)
async function createStream (config, barcodeData, outputType = exp.Output.PNG) {
const symbol = {
...defaultConfig,
...config
}
const res = await binary.invoke(symbol, barcodeData, outputType)

if (outputType === exp.Output.PNG) {
const pngData = png.render(res.data, res.width, res.height)
const base64Data = await png.blobToBase64(pngData)
const image = png.render(res.data, res.width, res.height, symbol.backgroundColor, symbol.foregroundColor)
const base64Data = await png.blobToBase64(image)

return {
data: base64Data,
Expand All @@ -49,13 +75,17 @@ async function createStream (symbol, barcodeData, outputType = exp.Output.PNG) {
* @param {String} barcodeData - data to encode
* @returns {Promise<Object>} object with resulting props (see docs)
*/
async function createFile (symbol, barcodeData) {
async function createFile (config, barcodeData) {
const symbol = {
...defaultConfig,
...config
}
const outputType = binary.getOutputType(symbol.fileName)
const res = await binary.invoke(symbol, barcodeData, outputType)

if (outputType === exp.Output.PNG) {
// write the bitmap to a PNG image file
const image = png.render(res.data, res.width, res.height)
const image = png.render(res.data, res.width, res.height, symbol.backgroundColor, symbol.foregroundColor)

await png.writeFile(image, symbol.fileName)
} else {
Expand Down
53 changes: 34 additions & 19 deletions src/lib/__tests__/binary.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ describe('Binary methods', () => {
whitespaceWidth: 3,
borderWidth: 5,
outputOptions: 2,
foregroundColor: '00ff00',
backgroundColor: 'ff0000',
foregroundColor: '00ff00ff',
backgroundColor: 'ff0000ff',
fileName: 'myfile.svg',
scale: 1.3,
option1: 1,
Expand Down Expand Up @@ -64,38 +64,53 @@ describe('Binary methods', () => {
)
})

it('should fallback to default values for ones missing from config', () => {
it('should fallback to default values for values missing in config', () => {
jest
.spyOn(symbology, 'createStream')
.mockReturnValue()

const symbol = {
symbology: 10
symbology: 10,
height: 30,
whitespaceWidth: 3,
borderWidth: 5,
outputOptions: 2,
foregroundColor: '00ff00ff',
backgroundColor: 'ff0000ff',
fileName: 'myfile.svg',
scale: 1.3,
option1: 1,
option2: 2,
option3: 3,
showHumanReadableText: true,
encoding: Encoding.DATA_MODE,
eci: 0,
primary: '',
text: ''
}
const barcodeData = 'primary data'

binary.createBuffer(symbol, barcodeData)

expect(symbology.createStream).toHaveBeenCalledWith(
barcodeData,
/* default config values */
symbol.symbology,
50,
0,
0,
-1,
'ffffff',
'000000',
'out.bmp',
1.0,
-1,
-1,
-1,
symbol.height,
symbol.whitespaceWidth,
symbol.borderWidth,
symbol.outputOptions,
symbol.backgroundColor,
symbol.foregroundColor,
symbol.fileName,
symbol.scale,
symbol.option1,
symbol.option2,
symbol.option3,
1,
barcodeData,
Encoding.DATA_MODE,
0,
''
symbol.encoding,
symbol.eci,
symbol.primary
)
})
})
Expand Down
35 changes: 31 additions & 4 deletions src/lib/__tests__/png.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,36 @@ describe('PNG Functions', () => {
})
})

describe('getRgbaColor()', () => {
it('should parse the alpha channel for an 8-digit hex color', () => {
const color = png.getRgbaColor('ff00ff00')

expect(color).toEqual({
red: 255,
green: 0,
blue: 255,
alpha: 0
})
})

it('should parse a 6-digit hex color, with alpha channel falling back to 255', () => {
const color = png.getRgbaColor('ff00ff')

expect(color).toEqual({
red: 255,
green: 0,
blue: 255,
alpha: 255
})
})
})

describe('render()', () => {
const image = PNGImage.createImage(2, 2)
const bitmap = Array(12).fill(0) // 2x2 black bitmap
const bitmap = [
...Array(6).fill(0), // 1x2 black line (top)
...Array(6).fill(255) // 1x2 white line (bottom)
]

beforeEach(() => {
jest
Expand All @@ -59,19 +86,19 @@ describe('PNG Functions', () => {
})

it('should fill in each pixel color from the given bitmap array', () => {
png.render(bitmap, 2, 2)
png.render(bitmap, 2, 2, '000000ff', 'ffffffff')

expect(image.setAt).toHaveBeenCalledTimes(4)
expect(image.setAt).toHaveBeenCalledWith(expect.any(Number), expect.any(Number), {
red: 0,
green: 0,
blue: 0,
alpha: 200
alpha: 255
})
})

it('should return the PNG buffer instance', () => {
const result = png.render(bitmap, 2, 2)
const result = png.render(bitmap, 2, 2, '000000ff', 'ffffffff')

expect(result).toEqual(image)
})
Expand Down
32 changes: 2 additions & 30 deletions src/lib/binary.js
Original file line number Diff line number Diff line change
@@ -1,47 +1,19 @@
const gyp = require('node-pre-gyp')
const path = require('path')
const Encoding = require('../enums/encoding')
const Output = require('../enums/output')
const binding = gyp.find(path.join(__dirname,'../../package.json'))
const symbology = require(binding)

/**
* Default Symbology config, populated with default values
*/
const defaultConfig = {
symbology: 20,
height: 50,
whitespaceWidth: 0,
borderWidth: 0,
outputOptions: -1,
foregroundColor: '000000',
backgroundColor: 'ffffff',
fileName: 'out.bmp',
scale: 1.0,
option1: -1,
option2: -1,
option3: -1,
showHumanReadableText: true,
encoding: Encoding.DATA_MODE,
eci: 0,
primary: ''
}

/**
* Calls the given function name from the c++ library wrapper, validates
* the struct values and passes the arguments sent in symbologyStruct
* in the correct order.
*
* @param {Symbol} config
* @param {Symbol} symbol
* @param {String} barcodeData
* @return {Object}
*/
function createBuffer (config, barcodeData) {
const symbol = {
...defaultConfig,
...config
}

function createBuffer (symbol, barcodeData) {
return symbology.createStream(
barcodeData,
symbol.symbology,
Expand Down

0 comments on commit 993699b

Please sign in to comment.