diff --git a/nw/convert/htmlfile.py b/nw/convert/file/html.py similarity index 91% rename from nw/convert/htmlfile.py rename to nw/convert/file/html.py index 0b175e77..b5acaf2e 100644 --- a/nw/convert/htmlfile.py +++ b/nw/convert/file/html.py @@ -13,9 +13,9 @@ import logging import nw -from nw.convert.textfile import TextFile -from nw.convert.tohtml import ToHtml -from nw.enum import nwAlert +from nw.convert.file.text import TextFile +from nw.convert.text.tohtml import ToHtml +from nw.enum import nwAlert logger = logging.getLogger(__name__) diff --git a/nw/convert/latexfile.py b/nw/convert/file/latex.py similarity index 90% rename from nw/convert/latexfile.py rename to nw/convert/file/latex.py index 448236ff..cdaeb66e 100644 --- a/nw/convert/latexfile.py +++ b/nw/convert/file/latex.py @@ -13,9 +13,9 @@ import logging import nw -from nw.convert.textfile import TextFile -from nw.convert.tolatex import ToLaTeX -from nw.enum import nwAlert +from nw.convert.file.text import TextFile +from nw.convert.text.tolatex import ToLaTeX +from nw.enum import nwAlert logger = logging.getLogger(__name__) diff --git a/nw/convert/markdownfile.py b/nw/convert/file/markdown.py similarity index 86% rename from nw/convert/markdownfile.py rename to nw/convert/file/markdown.py index 0f490d1e..b5d2c32c 100644 --- a/nw/convert/markdownfile.py +++ b/nw/convert/file/markdown.py @@ -13,9 +13,9 @@ import logging import nw -from nw.convert.textfile import TextFile -from nw.convert.tomarkdown import ToMarkdown -from nw.enum import nwAlert +from nw.convert.file.text import TextFile +from nw.convert.text.tomarkdown import ToMarkdown +from nw.enum import nwAlert logger = logging.getLogger(__name__) diff --git a/nw/convert/textfile.py b/nw/convert/file/text.py similarity index 96% rename from nw/convert/textfile.py rename to nw/convert/file/text.py index 3ffeb7ff..b4adee27 100644 --- a/nw/convert/textfile.py +++ b/nw/convert/file/text.py @@ -16,8 +16,8 @@ import nw from os import path from PyQt5.QtWidgets import QMessageBox -from nw.convert.tokenizer import Tokenizer -from nw.enum import nwAlert, nwItemLayout +from nw.convert.text.totext import ToText +from nw.enum import nwAlert, nwItemLayout logger = logging.getLogger(__name__) @@ -36,7 +36,7 @@ class TextFile(): self.expNotes = False self.winEnding = False - self.theConv = Tokenizer(self.theProject, self.theParent) + self.theConv = ToText(self.theProject, self.theParent) self.makeAlert = self.theParent.makeAlert self.setComments(False) diff --git a/nw/convert/tohtml.py b/nw/convert/text/tohtml.py similarity index 100% rename from nw/convert/tohtml.py rename to nw/convert/text/tohtml.py diff --git a/nw/convert/tolatex.py b/nw/convert/text/tolatex.py similarity index 100% rename from nw/convert/tolatex.py rename to nw/convert/text/tolatex.py diff --git a/nw/convert/tomarkdown.py b/nw/convert/text/tomarkdown.py similarity index 100% rename from nw/convert/tomarkdown.py rename to nw/convert/text/tomarkdown.py diff --git a/nw/convert/text/totext.py b/nw/convert/text/totext.py new file mode 100644 index 00000000..abb42e7c --- /dev/null +++ b/nw/convert/text/totext.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +"""novelWriter Plain Text Converter + + novelWriter – Plain Text Converter +==================================== + Extends the Tokenizer class to convert to plain text + + File History: + Created: 2019-10-26 [0.3.1] + +""" + +import textwrap +import logging +import re +import nw + +from nw.convert.tokenizer import Tokenizer + +logger = logging.getLogger(__name__) + +class ToText(Tokenizer): + + def __init__(self, theProject, theParent): + Tokenizer.__init__(self, theProject, theParent) + return + + def doConvert(self): + """Converts the tokenized text into plain text. + """ + + 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 + ) + + self.theResult = "" + thisPar = [] + for tType, tText, tFormat, tAlign in self.theTokens: + + # 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]+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 tAlign == self.A_CENTRE: + if self.wordWrap > 0: + if tLen > self.wordWrap: + aText = tWrap.wrap(tText) + for n in range(len(aText)): + aText[n] = self._centreText(aText[n],self.wordWrap) + tText = "\n".join(aText) + else: + tText = self._centreText(tText,self.wordWrap) + else: + if self.wordWrap > 0 and tLen > self.wordWrap: + 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 += "%s\n\n" % " ".join(thisPar) + thisPar = [] + + elif tType == self.T_HEAD1: + uLine = "="*min(tLen,self.wordWrap) + if tAlign == self.A_CENTRE: + uLine = self._centreText(uLine,self.wordWrap) + self.theResult += "%s\n%s\n\n" % (tText,uLine) + + elif tType == self.T_HEAD2: + uLine = "~"*min(tLen,self.wordWrap) + self.theResult += "%s\n%s\n\n" % (tText,uLine) + + elif tType == self.T_HEAD3: + uLine = "-"*min(tLen,self.wordWrap) + self.theResult += "%s\n%s\n\n" % (tText,uLine) + + elif tType == self.T_HEAD4: + self.theResult += "%s\n\n" % tText + + elif tType == self.T_SEP: + if self.wordWrap > 0 and tLen < self.wordWrap: + tText = self._centreText(tText,self.wordWrap) + self.theResult += "%s\n\n" % tText + + elif tType == self.T_TEXT: + thisPar.append(tText) + + 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 ToText diff --git a/nw/convert/tokenizer.py b/nw/convert/tokenizer.py index e4fb34ac..a59b4f13 100644 --- a/nw/convert/tokenizer.py +++ b/nw/convert/tokenizer.py @@ -276,97 +276,6 @@ class Tokenizer(): return - def doConvert(self): - """Converts the tokenized text into plain text. - """ - - 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 - ) - - self.theResult = "" - thisPar = [] - for tType, tText, tFormat, tAlign in self.theTokens: - - # 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]+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 tAlign == self.A_CENTRE: - if self.wordWrap > 0: - if tLen > self.wordWrap: - aText = tWrap.wrap(tText) - for n in range(len(aText)): - aText[n] = self._centreText(aText[n],self.wordWrap) - tText = "\n".join(aText) - else: - tText = self._centreText(tText,self.wordWrap) - else: - if self.wordWrap > 0 and tLen > self.wordWrap: - 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 += "%s\n\n" % " ".join(thisPar) - thisPar = [] - - elif tType == self.T_HEAD1: - uLine = "="*min(tLen,self.wordWrap) - if tAlign == self.A_CENTRE: - uLine = self._centreText(uLine,self.wordWrap) - self.theResult += "%s\n%s\n\n" % (tText,uLine) - - elif tType == self.T_HEAD2: - uLine = "~"*min(tLen,self.wordWrap) - self.theResult += "%s\n%s\n\n" % (tText,uLine) - - elif tType == self.T_HEAD3: - uLine = "-"*min(tLen,self.wordWrap) - self.theResult += "%s\n%s\n\n" % (tText,uLine) - - elif tType == self.T_HEAD4: - self.theResult += "%s\n\n" % tText - - elif tType == self.T_SEP: - if self.wordWrap > 0 and tLen < self.wordWrap: - tText = self._centreText(tText,self.wordWrap) - self.theResult += "%s\n\n" % tText - - elif tType == self.T_TEXT: - thisPar.append(tText) - - 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 - def windowsEndings(self): self.theResult = self.theResult.replace("\n","\r\n") return diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 68cba7ec..72c85ee4 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -17,9 +17,9 @@ from PyQt5.QtCore import Qt from PyQt5.QtWidgets import QTextBrowser from PyQt5.QtGui import QTextOption, QFont, QPalette, QColor -from nw.convert.tokenizer import Tokenizer -from nw.convert.tohtml import ToHtml -from nw.enum import nwItemType +from nw.convert.tokenizer import Tokenizer +from nw.convert.text.tohtml import ToHtml +from nw.enum import nwItemType logger = logging.getLogger(__name__) diff --git a/nw/gui/export.py b/nw/gui/export.py index 0fc82e6b..efcbca80 100644 --- a/nw/gui/export.py +++ b/nw/gui/export.py @@ -23,15 +23,15 @@ from PyQt5.QtWidgets import ( QLabel, QComboBox, QLineEdit, QPushButton, QFileDialog, QProgressBar, QSpinBox ) -from nw.project.document import NWDoc -from nw.tools.translate import numberToWord -from nw.tools.optlaststate import OptLastState -from nw.convert.textfile import TextFile -from nw.convert.htmlfile import HtmlFile -from nw.convert.markdownfile import MarkdownFile -from nw.convert.latexfile import LaTeXFile -from nw.constants import nwFiles -from nw.enum import nwItemType +from nw.project.document import NWDoc +from nw.tools.translate import numberToWord +from nw.tools.optlaststate import OptLastState +from nw.convert.file.text import TextFile +from nw.convert.file.html import HtmlFile +from nw.convert.file.markdown import MarkdownFile +from nw.convert.file.latex import LaTeXFile +from nw.constants import nwFiles +from nw.enum import nwItemType logger = logging.getLogger(__name__)