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

feat: Support image conversion for PDF URLs #111

Open
wants to merge 2 commits into
base: main
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 changes: 2 additions & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@
{
"singleQuote": true,
"parser": "typescript",
"printWidth": 120
"printWidth": 120,
"endOfLine": "auto"
}
],

Expand Down
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ $ npm install
# Build all packages
$ npm run build

# Start server, to use interfaces you need to build them beforehand
# Start server; some steps required the first time, see below
$ npm run start

# Wipe build/ and dist/ folders, build all packages, package to exe and copy dependencies/resources to build folder
Expand All @@ -35,5 +35,19 @@ $ npm run install:exec

```

## First time running with npm

The server needs terrain data before it can run, so first do a full build:

```bash
$ npm run build:exec
```

This will build the terrain data in `build/terrain/`. This needs to be symlinked to the top level before running with npm:

```bash
mklink /J .\terrain .\build\terrain
```

## Documentation
Start the server and direct to `localhost:8380/api` for API documentation
58 changes: 58 additions & 0 deletions apps/server/src/utilities/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ export class FileService {
return getDocument({ data: retrievedFile }).promise.then((document) => document.numPages);
}

async getNumberOfPdfPagesFromUrl(url: string): Promise<number> {
const doc = await this.getPdfFromUrl(url);
return doc.numPages;
}

/**
* Calling this function checks the safety of the supplied file path and throws an error if it deemed not safe against various potential attacks.
* @param filePath
Expand All @@ -127,6 +132,59 @@ export class FileService {
}
}

async getPdfFromUrl(url: string): Promise<PDFDocumentProxy> {
if (this.pdfCache.has(url)) {
return this.pdfCache.get(url);
}

const resp = await fetch(url);
if (!resp.ok) {
throw new Error('encountered error retrieving PDF file');
}

const data = new Uint8Array(await resp.arrayBuffer());

// Some PDFs need external cmaps.
const CMAP_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`;
const CMAP_PACKED = true;

// Where the standard fonts are located.
const STANDARD_FONT_DATA_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`;

// Load the PDF file.
const pdfDocument = await getDocument({
data,
cMapUrl: CMAP_URL,
cMapPacked: CMAP_PACKED,
standardFontDataUrl: STANDARD_FONT_DATA_URL,
}).promise;

this.pdfCache.set(url, pdfDocument);
return pdfDocument;
}

async getConvertedPdfFileFromUrl(url: string, pageNumber: number, scale: number = 4): Promise<StreamableFile> {
try {
const pngKey = `${url};;${pageNumber};;${scale}`;
if (this.pngCache.has(pngKey)) {
return new StreamableFile(this.pngCache.get(pngKey));
}

const file = await this.getPdfFromUrl(url);
const pngBuffer = await pdfToPng(file, pageNumber, scale);

if (!this.pngCache.has(pngKey)) {
this.pngCache.set(pngKey, pngBuffer);
}

return new StreamableFile(pngBuffer);
} catch (err) {
const message = `Error converting PDF to PNG: ${url}`;
this.logger.log(message, err);
throw new HttpException(message, HttpStatus.INTERNAL_SERVER_ERROR);
}
}

async getConvertedPdfFile(
directory: string,
fileName: string,
Expand Down
33 changes: 33 additions & 0 deletions apps/server/src/utilities/utilities.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,39 @@ export class UtilityController {
return convertedPdfFile;
}

@Get('pdf/fromUrl')
@ApiResponse({
status: 200,
description: 'A streamed converted png image',
type: StreamableFile,
})
async getPdfFromUrl(
@Query('encodedUrl') encodedUrl: string,
@Query('pagenumber', ParseIntPipe) pagenumber: number,
@Response({ passthrough: true }) res,
): Promise<StreamableFile> {
const url = decodeURIComponent(encodedUrl);
const convertedPdfFile = await this.fileService.getConvertedPdfFileFromUrl(`${url}`, pagenumber);

res.set({
'Content-Type': 'image/png',
'Content-Disposition': `attachment; filename=out-${pagenumber}.png`,
});

return convertedPdfFile;
}

@Get('pdf/fromUrl/numpages')
@ApiResponse({
status: 200,
description: 'Returns the number of pages in the pdf at the URL',
type: Number,
})
async getNumberOfPagesFromUrl(@Query('encodedUrl') encodedUrl: string): Promise<number> {
const url = decodeURIComponent(encodedUrl);
return this.fileService.getNumberOfPdfPagesFromUrl(url);
}

@Get('pdf/list')
@ApiResponse({
status: 200,
Expand Down
Loading