Add support for LaTeX export

This commit is contained in:
Veronica K. B. Olsen
2019-10-25 00:07:54 +02:00
parent 01dde7c8a4
commit b9608dcdbd
3 changed files with 226 additions and 2 deletions
+66
View File
@@ -0,0 +1,66 @@
# -*- coding: utf-8 -*-
"""novelWriter LaTeX File
novelWriter LaTeX File
==========================
Writes the project to a LaTeX file
File History:
Created: 2019-10-24 [0.3.1]
"""
import logging
import nw
from nw.convert.textfile import TextFile
from nw.convert.tolatex import ToLaTeX
from nw.enum import nwAlert
logger = logging.getLogger(__name__)
class LaTeXFile(TextFile):
def __init__(self, theProject, theParent):
TextFile.__init__(self, theProject, theParent)
self.theConv = ToLaTeX(self.theProject, self.theParent)
return
##
# Internal Functions
##
def _doOpenFile(self, filePath):
if self.winEnding:
tN = "\r\n"
else:
tN = "\n"
try:
self.outFile = open(filePath,mode="w+")
self.outFile.write(r"\documentclass[12pt]{report}"+tN+tN)
self.outFile.write(r"\usepackage[utf8]{inputenc}"+tN+tN)
self.outFile.write(r"\begin{document}"+tN+tN)
except Exception as e:
self.makeAlert(["Failed to open file.",str(e)], nwAlert.ERROR)
return False
return True
def _doCloseFile(self):
if self.winEnding:
tN = "\r\n"
else:
tN = "\n"
if self.outFile is not None:
self.outFile.write(r"\end{document}"+tN)
self.outFile.close()
return True
# END Class LaTeXFile
+155
View File
@@ -0,0 +1,155 @@
# -*- coding: utf-8 -*-
"""novelWriter LaTeX Converter
novelWriter LaTeX Converter
===============================
Extends the Tokenizer class to write LaTeX
File History:
Created: 2019-10-24 [0.3.1]
"""
import textwrap
import logging
import re
import nw
from nw.convert.tokenizer import Tokenizer
logger = logging.getLogger(__name__)
class ToLaTeX(Tokenizer):
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
return
def doAutoReplace(self):
Tokenizer.doAutoReplace(self)
repDict = {
"\u2013" : "--",
"\u2014" : "---",
"\u2500" : "---",
"\u2026" : "...",
}
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText)
return
def doConvert(self):
texTags = {
self.FMT_B_B : r"\textbf{",
self.FMT_B_E : r"}",
self.FMT_I_B : r"\textit{",
self.FMT_I_E : r"}",
self.FMT_U_B : r"\underline{",
self.FMT_U_E : r"}",
}
if self.wordWrap > 0:
tWrap = textwrap.TextWrapper(
width = self.wordWrap,
initial_indent = "",
subsequent_indent = "",
expand_tabs = True,
replace_whitespace = True,
fix_sentence_endings = False,
break_long_words = True,
drop_whitespace = True,
break_on_hyphens = True,
tabsize = 8,
max_lines = None
)
tComm = textwrap.TextWrapper(
width = self.wordWrap-2,
initial_indent = "",
subsequent_indent = "",
expand_tabs = True,
replace_whitespace = True,
fix_sentence_endings = False,
break_long_words = True,
drop_whitespace = True,
break_on_hyphens = True,
tabsize = 8,
max_lines = None
)
self.theResult = ""
thisPar = []
for tType, tText, tFormat, tAlign in self.theTokens:
begText = ""
endText = "\n"
if tAlign == self.A_CENTRE:
begText = "\\begin{center}\n"
endText = "\\end{center}\n\n"
# First check if we have a comment or plain text, as they need some
# extra replacing before we proceed to wrapping and final formatting.
if tType == self.T_COMMENT:
tText = "%% %s" % tText
elif tType == self.T_TEXT:
tTemp = tText
for xPos, xLen, xFmt in reversed(tFormat):
tTemp = tTemp[:xPos]+texTags[xFmt]+tTemp[xPos+xLen:]
tText = tTemp
tLen = len(tText)
# The text can now be word wrapped, if we have requested this and it's needed.
if self.wordWrap > 0 and tLen > self.wordWrap:
if tType == self.T_COMMENT:
aText = tComm.wrap(tText)
tText = "\n% ".join(aText)
else:
tText = tWrap.fill(tText)
# Then the text can receive final formatting before we append it to the results.
# We also store text lines in a buffer and merge them only when we find an empty line,
# indicating a new paragraph.
if tType == self.T_EMPTY:
if len(thisPar) > 0:
self.theResult += begText
self.theResult += "%s\n" % tWrap.fill(" ".join(thisPar))
self.theResult += endText
thisPar = []
elif tType == self.T_HEAD1:
self.theResult += begText
self.theResult += "{\\Huge %s}\n" % tText
self.theResult += endText
elif tType == self.T_HEAD2:
self.theResult += "\\chapter*{%s}\n\n" % tText
elif tType == self.T_HEAD3:
self.theResult += "\\section*{%s}\n\n" % tText
elif tType == self.T_HEAD4:
self.theResult += "\\subsection*{%s}\n\n" % tText
elif tType == self.T_SEP:
self.theResult += begText
self.theResult += "%s\n" % tText
self.theResult += endText
elif tType == self.T_TEXT:
thisPar.append(tText)
elif tType == self.T_PBREAK:
self.theResult += "\\newpage\n\n"
elif tType == self.T_COMMENT and self.doComments:
self.theResult += "%s\n\n" % tText
elif tType == self.T_COMMAND and self.doCommands:
self.theResult += "%% @%s\n\n" % tText
return
# END Class ToLaTeX
+5 -2
View File
@@ -29,6 +29,7 @@ from nw.tools.optlaststate import OptLastState
from nw.convert.textfile import TextFile from nw.convert.textfile import TextFile
from nw.convert.htmlfile import HtmlFile from nw.convert.htmlfile import HtmlFile
from nw.convert.markdownfile import MarkdownFile from nw.convert.markdownfile import MarkdownFile
from nw.convert.latexfile import LaTeXFile
from nw.constants import nwFiles from nw.constants import nwFiles
from nw.enum import nwItemType from nw.enum import nwItemType
@@ -127,6 +128,8 @@ class GuiExport(QDialog):
outFile = MarkdownFile(self.theProject, self.theParent) outFile = MarkdownFile(self.theProject, self.theParent)
elif eFormat == GuiExportMain.FMT_HTML: elif eFormat == GuiExportMain.FMT_HTML:
outFile = HtmlFile(self.theProject, self.theParent) outFile = HtmlFile(self.theProject, self.theParent)
elif eFormat == GuiExportMain.FMT_TEX:
outFile = LaTeXFile(self.theProject, self.theParent)
if outFile is None: if outFile is None:
return False return False
@@ -348,7 +351,7 @@ class GuiExportMain(QWidget):
self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML) self.outputFormat.addItem("HTML5 (.htm)", self.FMT_HTML)
# self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK) # self.outputFormat.addItem("HTML5 for eBook (.htm)", self.FMT_EBOOK)
# self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT) # self.outputFormat.addItem("Open Document (.odt)", self.FMT_ODT)
# self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX) self.outputFormat.addItem("LaTeX for PDF (.tex)", self.FMT_TEX)
self.outputFormat.currentIndexChanged.connect(self._updateFormat) self.outputFormat.currentIndexChanged.connect(self._updateFormat)
optIdx = self.outputFormat.findData(self.optState.getSetting("eFormat")) optIdx = self.outputFormat.findData(self.optState.getSetting("eFormat"))
@@ -417,7 +420,7 @@ class GuiExportMain(QWidget):
"Markdown files (*.md)", "Markdown files (*.md)",
"HTML files (*.htm *.html)", "HTML files (*.htm *.html)",
# "Open document files (*.odt)", # "Open document files (*.odt)",
# "LaTeX files (*.tex)", "LaTeX files (*.tex)",
"All files (*.*)", "All files (*.*)",
] ]