edit.js 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. document.addEventListener('DOMContentLoaded', () => {
  2. const zoom = 0.25// From the pdf2htmlex parameter --zoom
  3. const zoomRatio = 1 / zoom
  4. const saveButton = document.getElementById('saveButton')
  5. const whiteoutButton = document.getElementById('whiteoutButton')
  6. const editTextButton = document.getElementById('editTextButton')
  7. const saveForm = document.getElementById('saveForm')
  8. const htmlInput = document.getElementById('htmlInput')
  9. const pageContainer = document.getElementById('page-container')
  10. let mode = null
  11. const toggleMode = newMode => {
  12. if (mode === newMode) newMode = null
  13. switch (mode) {
  14. case 'whiteout': disableWhiteout(); break;
  15. case 'editText': disableEditText(); break;
  16. }
  17. switch (newMode) {
  18. case 'whiteout': enableWhiteout(); break;
  19. case 'editText': enableEditText(); break;
  20. }
  21. mode = newMode
  22. }
  23. document.querySelectorAll('#page-container img').forEach(image => {
  24. image.addEventListener('click', () => {
  25. const selection = getSelection()
  26. selection.removeAllRanges()
  27. const range = document.createRange()
  28. range.selectNode(image)
  29. selection.addRange(range)
  30. console.log('selected?')
  31. })
  32. })
  33. saveButton.addEventListener('click', () => {
  34. htmlInput.value = document.documentElement.outerHTML
  35. saveForm.submit()
  36. })
  37. // Get screen to print ratio
  38. const localStyleSheets = Array.from(document.styleSheets).filter(x => !x.href)
  39. const pageOnePixelWidth = localStyleSheets.map(ss =>
  40. Array.from(ss.rules)
  41. .find(rule => rule.selectorText === '.w0' && rule.style.width.endsWith('px'))
  42. ).find(x => x).style.width.split('px')[0]
  43. const pageOnePointWidth = localStyleSheets.map(ss =>
  44. Array.from(ss.rules)
  45. .filter(rule => rule instanceof CSSMediaRule && rule.conditionText === 'print')
  46. .map(mediaRule =>
  47. Array.from(mediaRule.cssRules)
  48. .filter(rule => rule.selectorText === '.w0' && rule.style.width.endsWith('pt'))
  49. .find(x => x)
  50. ).find(x => x)
  51. ).find(x => x).style.width.split('pt')[0]
  52. const pixelsPerPoint = 1.3333333333333333
  53. const pageOnePrintPixelWidth = pageOnePointWidth * pixelsPerPoint
  54. const screenToPrintRatio = pageOnePrintPixelWidth / pageOnePixelWidth
  55. const printToScreenRatio = pageOnePixelWidth / pageOnePrintPixelWidth
  56. const myStyle = document.createElement('style')
  57. myStyle.type = 'text/css'
  58. myStyle.innerHTML = `
  59. @media screen {
  60. .replacement {
  61. zoom: ${printToScreenRatio};
  62. }
  63. }
  64. `
  65. document.head.appendChild(myStyle)
  66. // Whiteout
  67. let start = null
  68. const box = document.createElement('div')
  69. box.className = 'whiteout-box'
  70. whiteoutButton.addEventListener('click', () => toggleMode('whiteout'))
  71. const enableWhiteout = () => {
  72. whiteoutButton.classList.add('active')
  73. pageContainer.classList.add('whiteout')
  74. pageContainer.addEventListener('mousedown', whiteoutMouseDown)
  75. }
  76. const disableWhiteout = () => {
  77. whiteoutButton.classList.remove('active')
  78. pageContainer.classList.remove('whiteout')
  79. pageContainer.removeEventListener('mousedown', whiteoutMouseDown)
  80. }
  81. const whiteoutMouseDown = event => {
  82. start = {x: event.clientX, y: event.clientY}
  83. document.body.appendChild(box)
  84. drawBox(start, start)
  85. window.addEventListener('mousemove', whiteoutMouseMove)
  86. window.addEventListener('mouseup', whiteoutMouseUp)
  87. }
  88. const whiteoutMouseMove = event => {
  89. let end = {x: event.clientX, y: event.clientY}
  90. drawBox(rect(start, end))
  91. }
  92. const whiteoutMouseUp = event => {
  93. let end = {x: event.clientX, y: event.clientY}
  94. document.body.removeChild(box)
  95. const selection = rect(start, end)
  96. console.log('whiteout', selection)
  97. whiteout(selection)
  98. window.removeEventListener('mousemove', whiteoutMouseMove)
  99. window.removeEventListener('mouseup', whiteoutMouseUp)
  100. }
  101. const rect = (a, b) => {
  102. const left = Math.min(a.x, b.x)
  103. const top = Math.min(a.y, b.y)
  104. const right = Math.max(a.x, b.x)
  105. const bottom = Math.max(a.y, b.y)
  106. return new DOMRect(left, top, right - left, bottom - top)
  107. }
  108. const drawBox = rect => {
  109. box.style.cssText = `
  110. top: ${rect.top - 1}px;
  111. left: ${rect.left - 1}px;
  112. width: ${rect.width}px;
  113. height: ${rect.height}px;
  114. `
  115. }
  116. const intersects = (r1, r2) =>
  117. !(r2.left > r1.right ||
  118. r2.right < r1.left ||
  119. r2.top > r1.bottom ||
  120. r2.bottom < r1.top)
  121. const whiteout = rect => {
  122. const elements = []
  123. const walk = (element) => {
  124. let elementRect
  125. if (element instanceof HTMLImageElement) {
  126. elementRect = element.getBoundingClientRect()
  127. } else {
  128. const range = document.createRange()
  129. range.selectNodeContents(element)
  130. elementRect = range.getBoundingClientRect()
  131. }
  132. if (intersects(rect, elementRect)) {
  133. if (element.childNodes && element.childNodes.length) {
  134. Array.from(element.childNodes).forEach(walk)
  135. } else if (element instanceof Text && element.textContent.length > 1) {
  136. while (element.textContent.length) {
  137. const next = element.splitText(1)
  138. walk(element)
  139. element = next
  140. }
  141. } else {
  142. elements.push(element)
  143. }
  144. }
  145. }
  146. walk(pageContainer)
  147. elements.forEach(element => {
  148. if (element instanceof Text) {
  149. const range = document.createRange()
  150. range.selectNodeContents(element)
  151. const elementRect = range.getBoundingClientRect()
  152. const replacement = document.createElement('span')
  153. replacement.className = 'replacement'
  154. replacement.style.cssText = `
  155. width: ${elementRect.width * zoomRatio * screenToPrintRatio}px;
  156. display: inline-block;
  157. position: relative;
  158. `
  159. element.replaceWith(replacement)
  160. } else if (element instanceof HTMLImageElement) {
  161. const canvas = document.createElement('canvas')
  162. canvas.width = element.offsetWidth * zoomRatio
  163. canvas.height = element.offsetHeight * zoomRatio
  164. const ctx = canvas.getContext('2d')
  165. ctx.fillStyle = 'white'
  166. ctx.drawImage(element, 0, 0, element.offsetWidth * zoomRatio, element.offsetHeight * zoomRatio)
  167. const elementRect = element.getBoundingClientRect()
  168. ctx.fillRect(
  169. (rect.x - elementRect.x) * zoomRatio,
  170. (rect.y - elementRect.y) * zoomRatio,
  171. rect.width * zoomRatio,
  172. rect.height * zoomRatio
  173. )
  174. element.src = canvas.toDataURL()
  175. }
  176. })
  177. }
  178. // Edit Text
  179. editTextButton.addEventListener('click', () => toggleMode('editText'))
  180. const enableEditText = () => {
  181. editTextButton.classList.add('active')
  182. pageContainer.classList.add('editText')
  183. document.querySelectorAll('.pc').forEach(page => {
  184. page.setAttribute('contenteditable', 'true')
  185. })
  186. }
  187. const disableEditText = () => {
  188. editTextButton.classList.remove('active')
  189. pageContainer.classList.remove('editText')
  190. document.querySelectorAll('.pc').forEach(page => {
  191. page.setAttribute('contenteditable', 'false')
  192. })
  193. }
  194. })