Skip to content

Commit

Permalink
#1075 initial implementation of the pdf/ua support
Browse files Browse the repository at this point in the history
  • Loading branch information
pofider committed Nov 21, 2023
1 parent 647e394 commit 7a556e4
Show file tree
Hide file tree
Showing 15 changed files with 1,413 additions and 834 deletions.
241 changes: 121 additions & 120 deletions packages/jsreport-pdf-utils/lib/main.js
Original file line number Diff line number Diff line change
@@ -1,120 +1,121 @@
const path = require('path')

const missingSecretMessage = 'pdf-sign extension uses encryption to store sensitive data and needs secret key to be defined. Please fill "encryption.secretKey" at the root of the config or disable encryption using "encryption.enabled=false".'

module.exports = (reporter, definition) => {
reporter.documentStore.registerComplexType('PdfOperationType', {
templateShortid: { type: 'Edm.String', referenceTo: 'templates' },
type: { type: 'Edm.String' },
mergeToFront: { type: 'Edm.Boolean' },
renderForEveryPage: { type: 'Edm.Boolean' },
mergeWholeDocument: { type: 'Edm.Boolean' },
enabled: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfMetaType', {
title: { type: 'Edm.String' },
author: { type: 'Edm.String' },
subject: { type: 'Edm.String' },
keywords: { type: 'Edm.String' },
creator: { type: 'Edm.String' },
producer: { type: 'Edm.String' },
language: { type: 'Edm.String' },
custom: { type: 'Edm.String' }
})

reporter.documentStore.registerComplexType('PdfAType', {
enabled: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfAccessibility', {
enabled: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfPasswordType', {
password: { type: 'Edm.String' },
ownerPassword: { type: 'Edm.String' },
printing: { type: 'Edm.String', schema: { type: 'null' } },
modifying: { type: 'Edm.Boolean' },
copying: { type: 'Edm.Boolean' },
annotating: { type: 'Edm.Boolean' },
fillingForms: { type: 'Edm.Boolean' },
contentAccessibility: { type: 'Edm.Boolean' },
documentAssembly: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfSignTemplateType', {
certificateAssetShortid: { type: 'Edm.String', referenceTo: 'assets', schema: { type: 'null' } },
reason: { type: 'Edm.String' }
})

if (reporter.compilation) {
reporter.compilation.resource('pdfjs-dist-lib-files', path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'lib'))
reporter.compilation.resource('pdfjs-dist-legacy-build-files', path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'legacy/build'))
}

