Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/add rgba array support #3124

Merged
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
3 changes: 2 additions & 1 deletion src/libs/FileSaver.js
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ var saveAs =
/* noop */
}
: // Use download attribute first if possible (#193 Lumia mobile) unless this is a native app
(typeof HTMLAnchorElement !== "undefined" && "download" in HTMLAnchorElement.prototype)
typeof HTMLAnchorElement !== "undefined" &&
"download" in HTMLAnchorElement.prototype
? function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement("a");
Expand Down
25 changes: 23 additions & 2 deletions src/modules/addimage.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,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 @@ -346,6 +356,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 @@ -746,13 +758,22 @@ import { atob, btoa } from "../libs/AtobBtoa.js";
}
});

/**
* 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.
*/

Pantura marked this conversation as resolved.
Show resolved Hide resolved
/**
* 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 @@ -876,7 +897,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
5 changes: 4 additions & 1 deletion src/modules/cell.js
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,10 @@ import { jsPDF } from "../jspdf.js";
});
}

if (autoSize || (Array.isArray(headers) && typeof headers[0] === "string")) {
if (
autoSize ||
(Array.isArray(headers) && typeof headers[0] === "string")
) {
var headerName;
for (i = 0; i < headerNames.length; i += 1) {
headerName = headerNames[i];
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);
Comment on lines +67 to +68
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assumed these accepted ArrayBuffer, not Uint8Array given the name...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not give enough thought to these before but the other code paths call it the same way.
If it is a buffer, make it an array, if it is an array, run it through arrayBufferToBinaryString.
https://github.com/MrRio/jsPDF/blob/master/src/modules/jpeg_support.js#L71-L83

All usage should be checked and name changed if that is uniform across all usage.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming of the arrayBufferToBinaryString should imo be changed on a separate PR.


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.
84 changes: 84 additions & 0 deletions test/specs/rgba.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
/* global describe, it, jsPDF, comparePdf, loadBinaryResource */
/**
* 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);
Pantura marked this conversation as resolved.
Show resolved Hide resolved

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

if (typeof isNode === "undefined" || !isNode) {
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";
Pantura marked this conversation as resolved.
Show resolved Hide resolved
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, "RGBA", 10, 10, 150, 60, undefined, undefined);

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