Skip to content

Commit

Permalink
Feature/add rgba array support (#3124)
Browse files Browse the repository at this point in the history
Co-authored-by: Antti Palola <[email protected]>
Co-authored-by: Lukas Hollaender <[email protected]>
  • Loading branch information
3 people authored Sep 9, 2021
1 parent 54a0ce9 commit b6ed102
Show file tree
Hide file tree
Showing 9 changed files with 245 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import "./modules/png_support.js";
import "./modules/gif_support.js";
import "./modules/bmp_support.js";
import "./modules/webp_support.js";
import "./modules/rgba_support.js";
import "./modules/setlanguage.js";
import "./modules/split_text_to_size.js";
import "./modules/standard_fonts_metrics.js";
Expand Down
25 changes: 23 additions & 2 deletions src/modules/addimage.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ import { atob, btoa } from "../libs/AtobBtoa.js";
var compareResult;
var fileType;

if (
fallbackFormat === "RGBA" ||
(imageData.data !== undefined &&
imageData.data instanceof Uint8ClampedArray &&
"height" in imageData &&
"width" in imageData)
) {
return "RGBA";
}

if (isArrayBufferView(imageData)) {
for (fileType in imageFileTypeHeaders) {
headerSchemata = imageFileTypeHeaders[fileType];
Expand Down Expand Up @@ -351,6 +361,8 @@ import { atob, btoa } from "../libs/AtobBtoa.js";
var generateAliasFromImageData = function(imageData) {
if (typeof imageData === "string" || isArrayBufferView(imageData)) {
return sHashCode(imageData);
} else if (isArrayBufferView(imageData.data)) {
return sHashCode(imageData.data);
}

return null;
Expand Down Expand Up @@ -759,13 +771,22 @@ import { atob, btoa } from "../libs/AtobBtoa.js";
return out;
});

/**
* Possible parameter for addImage, an RGBA buffer with size.
*
* @typedef {Object} RGBAData
* @property {Uint8ClampedArray} data - Single dimensional array of RGBA values. For example from canvas getImageData.
* @property {number} width - Image width as the data does not carry this information in itself.
* @property {number} height - Image height as the data does not carry this information in itself.
*/

/**
* Adds an Image to the PDF.
*
* @name addImage
* @public
* @function
* @param {string|HTMLImageElement|HTMLCanvasElement|Uint8Array} imageData imageData as base64 encoded DataUrl or Image-HTMLElement or Canvas-HTMLElement
* @param {string|HTMLImageElement|HTMLCanvasElement|Uint8Array|RGBAData} imageData imageData as base64 encoded DataUrl or Image-HTMLElement or Canvas-HTMLElement or object containing RGBA array (like output from canvas.getImageData).
* @param {string} format format of file if filetype-recognition fails or in case of a Canvas-Element needs to be specified (default for Canvas is JPEG), e.g. 'JPEG', 'PNG', 'WEBP'
* @param {number} x x Coordinate (in units declared at inception of PDF document) against left edge of the page
* @param {number} y y Coordinate (in units declared at inception of PDF document) against upper edge of the page
Expand Down Expand Up @@ -889,7 +910,7 @@ import { atob, btoa } from "../libs/AtobBtoa.js";
if (!result) {
if (supportsArrayBuffer()) {
// no need to convert if imageData is already uint8array
if (!(imageData instanceof Uint8Array)) {
if (!(imageData instanceof Uint8Array) && format !== "RGBA") {
dataAsBinaryString = imageData;
imageData = binaryStringToUint8Array(imageData);
}
Expand Down
81 changes: 81 additions & 0 deletions src/modules/rgba_support.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @license
*
* Copyright (c) 2021 Antti Palola, https://github.com/Pantura
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
* ====================================================================
*/

import { jsPDF } from "../jspdf.js";

/**
* jsPDF RGBA array PlugIn
* @name rgba_support
* @module
*/
(function(jsPDFAPI) {
"use strict";

/**
* @name processRGBA
* @function
*
* Process RGBA Array. This is a one-dimension array with pixel data [red, green, blue, alpha, red, green, ...].
* RGBA array data can be obtained from DOM canvas getImageData.
* @ignore
*/
jsPDFAPI.processRGBA = function(imageData, index, alias) {
"use strict";

var imagePixels = imageData.data;
var length = imagePixels.length;
// jsPDF takes alpha data separately so extract that.
var rgbOut = new Uint8Array((length / 4) * 3);
var alphaOut = new Uint8Array(length / 4);
var outIndex = 0;
var alphaIndex = 0;

for (var i = 0; i < length; i += 4) {
var r = imagePixels[i];
var g = imagePixels[i + 1];
var b = imagePixels[i + 2];
var alpha = imagePixels[i + 3];
rgbOut[outIndex++] = r;
rgbOut[outIndex++] = g;
rgbOut[outIndex++] = b;
alphaOut[alphaIndex++] = alpha;
}

var rgbData = this.__addimage__.arrayBufferToBinaryString(rgbOut);
var alphaData = this.__addimage__.arrayBufferToBinaryString(alphaOut);

return {
alpha: alphaData,
data: rgbData,
index: index,
alias: alias,
colorSpace: "DeviceRGB",
bitsPerComponent: 8,
width: imageData.width,
height: imageData.height
};
};
})(jsPDF.API);
Binary file added test/reference/blackpixel_rgba.pdf
Binary file not shown.
Binary file added test/reference/rgba.pdf
Binary file not shown.
Binary file added test/reference/rgba_alpha.pdf
Binary file not shown.
96 changes: 96 additions & 0 deletions test/specs/rgba.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
/* global jsPDF */
/**
* Standard spec tests
*/

describe("Module: RGBASupport", () => {
beforeAll(loadGlobals);
it("black pixel", () => {
var blackpixel = new Uint8ClampedArray([0, 0, 0, 255]);
var blackpixelData = {
data: blackpixel,
width: 1,
height: 1
};

const doc = new jsPDF({
orientation: "p",
unit: "pt",
format: "a4",
floatPrecision: 2
});
doc.addImage(blackpixelData, "RGBA", 15, 40, 1, 1);

comparePdf(doc.output(), "blackpixel_rgba.pdf", "addimage");
});

it("without format", () => {
var blackpixel = new Uint8ClampedArray([0, 0, 0, 255]);
var blackpixelData = {
data: blackpixel,
width: 1,
height: 1
};

const doc = new jsPDF({
orientation: "p",
unit: "pt",
format: "a4",
floatPrecision: 2
});
doc.addImage(blackpixelData, "RGBA", 15, 40, 1, 1);

comparePdf(doc.output(), "blackpixel_rgba.pdf", "addimage");
});

if (
(typeof isNode === "undefined" || !isNode) &&
navigator.userAgent.indexOf("Chrome") >= 0
) {
it("from canvas", () => {
const c = document.createElement("canvas");
const ctx = c.getContext("2d");
ctx.fillStyle = "#FF6600";
ctx.fillRect(0, 0, 150, 75);
const dataFromCanvas = ctx.getImageData(0, 0, 150, 75);
const doc = new jsPDF({
orientation: "p",
unit: "pt",
format: "a4",
floatPrecision: 2
});
doc.addImage(
dataFromCanvas,
"RGBA",
100,
200,
280,
210,
undefined,
undefined
);

comparePdf(doc.output(), "rgba.pdf", "addimage");
});

it("with alpha", () => {
const c = document.createElement("canvas");
const ctx = c.getContext("2d");
ctx.fillStyle = "#FFFFFF";
ctx.fillRect(0, 0, 150, 60);
ctx.fillStyle = "#AA00FF77";
ctx.fillRect(10, 10, 130, 40);
const dataFromCanvas = ctx.getImageData(0, 0, 150, 60);

const doc = new jsPDF({
orientation: "p",
unit: "px",
format: "a4",
floatPrecision: 2
});
doc.addImage(dataFromCanvas, 10, 10, 150, 60);

comparePdf(doc.output(), "rgba_alpha.pdf", "addimage");
});
}
});
28 changes: 25 additions & 3 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -499,7 +499,12 @@ declare module "jspdf" {
| "DeviceN";

export interface ImageOptions {
imageData: string | HTMLImageElement | HTMLCanvasElement | Uint8Array;
imageData:
| string
| HTMLImageElement
| HTMLCanvasElement
| Uint8Array
| RGBAData;
x: number;
y: number;
width: number;
Expand Down Expand Up @@ -646,6 +651,13 @@ declare module "jspdf" {
yStep?: number;
}

// Single dimensional array of RGBA values. For example from canvas getImageData.
export interface RGBAData {
data: Uint8ClampedArray;
width: number;
height: number;
}

export interface PubSub {
subscribe(
topic: string,
Expand Down Expand Up @@ -920,7 +932,12 @@ declare module "jspdf" {

// jsPDF plugin: addImage
addImage(
imageData: string | HTMLImageElement | HTMLCanvasElement | Uint8Array,
imageData:
| string
| HTMLImageElement
| HTMLCanvasElement
| Uint8Array
| RGBAData,
format: string,
x: number,
y: number,
Expand All @@ -931,7 +948,12 @@ declare module "jspdf" {
rotation?: number
): jsPDF;
addImage(
imageData: string | HTMLImageElement | HTMLCanvasElement | Uint8Array,
imageData:
| string
| HTMLImageElement
| HTMLCanvasElement
| Uint8Array
| RGBAData,
x: number,
y: number,
w: number,
Expand Down
19 changes: 19 additions & 0 deletions types/jspdf-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -649,3 +649,22 @@ function test_nullStyleArgument() {
doc.ellipse(0, 0, 0, 0, null);
doc.circle(0, 0, 0, null);
}

function test_addImageWithRGBAData() {
const doc = new jsPDF();
const rgbaData = new Uint8ClampedArray(16);
const imageData = {
data: rgbaData,
width: 2,
height: 2
};

doc.addImage({
imageData: imageData,
x: 0,
y: 0,
width: 100,
height: 100,
compression: "FAST"
});
}

0 comments on commit b6ed102

Please sign in to comment.