#! /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)