Skip to content

Commit

Permalink
resolves #207 create a browser-compatible version of the Document con…
Browse files Browse the repository at this point in the history
…verter

- Reduce the scope of the classes
- Use browserify to produce a browser-compatible file
- Inline files content (ie. replace readFileSync) to bundle everything into a single JavaScript file
- Add tests
  • Loading branch information
ggrossetie committed Sep 14, 2020
1 parent 83b9c42 commit 9b41d34
Show file tree
Hide file tree
Showing 12 changed files with 1,685 additions and 112 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules/
/test/output/*.pdf
/test/fixtures/*.html
/test/output/visual-comparison-workdir/
/dist/
11 changes: 5 additions & 6 deletions lib/document/document-converter.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,11 @@ class DocumentPDFConverter {
this.repeatTableElementsContent = fs.readFileSync(`${__dirname}/repeating-table-elements.js`, 'utf8')
this.pagedContent = fs.readFileSync(require.resolve('@ggrossetie/pagedjs/dist/paged.polyfill.js'), 'utf8')
this.pagedRendering = fs.readFileSync(`${__dirname}/paged-rendering.js`, 'utf8')
const stylesDirectoryPath = ospath.resolve(`${__dirname}/../../css`)
this.asciidoctorStyleContent = fs.readFileSync(`${stylesDirectoryPath}/asciidoctor.css`, 'utf8')
this.documentStyleContent = fs.readFileSync(`${stylesDirectoryPath}/document.css`, 'utf8')
this.titleDocumentStyleContent = fs.readFileSync(`${stylesDirectoryPath}/features/title-document-numbering.css`, 'utf8')
this.titlePageStyleContent = fs.readFileSync(`${stylesDirectoryPath}/features/title-page.css`, 'utf8')
this.bookStyleContent = fs.readFileSync(`${stylesDirectoryPath}/features/book.css`, 'utf8')
this.asciidoctorStyleContent = fs.readFileSync(`${__dirname}/../../css/asciidoctor.css`, 'utf8')
this.documentStyleContent = fs.readFileSync(`${__dirname}/../../css/document.css`, 'utf8')
this.titleDocumentStyleContent = fs.readFileSync(`${__dirname}/../../css/features/title-document-numbering.css`, 'utf8')
this.titlePageStyleContent = fs.readFileSync(`${__dirname}/../../css/features/title-page.css`, 'utf8')
this.bookStyleContent = fs.readFileSync(`${__dirname}/../../css/features/book.css`, 'utf8')
}

convert (node, transform, opts) {
Expand Down
93 changes: 48 additions & 45 deletions lib/document/paged-rendering.js
Original file line number Diff line number Diff line change
@@ -1,60 +1,63 @@
/* global Paged, PagedPolyfill */
window.AsciidoctorPDF = window.AsciidoctorPDF || {}
window.AsciidoctorPDF.status = 'rendering'
class PagedReadyHandler extends Paged.Handler {
afterRendered (pages) {
window.AsciidoctorPDF.status = 'ready'
}
;(function() {
window.AsciidoctorPDF = window.AsciidoctorPDF || {}
window.AsciidoctorPDF.status = 'rendering';

// BEGIN: workaround to prevent duplicated content at end/beginning of page
// https://gitlab.pagedmedia.org/tools/pagedjs/issues/126
constructor (chunker, polisher, caller) {
super(chunker, polisher, caller)
this.carriageReturn = String.fromCharCode(10)
}
class PagedReadyHandler extends Paged.Handler {
afterRendered (_) {
window.AsciidoctorPDF.status = 'ready'
}

checkNode (node) {
if (!node) return
if (node.nodeType !== 3) return
if (node.textContent === this.carriageReturn) {
node.remove()
// BEGIN: workaround to prevent duplicated content at end/beginning of page
// https://gitlab.pagedmedia.org/tools/pagedjs/issues/126
constructor (chunker, polisher, caller) {
super(chunker, polisher, caller)
this.carriageReturn = String.fromCharCode(10)
}
}

afterParsed (parsed) {
const template = document.querySelector('template').content
const breakAfterAvoidElements = template.querySelectorAll('[data-break-after="avoid"], [data-break-before="avoid"]')
for (const el of breakAfterAvoidElements) {
this.checkNode(el.previousSibling)
this.checkNode(el.nextSibling)
checkNode (node) {
if (!node) return
if (node.nodeType !== 3) return
if (node.textContent === this.carriageReturn) {
node.remove()
}
}
}
// END: workaround to prevent duplicated content at end/beginning of page
}

Paged.registerHandlers(PagedReadyHandler)
afterParsed (_) {
const template = document.querySelector('template').content
const breakAfterAvoidElements = template.querySelectorAll('[data-break-after="avoid"], [data-break-before="avoid"]')
for (const el of breakAfterAvoidElements) {
this.checkNode(el.previousSibling)
this.checkNode(el.nextSibling)
}
}
// END: workaround to prevent duplicated content at end/beginning of page
}

window.PagedConfig = window.PagedConfig || {}
Paged.registerHandlers(PagedReadyHandler)

window.PagedConfig.auto = false
window.PagedConfig = window.PagedConfig || {}
window.PagedConfig.auto = false

// same logic as pagedjs, but waiting for 'complete' instead of 'interactive'
const ready = new Promise(function (resolve, reject) {
if (document.readyState === 'complete') {
resolve(document.readyState)
return
}

document.onreadystatechange = function () {
const ready = new Promise(function (resolve, reject) {
if (document.readyState === 'complete') {
resolve(document.readyState)
return
}
}
})

ready.then(async function () {
const done = await PagedPolyfill.preview(window.PagedConfig.content, window.PagedConfig.stylesheets, window.PagedConfig.renderTo)
if (window.PagedConfig.after) {
await window.PagedConfig.after(done)
}
})
document.onreadystatechange = function () {
if (document.readyState === 'complete') {
resolve(document.readyState)
}
}
})

ready.then(async function () {
const done = await PagedPolyfill.preview(window.PagedConfig.content, window.PagedConfig.stylesheets, window.PagedConfig.renderTo)
if (window.PagedConfig.after) {
await window.PagedConfig.after(done)
}
})
})()

74 changes: 38 additions & 36 deletions lib/document/repeating-table-elements.js
Original file line number Diff line number Diff line change
@@ -1,48 +1,50 @@
/* global Paged */
class RepeatingTableElements extends Paged.Handler {
constructor (chunker, polisher, caller) { // eslint-disable-line no-useless-constructor
super(chunker, polisher, caller)
}
;(function () {
class RepeatingTableElements extends Paged.Handler {
constructor (chunker, polisher, caller) { // eslint-disable-line no-useless-constructor
super(chunker, polisher, caller)
}

afterPageLayout (pageElement, page, breakToken, chunker) {
// Find all split table elements
const tables = pageElement.querySelectorAll('table[data-split-from]')
afterPageLayout (pageElement, page, breakToken, chunker) {
// Find all split table elements
const tables = pageElement.querySelectorAll('table[data-split-from]')

tables.forEach((table) => {
// Get the reference UUID of the node
const ref = table.dataset.ref
// Find the node in the original source
const sourceTable = chunker.source.querySelector("[data-ref='" + ref + "']")
// Repeat the <thead> element (if exists)
this.repeatElement(sourceTable, table, ':scope > thead')
// Repeat the <colgroup> elements (if at least one exists)
this.repeatElements(sourceTable, table, ':scope > colgroup')
// Repeat the <caption> element (if exists)
this.repeatElement(sourceTable, table, ':scope > caption')
})
}

repeatElement (sourceTable, table, querySelector) {
const element = sourceTable.querySelector(querySelector)
if (element) {
// Clone the element
const clonedElement = element.cloneNode(true)
// Insert the element at the start of the split table
table.insertBefore(clonedElement, table.firstChild)
tables.forEach((table) => {
// Get the reference UUID of the node
const ref = table.dataset.ref
// Find the node in the original source
const sourceTable = chunker.source.querySelector("[data-ref='" + ref + "']")
// Repeat the <thead> element (if exists)
this.repeatElement(sourceTable, table, ':scope > thead')
// Repeat the <colgroup> elements (if at least one exists)
this.repeatElements(sourceTable, table, ':scope > colgroup')
// Repeat the <caption> element (if exists)
this.repeatElement(sourceTable, table, ':scope > caption')
})
}
}

repeatElements(sourceTable, table, querySelector) {
const elements = sourceTable.querySelectorAll(querySelector)
if (elements) {
elements.forEach((element) => {
repeatElement (sourceTable, table, querySelector) {
const element = sourceTable.querySelector(querySelector)
if (element) {
// Clone the element
const clonedElement = element.cloneNode(true)
// Insert the element at the start of the split table
table.insertBefore(clonedElement, table.firstChild)
})
}
}

repeatElements (sourceTable, table, querySelector) {
const elements = sourceTable.querySelectorAll(querySelector)
if (elements) {
elements.forEach((element) => {
// Clone the element
const clonedElement = element.cloneNode(true)
// Insert the element at the start of the split table
table.insertBefore(clonedElement, table.firstChild)
})
}
}
}
}

Paged.registerHandlers(RepeatingTableElements)
Paged.registerHandlers(RepeatingTableElements)
})()
6 changes: 3 additions & 3 deletions lib/document/stem.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const fileUrl = require('file-url')
const mathjaxFileUrl = fileUrl(require.resolve('mathjax/es5/tex-chtml-full.js'))
const fs = require('fs')
const mathjaxContent = fs.readFileSync(require.resolve('mathjax/es5/tex-chtml-full.js'), 'utf8')

module.exports = {
content: (node) => {
Expand Down Expand Up @@ -61,7 +61,7 @@ module.exports = {
window.PagedConfig.before = () => mathJaxReadyPromise
})()
</script>
<script type="text/javascript" src="${mathjaxFileUrl}"></script>`
<script data-type="mathjax">${mathjaxContent}</script>`
}
return ''
}
Expand Down
Loading

0 comments on commit 9b41d34

Please sign in to comment.