diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index dae0a69..508b8fb 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -37,12 +37,12 @@ jobs: - name: Unit Testing run: yarn test:coverage -# - name: Upload Coverage -# uses: codecov/codecov-action@v3 -# with: -# # token: ${{secrets.CODECOV_TOKEN}} # not required for public repos -# directory: coverage -# fail_ci_if_error: true + - name: Upload Coverage + uses: codecov/codecov-action@v3 + with: + token: ${{secrets.CODECOV_TOKEN}} # not required for public repos + directory: coverage + fail_ci_if_error: true - name: Build run: yarn build diff --git a/__tests__/compress.spec.ts b/__tests__/compress.spec.ts index 69756e3..4766db0 100644 --- a/__tests__/compress.spec.ts +++ b/__tests__/compress.spec.ts @@ -11,7 +11,11 @@ describe('compress', () => { ); const originalFile = await fs.promises.readFile(originalFilePath); - const originalPDF = await testHelper.parsePDF(originalFilePath); + const originalPDF = await testHelper.parsePDF({ + data: originalFile, + url: 0, + range: '', + }); const compressedFile = await compress(originalFilePath); const compressedPDF = await testHelper.parsePDF(compressedFile); @@ -21,4 +25,36 @@ describe('compress', () => { expect(compressedPDF.numrender).toEqual(originalPDF.numrender); expect(compressedPDF.text).toEqual(originalPDF.text); }); + + it('should compress a protected pdf file', async () => { + const originalFilePath = path.resolve( + __dirname, + '../examples/A17_FlightPlan-protected.pdf' + ); + + const originalFile = await fs.promises.readFile(originalFilePath); + // https://gitlab.com/autokent/pdf-parse/-/merge_requests/4 + const originalPDF = await testHelper.parsePDF({ + data: originalFile, + url: 0, + range: '', + password: 'a17', + }); + + const compressedFile = await compress(originalFilePath, { + pdfPassword: 'a17', + }); + // https://gitlab.com/autokent/pdf-parse/-/merge_requests/4 + const compressedPDF = await testHelper.parsePDF({ + data: compressedFile, + url: 0, + range: '', + password: 'a17', + }); + + expect(compressedFile.length).toBeLessThan(originalFile.length); + expect(compressedPDF.numpages).toEqual(originalPDF.numpages); + expect(compressedPDF.numrender).toEqual(originalPDF.numrender); + expect(compressedPDF.text).toEqual(originalPDF.text); + }); }); diff --git a/__tests__/test.helper.ts b/__tests__/test.helper.ts index e686546..966b3af 100644 --- a/__tests__/test.helper.ts +++ b/__tests__/test.helper.ts @@ -1,4 +1,3 @@ -import fs from 'fs'; import type pdfParser from 'pdf-parse'; // https://gitlab.com/autokent/pdf-parse/-/issues/24 @@ -6,16 +5,10 @@ import type pdfParser from 'pdf-parse'; const PDFParser = require('pdf-parse'); export async function parsePDF( - file: string | Buffer, + src: unknown, options?: pdfParser.Options ): Promise { - let dataBuffer: Buffer; - - if (typeof file === 'string') { - dataBuffer = fs.readFileSync(file); - } else dataBuffer = file; - - const data = await PDFParser(dataBuffer, options); + const data = await PDFParser(src, options); return data; } diff --git a/examples/A17_FlightPlan-protected.pdf b/examples/A17_FlightPlan-protected.pdf new file mode 100644 index 0000000..bcf673b Binary files /dev/null and b/examples/A17_FlightPlan-protected.pdf differ diff --git a/examples/compressed_pdf.pdf b/examples/compressed_pdf.pdf deleted file mode 100644 index d0e4a65..0000000 Binary files a/examples/compressed_pdf.pdf and /dev/null differ diff --git a/examples/protected.ts b/examples/protected.ts new file mode 100644 index 0000000..bef4403 --- /dev/null +++ b/examples/protected.ts @@ -0,0 +1,11 @@ +import path from 'path'; +import fs from 'fs'; +import { compress } from '@/index'; + +(async () => { + const pdf = path.resolve(__dirname, 'A17_FlightPlan-protected.pdf'); + const buffer = await compress(pdf, { pdfPassword: 'a17' }); + + const compressedPdf = path.resolve(__dirname, 'compressed_protected_pdf.pdf'); + await fs.promises.writeFile(compressedPdf, buffer); +})(); diff --git a/src/compress.ts b/src/compress.ts index edb60a9..cdd42e0 100644 --- a/src/compress.ts +++ b/src/compress.ts @@ -14,13 +14,19 @@ const defaultOptions: Required = { resolution: 'ebook', imageQuality: 100, gsModule: getBinPath(os.platform()), + pdfPassword: '', + removePasswordAfterCompression: false, }; async function compress(file: string | Buffer, options?: Options) { - const { resolution, imageQuality, compatibilityLevel, gsModule } = defaults( - options, - defaultOptions - ); + const { + resolution, + imageQuality, + compatibilityLevel, + gsModule, + pdfPassword, + removePasswordAfterCompression, + } = defaults(options, defaultOptions); const output = path.resolve(os.tmpdir(), Date.now().toString()); @@ -28,13 +34,37 @@ async function compress(file: string | Buffer, options?: Options) { let tempFile: string | undefined; if (typeof file === 'string') { - command = `${gsModule} -q -dNOPAUSE -dBATCH -dSAFER -dSimulateOverprint=true -sDEVICE=pdfwrite -dCompatibilityLevel=${compatibilityLevel} -dPDFSETTINGS=/${resolution} -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=${imageQuality} -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=${imageQuality} -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=${imageQuality} -sOutputFile=${output} ${file}`; + command = `${gsModule} -q -dNOPAUSE -dBATCH -dSAFER -dSimulateOverprint=true -sDEVICE=pdfwrite -dCompatibilityLevel=${compatibilityLevel} -dPDFSETTINGS=/${resolution} -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=${imageQuality} -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=${imageQuality} -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=${imageQuality} -sOutputFile=${output}`; + + if (pdfPassword) { + command = command.concat(` -sPDFPassword=${pdfPassword}`); + } + + if (!removePasswordAfterCompression) { + command = command.concat( + ` -sOwnerPassword=${pdfPassword} -sUserPassword=${pdfPassword}` + ); + } + + command = command.concat(` ${file}`); } else { tempFile = path.resolve(os.tmpdir(), (Date.now() * 2).toString()); await fs.promises.writeFile(tempFile, file); - command = `${gsModule} -q -dNOPAUSE -dBATCH -dSAFER -dSimulateOverprint=true -sDEVICE=pdfwrite -dCompatibilityLevel=${compatibilityLevel} -dPDFSETTINGS=/${resolution} -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=${imageQuality} -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=${imageQuality} -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=${imageQuality} -sOutputFile=${output} ${tempFile}`; + command = `${gsModule} -q -dNOPAUSE -dBATCH -dSAFER -dSimulateOverprint=true -sDEVICE=pdfwrite -dCompatibilityLevel=${compatibilityLevel} -dPDFSETTINGS=/${resolution} -dEmbedAllFonts=true -dSubsetFonts=true -dAutoRotatePages=/None -dColorImageDownsampleType=/Bicubic -dColorImageResolution=${imageQuality} -dGrayImageDownsampleType=/Bicubic -dGrayImageResolution=${imageQuality} -dMonoImageDownsampleType=/Bicubic -dMonoImageResolution=${imageQuality} -sOutputFile=${output}`; + + if (pdfPassword) { + command = command.concat(` -sPDFPassword=${pdfPassword}`); + } + + if (!removePasswordAfterCompression) { + command = command.concat( + ` -sOwnerPassword=${pdfPassword} -sUserPassword=${pdfPassword}` + ); + } + + command = command.concat(` ${tempFile}`); } await exec(command); diff --git a/src/types.ts b/src/types.ts index a3fa373..4e45dda 100644 --- a/src/types.ts +++ b/src/types.ts @@ -34,4 +34,12 @@ export type Options = { * `You can download binaries in releases section inside any version of this repository.` */ gsModule?: string; + /** + * The pdf password + */ + pdfPassword?: string; + /** + * Remove password of a protected pdf, after compression + */ + removePasswordAfterCompression?: boolean; };