Skip to content
This repository was archived by the owner on Apr 12, 2021. It is now read-only.

Commit 58f8e1f

Browse files
author
Duncan MacKenzie
committed
Add support for GIF images
1 parent ed0cbb4 commit 58f8e1f

7 files changed

Lines changed: 90 additions & 15 deletions

File tree

lib/image.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@ By Devon Govett
66
import fs from 'fs'
77
import JPEG from './image/jpeg'
88
import PNG from './image/png'
9+
import GIF from './image/gif'
910

1011
class PDFImage {
11-
static open(src, label) {
12+
static open(src, label, options) {
1213
let data
1314
if (Buffer.isBuffer(src)) {
1415
data = src
@@ -27,9 +28,11 @@ class PDFImage {
2728
}
2829

2930
if (data[0] === 0xff && data[1] === 0xd8) {
30-
return new JPEG(data, label)
31+
return new JPEG(data, label, options)
3132
} else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') {
32-
return new PNG(data, label)
33+
return new PNG(data, label, options)
34+
} else if (data[0] === 71 && data[1] === 73 && data[2] === 70) {
35+
return new GIF(data, label, options)
3336
} else {
3437
throw new Error('Unknown image format.')
3538
}

lib/image/gif.js

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import GIFuct from './gifuct-js';
2+
3+
const COLOR_SPACE_MAP = {
4+
1: 'DeviceGray',
5+
3: 'DeviceRGB',
6+
4: 'DeviceCMYK'
7+
}
8+
class GIF {
9+
constructor(data, label, { width, height } = {}) {
10+
this.data = data;
11+
this.label = label;
12+
13+
// validate data integrity
14+
15+
// assign bits per component to this.bits
16+
this.bits = 8;
17+
18+
// assign image width and height to this.width and this.height
19+
this.width = width || 0;
20+
this.height = height || 0;
21+
22+
this.colorSpace = COLOR_SPACE_MAP[3];
23+
24+
this.obj = null;
25+
}
26+
27+
embed(document) {
28+
if (this.obj) { return; }
29+
30+
const gif = new GIFuct(this.data);
31+
const data = gif.getFrame(0, false, false);
32+
console.log('Data from GIFuct:', data);
33+
34+
this.obj = document.ref({
35+
Type: 'XObject',
36+
Subtype: 'Image',
37+
BitsPerComponent: this.bits,
38+
Width: this.width,
39+
Height: this.height,
40+
ColorSpace: this.colorSpace,
41+
Filter: data.format === 'lzw' ? 'LZWDecode' : 'FlateDecode',
42+
});
43+
44+
this.obj.end(data);
45+
46+
// free memory
47+
return this.data = null;
48+
}
49+
};
50+
51+
export default GIF;

lib/image/gifuct-js/src/gif.js

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
1+
/* eslint-disable */
22
// object used to represent array buffer data for a gif file
33

4-
var DataParser = require('../node_modules/js-binary-schema-parser/src/dataparser');
4+
var DataParser = require('js-binary-schema-parser/src/dataparser');
55
var gifSchema = require('./schema');
66

77
function GIF(arrayBuffer){
@@ -25,32 +25,48 @@ function GIF(arrayBuffer){
2525
// if buildPatch is true, the returned image will be a clamped 8 bit image patch
2626
// for use directly with a canvas.
2727
GIF.prototype.decompressFrame = function(index, buildPatch){
28+
return this.getFrame(index, buildPatch, true);
29+
}
30+
31+
// set decompress to false to retrieve the LZW-compressed image data
32+
GIF.prototype.getFrame = function(index, buildPatch, decompress){
2833

2934
// make sure a valid frame is requested
3035
if(index >= this.raw.frames.length){ return null; }
3136

3237
var frame = this.raw.frames[index];
33-
if(frame.image){
38+
if (frame.image) {
39+
var isInterlaced = frame.image.descriptor.lct.interlaced;
40+
3441
// get the number of pixels
3542
var totalPixels = frame.image.descriptor.width * frame.image.descriptor.height;
43+
var pixels;
3644

3745
// do lzw decompression
38-
var pixels = lzw(frame.image.data.minCodeSize, frame.image.data.blocks, totalPixels);
46+
if (decompress !== false || buildPatch || isInterlaced) {
47+
pixels = lzw(frame.image.data.minCodeSize, frame.image.data.blocks, totalPixels);
48+
}
3949

4050
// deal with interlacing if necessary
41-
if(frame.image.descriptor.lct.interlaced){
51+
if (isInterlaced) {
4252
pixels = deinterlace(pixels, frame.image.descriptor.width);
4353
}
4454

55+
if (!pixels) {
56+
console.log('Returning image data:', frame.image.data.blocks);
57+
}
58+
4559
// setup usable image object
4660
var image = {
61+
data: !pixels ? frame.image.data.blocks : undefined,
4762
pixels: pixels,
4863
dims: {
4964
top: frame.image.descriptor.top,
5065
left: frame.image.descriptor.left,
5166
width: frame.image.descriptor.width,
5267
height: frame.image.descriptor.height
53-
}
68+
},
69+
format: pixels ? 'raw' : 'lzw'
5470
};
5571

5672
// color table
@@ -80,6 +96,7 @@ GIF.prototype.decompressFrame = function(index, buildPatch){
8096

8197
// frame does not contains image
8298
return null;
99+
};
83100

84101

85102
/**
@@ -230,7 +247,6 @@ GIF.prototype.decompressFrame = function(index, buildPatch){
230247

231248
return patchData;
232249
}
233-
};
234250

235251
// returns all frames decompressed
236252
GIF.prototype.decompressFrames = function(buildPatch){
@@ -244,4 +260,4 @@ GIF.prototype.decompressFrames = function(buildPatch){
244260
return frames;
245261
};
246262

247-
module.exports = GIF;
263+
export default GIF;

lib/image/gifuct-js/src/schema.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
/* eslint-disable */
22
// Schema for the js file parser to use to parse gif files
33
// For js object convenience (re-use), the schema objects are approximately reverse ordered
44

lib/mixins/images.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export default {
2525
if (src.width && src.height) {
2626
image = src;
2727
} else {
28-
image = this.openImage(src);
28+
image = this.openImage(src, { width: options.width, height: options.height });
2929
}
3030
}
3131

@@ -102,14 +102,14 @@ export default {
102102
return this;
103103
},
104104

105-
openImage(src) {
105+
openImage(src, options) {
106106
let image;
107107
if (typeof src === 'string') {
108108
image = this._imageRegistry[src];
109109
}
110110

111111
if (!image) {
112-
image = PDFImage.open(src, `I${++this._imageCount}`);
112+
image = PDFImage.open(src, `I${++this._imageCount}`, options);
113113
if (typeof src === 'string') {
114114
this._imageRegistry[src] = image;
115115
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@
6262
"@react-pdf/png-js": "^1.0.0",
6363
"lz-string": "^1.4.4",
6464
"crypto-js": "^3.1.9-1",
65+
"js-binary-schema-parser": "github:matt-way/jsBinarySchemaParser",
6566
"linebreak": "^0.3.0",
6667
"saslprep": "1.0.1"
6768
},

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3661,6 +3661,10 @@ jest@^23.4.2:
36613661
import-local "^1.0.0"
36623662
jest-cli "^23.6.0"
36633663

3664+
"js-binary-schema-parser@github:matt-way/jsBinarySchemaParser":
3665+
version "1.0.0"
3666+
resolved "https://codeload.github.com/matt-way/jsBinarySchemaParser/tar.gz/7169e72d1223b3479613f59513691843bedbdc17"
3667+
36643668
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
36653669
version "4.0.0"
36663670
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"

0 commit comments

Comments
 (0)