Merge pull request #660 from vkbo/build_tool

Build Tool Updates
This commit is contained in:
Veronica K. Berglyd Olsen
2021-02-10 19:30:40 +00:00
committed by GitHub
16 changed files with 461 additions and 310 deletions
+3 -1
View File
@@ -1,7 +1,8 @@
# -*- coding: utf-8 -*-
from nw.constants.iso import isoLanguage, isoCountry
from nw.constants.constants import (
nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes, nwUnicode
nwConst, nwLists, nwRegEx, nwFiles, nwKeyWords, nwLabels, nwQuotes,
nwUnicode, nwHtmlUnicode
)
from nw.constants.enum import (
nwAlert, nwDocAction, nwItemClass, nwItemLayout, nwItemType, nwOutline,
@@ -19,6 +20,7 @@ __all__ = [
"nwLabels",
"nwQuotes",
"nwUnicode",
"nwHtmlUnicode",
"nwAlert",
"nwDocAction",
"nwItemClass",
+59 -2
View File
@@ -265,7 +265,7 @@ class nwUnicode:
U_LCQUO = "\u300c" # Left corner bracket
U_RCQUO = "\u300d" # Right corner bracket
U_LWCQUO = "\u300e" # Left white corner bracket
U_RECQUO = "\u300f" # Right white corner bracket
U_RWCQUO = "\u300f" # Right white corner bracket
## Punctuation
U_FGDASH = "\u2012" # Figure dash
@@ -331,7 +331,7 @@ class nwUnicode:
H_LCQUO = "「"
H_RCQUO = "」"
H_LWCQUO = "『"
H_LWCQUO = "『"
H_RWCQUO = "』"
## Punctuation
H_FGDASH = "‒"
@@ -374,3 +374,60 @@ class nwUnicode:
H_LTRIS = "◂"
# END Class nwUnicode
class nwHtmlUnicode():
U_TO_H = {
## Quotes
nwUnicode.U_QUOT : nwUnicode.H_QUOT,
nwUnicode.U_APOS : nwUnicode.H_APOS,
nwUnicode.U_LAQUO : nwUnicode.H_LAQUO,
nwUnicode.U_RAQUO : nwUnicode.H_RAQUO,
nwUnicode.U_LSQUO : nwUnicode.H_LSQUO,
nwUnicode.U_RSQUO : nwUnicode.H_RSQUO,
nwUnicode.U_SBQUO : nwUnicode.H_SBQUO,
nwUnicode.U_SUQUO : nwUnicode.H_SUQUO,
nwUnicode.U_LDQUO : nwUnicode.H_LDQUO,
nwUnicode.U_RDQUO : nwUnicode.H_RDQUO,
nwUnicode.U_BDQUO : nwUnicode.H_BDQUO,
nwUnicode.U_UDQUO : nwUnicode.H_UDQUO,
nwUnicode.U_LSAQUO : nwUnicode.H_LSAQUO,
nwUnicode.U_RSAQUO : nwUnicode.H_RSAQUO,
nwUnicode.U_BDRQUO : nwUnicode.H_BDRQUO,
nwUnicode.U_LCQUO : nwUnicode.H_LCQUO,
nwUnicode.U_RCQUO : nwUnicode.H_RCQUO,
nwUnicode.U_LWCQUO : nwUnicode.H_LWCQUO,
nwUnicode.U_RWCQUO : nwUnicode.H_RWCQUO,
## Punctuation
nwUnicode.U_FGDASH : nwUnicode.H_FGDASH,
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
nwUnicode.U_HBAR : nwUnicode.H_HBAR,
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
nwUnicode.U_MAPOSS : nwUnicode.H_MAPOSS,
nwUnicode.U_PRIME : nwUnicode.H_PRIME,
nwUnicode.U_DPRIME : nwUnicode.H_DPRIME,
## Spaces
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
nwUnicode.U_THSP : nwUnicode.H_THSP,
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
nwUnicode.U_ENSP : nwUnicode.H_ENSP,
nwUnicode.U_EMSP : nwUnicode.H_EMSP,
## Symbols
nwUnicode.U_CHECK : nwUnicode.H_CHECK,
nwUnicode.U_CROSS : nwUnicode.H_CROSS,
nwUnicode.U_BULL : nwUnicode.H_BULL,
nwUnicode.U_TRBULL : nwUnicode.H_TRBULL,
nwUnicode.U_HYBULL : nwUnicode.H_HYBULL,
nwUnicode.U_FLOWER : nwUnicode.H_FLOWER,
nwUnicode.U_PERMIL : nwUnicode.H_PERMIL,
nwUnicode.U_DEGREE : nwUnicode.H_DEGREE,
nwUnicode.U_MINUS : nwUnicode.H_MINUS,
nwUnicode.U_TIMES : nwUnicode.H_TIMES,
nwUnicode.U_DIVIDE : nwUnicode.H_DIVIDE,
}
# END Class nwHtmlUnicode
+2
View File
@@ -73,12 +73,14 @@ class OptionState():
"excludeBody",
"textFont",
"textSize",
"lineHeight",
"noStyling",
"incSynopsis",
"incComments",
"incKeywords",
"incBodyText",
"replaceTabs",
"replaceUCode",
},
"GuiOutline": {
"headerOrder",
+99 -64
View File
@@ -25,10 +25,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
import logging
import re
from nw.core.tokenizer import Tokenizer
from nw.constants import nwUnicode, nwLabels, nwKeyWords
from nw.constants import nwLabels, nwKeyWords, nwHtmlUnicode
logger = logging.getLogger(__name__)
@@ -41,27 +40,13 @@ class ToHtml(Tokenizer):
def __init__(self, theProject, theParent):
Tokenizer.__init__(self, theProject, theParent)
self.genMode = self.M_EXPORT
self.genMode = self.M_EXPORT
self.cssStyles = True
self.fullHTML = []
self.repDict = {
"<" : "&lt;",
">" : "&gt;",
"&" : "&amp;",
nwUnicode.U_ENDASH : nwUnicode.H_ENDASH,
nwUnicode.U_EMDASH : nwUnicode.H_EMDASH,
nwUnicode.U_HELLIP : nwUnicode.H_HELLIP,
nwUnicode.U_NBSP : nwUnicode.H_NBSP,
nwUnicode.U_THSP : nwUnicode.H_THSP,
nwUnicode.U_THNBSP : nwUnicode.H_THNBSP,
nwUnicode.U_MAPOSS : nwUnicode.H_RSQUO,
}
self.revDict = {}
self.reReplace = []
self.reReverse = []
self._buildRegEx()
self.fullHTML = []
# Internals
self._trMap = {}
self.setReplaceUnicode(False)
return
@@ -88,6 +73,23 @@ class ToHtml(Tokenizer):
self.cssStyles = cssStyles
return
def setReplaceUnicode(self, doReplace):
"""Set the translation map to either minimal or full unicode to
html entities replacement.
"""
# Control characters must always be replaced
self._trMap = str.maketrans({
"<" : "&lt;",
">" : "&gt;",
"&" : "&amp;",
})
if doReplace:
# Extend to all relevant Unicode characters
self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H))
return
##
# Class Methods
##
@@ -97,30 +99,12 @@ class ToHtml(Tokenizer):
"""
return sum([len(x) for x in self.fullHTML])
def doAutoReplace(self):
def doPreProcessing(self):
"""Extend the auto-replace to also properly encode some unicode
characters into their respective HTML entities.
"""
Tokenizer.doAutoReplace(self)
self.theText = self.reReplace.sub(
lambda x: self.repDict[x.group(0)], self.theText
)
return
def doPostProcessing(self):
"""Reverse the html entities replacement on the markdown text.
Otherwise, all the &something; bits will also be in there.
"""
Tokenizer.doPostProcessing(self)
if self.genMode == self.M_PREVIEW:
# Doesn't matter for preview as we don't use the markdown
return
if self.keepMarkdown:
self.theMarkdown[-1] = self.reReverse.sub(
lambda x: self.revDict[x.group(0)], self.theMarkdown[-1]
)
Tokenizer.doPreProcessing(self)
self.theText = self.theText.translate(self._trMap)
return
def doConvert(self):
@@ -331,21 +315,84 @@ class ToHtml(Tokenizer):
if not self.cssStyles:
return theStyles
mScale = self.lineHeight/1.15
textAlign = "justify" if self.doJustify else "left"
theStyles.append("body {font-family: '%s'; font-size: %dpt}" % (
self.textFont, self.textSize)
)
theStyles.append("p {text-align: %s;}" % textAlign)
theStyles.append("h1, h2 {color: rgb(66, 113, 174);}")
theStyles.append("h3, h4 {color: rgb(50, 50, 50);}")
theStyles.append("h1, h2, h3, h4 {page-break-after: avoid;}")
theStyles.append("body {font-family: '%s'; font-size: %dpt;}" % (
self.textFont, self.textSize
))
theStyles.append((
"p {"
"text-align: %s; line-height: %d%%; "
"margin-top: %.2fem; margin-bottom: %.2fem;"
"}"
) % (
textAlign,
round(100 * self.lineHeight),
mScale * self.marginText[0],
mScale * self.marginText[1],
))
theStyles.append((
"h1 {"
"color: rgb(66, 113, 174); "
"page-break-after: avoid; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;"
"}"
) % (
mScale * self.marginHead1[0], mScale * self.marginHead1[1]
))
theStyles.append((
"h2 {"
"color: rgb(66, 113, 174); "
"page-break-after: avoid; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;"
"}"
) % (
mScale * self.marginHead2[0], mScale * self.marginHead2[1]
))
theStyles.append((
"h3 {"
"color: rgb(50, 50, 50); "
"page-break-after: avoid; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;"
"}"
) % (
mScale * self.marginHead3[0], mScale * self.marginHead3[1]
))
theStyles.append((
"h4 {"
"color: rgb(50, 50, 50); "
"page-break-after: avoid; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;"
"}"
) % (
mScale * self.marginHead4[0], mScale * self.marginHead4[1]
))
theStyles.append((
".title {"
"font-size: 2.5em; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;"
"}"
) % (
mScale * self.marginTitle[0], mScale * self.marginTitle[1]
))
theStyles.append((
".sep, .skip {"
"text-align: center; "
"margin-top: %.2fem; "
"margin-bottom: %.2fem;}"
) % (
mScale, mScale
))
theStyles.append("a {color: rgb(66, 113, 174);}")
theStyles.append(".title {font-size: 2.5em;}")
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
theStyles.append(".break {text-align: left;}")
theStyles.append(".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}")
theStyles.append(".skip {margin-top: 1em; margin-bottom: 1em;}")
theStyles.append(".synopsis {font-style: italic;}")
theStyles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
@@ -403,16 +450,4 @@ class ToHtml(Tokenizer):
return retText
def _buildRegEx(self):
"""Build the regular expressions
"""
self.revDict = dict(map(reversed, self.repDict.items()))
self.reReplace = re.compile(
"|".join([re.escape(k) for k in self.repDict.keys()]), flags=re.DOTALL
)
self.reReverse = re.compile(
"|".join([re.escape(k) for k in self.revDict.keys()]), flags=re.DOTALL
)
return
# END Class ToHtml
+9 -4
View File
@@ -32,7 +32,7 @@ from PyQt5.QtCore import QRegularExpression
from nw.core.document import NWDoc
from nw.core.tools import numberToWord, numberToRoman
from nw.constants import nwConst, nwItemLayout, nwItemType, nwRegEx
from nw.constants import nwConst, nwUnicode, nwItemLayout, nwItemType, nwRegEx
logger = logging.getLogger(__name__)
@@ -86,7 +86,7 @@ class Tokenizer():
self.theResult = "" # The result of the last document
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
self.textFont = "Serif" # Output text font
@@ -297,9 +297,10 @@ class Tokenizer():
return True
def doAutoReplace(self):
"""Run through the user's auto-replace dictionary.
def doPreProcessing(self):
"""Reun trough the various replace doctionaries.
"""
# Process the user's auto-replace dictionary
if len(self.theProject.autoReplace) > 0:
repDict = {}
for aKey, aVal in self.theProject.autoReplace.items():
@@ -307,6 +308,10 @@ class Tokenizer():
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)
# Process the character translation map
trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO}
self.theText = self.theText.translate(str.maketrans(trDict))
return
def doPostProcessing(self):
+18 -16
View File
@@ -194,23 +194,25 @@ class ToOdt(Tokenizer):
self._fSizeHead = f"{round(1.15 * self.textSize):d}pt"
self._fSizeText = f"{self.textSize:d}pt"
self._mTopTitle = self._emToCm(self.marginTitle[0])
self._mTopHead1 = self._emToCm(self.marginHead1[0])
self._mTopHead2 = self._emToCm(self.marginHead2[0])
self._mTopHead3 = self._emToCm(self.marginHead3[0])
self._mTopHead4 = self._emToCm(self.marginHead4[0])
self._mTopHead = self._emToCm(self.marginHead4[0])
self._mTopText = self._emToCm(self.marginText[0])
self._mTopMeta = self._emToCm(self.marginMeta[0])
mScale = self.lineHeight/1.15
self._mBotTitle = self._emToCm(self.marginTitle[1])
self._mBotHead1 = self._emToCm(self.marginHead1[1])
self._mBotHead2 = self._emToCm(self.marginHead2[1])
self._mBotHead3 = self._emToCm(self.marginHead3[1])
self._mBotHead4 = self._emToCm(self.marginHead4[1])
self._mBotHead = self._emToCm(self.marginHead4[1])
self._mBotText = self._emToCm(self.marginText[1])
self._mBotMeta = self._emToCm(self.marginMeta[1])
self._mTopTitle = self._emToCm(mScale * self.marginTitle[0])
self._mTopHead1 = self._emToCm(mScale * self.marginHead1[0])
self._mTopHead2 = self._emToCm(mScale * self.marginHead2[0])
self._mTopHead3 = self._emToCm(mScale * self.marginHead3[0])
self._mTopHead4 = self._emToCm(mScale * self.marginHead4[0])
self._mTopHead = self._emToCm(mScale * self.marginHead4[0])
self._mTopText = self._emToCm(mScale * self.marginText[0])
self._mTopMeta = self._emToCm(mScale * self.marginMeta[0])
self._mBotTitle = self._emToCm(mScale * self.marginTitle[1])
self._mBotHead1 = self._emToCm(mScale * self.marginHead1[1])
self._mBotHead2 = self._emToCm(mScale * self.marginHead2[1])
self._mBotHead3 = self._emToCm(mScale * self.marginHead3[1])
self._mBotHead4 = self._emToCm(mScale * self.marginHead4[1])
self._mBotHead = self._emToCm(mScale * self.marginHead4[1])
self._mBotText = self._emToCm(mScale * self.marginText[1])
self._mBotMeta = self._emToCm(mScale * self.marginMeta[1])
if self.colourHead:
self._colHead12 = "#2a6099"
+185 -144
View File
@@ -41,7 +41,7 @@ from PyQt5.QtWidgets import (
qApp, QDialog, QVBoxLayout, QHBoxLayout, QTextBrowser, QPushButton, QLabel,
QLineEdit, QGroupBox, QGridLayout, QProgressBar, QMenu, QAction,
QFileDialog, QFontDialog, QSpinBox, QScrollArea, QSplitter, QWidget,
QSizePolicy
QSizePolicy, QDoubleSpinBox
)
from nw.common import fuzzyTime, makeFileNameSafe
@@ -55,15 +55,17 @@ logger = logging.getLogger(__name__)
class GuiBuildNovel(QDialog):
FMT_ODT = 1
FMT_FODT = 2
FMT_PDF = 3
FMT_HTM = 4
FMT_MD = 5
FMT_GH = 6
FMT_NWD = 7
FMT_JSON_H = 8
FMT_JSON_M = 9
FMT_PDF = 1 # Print to PDF
FMT_ODT = 2 # Open Document file
FMT_FODT = 3 # Flat Open Document file
FMT_HTM = 4 # HTML5
FMT_NWD = 5 # nW Markdown
FMT_MD = 6 # Standard Markdown
FMT_GH = 7 # GitHub Markdown
FMT_JSON_H = 8 # HTML5 wrapped in JSON
FMT_JSON_M = 9 # nW Markdown wrapped in JSON
def __init__(self, theParent, theProject):
QDialog.__init__(self, theParent)
@@ -93,6 +95,9 @@ class GuiBuildNovel(QDialog):
self.docView = GuiBuildNovelDocView(self, self.theProject)
hS = self.theTheme.fontPixelSize
wS = 2*hS
# Title Formats
# =============
@@ -164,33 +169,39 @@ class GuiBuildNovel(QDialog):
self.boxTitle.addWidget(self.fmtTitle)
self.boxChapter = QHBoxLayout()
self.boxChapter.addWidget(self.fmtChapter)
self.boxUnnumbered = QHBoxLayout()
self.boxUnnumbered.addWidget(self.fmtUnnumbered)
self.boxUnnumb = QHBoxLayout()
self.boxUnnumb.addWidget(self.fmtUnnumbered)
self.boxScene = QHBoxLayout()
self.boxScene.addWidget(self.fmtScene)
self.boxSection = QHBoxLayout()
self.boxSection.addWidget(self.fmtSection)
self.titleForm.addWidget(QLabel("Title"), 0, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(QLabel("Chapter"), 1, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxChapter, 1, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(QLabel("Unnumbered"), 2, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxUnnumbered, 2, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(QLabel("Scene"), 3, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxScene, 3, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(QLabel("Section"), 4, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxSection, 4, 1, 1, 1, Qt.AlignRight)
titleLabel = QLabel("Title")
chapterLabel = QLabel("Chapter")
unnumbLabel = QLabel("Unnumbered")
sceneLabel = QLabel("Scene")
sectionLabel = QLabel("Section")
self.titleForm.addWidget(titleLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxTitle, 0, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(chapterLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxChapter, 1, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(unnumbLabel, 2, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxUnnumb, 2, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(sceneLabel, 3, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxScene, 3, 1, 1, 1, Qt.AlignRight)
self.titleForm.addWidget(sectionLabel, 4, 0, 1, 1, Qt.AlignLeft)
self.titleForm.addLayout(self.boxSection, 4, 1, 1, 1, Qt.AlignRight)
self.titleForm.setColumnStretch(0, 0)
self.titleForm.setColumnStretch(1, 1)
# Text Options
# =============
# Font Options
# ============
self.formatGroup = QGroupBox("Formatting Options", self)
self.formatForm = QGridLayout(self)
self.formatGroup.setLayout(self.formatForm)
self.fontGroup = QGroupBox("Font Options", self)
self.fontForm = QGridLayout(self)
self.fontGroup.setLayout(self.fontForm)
## Font Family
self.textFont = QLineEdit()
@@ -204,98 +215,111 @@ class GuiBuildNovel(QDialog):
self.fontButton.clicked.connect(self._selectFont)
self.textSize = QSpinBox(self)
self.textSize.setFixedWidth(5*self.theTheme.textNWidth)
self.textSize.setFixedWidth(6*self.theTheme.textNWidth)
self.textSize.setMinimum(6)
self.textSize.setMaximum(72)
self.textSize.setSingleStep(1)
self.textSize.setToolTip(
"The size is used for PDF and printing. Other formats have no size set."
)
self.textSize.setValue(
self.optState.getInt("GuiBuildNovel", "textSize", self.mainConf.textSize)
)
self.justifyText = QSwitch()
self.justifyText.setToolTip(
"Applies to PDF, printing, HTML, and Open Document exports."
)
self.justifyText.setChecked(
self.optState.getBool("GuiBuildNovel", "justifyText", False)
)
self.noStyling = QSwitch()
self.noStyling.setToolTip(
"Disable all styling of the text."
)
self.noStyling.setChecked(
self.optState.getBool("GuiBuildNovel", "noStyling", False)
self.lineHeight = QDoubleSpinBox(self)
self.lineHeight.setFixedWidth(6*self.theTheme.textNWidth)
self.lineHeight.setMinimum(0.8)
self.lineHeight.setMaximum(3.0)
self.lineHeight.setSingleStep(0.05)
self.lineHeight.setDecimals(2)
self.lineHeight.setValue(
self.optState.getFloat("GuiBuildNovel", "lineHeight", 1.15)
)
# Dummy box due to QGridView and QLineEdit expand bug
self.boxFont = QHBoxLayout()
self.boxFont.addWidget(self.textFont)
self.formatForm.addWidget(QLabel("Font family"), 0, 0, 1, 1, Qt.AlignLeft)
self.formatForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight)
self.formatForm.addWidget(self.fontButton, 0, 2, 1, 1, Qt.AlignRight)
self.formatForm.addWidget(QLabel("Font size"), 1, 0, 1, 1, Qt.AlignLeft)
self.formatForm.addWidget(self.textSize, 1, 1, 1, 2, Qt.AlignRight)
self.formatForm.addWidget(QLabel("Justify text"), 2, 0, 1, 1, Qt.AlignLeft)
self.formatForm.addWidget(self.justifyText, 2, 1, 1, 2, Qt.AlignRight)
self.formatForm.addWidget(QLabel("Disable styling"), 3, 0, 1, 1, Qt.AlignLeft)
self.formatForm.addWidget(self.noStyling, 3, 1, 1, 2, Qt.AlignRight)
fontFamilyLabel = QLabel("Font family")
fontSizeLabel = QLabel("Font size")
lineHeightLabel = QLabel("Line height")
justifyLabel = QLabel("Justify text")
stylingLabel = QLabel("Disable styling")
self.formatForm.setColumnStretch(0, 0)
self.formatForm.setColumnStretch(1, 1)
self.formatForm.setColumnStretch(2, 0)
self.fontForm.addWidget(fontFamilyLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addLayout(self.boxFont, 0, 1, 1, 1, Qt.AlignRight)
self.fontForm.addWidget(self.fontButton, 0, 2, 1, 1, Qt.AlignRight)
self.fontForm.addWidget(fontSizeLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addWidget(self.textSize, 1, 1, 1, 2, Qt.AlignRight)
self.fontForm.addWidget(lineHeightLabel, 2, 0, 1, 1, Qt.AlignLeft)
self.fontForm.addWidget(self.lineHeight, 2, 1, 1, 2, Qt.AlignRight)
# Include Switches
# ================
self.fontForm.setColumnStretch(0, 0)
self.fontForm.setColumnStretch(1, 1)
self.fontForm.setColumnStretch(2, 0)
self.textGroup = QGroupBox("Text Options", self)
# Styling Options
# ===============
self.styleGroup = QGroupBox("Styling Options", self)
self.styleForm = QGridLayout(self)
self.styleGroup.setLayout(self.styleForm)
self.justifyText = QSwitch(width=wS, height=hS)
self.justifyText.setChecked(
self.optState.getBool("GuiBuildNovel", "justifyText", False)
)
self.noStyling = QSwitch(width=wS, height=hS)
self.noStyling.setChecked(
self.optState.getBool("GuiBuildNovel", "noStyling", False)
)
self.styleForm.addWidget(justifyLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.styleForm.addWidget(self.justifyText, 1, 1, 1, 2, Qt.AlignRight)
self.styleForm.addWidget(stylingLabel, 2, 0, 1, 1, Qt.AlignLeft)
self.styleForm.addWidget(self.noStyling, 2, 1, 1, 2, Qt.AlignRight)
self.styleForm.setColumnStretch(0, 0)
self.styleForm.setColumnStretch(1, 1)
# Include Options
# ===============
self.textGroup = QGroupBox("Include Options", self)
self.textForm = QGridLayout(self)
self.textGroup.setLayout(self.textForm)
self.includeSynopsis = QSwitch()
self.includeSynopsis.setToolTip(
"Include synopsis comments in the output."
)
self.includeSynopsis = QSwitch(width=wS, height=hS)
self.includeSynopsis.setChecked(
self.optState.getBool("GuiBuildNovel", "incSynopsis", False)
)
self.includeComments = QSwitch()
self.includeComments.setToolTip(
"Include plain comments in the output."
)
self.includeComments = QSwitch(width=wS, height=hS)
self.includeComments.setChecked(
self.optState.getBool("GuiBuildNovel", "incComments", False)
)
self.includeKeywords = QSwitch()
self.includeKeywords.setToolTip(
"Include meta keywords (tags, references) in the output."
)
self.includeKeywords = QSwitch(width=wS, height=hS)
self.includeKeywords.setChecked(
self.optState.getBool("GuiBuildNovel", "incKeywords", False)
)
self.includeBody = QSwitch()
self.includeBody.setToolTip(
"Include body text in the output."
)
self.includeBody = QSwitch(width=wS, height=hS)
self.includeBody.setChecked(
self.optState.getBool("GuiBuildNovel", "incBodyText", True)
)
self.textForm.addWidget(QLabel("Include synopsis"), 0, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(QLabel("Include comments"), 1, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeComments, 1, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(QLabel("Include keywords"), 2, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeKeywords, 2, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(QLabel("Include body text"), 3, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeBody, 3, 1, 1, 1, Qt.AlignRight)
synopsisLabel = QLabel("Include synopsis")
commentsLabel = QLabel("Include comments")
keywordsLabel = QLabel("Include keywords")
bodyLabel = QLabel("Include body text")
self.textForm.addWidget(synopsisLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeSynopsis, 0, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(commentsLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeComments, 1, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(keywordsLabel, 2, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeKeywords, 2, 1, 1, 1, Qt.AlignRight)
self.textForm.addWidget(bodyLabel, 3, 0, 1, 1, Qt.AlignLeft)
self.textForm.addWidget(self.includeBody, 3, 1, 1, 1, Qt.AlignRight)
self.textForm.setColumnStretch(0, 1)
self.textForm.setColumnStretch(1, 0)
@@ -307,7 +331,7 @@ class GuiBuildNovel(QDialog):
self.fileForm = QGridLayout(self)
self.fileGroup.setLayout(self.fileForm)
self.novelFiles = QSwitch()
self.novelFiles = QSwitch(width=wS, height=hS)
self.novelFiles.setToolTip(
"Include files with layouts 'Book', 'Page', 'Partition', "
"'Chapter', 'Unnumbered', and 'Scene'."
@@ -316,13 +340,13 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "addNovel", True)
)
self.noteFiles = QSwitch()
self.noteFiles = QSwitch(width=wS, height=hS)
self.noteFiles.setToolTip("Include files with layout 'Note'.")
self.noteFiles.setChecked(
self.optState.getBool("GuiBuildNovel", "addNotes", False)
)
self.ignoreFlag = QSwitch()
self.ignoreFlag = QSwitch(width=wS, height=hS)
self.ignoreFlag.setToolTip(
"Ignore the 'Include when building project' setting and include "
"all files in the output."
@@ -331,12 +355,16 @@ class GuiBuildNovel(QDialog):
self.optState.getBool("GuiBuildNovel", "ignoreFlag", False)
)
self.fileForm.addWidget(QLabel("Include novel files"), 0, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
self.fileForm.addWidget(QLabel("Include note files"), 1, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
self.fileForm.addWidget(QLabel("Ignore export flag"), 2, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
novelLabel = QLabel("Include novel files")
notesLabel = QLabel("Include note files")
exportLabel = QLabel("Ignore export flag")
self.fileForm.addWidget(novelLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.novelFiles, 0, 1, 1, 1, Qt.AlignRight)
self.fileForm.addWidget(notesLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.noteFiles, 1, 1, 1, 1, Qt.AlignRight)
self.fileForm.addWidget(exportLabel, 2, 0, 1, 1, Qt.AlignLeft)
self.fileForm.addWidget(self.ignoreFlag, 2, 1, 1, 1, Qt.AlignRight)
self.fileForm.setColumnStretch(0, 1)
self.fileForm.setColumnStretch(1, 0)
@@ -348,16 +376,23 @@ class GuiBuildNovel(QDialog):
self.exportForm = QGridLayout(self)
self.exportGroup.setLayout(self.exportForm)
self.replaceTabs = QSwitch()
self.replaceTabs.setToolTip(
"Replace all tabs with eight spaces."
)
self.replaceTabs = QSwitch(width=wS, height=hS)
self.replaceTabs.setChecked(
self.optState.getBool("GuiBuildNovel", "replaceTabs", False)
)
self.exportForm.addWidget(QLabel("Replace tabs with spaces"), 0, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight)
self.replaceUCode = QSwitch(width=wS, height=hS)
self.replaceUCode.setChecked(
self.optState.getBool("GuiBuildNovel", "replaceUCode", False)
)
tabsLabel = QLabel("Replace tabs with spaces")
uCodeLabel = QLabel("Replace Unicode in HTML")
self.exportForm.addWidget(tabsLabel, 0, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceTabs, 0, 1, 1, 1, Qt.AlignRight)
self.exportForm.addWidget(uCodeLabel, 1, 0, 1, 1, Qt.AlignLeft)
self.exportForm.addWidget(self.replaceUCode, 1, 1, 1, 1, Qt.AlignRight)
self.exportForm.setColumnStretch(0, 1)
self.exportForm.setColumnStretch(1, 0)
@@ -447,7 +482,8 @@ class GuiBuildNovel(QDialog):
# The Tool Box
self.toolsBox = QVBoxLayout()
self.toolsBox.addWidget(self.titleGroup)
self.toolsBox.addWidget(self.formatGroup)
self.toolsBox.addWidget(self.fontGroup)
self.toolsBox.addWidget(self.styleGroup)
self.toolsBox.addWidget(self.textGroup)
self.toolsBox.addWidget(self.fileGroup)
self.toolsBox.addWidget(self.exportGroup)
@@ -607,6 +643,7 @@ class GuiBuildNovel(QDialog):
fmtSection = self.fmtSection.text().strip()
textFont = self.textFont.text()
textSize = self.textSize.value()
lineHeight = self.lineHeight.value()
justifyText = self.justifyText.isChecked()
noStyling = self.noStyling.isChecked()
incSynopsis = self.includeSynopsis.isChecked()
@@ -616,6 +653,7 @@ class GuiBuildNovel(QDialog):
noteFiles = self.noteFiles.isChecked()
ignoreFlag = self.ignoreFlag.isChecked()
includeBody = self.includeBody.isChecked()
replaceUCode = self.replaceUCode.isChecked()
# Get font information
fontInfo = QFontInfo(QFont(textFont, textSize))
@@ -632,6 +670,7 @@ class GuiBuildNovel(QDialog):
bldObj.setFont(textFont, textSize, textFixed)
bldObj.setJustify(justifyText)
bldObj.setLineHeight(lineHeight)
bldObj.setSynopsis(incSynopsis)
bldObj.setComments(incComments)
@@ -640,6 +679,7 @@ class GuiBuildNovel(QDialog):
if isHtml:
bldObj.setStyles(not noStyling)
bldObj.setReplaceUnicode(replaceUCode)
if isOdt:
bldObj.setColourHeaders(not noStyling)
@@ -668,7 +708,7 @@ class GuiBuildNovel(QDialog):
elif self._checkInclude(tItem, noteFiles, novelFiles, ignoreFlag):
bldObj.setText(tItem.itemHandle)
bldObj.doAutoReplace()
bldObj.doPreProcessing()
bldObj.tokenizeText()
bldObj.doHeaders()
if doConvert:
@@ -1012,14 +1052,14 @@ class GuiBuildNovel(QDialog):
nw.logException()
return False
if "htmlText" in theData.keys():
self.htmlText = theData["htmlText"]
dataCount += 1
if "buildTime" in theData.keys():
self.buildTime = theData["buildTime"]
if "htmlStyle" in theData.keys():
self.htmlStyle = theData["htmlStyle"]
dataCount += 1
if "buildTime" in theData.keys():
self.buildTime = theData["buildTime"]
if "htmlText" in theData.keys():
self.htmlText = theData["htmlText"]
dataCount += 1
return dataCount == 2
@@ -1032,9 +1072,9 @@ class GuiBuildNovel(QDialog):
try:
with open(buildCache, mode="w+", encoding="utf8") as outFile:
outFile.write(json.dumps({
"htmlText" : self.htmlText,
"htmlStyle" : self.htmlStyle,
"buildTime" : self.buildTime,
"htmlStyle" : self.htmlStyle,
"htmlText" : self.htmlText,
}, indent=2))
except Exception:
logger.error("Failed to save build cache")
@@ -1079,46 +1119,47 @@ class GuiBuildNovel(QDialog):
"section" : self.fmtSection.text().strip(),
})
winWidth = self.mainConf.rpxInt(self.width())
winHeight = self.mainConf.rpxInt(self.height())
justifyText = self.justifyText.isChecked()
noStyling = self.noStyling.isChecked()
textFont = self.textFont.text()
textSize = self.textSize.value()
novelFiles = self.novelFiles.isChecked()
noteFiles = self.noteFiles.isChecked()
ignoreFlag = self.ignoreFlag.isChecked()
incSynopsis = self.includeSynopsis.isChecked()
incComments = self.includeComments.isChecked()
incKeywords = self.includeKeywords.isChecked()
incBodyText = self.includeBody.isChecked()
replaceTabs = self.replaceTabs.isChecked()
winWidth = self.mainConf.rpxInt(self.width())
winHeight = self.mainConf.rpxInt(self.height())
justifyText = self.justifyText.isChecked()
noStyling = self.noStyling.isChecked()
textFont = self.textFont.text()
textSize = self.textSize.value()
lineHeight = self.lineHeight.value()
novelFiles = self.novelFiles.isChecked()
noteFiles = self.noteFiles.isChecked()
ignoreFlag = self.ignoreFlag.isChecked()
incSynopsis = self.includeSynopsis.isChecked()
incComments = self.includeComments.isChecked()
incKeywords = self.includeKeywords.isChecked()
incBodyText = self.includeBody.isChecked()
replaceTabs = self.replaceTabs.isChecked()
replaceUCode = self.replaceUCode.isChecked()
mainSplit = self.mainSplit.sizes()
if len(mainSplit) == 2:
boxWidth = self.mainConf.rpxInt(mainSplit[0])
docWidth = self.mainConf.rpxInt(mainSplit[1])
else:
boxWidth = 100
docWidth = 100
boxWidth = self.mainConf.rpxInt(mainSplit[0])
docWidth = self.mainConf.rpxInt(mainSplit[1])
# GUI Settings
self.optState.setValue("GuiBuildNovel", "winWidth", winWidth)
self.optState.setValue("GuiBuildNovel", "winHeight", winHeight)
self.optState.setValue("GuiBuildNovel", "boxWidth", boxWidth)
self.optState.setValue("GuiBuildNovel", "docWidth", docWidth)
self.optState.setValue("GuiBuildNovel", "justifyText", justifyText)
self.optState.setValue("GuiBuildNovel", "noStyling", noStyling)
self.optState.setValue("GuiBuildNovel", "textFont", textFont)
self.optState.setValue("GuiBuildNovel", "textSize", textSize)
self.optState.setValue("GuiBuildNovel", "addNovel", novelFiles)
self.optState.setValue("GuiBuildNovel", "addNotes", noteFiles)
self.optState.setValue("GuiBuildNovel", "ignoreFlag", ignoreFlag)
self.optState.setValue("GuiBuildNovel", "incSynopsis", incSynopsis)
self.optState.setValue("GuiBuildNovel", "incComments", incComments)
self.optState.setValue("GuiBuildNovel", "incKeywords", incKeywords)
self.optState.setValue("GuiBuildNovel", "incBodyText", incBodyText)
self.optState.setValue("GuiBuildNovel", "replaceTabs", replaceTabs)
self.optState.setValue("GuiBuildNovel", "winWidth", winWidth)
self.optState.setValue("GuiBuildNovel", "winHeight", winHeight)
self.optState.setValue("GuiBuildNovel", "boxWidth", boxWidth)
self.optState.setValue("GuiBuildNovel", "docWidth", docWidth)
self.optState.setValue("GuiBuildNovel", "justifyText", justifyText)
self.optState.setValue("GuiBuildNovel", "noStyling", noStyling)
self.optState.setValue("GuiBuildNovel", "textFont", textFont)
self.optState.setValue("GuiBuildNovel", "textSize", textSize)
self.optState.setValue("GuiBuildNovel", "lineHeight", lineHeight)
self.optState.setValue("GuiBuildNovel", "addNovel", novelFiles)
self.optState.setValue("GuiBuildNovel", "addNotes", noteFiles)
self.optState.setValue("GuiBuildNovel", "ignoreFlag", ignoreFlag)
self.optState.setValue("GuiBuildNovel", "incSynopsis", incSynopsis)
self.optState.setValue("GuiBuildNovel", "incComments", incComments)
self.optState.setValue("GuiBuildNovel", "incKeywords", incKeywords)
self.optState.setValue("GuiBuildNovel", "incBodyText", incBodyText)
self.optState.setValue("GuiBuildNovel", "replaceTabs", replaceTabs)
self.optState.setValue("GuiBuildNovel", "replaceUCode", replaceUCode)
self.optState.saveSettings()
return
+1 -1
View File
@@ -176,7 +176,7 @@ class GuiDocViewer(QTextBrowser):
# See issue #298
try:
aDoc.setText(tHandle)
aDoc.doAutoReplace()
aDoc.doPreProcessing()
aDoc.tokenizeText()
aDoc.doConvert()
aDoc.doPostProcessing()
@@ -5,17 +5,17 @@
<title>Lorem Ipsum</title>
</head>
<style>
body {font-family: 'DejaVu Sans'; font-size: 11pt}
p {text-align: left;}
h1, h2 {color: rgb(66, 113, 174);}
h3, h4 {color: rgb(50, 50, 50);}
h1, h2, h3, h4 {page-break-after: avoid;}
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
p {text-align: left; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
a {color: rgb(66, 113, 174);}
.title {font-size: 2.5em;}
.tags {color: rgb(245, 135, 31); font-weight: bold;}
.break {text-align: left;}
.sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}
.skip {margin-top: 1em; margin-bottom: 1em;}
.synopsis {font-style: italic;}
.comment {font-style: italic; color: rgb(100, 100, 100);}
article {width: 800px; margin: 40px auto;}
@@ -24,8 +24,8 @@ article {width: 800px; margin: 40px auto;}
<article>
<h1 class='title' style='text-align: center; page-break-before: auto;'>Lorem Ipsum</h1>
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
<p style='text-align: center;'>“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit&hellip;</p>
<p style='text-align: center;'>“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain&hellip;</p>
<p style='text-align: center;'>“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit</p>
<p style='text-align: center;'>“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain</p>
<p style='text-align: left;'>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of “de Finibus Bonorum et Malorum” (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, “Lorem ipsum dolor sit amet..”, comes from a line in section 1.10.32.</p>
<p style='text-align: left;'>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from “de Finibus Bonorum et Malorum” by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<h1 style='page-break-before: always;'>Prologue</h1>
@@ -5,17 +5,17 @@
<title>Lorem Ipsum</title>
</head>
<style>
body {font-family: 'DejaVu Sans'; font-size: 11pt}
p {text-align: justify;}
h1, h2 {color: rgb(66, 113, 174);}
h3, h4 {color: rgb(50, 50, 50);}
h1, h2, h3, h4 {page-break-after: avoid;}
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
a {color: rgb(66, 113, 174);}
.title {font-size: 2.5em;}
.tags {color: rgb(245, 135, 31); font-weight: bold;}
.break {text-align: left;}
.sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}
.skip {margin-top: 1em; margin-bottom: 1em;}
.synopsis {font-style: italic;}
.comment {font-style: italic; color: rgb(100, 100, 100);}
article {width: 800px; margin: 40px auto;}
@@ -24,16 +24,16 @@ article {width: 800px; margin: 40px auto;}
<article>
<h1 class='title' style='text-align: center; page-break-before: auto;'>Lorem Ipsum</h1>
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
<p style='text-align: center;'>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit&hellip;</p>
<p style='text-align: center;'>There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain&hellip;</p>
<p style='text-align: center;'>&ldquo;Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit&hellip;&rdquo;</p>
<p style='text-align: center;'>&ldquo;There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain&hellip;&rdquo;</p>
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
<p style='text-align: left;'>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of de Finibus Bonorum et Malorum (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, Lorem ipsum dolor sit amet.., comes from a line in section 1.10.32.</p>
<p style='text-align: left;'>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from de Finibus Bonorum et Malorum by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<p style='text-align: left;'>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of &ldquo;de Finibus Bonorum et Malorum&rdquo; (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, &ldquo;Lorem ipsum dolor sit amet..&rdquo;, comes from a line in section 1.10.32.</p>
<p style='text-align: left;'>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from &ldquo;de Finibus Bonorum et Malorum&rdquo; by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<h1 style='page-break-before: always;'>Prologue</h1>
<p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p>
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry&#39;s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h1 style='text-align: center; page-break-before: always;'>Act One</h1>
<p style='text-align: center;'>Fusce maximus felis libero</p>
<p style='text-align: center;'>&ldquo;Fusce maximus felis libero&rdquo;</p>
<h1 style='page-break-before: always;'>Chapter One: Chapter One</h1>
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
@@ -65,8 +65,8 @@ article {width: 800px; margin: 40px auto;}
<h1 style='page-break-before: always;'>Chapter Two: Why do we use it?</h1>
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
<p>&#09;It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</p>
<p>&#09;The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</p>
<p>&#09;Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
<p>&#09;The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using &#39;Content here, content here&#39;, making it look like readable English.</p>
<p>&#09;Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for &#39;lorem ipsum&#39; will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
<h1 style='page-break-before: always;'>Chapter Three: Chapter Two</h1>
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
@@ -5,17 +5,17 @@
<title>Lorem Ipsum</title>
</head>
<style>
body {font-family: 'DejaVu Sans'; font-size: 11pt}
p {text-align: justify;}
h1, h2 {color: rgb(66, 113, 174);}
h3, h4 {color: rgb(50, 50, 50);}
h1, h2, h3, h4 {page-break-after: avoid;}
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
a {color: rgb(66, 113, 174);}
.title {font-size: 2.5em;}
.tags {color: rgb(245, 135, 31); font-weight: bold;}
.break {text-align: left;}
.sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}
.skip {margin-top: 1em; margin-bottom: 1em;}
.synopsis {font-style: italic;}
.comment {font-style: italic; color: rgb(100, 100, 100);}
article {width: 800px; margin: 40px auto;}
@@ -24,16 +24,16 @@ article {width: 800px; margin: 40px auto;}
<article>
<h1 class='title' style='text-align: center; page-break-before: auto;'>Lorem Ipsum</h1>
<p style='text-align: center;'><strong>By lipsum.com</strong></p>
<p style='text-align: center;'>Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit&hellip;</p>
<p style='text-align: center;'>There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain&hellip;</p>
<p style='text-align: center;'>&ldquo;Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit&hellip;&rdquo;</p>
<p style='text-align: center;'>&ldquo;There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain&hellip;&rdquo;</p>
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
<p style='text-align: left;'>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of de Finibus Bonorum et Malorum (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, Lorem ipsum dolor sit amet.., comes from a line in section 1.10.32.</p>
<p style='text-align: left;'>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from de Finibus Bonorum et Malorum by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<p style='text-align: left;'>Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of &ldquo;de Finibus Bonorum et Malorum&rdquo; (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, &ldquo;Lorem ipsum dolor sit amet..&rdquo;, comes from a line in section 1.10.32.</p>
<p style='text-align: left;'>The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from &ldquo;de Finibus Bonorum et Malorum&rdquo; by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham.</p>
<h1 style='page-break-before: always;'>Prologue</h1>
<p class='synopsis'><strong>Synopsis:</strong> Explanation from the lipsum.com website.</p>
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<p><em>Lorem Ipsum</em> is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry&#39;s standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum.</p>
<h1 style='text-align: center; page-break-before: always;'>Act One</h1>
<p style='text-align: center;'>Fusce maximus felis libero</p>
<p style='text-align: center;'>&ldquo;Fusce maximus felis libero&rdquo;</p>
<h1 style='page-break-before: always;'>Chapter One: Chapter One</h1>
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
@@ -65,8 +65,8 @@ article {width: 800px; margin: 40px auto;}
<h1 style='page-break-before: always;'>Chapter Two: Why do we use it?</h1>
<p class='comment'><strong>Comment:</strong> Exctracted from the lipsum.com website.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;It is a long established fact that a reader will be distracted by the readable content of a page when looking at its layout.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using 'Content here, content here', making it look like readable English.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for 'lorem ipsum' will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;The point of using Lorem Ipsum is that it has a more-or-less normal distribution of letters, as opposed to using &#39;Content here, content here&#39;, making it look like readable English.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Many desktop publishing packages and web page editors now use Lorem Ipsum as their default model text, and a search for &#39;lorem ipsum&#39; will uncover many web sites still in their infancy. Various versions have evolved over the years, sometimes by accident, sometimes on purpose (injected humour and the like).</p>
<h1 style='page-break-before: always;'>Chapter Three: Chapter Two</h1>
<p style='margin-bottom: 0;'><span class='tags'>Point of View:</span> <a href='#tag_Bod'>Bod</a></p>
<p style='margin-bottom: 0; margin-top: 0;'><span class='tags'>Plot:</span> <a href='#tag_Main'>Main</a></p>
@@ -5,21 +5,21 @@
"authors": [
"lipsum.com"
],
"buildTime": 1611863279
"buildTime": 1612954696
},
"text": {
"css": [
"body {font-family: 'DejaVu Sans'; font-size: 11pt}",
"p {text-align: justify;}",
"h1, h2 {color: rgb(66, 113, 174);}",
"h3, h4 {color: rgb(50, 50, 50);}",
"h1, h2, h3, h4 {page-break-after: avoid;}",
"body {font-family: 'DejaVu Sans'; font-size: 11pt;}",
"p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}",
"h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}",
"h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}",
"h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}",
"h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}",
".title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}",
".sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}",
"a {color: rgb(66, 113, 174);}",
".title {font-size: 2.5em;}",
".tags {color: rgb(245, 135, 31); font-weight: bold;}",
".break {text-align: left;}",
".sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}",
".skip {margin-top: 1em; margin-bottom: 1em;}",
".synopsis {font-style: italic;}",
".comment {font-style: italic; color: rgb(100, 100, 100);}"
],
@@ -5,17 +5,17 @@
<title>Lorem Ipsum</title>
</head>
<style>
body {font-family: 'DejaVu Sans'; font-size: 11pt}
p {text-align: justify;}
h1, h2 {color: rgb(66, 113, 174);}
h3, h4 {color: rgb(50, 50, 50);}
h1, h2, h3, h4 {page-break-after: avoid;}
body {font-family: 'DejaVu Sans'; font-size: 11pt;}
p {text-align: justify; line-height: 115%; margin-top: 0.00em; margin-bottom: 0.58em;}
h1 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 1.00em; margin-bottom: 0.50em;}
h2 {color: rgb(66, 113, 174); page-break-after: avoid; margin-top: 0.83em; margin-bottom: 0.50em;}
h3 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
h4 {color: rgb(50, 50, 50); page-break-after: avoid; margin-top: 0.58em; margin-bottom: 0.50em;}
.title {font-size: 2.5em; margin-top: 1.00em; margin-bottom: 0.50em;}
.sep, .skip {text-align: center; margin-top: 1.00em; margin-bottom: 1.00em;}
a {color: rgb(66, 113, 174);}
.title {font-size: 2.5em;}
.tags {color: rgb(245, 135, 31); font-weight: bold;}
.break {text-align: left;}
.sep {text-align: center; margin-top: 1em; margin-bottom: 1em;}
.skip {margin-top: 1em; margin-bottom: 1em;}
.synopsis {font-style: italic;}
.comment {font-style: italic; color: rgb(100, 100, 100);}
article {width: 800px; margin: 40px auto;}
+22 -17
View File
@@ -374,7 +374,7 @@ def testCoreToHtml_Complex(dummyGUI, fncDir):
for i in range(len(docText)):
theHtml.theText = docText[i]
theHtml.doAutoReplace()
theHtml.doPreProcessing()
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == resText[i]
@@ -424,27 +424,32 @@ def testCoreToHtml_Methods(dummyGUI):
theHtml = ToHtml(theProject, dummyGUI)
theHtml.setKeepMarkdown(True)
# Auto-Replace
# Auto-Replace, keep Unicode
docText = "Text with <brackets> & shortdash, long—dash …\n"
theHtml.theText = docText
theHtml.doAutoReplace()
theHtml.setReplaceUnicode(False)
theHtml.doPreProcessing()
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<p>Text with &lt;brackets&gt; &amp; shortdash, long—dash …</p>\n"
)
# Auto-Replace, replace Unicode
docText = "Text with <brackets> & shortdash, long—dash …\n"
theHtml.theText = docText
theHtml.setReplaceUnicode(True)
theHtml.doPreProcessing()
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theResult == (
"<p>Text with &lt;brackets&gt; &amp; short&ndash;dash, long&mdash;dash &hellip;</p>\n"
)
# Revert on MD
assert theHtml.theMarkdown[-1] == (
"Text with &lt;brackets&gt; &amp; short&ndash;dash, long&mdash;dash &hellip;\n\n"
)
theHtml.doPostProcessing()
assert theHtml.theMarkdown[-1] == docText + "\n"
# With Preview, No Revert
# With Preview
theHtml.setPreview(True, True)
theHtml.theText = docText
theHtml.doAutoReplace()
theHtml.doPreProcessing()
theHtml.tokenizeText()
theHtml.doConvert()
assert theHtml.theMarkdown[-1] == (
@@ -456,18 +461,18 @@ def testCoreToHtml_Methods(dummyGUI):
)
# Result Size
assert theHtml.getFullResultSize() == 83
assert theHtml.getFullResultSize() == 147
# CSS
# ===
assert len(theHtml.getStyleSheet()) > 1
assert "p {text-align: left;}" in theHtml.getStyleSheet()
assert "p {text-align: justify;}" not in theHtml.getStyleSheet()
assert "p {text-align: left;" in " ".join(theHtml.getStyleSheet())
assert "p {text-align: justify;" not in " ".join(theHtml.getStyleSheet())
theHtml.setJustify(True)
assert "p {text-align: left;}" not in theHtml.getStyleSheet()
assert "p {text-align: justify;}" in theHtml.getStyleSheet()
assert "p {text-align: left;" not in " ".join(theHtml.getStyleSheet())
assert "p {text-align: justify;" in " ".join(theHtml.getStyleSheet())
theHtml.setStyles(False)
assert theHtml.getStyleSheet() == []
+2 -2
View File
@@ -176,8 +176,8 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, dummyGUI):
assert theToken.isNote is False
assert theToken.isNovel is True
# Auto replace
theToken.doAutoReplace()
# Pre Processing
theToken.doPreProcessing()
assert theToken.theText == docTextR
# Post Processing
+2
View File
@@ -110,6 +110,8 @@ def testGuiBuild_Tool(qtbot, monkeypatch, nwGUI, nwLipsum, refDir, outDir):
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.includeKeywords, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.replaceUCode, Qt.LeftButton)
qtbot.wait(stepDelay)
qtbot.mouseClick(nwBuild.noteFiles, Qt.LeftButton)
qtbot.wait(stepDelay)