Skip to content

Commit

Permalink
Scale page content (#991)
Browse files Browse the repository at this point in the history
* add function scaleContent to scale the content of a page by x and y

* scale annots

* refactor

* run linter

* refactor: repalce .forEach() and .asArray()

* add integration tests :web

* add integration tests :node

* Update src/api/PDFPage.ts

Co-authored-by: Andrew Dillon <[email protected]>

* Update src/api/PDFPage.ts

Co-authored-by: Andrew Dillon <[email protected]>

* Update src/api/PDFPage.ts

Co-authored-by: Andrew Dillon <[email protected]>

* Update src/api/PDFPage.ts

Co-authored-by: Andrew Dillon <[email protected]>

* refactorings, add scale method

* revert scratchpad test

* remove integration tests 'test19' and 'test20' and add them into 'test3', extend 'test3' for all plattforms

* fix merge

Co-authored-by: Jan-Niklaas Koch <[email protected]>
Co-authored-by: Andrew Dillon <[email protected]>
  • Loading branch information
3 people authored Oct 16, 2021
1 parent 5cb111a commit b57b257
Show file tree
Hide file tree
Showing 10 changed files with 186 additions and 34 deletions.
1 change: 1 addition & 0 deletions apps/deno/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ const assets = {
with_xfa_fields: readPdf('with_xfa_fields.pdf'),
fancy_fields: readPdf('fancy_fields.pdf'),
form_to_flatten: readPdf('form_to_flatten.pdf'),
with_annots: readPdf('with_annots.pdf'),
},
};

Expand Down
24 changes: 18 additions & 6 deletions apps/deno/tests/test3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,19 @@ export default async (assets: Assets) => {

const page0 = pdfDoc.insertPage(0, [305, 250]);
const page1 = pdfDoc.getPage(1);
const page2 = pdfDoc.addPage([305, 250]);

const docWithAnnots = await PDFDocument.load(pdfs.with_annots);
const [page2] = await pdfDoc.copyPages(docWithAnnots, [0]);
page2.scaleContent(0.5, 0.5);
pdfDoc.addPage(page2);
const [page3] = await pdfDoc.copyPages(docWithAnnots, [0]);
page3.scaleAnnotations(0.5, 0.5);
pdfDoc.addPage(page3);
const [page4] = await pdfDoc.copyPages(docWithAnnots, [0]);
page4.scale(0.5, 0.5);
pdfDoc.addPage(page4);

const page5 = pdfDoc.addPage([305, 250]);

const hotPink = rgb(1, 0, 1);
const red = rgb(1, 0, 0);
Expand Down Expand Up @@ -76,25 +88,25 @@ export default async (assets: Assets) => {
ySkew: degrees(15),
});

