|
|
@@ -1,8 +1,26 @@
|
|
|
document.addEventListener('DOMContentLoaded', () => {
|
|
|
- // Make page content editable
|
|
|
- document.querySelectorAll('.pf').forEach(page => {
|
|
|
- page.setAttribute('contenteditable', 'true')
|
|
|
- })
|
|
|
+ const zoom = 0.25// From the pdf2htmlex parameter --zoom
|
|
|
+ const zoomRatio = 1 / zoom
|
|
|
+ const saveButton = document.getElementById('saveButton')
|
|
|
+ const whiteoutButton = document.getElementById('whiteoutButton')
|
|
|
+ const editTextButton = document.getElementById('editTextButton')
|
|
|
+ const saveForm = document.getElementById('saveForm')
|
|
|
+ const htmlInput = document.getElementById('htmlInput')
|
|
|
+ const pageContainer = document.getElementById('page-container')
|
|
|
+
|
|
|
+ let mode = null
|
|
|
+ const toggleMode = newMode => {
|
|
|
+ if (mode === newMode) newMode = null
|
|
|
+ switch (mode) {
|
|
|
+ case 'whiteout': disableWhiteout(); break;
|
|
|
+ case 'editText': disableEditText(); break;
|
|
|
+ }
|
|
|
+ switch (newMode) {
|
|
|
+ case 'whiteout': enableWhiteout(); break;
|
|
|
+ case 'editText': enableEditText(); break;
|
|
|
+ }
|
|
|
+ mode = newMode
|
|
|
+ }
|
|
|
|
|
|
document.querySelectorAll('#page-container img').forEach(image => {
|
|
|
image.addEventListener('click', () => {
|
|
|
@@ -14,4 +32,179 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
console.log('selected?')
|
|
|
})
|
|
|
})
|
|
|
+
|
|
|
+ saveButton.addEventListener('click', () => {
|
|
|
+ htmlInput.value = document.documentElement.outerHTML
|
|
|
+ saveForm.submit()
|
|
|
+ })
|
|
|
+
|
|
|
+ // Get screen to print ratio
|
|
|
+ const localStyleSheets = Array.from(document.styleSheets).filter(x => !x.href)
|
|
|
+ const pageOnePixelWidth = localStyleSheets.map(ss =>
|
|
|
+ Array.from(ss.rules)
|
|
|
+ .find(rule => rule.selectorText === '.w0' && rule.style.width.endsWith('px'))
|
|
|
+ ).find(x => x).style.width.split('px')[0]
|
|
|
+ const pageOnePointWidth = localStyleSheets.map(ss =>
|
|
|
+ Array.from(ss.rules)
|
|
|
+ .filter(rule => rule instanceof CSSMediaRule && rule.conditionText === 'print')
|
|
|
+ .map(mediaRule =>
|
|
|
+ Array.from(mediaRule.cssRules)
|
|
|
+ .filter(rule => rule.selectorText === '.w0' && rule.style.width.endsWith('pt'))
|
|
|
+ .find(x => x)
|
|
|
+ ).find(x => x)
|
|
|
+ ).find(x => x).style.width.split('pt')[0]
|
|
|
+ const pixelsPerPoint = 1.3333333333333333
|
|
|
+ const pageOnePrintPixelWidth = pageOnePointWidth * pixelsPerPoint
|
|
|
+ const screenToPrintRatio = pageOnePrintPixelWidth / pageOnePixelWidth
|
|
|
+ const printToScreenRatio = pageOnePixelWidth / pageOnePrintPixelWidth
|
|
|
+
|
|
|
+ const myStyle = document.createElement('style')
|
|
|
+ myStyle.type = 'text/css'
|
|
|
+ myStyle.innerHTML = `
|
|
|
+ @media screen {
|
|
|
+ .replacement {
|
|
|
+ zoom: ${printToScreenRatio};
|
|
|
+ }
|
|
|
+ }
|
|
|
+ `
|
|
|
+ document.head.appendChild(myStyle)
|
|
|
+
|
|
|
+ // Whiteout
|
|
|
+ let start = null
|
|
|
+ const box = document.createElement('div')
|
|
|
+ box.className = 'whiteout-box'
|
|
|
+
|
|
|
+ whiteoutButton.addEventListener('click', () => toggleMode('whiteout'))
|
|
|
+
|
|
|
+ const enableWhiteout = () => {
|
|
|
+ whiteoutButton.classList.add('active')
|
|
|
+ pageContainer.classList.add('whiteout')
|
|
|
+ pageContainer.addEventListener('mousedown', whiteoutMouseDown)
|
|
|
+ }
|
|
|
+
|
|
|
+ const disableWhiteout = () => {
|
|
|
+ whiteoutButton.classList.remove('active')
|
|
|
+ pageContainer.classList.remove('whiteout')
|
|
|
+ pageContainer.removeEventListener('mousedown', whiteoutMouseDown)
|
|
|
+ }
|
|
|
+
|
|
|
+ const whiteoutMouseDown = event => {
|
|
|
+ start = {x: event.clientX, y: event.clientY}
|
|
|
+ document.body.appendChild(box)
|
|
|
+ drawBox(start, start)
|
|
|
+ window.addEventListener('mousemove', whiteoutMouseMove)
|
|
|
+ window.addEventListener('mouseup', whiteoutMouseUp)
|
|
|
+ }
|
|
|
+ const whiteoutMouseMove = event => {
|
|
|
+ let end = {x: event.clientX, y: event.clientY}
|
|
|
+ drawBox(rect(start, end))
|
|
|
+ }
|
|
|
+ const whiteoutMouseUp = event => {
|
|
|
+ let end = {x: event.clientX, y: event.clientY}
|
|
|
+ document.body.removeChild(box)
|
|
|
+ const selection = rect(start, end)
|
|
|
+ console.log('whiteout', selection)
|
|
|
+ whiteout(selection)
|
|
|
+ window.removeEventListener('mousemove', whiteoutMouseMove)
|
|
|
+ window.removeEventListener('mouseup', whiteoutMouseUp)
|
|
|
+ }
|
|
|
+ const rect = (a, b) => {
|
|
|
+ const left = Math.min(a.x, b.x)
|
|
|
+ const top = Math.min(a.y, b.y)
|
|
|
+ const right = Math.max(a.x, b.x)
|
|
|
+ const bottom = Math.max(a.y, b.y)
|
|
|
+ return new DOMRect(left, top, right - left, bottom - top)
|
|
|
+ }
|
|
|
+ const drawBox = rect => {
|
|
|
+ box.style.cssText = `
|
|
|
+ top: ${rect.top - 1}px;
|
|
|
+ left: ${rect.left - 1}px;
|
|
|
+ width: ${rect.width}px;
|
|
|
+ height: ${rect.height}px;
|
|
|
+ `
|
|
|
+ }
|
|
|
+
|
|
|
+ const intersects = (r1, r2) =>
|
|
|
+ !(r2.left > r1.right ||
|
|
|
+ r2.right < r1.left ||
|
|
|
+ r2.top > r1.bottom ||
|
|
|
+ r2.bottom < r1.top)
|
|
|
+
|
|
|
+ const whiteout = rect => {
|
|
|
+ const elements = []
|
|
|
+ const walk = (element) => {
|
|
|
+ let elementRect
|
|
|
+ if (element instanceof HTMLImageElement) {
|
|
|
+ elementRect = element.getBoundingClientRect()
|
|
|
+ } else {
|
|
|
+ const range = document.createRange()
|
|
|
+ range.selectNodeContents(element)
|
|
|
+ elementRect = range.getBoundingClientRect()
|
|
|
+ }
|
|
|
+
|
|
|
+ if (intersects(rect, elementRect)) {
|
|
|
+ if (element.childNodes && element.childNodes.length) {
|
|
|
+ Array.from(element.childNodes).forEach(walk)
|
|
|
+ } else if (element instanceof Text && element.textContent.length > 1) {
|
|
|
+ while (element.textContent.length) {
|
|
|
+ const next = element.splitText(1)
|
|
|
+ walk(element)
|
|
|
+ element = next
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ elements.push(element)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ walk(pageContainer)
|
|
|
+ elements.forEach(element => {
|
|
|
+ if (element instanceof Text) {
|
|
|
+ const range = document.createRange()
|
|
|
+ range.selectNodeContents(element)
|
|
|
+ const elementRect = range.getBoundingClientRect()
|
|
|
+ const replacement = document.createElement('span')
|
|
|
+ replacement.className = 'replacement'
|
|
|
+ replacement.style.cssText = `
|
|
|
+ width: ${elementRect.width * zoomRatio * screenToPrintRatio}px;
|
|
|
+ display: inline-block;
|
|
|
+ position: relative;
|
|
|
+ `
|
|
|
+ element.replaceWith(replacement)
|
|
|
+ } else if (element instanceof HTMLImageElement) {
|
|
|
+ const canvas = document.createElement('canvas')
|
|
|
+ canvas.width = element.offsetWidth * zoomRatio
|
|
|
+ canvas.height = element.offsetHeight * zoomRatio
|
|
|
+ const ctx = canvas.getContext('2d')
|
|
|
+ ctx.fillStyle = 'white'
|
|
|
+ ctx.drawImage(element, 0, 0, element.offsetWidth * zoomRatio, element.offsetHeight * zoomRatio)
|
|
|
+ const elementRect = element.getBoundingClientRect()
|
|
|
+ ctx.fillRect(
|
|
|
+ (rect.x - elementRect.x) * zoomRatio,
|
|
|
+ (rect.y - elementRect.y) * zoomRatio,
|
|
|
+ rect.width * zoomRatio,
|
|
|
+ rect.height * zoomRatio
|
|
|
+ )
|
|
|
+ element.src = canvas.toDataURL()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ // Edit Text
|
|
|
+ editTextButton.addEventListener('click', () => toggleMode('editText'))
|
|
|
+
|
|
|
+ const enableEditText = () => {
|
|
|
+ editTextButton.classList.add('active')
|
|
|
+ pageContainer.classList.add('editText')
|
|
|
+ document.querySelectorAll('.pc').forEach(page => {
|
|
|
+ page.setAttribute('contenteditable', 'true')
|
|
|
+ })
|
|
|
+ }
|
|
|
+
|
|
|
+ const disableEditText = () => {
|
|
|
+ editTextButton.classList.remove('active')
|
|
|
+ pageContainer.classList.remove('editText')
|
|
|
+ document.querySelectorAll('.pc').forEach(page => {
|
|
|
+ page.setAttribute('contenteditable', 'false')
|
|
|
+ })
|
|
|
+ }
|
|
|
})
|