Refactor core and base classes (#931)

* Improve the NWIndex class
* Improve the NWItem and NWDoc classes
* Make some optimisations in projects and update tests
* Minor changes to string formatting in main source file
* Add some more protection to core classes
* Make all converter class attributes private
This commit is contained in:
Veronica Berglyd Olsen
2021-12-18 16:03:45 +01:00
committed by GitHub
parent 2f75d88694
commit 5e2ab4f612
20 changed files with 889 additions and 866 deletions
+7 -11
View File
@@ -110,7 +110,7 @@ CONFIG = Config()
def main(sysArgs=None): def main(sysArgs=None):
"""Parse command line, set up logging, and launches main GUI. """Parse command line, set up logging, and launch main GUI.
""" """
if sysArgs is None: if sysArgs is None:
sysArgs = sys.argv[1:] sysArgs = sys.argv[1:]
@@ -130,8 +130,8 @@ def main(sysArgs=None):
] ]
helpMsg = ( helpMsg = (
"novelWriter {version} ({date})\n" f"novelWriter {__version__} ({__date__})\n"
"{copyright}\n" f"{__copyright__}\n"
"\n" "\n"
"This program is distributed in the hope that it will be useful,\n" "This program is distributed in the hope that it will be useful,\n"
"but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n"
@@ -147,10 +147,6 @@ def main(sysArgs=None):
" --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n" " --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n"
" --config= Alternative config file.\n" " --config= Alternative config file.\n"
" --data= Alternative user data path.\n" " --data= Alternative user data path.\n"
).format(
version=__version__,
copyright=__copyright__,
date=__date__,
) )
# Defaults # Defaults
@@ -165,9 +161,9 @@ def main(sysArgs=None):
# Parse Options # Parse Options
try: try:
inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt) inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt)
except getopt.GetoptError as E: except getopt.GetoptError as exc:
print(helpMsg) print(helpMsg)
print("ERROR: %s" % str(E)) print(f"ERROR: {str(exc)}")
sys.exit(2) sys.exit(2)
if len(inRemain) > 0: if len(inRemain) > 0:
@@ -268,7 +264,7 @@ def main(sysArgs=None):
elif CONFIG.osWindows: elif CONFIG.osWindows:
try: try:
import ctypes import ctypes
appID = "io.novelwriter.%s" % __version__ appID = f"io.novelwriter.{__version__}"
ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appID) ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appID)
except Exception: except Exception:
logger.error("Failed to set application name") logger.error("Failed to set application name")
@@ -281,7 +277,7 @@ def main(sysArgs=None):
return nwGUI return nwGUI
else: else:
nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)]) nwApp = QApplication([CONFIG.appName, (f"-style={qtStyle}")])
nwApp.setApplicationName(CONFIG.appName) nwApp.setApplicationName(CONFIG.appName)
nwApp.setApplicationVersion(__version__) nwApp.setApplicationVersion(__version__)
nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) nwApp.setWindowIcon(QIcon(CONFIG.appIcon))
+1 -1
View File
@@ -31,8 +31,8 @@ import logging
from datetime import datetime from datetime import datetime
from configparser import ConfigParser from configparser import ConfigParser
from PyQt5.QtWidgets import qApp
from PyQt5.QtCore import QCoreApplication from PyQt5.QtCore import QCoreApplication
from PyQt5.QtWidgets import qApp
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
from novelwriter.error import logException from novelwriter.error import logException
+23 -20
View File
@@ -56,15 +56,21 @@ class NWDoc():
return return
def __repr__(self):
return f"<NWDoc handle={self._docHandle}>"
def __bool__(self):
return self._docHandle is not None and bool(self._theItem)
## ##
# Class Methods # Class Methods
## ##
def readDocument(self, isOrphan=False): def readDocument(self, isOrphan=False):
"""Read a document from set handle, capturing potential file """Read the document specified by the handle set in the
system errors and parse meta data. If the document doesn't exist contructor, capturing potential file system errors and parse
on disk, return an empty string. If something went wrong, return meta data. If the document doesn't exist on disk, return an
None. empty string. If something went wrong, return None.
""" """
self._docError = "" self._docError = ""
if self._docHandle is None: if self._docHandle is None:
@@ -88,7 +94,6 @@ class NWDoc():
if os.path.isfile(docPath): if os.path.isfile(docPath):
try: try:
with open(docPath, mode="r", encoding="utf-8") as inFile: with open(docPath, mode="r", encoding="utf-8") as inFile:
# Check the first <= 10 lines for metadata # Check the first <= 10 lines for metadata
for i in range(10): for i in range(10):
inLine = inFile.readline() inLine = inFile.readline()
@@ -108,14 +113,15 @@ class NWDoc():
else: else:
# The document file does not exist, so we assume it's a new # The document file does not exist, so we assume it's a new
# document and initialise an empty text string. # document and initialise an empty text string.
logger.debug("The requested document does not exist.") logger.debug("The requested document does not exist")
return "" return ""
return theText return theText
def writeDocument(self, docText, forceWrite=False): def writeDocument(self, docText, forceWrite=False):
"""Write the document. The file is saved via a temp file in case """Write the document specified by the handle attribute. Handle
of save failure. Returns True if successful, False if not. any IO errors in the process Returns True if successful, False
if not.
""" """
self._docError = "" self._docError = ""
if self._docHandle is None: if self._docHandle is None:
@@ -156,9 +162,7 @@ class NWDoc():
# If we're here, the file was successfully saved, so we can # If we're here, the file was successfully saved, so we can
# replace the temp file with the actual file # replace the temp file with the actual file
if os.path.isfile(docPath): os.replace(docTemp, docPath)
os.unlink(docPath)
os.rename(docTemp, docPath)
self._prevHash = sha256sum(docPath) self._prevHash = sha256sum(docPath)
self._currHash = self._prevHash self._currHash = self._prevHash
@@ -174,11 +178,10 @@ class NWDoc():
logger.error("No document handle set") logger.error("No document handle set")
return False return False
docFile = self._docHandle+".nwd" chkList = [
os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd"),
chkList = [] os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd~"),
chkList.append(os.path.join(self.theProject.projContent, docFile)) ]
chkList.append(os.path.join(self.theProject.projContent, docFile+"~"))
for chkFile in chkList: for chkFile in chkList:
if os.path.isfile(chkFile): if os.path.isfile(chkFile):
@@ -196,18 +199,18 @@ class NWDoc():
## ##
def getFileLocation(self): def getFileLocation(self):
"""Return the file location of the current file. """Return the file location of the current document.
""" """
return self._fileLoc return self._fileLoc
def getCurrentItem(self): def getCurrentItem(self):
"""Return a pointer to the currently open item. """Return a pointer to the currently open NWItem.
""" """
return self._theItem return self._theItem
def getMeta(self): def getMeta(self):
"""Parses the document meta tag and returns the path and name as """Parse the document meta tag and return the name, parent,
a list and a string. class and layout meta values.
""" """
theName = self._docMeta.get("name", "") theName = self._docMeta.get("name", "")
theParent = self._docMeta.get("parent", None) theParent = self._docMeta.get("parent", None)
+45 -74
View File
@@ -27,7 +27,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os import os
import json import json
import logging import logging
import novelwriter
from time import time from time import time
@@ -49,10 +48,10 @@ class NWIndex():
def __init__(self, theProject): def __init__(self, theProject):
self.theProject = theProject
# Internal # Internal
self.mainConf = novelwriter.CONFIG self._indexBroken = False
self.theProject = theProject
self.indexBroken = False
# Indices # Indices
self._tagIndex = {} self._tagIndex = {}
@@ -67,6 +66,10 @@ class NWIndex():
return return
@property
def indexBroken(self):
return self._indexBroken
## ##
# Public Methods # Public Methods
## ##
@@ -88,11 +91,7 @@ class NWIndex():
""" """
logger.debug("Removing item '%s' from the index", tHandle) logger.debug("Removing item '%s' from the index", tHandle)
delTags = [] delTags = list(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex))
for tTag in self._tagIndex:
if self._tagIndex[tTag][1] == tHandle:
delTags.append(tTag)
for tTag in delTags: for tTag in delTags:
self._tagIndex.pop(tTag, None) self._tagIndex.pop(tTag, None)
@@ -157,7 +156,7 @@ class NWIndex():
except Exception: except Exception:
logger.error("Failed to load index file") logger.error("Failed to load index file")
logException() logException()
self.indexBroken = True self._indexBroken = True
return False return False
self._tagIndex = theData.get("tagIndex", {}) self._tagIndex = theData.get("tagIndex", {})
@@ -214,17 +213,17 @@ class NWIndex():
self._checkRefIndex() self._checkRefIndex()
self._checkFileIndex() self._checkFileIndex()
self._checkFileMeta() self._checkFileMeta()
self.indexBroken = False self._indexBroken = False
except Exception: except Exception:
logger.error("Error while checking index") logger.error("Error while checking index")
logException() logException()
self.indexBroken = True self._indexBroken = True
logger.verbose("Index check took %.3f ms", (time() - tStart)*1000) logger.verbose("Index check took %.3f ms", (time() - tStart)*1000)
logger.debug("Index check complete") logger.debug("Index check complete")
if self.indexBroken: if self._indexBroken:
self.clearIndex() self.clearIndex()
return return
@@ -237,7 +236,8 @@ class NWIndex():
"""Scan a piece of text associated with a handle. This will """Scan a piece of text associated with a handle. This will
update the indices accordingly. This function takes the handle update the indices accordingly. This function takes the handle
and text as separate inputs as we want to primarily scan the and text as separate inputs as we want to primarily scan the
files before we save them, unless we're rebuilding the index. files before we save them in which case we already have the
text.
""" """
theItem = self.theProject.projTree[tHandle] theItem = self.theProject.projTree[tHandle]
theRoot = self.theProject.projTree.getRootItem(tHandle) theRoot = self.theProject.projTree.getRootItem(tHandle)
@@ -259,7 +259,7 @@ class NWIndex():
cC, wC, pC = countWords(theText) cC, wC, pC = countWords(theText)
self._fileMeta[tHandle] = ["H0", cC, wC, pC] self._fileMeta[tHandle] = ["H0", cC, wC, pC]
# If the file is archived or trashed, we don't index the file itself # If the file is archived or in trash, we don't index the content
if self.theProject.projTree.isTrashRoot(theItem.itemParent): if self.theProject.projTree.isTrashRoot(theItem.itemParent):
logger.debug("Not indexing trash item '%s'", tHandle) logger.debug("Not indexing trash item '%s'", tHandle)
return False return False
@@ -276,16 +276,12 @@ class NWIndex():
self._refIndex.pop(tHandle, None) self._refIndex.pop(tHandle, None)
self._fileIndex[tHandle] = {} self._fileIndex[tHandle] = {}
# Also clear references to file in tag index # Also clear references to the file in the tags index
clearTags = [] clearTags = list(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex))
for aTag in self._tagIndex:
if self._tagIndex[aTag][1] == tHandle:
clearTags.append(aTag)
for aTag in clearTags: for aTag in clearTags:
self._tagIndex.pop(aTag) self._tagIndex.pop(aTag)
# Scan the text content # Scan the text content
nLine = 0
nTitle = 0 nTitle = 0
theLines = theText.splitlines() theLines = theText.splitlines()
for nLine, aLine in enumerate(theLines, start=1): for nLine, aLine in enumerate(theLines, start=1):
@@ -355,7 +351,7 @@ class NWIndex():
hText = aLine[5:].strip() hText = aLine[5:].strip()
elif aLine.startswith("#! "): elif aLine.startswith("#! "):
hDepth = "H1" hDepth = "H1"
hText = aLine[2:].strip() hText = aLine[3:].strip()
elif aLine.startswith("##! "): elif aLine.startswith("##! "):
hDepth = "H2" hDepth = "H2"
hText = aLine[4:].strip() hText = aLine[4:].strip()
@@ -374,6 +370,8 @@ class NWIndex():
} }
if self._fileMeta[tHandle][0] == "H0": if self._fileMeta[tHandle][0] == "H0":
# Since this initialises to H0, this ensures that only the
# first header level is recorded in the file meta index
self._fileMeta[tHandle][0] = hDepth self._fileMeta[tHandle][0] = hDepth
return True return True
@@ -542,8 +540,7 @@ class NWIndex():
hCount = [0, 0, 0, 0, 0] hCount = [0, 0, 0, 0, 0]
for tHandle in self._listNovelHandles(skipExcluded): for tHandle in self._listNovelHandles(skipExcluded):
for sTitle in self._fileIndex[tHandle]: for sTitle in self._fileIndex[tHandle]:
theData = self._fileIndex[tHandle][sTitle] iLevel = H_LEVEL.get(self._fileIndex[tHandle][sTitle]["level"], 0)
iLevel = H_LEVEL.get(theData["level"], 0)
hCount[iLevel] += 1 hCount[iLevel] += 1
return hCount return hCount
@@ -551,45 +548,29 @@ class NWIndex():
def getHandleWordCounts(self, tHandle): def getHandleWordCounts(self, tHandle):
"""Get all header word counts for a specific handle. """Get all header word counts for a specific handle.
""" """
theCounts = [] hRecord = self._fileIndex.get(tHandle, {})
hRecord = self._fileIndex.get(tHandle, None) return [(f"{tHandle}:{sTitle}", sData["wCount"]) for sTitle, sData in hRecord.items()]
if hRecord is None:
return theCounts
for sTitle, sData in hRecord.items():
theCounts.append((f"{tHandle}:{sTitle}", sData["wCount"]))
return theCounts
def getHandleHeaders(self, tHandle): def getHandleHeaders(self, tHandle):
"""Get all headers for a specific handle. """Get all headers for a specific handle.
""" """
theHeaders = [] hRecord = self._fileIndex.get(tHandle, {})
hRecord = self._fileIndex.get(tHandle, None) return [(sTitle, sData["level"], sData["title"]) for sTitle, sData in hRecord.items()]
if hRecord is None:
return theHeaders
for sTitle, sData in hRecord.items():
theHeaders.append((sTitle, sData["level"], sData["title"]))
return theHeaders
def getHandleHeaderLevel(self, tHandle): def getHandleHeaderLevel(self, tHandle):
"""Get the header level of the first header of a handle. """Get the header level of the first header of a handle.
""" """
if tHandle in self._fileMeta: return self._fileMeta.get(tHandle, ["H0"])[0]
return self._fileMeta[tHandle][0]
return "H0"
def getTableOfContents(self, maxDepth, skipExcluded=True): def getTableOfContents(self, maxDepth, skipExcluded=True):
"""Generate a table of contents up to a maxiumum depth. """Generate a table of contents up to a maximum depth.
""" """
tOrder = [] tOrder = []
tData = {} tData = {}
pKey = None pKey = None
for tHandle in self._listNovelHandles(skipExcluded): for tHandle in self._listNovelHandles(skipExcluded):
for sTitle in sorted(self._fileIndex[tHandle]): for sTitle in sorted(self._fileIndex[tHandle]):
tKey = "%s:%s" % (tHandle, sTitle) tKey = f"{tHandle}:{sTitle}"
theData = self._fileIndex[tHandle][sTitle] theData = self._fileIndex[tHandle][sTitle]
iLevel = H_LEVEL.get(theData["level"], 0) iLevel = H_LEVEL.get(theData["level"], 0)
if iLevel > maxDepth: if iLevel > maxDepth:
@@ -605,19 +586,17 @@ class NWIndex():
"words": theData["wCount"], "words": theData["wCount"],
} }
theToC = [] theToC = [(
for tKey in tOrder: tKey,
theToC.append(( tData[tKey]["level"],
tKey, tData[tKey]["title"],
tData[tKey]["level"], tData[tKey]["words"]
tData[tKey]["title"], ) for tKey in tOrder]
tData[tKey]["words"],
))
return theToC return theToC
def getCounts(self, tHandle, sTitle=None): def getCounts(self, tHandle, sTitle=None):
"""Returns the counts for a file, or a section of a file """Return the counts for a file, or a section of a file,
starting at title sTitle if it is provided. starting at title sTitle if it is provided.
""" """
cC = 0 cC = 0
@@ -640,12 +619,9 @@ class NWIndex():
def getReferences(self, tHandle, sTitle=None): def getReferences(self, tHandle, sTitle=None):
"""Extract all references made in a file, and optionally title """Extract all references made in a file, and optionally title
section. sTitle must be a string. section.
""" """
theRefs = {} theRefs = {x: [] for x in nwKeyWords.KEY_CLASS}
for tKey in nwKeyWords.KEY_CLASS:
theRefs[tKey] = []
if tHandle not in self._refIndex: if tHandle not in self._refIndex:
return theRefs return theRefs
@@ -669,15 +645,11 @@ class NWIndex():
"""Build a list of files referring back to our file, specified """Build a list of files referring back to our file, specified
by tHandle. by tHandle.
""" """
theRefs = {}
if tHandle is None: if tHandle is None:
return theRefs return {}
theTags = set()
for tTag in self._tagIndex:
if tHandle == self._tagIndex[tTag][1]:
theTags.add(tTag)
theRefs = {}
theTags = set(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex))
if theTags: if theTags:
for tHandle in self._refIndex: for tHandle in self._refIndex:
for sTitle in self._refIndex[tHandle]: for sTitle in self._refIndex[tHandle]:
@@ -690,10 +662,9 @@ class NWIndex():
def getTagSource(self, theTag): def getTagSource(self, theTag):
"""Return the source location of a given tag. """Return the source location of a given tag.
""" """
if theTag in self._tagIndex: theRef = self._tagIndex.get(theTag, [])
theRef = self._tagIndex[theTag] if len(theRef) == 4:
if len(theRef) == 4: return theRef[1], theRef[0], theRef[3]
return theRef[1], theRef[0], theRef[3]
return None, 0, "T000000" return None, 0, "T000000"
## ##
@@ -859,9 +830,9 @@ def countWords(theText):
return charCount, wordCount, paraCount return charCount, wordCount, paraCount
# We need to treat dashes as word separators for counting words. # We need to treat dashes as word separators for counting words.
# The check+replace apprach is much faster that direct replace for # The check+replace approach is much faster than direct replace for
# large texts, and a bit slower for small texts, but in the latter # large texts, and a bit slower for small texts, but in the latter
# case it doesn't matter. # case it doesn't really matter.
if nwUnicode.U_ENDASH in theText: if nwUnicode.U_ENDASH in theText:
theText = theText.replace(nwUnicode.U_ENDASH, " ") theText = theText.replace(nwUnicode.U_ENDASH, " ")
if nwUnicode.U_EMDASH in theText: if nwUnicode.U_EMDASH in theText:
+17 -16
View File
@@ -67,7 +67,7 @@ class NWItem():
## ##
def packXML(self, xParent): def packXML(self, xParent):
"""Packs all the data in the class instance into an XML object. """Pack all the data in the class instance into an XML object.
""" """
xPack = etree.SubElement(xParent, "item", attrib={ xPack = etree.SubElement(xParent, "item", attrib={
"handle": str(self.itemHandle), "handle": str(self.itemHandle),
@@ -91,7 +91,7 @@ class NWItem():
return return
def unpackXML(self, xItem): def unpackXML(self, xItem):
"""Sets the values from an XML entry of type 'item'. """Set the values from an XML entry of type 'item'.
""" """
if xItem.tag != "item": if xItem.tag != "item":
logger.error("XML entry is not an NWItem") logger.error("XML entry is not an NWItem")
@@ -133,7 +133,7 @@ class NWItem():
else: else:
# Sliently skip as we may otherwise cause orphaned # Sliently skip as we may otherwise cause orphaned
# items if an otherwise valid file is opened by a # items if an otherwise valid file is opened by a
# version of novelWriter that doesn't know the tag. # version of novelWriter that doesn't know the tag
logger.error("Unknown tag '%s'", xValue.tag) logger.error("Unknown tag '%s'", xValue.tag)
# Guarantees that <status> is parsed after <class> # Guarantees that <status> is parsed after <class>
@@ -143,7 +143,7 @@ class NWItem():
@staticmethod @staticmethod
def _subPack(xParent, name, attrib=None, text=None, none=True): def _subPack(xParent, name, attrib=None, text=None, none=True):
"""Packs the values into an xml element. """Pack the values into an XML element.
""" """
if not none and (text is None or text == "None"): if not none and (text is None or text == "None"):
return None return None
@@ -204,7 +204,7 @@ class NWItem():
return return
def setParent(self, theParent): def setParent(self, theParent):
"""Set the parent handle, and ensure that it is valid. """Set the parent handle, and ensure it is valid.
""" """
if theParent is None: if theParent is None:
self.itemParent = None self.itemParent = None
@@ -216,14 +216,15 @@ class NWItem():
def setOrder(self, theOrder): def setOrder(self, theOrder):
"""Set the item order, and ensure that it is valid. This value """Set the item order, and ensure that it is valid. This value
is purely a meta value, not actually used by novelWriter. is purely a meta value, and not actually used by novelWriter at
the moment.
""" """
self.itemOrder = checkInt(theOrder, 0) self.itemOrder = checkInt(theOrder, 0)
return return
def setType(self, theType): def setType(self, theType):
"""Set the item type from either a proper nwItemType, or set it """Set the item type from either a proper nwItemType, or set it
from a string representing a nwItemType. from a string representing an nwItemType.
""" """
if isinstance(theType, nwItemType): if isinstance(theType, nwItemType):
self.itemType = theType self.itemType = theType
@@ -236,7 +237,7 @@ class NWItem():
def setClass(self, theClass): def setClass(self, theClass):
"""Set the item class from either a proper nwItemClass, or set """Set the item class from either a proper nwItemClass, or set
it from a string representing a nwItemClass. it from a string representing an nwItemClass.
""" """
if isinstance(theClass, nwItemClass): if isinstance(theClass, nwItemClass):
self.itemClass = theClass self.itemClass = theClass
@@ -249,7 +250,7 @@ class NWItem():
def setLayout(self, theLayout): def setLayout(self, theLayout):
"""Set the item layout from either a proper nwItemLayout, or set """Set the item layout from either a proper nwItemLayout, or set
it from a string representing a nwItemLayout. it from a string representing an nwItemLayout.
""" """
if isinstance(theLayout, nwItemLayout): if isinstance(theLayout, nwItemLayout):
self.itemLayout = theLayout self.itemLayout = theLayout
@@ -273,7 +274,7 @@ class NWItem():
return return
def setExpanded(self, expState): def setExpanded(self, expState):
"""Save the expanded status of an item in the project tree. """Set the expanded status of an item in the project tree.
""" """
if isinstance(expState, str): if isinstance(expState, str):
self.isExpanded = (expState == str(True)) self.isExpanded = (expState == str(True))
@@ -282,7 +283,7 @@ class NWItem():
return return
def setExported(self, expState): def setExported(self, expState):
"""Save the export flag. """Set the export flag.
""" """
if isinstance(expState, str): if isinstance(expState, str):
self.isExported = (expState == str(True)) self.isExported = (expState == str(True))
@@ -297,29 +298,29 @@ class NWItem():
def setCharCount(self, theCount): def setCharCount(self, theCount):
"""Set the character count, and ensure that it is an integer. """Set the character count, and ensure that it is an integer.
""" """
self.charCount = checkInt(theCount, 0) self.charCount = max(0, checkInt(theCount, 0))
return return
def setWordCount(self, theCount): def setWordCount(self, theCount):
"""Set the word count, and ensure that it is an integer. """Set the word count, and ensure that it is an integer.
""" """
self.wordCount = checkInt(theCount, 0) self.wordCount = max(0, checkInt(theCount, 0))
return return
def setParaCount(self, theCount): def setParaCount(self, theCount):
"""Set the paragraph count, and ensure that it is an integer. """Set the paragraph count, and ensure that it is an integer.
""" """
self.paraCount = checkInt(theCount, 0) self.paraCount = max(0, checkInt(theCount, 0))
return return
def setCursorPos(self, thePosition): def setCursorPos(self, thePosition):
"""Set the cursor position, and ensure that it is an integer. """Set the cursor position, and ensure that it is an integer.
""" """
self.cursorPos = checkInt(thePosition, 0) self.cursorPos = max(0, checkInt(thePosition, 0))
return return
def saveInitialCount(self): def saveInitialCount(self):
"""Set the initial word count. """Save the initial word count.
""" """
self.initCount = self.wordCount self.initCount = self.wordCount
return return
+1 -1
View File
@@ -123,7 +123,7 @@ class OptionState():
## ##
def setValue(self, group, name, value): def setValue(self, group, name, value):
"""Saves a value, with a given group and name. """Save a value, with a given group and name.
""" """
if group not in VALID_MAP: if group not in VALID_MAP:
logger.error("Unknown option group '%s'", group) logger.error("Unknown option group '%s'", group)
+25 -22
View File
@@ -237,12 +237,13 @@ class NWProject():
return return
def newProject(self, projData=None): def newProject(self, projData):
"""Create a new project by populating the project tree with a """Create a new project by populating the project tree with a
few starter items. few starter items.
""" """
if projData is None: if not isinstance(projData, dict):
projData = {} logger.error("Invalid call to newProject function")
return False
popMinimal = projData.get("popMinimal", True) popMinimal = projData.get("popMinimal", True)
popCustom = projData.get("popCustom", False) popCustom = projData.get("popCustom", False)
@@ -473,8 +474,8 @@ class NWProject():
# 1.2 : Changes the way autoReplace entries are stored. The 1.1 # 1.2 : Changes the way autoReplace entries are stored. The 1.1
# parser will lose the autoReplace settings if allowed to # parser will lose the autoReplace settings if allowed to
# read the file. Introduced in version 0.10. # read the file. Introduced in version 0.10.
# 1.3 : Reduces the number of layouts to onlye two. One for # 1.3 : Reduces the number of layouts to only two. One for novel
# novel documents and one for project notes. Introduced in # documents and one for project notes. Introduced in
# version 1.5. # version 1.5.
if fileVersion not in ("1.0", "1.1", "1.2", "1.3"): if fileVersion not in ("1.0", "1.1", "1.2", "1.3"):
@@ -630,8 +631,8 @@ class NWProject():
def saveProject(self, autoSave=False): def saveProject(self, autoSave=False):
"""Save the project main XML file. The saving command itself """Save the project main XML file. The saving command itself
uses a temporary filename, and the file is renamed afterwards to uses a temporary filename, and the file is replaced afterwards
make sure if the save fails, we're not left with a truncated to make sure if the save fails, we're not left with a truncated
file. file.
""" """
if self.projPath is None: if self.projPath is None:
@@ -720,11 +721,15 @@ class NWProject():
# If we're here, the file was successfully saved, # If we're here, the file was successfully saved,
# so let's sort out the temps and backups # so let's sort out the temps and backups
if os.path.isfile(backFile): try:
os.unlink(backFile) if os.path.isfile(saveFile):
if os.path.isfile(saveFile): os.replace(saveFile, backFile)
os.rename(saveFile, backFile) os.replace(tempFile, saveFile)
os.rename(tempFile, saveFile) except Exception as exc:
self.theParent.makeAlert(self.tr(
"Failed to save project."
), nwAlert.ERROR, exception=exc)
return False
# Save project GUI options # Save project GUI options
self.optState.saveSettings() self.optState.saveSettings()
@@ -857,9 +862,9 @@ class NWProject():
def extractSampleProject(self, projData): def extractSampleProject(self, projData):
"""Make a copy of the sample project. """Make a copy of the sample project.
First, try to copy the content of the sample folder to the new First, look for the sample.zip file in the assets folder and
project path, or if the folder doesn't exist, look for the zip unpack it. If it doesn't exist, try to copy the content of the
file in the assets folder. sample folder to the new project path. If neither exits, error.
""" """
projPath = projData.get("projPath", None) projPath = projData.get("projPath", None)
if projPath is None: if projPath is None:
@@ -965,14 +970,14 @@ class NWProject():
return True return True
def setBookTitle(self, bookTitle): def setBookTitle(self, bookTitle):
"""Set the boom title, that is, the title to include in exports. """Set the book title, that is, the title to include in exports.
""" """
self.bookTitle = bookTitle.strip() self.bookTitle = bookTitle.strip()
self.setProjectChanged(True) self.setProjectChanged(True)
return True return True
def setBookAuthors(self, bookAuthors): def setBookAuthors(self, bookAuthors):
"""A line separated list of book authors, parsed into an array. """A line-separated list of authors, parsed into an array.
""" """
if not isinstance(bookAuthors, str): if not isinstance(bookAuthors, str):
return False return False
@@ -1097,8 +1102,7 @@ class NWProject():
return True return True
def setAutoReplace(self, autoReplace): def setAutoReplace(self, autoReplace):
"""Update the auto-replace dictionary. This replaces the entire """Update the auto-replace dictionary.
dictionary, so alterations have to be made in a copy.
""" """
self.autoReplace = autoReplace self.autoReplace = autoReplace
self.setProjectChanged(True) self.setProjectChanged(True)
@@ -1128,7 +1132,7 @@ class NWProject():
## ##
def getAuthors(self): def getAuthors(self):
"""Returns a formatted string of authors. """Return a formatted string of authors.
""" """
nAuth = len(self.bookAuthors) nAuth = len(self.bookAuthors)
authString = "" authString = ""
@@ -1225,8 +1229,7 @@ class NWProject():
return it. The variable is cast to a string before lookup. If return it. The variable is cast to a string before lookup. If
the word does not exist, it returns itself. the word does not exist, it returns itself.
""" """
theValue = str(theWord) return self.langData.get(str(theWord), str(theWord))
return self.langData.get(theValue, theValue)
## ##
# Internal Functions # Internal Functions
-3
View File
@@ -25,7 +25,6 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
import os import os
import logging import logging
import novelwriter
from novelwriter.error import logException from novelwriter.error import logException
@@ -36,8 +35,6 @@ class NWSpellEnchant():
def __init__(self): def __init__(self):
self.mainConf = novelwriter.CONFIG
self._theDict = None self._theDict = None
self._projDict = set() self._projDict = set()
self._projectDict = None self._projectDict = None
+49 -41
View File
@@ -40,9 +40,9 @@ class ToHtml(Tokenizer):
def __init__(self, theProject): def __init__(self, theProject):
Tokenizer.__init__(self, theProject) Tokenizer.__init__(self, theProject)
self.genMode = self.M_EXPORT self._genMode = self.M_EXPORT
self.cssStyles = True self._cssStyles = True
self.fullHTML = [] self._fullHTML = []
# Internals # Internals
self._trMap = {} self._trMap = {}
@@ -50,6 +50,14 @@ class ToHtml(Tokenizer):
return return
##
# Properties
##
@property
def fullHTML(self):
return self._fullHTML
## ##
# Setters # Setters
## ##
@@ -59,17 +67,17 @@ class ToHtml(Tokenizer):
need to make a few changes to formatting, which is managed by need to make a few changes to formatting, which is managed by
these flags. these flags.
""" """
self.genMode = self.M_PREVIEW self._genMode = self.M_PREVIEW
self.doKeywords = True self._doKeywords = True
self.doComments = doComments self._doComments = doComments
self.doSynopsis = doSynopsis self._doSynopsis = doSynopsis
return return
def setStyles(self, cssStyles): def setStyles(self, cssStyles):
"""Enable/disable CSS styling. Some elements may still have """Enable/disable CSS styling. Some elements may still have
class tags. class tags.
""" """
self.cssStyles = cssStyles self._cssStyles = cssStyles
return return
def setReplaceUnicode(self, doReplace): def setReplaceUnicode(self, doReplace):
@@ -93,21 +101,21 @@ class ToHtml(Tokenizer):
def getFullResultSize(self): def getFullResultSize(self):
"""Return the size of the full HTML result. """Return the size of the full HTML result.
""" """
return sum([len(x) for x in self.fullHTML]) return sum([len(x) for x in self._fullHTML])
def doPreProcessing(self): def doPreProcessing(self):
"""Extend the auto-replace to also properly encode some unicode """Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities. characters into their respective HTML entities.
""" """
Tokenizer.doPreProcessing(self) Tokenizer.doPreProcessing(self)
self.theText = self.theText.translate(self._trMap) self._theText = self._theText.translate(self._trMap)
return return
def doConvert(self): def doConvert(self):
"""Convert the list of text tokens into a HTML document saved """Convert the list of text tokens into a HTML document saved
to theResult. to theResult.
""" """
if self.genMode == self.M_PREVIEW: if self._genMode == self.M_PREVIEW:
htmlTags = { # HTML4 + CSS2 (for Qt) htmlTags = { # HTML4 + CSS2 (for Qt)
self.FMT_B_B: "<b>", self.FMT_B_B: "<b>",
self.FMT_B_E: "</b>", self.FMT_B_E: "</b>",
@@ -126,7 +134,7 @@ class ToHtml(Tokenizer):
self.FMT_D_E: "</del>", self.FMT_D_E: "</del>",
} }
if self.isNovel and self.genMode != self.M_PREVIEW: if self._isNovel and self._genMode != self.M_PREVIEW:
# For story files, we bump the titles one level up # For story files, we bump the titles one level up
h1Cl = " class='title'" h1Cl = " class='title'"
h1 = "h1" h1 = "h1"
@@ -140,13 +148,13 @@ class ToHtml(Tokenizer):
h3 = "h3" h3 = "h3"
h4 = "h4" h4 = "h4"
self.theResult = "" self._theResult = ""
thisPar = [] thisPar = []
parStyle = None parStyle = None
tmpResult = [] tmpResult = []
for tType, tLine, tDirty, tFormat, tStyle in self.theTokens: for tType, tLine, tDirty, tFormat, tStyle in self._theTokens:
# Replace < and > and recompute formatting positions # Replace < and > and recompute formatting positions
cText = [] cText = []
@@ -168,7 +176,7 @@ class ToHtml(Tokenizer):
# Styles # Styles
aStyle = [] aStyle = []
if tStyle is not None and self.cssStyles: if tStyle is not None and self._cssStyles:
if tStyle & self.A_LEFT: if tStyle & self.A_LEFT:
aStyle.append("text-align: left;") aStyle.append("text-align: left;")
elif tStyle & self.A_RIGHT: elif tStyle & self.A_RIGHT:
@@ -200,7 +208,7 @@ class ToHtml(Tokenizer):
else: else:
hStyle = "" hStyle = ""
if self.linkHeaders: if self._linkHeaders:
aNm = f"<a name='T{tLine:06d}'></a>" aNm = f"<a name='T{tLine:06d}'></a>"
else: else:
aNm = "" aNm = ""
@@ -209,7 +217,7 @@ class ToHtml(Tokenizer):
if tType == self.T_EMPTY: if tType == self.T_EMPTY:
if parStyle is None: if parStyle is None:
parStyle = "" parStyle = ""
if len(thisPar) > 1 and self.cssStyles: if len(thisPar) > 1 and self._cssStyles:
parClass = " class='break'" parClass = " class='break'"
else: else:
parClass = "" parClass = ""
@@ -257,21 +265,21 @@ class ToHtml(Tokenizer):
tTemp = tTemp[:xPos] + htmlTags[xFmt] + tTemp[xPos+xLen:] tTemp = tTemp[:xPos] + htmlTags[xFmt] + tTemp[xPos+xLen:]
thisPar.append(tTemp.rstrip()) thisPar.append(tTemp.rstrip())
elif tType == self.T_SYNOPSIS and self.doSynopsis: elif tType == self.T_SYNOPSIS and self._doSynopsis:
tmpResult.append(self._formatSynopsis(tText)) tmpResult.append(self._formatSynopsis(tText))
elif tType == self.T_COMMENT and self.doComments: elif tType == self.T_COMMENT and self._doComments:
tmpResult.append(self._formatComments(tText)) tmpResult.append(self._formatComments(tText))
elif tType == self.T_KEYWORD and self.doKeywords: elif tType == self.T_KEYWORD and self._doKeywords:
tTemp = f"<p{hStyle}>{self._formatKeywords(tText)}</p>\n" tTemp = f"<p{hStyle}>{self._formatKeywords(tText)}</p>\n"
tmpResult.append(tTemp) tmpResult.append(tTemp)
self.theResult = "".join(tmpResult) self._theResult = "".join(tmpResult)
tmpResult = [] tmpResult = []
if self.genMode != self.M_PREVIEW: if self._genMode != self.M_PREVIEW:
self.fullHTML.append(self.theResult) self._fullHTML.append(self._theResult)
return return
@@ -281,7 +289,7 @@ class ToHtml(Tokenizer):
with open(savePath, mode="w", encoding="utf-8") as outFile: with open(savePath, mode="w", encoding="utf-8") as outFile:
theStyle = self.getStyleSheet() theStyle = self.getStyleSheet()
theStyle.append("article {width: 800px; margin: 40px auto;}") theStyle.append("article {width: 800px; margin: 40px auto;}")
bodyText = "".join(self.fullHTML) bodyText = "".join(self._fullHTML)
bodyText = bodyText.replace("\t", "&#09;").rstrip() bodyText = bodyText.replace("\t", "&#09;").rstrip()
theHtml = ( theHtml = (
@@ -314,24 +322,24 @@ class ToHtml(Tokenizer):
""" """
htmlText = [] htmlText = []
tabSpace = spaceChar*nSpaces tabSpace = spaceChar*nSpaces
for aLine in self.fullHTML: for aLine in self._fullHTML:
htmlText.append(aLine.replace("\t", tabSpace)) htmlText.append(aLine.replace("\t", tabSpace))
self.fullHTML = htmlText self._fullHTML = htmlText
return return
def getStyleSheet(self): def getStyleSheet(self):
"""Generate a stylesheet appropriate for the current settings. """Generate a stylesheet appropriate for the current settings.
""" """
theStyles = [] theStyles = []
if not self.cssStyles: if not self._cssStyles:
return theStyles return theStyles
mScale = self.lineHeight/1.15 mScale = self._lineHeight/1.15
textAlign = "justify" if self.doJustify else "left" textAlign = "justify" if self._doJustify else "left"
theStyles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format( theStyles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format(
self.textFont, self.textSize self._textFont, self._textSize
)) ))
theStyles.append(( theStyles.append((
"p {{" "p {{"
@@ -340,9 +348,9 @@ class ToHtml(Tokenizer):
"}}" "}}"
).format( ).format(
textAlign, textAlign,
round(100 * self.lineHeight), round(100 * self._lineHeight),
mScale * self.marginText[0], mScale * self._marginText[0],
mScale * self.marginText[1], mScale * self._marginText[1],
)) ))
theStyles.append(( theStyles.append((
"h1 {{" "h1 {{"
@@ -352,7 +360,7 @@ class ToHtml(Tokenizer):
"margin-bottom: {1:.2f}em;" "margin-bottom: {1:.2f}em;"
"}}" "}}"
).format( ).format(
mScale * self.marginHead1[0], mScale * self.marginHead1[1] mScale * self._marginHead1[0], mScale * self._marginHead1[1]
)) ))
theStyles.append(( theStyles.append((
"h2 {{" "h2 {{"
@@ -362,7 +370,7 @@ class ToHtml(Tokenizer):
"margin-bottom: {1:.2f}em;" "margin-bottom: {1:.2f}em;"
"}}" "}}"
).format( ).format(
mScale * self.marginHead2[0], mScale * self.marginHead2[1] mScale * self._marginHead2[0], mScale * self._marginHead2[1]
)) ))
theStyles.append(( theStyles.append((
"h3 {{" "h3 {{"
@@ -372,7 +380,7 @@ class ToHtml(Tokenizer):
"margin-bottom: {1:.2f}em;" "margin-bottom: {1:.2f}em;"
"}}" "}}"
).format( ).format(
mScale * self.marginHead3[0], mScale * self.marginHead3[1] mScale * self._marginHead3[0], mScale * self._marginHead3[1]
)) ))
theStyles.append(( theStyles.append((
"h4 {{" "h4 {{"
@@ -382,7 +390,7 @@ class ToHtml(Tokenizer):
"margin-bottom: {1:.2f}em;" "margin-bottom: {1:.2f}em;"
"}}" "}}"
).format( ).format(
mScale * self.marginHead4[0], mScale * self.marginHead4[1] mScale * self._marginHead4[0], mScale * self._marginHead4[1]
)) ))
theStyles.append(( theStyles.append((
".title {{" ".title {{"
@@ -391,7 +399,7 @@ class ToHtml(Tokenizer):
"margin-bottom: {1:.2f}em;" "margin-bottom: {1:.2f}em;"
"}}" "}}"
).format( ).format(
mScale * self.marginTitle[0], mScale * self.marginTitle[1] mScale * self._marginTitle[0], mScale * self._marginTitle[1]
)) ))
theStyles.append(( theStyles.append((
".sep, .skip {{" ".sep, .skip {{"
@@ -418,7 +426,7 @@ class ToHtml(Tokenizer):
def _formatSynopsis(self, tText): def _formatSynopsis(self, tText):
"""Apply HTML formatting to synopsis. """Apply HTML formatting to synopsis.
""" """
if self.genMode == self.M_PREVIEW: if self._genMode == self.M_PREVIEW:
sSynop = self._trSynopsis sSynop = self._trSynopsis
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n" return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n"
else: else:
@@ -428,7 +436,7 @@ class ToHtml(Tokenizer):
def _formatComments(self, tText): def _formatComments(self, tText):
"""Apply HTML formatting to comments. """Apply HTML formatting to comments.
""" """
if self.genMode == self.M_PREVIEW: if self._genMode == self.M_PREVIEW:
return f"<p class='comment'>{tText}</p>\n" return f"<p class='comment'>{tText}</p>\n"
else: else:
sComm = self._localLookup("Comment") sComm = self._localLookup("Comment")
@@ -449,7 +457,7 @@ class ToHtml(Tokenizer):
if theBits[0] == nwKeyWords.TAG_KEY: if theBits[0] == nwKeyWords.TAG_KEY:
retText += f"<a name='tag_{theBits[1]}'>{theBits[1]}</a>" retText += f"<a name='tag_{theBits[1]}'>{theBits[1]}</a>"
else: else:
if self.genMode == self.M_PREVIEW: if self._genMode == self.M_PREVIEW:
for tTag in theBits[1:]: for tTag in theBits[1:]:
refTags.append(f"<a href='#{theBits[0][1:]}={tTag}'>{tTag}</a>") refTags.append(f"<a href='#{theBits[0][1:]}={tTag}'>{tTag}</a>")
retText += ", ".join(refTags) retText += ", ".join(refTags)
+194 -178
View File
@@ -85,62 +85,62 @@ class Tokenizer():
self.mainConf = novelwriter.CONFIG self.mainConf = novelwriter.CONFIG
# Data Variables # Data Variables
self.theText = "" # The raw text to be tokenized self._theText = "" # The raw text to be tokenized
self.theHandle = None # The handle associated with the text self._theHandle = None # The handle associated with the text
self.theItem = None # The NWItem associated with the handle self._theItem = None # The NWItem associated with the handle
self.theTokens = [] # The list of the processed tokens self._theTokens = [] # The list of the processed tokens
self.theResult = "" # The result of the last document self._theResult = "" # The result of the last document
self.keepMarkdown = False # Whether to keep the markdown text self._keepMarkdown = False # Whether to keep the markdown text
self.theMarkdown = [] # The result novelWriter markdown of all documents self._theMarkdown = [] # The result novelWriter markdown of all documents
# User Settings # User Settings
self.textFont = "Serif" # Output text font self._textFont = "Serif" # Output text font
self.textSize = 11 # Output text size self._textSize = 11 # Output text size
self.textFixed = False # Fixed width text self._textFixed = False # Fixed width text
self.lineHeight = 1.15 # Line height in units of em self._lineHeight = 1.15 # Line height in units of em
self.blockIndent = 4.00 # Block indent in units of em self._blockIndent = 4.00 # Block indent in units of em
self.doJustify = False # Justify text self._doJustify = False # Justify text
self.doBodyText = True # Include body text self._doBodyText = True # Include body text
self.doSynopsis = False # Also process synopsis comments self._doSynopsis = False # Also process synopsis comments
self.doComments = False # Also process comments self._doComments = False # Also process comments
self.doKeywords = False # Also process keywords like tags and references self._doKeywords = False # Also process keywords like tags and references
# Margins # Margins
self.marginTitle = (1.000, 0.500) self._marginTitle = (1.000, 0.500)
self.marginHead1 = (1.000, 0.500) self._marginHead1 = (1.000, 0.500)
self.marginHead2 = (0.834, 0.500) self._marginHead2 = (0.834, 0.500)
self.marginHead3 = (0.584, 0.500) self._marginHead3 = (0.584, 0.500)
self.marginHead4 = (0.584, 0.500) self._marginHead4 = (0.584, 0.500)
self.marginText = (0.000, 0.584) self._marginText = (0.000, 0.584)
self.marginMeta = (0.000, 0.584) self._marginMeta = (0.000, 0.584)
# Title Formats # Title Formats
self.fmtTitle = "%title%" # Formatting for titles self._fmtTitle = "%title%" # Formatting for titles
self.fmtChapter = "%title%" # Formatting for numbered chapters self._fmtChapter = "%title%" # Formatting for numbered chapters
self.fmtUnNum = "%title%" # Formatting for unnumbered chapters self._fmtUnNum = "%title%" # Formatting for unnumbered chapters
self.fmtScene = "%title%" # Formatting for scenes self._fmtScene = "%title%" # Formatting for scenes
self.fmtSection = "%title%" # Formatting for sections self._fmtSection = "%title%" # Formatting for sections
self.hideScene = False # Do not include scene headers self._hideScene = False # Do not include scene headers
self.hideSection = False # Do not include section headers self._hideSection = False # Do not include section headers
self.linkHeaders = False # Add an anchor before headers self._linkHeaders = False # Add an anchor before headers
# Instance Variables # Instance Variables
self.numChapter = 0 # Counter for chapter numbers self._numChapter = 0 # Counter for chapter numbers
self.numChScene = 0 # Counter for scene number within chapter self._numChScene = 0 # Counter for scene number within chapter
self.numAbsScene = 0 # Counter for scene number within novel self._numAbsScene = 0 # Counter for scene number within novel
self.firstScene = False # Flag to indicate that the first scene of the chapter self._firstScene = False # Flag to indicate that the first scene of the chapter
# This File # This File
self.isNone = False # Document has unknown layout self._isNone = False # Document has unknown layout
self.isNovel = False # Document is a novel document self._isNovel = False # Document is a novel document
self.isNote = False # Document is a project note self._isNote = False # Document is a project note
self.isFirst = True # Document is the first in a set self._isFirst = True # Document is the first in a set
# Error Handling # Error Handling
self.errData = [] self._errData = []
# Function Mapping # Function Mapping
self._localLookup = self.theProject.localLookup self._localLookup = self.theProject.localLookup
@@ -151,100 +151,116 @@ class Tokenizer():
return return
##
# Properties
##
@property
def theResult(self):
return self._theResult
@property
def theMarkdown(self):
return self._theMarkdown
@property
def errData(self):
return self._errData
## ##
# Setters # Setters
## ##
def setTitleFormat(self, fmtTitle): def setTitleFormat(self, fmtTitle):
self.fmtTitle = fmtTitle.strip() self._fmtTitle = fmtTitle.strip()
return return
def setChapterFormat(self, fmtChapter): def setChapterFormat(self, fmtChapter):
self.fmtChapter = fmtChapter.strip() self._fmtChapter = fmtChapter.strip()
return return
def setUnNumberedFormat(self, fmtUnNum): def setUnNumberedFormat(self, fmtUnNum):
self.fmtUnNum = fmtUnNum.strip() self._fmtUnNum = fmtUnNum.strip()
return return
def setSceneFormat(self, fmtScene, hideScene): def setSceneFormat(self, fmtScene, hideScene):
self.fmtScene = fmtScene.strip() self._fmtScene = fmtScene.strip()
self.hideScene = hideScene self._hideScene = hideScene
return return
def setSectionFormat(self, fmtSection, hideSection): def setSectionFormat(self, fmtSection, hideSection):
self.fmtSection = fmtSection.strip() self._fmtSection = fmtSection.strip()
self.hideSection = hideSection self._hideSection = hideSection
return return
def setFont(self, textFont, textSize, textFixed=False): def setFont(self, textFont, textSize, textFixed=False):
self.textFont = textFont self._textFont = textFont
self.textSize = round(int(textSize)) self._textSize = round(int(textSize))
self.textFixed = textFixed self._textFixed = textFixed
return return
def setLineHeight(self, lineHeight): def setLineHeight(self, lineHeight):
self.lineHeight = min(max(float(lineHeight), 0.5), 5.0) self._lineHeight = min(max(float(lineHeight), 0.5), 5.0)
return return
def setBlockIndent(self, blockIndent): def setBlockIndent(self, blockIndent):
self.blockIndent = min(max(float(blockIndent), 0.0), 10.0) self._blockIndent = min(max(float(blockIndent), 0.0), 10.0)
return return
def setJustify(self, doJustify): def setJustify(self, doJustify):
self.doJustify = doJustify self._doJustify = doJustify
return return
def setTitleMargins(self, mUpper, mLower): def setTitleMargins(self, mUpper, mLower):
self.marginTitle = (float(mUpper), float(mLower)) self._marginTitle = (float(mUpper), float(mLower))
return return
def setHead1Margins(self, mUpper, mLower): def setHead1Margins(self, mUpper, mLower):
self.marginHead1 = (float(mUpper), float(mLower)) self._marginHead1 = (float(mUpper), float(mLower))
return return
def setHead2Margins(self, mUpper, mLower): def setHead2Margins(self, mUpper, mLower):
self.marginHead2 = (float(mUpper), float(mLower)) self._marginHead2 = (float(mUpper), float(mLower))
return return
def setHead3Margins(self, mUpper, mLower): def setHead3Margins(self, mUpper, mLower):
self.marginHead3 = (float(mUpper), float(mLower)) self._marginHead3 = (float(mUpper), float(mLower))
return return
def setHead4Margins(self, mUpper, mLower): def setHead4Margins(self, mUpper, mLower):
self.marginHead4 = (float(mUpper), float(mLower)) self._marginHead4 = (float(mUpper), float(mLower))
return return
def setTextMargins(self, mUpper, mLower): def setTextMargins(self, mUpper, mLower):
self.marginText = (float(mUpper), float(mLower)) self._marginText = (float(mUpper), float(mLower))
return return
def setMetaMargins(self, mUpper, mLower): def setMetaMargins(self, mUpper, mLower):
self.marginMeta = (float(mUpper), float(mLower)) self._marginMeta = (float(mUpper), float(mLower))
return return
def setLinkHeaders(self, linkHeaders): def setLinkHeaders(self, linkHeaders):
self.linkHeaders = linkHeaders self._linkHeaders = linkHeaders
return return
def setBodyText(self, doBodyText): def setBodyText(self, doBodyText):
self.doBodyText = doBodyText self._doBodyText = doBodyText
return return
def setSynopsis(self, doSynopsis): def setSynopsis(self, doSynopsis):
self.doSynopsis = doSynopsis self._doSynopsis = doSynopsis
return return
def setComments(self, doComments): def setComments(self, doComments):
self.doComments = doComments self._doComments = doComments
return return
def setKeywords(self, doKeywords): def setKeywords(self, doKeywords):
self.doKeywords = doKeywords self._doKeywords = doKeywords
return return
def setKeepMarkdown(self, keepMarkdown): def setKeepMarkdown(self, keepMarkdown):
self.keepMarkdown = keepMarkdown self._keepMarkdown = keepMarkdown
return return
## ##
@@ -261,20 +277,20 @@ class Tokenizer():
if theItem.itemType != nwItemType.ROOT: if theItem.itemType != nwItemType.ROOT:
return False return False
if self.isFirst: if self._isFirst:
textAlign = self.A_CENTRE textAlign = self.A_CENTRE
self.isFirst = False self._isFirst = False
else: else:
textAlign = self.A_PBB | self.A_CENTRE textAlign = self.A_PBB | self.A_CENTRE
locNotes = self._localLookup("Notes") locNotes = self._localLookup("Notes")
theTitle = f"{locNotes}: {theItem.itemName}" theTitle = f"{locNotes}: {theItem.itemName}"
self.theTokens = [] self._theTokens = []
self.theTokens.append(( self._theTokens.append((
self.T_TITLE, 0, theTitle, None, textAlign self.T_TITLE, 0, theTitle, None, textAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
self.theMarkdown.append(f"# {theTitle}\n\n") self._theMarkdown.append(f"# {theTitle}\n\n")
return True return True
@@ -282,33 +298,33 @@ class Tokenizer():
"""Set the text for the tokenizer from a handle. If theText is """Set the text for the tokenizer from a handle. If theText is
not set, load it from the file. not set, load it from the file.
""" """
self.theHandle = theHandle self._theHandle = theHandle
self.theItem = self.theProject.projTree[theHandle] self._theItem = self.theProject.projTree[theHandle]
if self.theItem is None: if self._theItem is None:
return False return False
self.theText = "" self._theText = ""
if theText is not None: if theText is not None:
# If the text is set, just use that # If the text is set, just use that
self.theText = theText self._theText = theText
else: else:
# Otherwise, load it from file # Otherwise, load it from file
theDoc = NWDoc(self.theProject, theHandle) theDoc = NWDoc(self.theProject, theHandle)
theText = theDoc.readDocument() theText = theDoc.readDocument()
if theText: if theText:
self.theText = theText self._theText = theText
docSize = len(self.theText) docSize = len(self._theText)
if docSize > nwConst.MAX_DOCSIZE: if docSize > nwConst.MAX_DOCSIZE:
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format( errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
self.theItem.itemName, f"{docSize/1.0e6:.2f}" self._theItem.itemName, f"{docSize/1.0e6:.2f}"
) )
self.theText = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal) self._theText = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal)
self.errData.append(errVal) self._errData.append(errVal)
self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT self._isNone = self._theItem.itemLayout == nwItemLayout.NO_LAYOUT
self.isNovel = self.theItem.itemLayout == nwItemLayout.DOCUMENT self._isNovel = self._theItem.itemLayout == nwItemLayout.DOCUMENT
self.isNote = self.theItem.itemLayout == nwItemLayout.NOTE self._isNote = self._theItem.itemLayout == nwItemLayout.NOTE
return True return True
@@ -321,11 +337,11 @@ class Tokenizer():
for aKey, aVal in self.theProject.autoReplace.items(): for aKey, aVal in self.theProject.autoReplace.items():
repDict[f"<{aKey}>"] = aVal repDict[f"<{aKey}>"] = aVal
xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL) 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) self._theText = xRep.sub(lambda x: repDict[x.group(0)], self._theText)
# Process the character translation map # Process the character translation map
trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO} trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO}
self.theText = self.theText.translate(str.maketrans(trDict)) self._theText = self._theText.translate(str.maketrans(trDict))
return return
@@ -341,8 +357,8 @@ class Tokenizer():
escReplace = re.compile( escReplace = re.compile(
"|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL "|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL
) )
self.theResult = escReplace.sub( self._theResult = escReplace.sub(
lambda x: escapeDict[x.group(0)], self.theResult lambda x: escapeDict[x.group(0)], self._theResult
) )
return return
@@ -356,7 +372,7 @@ class Tokenizer():
The format of the token list is an entry with a five-tuple for The format of the token list is an entry with a five-tuple for
each line in the file. The tuple is as follows: each line in the file. The tuple is as follows:
1: The type of the block, self.T_* 1: The type of the block, self.T_*
2: The line in file where this block occurred 2: The line in the file where this block occurred
3: The text content of the block, without leading tags 3: The text content of the block, without leading tags
4: The internal formatting map of the text, self.FMT_* 4: The internal formatting map of the text, self.FMT_*
5: The style of the block, self.A_* 5: The style of the block, self.A_*
@@ -368,20 +384,20 @@ class Tokenizer():
(QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]), (QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]),
] ]
self.theTokens = [] self._theTokens = []
tmpMarkdown = [] tmpMarkdown = []
nLine = 0 nLine = 0
breakNext = False breakNext = False
for aLine in self.theText.splitlines(): for aLine in self._theText.splitlines():
nLine += 1 nLine += 1
sLine = aLine.strip() sLine = aLine.strip()
# Check for blank lines # Check for blank lines
if len(sLine) == 0: if len(sLine) == 0:
self.theTokens.append(( self._theTokens.append((
self.T_EMPTY, nLine, "", None, self.A_NONE self.T_EMPTY, nLine, "", None, self.A_NONE
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
continue continue
@@ -403,7 +419,7 @@ class Tokenizer():
continue continue
elif sLine == "[VSPACE]": elif sLine == "[VSPACE]":
self.theTokens.append( self._theTokens.append(
(self.T_SKIP, nLine, "", None, sAlign) (self.T_SKIP, nLine, "", None, sAlign)
) )
continue continue
@@ -411,11 +427,11 @@ class Tokenizer():
elif sLine.startswith("[VSPACE:") and sLine.endswith("]"): elif sLine.startswith("[VSPACE:") and sLine.endswith("]"):
nSkip = checkInt(sLine[8:-1], 0) nSkip = checkInt(sLine[8:-1], 0)
if nSkip >= 1: if nSkip >= 1:
self.theTokens.append( self._theTokens.append(
(self.T_SKIP, nLine, "", None, sAlign) (self.T_SKIP, nLine, "", None, sAlign)
) )
if nSkip > 1: if nSkip > 1:
self.theTokens += (nSkip - 1) * [ self._theTokens += (nSkip - 1) * [
(self.T_SKIP, nLine, "", None, self.A_NONE) (self.T_SKIP, nLine, "", None, self.A_NONE)
] ]
continue continue
@@ -424,87 +440,87 @@ class Tokenizer():
cLine = aLine[1:].lstrip() cLine = aLine[1:].lstrip()
synTag = cLine[:9].lower() synTag = cLine[:9].lower()
if synTag == "synopsis:": if synTag == "synopsis:":
self.theTokens.append(( self._theTokens.append((
self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign
)) ))
if self.doSynopsis and self.keepMarkdown: if self._doSynopsis and self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
else: else:
self.theTokens.append(( self._theTokens.append((
self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign
)) ))
if self.doComments and self.keepMarkdown: if self._doComments and self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[0] == "@": elif aLine[0] == "@":
self.theTokens.append(( self._theTokens.append((
self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign
)) ))
if self.doKeywords and self.keepMarkdown: if self._doKeywords and self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:2] == "# ": elif aLine[:2] == "# ":
if self.isNovel: if self._isNovel:
sAlign |= self.A_CENTRE sAlign |= self.A_CENTRE
sAlign |= self.A_PBB sAlign |= self.A_PBB
self.theTokens.append(( self._theTokens.append((
self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:3] == "## ": elif aLine[:3] == "## ":
if self.isNovel: if self._isNovel:
sAlign |= self.A_PBB sAlign |= self.A_PBB
self.theTokens.append(( self._theTokens.append((
self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:4] == "### ": elif aLine[:4] == "### ":
self.theTokens.append(( self._theTokens.append((
self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:5] == "#### ": elif aLine[:5] == "#### ":
self.theTokens.append(( self._theTokens.append((
self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:3] == "#! ": elif aLine[:3] == "#! ":
if self.isNovel: if self._isNovel:
tStyle = self.T_TITLE tStyle = self.T_TITLE
else: else:
tStyle = self.T_HEAD1 tStyle = self.T_HEAD1
self.theTokens.append(( self._theTokens.append((
tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
elif aLine[:4] == "##! ": elif aLine[:4] == "##! ":
if self.isNovel: if self._isNovel:
tStyle = self.T_UNNUM tStyle = self.T_UNNUM
sAlign |= self.A_PBB sAlign |= self.A_PBB
else: else:
tStyle = self.T_HEAD2 tStyle = self.T_HEAD2
self.theTokens.append(( self._theTokens.append((
tStyle, nLine, aLine[4:].strip(), None, sAlign tStyle, nLine, aLine[4:].strip(), None, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
else: else:
if not self.doBodyText: if not self._doBodyText:
# Skip all body text # Skip all body text
continue continue
@@ -554,33 +570,33 @@ class Tokenizer():
# Save the line as is, but append the array of formatting locations # Save the line as is, but append the array of formatting locations
# sorted by position # sorted by position
fmtPos = sorted(fmtPos, key=itemgetter(0)) fmtPos = sorted(fmtPos, key=itemgetter(0))
self.theTokens.append(( self._theTokens.append((
self.T_TEXT, nLine, aLine, fmtPos, sAlign self.T_TEXT, nLine, aLine, fmtPos, sAlign
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("%s\n" % aLine) tmpMarkdown.append("%s\n" % aLine)
# If we have content, turn off the first page flag # If we have content, turn off the first page flag
if self.isFirst and self.theTokens: if self._isFirst and self._theTokens:
self.isFirst = False self._isFirst = False
# Make sure the token array doesn't start with a page break # Make sure the token array doesn't start with a page break
# on the very first page, adding a blank first page. # on the very first page, adding a blank first page.
if self.theTokens[0][4] & self.A_PBB: if self._theTokens[0][4] & self.A_PBB:
tToken = self.theTokens[0] tToken = self._theTokens[0]
self.theTokens[0] = ( self._theTokens[0] = (
tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] & ~self.A_PBB tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] & ~self.A_PBB
) )
# Always add an empty line at the end of the file # Always add an empty line at the end of the file
self.theTokens.append(( self._theTokens.append((
self.T_EMPTY, nLine, "", None, self.A_NONE self.T_EMPTY, nLine, "", None, self.A_NONE
)) ))
if self.keepMarkdown: if self._keepMarkdown:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
if self.keepMarkdown: if self._keepMarkdown:
self.theMarkdown.append("".join(tmpMarkdown)) self._theMarkdown.append("".join(tmpMarkdown))
# Second Pass # Second Pass
# =========== # ===========
@@ -588,13 +604,13 @@ class Tokenizer():
pToken = (self.T_EMPTY, 0, "", None, self.A_NONE) pToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
nToken = (self.T_EMPTY, 0, "", None, self.A_NONE) nToken = (self.T_EMPTY, 0, "", None, self.A_NONE)
tCount = len(self.theTokens) tCount = len(self._theTokens)
for n, tToken in enumerate(self.theTokens): for n, tToken in enumerate(self._theTokens):
if n > 0: if n > 0:
pToken = self.theTokens[n-1] pToken = self._theTokens[n-1]
if n < tCount - 1: if n < tCount - 1:
nToken = self.theTokens[n+1] nToken = self._theTokens[n+1]
if tToken[0] == self.T_KEYWORD: if tToken[0] == self.T_KEYWORD:
aStyle = tToken[4] aStyle = tToken[4]
@@ -602,7 +618,7 @@ class Tokenizer():
aStyle |= self.A_Z_TOPMRG aStyle |= self.A_Z_TOPMRG
if nToken[0] == self.T_KEYWORD: if nToken[0] == self.T_KEYWORD:
aStyle |= self.A_Z_BTMMRG aStyle |= self.A_Z_BTMMRG
self.theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tToken[2], tToken[3], aStyle tToken[0], tToken[1], tToken[2], tToken[3], aStyle
) )
@@ -612,20 +628,20 @@ class Tokenizer():
"""Apply formatting to the text headers for novel files. This """Apply formatting to the text headers for novel files. This
also applies chapter and scene numbering. also applies chapter and scene numbering.
""" """
if not self.isNovel: if not self._isNovel:
return False return False
for n, tToken in enumerate(self.theTokens): for n, tToken in enumerate(self._theTokens):
# In case we see text before a scene, we reset the flag # In case we see text before a scene, we reset the flag
if tToken[0] == self.T_TEXT: if tToken[0] == self.T_TEXT:
self.firstScene = False self._firstScene = False
elif tToken[0] == self.T_HEAD1: elif tToken[0] == self.T_HEAD1:
# Partition # Partition
tTemp = self._formatHeading(self.fmtTitle, tToken[2]) tTemp = self._formatHeading(self._fmtTitle, tToken[2])
self.theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
@@ -634,75 +650,75 @@ class Tokenizer():
# Numbered or Unnumbered # Numbered or Unnumbered
if tToken[0] == self.T_UNNUM: if tToken[0] == self.T_UNNUM:
tTemp = self._formatHeading(self.fmtUnNum, tToken[2]) tTemp = self._formatHeading(self._fmtUnNum, tToken[2])
else: else:
self.numChapter += 1 self._numChapter += 1
tTemp = self._formatHeading(self.fmtChapter, tToken[2]) tTemp = self._formatHeading(self._fmtChapter, tToken[2])
# Format the chapter header # Format the chapter header
self.theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
# Set scene variables # Set scene variables
self.firstScene = True self._firstScene = True
self.numChScene = 0 self._numChScene = 0
elif tToken[0] == self.T_HEAD3: elif tToken[0] == self.T_HEAD3:
# Scene # Scene
self.numChScene += 1 self._numChScene += 1
self.numAbsScene += 1 self._numAbsScene += 1
tTemp = self._formatHeading(self.fmtScene, tToken[2]) tTemp = self._formatHeading(self._fmtScene, tToken[2])
if tTemp == "" and self.hideScene: if tTemp == "" and self._hideScene:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
) )
elif tTemp == "" and not self.hideScene: elif tTemp == "" and not self._hideScene:
if self.firstScene: if self._firstScene:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
) )
else: else:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_SKIP, tToken[1], "", None, tToken[4] self.T_SKIP, tToken[1], "", None, tToken[4]
) )
elif tTemp == self.fmtScene: elif tTemp == self._fmtScene:
if self.firstScene: if self._firstScene:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
) )
else: else:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
) )
else: else:
self.theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
# Definitely no longer the first scene # Definitely no longer the first scene
self.firstScene = False self._firstScene = False
elif tToken[0] == self.T_HEAD4: elif tToken[0] == self.T_HEAD4:
# Section # Section
tTemp = self._formatHeading(self.fmtSection, tToken[2]) tTemp = self._formatHeading(self._fmtSection, tToken[2])
if tTemp == "" and self.hideSection: if tTemp == "" and self._hideSection:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_EMPTY, tToken[1], "", None, self.A_NONE self.T_EMPTY, tToken[1], "", None, self.A_NONE
) )
elif tTemp == "" and not self.hideSection: elif tTemp == "" and not self._hideSection:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_SKIP, tToken[1], "", None, tToken[4] self.T_SKIP, tToken[1], "", None, tToken[4]
) )
elif tTemp == self.fmtSection: elif tTemp == self._fmtSection:
self.theTokens[n] = ( self._theTokens[n] = (
self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE
) )
else: else:
self.theTokens[n] = ( self._theTokens[n] = (
tToken[0], tToken[1], tTemp, None, tToken[4] tToken[0], tToken[1], tTemp, None, tToken[4]
) )
@@ -712,7 +728,7 @@ class Tokenizer():
"""Save the data to a plain text file. """Save the data to a plain text file.
""" """
with open(savePath, mode="w", encoding="utf-8") as outFile: with open(savePath, mode="w", encoding="utf-8") as outFile:
for nwdPage in self.theMarkdown: for nwdPage in self._theMarkdown:
outFile.write(nwdPage) outFile.write(nwdPage)
return return
@@ -724,15 +740,15 @@ class Tokenizer():
"""Replaces the %keyword% strings. """Replaces the %keyword% strings.
""" """
theTitle = theTitle.replace(r"%title%", theText) theTitle = theTitle.replace(r"%title%", theText)
theTitle = theTitle.replace(r"%ch%", str(self.numChapter)) theTitle = theTitle.replace(r"%ch%", str(self._numChapter))
theTitle = theTitle.replace(r"%sc%", str(self.numChScene)) theTitle = theTitle.replace(r"%sc%", str(self._numChScene))
theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene)) theTitle = theTitle.replace(r"%sca%", str(self._numAbsScene))
if r"%chw%" in theTitle: if r"%chw%" in theTitle:
theTitle = theTitle.replace(r"%chw%", self._localLookup(self.numChapter)) theTitle = theTitle.replace(r"%chw%", self._localLookup(self._numChapter))
if r"%chi%" in theTitle: if r"%chi%" in theTitle:
theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True)) theTitle = theTitle.replace(r"%chi%", numberToRoman(self._numChapter, True))
if r"%chI%" in theTitle: if r"%chI%" in theTitle:
theTitle = theTitle.replace(r"%chI%", numberToRoman(self.numChapter, False)) theTitle = theTitle.replace(r"%chI%", numberToRoman(self._numChapter, False))
return theTitle[:1].upper() + theTitle[1:] return theTitle[:1].upper() + theTitle[1:]
+24 -16
View File
@@ -39,21 +39,29 @@ class ToMarkdown(Tokenizer):
def __init__(self, theProject): def __init__(self, theProject):
Tokenizer.__init__(self, theProject) Tokenizer.__init__(self, theProject)
self.genMode = self.M_STD self._genMode = self.M_STD
self.fullMD = [] self._fullMD = []
return return
##
# Properties
##
@property
def fullMD(self):
return self._fullMD
## ##
# Setters # Setters
## ##
def setStandardMarkdown(self): def setStandardMarkdown(self):
self.genMode = self.M_STD self._genMode = self.M_STD
return return
def setGitHubMarkdown(self): def setGitHubMarkdown(self):
self.genMode = self.M_GH self._genMode = self.M_GH
return return
## ##
@@ -63,13 +71,13 @@ class ToMarkdown(Tokenizer):
def getFullResultSize(self): def getFullResultSize(self):
"""Return the size of the full Markdown result. """Return the size of the full Markdown result.
""" """
return sum([len(x) for x in self.fullMD]) return sum([len(x) for x in self._fullMD])
def doConvert(self): def doConvert(self):
"""Convert the list of text tokens into a HTML document saved """Convert the list of text tokens into a HTML document saved
to theResult. to theResult.
""" """
if self.genMode == self.M_STD: if self._genMode == self.M_STD:
# Standard # Standard
mdTags = { mdTags = {
self.FMT_B_B: "**", self.FMT_B_B: "**",
@@ -90,12 +98,12 @@ class ToMarkdown(Tokenizer):
self.FMT_D_E: "~~", self.FMT_D_E: "~~",
} }
self.theResult = "" self._theResult = ""
thisPar = [] thisPar = []
tmpResult = [] tmpResult = []
for tType, _, tText, tFormat, tStyle in self.theTokens: for tType, _, tText, tFormat, tStyle in self._theTokens:
# Process Text Type # Process Text Type
if tType == self.T_EMPTY: if tType == self.T_EMPTY:
@@ -140,21 +148,21 @@ class ToMarkdown(Tokenizer):
tTemp = tTemp[:xPos] + mdTags[xFmt] + tTemp[xPos+xLen:] tTemp = tTemp[:xPos] + mdTags[xFmt] + tTemp[xPos+xLen:]
thisPar.append(tTemp.rstrip()) thisPar.append(tTemp.rstrip())
elif tType == self.T_SYNOPSIS and self.doSynopsis: elif tType == self.T_SYNOPSIS and self._doSynopsis:
locName = self._localLookup("Synopsis") locName = self._localLookup("Synopsis")
tmpResult.append(f"**{locName}:** {tText}\n\n") tmpResult.append(f"**{locName}:** {tText}\n\n")
elif tType == self.T_COMMENT and self.doComments: elif tType == self.T_COMMENT and self._doComments:
locName = self._localLookup("Comment") locName = self._localLookup("Comment")
tmpResult.append(f"**{locName}:** {tText}\n\n") tmpResult.append(f"**{locName}:** {tText}\n\n")
elif tType == self.T_KEYWORD and self.doKeywords: elif tType == self.T_KEYWORD and self._doKeywords:
tmpResult.append(self._formatKeywords(tText, tStyle)) tmpResult.append(self._formatKeywords(tText, tStyle))
self.theResult = "".join(tmpResult) self._theResult = "".join(tmpResult)
tmpResult = [] tmpResult = []
self.fullMD.append(self.theResult) self._fullMD.append(self._theResult)
return return
@@ -162,7 +170,7 @@ class ToMarkdown(Tokenizer):
"""Save the data to a plain text file. """Save the data to a plain text file.
""" """
with open(savePath, mode="w", encoding="utf-8") as outFile: with open(savePath, mode="w", encoding="utf-8") as outFile:
theText = "".join(self.fullMD) theText = "".join(self._fullMD)
outFile.write(theText) outFile.write(theText)
return return
@@ -172,10 +180,10 @@ class ToMarkdown(Tokenizer):
""" """
fullMD = [] fullMD = []
eightSpace = spaceChar*nSpaces eightSpace = spaceChar*nSpaces
for aPage in self.fullMD: for aPage in self._fullMD:
fullMD.append(aPage.replace("\t", eightSpace)) fullMD.append(aPage.replace("\t", eightSpace))
self.fullMD = fullMD self._fullMD = fullMD
return return
## ##
+77 -77
View File
@@ -115,27 +115,27 @@ class ToOdt(Tokenizer):
self._errData = [] # List of errors encountered self._errData = [] # List of errors encountered
# Properties # Properties
self.textFont = "Liberation Serif" self._textFont = "Liberation Serif"
self.textSize = 12 self._textSize = 12
self.textFixed = False self._textFixed = False
self.colourHead = False self._colourHead = False
self.headerText = "" self._headerText = ""
# Internal # Internal
self._fontFamily = "&apos;Liberation Serif&apos;" self._fontFamily = "&apos;Liberation Serif&apos;"
self._fontPitch = "variable" self._fontPitch = "variable"
self._fSizeTitle = "30pt" self._fSizeTitle = "30pt"
self._fSizeHead1 = "24pt" self._fSizeHead1 = "24pt"
self._fSizeHead2 = "20pt" self._fSizeHead2 = "20pt"
self._fSizeHead3 = "16pt" self._fSizeHead3 = "16pt"
self._fSizeHead4 = "14pt" self._fSizeHead4 = "14pt"
self._fSizeHead = "14pt" self._fSizeHead = "14pt"
self._fSizeText = "12pt" self._fSizeText = "12pt"
self._lineHeight = "115%" self._fLineHeight = "115%"
self._blockIndent = "1.693cm" self._fBlockIndent = "1.693cm"
self._textAlign = "left" self._textAlign = "left"
self._dLanguage = "en" self._dLanguage = "en"
self._dCountry = "GB" self._dCountry = "GB"
# Text Margings in Units of em # Text Margings in Units of em
self._mTopTitle = "0.423cm" self._mTopTitle = "0.423cm"
@@ -192,7 +192,7 @@ class ToOdt(Tokenizer):
def setColourHeaders(self, doColour): def setColourHeaders(self, doColour):
"""Enable/disable coloured headings and comments. """Enable/disable coloured headings and comments.
""" """
self.colourHead = doColour self._colourHead = doColour
return return
## ##
@@ -209,40 +209,40 @@ class ToOdt(Tokenizer):
# Initialise Variables # Initialise Variables
# ==================== # ====================
self._fontFamily = self.textFont self._fontFamily = self._textFont
if len(self.textFont.split()) > 1: if len(self._textFont.split()) > 1:
self._fontFamily = f"'{self.textFont}'" self._fontFamily = f"'{self._textFont}'"
self._fontPitch = "fixed" if self.textFixed else "variable" self._fontPitch = "fixed" if self._textFixed else "variable"
self._fSizeTitle = f"{round(2.50 * self.textSize):d}pt" self._fSizeTitle = f"{round(2.50 * self._textSize):d}pt"
self._fSizeHead1 = f"{round(2.00 * self.textSize):d}pt" self._fSizeHead1 = f"{round(2.00 * self._textSize):d}pt"
self._fSizeHead2 = f"{round(1.60 * self.textSize):d}pt" self._fSizeHead2 = f"{round(1.60 * self._textSize):d}pt"
self._fSizeHead3 = f"{round(1.30 * self.textSize):d}pt" self._fSizeHead3 = f"{round(1.30 * self._textSize):d}pt"
self._fSizeHead4 = f"{round(1.15 * self.textSize):d}pt" self._fSizeHead4 = f"{round(1.15 * self._textSize):d}pt"
self._fSizeHead = f"{round(1.15 * self.textSize):d}pt" self._fSizeHead = f"{round(1.15 * self._textSize):d}pt"
self._fSizeText = f"{self.textSize:d}pt" self._fSizeText = f"{self._textSize:d}pt"
mScale = self.lineHeight/1.15 mScale = self._lineHeight/1.15
self._mTopTitle = self._emToCm(mScale * self.marginTitle[0]) self._mTopTitle = self._emToCm(mScale * self._marginTitle[0])
self._mTopHead1 = self._emToCm(mScale * self.marginHead1[0]) self._mTopHead1 = self._emToCm(mScale * self._marginHead1[0])
self._mTopHead2 = self._emToCm(mScale * self.marginHead2[0]) self._mTopHead2 = self._emToCm(mScale * self._marginHead2[0])
self._mTopHead3 = self._emToCm(mScale * self.marginHead3[0]) self._mTopHead3 = self._emToCm(mScale * self._marginHead3[0])
self._mTopHead4 = self._emToCm(mScale * self.marginHead4[0]) self._mTopHead4 = self._emToCm(mScale * self._marginHead4[0])
self._mTopHead = self._emToCm(mScale * self.marginHead4[0]) self._mTopHead = self._emToCm(mScale * self._marginHead4[0])
self._mTopText = self._emToCm(mScale * self.marginText[0]) self._mTopText = self._emToCm(mScale * self._marginText[0])
self._mTopMeta = self._emToCm(mScale * self.marginMeta[0]) self._mTopMeta = self._emToCm(mScale * self._marginMeta[0])
self._mBotTitle = self._emToCm(mScale * self.marginTitle[1]) self._mBotTitle = self._emToCm(mScale * self._marginTitle[1])
self._mBotHead1 = self._emToCm(mScale * self.marginHead1[1]) self._mBotHead1 = self._emToCm(mScale * self._marginHead1[1])
self._mBotHead2 = self._emToCm(mScale * self.marginHead2[1]) self._mBotHead2 = self._emToCm(mScale * self._marginHead2[1])
self._mBotHead3 = self._emToCm(mScale * self.marginHead3[1]) self._mBotHead3 = self._emToCm(mScale * self._marginHead3[1])
self._mBotHead4 = self._emToCm(mScale * self.marginHead4[1]) self._mBotHead4 = self._emToCm(mScale * self._marginHead4[1])
self._mBotHead = self._emToCm(mScale * self.marginHead4[1]) self._mBotHead = self._emToCm(mScale * self._marginHead4[1])
self._mBotText = self._emToCm(mScale * self.marginText[1]) self._mBotText = self._emToCm(mScale * self._marginText[1])
self._mBotMeta = self._emToCm(mScale * self.marginMeta[1]) self._mBotMeta = self._emToCm(mScale * self._marginMeta[1])
if self.colourHead: if self._colourHead:
self._colHead12 = "#2a6099" self._colHead12 = "#2a6099"
self._opaHead12 = "100%" self._opaHead12 = "100%"
self._colHead34 = "#444444" self._colHead34 = "#444444"
@@ -250,9 +250,9 @@ class ToOdt(Tokenizer):
self._colMetaTx = "#813709" self._colMetaTx = "#813709"
self._opaMetaTx = "100%" self._opaMetaTx = "100%"
self._lineHeight = f"{round(100 * self.lineHeight):d}%" self._fLineHeight = f"{round(100 * self._lineHeight):d}%"
self._blockIndent = self._emToCm(self.blockIndent) self._fBlockIndent = self._emToCm(self._blockIndent)
self._textAlign = "justify" if self.doJustify else "left" self._textAlign = "justify" if self._doJustify else "left"
# Clear Errors # Clear Errors
self._errData = [] self._errData = []
@@ -260,10 +260,10 @@ class ToOdt(Tokenizer):
# Document Header # Document Header
# =============== # ===============
if self.headerText == "": if self._headerText == "":
theTitle = self.theProject.bookTitle theTitle = self.theProject.bookTitle
theAuth = self.theProject.getAuthors() theAuth = self.theProject.getAuthors()
self.headerText = f"{theTitle} / {theAuth} /" self._headerText = f"{theTitle} / {theAuth} /"
# Create Roots # Create Roots
# ============ # ============
@@ -272,7 +272,7 @@ class ToOdt(Tokenizer):
tAttr[_mkTag("office", "version")] = X_VERS tAttr[_mkTag("office", "version")] = X_VERS
fAttr = {} fAttr = {}
fAttr[_mkTag("style", "name")] = self.textFont fAttr[_mkTag("style", "name")] = self._textFont
fAttr[_mkTag("style", "font-pitch")] = self._fontPitch fAttr[_mkTag("style", "font-pitch")] = self._fontPitch
if self._isFlat: if self._isFlat:
@@ -345,7 +345,7 @@ class ToOdt(Tokenizer):
def doConvert(self): def doConvert(self):
"""Convert the list of text tokens into XML elements. """Convert the list of text tokens into XML elements.
""" """
self.theResult = "" # Not used, but cleared just in case self._theResult = "" # Not used, but cleared just in case
odtTags = { odtTags = {
self.FMT_B_B: "_B", # Bold open format self.FMT_B_B: "_B", # Bold open format
@@ -359,7 +359,7 @@ class ToOdt(Tokenizer):
thisPar = [] thisPar = []
thisFmt = [] thisFmt = []
parStyle = None parStyle = None
for tType, _, tText, tFormat, tStyle in self.theTokens: for tType, _, tText, tFormat, tStyle in self._theTokens:
# Styles # Styles
oStyle = ODTParagraphStyle() oStyle = ODTParagraphStyle()
@@ -385,14 +385,14 @@ class ToOdt(Tokenizer):
oStyle.setMarginTop("0.000cm") oStyle.setMarginTop("0.000cm")
if tStyle & self.A_IND_L: if tStyle & self.A_IND_L:
oStyle.setMarginLeft(self._blockIndent) oStyle.setMarginLeft(self._fBlockIndent)
if tStyle & self.A_IND_R: if tStyle & self.A_IND_R:
oStyle.setMarginRight(self._blockIndent) oStyle.setMarginRight(self._fBlockIndent)
# Process Text Types # Process Text Types
if tType == self.T_EMPTY: if tType == self.T_EMPTY:
if len(thisPar) > 1 and parStyle is not None: if len(thisPar) > 1 and parStyle is not None:
if self.doJustify: if self._doJustify:
parStyle.setTextAlign("left") parStyle.setTextAlign("left")
if len(thisPar) > 0: if len(thisPar) > 0:
@@ -449,15 +449,15 @@ class ToOdt(Tokenizer):
thisPar.append(tTxt) thisPar.append(tTxt)
thisFmt.append(tFmt) thisFmt.append(tFmt)
elif tType == self.T_SYNOPSIS and self.doSynopsis: elif tType == self.T_SYNOPSIS and self._doSynopsis:
tTemp, fTemp = self._formatSynopsis(tText) tTemp, fTemp = self._formatSynopsis(tText)
self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp)
elif tType == self.T_COMMENT and self.doComments: elif tType == self.T_COMMENT and self._doComments:
tTemp, fTemp = self._formatComments(tText) tTemp, fTemp = self._formatComments(tText)
self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp)
elif tType == self.T_KEYWORD and self.doKeywords: elif tType == self.T_KEYWORD and self._doKeywords:
tTemp, fTemp = self._formatKeywords(tText) tTemp, fTemp = self._formatKeywords(tText)
self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp)
@@ -696,7 +696,7 @@ class ToOdt(Tokenizer):
def _emToCm(self, emVal): def _emToCm(self, emVal):
"""Converts an em value to centimetres. """Converts an em value to centimetres.
""" """
return f"{emVal*2.54/72*self.textSize:.3f}cm" return f"{emVal*2.54/72*self._textSize:.3f}cm"
## ##
# Style Elements # Style Elements
@@ -747,7 +747,7 @@ class ToOdt(Tokenizer):
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr) etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
theAttr = {} theAttr = {}
theAttr[_mkTag("style", "font-name")] = self.textFont theAttr[_mkTag("style", "font-name")] = self._textFont
theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-family")] = self._fontFamily
theAttr[_mkTag("fo", "font-size")] = self._fSizeText theAttr[_mkTag("fo", "font-size")] = self._fSizeText
theAttr[_mkTag("fo", "language")] = self._dLanguage theAttr[_mkTag("fo", "language")] = self._dLanguage
@@ -764,7 +764,7 @@ class ToOdt(Tokenizer):
xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr) xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr)
theAttr = {} theAttr = {}
theAttr[_mkTag("style", "font-name")] = self.textFont theAttr[_mkTag("style", "font-name")] = self._textFont
theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-family")] = self._fontFamily
theAttr[_mkTag("fo", "font-size")] = self._fSizeText theAttr[_mkTag("fo", "font-size")] = self._fSizeText
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr) etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
@@ -787,7 +787,7 @@ class ToOdt(Tokenizer):
etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr) etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr)
theAttr = {} theAttr = {}
theAttr[_mkTag("style", "font-name")] = self.textFont theAttr[_mkTag("style", "font-name")] = self._textFont
theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-family")] = self._fontFamily
theAttr[_mkTag("fo", "font-size")] = self._fSizeHead theAttr[_mkTag("fo", "font-size")] = self._fSizeHead
etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr) etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr)
@@ -816,8 +816,8 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopText) oStyle.setMarginTop(self._mTopText)
oStyle.setMarginBottom(self._mBotText) oStyle.setMarginBottom(self._mBotText)
oStyle.setLineHeight(self._lineHeight) oStyle.setLineHeight(self._fLineHeight)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeText) oStyle.setFontSize(self._fSizeText)
oStyle.setTextAlign(self._textAlign) oStyle.setTextAlign(self._textAlign)
@@ -834,8 +834,8 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopMeta) oStyle.setMarginTop(self._mTopMeta)
oStyle.setMarginBottom(self._mBotMeta) oStyle.setMarginBottom(self._mBotMeta)
oStyle.setLineHeight(self._lineHeight) oStyle.setLineHeight(self._fLineHeight)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeText) oStyle.setFontSize(self._fSizeText)
oStyle.setColor(self._colMetaTx) oStyle.setColor(self._colMetaTx)
@@ -855,7 +855,7 @@ class ToOdt(Tokenizer):
oStyle.setTextAlign("center") oStyle.setTextAlign("center")
oStyle.setMarginTop(self._mTopTitle) oStyle.setMarginTop(self._mTopTitle)
oStyle.setMarginBottom(self._mBotTitle) oStyle.setMarginBottom(self._mBotTitle)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeTitle) oStyle.setFontSize(self._fSizeTitle)
oStyle.setFontWeight("bold") oStyle.setFontWeight("bold")
@@ -874,7 +874,7 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopHead1) oStyle.setMarginTop(self._mTopHead1)
oStyle.setMarginBottom(self._mBotHead1) oStyle.setMarginBottom(self._mBotHead1)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeHead1) oStyle.setFontSize(self._fSizeHead1)
oStyle.setColor(self._colHead12) oStyle.setColor(self._colHead12)
@@ -895,7 +895,7 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopHead2) oStyle.setMarginTop(self._mTopHead2)
oStyle.setMarginBottom(self._mBotHead2) oStyle.setMarginBottom(self._mBotHead2)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeHead2) oStyle.setFontSize(self._fSizeHead2)
oStyle.setColor(self._colHead12) oStyle.setColor(self._colHead12)
@@ -916,7 +916,7 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopHead3) oStyle.setMarginTop(self._mTopHead3)
oStyle.setMarginBottom(self._mBotHead3) oStyle.setMarginBottom(self._mBotHead3)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeHead3) oStyle.setFontSize(self._fSizeHead3)
oStyle.setColor(self._colHead34) oStyle.setColor(self._colHead34)
@@ -937,7 +937,7 @@ class ToOdt(Tokenizer):
oStyle.setClass("text") oStyle.setClass("text")
oStyle.setMarginTop(self._mTopHead4) oStyle.setMarginTop(self._mTopHead4)
oStyle.setMarginBottom(self._mBotHead4) oStyle.setMarginBottom(self._mBotHead4)
oStyle.setFontName(self.textFont) oStyle.setFontName(self._textFont)
oStyle.setFontFamily(self._fontFamily) oStyle.setFontFamily(self._fontFamily)
oStyle.setFontSize(self._fSizeHead4) oStyle.setFontSize(self._fSizeHead4)
oStyle.setColor(self._colHead34) oStyle.setColor(self._colHead34)
@@ -972,7 +972,7 @@ class ToOdt(Tokenizer):
xPar = etree.SubElement(xHead, _mkTag("text", "p"), attrib={ xPar = etree.SubElement(xHead, _mkTag("text", "p"), attrib={
_mkTag("text", "style-name"): "Header" _mkTag("text", "style-name"): "Header"
}) })
xPar.text = self.headerText.strip() + " " xPar.text = self._headerText.strip() + " "
xTail = etree.SubElement(xPar, _mkTag("text", "page-number"), attrib={ xTail = etree.SubElement(xPar, _mkTag("text", "page-number"), attrib={
_mkTag("text", "select-page"): "current" _mkTag("text", "select-page"): "current"
+21 -19
View File
@@ -120,27 +120,29 @@ class NWErrorMessage(QDialog):
kernelVersion = "Unknown" kernelVersion = "Unknown"
try: try:
import lxml
lxmlVersion = lxml.__version__
except Exception:
lxmlVersion = "Unknown"
try:
import enchant
enchantVersion = enchant.__version__
except Exception:
enchantVersion = "Unknown"
try:
exTrace = "\n".join(format_tb(exTrace))
self.msgBody.setPlainText(( self.msgBody.setPlainText((
"Environment:\n" "Environment:\n"
"novelWriter Version: {nwVersion}\n" f"novelWriter Version: {__version__}\n"
"Host OS: {osType} ({osKernel})\n" f"Host OS: {sys.platform} ({kernelVersion})\n"
"Python: {pyVersion} ({pyHexVer:#x})\n" f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n"
"Qt: {qtVers}, PyQt: {pyqtVers}\n" f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n"
"\n" f"lxml: {lxmlVersion}\n"
"{exType}:\n{exMessage}\n" f"enchant: {enchantVersion}\n\n"
"\n" f"{exType.__name__}:\n{str(exValue)}\n\n"
"Traceback:\n{exTrace}\n" f"Traceback:\n{exTrace}\n"
).format(
nwVersion=__version__,
osType=sys.platform,
osKernel=kernelVersion,
pyVersion=sys.version.split()[0],
pyHexVer=sys.hexversion,
qtVers=QT_VERSION_STR,
pyqtVers=PYQT_VERSION_STR,
exType=exType.__name__,
exMessage=str(exValue),
exTrace="\n".join(format_tb(exTrace)),
)) ))
except Exception: except Exception:
self.msgBody.setPlainText("Failed to generate error report ...") self.msgBody.setPlainText("Failed to generate error report ...")
+3
View File
@@ -44,6 +44,7 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
# Not a valid handle # Not a valid handle
theDoc = NWDoc(theProject, "stuff") theDoc = NWDoc(theProject, "stuff")
assert bool(theDoc) is False
assert theDoc.readDocument() is None assert theDoc.readDocument() is None
# Non-existent handle # Non-existent handle
@@ -67,6 +68,8 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal):
assert nHandle is not None assert nHandle is not None
xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle) xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle)
theDoc = NWDoc(theProject, xHandle) theDoc = NWDoc(theProject, xHandle)
assert bool(theDoc) is True
assert repr(theDoc) == f"<NWDoc handle={xHandle}>"
assert theDoc.readDocument() == "" assert theDoc.readDocument() == ""
# Write Document # Write Document
+3
View File
@@ -219,6 +219,9 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI):
assert theIndex.notesChangedSince(0) is True assert theIndex.notesChangedSince(0) is True
assert theIndex.indexChangedSince(0) is True assert theIndex.indexChangedSince(0) is True
assert theIndex.getHandleHeaderLevel(cHandle) == "H1"
assert theIndex.getHandleHeaderLevel(nHandle) == "H1"
# Zero Items # Zero Items
assert theIndex.checkThese([], cItem) == [] assert theIndex.checkThese([], cItem) == []
+59 -46
View File
@@ -48,36 +48,39 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI):
theProject.projTree.setSeed(42) theProject.projTree.setSeed(42)
# Setting no data should fail # Setting no data should fail
assert not theProject.newProject({}) assert theProject.newProject({}) is False
# Wrong type should also fail
assert theProject.newProject("stuff") is False
# Try again with a proper path # Try again with a proper path
assert theProject.newProject({"projPath": fncDir}) assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
# Creating the project once more should fail # Creating the project once more should fail
assert not theProject.newProject({"projPath": fncDir}) assert theProject.newProject({"projPath": fncDir}) is False
# Check the new project # Check the new project
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
# Open again # Open again
assert theProject.openProject(projFile) assert theProject.openProject(projFile) is True
# Save and close # Save and close
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert not theProject.projChanged assert theProject.projChanged is False
# Open a second time # Open a second time
assert theProject.openProject(projFile) assert theProject.openProject(projFile) is True
assert not theProject.openProject(projFile) assert theProject.openProject(projFile) is False
assert theProject.openProject(projFile, overrideLock=True) assert theProject.openProject(projFile, overrideLock=True) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
@@ -116,9 +119,9 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42) theProject.projTree.setSeed(42)
assert theProject.newProject(projData) assert theProject.newProject(projData) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
@@ -158,9 +161,9 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42) theProject.projTree.setSeed(42)
assert theProject.newProject(projData) assert theProject.newProject(projData) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
@@ -207,11 +210,11 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir):
srcDoc = os.path.join(srcSample, "content", docFile) srcDoc = os.path.join(srcSample, "content", docFile)
zipObj.write(srcDoc, "content/"+docFile) zipObj.write(srcDoc, "content/"+docFile)
assert theProject.newProject(projData) assert theProject.newProject(projData) is True
assert theProject.openProject(fncDir) assert theProject.openProject(fncDir) is True
assert theProject.projName == "Sample Project" assert theProject.projName == "Sample Project"
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
os.unlink(dstSample) os.unlink(dstSample)
# END Test testCoreProject_NewSampleA # END Test testCoreProject_NewSampleA
@@ -242,11 +245,11 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir):
assert not theProject.newProject(projData) assert not theProject.newProject(projData)
monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx") monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx")
assert theProject.newProject(projData) assert theProject.newProject(projData) is True
assert theProject.openProject(fncDir) assert theProject.openProject(fncDir) is True
assert theProject.projName == "Sample Project" assert theProject.projName == "Sample Project"
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
# Misdirect the appRoot path so neither is possible # Misdirect the appRoot path so neither is possible
tmpConf.appRoot = tmpDir tmpConf.appRoot = tmpDir
@@ -266,11 +269,11 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42) theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
assert theProject.openProject(projFile) assert theProject.openProject(projFile) is True
assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None)) assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None))
assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None)) assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None))
@@ -281,13 +284,13 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI):
assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str) assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str)
assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str) assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str)
assert theProject.projChanged assert theProject.projChanged is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert not theProject.projChanged assert theProject.projChanged is False
# END Test testCoreProject_NewRoot # END Test testCoreProject_NewRoot
@@ -303,21 +306,21 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI):
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theProject.projTree.setSeed(42) theProject.projTree.setSeed(42)
assert theProject.newProject({"projPath": fncDir}) assert theProject.newProject({"projPath": fncDir}) is True
assert theProject.setProjectPath(fncDir) assert theProject.setProjectPath(fncDir) is True
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
assert theProject.openProject(projFile) assert theProject.openProject(projFile) is True
assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str) assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str)
assert isinstance(theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"), str) assert isinstance(theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"), str)
assert theProject.projChanged assert theProject.projChanged
assert theProject.saveProject() assert theProject.saveProject() is True
assert theProject.closeProject() assert theProject.closeProject() is True
copyfile(projFile, testFile) copyfile(projFile, testFile)
assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8])
assert not theProject.projChanged assert theProject.projChanged is False
# END Test testCoreProject_NewFile # END Test testCoreProject_NewFile
@@ -466,6 +469,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
testFile = os.path.join(nwMinimal, "nwProject.nwx") testFile = os.path.join(nwMinimal, "nwProject.nwx")
backFile = os.path.join(nwMinimal, "nwProject.bak")
compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx") compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx")
# Nothing to save # Nothing to save
@@ -476,7 +480,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
# Fail on folder structure check # Fail on folder structure check
with monkeypatch.context() as mp: with monkeypatch.context() as mp:
mp.setattr("os.path.isdir", lambda *args: False) mp.setattr("os.path.isdir", lambda *a: False)
assert theProject.saveProject() is False assert theProject.saveProject() is False
# Fail on open file # Fail on open file
@@ -484,6 +488,12 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
mp.setattr("builtins.open", causeOSError) mp.setattr("builtins.open", causeOSError)
assert theProject.saveProject() is False assert theProject.saveProject() is False
# Fail on creating .bak file
with monkeypatch.context() as mp:
mp.setattr("os.replace", causeOSError)
assert theProject.saveProject() is False
assert os.path.isfile(backFile) is False
# Successful save # Successful save
saveCount = theProject.saveCount saveCount = theProject.saveCount
autoCount = theProject.autoCount autoCount = theProject.autoCount
@@ -492,6 +502,9 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir):
assert theProject.autoCount == autoCount assert theProject.autoCount == autoCount
assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9]) assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9])
# Check that a second save creates a .bak file
assert os.path.isfile(backFile) is True
# Successful autosave # Successful autosave
saveCount = theProject.saveCount saveCount = theProject.saveCount
autoCount = theProject.autoCount autoCount = theProject.autoCount
+52 -52
View File
@@ -38,12 +38,12 @@ def testCoreToHtml_ConvertFormat(mockGUI):
# Novel Files Headers # Novel Files Headers
# =================== # ===================
theHtml.isNovel = True theHtml._isNovel = True
theHtml.isNote = False theHtml._isNote = False
theHtml.isFirst = True theHtml._isFirst = True
# Header 1 # Header 1
theHtml.theText = "# Partition\n" theHtml._theText = "# Partition\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -51,7 +51,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Header 2 # Header 2
theHtml.theText = "## Chapter Title\n" theHtml._theText = "## Chapter Title\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -59,19 +59,19 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Header 3 # Header 3
theHtml.theText = "### Scene Title\n" theHtml._theText = "### Scene Title\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h2>Scene Title</h2>\n" assert theHtml.theResult == "<h2>Scene Title</h2>\n"
# Header 4 # Header 4
theHtml.theText = "#### Section Title\n" theHtml._theText = "#### Section Title\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h3>Section Title</h3>\n" assert theHtml.theResult == "<h3>Section Title</h3>\n"
# Title # Title
theHtml.theText = "#! Title\n" theHtml._theText = "#! Title\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -79,7 +79,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Unnumbered # Unnumbered
theHtml.theText = "##! Prologue\n" theHtml._theText = "##! Prologue\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n" assert theHtml.theResult == "<h1 style='page-break-before: always;'>Prologue</h1>\n"
@@ -87,37 +87,37 @@ def testCoreToHtml_ConvertFormat(mockGUI):
# Note Files Headers # Note Files Headers
# ================== # ==================
theHtml.isNovel = False theHtml._isNovel = False
theHtml.isNote = True theHtml._isNote = True
theHtml.isFirst = True theHtml._isFirst = True
theHtml.setLinkHeaders(True) theHtml.setLinkHeaders(True)
# Header 1 # Header 1
theHtml.theText = "# Heading One\n" theHtml._theText = "# Heading One\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h1><a name='T000001'></a>Heading One</h1>\n" assert theHtml.theResult == "<h1><a name='T000001'></a>Heading One</h1>\n"
# Header 2 # Header 2
theHtml.theText = "## Heading Two\n" theHtml._theText = "## Heading Two\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n" assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
# Header 3 # Header 3
theHtml.theText = "### Heading Three\n" theHtml._theText = "### Heading Three\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h3><a name='T000001'></a>Heading Three</h3>\n" assert theHtml.theResult == "<h3><a name='T000001'></a>Heading Three</h3>\n"
# Header 4 # Header 4
theHtml.theText = "#### Heading Four\n" theHtml._theText = "#### Heading Four\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n" assert theHtml.theResult == "<h4><a name='T000001'></a>Heading Four</h4>\n"
# Title # Title
theHtml.theText = "#! Heading One\n" theHtml._theText = "#! Heading One\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -125,7 +125,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Unnumbered # Unnumbered
theHtml.theText = "##! Heading Two\n" theHtml._theText = "##! Heading Two\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n" assert theHtml.theResult == "<h2><a name='T000001'></a>Heading Two</h2>\n"
@@ -134,7 +134,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
# ========== # ==========
# Text # Text
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theHtml._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -143,7 +143,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Text w/Hard Break # Text w/Hard Break
theHtml.theText = "Line one \nLine two \nLine three\n" theHtml._theText = "Line one \nLine two \nLine three\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -151,13 +151,13 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Synopsis # Synopsis
theHtml.theText = "%synopsis: The synopsis ...\n" theHtml._theText = "%synopsis: The synopsis ...\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "" assert theHtml.theResult == ""
theHtml.setSynopsis(True) theHtml.setSynopsis(True)
theHtml.theText = "%synopsis: The synopsis ...\n" theHtml._theText = "%synopsis: The synopsis ...\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -165,13 +165,13 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Comment # Comment
theHtml.theText = "% A comment ...\n" theHtml._theText = "% A comment ...\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "" assert theHtml.theResult == ""
theHtml.setComments(True) theHtml.setComments(True)
theHtml.theText = "% A comment ...\n" theHtml._theText = "% A comment ...\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -179,13 +179,13 @@ def testCoreToHtml_ConvertFormat(mockGUI):
) )
# Keywords # Keywords
theHtml.theText = "@char: Bod, Jane\n" theHtml._theText = "@char: Bod, Jane\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == "" assert theHtml.theResult == ""
theHtml.setKeywords(True) theHtml.setKeywords(True)
theHtml.theText = "@char: Bod, Jane\n" theHtml._theText = "@char: Bod, Jane\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -195,7 +195,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
# Multiple Keywords # Multiple Keywords
theHtml.setKeywords(True) theHtml.setKeywords(True)
theHtml.theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" theHtml._theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -218,7 +218,7 @@ def testCoreToHtml_ConvertFormat(mockGUI):
theHtml.setPreview(True, True) theHtml.setPreview(True, True)
# Text (HTML4) # Text (HTML4)
theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theHtml._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -238,15 +238,15 @@ def testCoreToHtml_ConvertDirect(mockGUI):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theHtml = ToHtml(theProject) theHtml = ToHtml(theProject)
theHtml.isNovel = True theHtml._isNovel = True
theHtml.isNote = False theHtml._isNote = False
theHtml.setLinkHeaders(True) theHtml.setLinkHeaders(True)
# Special Titles # Special Titles
# ============== # ==============
# Title # Title
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE), (theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
] ]
@@ -257,7 +257,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
) )
# Unnumbered # Unnumbered
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB), (theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
] ]
@@ -271,7 +271,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# ========== # ==========
# Separator # Separator
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE), (theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
] ]
@@ -279,7 +279,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
assert theHtml.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n" assert theHtml.theResult == "<p class='sep' style='text-align: center;'>* * *</p>\n"
# Skip # Skip
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_SKIP, 1, "", None, theHtml.A_NONE), (theHtml.T_SKIP, 1, "", None, theHtml.A_NONE),
(theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE),
] ]
@@ -293,7 +293,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# Align Left # Align Left
theHtml.setStyles(False) theHtml.setStyles(False)
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -304,7 +304,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
theHtml.setStyles(True) theHtml.setStyles(True)
# Align Left # Align Left
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -313,7 +313,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
) )
# Align Right # Align Right
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -322,7 +322,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
) )
# Align Centre # Align Centre
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -331,7 +331,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
) )
# Align Justify # Align Justify
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -343,7 +343,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# ========== # ==========
# Page Break Always # Page Break Always
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA), (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA),
] ]
theHtml.doConvert() theHtml.doConvert()
@@ -356,7 +356,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
# ====== # ======
# Indent Left # Indent Left
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L), (theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L),
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
] ]
@@ -366,7 +366,7 @@ def testCoreToHtml_ConvertDirect(mockGUI):
) )
# Indent Right # Indent Right
theHtml.theTokens = [ theHtml._theTokens = [
(theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R), (theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R),
(theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE),
] ]
@@ -384,33 +384,33 @@ def testCoreToHtml_SpecialCases(mockGUI):
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theHtml = ToHtml(theProject) theHtml = ToHtml(theProject)
theHtml.isNovel = True theHtml._isNovel = True
# Greater/Lesser than symbols # Greater/Lesser than symbols
# =========================== # ===========================
theHtml.theText = "Text with > and < with some **bold text** in it.\n" theHtml._theText = "Text with > and < with some **bold text** in it.\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
"<p>Text with &gt; and &lt; with some <strong>bold text</strong> in it.</p>\n" "<p>Text with &gt; and &lt; with some <strong>bold text</strong> in it.</p>\n"
) )
theHtml.theText = "Text with some <**bold text**> in it.\n" theHtml._theText = "Text with some <**bold text**> in it.\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
"<p>Text with some &lt;<strong>bold text</strong>&gt; in it.</p>\n" "<p>Text with some &lt;<strong>bold text</strong>&gt; in it.</p>\n"
) )
theHtml.theText = "Let's > be > _difficult **shall** > we_?\n" theHtml._theText = "Let's > be > _difficult **shall** > we_?\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
"<p>Let's &gt; be &gt; <em>difficult <strong>shall</strong> &gt; we</em>?</p>\n" "<p>Let's &gt; be &gt; <em>difficult <strong>shall</strong> &gt; we</em>?</p>\n"
) )
theHtml.theText = "Test > text _<**bold**>_ and more.\n" theHtml._theText = "Test > text _<**bold**>_ and more.\n"
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
assert theHtml.theResult == ( assert theHtml.theResult == (
@@ -426,7 +426,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theHtml = ToHtml(theProject) theHtml = ToHtml(theProject)
theHtml.isNovel = True theHtml._isNovel = True
# Build Project # Build Project
# ============= # =============
@@ -472,7 +472,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir):
] ]
for i in range(len(docText)): for i in range(len(docText)):
theHtml.theText = docText[i] theHtml._theText = docText[i]
theHtml.doPreProcessing() theHtml.doPreProcessing()
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
@@ -526,7 +526,7 @@ def testCoreToHtml_Methods(mockGUI):
# Auto-Replace, keep Unicode # Auto-Replace, keep Unicode
docText = "Text with <brackets> & shortdash, long—dash …\n" docText = "Text with <brackets> & shortdash, long—dash …\n"
theHtml.theText = docText theHtml._theText = docText
theHtml.setReplaceUnicode(False) theHtml.setReplaceUnicode(False)
theHtml.doPreProcessing() theHtml.doPreProcessing()
theHtml.tokenizeText() theHtml.tokenizeText()
@@ -537,7 +537,7 @@ def testCoreToHtml_Methods(mockGUI):
# Auto-Replace, replace Unicode # Auto-Replace, replace Unicode
docText = "Text with <brackets> & shortdash, long—dash …\n" docText = "Text with <brackets> & shortdash, long—dash …\n"
theHtml.theText = docText theHtml._theText = docText
theHtml.setReplaceUnicode(True) theHtml.setReplaceUnicode(True)
theHtml.doPreProcessing() theHtml.doPreProcessing()
theHtml.tokenizeText() theHtml.tokenizeText()
@@ -548,7 +548,7 @@ def testCoreToHtml_Methods(mockGUI):
# With Preview # With Preview
theHtml.setPreview(True, True) theHtml.setPreview(True, True)
theHtml.theText = docText theHtml._theText = docText
theHtml.doPreProcessing() theHtml.doPreProcessing()
theHtml.tokenizeText() theHtml.tokenizeText()
theHtml.doConvert() theHtml.doConvert()
File diff suppressed because it is too large Load Diff
+27 -27
View File
@@ -38,42 +38,42 @@ def testCoreToMarkdown_ConvertFormat(mockGUI):
# Headers # Headers
# ======= # =======
theMD.isNovel = True theMD._isNovel = True
theMD.isNote = False theMD._isNote = False
theMD.isFirst = True theMD._isFirst = True
# Header 1 # Header 1
theMD.theText = "# Partition\n" theMD._theText = "# Partition\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "# Partition\n\n" assert theMD.theResult == "# Partition\n\n"
# Header 2 # Header 2
theMD.theText = "## Chapter Title\n" theMD._theText = "## Chapter Title\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "## Chapter Title\n\n" assert theMD.theResult == "## Chapter Title\n\n"
# Header 3 # Header 3
theMD.theText = "### Scene Title\n" theMD._theText = "### Scene Title\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "### Scene Title\n\n" assert theMD.theResult == "### Scene Title\n\n"
# Header 4 # Header 4
theMD.theText = "#### Section Title\n" theMD._theText = "#### Section Title\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "#### Section Title\n\n" assert theMD.theResult == "#### Section Title\n\n"
# Title # Title
theMD.theText = "#! Title\n" theMD._theText = "#! Title\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "# Title\n\n" assert theMD.theResult == "# Title\n\n"
# Unnumbered # Unnumbered
theMD.theText = "##! Prologue\n" theMD._theText = "##! Prologue\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "## Prologue\n\n" assert theMD.theResult == "## Prologue\n\n"
@@ -83,7 +83,7 @@ def testCoreToMarkdown_ConvertFormat(mockGUI):
# Text for GitHub Markdown # Text for GitHub Markdown
theMD.setGitHubMarkdown() theMD.setGitHubMarkdown()
theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theMD._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == ( assert theMD.theResult == (
@@ -92,7 +92,7 @@ def testCoreToMarkdown_ConvertFormat(mockGUI):
# Text for Standard Markdown # Text for Standard Markdown
theMD.setStandardMarkdown() theMD.setStandardMarkdown()
theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theMD._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == ( assert theMD.theResult == (
@@ -100,50 +100,50 @@ def testCoreToMarkdown_ConvertFormat(mockGUI):
) )
# Text w/Hard Break # Text w/Hard Break
theMD.theText = "Line one \nLine two \nLine three\n" theMD._theText = "Line one \nLine two \nLine three\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "Line one \nLine two \nLine three\n\n" assert theMD.theResult == "Line one \nLine two \nLine three\n\n"
# Synopsis # Synopsis
theMD.theText = "%synopsis: The synopsis ...\n" theMD._theText = "%synopsis: The synopsis ...\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "" assert theMD.theResult == ""
theMD.setSynopsis(True) theMD.setSynopsis(True)
theMD.theText = "%synopsis: The synopsis ...\n" theMD._theText = "%synopsis: The synopsis ...\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "**Synopsis:** The synopsis ...\n\n" assert theMD.theResult == "**Synopsis:** The synopsis ...\n\n"
# Comment # Comment
theMD.theText = "% A comment ...\n" theMD._theText = "% A comment ...\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "" assert theMD.theResult == ""
theMD.setComments(True) theMD.setComments(True)
theMD.theText = "% A comment ...\n" theMD._theText = "% A comment ...\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "**Comment:** A comment ...\n\n" assert theMD.theResult == "**Comment:** A comment ...\n\n"
# Keywords # Keywords
theMD.theText = "@char: Bod, Jane\n" theMD._theText = "@char: Bod, Jane\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "" assert theMD.theResult == ""
theMD.setKeywords(True) theMD.setKeywords(True)
theMD.theText = "@char: Bod, Jane\n" theMD._theText = "@char: Bod, Jane\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == "**Characters:** Bod, Jane\n\n" assert theMD.theResult == "**Characters:** Bod, Jane\n\n"
# Multiple Keywords # Multiple Keywords
theMD.setKeywords(True) theMD.setKeywords(True)
theMD.theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" theMD._theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n"
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
assert theMD.theResult == ( assert theMD.theResult == (
@@ -164,14 +164,14 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theMD = ToMarkdown(theProject) theMD = ToMarkdown(theProject)
theMD.isNovel = True theMD._isNovel = True
theMD.isNote = False theMD._isNote = False
# Special Titles # Special Titles
# ============== # ==============
# Title # Title
theMD.theTokens = [ theMD._theTokens = [
(theMD.T_TITLE, 1, "A Title", None, theMD.A_PBB | theMD.A_CENTRE), (theMD.T_TITLE, 1, "A Title", None, theMD.A_PBB | theMD.A_CENTRE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
] ]
@@ -179,7 +179,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
assert theMD.theResult == "# A Title\n\n" assert theMD.theResult == "# A Title\n\n"
# Unnumbered # Unnumbered
theMD.theTokens = [ theMD._theTokens = [
(theMD.T_UNNUM, 1, "Prologue", None, theMD.A_PBB), (theMD.T_UNNUM, 1, "Prologue", None, theMD.A_PBB),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
] ]
@@ -190,7 +190,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
# ========== # ==========
# Separator # Separator
theMD.theTokens = [ theMD._theTokens = [
(theMD.T_SEP, 1, "* * *", None, theMD.A_CENTRE), (theMD.T_SEP, 1, "* * *", None, theMD.A_CENTRE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
] ]
@@ -198,7 +198,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI):
assert theMD.theResult == "* * *\n\n" assert theMD.theResult == "* * *\n\n"
# Skip # Skip
theMD.theTokens = [ theMD._theTokens = [
(theMD.T_SKIP, 1, "", None, theMD.A_NONE), (theMD.T_SKIP, 1, "", None, theMD.A_NONE),
(theMD.T_EMPTY, 1, "", None, theMD.A_NONE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE),
] ]
@@ -214,7 +214,7 @@ def testCoreToMarkdown_Complex(mockGUI, fncDir):
""" """
theProject = NWProject(mockGUI) theProject = NWProject(mockGUI)
theMD = ToMarkdown(theProject) theMD = ToMarkdown(theProject)
theMD.isNovel = True theMD._isNovel = True
# Build Project # Build Project
# ============= # =============
@@ -239,7 +239,7 @@ def testCoreToMarkdown_Complex(mockGUI, fncDir):
] ]
for i in range(len(docText)): for i in range(len(docText)):
theMD.theText = docText[i] theMD._theText = docText[i]
theMD.doPreProcessing() theMD.doPreProcessing()
theMD.tokenizeText() theMD.tokenizeText()
theMD.doConvert() theMD.doConvert()
+25 -25
View File
@@ -236,7 +236,7 @@ def testCoreToOdt_Convert(mockGUI):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=True) theDoc = ToOdt(theProject, isFlat=True)
theDoc.isNovel = True theDoc._isNovel = True
def getStyle(styleName): def getStyle(styleName):
for aSet in theDoc._autoPara.values(): for aSet in theDoc._autoPara.values():
@@ -248,7 +248,7 @@ def testCoreToOdt_Convert(mockGUI):
# ======= # =======
# Header 1 # Header 1
theDoc.theText = "# Title\n" theDoc._theText = "# Title\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -261,7 +261,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Header 2 # Header 2
theDoc.theText = "## Chapter\n" theDoc._theText = "## Chapter\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -274,7 +274,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Header 3 # Header 3
theDoc.theText = "### Scene\n" theDoc._theText = "### Scene\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -287,7 +287,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Header 4 # Header 4
theDoc.theText = "#### Section\n" theDoc._theText = "#### Section\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -300,7 +300,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Title # Title
theDoc.theText = "#! Title\n" theDoc._theText = "#! Title\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -313,7 +313,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Unnumbered chapter # Unnumbered chapter
theDoc.theText = "##! Prologue\n" theDoc._theText = "##! Prologue\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -329,7 +329,7 @@ def testCoreToOdt_Convert(mockGUI):
# ========== # ==========
# Nested Text # Nested Text
theDoc.theText = "Some ~~nested **bold** and _italics_ text~~ text." theDoc._theText = "Some ~~nested **bold** and _italics_ text~~ text."
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -347,7 +347,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Hard Break # Hard Break
theDoc.theText = "Some text.\nNext line\n" theDoc._theText = "Some text.\nNext line\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -360,7 +360,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Tab # Tab
theDoc.theText = "\tItem 1\tItem 2\n" theDoc._theText = "\tItem 1\tItem 2\n"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -373,7 +373,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Tab in Format # Tab in Format
theDoc.theText = "Some **bold\ttext**" theDoc._theText = "Some **bold\ttext**"
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.initDocument() theDoc.initDocument()
theDoc.doConvert() theDoc.doConvert()
@@ -387,7 +387,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Multiple Spaces # Multiple Spaces
theDoc.theText = ( theDoc._theText = (
"### Scene\n\n" "### Scene\n\n"
"Hello World\n\n" "Hello World\n\n"
"Hello World\n\n" "Hello World\n\n"
@@ -408,7 +408,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Synopsis, Comment, Keywords # Synopsis, Comment, Keywords
theDoc.theText = ( theDoc._theText = (
"### Scene\n\n" "### Scene\n\n"
"@pov: Jane\n\n" "@pov: Jane\n\n"
"% synopsis: So it begins\n\n" "% synopsis: So it begins\n\n"
@@ -435,7 +435,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Scene Separator # Scene Separator
theDoc.theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" theDoc._theText = "### Scene One\n\nText\n\n### Scene Two\n\nText"
theDoc.setSceneFormat("* * *", False) theDoc.setSceneFormat("* * *", False)
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.doHeaders() theDoc.doHeaders()
@@ -453,7 +453,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Scene Break # Scene Break
theDoc.theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" theDoc._theText = "### Scene One\n\nText\n\n### Scene Two\n\nText"
theDoc.setSceneFormat("", False) theDoc.setSceneFormat("", False)
theDoc.tokenizeText() theDoc.tokenizeText()
theDoc.doHeaders() theDoc.doHeaders()
@@ -471,7 +471,7 @@ def testCoreToOdt_Convert(mockGUI):
) )
# Paragraph Styles # Paragraph Styles
theDoc.theText = ( theDoc._theText = (
"### Scene\n\n" "### Scene\n\n"
"@pov: Jane\n" "@pov: Jane\n"
"@char: John\n" "@char: John\n"
@@ -513,7 +513,7 @@ def testCoreToOdt_Convert(mockGUI):
assert getStyle("P8")._pAttr["margin-right"] == ["fo", "1.693cm"] assert getStyle("P8")._pAttr["margin-right"] == ["fo", "1.693cm"]
# Justified # Justified
theDoc.theText = ( theDoc._theText = (
"### Scene\n\n" "### Scene\n\n"
"Regular paragraph\n\n" "Regular paragraph\n\n"
"with\nbreak\n\n" "with\nbreak\n\n"
@@ -536,7 +536,7 @@ def testCoreToOdt_Convert(mockGUI):
assert getStyle("P9")._pAttr["text-align"] == ["fo", "left"] assert getStyle("P9")._pAttr["text-align"] == ["fo", "left"]
# Page Breaks # Page Breaks
theDoc.theText = ( theDoc._theText = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"
@@ -568,11 +568,11 @@ def testCoreToOdt_ConvertDirect(mockGUI):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=True) theDoc = ToOdt(theProject, isFlat=True)
theDoc.isNovel = True theDoc._isNovel = True
# Justified # Justified
theDoc = ToOdt(theProject, isFlat=True) theDoc = ToOdt(theProject, isFlat=True)
theDoc.theTokens = [ theDoc._theTokens = [
(theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_JUSTIFY), (theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_JUSTIFY),
(theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE), (theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE),
] ]
@@ -593,7 +593,7 @@ def testCoreToOdt_ConvertDirect(mockGUI):
# Page Break After # Page Break After
theDoc = ToOdt(theProject, isFlat=True) theDoc = ToOdt(theProject, isFlat=True)
theDoc.theTokens = [ theDoc._theTokens = [
(theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_PBA), (theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_PBA),
(theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE), (theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE),
] ]
@@ -623,12 +623,12 @@ def testCoreToOdt_SaveFlat(mockGUI, fncDir, outDir, refDir):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=True) theDoc = ToOdt(theProject, isFlat=True)
theDoc.isNovel = True theDoc._isNovel = True
assert theDoc.setLanguage(None) is False assert theDoc.setLanguage(None) is False
assert theDoc.setLanguage("nb_NO") is True assert theDoc.setLanguage("nb_NO") is True
theDoc.setColourHeaders(True) theDoc.setColourHeaders(True)
theDoc.theText = ( theDoc._theText = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"
@@ -660,9 +660,9 @@ def testCoreToOdt_SaveFull(mockGUI, fncDir, outDir, refDir):
mockGUI.theIndex = NWIndex(theProject) mockGUI.theIndex = NWIndex(theProject)
theDoc = ToOdt(theProject, isFlat=False) theDoc = ToOdt(theProject, isFlat=False)
theDoc.isNovel = True theDoc._isNovel = True
theDoc.theText = ( theDoc._theText = (
"## Chapter One\n\n" "## Chapter One\n\n"
"Text\n\n" "Text\n\n"
"## Chapter Two\n\n" "## Chapter Two\n\n"