Compare commits
No commits in common. "90ecb8bf64c6306eee90e288e83cb22189588d4d" and "bcbb7406f1acc7ee709b850c42ba4710766501e5" have entirely different histories.
90ecb8bf64
...
bcbb7406f1
|
@ -1,4 +1,3 @@
|
|||
venv/
|
||||
input/
|
||||
output/
|
||||
include/main.bib
|
||||
out/
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesClass{bookreport}[2022/3/12 Book Report]
|
||||
|
||||
\LoadClass{note}
|
||||
\LoadClass{article}
|
||||
|
||||
\usepackage{iflang}
|
||||
\usepackage[margin=1in]{geometry}
|
||||
|
||||
\renewcommand{\maketitle}{
|
||||
\IfLanguageName{portuguese}
|
381
generate.py
381
generate.py
|
@ -1,46 +1,18 @@
|
|||
#!/bin/env python3.9
|
||||
from subprocess import run
|
||||
from datetime import date
|
||||
from os import makedirs, environ
|
||||
from os.path import relpath
|
||||
from re import findall
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from sys import argv, stderr
|
||||
from shutil import copy, copytree, rmtree
|
||||
#!/bin/python3.9
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from html.parser import HTMLParser
|
||||
from pygments import highlight
|
||||
from pygments.lexers import get_lexer_by_name
|
||||
from pygments.formatters import HtmlFormatter
|
||||
import sys
|
||||
|
||||
base_author = 'Augusto Gunsch'
|
||||
|
||||
input_root = Path('input')
|
||||
output_root = Path('output')
|
||||
file_output_root = output_root / Path('files')
|
||||
templates_root = Path('templates')
|
||||
static_root = Path('static')
|
||||
|
||||
if len(argv) > 1 and 'clean' not in argv:
|
||||
print('usage: {} [clean]'.format(argv[0]), file=stderr)
|
||||
if len(sys.argv) > 1 and 'clean' not in sys.argv:
|
||||
print('usage: %s [clean]' % sys.argv[0])
|
||||
exit(1)
|
||||
|
||||
if 'clean' in argv:
|
||||
print('Cleaning output root')
|
||||
rmtree(output_root, ignore_errors=True)
|
||||
|
||||
templates = {}
|
||||
for template in templates_root.glob('*.html'):
|
||||
templates[template.stem] = template.read_text()
|
||||
|
||||
|
||||
def render_template(template, **kwargs):
|
||||
for var, val in kwargs.items():
|
||||
template = template.replace('${%s}' % var, str(val))
|
||||
|
||||
return template
|
||||
|
||||
|
||||
class CodeHighlighter(HTMLParser):
|
||||
data = ''
|
||||
reading_code = False
|
||||
|
@ -91,249 +63,148 @@ class CodeHighlighter(HTMLParser):
|
|||
|
||||
highlighter = CodeHighlighter()
|
||||
|
||||
input_ = 'input'
|
||||
outroot = 'out'
|
||||
output = 'Files'
|
||||
templates = 'templates'
|
||||
|
||||
class TeXFile:
|
||||
def extract_tex_metadata(self):
|
||||
m = findall(r'\\usepackage\[(.*)\]\{babel\}', self.raw_content)
|
||||
self.lang = m[0] if m else 'english'
|
||||
if 'clean' in sys.argv:
|
||||
shutil.rmtree(outroot, ignore_errors=True)
|
||||
|
||||
m = findall(r'\\title\{(.*)\}', self.raw_content)
|
||||
self.title = m[0] if m else self.input_file.stem.replace('_', ' ')
|
||||
|
||||
m = findall(r'\\author\{(.*)\}', self.raw_content)
|
||||
self.author = m[0] if m else base_author
|
||||
|
||||
m = findall(r'\\date\{(.*)\}', self.raw_content)
|
||||
self.date = m[0] if m else date.today().strftime('%d/%m/%Y')
|
||||
|
||||
m = findall(r'\\documentclass\{(.*)\}', self.raw_content)
|
||||
self.document_class = m[0] if m else 'article'
|
||||
|
||||
m = findall(r'\\usepackage(\[.*\])?\{biblatex\}', self.raw_content)
|
||||
self.biblatex = bool(m)
|
||||
|
||||
def expand_macros(self):
|
||||
content = self.raw_content
|
||||
breadcrumbs = str(self.pretty_breadcrumbs).replace('>',
|
||||
r'\textgreater\hspace{1pt}')
|
||||
content = content.replace(r'\breadcrumbs', breadcrumbs)
|
||||
outdir = (file_output_root/self.breadcrumbs).parent
|
||||
content = content.replace(r'\outdir', str(outdir))
|
||||
self.content = content
|
||||
|
||||
def __init__(self, input_file):
|
||||
self.input_file = input_file
|
||||
|
||||
self.breadcrumbs = Path(*input_file.parts[len(input_root.parts):]).with_suffix('')
|
||||
self.pretty_breadcrumbs = str(self.breadcrumbs) \
|
||||
.replace('_', ' ') \
|
||||
.replace('/', ' > ')
|
||||
|
||||
with open(input_file, 'r') as f:
|
||||
self.raw_content = f.read()
|
||||
|
||||
self.mtime = input_file.stat().st_mtime
|
||||
self.extract_tex_metadata()
|
||||
|
||||
self.expand_macros()
|
||||
try:
|
||||
os.mkdir(outroot)
|
||||
shutil.copy(templates + '/stylesheet.css', outroot + '/stylesheet.css')
|
||||
shutil.copy(templates + '/highlight.css', outroot + '/highlight.css')
|
||||
shutil.copy(templates + '/cabinet.png', outroot + '/cabinet.png')
|
||||
shutil.copy(templates + '/jquery.js', outroot + '/jquery.js')
|
||||
shutil.copytree(templates + '/mathjax', outroot + '/mathjax')
|
||||
shutil.copytree(templates + '/bootstrap', outroot + '/bootstrap')
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
class FromTeX:
|
||||
def __init__(self, tex_file, ext):
|
||||
self.tex_file = tex_file
|
||||
with open(templates + '/file.html', 'r') as template:
|
||||
file_template = template.read()
|
||||
|
||||
self.output_file = file_output_root / self.tex_file.breadcrumbs.with_suffix(ext)
|
||||
|
||||
self.mtime = self.output_file.stat().st_mtime \
|
||||
if self.output_file.exists() else 0
|
||||
self.is_outdated = self.mtime < self.tex_file.mtime
|
||||
with open(templates + '/index.html', 'r') as template:
|
||||
index_template = template.read()
|
||||
|
||||
|
||||
class HtmlFile(FromTeX):
|
||||
def __init__(self, tex_file):
|
||||
super().__init__(tex_file, '.html')
|
||||
def render_template(template, **kwargs):
|
||||
for var, val in kwargs.items():
|
||||
template = template.replace('${%s}' % var, val)
|
||||
|
||||
def write_output(self):
|
||||
args = [
|
||||
return template
|
||||
|
||||
|
||||
class File:
|
||||
def __init__(self, root, outdir, name):
|
||||
self.outdir = outdir
|
||||
self.basename = name[:-4]
|
||||
self.pdf = self.basename + '.pdf'
|
||||
self.html = self.basename + '.html'
|
||||
self.path = self.outdir.removeprefix(outroot + '/') + '/' + self.html
|
||||
self.pretty_path = self.path.replace('_', ' ').removesuffix('.html')
|
||||
self.input_path = root + '/' + name
|
||||
|
||||
self.root_reference = re.sub(r'.+?/', '../', outdir)
|
||||
self.root_reference = re.sub(r'/[^\.]+$', '/', self.root_reference)
|
||||
|
||||
path = '%s/%s' % (root, name)
|
||||
with open(path, 'r') as f:
|
||||
content = f.read()
|
||||
|
||||
m = re.findall(r'\\selectlanguage\{(.*?)\}', content)
|
||||
if not m:
|
||||
m = re.findall(r'\\usepackage\[(.*?)\]\{babel\}', content)
|
||||
if not m:
|
||||
m = re.findall(r'\\documentclass\[(.*?)\]\{.*\}', content)
|
||||
lang = m[0] if len(m) > 0 else 'english'
|
||||
|
||||
m = re.findall(r'\\documentclass\{(.*?)\}', content)
|
||||
doc_class = m[0] if len(m) > 0 else 'article'
|
||||
|
||||
options = [
|
||||
'pandoc',
|
||||
'--mathjax=static/mathjax/es5/tex-mml-chtml.js',
|
||||
'--mathjax=templates/mathjax/es5/tex-mml-chtml.js',
|
||||
'-f', 'latex',
|
||||
'-t', 'html',
|
||||
'-'
|
||||
]
|
||||
proc = run(args,
|
||||
input=self.tex_file.content,
|
||||
encoding='utf-8',
|
||||
capture_output=True)
|
||||
|
||||
if proc.returncode != 0:
|
||||
print(proc.stderr, file=stderr)
|
||||
exit(proc.returncode)
|
||||
|
||||
body = proc.stdout
|
||||
|
||||
try:
|
||||
template = templates[self.tex_file.document_class]
|
||||
except:
|
||||
print('No template named "{}.html"'.format(self.tex_file.document_class),
|
||||
file=stderr)
|
||||
exit(2)
|
||||
|
||||
root = Path(relpath(output_root, start=self.output_file)).parent
|
||||
|
||||
if self.tex_file.lang == 'portuguese':
|
||||
lang_title = 'Título'
|
||||
lang_author = 'Autor'
|
||||
lang_date = 'Data da Ficha'
|
||||
else:
|
||||
lang_title = 'Title'
|
||||
lang_author = 'Author'
|
||||
lang_date = 'Report Date'
|
||||
|
||||
content = render_template(template,
|
||||
lang_title=lang_title,
|
||||
lang_author=lang_author,
|
||||
lang_date=lang_date,
|
||||
title=self.tex_file.title,
|
||||
date=self.tex_file.date,
|
||||
author=self.tex_file.author,
|
||||
breadcrumbs=self.tex_file.pretty_breadcrumbs,
|
||||
pdf=self.output_file.with_suffix('.pdf').name,
|
||||
root=root,
|
||||
body=body)
|
||||
|
||||
highlighter.feed(content)
|
||||
content = highlighter.output()
|
||||
|
||||
makedirs(self.output_file.parent, exist_ok=True)
|
||||
with open(self.output_file, 'w') as f:
|
||||
f.write(content)
|
||||
|
||||
|
||||
class PdfFile(FromTeX):
|
||||
def __init__(self, tex_file):
|
||||
super().__init__(tex_file, '.pdf')
|
||||
|
||||
def run_pdflatex(self):
|
||||
args = [
|
||||
'pdflatex',
|
||||
'-jobname', self.output_file.stem,
|
||||
'-output-directory', self.output_file.parent,
|
||||
'-shell-escape'
|
||||
path
|
||||
]
|
||||
|
||||
env = {
|
||||
**environ,
|
||||
'TEXINPUTS': './include:'
|
||||
}
|
||||
if doc_class == 'bookreport':
|
||||
options.append('-s')
|
||||
options.append('--template')
|
||||
options.append('templates/default.html')
|
||||
|
||||
proc = run(args,
|
||||
env=env,
|
||||
input=bytes(self.tex_file.content, 'utf-8'),
|
||||
capture_output=True)
|
||||
self.content = subprocess.check_output(options).decode()
|
||||
|
||||
if proc.returncode != 0:
|
||||
print(proc.stdout, file=stderr)
|
||||
print(proc.stderr, file=stderr)
|
||||
exit(proc.returncode)
|
||||
|
||||
def run_biber(self):
|
||||
args = [
|
||||
'biber',
|
||||
self.output_file.with_suffix('')
|
||||
]
|
||||
|
||||
proc = run(args,
|
||||
capture_output=True)
|
||||
|
||||
if proc.returncode != 0:
|
||||
print(proc.stdout, file=stderr)
|
||||
print(proc.stderr, file=stderr)
|
||||
exit(proc.returncode)
|
||||
|
||||
def write_output(self):
|
||||
makedirs(self.output_file.parent, exist_ok=True)
|
||||
|
||||
self.run_pdflatex()
|
||||
|
||||
if self.tex_file.biblatex:
|
||||
self.run_biber()
|
||||
self.run_pdflatex()
|
||||
|
||||
|
||||
def write_files():
|
||||
changed = False
|
||||
|
||||
for input_file in input_root.glob('**/*.tex'):
|
||||
tex_file = TeXFile(input_file)
|
||||
|
||||
html_file = HtmlFile(tex_file)
|
||||
pdf_file = PdfFile(tex_file)
|
||||
|
||||
if html_file.is_outdated:
|
||||
print('Generating "{}"'.format(html_file.output_file))
|
||||
html_file.write_output()
|
||||
changed = True
|
||||
|
||||
if pdf_file.is_outdated:
|
||||
print('Generating "{}"'.format(pdf_file.output_file))
|
||||
pdf_file.write_output()
|
||||
changed = True
|
||||
|
||||
return changed
|
||||
|
||||
def copy_static_files():
|
||||
if not output_root.exists():
|
||||
makedirs(output_root)
|
||||
|
||||
for entity in static_root.iterdir():
|
||||
dest = output_root/Path(*entity.parts[len(static_root.parts):])
|
||||
if not dest.exists():
|
||||
print('Copying "{}" to "{}"'.format(entity, dest))
|
||||
if entity.is_dir():
|
||||
copytree(entity, dest)
|
||||
if doc_class == 'bookreport':
|
||||
if lang == 'portuguese':
|
||||
self.content = re.sub(r'!\*\*title\*\*!', 'Título', self.content)
|
||||
self.content = re.sub(r'!\*\*author\*\*!', 'Autor', self.content)
|
||||
self.content = re.sub(r'!\*\*date\*\*!', 'Data da Ficha', self.content)
|
||||
else:
|
||||
copy(entity, dest)
|
||||
self.content = re.sub(r'!\*\*title\*\*!', 'Title', self.content)
|
||||
self.content = re.sub(r'!\*\*author\*\*!', 'Author', self.content)
|
||||
self.content = re.sub(r'!\*\*date\*\*!', 'Report Date', self.content)
|
||||
|
||||
def make_details(directory):
|
||||
html = ''
|
||||
def expand_html(self):
|
||||
title = self.basename.replace('_', ' ')
|
||||
|
||||
if directory != input_root:
|
||||
html += '<details open>'
|
||||
html += '<summary>{}</summary>'.format(directory.name.replace('_', ' '))
|
||||
expanded = render_template(file_template,
|
||||
title=title,
|
||||
path=self.pretty_path,
|
||||
root=self.root_reference,
|
||||
pdf=self.pdf,
|
||||
content=self.content)
|
||||
|
||||
html += '<ul>'
|
||||
for file in directory.iterdir():
|
||||
if file.is_file():
|
||||
if file.suffix == '.tex':
|
||||
outfile = Path(*file.resolve().parts[len(input_root.resolve().parts):])
|
||||
outfile = ('files'/outfile).with_suffix('.html')
|
||||
highlighter.feed(expanded)
|
||||
|
||||
html += '<li><a href="{}">{}</a></li>'.format(outfile,
|
||||
file.stem.replace('_', ' '))
|
||||
else:
|
||||
html += make_details(file)
|
||||
html += '</ul>'
|
||||
return highlighter.output()
|
||||
|
||||
if directory != input_root:
|
||||
html += '</details>'
|
||||
def write_html(self):
|
||||
html_content = self.expand_html()
|
||||
|
||||
return html
|
||||
with open(self.outdir + '/' + self.html, 'w') as f:
|
||||
f.write(html_content)
|
||||
|
||||
def make_index():
|
||||
html = '<ul id="toc">'
|
||||
html += make_details(input_root)
|
||||
html += '</ul>'
|
||||
def write_pdf(self):
|
||||
subprocess.run(['latexmk', '-shell-escape', '-pdf', '-outdir=%s' % self.outdir, self.input_path])
|
||||
|
||||
index = render_template(templates['index'],
|
||||
toc=html)
|
||||
subprocess.run(['latexmk', '-c', '-outdir=%s' % self.outdir, self.input_path])
|
||||
|
||||
with open(output_root / 'index.html', 'w') as f:
|
||||
f.write(index)
|
||||
def write(self):
|
||||
html = self.outdir + '/' + self.html
|
||||
pdf = self.outdir + '/' + self.pdf
|
||||
input_time = os.path.getmtime(self.input_path)
|
||||
if not os.path.isfile(html) or input_time > os.path.getmtime(html):
|
||||
self.write_html()
|
||||
if not os.path.isfile(pdf) or input_time > os.path.getmtime(pdf):
|
||||
self.write_pdf()
|
||||
|
||||
copy_static_files()
|
||||
outdated_index = write_files()
|
||||
|
||||
if outdated_index:
|
||||
print('Generating index')
|
||||
make_index()
|
||||
toc = '<ul>'
|
||||
|
||||
for root, dirs, files in os.walk(input_, topdown=True):
|
||||
outdir = outroot + '/' + output + root[len(input_):]
|
||||
|
||||
os.makedirs(outdir, exist_ok=True)
|
||||
|
||||
outfiles = []
|
||||
|
||||
if len(files) or len(dirs):
|
||||
for file in files:
|
||||
if file.endswith('.tex'):
|
||||
f = File(root, outdir, file)
|
||||
|
||||
f.write()
|
||||
|
||||
toc += '<li><a href="%s">%s</a></li>' % (f.path, f.pretty_path)
|
||||
|
||||
toc += '</ul>'
|
||||
|
||||
|
||||
with open(outroot + '/index.html', 'w') as f:
|
||||
f.write(render_template(index_template,
|
||||
toc=toc))
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
\NeedsTeXFormat{LaTeX2e}
|
||||
\ProvidesClass{note}[2022/3/14 Note]
|
||||
|
||||
\LoadClass{article}
|
||||
|
||||
\usepackage[margin=1in]{geometry}
|
||||
|
||||
\newcommand{\noteheader}[1]{
|
||||
\noindent\large
|
||||
\begin{center}
|
||||
#1
|
||||
\end{center}
|
||||
}
|
|
@ -1,48 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="${root}/bootstrap/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="${root}/highlight.css"/>
|
||||
<link rel="stylesheet" href="${root}/stylesheet.css"/>
|
||||
<script src="${root}/jquery.js"></script>
|
||||
<script src="${root}/bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
window.MathJax = {
|
||||
options: {
|
||||
enableMenu: false,
|
||||
enableEnrichment: false
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="${root}/mathjax/es5/tex-mml-chtml.js"></script>
|
||||
<title>${title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header class="container-fluid">
|
||||
<h1 id="cabinet"><img id="cabinet-icon" src="${root}/cabinet.png"/>Cabinet</h1>
|
||||
</header>
|
||||
<nav class="container-fluid">
|
||||
<a class="button" id="back" href="${root}/index.html">← Back</a>
|
||||
<span id="path">${breadcrumbs}</span>
|
||||
<a class="button" id="pdf" href="${pdf}">↓ PDF</a>
|
||||
</nav>
|
||||
<main class="container">
|
||||
<table id="meta">
|
||||
<tr>
|
||||
<td>${lang_title}:</td> <td>${title}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${lang_author}:</td> <td>${author}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>${lang_date}:</td> <td>${date}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
${body}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
|
@ -0,0 +1,28 @@
|
|||
<style>
|
||||
#meta {
|
||||
width: 100%;
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
#meta td:nth-child(2) {
|
||||
text-align: right;
|
||||
}
|
||||
</style>
|
||||
|
||||
<table id="meta">
|
||||
<tr>
|
||||
<td>!**title**!:</td> <td>$title$</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>!**author**!:</td> <td>$author$</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>!**date**!:</td> <td>$date$</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<hr>
|
||||
|
||||
<article>
|
||||
$body$
|
||||
</article>
|
|
@ -0,0 +1,34 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<link rel="stylesheet" href="${root}bootstrap/css/bootstrap.min.css"/>
|
||||
<link rel="stylesheet" href="${root}highlight.css"/>
|
||||
<link rel="stylesheet" href="${root}stylesheet.css"/>
|
||||
<script src="${root}jquery.js"></script>
|
||||
<script src="${root}bootstrap/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
window.MathJax = {
|
||||
options: {
|
||||
enableMenu: false,
|
||||
enableEnrichment: false
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<script src="${root}mathjax/es5/tex-mml-chtml.js"></script>
|
||||
<title>${title}</title>
|
||||
</head>
|
||||
<body>
|
||||
<header class="container-fluid">
|
||||
<h1 id="cabinet"><img id="cabinet-icon" src="${root}cabinet.png"/>Cabinet</h1>
|
||||
</header>
|
||||
<nav class="container-fluid">
|
||||
<a class="button" id="back" href="${root}index.html">← Back</a>
|
||||
<span id="path">${path}</span>
|
||||
<a class="button" id="pdf" href="${pdf}">↓ PDF</a>
|
||||
</nav>
|
||||
<main class="container">
|
||||
${content}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue