Skip to content
Snippets Groups Projects
mkdocs2pdf.py 7.68 KiB
Newer Older
# Combine tous les fichiers markdown de la documentation en un seul fichier
# - pandoc
# - texlive (lister les paquets)
mathias.chouet's avatar
mathias.chouet committed
# - texlive-bibtex-extra
# - texlive-font-utils
# - latexmk
# - python3 ./mkdocs2pdf.py
#
import os
import sys
import yaml
import re
import shutil
baseDir = os.getcwd();
latexSourceDir = os.path.join(baseDir, 'docs/latex')
buildDir = os.path.join(baseDir, 'build/pdf_build')
outputDir = os.path.join(baseDir, 'dist/assets/docs/pdf')

latexModelDir = 'latex_models'
modelDir = os.path.join(buildDir, 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)

# 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"
mathias.chouet's avatar
mathias.chouet committed
                    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
        '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):
        # 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:
def getLatexModel():
    # Clone Git repository
    os.chdir(buildDir)
        '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)
        'ln -s {} .'.format(relPathToMergedTexDoc)
    )
    latexTemplate = filenamePrefix + lang + '.tex'
    relPathToLatexTemplate = os.path.join(latexSourceDir, latexTemplate)
        'ln -s {}'.format(relPathToLatexTemplate)
        'ln -s {}'.format(os.path.join(latexSourceDir, 'logo_pole.png'))
    runCommand(
        'ln -s {}/schema_rugosite_fond.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))
    )
    runCommand(
        'ln -s {}/bloc_cylindre.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))
    )
    runCommand(
        'ln -s {}/bloc_face_arrondie.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))
    )
    runCommand(
        'ln -s {}/bloc_base_carree.png'.format(os.path.join(baseDir, 'docs', lang, 'calculators', 'pam'))
    )
        'rm rapport_inrae/logos.tex'
    )
        'ln -s {} 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( baseDir, 'build', 'cassiopee_version.tex')
    shutil.copy(cvt, modelDir)

        '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
    os.makedirs(buildDir, exist_ok=True)
    # Prepare output directory
    os.makedirs(outputDir, exist_ok=True)

    # Read config
    yamlPath = '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(buildDir, 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(buildDir)


if __name__ == '__main__':
mathias.chouet's avatar
mathias.chouet committed

    for l in ['fr', 'en']:
        print('building PDF doc for language "{}"'.format(l))
        buildDocForLang(l)