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

Dynamic page margins v2 #2458

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3,478 changes: 1,552 additions & 1,926 deletions build/pdfmake.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/pdfmake.js.map

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/pdfmake.min.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/pdfmake.min.js.map

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion build/vfs_fonts.js

Large diffs are not rendered by default.

5,104 changes: 2,552 additions & 2,552 deletions src/3rd-party/svg-to-pdfkit/source.js

Large diffs are not rendered by default.

40 changes: 27 additions & 13 deletions src/DocumentContext.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { isString } from './helpers/variableType';
import { functionalizePageMargin } from './PageSize';
import { EventEmitter } from 'events';

/**
Expand All @@ -10,10 +11,10 @@ class DocumentContext extends EventEmitter {
super();
this.pages = [];

this.pageMargins = pageMargins;
this.pageMargins = functionalizePageMargin(pageMargins);

this.x = pageMargins.left;
this.availableWidth = pageSize.width - pageMargins.left - pageMargins.right;
this.x = this.pageMargins(1).left;
this.availableWidth = pageSize.width - this.pageMargins(1).left - this.pageMargins(1).right;
this.availableHeight = 0;
this.page = -1;

Expand Down Expand Up @@ -131,9 +132,13 @@ class DocumentContext extends EventEmitter {
}

initializePage() {
this.y = this.pageMargins.top;
this.availableHeight = this.getCurrentPage().pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
this.pageSnapshot().availableWidth = this.getCurrentPage().pageSize.width - this.pageMargins.left - this.pageMargins.right;
this.y = this.getCurrentPage().pageMargins.top;
this.availableHeight = this.getCurrentPage().pageSize.height
- this.getCurrentPage().pageMargins.top
- this.getCurrentPage().pageMargins.bottom;
this.pageSnapshot().availableWidth = this.getCurrentPage().pageSize.width
- this.getCurrentPage().pageMargins.left
- this.getCurrentPage().pageMargins.right;
}

pageSnapshot() {
Expand All @@ -147,11 +152,11 @@ class DocumentContext extends EventEmitter {
moveTo(x, y) {
if (x !== undefined && x !== null) {
this.x = x;
this.availableWidth = this.getCurrentPage().pageSize.width - this.x - this.pageMargins.right;
this.availableWidth = this.getCurrentPage().pageSize.width - this.x - this.getCurrentPage().pageMargins.right;
}
if (y !== undefined && y !== null) {
this.y = y;
this.availableHeight = this.getCurrentPage().pageSize.height - this.y - this.pageMargins.bottom;
this.availableHeight = this.getCurrentPage().pageSize.height - this.y - this.getCurrentPage().pageMargins.bottom;
}
}

Expand Down Expand Up @@ -218,7 +223,11 @@ class DocumentContext extends EventEmitter {
}

addPage(pageSize) {
let page = { items: [], pageSize: pageSize };
let page = {
items: [],
pageSize: pageSize,
pageMargins: this.pageMargins(this.pages.length + 1),
};
this.pages.push(page);
this.backgroundLength.push(0);
this.page = this.pages.length - 1;
Expand All @@ -239,8 +248,12 @@ class DocumentContext extends EventEmitter {

getCurrentPosition() {
let pageSize = this.getCurrentPage().pageSize;
let innerHeight = pageSize.height - this.pageMargins.top - this.pageMargins.bottom;
let innerWidth = pageSize.width - this.pageMargins.left - this.pageMargins.right;
let innerHeight = pageSize.height
- this.getCurrentPage().pageMargins.top
- this.getCurrentPage().pageMargins.bottom;
let innerWidth = pageSize.width
- this.getCurrentPage().pageMargins.left
- this.getCurrentPage().pageMargins.right;

return {
pageNumber: this.page + 1,
Expand All @@ -249,8 +262,9 @@ class DocumentContext extends EventEmitter {
pageInnerWidth: innerWidth,
left: this.x,
top: this.y,
verticalRatio: ((this.y - this.pageMargins.top) / innerHeight),
horizontalRatio: ((this.x - this.pageMargins.left) / innerWidth)
verticalRatio: ((this.y - this.getCurrentPage().pageMargins.top) / innerHeight),
horizontalRatio: ((this.x - this.getCurrentPage().pageMargins.left) / innerWidth),
pageMargins: this.getCurrentPage().pageMargins,
};
}
}
Expand Down
4 changes: 3 additions & 1 deletion src/ElementWriter.js
Original file line number Diff line number Diff line change
Expand Up @@ -362,7 +362,9 @@ class ElementWriter extends EventEmitter {
*/
pushContext(contextOrWidth, height) {
if (contextOrWidth === undefined) {
height = this.context().getCurrentPage().height - this.context().pageMargins.top - this.context().pageMargins.bottom;
height = this.context().getCurrentPage().height
- this.context().getCurrentPage().pageMargins.top
- this.context().getCurrentPage().pageMargins.bottom;
contextOrWidth = this.context().availableWidth;
}

Expand Down
2 changes: 1 addition & 1 deletion src/LayoutBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ class LayoutBuilder {
let node = nodeGetter(pageIndex + 1, l, this.writer.context().pages[pageIndex].pageSize);

if (node) {
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.pageMargins);
let sizes = sizeFunction(this.writer.context().getCurrentPage().pageSize, this.writer.context().getCurrentPage().pageMargins);
this.writer.beginUnbreakableBlock(sizes.width, sizes.height);
node = this.docPreprocessor.preprocessDocument(node);
this.processNode(this.docMeasure.measureDocument(node));
Expand Down
61 changes: 60 additions & 1 deletion src/PageSize.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import sizes from './standardPageSizes';
import { isString, isNumber } from './helpers/variableType';
import { isString, isNumber, isFunction, isObject, isValue } from './helpers/variableType';

export function normalizePageSize(pageSize, pageOrientation) {
function isNeedSwapPageSizes(pageOrientation) {
Expand Down Expand Up @@ -36,6 +36,27 @@ export function normalizePageSize(pageSize, pageOrientation) {
return size;
}

function isPageMarginObject(margin) {
if (isObject(margin)) {
const { left, top, right, bottom } = margin;

if (isValue(left) && isValue(top) && isValue(right) && isValue(bottom)) {
return true;
}
}
return false;
}


/*
* Accepts margin definition as being:
* * a number to set same margin size on all margins
* * a function which will receive pageNumber as argument
* * an array with two numbers to set horizontal and vertical margin respectively
* * an array with four numbers to set left, top, right and bottom margin respectively
*
* Normalized value is an object with the four margins as property.
* */
export function normalizePageMargin(margin) {
if (isNumber(margin)) {
margin = { left: margin, right: margin, top: margin, bottom: margin };
Expand All @@ -49,5 +70,43 @@ export function normalizePageMargin(margin) {
}
}

if (!isFunction(margin) && !isPageMarginObject(margin)) {
throw new Error('Invalid pageMargins definition');
}

return margin;
}

/*
* Returns a function accepting pageNumber as argument and returning
* a normalized page margin object
* */
export function functionalizePageMargin(margin) {
let marginFn;
if (isFunction(margin)) {
marginFn = function (pageNumber) {
return normalizePageMargin(margin(pageNumber));
};
} else {
if (!isPageMarginObject(margin)) {
margin = normalizePageMargin(margin);
}

const { left, top, right, bottom } = margin;
marginFn = function () {
return {
left,
top,
right,
bottom,
};
};

}

if (!isFunction(marginFn)) {
throw new Error(`Unable to functionalize pageMargin: ${margin}`);
}

return marginFn;
}
8 changes: 8 additions & 0 deletions src/helpers/variableType.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,14 @@ export function isNumber(variable) {
return (typeof variable === 'number') || (variable instanceof Number);
}

/**
* @param {any} variable
* @returns {boolean}
*/
export function isFunction(variable) {
return (typeof variable === 'function') || (variable instanceof Function);
}

/**
* @param {any} variable
* @returns {boolean}
Expand Down
6 changes: 3 additions & 3 deletions tests/integration/integrationTestHelper.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ var LayoutBuilder = require('../../js/LayoutBuilder').default;
var SVGMeasure = require('../../js/SVGMeasure').default;

class IntegrationTestHelper {
constructor() {
this.MARGINS = { top: 40, left: 40, right: 40, bottom: 40 };
constructor(options = {}) {
this.MARGINS = options.margins || { top: 40, left: 40, right: 40, bottom: 40 };
this.LINE_HEIGHT = 14.0625;
this.DEFAULT_BULLET_SPACER = '9. ';
}
Expand All @@ -28,7 +28,7 @@ class IntegrationTestHelper {
var pageSize = { width: size[0], height: size[1], orientation: 'portrait' };

this.pdfDocument = new PDFDocument(fontDescriptors, docDefinition.images, docDefinition.attachments, { size: [pageSize.width, pageSize.height], compress: false });
var builder = new LayoutBuilder(pageSize, { left: this.MARGINS.left, right: this.MARGINS.right, top: this.MARGINS.top, bottom: this.MARGINS.bottom }, new SVGMeasure());
var builder = new LayoutBuilder(pageSize, this.MARGINS, new SVGMeasure());

return builder.layoutDocument(
docDefinition.content,
Expand Down
125 changes: 125 additions & 0 deletions tests/integration/pageMargins.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
'use strict';

var assert = require('assert');

var integrationTestHelper = require('./integrationTestHelper');

describe('Integration test: pageMargins', function () {
it('margins values with function using different values for even and odd pages', function () {
function marginFn(pageNumber) {
if (pageNumber % 2 === 0) {
return 10;
}
return [20, 30];
}

var testHelper = new integrationTestHelper({ margins: marginFn });

var pages = testHelper.renderPages('A7', {
content: [
{ text: 'First page', pageBreak: 'after' },
{ text: 'Second page', pageBreak: 'after' },
{ text: 'Third page', pageBreak: 'after' },
{ text: 'Fourth page'},
]
});

assert.equal(pages.length, 4);
assert.deepEqual(pages[0].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[1].pageMargins, { left: 10, right: 10, top: 10, bottom: 10 });
assert.deepEqual(pages[2].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[3].pageMargins, { left: 10, right: 10, top: 10, bottom: 10 });
});

it('margins values when function use dynamic left value', function () {
function marginFn(pageNumber) {
return [(pageNumber %2 === 0) ? 0 : 20, 30, 30, 30];
}

var testHelper = new integrationTestHelper({ margins: marginFn });

var pages = testHelper.renderPages('A7', {
content: [
{ text: 'First page', pageBreak: 'after' },
{ text: 'Second page', pageBreak: 'after' },
{ text: 'Third page', pageBreak: 'after' },
{ text: 'Fourth page'},
]
});

assert.equal(pages.length, 4);
assert.deepEqual(pages[0].pageMargins, { left: 20, right: 30, top: 30, bottom: 30 });
assert.deepEqual(pages[1].pageMargins, { left: 0, right: 30, top: 30, bottom: 30 });
assert.deepEqual(pages[2].pageMargins, { left: 20, right: 30, top: 30, bottom: 30 });
assert.deepEqual(pages[3].pageMargins, { left: 0, right: 30, top: 30, bottom: 30 });
});

it('margins values when function use dynamic top value', function () {
function marginFn(pageNumber) {
return [30, (pageNumber % 2 === 0) ? 0 : 20, 30, 30];
}

var testHelper = new integrationTestHelper({ margins: marginFn });

var pages = testHelper.renderPages('A7', {
content: [
{ text: 'First page', pageBreak: 'after' },
{ text: 'Second page', pageBreak: 'after' },
{ text: 'Third page', pageBreak: 'after' },
{ text: 'Fourth page'},
]
});

assert.equal(pages.length, 4);
assert.deepEqual(pages[0].pageMargins, { left: 30, right: 30, top: 20, bottom: 30 });
assert.deepEqual(pages[1].pageMargins, { left: 30, right: 30, top: 0, bottom: 30 });
assert.deepEqual(pages[2].pageMargins, { left: 30, right: 30, top: 20, bottom: 30 });
assert.deepEqual(pages[3].pageMargins, { left: 30, right: 30, top: 0, bottom: 30 });
});

it('margins values when function use dynamic horizontal value', function () {
function marginFn(pageNumber) {
return [(pageNumber %2 === 0) ? 0 : 20, 30];
}

var testHelper = new integrationTestHelper({ margins: marginFn });

var pages = testHelper.renderPages('A7', {
content: [
{ text: 'First page', pageBreak: 'after' },
{ text: 'Second page', pageBreak: 'after' },
{ text: 'Third page', pageBreak: 'after' },
{ text: 'Fourth page'},
]
});

assert.equal(pages.length, 4);
assert.deepEqual(pages[0].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[1].pageMargins, { left: 0, right: 0, top: 30, bottom: 30 });
assert.deepEqual(pages[2].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[3].pageMargins, { left: 0, right: 0, top: 30, bottom: 30 });
});

it('margins values when function use dynamic vertical value', function () {
function marginFn(pageNumber) {
return [20, (pageNumber %2 === 0) ? 0 : 30];
}

var testHelper = new integrationTestHelper({ margins: marginFn });

var pages = testHelper.renderPages('A7', {
content: [
{ text: 'First page', pageBreak: 'after' },
{ text: 'Second page', pageBreak: 'after' },
{ text: 'Third page', pageBreak: 'after' },
{ text: 'Fourth page'},
]
});

assert.equal(pages.length, 4);
assert.deepEqual(pages[0].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[1].pageMargins, { left: 20, right: 20, top: 0, bottom: 0 });
assert.deepEqual(pages[2].pageMargins, { left: 20, right: 20, top: 30, bottom: 30 });
assert.deepEqual(pages[3].pageMargins, { left: 20, right: 20, top: 0, bottom: 0 });
});
});
2 changes: 1 addition & 1 deletion tests/unit/LayoutBuilder.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1910,7 +1910,7 @@ describe('LayoutBuilder', function () {

builder.layoutDocument(docStructure, pdfDocument, styleDictionary, defaultStyle, background, header, footer, watermark, pageBreakBeforeFunction);

assert.deepEqual(pageBreakBeforeFunction.getCall(0).args[0].startPosition, { pageNumber: 1, left: 40, top: 40, verticalRatio: 0, horizontalRatio: 0, pageOrientation: 'portrait', pageInnerHeight: 720, pageInnerWidth: 320 });
assert.deepEqual(pageBreakBeforeFunction.getCall(0).args[0].startPosition, { pageNumber: 1, left: 40, top: 40, verticalRatio: 0, horizontalRatio: 0, pageOrientation: 'portrait', pageInnerHeight: 720, pageInnerWidth: 320, pageMargins: { left: 40, top: 40, right: 40, bottom: 40 } });
});

it('should provide the pageOrientation of the node', function () {
Expand Down
Loading