1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
|
import subprocess, shlex
import os, shutil, re
from pathlib import Path
from typing import IO
from plasTeX.Renderers.PageTemplate import Renderer as _Renderer
from plasTeX.Renderers.HTML5.Config import addConfig
from plasTeX.Logging import getLogger
log = getLogger()
class HTML5(_Renderer):
"""Renderer targetting HTML5."""
fileExtension = '.html'
imageTypes = ['.svg', '.png','.jpg','.jpeg','.gif']
vectorImageTypes = ['.svg']
vectorBitmap = False
def loadTemplates(self, document):
"""Load templates as in PageTemplate but also look for packages that
want to override some templates and handles extra css and javascript."""
_Renderer.loadTemplates(self, document)
rendererdata = document.rendererdata.setdefault('html5', dict())
config = document.config
if 'html5' not in config:
addConfig(config)
srcDir = document.userdata.get('working-dir', '.') # type: str
buildDir = os.getcwd()
# Theme css has already been copied by PageTemplate.loadTemplates,
# provided config['general']['copy-theme-extras'] is true
# Still try to create styles directory is case it's false
try:
os.mkdir('styles')
except OSError:
# This should mean the directory already exists
pass
# Start building the css list for use by the layout template
if config['html5']['use-theme-css'] and config['general']['copy-theme-extras']:
rendererdata['css'] = ['theme-' + config['html5']['theme-css'] + '.css']
else:
rendererdata['css'] = []
# Theme js has already been copied by PageTemplate.loadTemplates,
# provided config['general']['copy-theme-extras'] is true
# Still try to create js directory is case it's false
try:
os.mkdir('js')
except OSError:
pass
# Start building the js list for use by the layout template
if self.loadedTheme:
theme_js_path = Path(self.loadedTheme)/'js'
if (config['html5']['use-theme-js'] and
config['general']['copy-theme-extras'] and
theme_js_path.exists()):
rendererdata['js'] = sorted(path.name
for path in theme_js_path.glob('*.js'))
else:
rendererdata['js'] = []
for resrc in document.packageResources:
# Next line may load templates or change
# document.rendererdata['html5'] or copy some files to buildDir
resrc.alter(
renderer=self,
rendererName='html5',
document=document,
target=Path(buildDir))
# Last loaded files (hence overriding everything else) come from user
# configuration
cssBuildDir = os.path.join(buildDir, 'styles')
for css in config['html5']['extra-css']:
rendererdata['css'].append(css)
try:
shutil.copy(os.path.join(srcDir, css), cssBuildDir)
except FileNotFoundError:
log.error('File ' + css + ' not found')
except IOError as err:
log.error(str(err))
jsBuildDir = os.path.join(buildDir, 'js')
for js in config['html5']['extra-js']:
rendererdata['js'].append(js)
try:
shutil.copy(os.path.join(srcDir, js), jsBuildDir)
except FileNotFoundError:
log.error('File ' + js + ' not found')
except IOError as err:
log.error(str(err))
def processFileContent(self, document, s):
s = _Renderer.processFileContent(self, document, s)
# Remove empty paragraphs
s = re.compile(r'<p>\s*</p>', re.I).sub(r'', s)
# Add a non-breaking space to empty table cells
s = re.compile(r'(<(td|th)\b[^>]*>)\s*(</\2>)', re.I).sub(r'\1 \3', s)
for fun in document.rendererdata['html5'].get('processFileContents', []):
s = fun(document, s)
filters = document.config['html5']['filters']
for filter_ in filters:
proc = subprocess.Popen(
shlex.split(filter_),
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
output, output_err = proc.communicate(s.encode(encoding="utf-8"))
if not output_err:
s = output.decode(encoding="utf-8")
return s
def cleanup(self, document, files, postProcess=None):
"""
Cleanup method called at the end of rendering.
Uses the base renderer cleanup but calls packages callbacks before and
after. Callbacks should be listed in
document.userdata['preCleanupCallbacks']
or document.userdata['postCleanupCallbacks']. Each call back should accept the
current document as its only argument. Pre-cleanup call back must return
the list of path of files they created (relative to the output directory).
"""
rendererdata = document.rendererdata.get('html5', dict())
preCleanupCallbacks = rendererdata.get('preCleanupCallbacks', [])
for preCleanupCallback in preCleanupCallbacks:
files += preCleanupCallback(document)
_Renderer.cleanup(self, document, files, postProcess)
Renderer = HTML5
|