From 489eae2e0d2deeb03f19d76254e21d22adcb6ee3 Mon Sep 17 00:00:00 2001 From: "Veronica K. B. Olsen" <1619840+vkbo@users.noreply.github.com> Date: Tue, 29 Oct 2019 14:49:11 +0100 Subject: [PATCH] Adding non-breaking space support for editor and html, latex export --- nw/constants.py | 68 +++++++++++++++++++++----------------- nw/convert/file/html.py | 25 +++++++------- nw/convert/file/text.py | 1 + nw/convert/text/tohtml.py | 15 +++++---- nw/convert/text/tolatex.py | 15 +++++++++ nw/convert/text/totext.py | 12 +++++++ nw/convert/tokenizer.py | 3 ++ nw/gui/doceditor.py | 15 ++++++--- nw/gui/docviewer.py | 1 + nw/project/document.py | 3 +- 10 files changed, 104 insertions(+), 54 deletions(-) diff --git a/nw/constants.py b/nw/constants.py index e06942c8..92c693dc 100644 --- a/nw/constants.py +++ b/nw/constants.py @@ -149,7 +149,9 @@ class nwUnicode: """Suppoted unicode character constants and translation maps. """ - # Quotation Marks + # Unicode Constants + + ## Quotation Marks U_QUOT = "\u0022" # Quotation mark U_APOS = "\u0027" # Apostrophe U_LAQUO = "\u00ab" # Left-pointing double angle quotation mark @@ -170,38 +172,44 @@ class nwUnicode: U_LWCQUO = "\u300e" # Left white corner bracket U_RECQUO = "\u300f" # Right white corner bracket - # Punctuation - U_NDASH = "\u2013" # Short dash - U_MDASH = "\u2014" # Long dash + ## Punctuation + U_ENDASH = "\u2013" # Short dash + U_EMDASH = "\u2014" # Long dash U_HELLIP = "\u2026" # Ellipsis - # Other + ## Other U_NBSP = "\u00a0" # Non-breaking space + U_PARA = "\u2029" # Paragraph separator - HTML = { - U_QUOT : """, - U_APOS : "'", - U_LAQUO : "«", - U_RAQUO : "»", - U_LSQUO : "‘", - U_RSQUO : "’", - U_SBQUO : "‚", - U_SUQUO : "‛", - U_LDQUO : "“", - U_RDQUO : "”", - U_BDQUO : "„", - U_UDQUO : "‟", - U_LSAQUO : "‹", - U_RSAQUO : "›", - U_BDRQUO : "⹂", - U_LCQUO : "「", - U_RCQUO : "」", - U_LWCQUO : "『", - U_LWCQUO : "『", - U_NDASH : "–", - U_MDASH : "—", - U_HELLIP : "…", - U_NBSP : " ", - } + # HTML Equivalents + + ## Quotes + H_QUOT = """ + H_APOS = "'" + H_LAQUO = "«" + H_RAQUO = "»" + H_LSQUO = "‘" + H_RSQUO = "’" + H_SBQUO = "‚" + H_SUQUO = "‛" + H_LDQUO = "“" + H_RDQUO = "”" + H_BDQUO = "„" + H_UDQUO = "‟" + H_LSAQUO = "‹" + H_RSAQUO = "›" + H_BDRQUO = "⹂" + H_LCQUO = "「" + H_RCQUO = "」" + H_LWCQUO = "『" + H_LWCQUO = "『" + + ## Punctuation + H_ENDASH = "–" + H_EMDASH = "—" + H_HELLIP = "…" + + ## Other + H_NBSP = " " # END Class nwUnicode diff --git a/nw/convert/file/html.py b/nw/convert/file/html.py index 278acab7..1dc89b39 100644 --- a/nw/convert/file/html.py +++ b/nw/convert/file/html.py @@ -38,18 +38,19 @@ class HtmlFile(TextFile): self.outFile.write("\n") self.outFile.write("\n") self.outFile.write("\n") - self.outFile.write("\n") + self.outFile.write(" \n") + self.outFile.write(" \n") self.outFile.write("\n") self.outFile.write("\n") self.outFile.write("
\n") diff --git a/nw/convert/file/text.py b/nw/convert/file/text.py index 9c490b3d..9301a823 100644 --- a/nw/convert/file/text.py +++ b/nw/convert/file/text.py @@ -138,6 +138,7 @@ class TextFile(): self.theConv.tokenizeText() self.theConv.doHeaders() self.theConv.doConvert() + self.theConv.doPostProcessing() if self.theConv.theResult is not None and self.outFile is not None: self.outFile.write(self.theConv.theResult) diff --git a/nw/convert/text/tohtml.py b/nw/convert/text/tohtml.py index 0c086a68..64ddbbb7 100644 --- a/nw/convert/text/tohtml.py +++ b/nw/convert/text/tohtml.py @@ -15,6 +15,7 @@ import re import nw from nw.convert.tokenizer import Tokenizer +from nw.constants import nwUnicode logger = logging.getLogger(__name__) @@ -28,13 +29,13 @@ class ToHtml(Tokenizer): Tokenizer.doAutoReplace(self) repDict = { - "<" : "<", - ">" : ">", - "&" : "&", - "\u2013" : "&endash;", - "\u2014" : "$emdash;", - "\u2500" : "$emdash;", - "\u2026" : "…", + "<" : "<", + ">" : ">", + "&" : "&", + nwUnicode.U_ENDASH : nwUnicode.H_ENDASH, + nwUnicode.U_EMDASH : nwUnicode.H_EMDASH, + nwUnicode.U_HELLIP : nwUnicode.H_HELLIP, + nwUnicode.U_NBSP : nwUnicode.H_NBSP, } 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) diff --git a/nw/convert/text/tolatex.py b/nw/convert/text/tolatex.py index cfcec012..435fee84 100644 --- a/nw/convert/text/tolatex.py +++ b/nw/convert/text/tolatex.py @@ -16,6 +16,7 @@ import re import nw from nw.convert.tokenizer import Tokenizer +from nw.constants import nwUnicode logger = logging.getLogger(__name__) @@ -26,6 +27,20 @@ class ToLaTeX(Tokenizer): self.texCodecFail = False return + def doPostProcessing(self): + """The latexcodec misses dashes and non-breaking spaces, so we do those here. + """ + + repDict = { + nwUnicode.U_ENDASH : "--", + nwUnicode.U_EMDASH : "---", + nwUnicode.U_NBSP : "~", + } + xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL) + self.theResult = xRep.sub(lambda x: repDict[x.group(0)], self.theResult) + + return + def doConvert(self): texTags = { diff --git a/nw/convert/text/totext.py b/nw/convert/text/totext.py index fc21163e..5b226999 100644 --- a/nw/convert/text/totext.py +++ b/nw/convert/text/totext.py @@ -16,6 +16,7 @@ import re import nw from nw.convert.tokenizer import Tokenizer +from nw.constants import nwUnicode logger = logging.getLogger(__name__) @@ -25,6 +26,17 @@ class ToText(Tokenizer): Tokenizer.__init__(self, theProject, theParent) return + def doAutoReplace(self): + Tokenizer.doAutoReplace(self) + + repDict = { + nwUnicode.U_NBSP : " ", + } + 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): """Converts the tokenized text into plain text. """ diff --git a/nw/convert/tokenizer.py b/nw/convert/tokenizer.py index 63abc1ae..7be25952 100644 --- a/nw/convert/tokenizer.py +++ b/nw/convert/tokenizer.py @@ -149,6 +149,9 @@ class Tokenizer(): self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText) return + def doPostProcessing(self): + return + def tokenizeText(self): """Scan the text for either lines starting with specific characters that indicate headers, comments, commands etc, or just contains plain text. in the case of plain text, apply the diff --git a/nw/gui/doceditor.py b/nw/gui/doceditor.py index e2ca9237..1d0c35ac 100644 --- a/nw/gui/doceditor.py +++ b/nw/gui/doceditor.py @@ -218,7 +218,14 @@ class GuiDocEditor(QTextEdit): return self.docChanged def getText(self): - theText = self.toPlainText() + """Get the text content of the current document. This method uses QTextEdit->toPlainText for + Qt versions lower than 5.9, and the QDocument->toRawText for higher version. The latter + preserves non-breaking spaces, which the former does not. + """ + if self.mainConf.verQtValue >= 50900: + theText = self.qDocument.toRawText().replace(nwUnicode.U_PARA,"\n") + else: + theText = self.toPlainText() return theText def setCursorPosition(self, thePosition): @@ -466,11 +473,11 @@ class GuiDocEditor(QTextEdit): elif self.mainConf.doReplaceDash and theTwo == "--": theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2) - theCursor.insertText(nwUnicode.U_NDASH) + theCursor.insertText(nwUnicode.U_ENDASH) - elif self.mainConf.doReplaceDash and theTwo == nwUnicode.U_NDASH+"-": + elif self.mainConf.doReplaceDash and theTwo == nwUnicode.U_ENDASH+"-": theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 2) - theCursor.insertText(nwUnicode.U_MDASH) + theCursor.insertText(nwUnicode.U_EMDASH) elif self.mainConf.doReplaceDots and theThree == "...": theCursor.movePosition(QTextCursor.Left, QTextCursor.KeepAnchor, 3) diff --git a/nw/gui/docviewer.py b/nw/gui/docviewer.py index 72c85ee4..76c57e67 100644 --- a/nw/gui/docviewer.py +++ b/nw/gui/docviewer.py @@ -106,6 +106,7 @@ class GuiDocViewer(QTextBrowser): aDoc.doAutoReplace() aDoc.tokenizeText() aDoc.doConvert() + aDoc.doPostProcessing() self.setHtml(aDoc.theResult) if self.theHandle == tHandle: self.verticalScrollBar().setValue(sPos) diff --git a/nw/project/document.py b/nw/project/document.py index 7f5b550a..923f5c9e 100644 --- a/nw/project/document.py +++ b/nw/project/document.py @@ -100,7 +100,8 @@ class NWDoc(): self.makeAlert(["Could not save document.",str(e)], nwAlert.ERROR) return False - if path.isfile(docTemp): unlink(docTemp) + if path.isfile(docTemp): + unlink(docTemp) self.theParent.statusBar.setStatus("Saved Document: %s" % self.theItem.itemName)