if (reporter.documentStore.model.entityTypes.TemplateType) {
reporter.documentStore.model.entityTypes.TemplateType.pdfOperations = { type: 'Collection(jsreport.PdfOperationType)' }
reporter.documentStore.model.entityTypes.TemplateType.pdfMeta = { type: 'jsreport.PdfMetaType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfA = { type: 'jsreport.PdfAType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfPassword = { type: 'jsreport.PdfPasswordType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfSign = { type: 'jsreport.PdfSignTemplateType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfAccessibility = { type: 'jsreport.PdfAccessibility', schema: { type: 'null' } }
}

reporter.documentStore.on('before-init', () => {
if (reporter.documentStore.model.entityTypes.AssetType) {
reporter.documentStore.registerComplexType('PdfSignAssetType', {
passwordRaw: { type: 'Edm.String', visible: false },
passwordSecure: { type: 'Edm.String', encrypted: true, visible: false },
passwordFilled: { type: 'Edm.Boolean' }
})

reporter.documentStore.model.entityTypes.AssetType.pdfSign = { type: 'jsreport.PdfSignAssetType' }
}
})

reporter.initializeListeners.add('pdf-utils', async (req, res) => {
if (reporter.documentStore.collection('assets') == null) {
return
}

reporter.documentStore.collection('assets').beforeInsertListeners.add('pdf-sign', async (doc, req) => {
if (!doc.pdfSign || !doc.pdfSign.passwordRaw) {
return
}

try {
doc.pdfSign.passwordSecure = await reporter.encryption.encrypt(doc.pdfSign.passwordRaw)
} catch (e) {
if (e.encryptionNoSecret) {
e.message = missingSecretMessage
}

throw e
}

doc.pdfSign.passwordRaw = null
doc.pdfSign.passwordFilled = true
})

reporter.documentStore.collection('assets').beforeUpdateListeners.add('pdf-sign', async (q, u, req) => {
if (!u.$set.pdfSign || !u.$set.pdfSign.passwordRaw) {
return
}

try {
u.$set.pdfSign.passwordSecure = await reporter.encryption.encrypt(u.$set.pdfSign.passwordRaw)
} catch (e) {
if (e.encryptionNoSecret) {
e.message = missingSecretMessage
}

throw e
}

u.$set.pdfSign.passwordRaw = null
u.$set.pdfSign.passwordFilled = true
})
})
}
const path = require('path')

const missingSecretMessage = 'pdf-sign extension uses encryption to store sensitive data and needs secret key to be defined. Please fill "encryption.secretKey" at the root of the config or disable encryption using "encryption.enabled=false".'

module.exports = (reporter, definition) => {
reporter.documentStore.registerComplexType('PdfOperationType', {
templateShortid: { type: 'Edm.String', referenceTo: 'templates' },
type: { type: 'Edm.String' },
mergeToFront: { type: 'Edm.Boolean' },
renderForEveryPage: { type: 'Edm.Boolean' },
mergeWholeDocument: { type: 'Edm.Boolean' },
enabled: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfMetaType', {
title: { type: 'Edm.String' },
author: { type: 'Edm.String' },
subject: { type: 'Edm.String' },
keywords: { type: 'Edm.String' },
creator: { type: 'Edm.String' },
producer: { type: 'Edm.String' },
language: { type: 'Edm.String' },
custom: { type: 'Edm.String' }
})

reporter.documentStore.registerComplexType('PdfAType', {
enabled: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfAccessibility', {
enabled: { type: 'Edm.Boolean' },
pdfUA: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfPasswordType', {
password: { type: 'Edm.String' },
ownerPassword: { type: 'Edm.String' },
printing: { type: 'Edm.String', schema: { type: 'null' } },
modifying: { type: 'Edm.Boolean' },
copying: { type: 'Edm.Boolean' },
annotating: { type: 'Edm.Boolean' },
fillingForms: { type: 'Edm.Boolean' },
contentAccessibility: { type: 'Edm.Boolean' },
documentAssembly: { type: 'Edm.Boolean' }
})

reporter.documentStore.registerComplexType('PdfSignTemplateType', {
certificateAssetShortid: { type: 'Edm.String', referenceTo: 'assets', schema: { type: 'null' } },
reason: { type: 'Edm.String' }
})

if (reporter.compilation) {
reporter.compilation.resource('pdfjs-dist-lib-files', path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'lib'))
reporter.compilation.resource('pdfjs-dist-legacy-build-files', path.join(path.dirname(require.resolve('pdfjs-dist/package.json')), 'legacy/build'))
}

if (reporter.documentStore.model.entityTypes.TemplateType) {
reporter.documentStore.model.entityTypes.TemplateType.pdfOperations = { type: 'Collection(jsreport.PdfOperationType)' }
reporter.documentStore.model.entityTypes.TemplateType.pdfMeta = { type: 'jsreport.PdfMetaType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfA = { type: 'jsreport.PdfAType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfPassword = { type: 'jsreport.PdfPasswordType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfSign = { type: 'jsreport.PdfSignTemplateType', schema: { type: 'null' } }
reporter.documentStore.model.entityTypes.TemplateType.pdfAccessibility = { type: 'jsreport.PdfAccessibility', schema: { type: 'null' } }
}

reporter.documentStore.on('before-init', () => {
if (reporter.documentStore.model.entityTypes.AssetType) {
reporter.documentStore.registerComplexType('PdfSignAssetType', {
passwordRaw: { type: 'Edm.String', visible: false },
passwordSecure: { type: 'Edm.String', encrypted: true, visible: false },
passwordFilled: { type: 'Edm.Boolean' }
})

reporter.documentStore.model.entityTypes.AssetType.pdfSign = { type: 'jsreport.PdfSignAssetType' }
}
})

reporter.initializeListeners.add('pdf-utils', async (req, res) => {
if (reporter.documentStore.collection('assets') == null) {
return
}

reporter.documentStore.collection('assets').beforeInsertListeners.add('pdf-sign', async (doc, req) => {
if (!doc.pdfSign || !doc.pdfSign.passwordRaw) {
return
}

try {
doc.pdfSign.passwordSecure = await reporter.encryption.encrypt(doc.pdfSign.passwordRaw)
} catch (e) {
if (e.encryptionNoSecret) {
e.message = missingSecretMessage
}

throw e
}

doc.pdfSign.passwordRaw = null
doc.pdfSign.passwordFilled = true
})

reporter.documentStore.collection('assets').beforeUpdateListeners.add('pdf-sign', async (q, u, req) => {
if (!u.$set.pdfSign || !u.$set.pdfSign.passwordRaw) {
return
}

try {
u.$set.pdfSign.passwordSecure = await reporter.encryption.encrypt(u.$set.pdfSign.passwordRaw)
} catch (e) {
if (e.encryptionNoSecret) {
e.message = missingSecretMessage
}

throw e
}

u.$set.pdfSign.passwordRaw = null
u.$set.pdfSign.passwordFilled = true
})
})
}
4 changes: 4 additions & 0 deletions packages/jsreport-pdf-utils/lib/pdfManipulator.js
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,10 @@ module.exports = (contentBuffer, { pdfMeta, pdfPassword, pdfSign, pdfA, outlines
doc.pdfA()
}

if (pdfAccessibility?.pdfUA === true) {
doc.pdfUA()
}

try {
currentBuffer = await doc.asBuffer()
} catch (e) {
Expand Down
24 changes: 19 additions & 5 deletions packages/jsreport-pdf-utils/lib/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ module.exports = (reporter, definition) => {

reporter.addRequestContextMetaConfig('pdfUtilsForms', { sandboxHidden: true })
reporter.addRequestContextMetaConfig('pdfUtilsOutlines', { sandboxHidden: true })
reporter.addRequestContextMetaConfig('pdfUtilsAccessibility', { sandboxHidden: true })

reporter.extendProxy(proxyExtend)

Expand Down Expand Up @@ -94,11 +95,28 @@ module.exports = (reporter, definition) => {
return helpersScript
})

reporter.beforeRenderListeners.add('pdf-utils', (req, res) => {
// we use just root template setting for pdfAccessibility
// this is because otherwise you need to set the accessibility on every merged/template
// the use case when you would want root template to have for example pdf/ua enabled but not some child renders doesn't exist I tihnk
if (!req.context.pdfUtilsAccessibility && !req.template.pdfAccessibility) {
return
}

if (req.context.pdfUtilsAccessibility) {
req.template.pdfAccessibility = req.context.pdfUtilsAccessibility
} else {
req.context.pdfUtilsAccessibility = req.template.pdfAccessibility
}
})

// we insert to the front so we can run before reports or scripts
reporter.afterRenderListeners.insert(0, 'pdf-utils', async (req, res) => {
if (
req.template.pdfPassword == null &&
req.template.pdfMeta == null &&
req.template.pdfAccessibility?.enabled !== true &&
req.template.pdfAccessibility?.pdfUA !== true &&
req.template.pdfA?.enabled !== true &&
req.template.pdfSign == null &&
(!req.template.pdfOperations || req.template.pdfOperations.length === 0) &&
Expand Down Expand Up @@ -181,10 +199,6 @@ module.exports = (reporter, definition) => {

reporter.logger.info('pdf-utils is starting pdf processing', req)

if (req.template.pdfAccessibility?.enabled) {
req.context.pdfAccessibility = { enabled: true }
}

try {
res.content = await (require('./pdfProcessing.js')(
{
Expand All @@ -193,7 +207,7 @@ module.exports = (reporter, definition) => {
outlines: req.context.pdfUtilsOutlines,
pdfMeta: req.template.pdfMeta,
pdfA: req.template.pdfA,
pdfAccessibility: req.template.pdfAccessibility || req.context.pdfAccessibility,
pdfAccessibility: req.context.pdfUtilsAccessibility,
pdfPassword,
pdfSign,
removeHiddenMarks: !req.options.pdfUtils || req.options.pdfUtils.removeHiddenMarks !== false
Expand Down

0 comments on commit 7a556e4

Please sign in to comment.