UNPKG

pdf-lib

Version:

Library for creating and modifying PDF files in JavaScript

118 lines (117 loc) 5.97 kB
import pako from 'pako'; import PNG from 'png-ts'; import { PDFArray, PDFDictionary, PDFName, PDFNumber, PDFRawStream, } from '../../pdf-objects'; import { isInstance, validate } from '../../../utils/validate'; /** * A note of thanks to the developers of https://github.com/devongovett/pdfkit, * as this class borrows heavily from: * https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/image/png.coffee */ var PNGXObjectFactory = /** @class */ (function () { function PNGXObjectFactory(data) { var _this = this; this.embedImageIn = function (document) { _this.document = document; _this.xObjDict = PDFDictionary.from({ Type: PDFName.from('XObject'), Subtype: PDFName.from('Image'), BitsPerComponent: PDFNumber.fromNumber(_this.image.bits), Width: PDFNumber.fromNumber(_this.width), Height: PDFNumber.fromNumber(_this.height), Filter: PDFName.from('FlateDecode'), }, document.index); if (!_this.image.hasAlphaChannel) { var params = PDFDictionary.from({ Predictor: PDFNumber.fromNumber(15), Colors: PDFNumber.fromNumber(_this.image.colors), BitsPerComponent: PDFNumber.fromNumber(_this.image.bits), Columns: PDFNumber.fromNumber(_this.width), }, document.index); _this.xObjDict.set('DecodeParms', params); } if (_this.image.palette.length === 0) { _this.xObjDict.set('ColorSpace', PDFName.from(_this.image.colorSpace)); } else { var paletteStream = document.register(PDFRawStream.from(PDFDictionary.from({ Length: PDFNumber.fromNumber(_this.image.palette.length), }, document.index), new Uint8Array(_this.image.palette))); _this.xObjDict.set('ColorSpace', PDFArray.fromArray([ PDFName.from('Indexed'), PDFName.from('DeviceRGB'), PDFNumber.fromNumber(_this.image.palette.length / 3 - 1), paletteStream, ], document.index)); } // TODO: Handle the following two transparency types. They don't seem to be // fully handled in: // https://github.com/devongovett/pdfkit/blob/e71edab0dd4657b5a767804ba86c94c58d01fbca/lib/image/png.coffee // if (this.image.transparency.grayscale) // if (this.image.transparency.rgb) // prettier-ignore return (_this.image.transparency.indexed ? _this.loadIndexedAlphaChannel() : _this.image.hasAlphaChannel ? _this.splitAlphaChannel() : _this.finalize()); }; /** @hidden */ this.finalize = function () { if (_this.alphaChannel) { var deflatedAlphaChannel = pako.deflate(_this.alphaChannel); var alphaStreamDict = PDFDictionary.from({ Type: PDFName.from('XObject'), Subtype: PDFName.from('Image'), Height: PDFNumber.fromNumber(_this.height), Width: PDFNumber.fromNumber(_this.width), BitsPerComponent: PDFNumber.fromNumber(8), Filter: PDFName.from('FlateDecode'), ColorSpace: PDFName.from('DeviceGray'), Decode: PDFArray.fromArray([PDFNumber.fromNumber(0), PDFNumber.fromNumber(1)], _this.document.index), Length: PDFNumber.fromNumber(deflatedAlphaChannel.length), }, _this.document.index); var smaskStream = _this.document.register(PDFRawStream.from(alphaStreamDict, deflatedAlphaChannel)); _this.xObjDict.set('SMask', smaskStream); } _this.xObjDict.set('Length', PDFNumber.fromNumber(_this.imgData.length)); var xObj = _this.document.register(PDFRawStream.from(_this.xObjDict, _this.imgData)); return xObj; }; /** @hidden */ this.splitAlphaChannel = function () { var pixels = _this.image.decodePixels(); var colorByteSize = (_this.image.colors * _this.image.bits) / 8; var pixelCount = _this.image.width * _this.image.height; _this.imgData = new Uint8Array(pixelCount * colorByteSize); _this.alphaChannel = new Uint8Array(pixelCount); var i = 0; var p = 0; var a = 0; while (i < pixels.length) { _this.imgData[p++] = pixels[i++]; _this.imgData[p++] = pixels[i++]; _this.imgData[p++] = pixels[i++]; _this.alphaChannel[a++] = pixels[i++]; } _this.imgData = pako.deflate(_this.imgData); return _this.finalize(); }; /** @hidden */ this.loadIndexedAlphaChannel = function () { var transparency = _this.image.transparency.indexed; var pixels = _this.image.decodePixels(); _this.alphaChannel = new Uint8Array(_this.width * _this.height); // Can't use forEach here, because it's missing on React Native Android for (var idx = 0; idx < pixels.length; idx++) { _this.alphaChannel[idx] = transparency[pixels[idx]]; } return _this.finalize(); }; validate(data, isInstance(Uint8Array), '"data" must be an instance of Uint8Array'); this.image = PNG.load(data); this.width = this.image.width; this.height = this.image.height; this.imgData = this.image.imgData; } PNGXObjectFactory.for = function (data) { return new PNGXObjectFactory(data); }; return PNGXObjectFactory; }()); export default PNGXObjectFactory;