[PYTHON] Faisons un livre Kindle qui visualise des formules mathématiques à partir de fichiers TeX

Rakuten Kobo et iBooks sont compatibles avec MathML, et le rendu est également assez bon, mais le plus grand Kindle d'Amazon n'est pas compatible (c'est peut-être une relation rentable, je ne pense pas qu'elle sera prise en charge à l'avenir) , Afin de créer un livre Kindle semblable aux mathématiques de type redistribution, il n'est pas disposé à imager des formules mathématiques.

J'aimerais au moins utiliser le svg d'image vectorielle pour convertir la formule en image, mais je ne sais pas si Kindle le prend en charge ou non (n'est-il pas pris en charge par les applications iOS?). , Il semble sûr de le rendre png tranquillement. Cependant, il n'est pas possible d'imager manuellement tous les documents tels que tex contenant des centaines de formules mathématiques et de les formater dans des formats tels que epub3, le but de cet article est donc d'automatiser. ..

point important

L'environnement est macOS. Veuillez noter que la gestion du sens de nommage des variables et des répertoires peut ne pas être professionnelle. Il peut être réécrit un peu plus intelligemment plus tard.

Spécialement utilisé

Procédure approximative

  1. Création de documents avec tex
  2. Convertissez en tex-> html + mathjax avec pandoc 3.Faire la partie mathjax png
  3. Convertir en html-> epub3 avec pandoc
  4. Décompressez epub3, retravaillez et recompressez
  5. Insérez epub3 dans Kindle Previewer et créez mobi (kindlegen est également OK)

Code pour le moment

import subprocess, lxml.html, lxml.etree, imagesize, re, tempfile

def tex2html(source, target): #Veuillez modifier les options pandoc en conséquence
    subprocess.call(['pandoc',
                     '-s',
                     '-t', 'html5',
                     '--mathjax',
                     '--css=stylesheet.css',
                     '-o', target, source])    

def get_html(filename, encoding='utf-8', xml=False):
    #Lxml lors de l'analyse de xhtml.Lors de l'analyse d'etree et html, lxml.Utilisez html
    with open(filename, 'r', encoding=encoding) as f:
        if xml == True:
            html = lxml.etree.parse(f).getroot()
        else:
            html = lxml.html.parse(f).getroot()
    return html

def write_html(html, filename, encoding='utf-8', xml=False):
    #Lxml lors de l'analyse de xhtml.Lors de l'analyse d'etree et html, lxml.Utilisez html
    if xml == True:
        src = lxml.etree.tostring(html, 
                                  encoding=encoding,
                                  xml_declaration=True,
                                  doctype='<!DOCTYPE html>',
                                  method='xml',
                                  pretty_print=True).decode(encoding)
    else:
        src = lxml.html.tostring(html,
                                 encoding=encoding,
                                 doctype='<!DOCTYPE html>',
                                 pretty_print=True).decode(encoding)
    with open(filename, 'w', encoding=encoding) as f:
        f.write(src)

def convert2png(filename, newfilename, convert=True):
    html= get_html(filename)
    textemplate = r'''\documentclass[a0paper, uplatex]{jsarticle}
\usepackage[dvipdfmx]{graphicx}
\usepackage[margin=1cm]{geometry}
\usepackage{amsmath,amssymb}
\pagestyle{empty}
\begin{document}
\scalebox{4}{\parbox{.25\linewidth}{MATH}}
\end{document}
'''
    imgs = {} #Pour enregistrer le code tex et le fichier image afin de ne pas faire la même image
    for span in html.xpath(r'//span[@class="math inline" or @class="math display"]'):
        tex = span.text
        if tex in imgs:
            imgsrc = imgs[tex]
        else:
            imgsrc = r'math{0:04d}.png'.format(len(imgs)+1)
            imgs[tex] = imgsrc
            if convert == True:
                with open('tmp.tex', 'w') as texf:
                    texf.write(textemplate.replace('MATH', tex))
                subprocess.call('uplatex tmp.tex', shell=True)
                subprocess.call('dvipdfmx tmp.dvi', shell=True)
                subprocess.call('convert -trim tmp.pdf '+imgsrc, shell=True)
        span.tag = 'img'
        span.text = None
        span.attrib['src'] = imgsrc
        span.attrib['alt'] = tex
        width, height = imagesize.get(imgsrc)
        span.attrib['height'] = str(height)

    write_html(html, newfilename)
                           
def html2epub(source, target, css, cover):
    subprocess.call(['pandoc',
                     '-t', 'epub3',
                     '--toc',
                     '--epub-chapter-level=1',
                     '--epub-stylesheet', css,
                     '--epub-cover-image', cover,
                     '-o', target, source])

def extract_epub(filename, tmpdir):
    subprocess.call('mkdir {0}'.format(tmpdir), shell=True)
    subprocess.call('unzip -d {0} {1}'.format(tmpdir, filename), shell=True)

def make_epub(filename, tmpdir):
    subprocess.os.chdir(tmpdir)
    subprocess.call(r'zip -0 ../{filename} mimetype;zip -XrD ../{filename} *'.format(filename=filename, tmpdir = tmpdir),shell=True)
    subprocess.os.chdir('../')
    
def make_epub_for_kindle(name, css, cover, convert=True):
    tmpdir = name + '_tmpdir'
    if not subprocess.os.path.isdir(tmpdir):
        subprocess.os.mkdir(tmpdir)
    subprocess.os.chdir(tmpdir)
    tex2html('../'+name+'.tex', name+'.html')
    convert2png(name+'.html', name+'2.html', convert)
    html2epub(name+'2.html', name+'0.epub', '../'+css, '../'+cover)
    epubdir = tempfile.TemporaryDirectory(dir='./')
    extract_epub(name+'0.epub', epubdir.name)
    ns = {'xhtml': 'http://www.w3.org/1999/xhtml'}
    for filename in [fn for fn in subprocess.os.listdir(epubdir.name) if re.match(r'ch[\d]{3}.xhtml', fn)]:
        xhtml = get_html(epubdir.name+'/'+filename, xml=True)
        xhtml.attrib['{http://www.idpf.org/2007/ops}lang'] = 'ja'
        xhtml.attrib['lang'] = 'ja'
        for img in xhtml.xpath('//xhtml:img', namespaces=ns):
            height = int(img.attrib['height'])
            img.attrib['style'] = 'height: ' + str(round(height/40, 2)) + 'em;'
            del img.attrib['height']
        write_html(xhtml, epubdir.name+'/'+filename, xml=True)
    make_epub(name+'.epub', epubdir.name)
    epubdir.cleanup()
    subprocess.os.chdir('../')

Description de chaque fonction

tex2html(source, target)

Utilisez pandoc pour convertir le fichier TeX en HTML5. Avec l'option --mathjax, la partie de la formule est

<span class="math inline">\(e^{\pi i} = -1\)</span>

<span class="math display">\[e^{\pi i} = -1\]</span>

Il est produit sous la forme de. Cette partie est extraite avec «lxml» et mise en image séquentiellement.

get_html(filename, encoding='utf-8', xml=False)

Analyse le document HTML avec lxml et renvoie l'élément racine élément html. Par défaut, HTML est utilisé, mais si vous souhaitez analyser XHTML, ajoutez l'option xml = True`.

write_html(html, filename, encoding='utf-8', xml=False)

Écrivez un fichier HTML avec l'élément html comme argument.

convert2png(filename, newfilename, convert=True)

Dans un document HTML

<span class="math inline">\(e^{\pi i} = -1\)</span>

Image Magick's `Convert 'to image such parts

<img src="math0001.png " class="math inline" alt="\(e^{\pi i} = -1\)" height="30">

Nous le remplacerons par l'élément ʻimgdu formulaire. L'attributheight est une valeur assez importante utilisée pour ajuster la hauteur de la dernière formule. Ceci est récupéré par une bibliothèque python appelée ʻimagesize. S'il existe de nombreuses formules, la conversion prendra du temps. Si vous définissez convert = False, la procédure de conversion d'image sera ignorée. Si vous n'avez pas besoin de convertir à nouveau, comme lors de la retouche, le paramétrer sur «False» vous fera gagner du temps.

En passant, l'image générée est un peu plus grande (4 fois le réglage initial, peut-être environ 40 pt). Si vous créez une image avec les paramètres par défaut, elle sera écrasée et illisible, nous utilisons donc une méthode pour l'agrandir et la réduire.

Au fait, lorsque j'essaie de créer une image tex, j'obtiens un hit appelé dvipng, mais il semble difficile de prendre en charge la conversion japonaise, alors comment convertir un pdf en png avec convert de imagemagick Est facile.

html2epub(source, target, css, cover)

Convertissez le document HTML qui a été imagé de la formule en epub avec pandoc. Pour css, entrez le nom de fichier de la feuille de style à incorporer, et pour cover, entrez le nom de fichier de l'image de couverture à incorporer.

extract_epub(filename, tmpdir)

Décompressez dans tmpdir pour modifier l'epub créé par html2epub. Je pense que epub3 est relativement célèbre pour être un fichier zip.

make_epub(filename, tmpdir)

Rassemblez les fichiers retravaillés dans epub (je me suis référé ici: Compresser vers EPUB à l'aide du terminal).

make_epub_for_kindle(name, css, cover, convert=True)

Il s'agit d'une version instantanée d'une série de processus. Le name.tex dans le répertoire courant est utilisé comme source, et finalement le fichier name.epub est généré. Étant donné que de nombreux fichiers sont générés dans le processus, il est conçu pour créer un répertoire appelé nom_tmpdir et y afficher les fichiers générés. Placez les fichiers css et cover dans le même répertoire que name.tex.

Quelle est la solution?

J'ai créé un epub avec pandoc une fois, je l'ai décompressé, * modifié * et en ai fait à nouveau un epub. Ce que fait ce remaniement, c'est d'ajuster la hauteur de l'image formulée mathématiquement. Si MathML le prend en charge, vous n'avez pas à vous en soucier, mais en ce qui concerne l'imagerie de formules mathématiques, vous devez ajuster la largeur ou la hauteur, et si vous spécifiez absolument (ou non), la visionneuse epub Même si vous modifiez la taille de la police avec, la taille de la formule ne change pas. Pour éviter cela

<img src="math001.png " style='height: 1.0em;'>

Il semble qu'il n'y ait pas d'autre choix que de spécifier par rapport à l'attribut style (veuillez me faire savoir s'il existe une autre meilleure façon). Extrait du code qui définit cette partie:

for img in xhtml.xpath('//xhtml:img', namespaces=ns):
    height = int(img.attrib['height'])
    img.attrib['style'] = 'height: ' + str(round(height/40, 2)) + 'em;'

Plus précisément, l'unité «e» est ajoutée à la valeur obtenue en divisant la hauteur de l'image png réelle par 40. Veuillez ajuster dans certains cas.

Vous pensez peut-être que si vous définissez l'attribut style au stade de html2epub (), vous n'avez pas à vous soucier de le modifier, mais lorsque vous convertissez html en epub avec pandoc, , ʻImgIl existe une spécification de clause selon laquelle l'attributstyle` de l'élément est supprimé.

Eh bien, cela dit, il y a d'autres éléments qui modifient le ʻepub généré par pandoc, donc je pense que ce travail de décompression et de compression n'est pas inutile. Par exemple, l'ajout de l'attribut «lang» et de l'attribut «epub: lang» à l'élément «html» n'est que le moment de cette retouche. Il y a pas mal d'autres éléments qui devraient être modifiés (pour certaines personnes), tels que l'attribut ʻid de l'élément section.

stylesheet.css

Si l'attribut class est math display, display: block sera requis.

stylesheet.css


img.math.display{
    display: block;
    margin: .5em auto; /*Centré*/
}

img.math.inline{
    margin-left: .2em;
    margin-right: .2em;
}

J'aime les marges. De plus, veuillez ajuster vous-même le titre et l'interligne.

échantillon

sample.tex


\documentclass[uplatex]{jsbook}
\usepackage{amsmath,amssymb}
\begin{document}
\title{Système de nombres}

\chapter{Entier naturel}

Le plus petit ensemble garanti par l'abricot infini$\omega$Puis$\emptyset\in\omega$Et n'importe quel$x\in\omega$Contre
\begin{align*}
  \sigma: x\mapsto x\cup\{x\}
\end{align*}
Par$\omega$De$\omega$Le mappage vers est défini.

\section{Système d'axiomes de Peano}

$(\omega, \emptyset, \sigma)$Satisfait au soi-disant système d'axiomes de Peano.

\chapter{entier}

Deux nombres naturels$m, n$Contre$(m, n)$L'entier$m-n$L'astuce de la composition entière est de la considérer comme.

\section{Relation équivalente}

$(1, 0)$Quand$(2, 1)$は同じ整数Quandみるので,同値関係が必要になる.

\end{document}
make_epub_for_kindle('sample', 'stylesheet.css', 'cover.png')
スクリーンショット 2017-09-17 21.12.48.png

Probablement

Cette fois, il est basé sur le fichier TeX, mais il semble que cela puisse être fait à partir de la démarque à la mode. Cependant, puisque TeX est utilisé pour l'imagerie, un environnement TeX est essentiel.

Recommended Posts

Faisons un livre Kindle qui visualise des formules mathématiques à partir de fichiers TeX
D'un livre que les programmeurs peuvent apprendre ... (Python): Pointer
"Un livre qui comprend Flask à partir de zéro" Lecture d'un mémo
À partir d'un livre que les programmeurs peuvent apprendre ... (Python): À propos du tri
À partir d'un livre que les programmeurs peuvent apprendre (Python): Décoder les messages
À partir d'un livre que le programmeur peut apprendre ... (Python): trouver la valeur la plus fréquente
À partir d'un livre que les programmeurs peuvent apprendre ... (Python): examen des tableaux
D'un livre que les pensées du programmeur peuvent être apprises: résumez les parties de petits problèmes
À partir d'un livre que les programmeurs peuvent apprendre (Python): valeur de l'écart de traitement statistique
Créer un identifiant Santa à partir d'une image du Père Noël
python / Créer un dict à partir d'une liste.
Créez un Discord Bot qui peut rechercher et coller des images
À partir d'un livre que le programmeur peut apprendre ... (Python): Recherche conditionnelle (valeur maximale)
D'un livre qui apprend de manière intéressante la façon de penser du programmeur (Python)