Skip to content

Commit

Permalink
feat: embed same image once in final document (#1857)
Browse files Browse the repository at this point in the history
  • Loading branch information
diegomura authored Jun 4, 2022
1 parent 9347466 commit d958b0a
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 63 deletions.
7 changes: 7 additions & 0 deletions .changeset/hungry-llamas-stare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'@react-pdf/pdfkit': minor
'@react-pdf/layout': patch
'@react-pdf/render': patch
---

feat: embed same image once in final document
3 changes: 2 additions & 1 deletion packages/layout/src/image/fetchImage.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ const fetchImage = async node => {
}

node.image = await resolveImage(source, { cache });
node.image.key = source.data ? source.data.toString() : source.uri;
} catch (e) {
node.image = { width: 0, height: 0 };
node.image = { width: 0, height: 0, key: null };
console.warn(e.message);
}
};
Expand Down
9 changes: 5 additions & 4 deletions packages/pdfkit/src/image.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import fs from 'fs';
import Data from './data';
import JPEG from './image/jpeg';
import PNG from './image/png';

Expand All @@ -22,11 +21,13 @@ class PDFImage {

if (data[0] === 0xff && data[1] === 0xd8) {
return new JPEG(data, label);
} else if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') {
}

if (data[0] === 0x89 && data.toString('ascii', 1, 4) === 'PNG') {
return new PNG(data, label);
} else {
throw new Error('Unknown image format.');
}

throw new Error('Unknown image format.');
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/pdfkit/src/image/jpeg.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ let MARKERS = [
0xffcc,
0xffcd,
0xffce,
0xffcf,
0xffcf
];

class JPEG {
Expand Down Expand Up @@ -75,7 +75,7 @@ class JPEG {
Width: this.width,
Height: this.height,
ColorSpace: this.colorSpace,
Filter: 'DCTDecode',
Filter: 'DCTDecode'
});

// add extra decode params for CMYK images. By swapping the
Expand All @@ -88,7 +88,7 @@ class JPEG {
this.obj.end(this.data);

// free memory
return (this.data = null);
this.data = null;
}
}

Expand Down
7 changes: 3 additions & 4 deletions packages/pdfkit/src/image/png.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,8 @@ class PNGImage {
let dataDecoded = false;

this.document = document;
if (this.obj) {
return;
}

if (this.obj) return;

const { hasAlphaChannel } = this.image;
const isInterlaced = this.image.interlaceMethod === 1;
Expand Down Expand Up @@ -118,7 +117,7 @@ class PNGImage {

// free memory
this.image = null;
return (this.imgData = null);
this.imgData = null;
}

splitAlphaChannel() {
Expand Down
75 changes: 25 additions & 50 deletions packages/pdfkit/src/mixins/images.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,11 @@ import PDFImage from '../image';
export default {
initImages() {
this._imageRegistry = {};
return (this._imageCount = 0);
this._imageCount = 0;
},

image(src, x, y, options) {
let bh, bp, bw, image, ip, left, left1;
if (options == null) {
options = {};
}
if (typeof x === 'object') {
options = x;
x = null;
}

x = (left = x != null ? x : options.x) != null ? left : this.x;
y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;
embedImage(src) {
let image;

if (typeof src === 'string') {
image = this._imageRegistry[src];
Expand All @@ -35,6 +25,27 @@ export default {
image.embed(this);
}

return image;
},

image(src, x, y, options = {}) {
let bh;
let bp;
let bw;
let ip;
let left;
let left1;

if (typeof x === 'object') {
options = x;
x = null;
}

const image = src instanceof PDFImage ? src : this.embedImage(src);

x = (left = x != null ? x : options.x) != null ? left : this.x;
y = (left1 = y != null ? y : options.y) != null ? left1 : this.y;

if (this.page.xobjects[image.label] == null) {
this.page.xobjects[image.label] = image.obj;
}
Expand Down Expand Up @@ -64,42 +75,6 @@ export default {
h = bh;
w = bh * ip;
}
} else if (options.cover) {
[bw, bh] = Array.from(options.cover);
bp = bw / bh;
ip = image.width / image.height;
if (ip > bp) {
h = bh;
w = bh * ip;
} else {
w = bw;
h = bw / ip;
}
}

if (options.fit || options.cover) {
if (options.align === 'center') {
x = x + bw / 2 - w / 2;
} else if (options.align === 'right') {
x = x + bw - w;
}

if (options.valign === 'center') {
y = y + bh / 2 - h / 2;
} else if (options.valign === 'bottom') {
y = y + bh - h;
}
}

// create link annotations if the link option is given
if (options.link != null) {
this.link(x, y, w, h, options.link);
}
if (options.goTo != null) {
this.goTo(x, y, w, h, options.goTo);
}
if (options.destination != null) {
this.addNamedDestination(options.destination, 'XYZ', x, y, null);
}

// Set the current y position to below the image if it is in the document flow
Expand Down Expand Up @@ -129,5 +104,5 @@ export default {
}

return image;
},
}
};
11 changes: 10 additions & 1 deletion packages/render/src/primitives/renderImage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import clipNode from '../operations/clipNode';
import resolveObjectFit from '../utils/resolveObjectFit';

const IMAGE_CACHE = new Map();

const drawImage = (ctx, node) => {
const { left, top } = node.box;
const opacity = node.style?.opacity;
Expand All @@ -24,10 +26,17 @@ const drawImage = (ctx, node) => {

if (node.image.data) {
if (width !== 0 && height !== 0) {
const cacheKey = node.image.key;

const image =
IMAGE_CACHE.get(cacheKey) || ctx.embedImage(node.image.data);

if (cacheKey) IMAGE_CACHE.set(cacheKey, image);

ctx
.fillOpacity(opacity || 1)
.image(
node.image.data,
image,
left + paddingLeft + xOffset,
top + paddingTop + yOffset,
{
Expand Down

0 comments on commit d958b0a

Please sign in to comment.