page2.setFontSize(24);
page2.drawText('This is the last page!', {
page5.setFontSize(24);
page5.drawText('This is the last page!', {
x: 30,
y: 215,
font: helveticaFont,
color: hotPink,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 205 },
end: { x: 30 + lastPageTextWidth, y: 205 },
color: hotPink,
thickness: 5,
});
page2.drawImage(cmykImage, {
page5.drawImage(cmykImage, {
...cmykDims,
x: 30,
y: 30,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 240 },
end: { x: 30 + lastPageTextWidth, y: 240 },
color: hotPink,
Expand Down
7 changes: 4 additions & 3 deletions apps/node/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ const assets = {
with_xfa_fields: readPdf('with_xfa_fields.pdf'),
fancy_fields: readPdf('fancy_fields.pdf'),
form_to_flatten: readPdf('form_to_flatten.pdf'),
with_annots: readPdf('with_annots.pdf'),
},
};

Expand Down Expand Up @@ -161,9 +162,9 @@ const main = async () => {

// prettier-ignore
const allTests = [
test1, test2, test3, test4, test5, test6, test7, test8, test9, test10,
test11, test12, test13, test14, test15, test16, test17, test18
];
test1, test2, test3, test4, test5, test6, test7, test8, test9, test10,
test11, test12, test13, test14, test15, test16, test17, test18,
];

const tests = testIdx ? [allTests[testIdx - 1]] : allTests;

Expand Down
24 changes: 18 additions & 6 deletions apps/node/tests/test3.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,19 @@ export default async (assets: Assets) => {

const page0 = pdfDoc.insertPage(0, [305, 250]);
const page1 = pdfDoc.getPage(1);
const page2 = pdfDoc.addPage([305, 250]);

const docWithAnnots = await PDFDocument.load(assets.pdfs.with_annots);
const [page2] = await pdfDoc.copyPages(docWithAnnots, [0]);
page2.scaleContent(0.5, 0.5);
pdfDoc.addPage(page2);
const [page3] = await pdfDoc.copyPages(docWithAnnots, [0]);
page3.scaleAnnotations(0.5, 0.5);
pdfDoc.addPage(page3);
const [page4] = await pdfDoc.copyPages(docWithAnnots, [0]);
page4.scale(0.5, 0.5);
pdfDoc.addPage(page4);

const page5 = pdfDoc.addPage([305, 250]);

const hotPink = rgb(1, 0, 1);
const red = rgb(1, 0, 0);
Expand Down Expand Up @@ -73,25 +85,25 @@ export default async (assets: Assets) => {
ySkew: degrees(15),
});

page2.setFontSize(24);
page2.drawText('This is the last page!', {
page5.setFontSize(24);
page5.drawText('This is the last page!', {
x: 30,
y: 215,
font: helveticaFont,
color: hotPink,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 205 },
end: { x: 30 + lastPageTextWidth, y: 205 },
color: hotPink,
thickness: 5,
});
page2.drawImage(cmykImage, {
page5.drawImage(cmykImage, {
...cmykDims,
x: 30,
y: 30,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 240 },
end: { x: 30 + lastPageTextWidth, y: 240 },
color: hotPink,
Expand Down
25 changes: 19 additions & 6 deletions apps/rn/src/tests/test3.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,20 @@ export default async () => {

const page0 = pdfDoc.insertPage(0, [305, 250]);
const page1 = pdfDoc.getPage(1);
const page2 = pdfDoc.addPage([305, 250]);

const existingPdfBytes = await fetchBinaryAsset('pdfs/with_annots.pdf');
const docWithAnnots = await PDFDocument.load(existingPdfBytes);
const [page2] = await pdfDoc.copyPages(docWithAnnots, [0]);
page2.scaleContent(0.5, 0.5);
pdfDoc.addPage(page2);
const [page3] = await pdfDoc.copyPages(docWithAnnots, [0]);
page3.scaleAnnotations(0.5, 0.5);
pdfDoc.addPage(page3);
const [page4] = await pdfDoc.copyPages(docWithAnnots, [0]);
page4.scale(0.5, 0.5);
pdfDoc.addPage(page4);

const page5 = pdfDoc.addPage([305, 250]);

const hotPink = rgb(1, 0, 1);
const red = rgb(1, 0, 0);
Expand Down Expand Up @@ -80,25 +93,25 @@ export default async () => {
ySkew: degrees(15),
});

page2.setFontSize(24);
page2.drawText('This is the last page!', {
page5.setFontSize(24);
page5.drawText('This is the last page!', {
x: 30,
y: 215,
font: helveticaFont,
color: hotPink,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 205 },
end: { x: 30 + lastPageTextWidth, y: 205 },
color: hotPink,
thickness: 5,
});
page2.drawImage(cmykImage, {
page5.drawImage(cmykImage, {
...cmykDims,
x: 30,
y: 30,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 240 },
end: { x: 30 + lastPageTextWidth, y: 240 },
color: hotPink,
Expand Down
25 changes: 19 additions & 6 deletions apps/web/test3.html
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,20 @@

const page0 = pdfDoc.insertPage(0, [305, 250]);
const page1 = pdfDoc.getPage(1);
const page2 = pdfDoc.addPage([305, 250]);

const existingPdfBytes = await fetchBinaryAsset('pdfs/with_annots.pdf');
const docWithAnnots = await PDFDocument.load(existingPdfBytes);
const [page2] = await pdfDoc.copyPages(docWithAnnots, [0]);
page2.scaleContent(0.5, 0.5);
pdfDoc.addPage(page2);
const [page3] = await pdfDoc.copyPages(docWithAnnots, [0]);
page3.scaleAnnotations(0.5, 0.5);
pdfDoc.addPage(page3);
const [page4] = await pdfDoc.copyPages(docWithAnnots, [0]);
page4.scale(0.5, 0.5);
pdfDoc.addPage(page4);

const page5 = pdfDoc.addPage([305, 250]);

const hotPink = rgb(1, 0, 1);
const red = rgb(1, 0, 0);
Expand Down Expand Up @@ -124,25 +137,25 @@
ySkew: degrees(15),
});

page2.setFontSize(24);
page2.drawText('This is the last page!', {
page5.setFontSize(24);
page5.drawText('This is the last page!', {
x: 30,
y: 215,
font: helveticaFont,
color: hotPink,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 205 },
end: { x: 30 + lastPageTextWidth, y: 205 },
color: hotPink,
thickness: 5,
});
page2.drawImage(cmykImage, {
page5.drawImage(cmykImage, {
...cmykDims,
x: 30,
y: 30,
});
page2.drawLine({
page5.drawLine({
start: { x: 30, y: 240 },
end: { x: 30 + lastPageTextWidth, y: 240 },
color: hotPink,
Expand Down
Binary file added assets/pdfs/with_annots.pdf
Binary file not shown.
2 changes: 1 addition & 1 deletion scratchpad/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PDFDocument } from 'src/index';
// This data can be obtained in a number of different ways
// If your running in a Node environment, you could use fs.readFile()
// In the browser, you could make a fetch() call and use res.arrayBuffer()
const existingPdfBytes = fs.readFileSync('assets/pdfs/with_viewer_prefs.pdf');
const existingPdfBytes = fs.readFileSync('assets/pdfs/with_annots.pdf');

// Load a PDFDocument without updating its existing metadata
const pdfDoc = await PDFDocument.load(existingPdfBytes);
Expand Down
102 changes: 96 additions & 6 deletions src/api/PDFPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
pushGraphicsState,
translate,
LineCapStyle,
scale,
} from 'src/api/operators';
import PDFDocument from 'src/api/PDFDocument';
import PDFEmbeddedPage from 'src/api/PDFEmbeddedPage';
Expand All @@ -39,6 +40,8 @@ import {
PDFOperator,
PDFPageLeaf,
PDFRef,
PDFDict,
PDFArray,
} from 'src/core';
import {
addRandomSuffix,
Expand Down Expand Up @@ -566,6 +569,77 @@ export default class PDFPage {
this.node.wrapContentStreams(startRef, endRef);
}

/**
* Scale the size, content and annotations of a page.
* ```js
* p.scale(0.5, 0.5);
* ```
* @param x The factor by wich the width for the page should be scaled (e.g. 0.5 is 50%)
* @param y The factor by wich the height for the page should be scaled (e.g. 0.5 is 50%)
*/
scale(x: number, y: number): void {
assertIs(x, 'x', ['number']);
assertIs(y, 'y', ['number']);
this.setSize(this.getWidth() * x, this.getHeight() * y);
this.scaleContent(x, y);
this.scaleAnnotations(x, y);
}

/**
* Scale the content of a page. This is useful after resizing an exisiting page.
* This scales only the content not the annotations. See also: [[scaleAnnotations]]
* ```js
* // bisect the size of the page
* p.setSize(p.getWidth() / 2, p.getHeight() / 2);
*
* // scale the content of the page down by 50% in x and y
* page.scaleContent(0.5, 0.5);
* ```
* @param x The factor by wich the x-axis for the content should be scaled (e.g. 0.5 is 50%)
* @param y The factor by wich the y-axis for the content should be scaled (e.g. 0.5 is 50%)
*/
scaleContent(x: number, y: number): void {
assertIs(x, 'x', ['number']);
assertIs(y, 'y', ['number']);

this.node.normalize();
this.getContentStream();

const start = this.createContentStream(pushGraphicsState(), scale(x, y));
const startRef = this.doc.context.register(start);

const end = this.createContentStream(popGraphicsState());
const endRef = this.doc.context.register(end);

this.node.wrapContentStreams(startRef, endRef);
}

/**
* Scale the annotations of a page. This is useful if you want to scale a page with comments or other annotations.
* ```js
* // scale the content of the page down by 50% in x and y
* page.scaleContent(0.5, 0.5);
*
* // scale the content of the page down by 50% in x and y
* page.scaleannotations(0.5, 0.5);
* ```
* See also: [[scaleContent]]
* @param x The factor by wich the x-axis for the annotations should be scaled (e.g. 0.5 is 50%)
* @param y The factor by wich the y-axis for the annotations should be scaled (e.g. 0.5 is 50%)
*/
scaleAnnotations(x: number, y: number) {
assertIs(x, 'x', ['number']);
assertIs(y, 'y', ['number']);
const annots = this.node.Annots();
if (!annots) return;

// loop annotations
for (let idx = 0; idx < annots.size(); idx++) {
const annot = annots.lookup(idx);
if (annot instanceof PDFDict) this.scaleAnnot(annot, x, y);
}
}

/**
* Reset the x and y coordinates of this page to `(0, 0)`. This operation is
* often useful after calling [[translateContent]]. For example:
Expand Down Expand Up @@ -1058,16 +1132,16 @@ export default class PDFPage {

// prettier-ignore
const xScale = (
options.width !== undefined ? options.width / embeddedPage.width
: options.xScale !== undefined ? options.xScale
: 1
options.width !== undefined ? options.width / embeddedPage.width
: options.xScale !== undefined ? options.xScale
: 1
);

// prettier-ignore
const yScale = (
options.height !== undefined ? options.height / embeddedPage.height
: options.yScale !== undefined ? options.yScale
: 1
options.height !== undefined ? options.height / embeddedPage.height
: options.yScale !== undefined ? options.yScale
: 1
);

const contentStream = this.getContentStream();
Expand Down Expand Up @@ -1500,4 +1574,20 @@ export default class PDFPage {

return key;
}

private scaleAnnot(annot: PDFDict, x: number, y: number) {
const selectors = ['RD', 'CL', 'Vertices', 'QuadPoints', 'L', 'Rect'];
for (let sel of selectors) {
const list = annot.get(PDFName.of(sel));
if (list instanceof PDFArray) list.scalePDFNumbers(x, y);
}

const pdfNameInkList = annot.get(PDFName.of('InkList')) as PDFArray;

for (let index = 0; index < pdfNameInkList?.size(); index++) {
const arr = pdfNameInkList.get(index);
if (arr instanceof PDFArray) arr.scalePDFNumbers(x, y);
}
}

}
Loading

0 comments on commit b57b257

Please sign in to comment.