diff --git a/nw/core/tohtml.py b/nw/core/tohtml.py
index 2b98be99..ec3cec22 100644
--- a/nw/core/tohtml.py
+++ b/nw/core/tohtml.py
@@ -102,19 +102,35 @@ class ToHtml(Tokenizer):
self.theResult = ""
thisPar = []
- for tType, tText, tFormat, tAlign in self.theTokens:
+ for tType, tText, tFormat, tStyle in self.theTokens:
# Styles
aStyle = []
- if tAlign == self.A_CENTRE:
- aStyle.append("text-align: center;")
- elif tAlign == self.A_RIGHT:
- aStyle.append("text-align: right;")
- elif tAlign == self.A_JUSTIFY:
- aStyle.append("text-align: justify;")
-
- if tType == self.T_HEAD2:
- aStyle.append("page-break-before: always;")
+ if tStyle is not None:
+ if tStyle & self.A_LEFT:
+ aStyle.append("text-align: left;")
+ if tStyle & self.A_RIGHT:
+ aStyle.append("text-align: right;")
+ if tStyle & self.A_CENTRE:
+ aStyle.append("text-align: center;")
+ if tStyle & self.A_JUSTIFY:
+ aStyle.append("text-align: justify;")
+ if tStyle & self.A_PBB:
+ aStyle.append("page-break-before: always;")
+ if tStyle & self.A_PBB_L:
+ aStyle.append("page-break-before: left;")
+ if tStyle & self.A_PBB_R:
+ aStyle.append("page-break-before: right;")
+ if tStyle & self.A_PBB_AV:
+ aStyle.append("page-break-before: avoid;")
+ if tStyle & self.A_PBA:
+ aStyle.append("page-break-after: always;")
+ if tStyle & self.A_PBA_L:
+ aStyle.append("page-break-after: left;")
+ if tStyle & self.A_PBA_R:
+ aStyle.append("page-break-after: right;")
+ if tStyle & self.A_PBA_AV:
+ aStyle.append("page-break-after: avoid;")
if len(aStyle) > 0:
hStyle = " style='%s'" % (" ".join(aStyle))
diff --git a/nw/core/tokenizer.py b/nw/core/tokenizer.py
index a39d12f2..84fbb100 100644
--- a/nw/core/tokenizer.py
+++ b/nw/core/tokenizer.py
@@ -60,10 +60,18 @@ class Tokenizer():
T_SKIP = 11 # Paragraph break
T_PBREAK = 12 # Page break
- A_LEFT = 1 # Left aligned
- A_RIGHT = 2 # Right aligned
- A_CENTRE = 3 # Centred
- A_JUSTIFY = 4 # Justified
+ A_LEFT = 1 # Left aligned
+ A_RIGHT = 2 # Right aligned
+ A_CENTRE = 4 # Centred
+ A_JUSTIFY = 8 # Justified
+ A_PBB = 16 # Page break before
+ A_PBB_L = 32 # Page break before, left
+ A_PBB_R = 64 # Page break before, right
+ A_PBB_AV = 128 # Page break, avoid
+ A_PBA = 256 # Page break after
+ A_PBA_L = 512 # Page break after, left
+ A_PBA_R = 1024 # Page break after, right
+ A_PBA_AV = 2048 # Page break, avoid
def __init__(self, theProject, theParent):
@@ -208,6 +216,13 @@ class Tokenizer():
just contains plain text. in the case of plain text, apply the
same RegExes that the syntax highlighter uses and save the
locations of these formatting tags into the token array.
+
+ The format of the token list is an entry with a four-tuple for
+ each line in the file. The tuple is as follows:
+ 1: The type of the block, self.T_*
+ 2: The text content of the block, without leading tags
+ 3: The internal formatting map of the text, self.FMT_*
+ 4: The style of the block, self.A_*
"""
# RegExes for adding formatting tags within text lines
@@ -233,23 +248,63 @@ class Tokenizer():
# Tag lines starting with specific characters
if len(aLine.strip()) == 0:
- self.theTokens.append((self.T_EMPTY, "", None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ ))
elif aLine[0] == "%":
cLine = aLine[1:].strip()
if cLine.lower().startswith("synopsis:"):
- self.theTokens.append((self.T_SYNOPSIS, cLine[9:].strip(), None, defAlign))
+ self.theTokens.append((
+ self.T_SYNOPSIS,
+ cLine[9:].strip(),
+ None,
+ defAlign
+ ))
else:
- self.theTokens.append((self.T_COMMENT, aLine[1:].strip(), None, defAlign))
+ self.theTokens.append((
+ self.T_COMMENT,
+ aLine[1:].strip(),
+ None,
+ defAlign
+ ))
elif aLine[0] == "@":
- self.theTokens.append((self.T_KEYWORD, aLine[1:].strip(), None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_KEYWORD,
+ aLine[1:].strip(),
+ None,
+ self.A_LEFT
+ ))
elif aLine[:2] == "# ":
- self.theTokens.append((self.T_HEAD1, aLine[2:].strip(), None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_HEAD1,
+ aLine[2:].strip(),
+ None,
+ self.A_LEFT | self.A_PBB
+ ))
elif aLine[:3] == "## ":
- self.theTokens.append((self.T_HEAD2, aLine[3:].strip(), None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_HEAD2,
+ aLine[3:].strip(),
+ None,
+ self.A_LEFT | self.A_PBA_AV
+ ))
elif aLine[:4] == "### ":
- self.theTokens.append((self.T_HEAD3, aLine[4:].strip(), None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_HEAD3,
+ aLine[4:].strip(),
+ None,
+ self.A_LEFT | self.A_PBA_AV
+ ))
elif aLine[:5] == "#### ":
- self.theTokens.append((self.T_HEAD4, aLine[5:].strip(), None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_HEAD4,
+ aLine[5:].strip(),
+ None,
+ self.A_LEFT | self.A_PBA_AV
+ ))
else:
if not self.doBodyText:
# Skip all body text
@@ -270,10 +325,20 @@ class Tokenizer():
# Save the line as is, but append the array of formatting locations
# sorted by position
fmtPos = sorted(fmtPos, key=itemgetter(0))
- self.theTokens.append((self.T_TEXT, aLine, fmtPos, defAlign))
+ self.theTokens.append((
+ self.T_TEXT,
+ aLine,
+ fmtPos,
+ defAlign
+ ))
# Always add an empty line at the end
- self.theTokens.append((self.T_EMPTY, "", None, self.A_LEFT))
+ self.theTokens.append((
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ ))
return
@@ -308,53 +373,126 @@ class Tokenizer():
if tType == self.T_TEXT:
self.firstScene = False
- elif tType == self.T_HEAD2:
+ elif tType == self.T_HEAD2: # Novel Chapter
if not isUnNum:
self.numChapter += 1
tText = self._formatChapter(tText,isUnNum)
- self.theTokens[n] = (tType, tText, None, self.A_LEFT)
+ self.theTokens[n] = (
+ tType,
+ tText,
+ None,
+ self.A_LEFT | self.A_PBB_R
+ )
self.firstScene = True
- elif tType == self.T_HEAD3:
+ elif tType == self.T_HEAD3: # Novel Scene
tTemp = self._formatScene(tText)
if tTemp == "" and self.hideScene:
- self.theTokens[n] = (self.T_EMPTY, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ )
elif tTemp == "" and not self.hideScene:
if self.firstScene:
- self.theTokens[n] = (self.T_EMPTY, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ )
else:
- self.theTokens[n] = (self.T_SKIP, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_SKIP,
+ "",
+ None,
+ None
+ )
elif tTemp == self.fmtScene:
if self.firstScene:
- self.theTokens[n] = (self.T_EMPTY, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ )
else:
- self.theTokens[n] = (self.T_SEP, tTemp, None, self.A_CENTRE)
+ self.theTokens[n] = (
+ self.T_SEP,
+ tTemp,
+ None,
+ self.A_CENTRE
+ )
else:
- self.theTokens[n] = (tType, tTemp, None, self.A_LEFT)
+ self.theTokens[n] = (
+ tType,
+ tTemp,
+ None,
+ self.A_LEFT | self.A_PBA_AV
+ )
self.firstScene = False
- elif tType == self.T_HEAD4:
+ elif tType == self.T_HEAD4: # Novel Section
tTemp = self._formatSection(tText)
if tTemp == "" and self.hideSection:
- self.theTokens[n] = (self.T_EMPTY, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_EMPTY,
+ "",
+ None,
+ None
+ )
elif tTemp == "" and not self.hideSection:
- self.theTokens[n] = (self.T_SKIP, "", None, self.A_LEFT)
+ self.theTokens[n] = (
+ self.T_SKIP,
+ "",
+ None,
+ None
+ )
elif tTemp == self.fmtSection:
- self.theTokens[n] = (self.T_SEP, tTemp, None, self.A_CENTRE)
+ self.theTokens[n] = (
+ self.T_SEP,
+ tTemp,
+ None,
+ self.A_CENTRE
+ )
else:
- self.theTokens[n] = (tType, tTemp, None, self.A_LEFT)
+ self.theTokens[n] = (
+ tType,
+ tTemp,
+ None,
+ self.A_LEFT | self.A_PBA_AV
+ )
- # For title page and partitions, we need to centre all text
- # and for some formats, we need a page break
+ # For title page and partitions, we need to centre all text.
+ # For partition, we also add a page break before, and for
+ # both types we always add a page break after the content.
if isTitle or isPart:
for n in range(len(self.theTokens)):
tToken = self.theTokens[n]
tType = tToken[0]
tText = tToken[1]
tFormat = tToken[2]
- self.theTokens[n] = (tType, tText, tFormat, self.A_CENTRE)
-
- self.theTokens.append((self.T_PBREAK, "", None, self.A_LEFT))
+ if isTitle:
+ self.theTokens[n] = (
+ tType,
+ tText,
+ tFormat,
+ self.A_CENTRE
+ )
+ else:
+ self.theTokens[n] = (
+ tType,
+ tText,
+ tFormat,
+ self.A_CENTRE | self.A_PBB_R
+ )
+ self.theTokens.append((
+ self.T_PBREAK,
+ "",
+ None,
+ None
+ ))
return
diff --git a/nw/gui/build.py b/nw/gui/build.py
index 455c7ec7..0cca7332 100644
--- a/nw/gui/build.py
+++ b/nw/gui/build.py
@@ -129,21 +129,6 @@ class GuiBuildNovel(QDialog):
self.titleForm.setColumnStretch(0, 1)
self.titleForm.setColumnStretch(1, 0)
- # Text Options
- # =============
- self.textGroup = QGroupBox("Text Options", self)
- self.textForm = QGridLayout(self)
- self.textGroup.setLayout(self.textForm)
-
- self.justifyText = QSwitch()
- self.justifyText.setChecked(self.optState.getBool("GuiBuildNovel", "justifyText", False))
-
- self.textForm.addWidget(QLabel("Justify text"), 0, 0)
- self.textForm.addWidget(self.justifyText, 0, 1)
-
- self.textForm.setColumnStretch(0, 1)
- self.textForm.setColumnStretch(1, 0)
-
# Build Settings
# ==============
self.buildGroup = QGroupBox("Build Overrides", self)
@@ -159,6 +144,21 @@ class GuiBuildNovel(QDialog):
self.buildForm.setColumnStretch(0, 1)
self.buildForm.setColumnStretch(1, 0)
+ # Text Options
+ # =============
+ self.textGroup = QGroupBox("Text Options", self)
+ self.textForm = QGridLayout(self)
+ self.textGroup.setLayout(self.textForm)
+
+ self.justifyText = QSwitch()
+ self.justifyText.setChecked(self.optState.getBool("GuiBuildNovel", "justifyText", False))
+
+ self.textForm.addWidget(QLabel("Justify text"), 0, 0)
+ self.textForm.addWidget(self.justifyText, 0, 1)
+
+ self.textForm.setColumnStretch(0, 1)
+ self.textForm.setColumnStretch(1, 0)
+
# Include Switches
# ================
self.includeGroup = QGroupBox("Include Non-Text Elements", self)
@@ -230,7 +230,7 @@ class GuiBuildNovel(QDialog):
self.saveODT.triggered.connect(lambda: self._saveDocument(self.FMT_ODT))
self.saveMenu.addAction(self.saveODT)
- # self.savePDF = QAction("Portable Document (.pdf)")
+ # self.savePDF = QAction("Portable Document Format (.pdf)")
# self.savePDF.triggered.connect(lambda: self._saveDocument(self.FMT_PDF))
# self.saveMenu.addAction(self.savePDF)
@@ -238,13 +238,14 @@ class GuiBuildNovel(QDialog):
self.saveHTM1.triggered.connect(lambda: self._saveDocument(self.FMT_HTM1))
self.saveMenu.addAction(self.saveHTM1)
- self.saveHTM2 = QAction("Plain HTML (.htm)")
+ self.saveHTM2 = QAction("%s HTML (.htm)" % nw.__package__)
self.saveHTM2.triggered.connect(lambda: self._saveDocument(self.FMT_HTM2))
self.saveMenu.addAction(self.saveHTM2)
- # self.saveMD = QAction("Markdown (.md)")
- # self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
- # self.saveMenu.addAction(self.saveMD)
+ if self.mainConf.verQtValue >= 51400:
+ self.saveMD = QAction("Markdown (.md)")
+ self.saveMD.triggered.connect(lambda: self._saveDocument(self.FMT_MD))
+ self.saveMenu.addAction(self.saveMD)
self.saveTXT = QAction("Plain Text (.txt)")
self.saveTXT.triggered.connect(lambda: self._saveDocument(self.FMT_TXT))
@@ -261,8 +262,8 @@ class GuiBuildNovel(QDialog):
# Assemble GUI
# ============
self.toolsBox.addWidget(self.titleGroup)
- self.toolsBox.addWidget(self.textGroup)
self.toolsBox.addWidget(self.buildGroup)
+ self.toolsBox.addWidget(self.textGroup)
self.toolsBox.addWidget(self.includeGroup)
self.toolsBox.addWidget(self.addsGroup)
self.toolsBox.addStretch(1)