[PYTHON] Lassen Sie uns ein Kindle-Buch erstellen, das mathematische Formeln aus TeX-Dateien visualisiert

Rakuten Kobo und iBooks sind mit MathML kompatibel, und das Rendern ist auch recht gut, aber der größte Amazon Kindle ist nicht kompatibel (möglicherweise handelt es sich um eine kostengünstige Beziehung, ich glaube nicht, dass sie in Zukunft unterstützt wird). , Um ein mathematisches Kindle-Buch vom Reflow-Typ zu erstellen, ist es nicht bereit, mathematische Formeln abzubilden.

Ich möchte das Vektorbild svg zumindest verwenden, um die Formel in ein Bild zu konvertieren, bin mir aber nicht sicher, ob Kindle es unterstützt oder nicht (Wird es von iOS-Apps nicht unterstützt?). , Es scheint sicher zu sein, es leise png zu machen. Es ist jedoch nicht möglich, alle Dokumente wie tex, die Hunderte von mathematischen Formeln enthalten, manuell abzubilden und in Formate wie epub3 zu formatieren. Daher dient dieser Artikel der Automatisierung. ..

wichtiger Punkt

Die Umgebung ist macOS. Bitte beachten Sie, dass der Umgang mit der Benennung von Variablen und Verzeichnissen möglicherweise unprofessionell ist. Es kann später etwas schlauer umgeschrieben werden.

Speziell verwendet

Grobe Prozedur

  1. Dokumenterstellung mit tex
  2. Konvertieren Sie mit pandoc in tex-> html + mathjax
  3. Machen Sie den Mathjax-Teil png
  4. Mit pandoc in html-> epub3 konvertieren
  5. Epub3 entpacken, überarbeiten und erneut komprimieren
  6. Fügen Sie epub3 in Kindle Previewer ein und erstellen Sie mobi (kindlegen ist auch OK)

Code vorerst

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

def tex2html(source, target): #Bitte ändern Sie die Pandoc-Optionen entsprechend
    subprocess.call(['pandoc',
                     '-s',
                     '-t', 'html5',
                     '--mathjax',
                     '--css=stylesheet.css',
                     '-o', target, source])    

def get_html(filename, encoding='utf-8', xml=False):
    #Lxml beim Parsen von xhtml.Beim Parsen von etree und html, lxml.Verwenden Sie 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 beim Parsen von xhtml.Beim Parsen von etree und html, lxml.Verwenden Sie 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 = {} #Zum Aufzeichnen des Tex-Codes und der Bilddatei, um nicht dasselbe Bild zu erstellen
    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('../')

Beschreibung jeder Funktion

tex2html(source, target)

Verwenden Sie "pandoc", um die TeX-Datei in HTML5 zu konvertieren. Mit der Option "--mathjax" lautet der Teil der Formel

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

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

Es wird in Form von ausgegeben. Dieser Teil wird mit "lxml" extrahiert und nacheinander abgebildet.

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

Analysiert das HTML-Dokument mit lxml und gibt das Stammelement html zurück. Standardmäßig wird HTML angenommen. Wenn Sie jedoch XHTML analysieren möchten, fügen Sie die Option "xml = True" hinzu.

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

Schreiben Sie eine HTML-Datei mit dem Element "html" als Argument.

convert2png(filename, newfilename, convert=True)

In einem HTML-Dokument

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

Bildmagie "konvertieren", um solche Teile abzubilden

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

Wir werden es durch ein "img" -Element des Formulars ersetzen. Das Attribut "height" ist ein ziemlich wichtiger Wert, mit dem die Höhe der letzten Formel angepasst wird. Dies wird von einer Python-Bibliothek namens "imagesize" aufgenommen. Wenn es viele Formeln gibt, dauert die Konvertierung einige Zeit. Wenn Sie "convert = False" setzen, wird der Bildkonvertierungsvorgang übersprungen. Wenn Sie nicht erneut konvertieren müssen, z. B. bei der Überarbeitung, sparen Sie Zeit, wenn Sie den Wert auf "Falsch" setzen.

Das erzeugte Bild wird übrigens etwas größer (4-fache der ursprünglichen Einstellung, möglicherweise ca. 40pt). Wenn Sie ein Bild mit den Standardeinstellungen erstellen, wird es beschädigt und nicht lesbar. Daher verwenden wir eine Methode, um es zu vergrößern und zu verkleinern.

Übrigens, wenn ich versuche, tex abzubilden, bekomme ich einen Treffer namens "dvipng", aber es scheint mühsam zu sein, die japanische Konvertierung zu unterstützen Ist einfach.

html2epub(source, target, css, cover)

Konvertieren Sie das HTML-Dokument, das von der Formel abgebildet wurde, in epub mit pandoc. Geben Sie für "css" den Dateinamen des einzubettenden Stylesheets und für "cover" den Dateinamen des einzubettenden Titelbilds ein.

extract_epub(filename, tmpdir)

Entpacken Sie nach "tmpdir", um das von "html2epub" erstellte Epub zu ändern. Ich denke, dass epub3 relativ berühmt dafür ist, eine Zip-Datei zu sein.

make_epub(filename, tmpdir)

Stellen Sie die überarbeiteten Dateien in epub zusammen (ich habe hier darauf verwiesen: Mit Terminal in EPUB komprimieren).

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

