Skip to content

Commit

Permalink
working page number post render
Browse files Browse the repository at this point in the history
  • Loading branch information
Blake Holifield committed Sep 17, 2024
1 parent bfd80a9 commit bfd2f0d
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 52 deletions.
26 changes: 7 additions & 19 deletions src/browser/clusterTask.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import { PDFDocument } from 'pdf-lib';

// Match the timeout on the gateway
const BROWSER_TIMEOUT = 60_000;
const pdfCache = PdfCache.getInstance();

const getNewPdfName = (id: string) => {
const pdfFilename = `report_${id}.pdf`;
Expand All @@ -30,20 +29,20 @@ export const generatePdf = async (
authHeader,
authCookie,
}: PdfRequestBody,
collectionId: string
collectionId: string,
order: number
): Promise<void> => {
const pdfPath = getNewPdfName(componentId);
await cluster.queue(async ({ page }: { page: Page }) => {
const updateMessage = {
status: PdfStatus.Generating,
filepath: '',
order: order,
componentId: componentId,
collectionId,
};
await UpdateStatus(updateMessage);
await page.setViewport({ width: pageWidth, height: pageHeight });
const offsetSize = pdfCache.getTotalPagesForCollection(collectionId);
apiLogger.debug(`PDF offset by: ${offsetSize}`);
// Enables console logging in Headless mode - handy for debugging components
page.on('console', (msg) => apiLogger.info(`[Headless log] ${msg.text()}`));

Expand Down Expand Up @@ -122,6 +121,7 @@ export const generatePdf = async (
filepath: '',
componentId: componentId,
error: response,
order: order,
};
UpdateStatus(updated);
PdfCache.getInstance().invalidateCollection(collectionId, response);
Expand All @@ -138,6 +138,7 @@ export const generatePdf = async (
status: PdfStatus.Failed,
filepath: '',
componentId: componentId,
order: order,
error: pageResponse?.statusText() || 'Page status not found',
};
UpdateStatus(updated);
Expand All @@ -153,20 +154,6 @@ export const generatePdf = async (
}

const { headerTemplate, footerTemplate } = getHeaderAndFooterTemplates();
// Pain.
await page.addStyleTag({
content: '.empty-page { page-break-after: always; visibility: hidden; }',
});
await page.evaluate((offsetSize) => {
Array.from({ length: offsetSize }).forEach(() => {
const emptyPage = document.createElement('div');
emptyPage.className = 'empty-page';
emptyPage.textContent = 'empty';
document.body.prepend(emptyPage);
return emptyPage;
});
}, offsetSize);
const pageRange = `${offsetSize + 1}-`;

try {
const buffer = await page.pdf({
Expand All @@ -180,7 +167,6 @@ export const generatePdf = async (
displayHeaderFooter: true,
headerTemplate,
footerTemplate,
pageRanges: pageRange,
timeout: BROWSER_TIMEOUT,
});
await uploadPDF(componentId, pdfPath).catch((error: unknown) => {
Expand All @@ -195,6 +181,7 @@ export const generatePdf = async (
filepath: pdfPath,
componentId: componentId,
numPages: numPages,
order: order,
};
UpdateStatus(updated);
} catch (error: unknown) {
Expand All @@ -203,6 +190,7 @@ export const generatePdf = async (
status: PdfStatus.Failed,
filepath: '',
componentId: componentId,
order: order,
error: JSON.stringify(error),
};
UpdateStatus(updated);
Expand Down
56 changes: 44 additions & 12 deletions src/common/pdfCache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import PDFMerger from 'pdf-merger-js';
import { downloadPDF, uploadPDF } from '../common/objectStore';
import os from 'os';
import fs from 'fs';
import { PDFDocument, PDFPage, Color, ColorTypes } from 'pdf-lib';

export enum PdfStatus {
Generating = 'Generating',
Expand Down Expand Up @@ -66,6 +67,36 @@ export type PDFComponent = {
componentId: string;
error?: string;
numPages?: number;
order?: number;
};

const addPageNumbers = async (pdfBuffer: Uint8Array): Promise<Uint8Array> => {
// Load the PDF
const pdfDoc = await PDFDocument.load(pdfBuffer);

// Get the number of pages
const pageCount = pdfDoc.getPageCount();
for (let i = 0; i < pageCount; i++) {
const page: PDFPage = pdfDoc.getPage(i);
const width = page.getWidth();
// Calculate position for page number
const xPosition = width / 2 - 10; // Position at the middle
const yPosition = 10; // Position from bottom edge (bottom is 0)

// Add text for page number
const black: Color = { red: 0, blue: 0, green: 0, type: ColorTypes.RGB };
const fontSize = 8;

page.drawText(`Page ${i + 1}`, {
x: xPosition,
y: yPosition,
size: fontSize,
color: black,
});
}

// Save the modified PDF and return as a Uint8Array
return await pdfDoc.save();
};

class PdfCache {
Expand Down Expand Up @@ -123,17 +154,15 @@ class PdfCache {
return [];
}

public getTotalPagesForCollection(collectionId: string) {
let pageCount = 0;
const components = this.getComponents(collectionId);
if (components?.length > 1) {
components.forEach((n) => {
pageCount += n.numPages || 0;
});
}
return pageCount;
}
// Function to sort PDFComponents by order
private sortComponents(components: PDFComponent[]): PDFComponent[] {
return components.slice().sort((a, b) => {
const orderA = a.order || Number.MAX_VALUE;
const orderB = b.order || Number.MAX_VALUE;

return orderA - orderB;
});
}
private updateCollectionState(
collectionId: string,
status: PdfStatus,
Expand Down Expand Up @@ -258,6 +287,8 @@ class PdfCache {
);
return;
}
// Sort the pages for the correct finalized PDF order
const sortedSlices = this.sortComponents(collection.components);
apiLogger.debug(`Merging slices for collection ${collectionId}`);
const tmpdir = `/tmp/${collectionId}-components/*`;
ensureDirSync(tmpdir);
Expand All @@ -266,16 +297,17 @@ class PdfCache {
// Since we can merge the PDFs without saving them to disk, we
// can sequentially grab all the s3 stored PDFs as a UINT8 array
// and merge them in memory much faster than writing to disk
for (const component of collection.components) {
for (const component of sortedSlices) {
const response = await downloadPDF(component.componentId);
const stream = await response?.Body?.transformToByteArray();
// TODO: It might be better to throw an error if stream is null,
// but the error passes down more accurately this way
await merger.add(stream!);
}
const buffer = await merger.saveAsBuffer();
const completed = await addPageNumbers(buffer);
const path = `${os.tmpdir()}/${collectionId}`;
fs.writeFileSync(path, buffer);
fs.writeFileSync(path, completed);
apiLogger.debug(`${path} written to disk`);
await uploadPDF(collectionId, path);
apiLogger.debug(`${collectionId} written to s3`);
Expand Down
4 changes: 2 additions & 2 deletions src/server/cluster.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ import { CHROMIUM_PATH } from '../browser/helpers';
export const GetPupCluster = async () => {
const cluster = await Cluster.launch({
concurrency: Cluster.CONCURRENCY_CONTEXT,
maxConcurrency: 1,
maxConcurrency: 3,
// If a queued task fails, how many times will it retry before returning an error
retryLimit: 1,
retryLimit: 2,
puppeteerOptions: {
timeout: BROWSER_TIMEOUT,
...(config?.IS_PRODUCTION
Expand Down
7 changes: 1 addition & 6 deletions src/server/render-template/Footer.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
import FooterContainer from './FooterContainer';
import PagesMarker from './PagesMaker';

const Footer = () => (
<FooterContainer>
<PagesMarker />
</FooterContainer>
);
const Footer = () => <FooterContainer></FooterContainer>;

export default Footer;
20 changes: 8 additions & 12 deletions src/server/routes/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,30 +188,26 @@ router.post(
? req.body.payload
: [req.body.payload];

const configHeaders: string | string[] | undefined =
req.headers[config?.OPTIONS_HEADER_NAME];
if (configHeaders) {
delete req.headers[config?.OPTIONS_HEADER_NAME];
}

try {
const requiredCalls = requestConfigs.length;
if (requiredCalls === 1) {
const pdfDetails = getPdfRequestBody(requestConfigs[0]);
const configHeaders: string | string[] | undefined =
req.headers[config?.OPTIONS_HEADER_NAME];
if (configHeaders) {
delete req.headers[config?.OPTIONS_HEADER_NAME];
}
apiLogger.debug(`Single call to generator queued for ${collectionId}`);
pdfCache.setExpectedLength(collectionId, requiredCalls);
generatePdf(pdfDetails, collectionId);
generatePdf(pdfDetails, collectionId, 1);
return res.status(202).send({ statusID: collectionId });
}
pdfCache.setExpectedLength(collectionId, requiredCalls);
apiLogger.debug(`Queueing ${requiredCalls} for ${collectionId}`);
for (let x = 0; x < Number(requiredCalls); x++) {
const pdfDetails = getPdfRequestBody(requestConfigs[x]);
const configHeaders: string | string[] | undefined =
req.headers[config?.OPTIONS_HEADER_NAME];
if (configHeaders) {
delete req.headers[config?.OPTIONS_HEADER_NAME];
}
generatePdf(pdfDetails, collectionId);
generatePdf(pdfDetails, collectionId, x + 1);
}

return res.status(202).send({ statusID: collectionId });
Expand Down
2 changes: 1 addition & 1 deletion src/server/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ import PdfCache, { PDFComponent } from '../common/pdfCache';
const pdfCache = PdfCache.getInstance();

export const UpdateStatus = async (updateMessage: PDFComponent) => {
pdfCache.addToCollection(updateMessage.collectionId, updateMessage);
await produceMessage(UPDATE_TOPIC, updateMessage)
.then(() => {
apiLogger.debug('Generating message sent');
})
.catch((error: unknown) => {
apiLogger.error(`Kafka message not sent: ${error}`);
});
pdfCache.addToCollection(updateMessage.collectionId, updateMessage);
pdfCache.verifyCollection(updateMessage.collectionId);
};

Expand Down

0 comments on commit bfd2f0d

Please sign in to comment.