-
François Grand authoredFrançois Grand authored
mkdocs2pdf.py 8.40 KiB
#! /bin/python3
#
# Combine tous les fichiers markdown de la documentation en un seul fichier
#
# Requis :
# - pandoc
# - texlive (lister les paquets)
# - texlive-bibtex-extra
# - texlive-font-utils
# - latexmk
#
# Usage :
# - python3 ./mkdocs2pdf.py
#
import os
import sys
import yaml
import re
import shutil
# verbose output
verbose = False
baseDir = os.getcwd()
buildDir = os.path.join(baseDir, 'build')
latexSourceDir = os.path.join(baseDir, 'docs/latex')
pdfBuildDir = os.path.join(buildDir, 'pdf_build')
outputDir = os.path.join(baseDir, 'dist/assets/docs/pdf')
latexModelDir = 'latex_models'
modelDir = os.path.join(pdfBuildDir, latexModelDir)
mergedDocFilenamePrefix = 'cassiopee_doc_contents_'
filenamePrefix = 'cassiopee_doc_'
latexModelRepository = 'https://gitlab.irstea.fr/david.dorchies/latex_models.git'
def runCommand(cmd):
if os.waitstatus_to_exitcode(os.system(cmd)) != 0:
raise RuntimeError("error executing:",cmd)
# Create a symbolic link
def createLink(src):
# check if destination already exists
dest = os.path.basename(src)
if os.path.exists(dest):
if not os.path.islink(dest):
raise Exception('{} exists but is not a symbolic link'.format(dest))
else:
runCommand('ln -s {}'.format(src))
def createEmptyDir(path):
if os.path.exists(path):
shutil.rmtree(path)
os.makedirs(path)
def createDir(path):
if not os.path.exists(path):
os.makedirs(path)
# Reads an MkDocs configuration file
def readConfig(sYAML):
f = open(sYAML, 'r')
with f:
try:
dMkdocsYaml = yaml.load(f, Loader=yaml.SafeLoader)
except yaml.YAMLError as e:
sys.exit("ERROR on YAML loading {}: {}".format(sYAML, str(e)))
return dMkdocsYaml
def getMdHeader(title, level):
return "#" * level + " " + title + "\n"
def shiftMdHeaders(mdContent, level):
if level == 0:
return mdContent
lMd = mdContent.splitlines()
for i, item in enumerate(lMd):
if len(item) > 0 :
if item[0] == "#":
lMd[i] = ("#" * level) + item
return "\n".join(lMd)
# Browses MkDocs configuration file and merges .md files
def exploreAndMerge(docs_dir, nav, output = '', level = 0):
"""
@param docs_dir path to the mkdocs content files
@param nav dictionnary with the structure to explore
@param output markdown files content already merged
"""
if type(nav) is str:
nav = [nav]
for d in nav:
if type(d) is str:
filepath = os.path.join(docs_dir, d)
f = open(filepath, 'r')
# Triple "../" because file will be compiled from pdf_build/latex_models
path = os.path.join(baseDir, os.path.dirname(filepath))
s = f.read() + "\n"
# Modification of image and links paths
s = re.sub(r'(\!\[.+\]\()(.+)(\))', r'\1'+path+r'/\2\3', s)
s = re.sub(r'(\\\()(.+?)(\\\))', r'$\2$', s)
s = shiftMdHeaders(s, level)
output += "\n" + s
elif type(d) is dict:
level += 1
for key, value in d.items():
if type(value) is list:
output += "\n" + getMdHeader(key, level) + "\n"
output = exploreAndMerge(docs_dir, value, output, level)
else:
output = exploreAndMerge(docs_dir, value, output, level-1) # do not dig level when sub-chapter has only one entry (alias)
level -= 1
return output
# Creates a filePath.tex LaTeX contents file based on filePath.md
def convertMdToTex(filePath):
# Convert .md to .tex
runCommand(
'pandoc {0}.md -f markdown -t latex -s -o {0}.tex'.format(filePath)
)
# Remove header of tex file
bContent = False
ls = []
with open('{}.tex'.format(filePath), 'r') as f:
for line in f:
if line.strip() == '\\end{document}':
bContent = False
exit
if bContent:
ls.append(line)
if line.strip() == '\\begin{document}':
bContent = True
# Adjust generated content
for i, line in enumerate(ls):
l = line
# adjust images max width/height
l = l.replace('\\includegraphics', '\\includegraphics[max size={\\textwidth}{0.9\\textheight}]')
# force figures placement
l = l.replace('\\begin{figure}', '\\begin{figure}[h!]')
# make som subsubsections invisible (for CHANGELOG)
l = re.sub(r'(\\subsubsection)({[0-9]+.[0-9]+.[0-9]+)', r'\1*\2', l)
ls[i] = l
with open('{}.tex'.format(filePath), 'w') as f:
f.writelines(ls)
def getLatexModel():
# Clone Git repository
os.chdir(pdfBuildDir)
if os.path.isdir(latexModelDir):
# git directory exists, update it
os.chdir(latexModelDir)
runCommand('git pull')
# platform independent "cd .."
os.chdir(os.path.dirname(os.getcwd()))
else:
runCommand('git clone {} {}'.format(latexModelRepository, latexModelDir))
# back to original working drectory
os.chdir(baseDir)
# Inject generated merged documentation and necessary resources (template, logos…)
# into LaTeX models directory by symlinking them
def injectContentIntoModel(mergedDocFilenameTex, lang):
"""
@param mergedDocFilenameTex filename of the generated LaTeX merged doc, with its .tex extension, without directory
@param lang language; used to inject content in the right template
"""
# Symlink necessary resources
os.chdir(modelDir)
relPathToMergedTexDoc = os.path.join('..', mergedDocFilenameTex)
createLink(relPathToMergedTexDoc)
latexTemplate = filenamePrefix + lang + '.tex'
relPathToLatexTemplate = os.path.join(latexSourceDir, latexTemplate)
createLink(relPathToLatexTemplate)
createLink(os.path.join(latexSourceDir, 'logo_pole.png'))
createLink('{}/schema_rugosite_fond.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
createLink('{}/bloc_cylindre.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
createLink('{}/bloc_face_arrondie.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
createLink('{}/bloc_base_carree.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam')))
runCommand('rm rapport_inrae/logos.tex')
createLink('{} rapport_inrae/'.format(os.path.join(latexSourceDir, 'logos.tex')))
# back to original working drectory
os.chdir(baseDir)
# Build a PDF file from the LaTeX source
def buildPDF(lang):
# Compile LaTeX source
os.chdir(modelDir)
sourceTexFile = filenamePrefix + lang + '.tex'
outputPdfFile = filenamePrefix + lang + '.pdf'
# copy Cassiopée version LateX file
cvt = os.path.join(buildDir, 'cassiopee_version.tex')
shutil.copy(cvt, modelDir)
if verbose:
os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null'.format(sourceTexFile))
else:
os.system('latexmk -f -xelatex -pdf -interaction=nonstopmode {} > /dev/null 2>&1'.format(sourceTexFile))
# copy generated PDF to release directory
shutil.copy(outputPdfFile, outputDir)
# back to original working drectory
os.chdir(baseDir)
# Creates the PDF documentation file for the given language
def buildDocForLang(lang):
# Prepare temporary build directory
createEmptyDir(pdfBuildDir)
# Prepare output directory
createDir(outputDir)
# Read config
yamlPath = 'mkdocs/mkdocs-' + lang + '.yml'
dMkdocsYaml = readConfig(yamlPath)
# Create string with merged MarkDown
s = exploreAndMerge(dMkdocsYaml['docs_dir'], dMkdocsYaml['nav'])
# Save the merged .md file
mergedDocFilename = mergedDocFilenamePrefix + lang
mergedDocOutputPath = os.path.join(pdfBuildDir, mergedDocFilename)
# remove internal links @TODO convert them to hyperref ?
s = re.sub(r'\[([^/]+)\]\([^ ]+\.md\)', r'\1', s)
with open('{}.md'.format(mergedDocOutputPath), 'w') as f:
f.writelines(s)
# Convert to tex format
convertMdToTex(mergedDocOutputPath)
# Get INRAE report LaTeX model and inject merged content inside
getLatexModel()
injectContentIntoModel(mergedDocFilename + '.tex', lang)
# Build PDF from LaTeX source
buildPDF(lang)
# Clean build dir
shutil.rmtree(pdfBuildDir)
#raise RuntimeError()
if __name__ == '__main__':
for l in ['fr', 'en']:
print('building PDF doc for language "{}"'.format(l))
buildDocForLang(l)