Es ist eine One-Touch-Version einer Reihe von Prozessen. Der Name.tex im aktuellen Verzeichnis wird als Quelle verwendet, und schließlich wird die Datei name.epub generiert. Da dabei viele Dateien generiert werden, wird ein Verzeichnis mit dem Namen "name_tmpdir" erstellt und die generierten Dateien dort ausgegeben. Legen Sie die Dateien "css" und "cover" im selben Verzeichnis wie "name.tex" ab.

Was ist das Problem?

Ich habe einmal einen Epub mit Pandoc erstellt, ihn entpackt, * modifiziert * und wieder zu einem Epub gemacht. Was zum Teufel mit dieser Überarbeitung macht, ist die Anpassung der Höhe des mathematisch formulierten Bildes. Wenn MathML dies unterstützt, müssen Sie sich darüber keine Gedanken machen. Wenn Sie jedoch mathematische Formeln abbilden möchten, müssen Sie die Breite oder Höhe anpassen, und wenn Sie absolut angeben (oder nicht angeben), den Epub-Viewer Auch wenn Sie die Schriftgröße mit ändern, ändert sich die Größe der Formel nicht. Um es zu umgehen,

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

Es scheint, dass es keine andere Wahl gibt, als relativ zum Attribut "style" anzugeben (bitte lassen Sie mich wissen, ob es einen anderen besseren Weg gibt). Auszug aus dem Code, der diesen Teil festlegt:

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

Insbesondere wird die Einheit "em" zu dem Wert addiert, der durch Teilen der Höhe des tatsächlichen PNG-Bildes durch 40 erhalten wird. Bitte in einigen Fällen anpassen.

Wenn Sie das Attribut "style" in der Phase von "html2epub ()" festlegen, denken Sie möglicherweise, dass Sie sich nicht die Mühe machen müssen, es zu ändern, sondern wenn Sie HTML mit "pandoc" in epub konvertieren , Es gibt eine Klauselspezifikation, dass das Attribut style des Elements img gelöscht wird.

Nun, nachdem dies gesagt wurde, gibt es andere Elemente, die das vom "Pandoc" erzeugte "Epub" modifizieren, daher denke ich, dass diese Dekomprimierungs- und Komprimierungsarbeit nicht verschwenderisch ist. Das Hinzufügen des Attributs "lang" und des Attributs "epub: lang" zum Element "html" ist beispielsweise nur der Zeitpunkt für diese Überarbeitung. Es gibt einige andere Elemente, die geändert werden sollten (für einige Leute), wie z. B. das Attribut "id" des Elements "section".

stylesheet.css

Wenn das Attribut "class" "math display" ist, ist "display: block" erforderlich.

stylesheet.css


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

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

Ich mag die Ränder. Passen Sie außerdem den Kurs- und Zeilenabstand selbst an.

Stichprobe

sample.tex


\documentclass[uplatex]{jsbook}
\usepackage{amsmath,amssymb}
\begin{document}
\title{Zahlensystem}

\chapter{Natürliche Zahl}

Das kleinste Set, das von der unendlichen Aprikose garantiert wird$\omega$Dann$\emptyset\in\omega$Und jeder$x\in\omega$Gegen
\begin{align*}
  \sigma: x\mapsto x\cup\{x\}
\end{align*}
Durch$\omega$Von$\omega$Die Zuordnung zu ist definiert.

\section{Peanos Axiomensystem}

$(\omega, \emptyset, \sigma)$Erfüllt das sogenannte Peano-Axiomensystem.

\chapter{ganze Zahl}

Zwei natürliche Zahlen$m, n$Gegen$(m, n)$Die ganze Zahl$m-n$Der Hinweis auf eine ganzzahlige Zusammensetzung ist, sie als zu betrachten.

\section{Äquivalente Beziehung}

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

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

Wahrscheinlich

Diesmal basierte es auf der TeX-Datei, aber es scheint, dass dies mit dem trendigen Abschlag möglich ist. Da TeX jedoch für die Bildgebung verwendet wird, ist eine TeX-Umgebung unerlässlich.

Recommended Posts

Lassen Sie uns ein Kindle-Buch erstellen, das mathematische Formeln aus TeX-Dateien visualisiert
Aus einem Buch, das Programmierer lernen können ... (Python): Zeiger
"Ein Buch, das Flask von Grund auf versteht" Memo lesen
Aus einem Buch, das Programmierer lernen können ... (Python): Über das Sortieren
Aus einem Buch, das Programmierer lernen können (Python): Nachrichten dekodieren
Aus einem Buch, das der Programmierer lernen kann ... (Python): Finden Sie den häufigsten Wert
Aus einem Buch, das Programmierer lernen können ... (Python): Überprüfung von Arrays
Aus einem Buch, in dem die Gedanken des Programmierers gelernt werden können: Fassen Sie die Teile kleiner Probleme zusammen
Aus einem Buch, das Programmierer lernen können (Python): Statistischer Verarbeitungsabweichungswert
Machen Sie eine Santa-Kennung aus einem Santa-Bild
Python / Machen Sie ein Diktat aus einer Liste.
Erstellen Sie einen Discord Bot, der Bilder suchen und einfügen kann
Aus einem Buch, das der Programmierer lernen kann ... (Python): Bedingte Suche (Maximalwert)
Aus einem Buch, das die Denkweise des Programmierers interessanterweise gelernt hat (Python)