Add forced line breaks feature (#2063)
This commit is contained in:
@@ -3,7 +3,7 @@
|
|||||||
"Short Description": "Short Description",
|
"Short Description": "Short Description",
|
||||||
"Footnotes": "Footnotes",
|
"Footnotes": "Footnotes",
|
||||||
"Comment": "Comment",
|
"Comment": "Comment",
|
||||||
"Note": "Note",
|
"Notes": "Notes",
|
||||||
"Tag": "Tag",
|
"Tag": "Tag",
|
||||||
"Point of View": "Point of View",
|
"Point of View": "Point of View",
|
||||||
"Focus": "Focus",
|
"Focus": "Focus",
|
||||||
|
|||||||
@@ -61,10 +61,11 @@ class nwConst:
|
|||||||
class nwRegEx:
|
class nwRegEx:
|
||||||
|
|
||||||
WORDS = r"\b[^\s\-\+\/–—\[\]:]+\b"
|
WORDS = r"\b[^\s\-\+\/–—\[\]:]+\b"
|
||||||
|
BREAK = r"(?i)(?<!\\)(\[br\]\n?)"
|
||||||
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
FMT_EI = r"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||||
FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
FMT_EB = r"(?<![\w\\])(\*{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||||
FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
FMT_ST = r"(?<![\w\\])(~{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||||
FMT_SC = r"(?i)(?<!\\)(\[[\/\!]?(?:b|i|s|u|m|sup|sub)\])"
|
FMT_SC = r"(?i)(?<!\\)(\[(?:b|/b|i|/i|s|/s|u|/u|m|/m|sup|/sup|sub|/sub|br)\])"
|
||||||
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])"
|
FMT_SV = r"(?i)(?<!\\)(\[(?:footnote):)(.+?)(?<!\\)(\])"
|
||||||
|
|
||||||
|
|
||||||
@@ -84,6 +85,7 @@ class nwShortcode:
|
|||||||
SUP_C = "[/sup]"
|
SUP_C = "[/sup]"
|
||||||
SUB_O = "[sub]"
|
SUB_O = "[sub]"
|
||||||
SUB_C = "[/sub]"
|
SUB_C = "[/sub]"
|
||||||
|
BREAK = "[br]"
|
||||||
|
|
||||||
FOOTNOTE_B = "[footnote:"
|
FOOTNOTE_B = "[footnote:"
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ SETTINGS_TEMPLATE: dict[str, tuple[type, str | int | float | bool]] = {
|
|||||||
"headings.centerPart": (bool, True),
|
"headings.centerPart": (bool, True),
|
||||||
"headings.centerChapter": (bool, False),
|
"headings.centerChapter": (bool, False),
|
||||||
"headings.centerScene": (bool, False),
|
"headings.centerScene": (bool, False),
|
||||||
"headings.breakTitle": (bool, True),
|
"headings.breakTitle": (bool, False),
|
||||||
"headings.breakPart": (bool, True),
|
"headings.breakPart": (bool, True),
|
||||||
"headings.breakChapter": (bool, True),
|
"headings.breakChapter": (bool, True),
|
||||||
"headings.breakScene": (bool, False),
|
"headings.breakScene": (bool, False),
|
||||||
|
|||||||
@@ -267,8 +267,8 @@ class NWBuildDocument:
|
|||||||
self._build.getBool("headings.hideSection")
|
self._build.getBool("headings.hideSection")
|
||||||
)
|
)
|
||||||
bldObj.setTitleStyle(
|
bldObj.setTitleStyle(
|
||||||
self._build.getBool("headings.centerPart"),
|
self._build.getBool("headings.centerTitle"),
|
||||||
self._build.getBool("headings.breakPart")
|
self._build.getBool("headings.breakTitle")
|
||||||
)
|
)
|
||||||
bldObj.setPartitionStyle(
|
bldObj.setPartitionStyle(
|
||||||
self._build.getBool("headings.centerPart"),
|
self._build.getBool("headings.centerPart"),
|
||||||
|
|||||||
@@ -137,6 +137,7 @@ class nwDocInsert(Enum):
|
|||||||
VSPACE_M = 9
|
VSPACE_M = 9
|
||||||
LIPSUM = 10
|
LIPSUM = 10
|
||||||
FOOTNOTE = 11
|
FOOTNOTE = 11
|
||||||
|
LINE_BRK = 12
|
||||||
|
|
||||||
|
|
||||||
class nwView(Enum):
|
class nwView(Enum):
|
||||||
|
|||||||
@@ -255,24 +255,19 @@ class ToDocX(Tokenizer):
|
|||||||
self._processFragments(par, S_NORM, tText, tFormat)
|
self._processFragments(par, S_NORM, tText, tFormat)
|
||||||
|
|
||||||
elif tType == BlockTyp.TITLE:
|
elif tType == BlockTyp.TITLE:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._processFragments(par, S_TITLE, tText, tFormat)
|
||||||
self._processFragments(par, S_TITLE, tHead, tFormat)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD1:
|
elif tType == BlockTyp.HEAD1:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._processFragments(par, S_HEAD1, tText, tFormat)
|
||||||
self._processFragments(par, S_HEAD1, tHead, tFormat)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD2:
|
elif tType == BlockTyp.HEAD2:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._processFragments(par, S_HEAD2, tText, tFormat)
|
||||||
self._processFragments(par, S_HEAD2, tHead, tFormat)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD3:
|
elif tType == BlockTyp.HEAD3:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._processFragments(par, S_HEAD3, tText, tFormat)
|
||||||
self._processFragments(par, S_HEAD3, tHead, tFormat)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD4:
|
elif tType == BlockTyp.HEAD4:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._processFragments(par, S_HEAD4, tText, tFormat)
|
||||||
self._processFragments(par, S_HEAD4, tHead, tFormat)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
self._processFragments(par, S_SEP, tText)
|
self._processFragments(par, S_SEP, tText)
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ from pathlib import Path
|
|||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
from novelwriter.common import formatTimeStamp
|
from novelwriter.common import formatTimeStamp
|
||||||
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode
|
from novelwriter.constants import nwHtmlUnicode
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
@@ -211,23 +211,23 @@ class ToHtml(Tokenizer):
|
|||||||
lines.append(f"<p{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
|
lines.append(f"<p{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.TITLE:
|
elif tType == BlockTyp.TITLE:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "<br>")
|
tHead = tText.replace("\n", "<br>")
|
||||||
lines.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n")
|
lines.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD1:
|
elif tType == BlockTyp.HEAD1:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "<br>")
|
tHead = tText.replace("\n", "<br>")
|
||||||
lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n")
|
lines.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD2:
|
elif tType == BlockTyp.HEAD2:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "<br>")
|
tHead = tText.replace("\n", "<br>")
|
||||||
lines.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
|
lines.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD3:
|
elif tType == BlockTyp.HEAD3:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "<br>")
|
tHead = tText.replace("\n", "<br>")
|
||||||
lines.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n")
|
lines.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD4:
|
elif tType == BlockTyp.HEAD4:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "<br>")
|
tHead = tText.replace("\n", "<br>")
|
||||||
lines.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n")
|
lines.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ class ComStyle(NamedTuple):
|
|||||||
textClass: str = ""
|
textClass: str = ""
|
||||||
|
|
||||||
|
|
||||||
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
|
|
||||||
COMMENT_STYLE = {
|
COMMENT_STYLE = {
|
||||||
nwComment.PLAIN: ComStyle("Comment", "comment", "comment"),
|
nwComment.PLAIN: ComStyle("Comment", "comment", "comment"),
|
||||||
nwComment.IGNORE: ComStyle(),
|
nwComment.IGNORE: ComStyle(),
|
||||||
@@ -67,13 +66,12 @@ COMMENT_STYLE = {
|
|||||||
nwComment.COMMENT: ComStyle(),
|
nwComment.COMMENT: ComStyle(),
|
||||||
nwComment.STORY: ComStyle("", "modifier", "note"),
|
nwComment.STORY: ComStyle("", "modifier", "note"),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Lookups
|
|
||||||
HEADINGS = [BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4]
|
HEADINGS = [BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4]
|
||||||
SKIP_INDENT = [
|
SKIP_INDENT = [
|
||||||
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
|
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
|
||||||
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
|
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
|
||||||
]
|
]
|
||||||
|
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
|
||||||
|
|
||||||
|
|
||||||
class Tokenizer(ABC):
|
class Tokenizer(ABC):
|
||||||
@@ -182,8 +180,6 @@ class Tokenizer(ABC):
|
|||||||
(REGEX_PATTERNS.markdownBold, [0, TextFmt.B_B, 0, TextFmt.B_E]),
|
(REGEX_PATTERNS.markdownBold, [0, TextFmt.B_B, 0, TextFmt.B_E]),
|
||||||
(REGEX_PATTERNS.markdownStrike, [0, TextFmt.D_B, 0, TextFmt.D_E]),
|
(REGEX_PATTERNS.markdownStrike, [0, TextFmt.D_B, 0, TextFmt.D_E]),
|
||||||
]
|
]
|
||||||
self._rxShortCodes = REGEX_PATTERNS.shortcodePlain
|
|
||||||
self._rxShortCodeVals = REGEX_PATTERNS.shortcodeValue
|
|
||||||
|
|
||||||
self._shortCodeFmt = {
|
self._shortCodeFmt = {
|
||||||
nwShortcode.ITALIC_O: TextFmt.I_B, nwShortcode.ITALIC_C: TextFmt.I_E,
|
nwShortcode.ITALIC_O: TextFmt.I_B, nwShortcode.ITALIC_C: TextFmt.I_E,
|
||||||
@@ -456,20 +452,22 @@ class Tokenizer(ABC):
|
|||||||
self._text = ""
|
self._text = ""
|
||||||
self._handle = None
|
self._handle = None
|
||||||
|
|
||||||
if (tItem := self._project.tree[tHandle]) and tItem.isRootType():
|
if (item := self._project.tree[tHandle]) and item.isRootType():
|
||||||
self._handle = tHandle
|
self._handle = tHandle
|
||||||
|
style = BlockFmt.CENTRE
|
||||||
if self._isFirst:
|
if self._isFirst:
|
||||||
textAlign = BlockFmt.CENTRE
|
|
||||||
self._isFirst = False
|
self._isFirst = False
|
||||||
else:
|
else:
|
||||||
textAlign = BlockFmt.PBB | BlockFmt.CENTRE
|
style |= BlockFmt.PBB
|
||||||
|
|
||||||
trNotes = self._localLookup("Notes")
|
title = item.itemName
|
||||||
title = f"{trNotes}: {tItem.itemName}"
|
if not item.isNovelLike():
|
||||||
self._blocks = []
|
notes = self._localLookup("Notes")
|
||||||
self._blocks.append((
|
title = f"{notes}: {title}"
|
||||||
BlockTyp.TITLE, f"{self._handle}:T0001", title, [], textAlign
|
|
||||||
))
|
self._blocks = [(
|
||||||
|
BlockTyp.TITLE, f"{self._handle}:T0001", title, [], style
|
||||||
|
)]
|
||||||
if self._keepRaw:
|
if self._keepRaw:
|
||||||
self._raw.append(f"#! {title}\n\n")
|
self._raw.append(f"#! {title}\n\n")
|
||||||
|
|
||||||
@@ -523,25 +521,30 @@ class Tokenizer(ABC):
|
|||||||
isNovel = self._isNovel
|
isNovel = self._isNovel
|
||||||
keepRaw = self._keepRaw
|
keepRaw = self._keepRaw
|
||||||
doJustify = self._doJustify
|
doJustify = self._doJustify
|
||||||
|
keepBreaks = self._keepBreaks
|
||||||
indentFirst = self._indentFirst
|
indentFirst = self._indentFirst
|
||||||
firstIndent = self._firstIndent
|
firstIndent = self._firstIndent
|
||||||
|
|
||||||
if self._isNovel:
|
if self._isNovel:
|
||||||
self._hFormatter.setHandle(self._handle)
|
self._hFormatter.setHandle(self._handle)
|
||||||
|
|
||||||
|
# Replace all instances of [br] with a placeholder character
|
||||||
|
text = REGEX_PATTERNS.lineBreak.sub("\uffff", self._text)
|
||||||
|
|
||||||
nHead = 0
|
nHead = 0
|
||||||
breakNext = False
|
breakNext = False
|
||||||
tmpMarkdown = []
|
rawText = []
|
||||||
tHandle = self._handle or ""
|
tHandle = self._handle or ""
|
||||||
tBlocks: list[T_Block] = [B_EMPTY]
|
tBlocks: list[T_Block] = [B_EMPTY]
|
||||||
for aLine in self._text.splitlines():
|
for bLine in text.splitlines():
|
||||||
|
aLine = bLine.replace("\uffff", "") # Remove placeholder characters
|
||||||
sLine = aLine.strip().lower()
|
sLine = aLine.strip().lower()
|
||||||
|
|
||||||
# Check for blank lines
|
# Check for blank lines
|
||||||
if not sLine:
|
if not sLine:
|
||||||
tBlocks.append(B_EMPTY)
|
tBlocks.append(B_EMPTY)
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append("\n")
|
rawText.append("\n")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if breakNext:
|
if breakNext:
|
||||||
@@ -607,13 +610,13 @@ class Tokenizer(ABC):
|
|||||||
BlockTyp.COMMENT, "", tLine, tFmt, sAlign
|
BlockTyp.COMMENT, "", tLine, tFmt, sAlign
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif cStyle == nwComment.FOOTNOTE:
|
elif cStyle == nwComment.FOOTNOTE:
|
||||||
tLine, tFmt = self._extractFormats(cText, skip=TextFmt.FNOTE)
|
tLine, tFmt = self._extractFormats(cText, skip=TextFmt.FNOTE)
|
||||||
self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
|
self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif aLine.startswith("@"):
|
elif aLine.startswith("@"):
|
||||||
# Keywords
|
# Keywords
|
||||||
@@ -628,7 +631,7 @@ class Tokenizer(ABC):
|
|||||||
BlockTyp.KEYWORD, tTag[1:], tLine, tFmt, sAlign
|
BlockTyp.KEYWORD, tTag[1:], tLine, tFmt, sAlign
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif aLine.startswith(("# ", "#! ")):
|
elif aLine.startswith(("# ", "#! ")):
|
||||||
# Title or Partition Headings
|
# Title or Partition Headings
|
||||||
@@ -664,7 +667,7 @@ class Tokenizer(ABC):
|
|||||||
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif aLine.startswith(("## ", "##! ")):
|
elif aLine.startswith(("## ", "##! ")):
|
||||||
# (Unnumbered) Chapter Headings
|
# (Unnumbered) Chapter Headings
|
||||||
@@ -699,7 +702,7 @@ class Tokenizer(ABC):
|
|||||||
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif aLine.startswith(("### ", "###! ")):
|
elif aLine.startswith(("### ", "###! ")):
|
||||||
# (Alternative) Scene Headings
|
# (Alternative) Scene Headings
|
||||||
@@ -740,7 +743,7 @@ class Tokenizer(ABC):
|
|||||||
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
elif aLine.startswith("#### "):
|
elif aLine.startswith("#### "):
|
||||||
# Section Headings
|
# Section Headings
|
||||||
@@ -770,7 +773,7 @@ class Tokenizer(ABC):
|
|||||||
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# Text Lines
|
# Text Lines
|
||||||
@@ -786,19 +789,19 @@ class Tokenizer(ABC):
|
|||||||
alnRight = False
|
alnRight = False
|
||||||
indLeft = False
|
indLeft = False
|
||||||
indRight = False
|
indRight = False
|
||||||
if aLine.startswith(">>"):
|
if bLine.startswith(">>"):
|
||||||
alnRight = True
|
alnRight = True
|
||||||
aLine = aLine[2:].lstrip(" ")
|
bLine = bLine[2:].lstrip(" ")
|
||||||
elif aLine.startswith(">"):
|
elif bLine.startswith(">"):
|
||||||
indLeft = True
|
indLeft = True
|
||||||
aLine = aLine[1:].lstrip(" ")
|
bLine = bLine[1:].lstrip(" ")
|
||||||
|
|
||||||
if aLine.endswith("<<"):
|
if bLine.endswith("<<"):
|
||||||
alnLeft = True
|
alnLeft = True
|
||||||
aLine = aLine[:-2].rstrip(" ")
|
bLine = bLine[:-2].rstrip(" ")
|
||||||
elif aLine.endswith("<"):
|
elif bLine.endswith("<"):
|
||||||
indRight = True
|
indRight = True
|
||||||
aLine = aLine[:-1].rstrip(" ")
|
bLine = bLine[:-1].rstrip(" ")
|
||||||
|
|
||||||
if alnLeft and alnRight:
|
if alnLeft and alnRight:
|
||||||
sAlign |= BlockFmt.CENTRE
|
sAlign |= BlockFmt.CENTRE
|
||||||
@@ -813,12 +816,12 @@ class Tokenizer(ABC):
|
|||||||
sAlign |= BlockFmt.IND_R
|
sAlign |= BlockFmt.IND_R
|
||||||
|
|
||||||
# Process formats
|
# Process formats
|
||||||
tLine, tFmt = self._extractFormats(aLine, hDialog=isNovel)
|
tLine, tFmt = self._extractFormats(bLine, hDialog=isNovel)
|
||||||
tBlocks.append((
|
tBlocks.append((
|
||||||
BlockTyp.TEXT, "", tLine, tFmt, sAlign
|
BlockTyp.TEXT, "", tLine, tFmt, sAlign
|
||||||
))
|
))
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append(f"{aLine}\n")
|
rawText.append(f"{aLine}\n")
|
||||||
|
|
||||||
# If we have content, turn off the first page flag
|
# If we have content, turn off the first page flag
|
||||||
if self._isFirst and len(tBlocks) > 1:
|
if self._isFirst and len(tBlocks) > 1:
|
||||||
@@ -834,8 +837,8 @@ class Tokenizer(ABC):
|
|||||||
# Always add an empty line at the end of the file
|
# Always add an empty line at the end of the file
|
||||||
tBlocks.append(B_EMPTY)
|
tBlocks.append(B_EMPTY)
|
||||||
if keepRaw:
|
if keepRaw:
|
||||||
tmpMarkdown.append("\n")
|
rawText.append("\n")
|
||||||
self._raw.append("".join(tmpMarkdown))
|
self._raw.append("".join(rawText))
|
||||||
|
|
||||||
# Second Pass
|
# Second Pass
|
||||||
# ===========
|
# ===========
|
||||||
@@ -844,7 +847,7 @@ class Tokenizer(ABC):
|
|||||||
# It also ensures that there isn't paragraph spacing between
|
# It also ensures that there isn't paragraph spacing between
|
||||||
# meta data lines for formats that have spacing.
|
# meta data lines for formats that have spacing.
|
||||||
|
|
||||||
lineSep = "\n" if self._keepBreaks else " "
|
lineSep = "\n" if keepBreaks else " "
|
||||||
|
|
||||||
pLines: list[T_Block] = []
|
pLines: list[T_Block] = []
|
||||||
sBlocks: list[T_Block] = []
|
sBlocks: list[T_Block] = []
|
||||||
@@ -894,9 +897,12 @@ class Tokenizer(ABC):
|
|||||||
# enabled, and there is no alignment, we apply it.
|
# enabled, and there is no alignment, we apply it.
|
||||||
if doJustify and not cStyle & BlockFmt.ALIGNED:
|
if doJustify and not cStyle & BlockFmt.ALIGNED:
|
||||||
cStyle |= BlockFmt.JUSTIFY
|
cStyle |= BlockFmt.JUSTIFY
|
||||||
|
|
||||||
|
pTxt = pLines[0][2].replace("\uffff", "\n")
|
||||||
sBlocks.append((
|
sBlocks.append((
|
||||||
BlockTyp.TEXT, pLines[0][1], pLines[0][2], pLines[0][3], cStyle
|
BlockTyp.TEXT, pLines[0][1], pTxt, pLines[0][3], cStyle
|
||||||
))
|
))
|
||||||
|
|
||||||
elif nLines > 1:
|
elif nLines > 1:
|
||||||
# The paragraph contains multiple lines, so we need to
|
# The paragraph contains multiple lines, so we need to
|
||||||
# join them according to the line break policy, and
|
# join them according to the line break policy, and
|
||||||
@@ -907,8 +913,11 @@ class Tokenizer(ABC):
|
|||||||
tLen = len(tTxt)
|
tLen = len(tTxt)
|
||||||
tTxt += f"{aBlock[2]}{lineSep}"
|
tTxt += f"{aBlock[2]}{lineSep}"
|
||||||
tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aBlock[3])
|
tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aBlock[3])
|
||||||
|
cStyle |= aBlock[4]
|
||||||
|
|
||||||
|
pTxt = tTxt[:-1].replace("\uffff", "\n")
|
||||||
sBlocks.append((
|
sBlocks.append((
|
||||||
BlockTyp.TEXT, pLines[0][1], tTxt[:-1], tFmt, cStyle
|
BlockTyp.TEXT, pLines[0][1], pTxt, tFmt, cStyle
|
||||||
))
|
))
|
||||||
|
|
||||||
# Reset buffer and make sure text indent is on for next pass
|
# Reset buffer and make sure text indent is on for next pass
|
||||||
@@ -1136,12 +1145,12 @@ class Tokenizer(ABC):
|
|||||||
# Post-process text and format
|
# Post-process text and format
|
||||||
result = text
|
result = text
|
||||||
formats = []
|
formats = []
|
||||||
for pos, end, fmt, key in reversed(sorted(temp, key=lambda x: x[0])):
|
for pos, end, fmt, meta in reversed(sorted(temp, key=lambda x: x[0])):
|
||||||
if fmt > 0:
|
if fmt > 0:
|
||||||
if end > pos:
|
if end > pos:
|
||||||
result = result[:pos] + result[end:]
|
result = result[:pos] + result[end:]
|
||||||
formats = [(p+pos-end if p > pos else p, f, k) for p, f, k in formats]
|
formats = [(p+pos-end if p > pos else p, f, m) for p, f, m in formats]
|
||||||
formats.insert(0, (pos, fmt, key))
|
formats.insert(0, (pos, fmt, meta))
|
||||||
|
|
||||||
return result, formats
|
return result, formats
|
||||||
|
|
||||||
@@ -1187,6 +1196,7 @@ class HeadingFormatter:
|
|||||||
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
def apply(self, hFormat: str, text: str, nHead: int) -> str:
|
||||||
"""Apply formatting to a specific heading."""
|
"""Apply formatting to a specific heading."""
|
||||||
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
hFormat = hFormat.replace(nwHeadFmt.TITLE, text)
|
||||||
|
hFormat = hFormat.replace(nwHeadFmt.BR, "\n")
|
||||||
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
hFormat = hFormat.replace(nwHeadFmt.CH_NUM, str(self._chCount))
|
||||||
hFormat = hFormat.replace(nwHeadFmt.SC_NUM, str(self._scChCount))
|
hFormat = hFormat.replace(nwHeadFmt.SC_NUM, str(self._scChCount))
|
||||||
hFormat = hFormat.replace(nwHeadFmt.SC_ABS, str(self._scAbsCount))
|
hFormat = hFormat.replace(nwHeadFmt.SC_ABS, str(self._scAbsCount))
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import logging
|
|||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from novelwriter.constants import nwHeadFmt, nwUnicode
|
from novelwriter.constants import nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
||||||
from novelwriter.formats.tokenizer import Tokenizer
|
from novelwriter.formats.tokenizer import Tokenizer
|
||||||
@@ -113,23 +113,23 @@ class ToMarkdown(Tokenizer):
|
|||||||
lines.append(f"{tTemp}\n\n")
|
lines.append(f"{tTemp}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.TITLE:
|
elif tType == BlockTyp.TITLE:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
tHead = tText.replace("\n", " - ")
|
||||||
lines.append(f"# {tHead}\n\n")
|
lines.append(f"# {tHead}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD1:
|
elif tType == BlockTyp.HEAD1:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
tHead = tText.replace("\n", " - ")
|
||||||
lines.append(f"# {tHead}\n\n")
|
lines.append(f"# {tHead}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD2:
|
elif tType == BlockTyp.HEAD2:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
tHead = tText.replace("\n", " - ")
|
||||||
lines.append(f"## {tHead}\n\n")
|
lines.append(f"## {tHead}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD3:
|
elif tType == BlockTyp.HEAD3:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
tHead = tText.replace("\n", " - ")
|
||||||
lines.append(f"### {tHead}\n\n")
|
lines.append(f"### {tHead}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD4:
|
elif tType == BlockTyp.HEAD4:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
tHead = tText.replace("\n", " - ")
|
||||||
lines.append(f"#### {tHead}\n\n")
|
lines.append(f"#### {tHead}\n\n")
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
|
|||||||
@@ -444,24 +444,19 @@ class ToOdt(Tokenizer):
|
|||||||
|
|
||||||
elif tType == BlockTyp.TITLE:
|
elif tType == BlockTyp.TITLE:
|
||||||
# Title must be text:p
|
# Title must be text:p
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._addTextPar(xText, S_TITLE, oStyle, tText, isHead=False)
|
||||||
self._addTextPar(xText, S_TITLE, oStyle, tHead, isHead=False)
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD1:
|
elif tType == BlockTyp.HEAD1:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._addTextPar(xText, S_HEAD1, oStyle, tText, isHead=True, oLevel="1")
|
||||||
self._addTextPar(xText, S_HEAD1, oStyle, tHead, isHead=True, oLevel="1")
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD2:
|
elif tType == BlockTyp.HEAD2:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._addTextPar(xText, S_HEAD2, oStyle, tText, isHead=True, oLevel="2")
|
||||||
self._addTextPar(xText, S_HEAD2, oStyle, tHead, isHead=True, oLevel="2")
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD3:
|
elif tType == BlockTyp.HEAD3:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._addTextPar(xText, S_HEAD3, oStyle, tText, isHead=True, oLevel="3")
|
||||||
self._addTextPar(xText, S_HEAD3, oStyle, tHead, isHead=True, oLevel="3")
|
|
||||||
|
|
||||||
elif tType == BlockTyp.HEAD4:
|
elif tType == BlockTyp.HEAD4:
|
||||||
tHead = tText.replace(nwHeadFmt.BR, "\n")
|
self._addTextPar(xText, S_HEAD4, oStyle, tText, isHead=True, oLevel="4")
|
||||||
self._addTextPar(xText, S_HEAD4, oStyle, tHead, isHead=True, oLevel="4")
|
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
self._addTextPar(xText, S_SEP, oStyle, tText)
|
self._addTextPar(xText, S_SEP, oStyle, tText)
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ from PyQt5.QtGui import (
|
|||||||
)
|
)
|
||||||
from PyQt5.QtPrintSupport import QPrinter
|
from PyQt5.QtPrintSupport import QPrinter
|
||||||
|
|
||||||
from novelwriter.constants import nwHeadFmt, nwStyles, nwUnicode
|
from novelwriter.constants import nwStyles, nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
|
||||||
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
|
||||||
@@ -217,7 +217,7 @@ class ToQTextDocument(Tokenizer):
|
|||||||
elif tType in HEADINGS:
|
elif tType in HEADINGS:
|
||||||
bFmt, cFmt = self._genHeadStyle(tType, tMeta, bFmt)
|
bFmt, cFmt = self._genHeadStyle(tType, tMeta, bFmt)
|
||||||
newBlock(cursor, bFmt)
|
newBlock(cursor, bFmt)
|
||||||
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
|
cursor.insertText(tText, cFmt)
|
||||||
|
|
||||||
elif tType == BlockTyp.SEP:
|
elif tType == BlockTyp.SEP:
|
||||||
newBlock(cursor, bFmt)
|
newBlock(cursor, bFmt)
|
||||||
|
|||||||
@@ -877,6 +877,8 @@ class GuiDocEditor(QPlainTextEdit):
|
|||||||
after = False
|
after = False
|
||||||
elif insert == nwDocInsert.FOOTNOTE:
|
elif insert == nwDocInsert.FOOTNOTE:
|
||||||
self._insertCommentStructure(nwComment.FOOTNOTE)
|
self._insertCommentStructure(nwComment.FOOTNOTE)
|
||||||
|
elif insert == nwDocInsert.LINE_BRK:
|
||||||
|
text = nwShortcode.BREAK
|
||||||
|
|
||||||
if text:
|
if text:
|
||||||
if block:
|
if block:
|
||||||
|
|||||||
@@ -570,8 +570,8 @@ class GuiMainMenu(QMenuBar):
|
|||||||
lambda: self.requestDocInsert.emit(nwDocInsert.SHORT)
|
lambda: self.requestDocInsert.emit(nwDocInsert.SHORT)
|
||||||
)
|
)
|
||||||
|
|
||||||
# Insert > Symbols
|
# Insert > Breaks and Vertical Space
|
||||||
self.mInsBreaks = self.insMenu.addMenu(self.tr("Page Break and Space"))
|
self.mInsBreaks = self.insMenu.addMenu(self.tr("Breaks and Vertical Space"))
|
||||||
|
|
||||||
# Insert > New Page
|
# Insert > New Page
|
||||||
self.aInsNewPage = self.mInsBreaks.addAction(self.tr("Page Break"))
|
self.aInsNewPage = self.mInsBreaks.addAction(self.tr("Page Break"))
|
||||||
@@ -579,6 +579,12 @@ class GuiMainMenu(QMenuBar):
|
|||||||
lambda: self.requestDocInsert.emit(nwDocInsert.NEW_PAGE)
|
lambda: self.requestDocInsert.emit(nwDocInsert.NEW_PAGE)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Insert > Forced Line Break
|
||||||
|
self.aInsLineBreak = self.mInsBreaks.addAction(self.tr("Forced Line Break"))
|
||||||
|
self.aInsLineBreak.triggered.connect(
|
||||||
|
lambda: self.requestDocInsert.emit(nwDocInsert.LINE_BRK)
|
||||||
|
)
|
||||||
|
|
||||||
# Insert > Vertical Space (Single)
|
# Insert > Vertical Space (Single)
|
||||||
self.aInsVSpaceS = self.mInsBreaks.addAction(self.tr("Vertical Space (Single)"))
|
self.aInsVSpaceS = self.mInsBreaks.addAction(self.tr("Vertical Space (Single)"))
|
||||||
self.aInsVSpaceS.triggered.connect(
|
self.aInsVSpaceS.triggered.connect(
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ class RegExPatterns:
|
|||||||
|
|
||||||
# Static RegExes
|
# Static RegExes
|
||||||
_rxWords = re.compile(nwRegEx.WORDS, re.UNICODE)
|
_rxWords = re.compile(nwRegEx.WORDS, re.UNICODE)
|
||||||
|
_rxBreak = re.compile(nwRegEx.BREAK, re.UNICODE)
|
||||||
_rxItalic = re.compile(nwRegEx.FMT_EI, re.UNICODE)
|
_rxItalic = re.compile(nwRegEx.FMT_EI, re.UNICODE)
|
||||||
_rxBold = re.compile(nwRegEx.FMT_EB, re.UNICODE)
|
_rxBold = re.compile(nwRegEx.FMT_EB, re.UNICODE)
|
||||||
_rxStrike = re.compile(nwRegEx.FMT_ST, re.UNICODE)
|
_rxStrike = re.compile(nwRegEx.FMT_ST, re.UNICODE)
|
||||||
@@ -44,6 +45,11 @@ class RegExPatterns:
|
|||||||
"""Split text into words."""
|
"""Split text into words."""
|
||||||
return self._rxWords
|
return self._rxWords
|
||||||
|
|
||||||
|
@property
|
||||||
|
def lineBreak(self) -> re.Pattern:
|
||||||
|
"""Find forced line break."""
|
||||||
|
return self._rxBreak
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def markdownItalic(self) -> re.Pattern:
|
def markdownItalic(self) -> re.Pattern:
|
||||||
"""Markdown italic style."""
|
"""Markdown italic style."""
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
%%~name: Title Page
|
%%~name: Title Page
|
||||||
%%~path: 7031beac91f75/53b69b83cdafc
|
%%~path: 7031beac91f75/53b69b83cdafc
|
||||||
%%~kind: NOVEL/DOCUMENT
|
%%~kind: NOVEL/DOCUMENT
|
||||||
%%~hash: c5dc35d18ecb074a9e41a1410d1bff8021cf0a5b
|
%%~hash: 4072adb6d21ff877577f033f19714d9bd01396f3
|
||||||
%%~date: Unknown/2023-08-25 16:51:52
|
%%~date: Unknown/2024-10-24 16:25:44
|
||||||
|
|
||||||
|
Jane Smith[br]
|
||||||
|
42 Main Street[br]
|
||||||
|
1234 Capital City <<
|
||||||
|
|
||||||
|
[vspace:5]
|
||||||
|
|
||||||
#! My Novel
|
#! My Novel
|
||||||
|
|
||||||
>> **By Jane Smith** <<
|
>> **By Jane Smith** <<
|
||||||
|
|
||||||
>> This is the title page. <<
|
>> This is the title page. <<
|
||||||
>> It should be the first document of the project. <<
|
>> It should be the first document of the project. <<
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
%%~name: Interlude
|
%%~name: Interlude
|
||||||
%%~path: 7031beac91f75/ba8a28a246524
|
%%~path: 7031beac91f75/ba8a28a246524
|
||||||
%%~kind: NOVEL/DOCUMENT
|
%%~kind: NOVEL/DOCUMENT
|
||||||
%%~hash: 721d3d15e0233186354ac6fa61f27db10f38e6bc
|
%%~hash: 5c8f68d48573b576dcaacf6d6928c496ec361b9d
|
||||||
%%~date: Unknown/2024-03-14 22:55:04
|
%%~date: Unknown/2024-10-24 01:22:15
|
||||||
##! Interlude
|
##! Interlude
|
||||||
|
|
||||||
% Notice that this document has a title with a ‘!’ in it in addition to the two hash symbols. This means it is an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue.
|
% Notice that this document has a title with a ‘!’ in it in addition to the two hash symbols. This means it is an unnumbered chapter. Unnumbered chapters can be treated separately from numbered chapters during export. Perfect for when you want to add an interlude, or for a prologue or epilogue.
|
||||||
|
|
||||||
I am the very model of a modern Major-General
|
I am the very model of a modern Major-General[br]
|
||||||
I've information vegetable, animal, and mineral
|
I've information vegetable, animal, and mineral[br]
|
||||||
I know the kings of England, and I quote the fights historical
|
I know the kings of England, and I quote the fights historical[br]
|
||||||
From Marathon to Waterloo, in order categorical
|
From Marathon to Waterloo, in order categorical <<
|
||||||
|
|
||||||
I'm very well acquainted, too, with matters mathematical <<
|
I'm very well acquainted, too, with matters mathematical[br]
|
||||||
I understand equations, both the simple and quadratical
|
I understand equations, both the simple and quadratical[br]
|
||||||
About binomial theorem I'm teeming with a lot o’ news
|
About binomial theorem I'm teeming with a lot o’ news[br]
|
||||||
With many cheerful facts about the square of the hypotenuse
|
With many cheerful facts about the square of the hypotenuse <<
|
||||||
|
|
||||||
With many cheerful facts about the square of the hypotenuse <<
|
With many cheerful facts about the square of the hypotenuse[br]
|
||||||
With many cheerful facts about the square of the hypotenuse
|
With many cheerful facts about the square of the hypotenuse[br]
|
||||||
With many cheerful facts about the square of the hypotepotenuse
|
With many cheerful facts about the square of the hypotepotenuse <<
|
||||||
|
|
||||||
% Notice that the lines in the verse end in a single line break. Single line breaks do not create a new paragraph but instead insert a break within the paragraph. Press Ctrl+R to see what this renders like.
|
% Notice that the lines in the verse end in a single line break. Single line breaks do not create a new paragraph but instead insert a break within the paragraph. Press Ctrl+R to see what this renders like.
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version='1.0' encoding='utf-8'?>
|
<?xml version='1.0' encoding='utf-8'?>
|
||||||
<novelWriterXML appVersion="2.6a2" hexVersion="0x020600a2" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-23 17:41:09">
|
<novelWriterXML appVersion="2.6a2" hexVersion="0x020600a2" fileVersion="1.5" fileRevision="4" timeStamp="2024-10-24 16:26:12">
|
||||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2074" autoCount="277" editTime="93039">
|
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="2077" autoCount="279" editTime="93511">
|
||||||
<name>Sample Project</name>
|
<name>Sample Project</name>
|
||||||
<author>Jane Smith</author>
|
<author>Jane Smith</author>
|
||||||
</project>
|
</project>
|
||||||
@@ -36,13 +36,13 @@
|
|||||||
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
<entry key="i56be10" count="1" red="220" green="138" blue="221" shape="BLOCK_4">Main</entry>
|
||||||
</importance>
|
</importance>
|
||||||
</settings>
|
</settings>
|
||||||
<content items="31" novelWords="996" notesWords="416">
|
<content items="31" novelWords="1004" notesWords="416">
|
||||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||||
<meta expanded="yes" />
|
<meta expanded="yes" />
|
||||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="53b69b83cdafc" parent="7031beac91f75" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H1" charCount="95" wordCount="19" paraCount="2" cursorPos="31" />
|
<meta expanded="no" heading="H1" charCount="136" wordCount="27" paraCount="3" cursorPos="69" />
|
||||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H3" charCount="2953" wordCount="520" paraCount="15" cursorPos="66" />
|
<meta expanded="no" heading="H3" charCount="2937" wordCount="520" paraCount="15" cursorPos="4" />
|
||||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
@@ -66,7 +66,7 @@
|
|||||||
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
<name status="s90e6c9" import="ia857f0" active="yes">Another Scene</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
<item handle="ba8a28a246524" parent="7031beac91f75" root="7031beac91f75" order="4" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||||
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="357" />
|
<meta expanded="no" heading="H2" charCount="617" wordCount="101" paraCount="3" cursorPos="1182" />
|
||||||
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
<name status="s78ea90" import="ia857f0" active="yes">Interlude</name>
|
||||||
</item>
|
</item>
|
||||||
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
<item handle="96b68994dfa3d" parent="7031beac91f75" root="7031beac91f75" order="5" type="FILE" class="NOVEL" layout="NOTE">
|
||||||
|
|||||||
@@ -2,19 +2,19 @@
|
|||||||
"meta": {
|
"meta": {
|
||||||
"projectName": "Lorem Ipsum",
|
"projectName": "Lorem Ipsum",
|
||||||
"novelAuthor": "lipsum.com",
|
"novelAuthor": "lipsum.com",
|
||||||
"buildTime": 1729029144,
|
"buildTime": 1729725334,
|
||||||
"buildTimeStr": "2024-10-15 23:52:24"
|
"buildTimeStr": "2024-10-24 01:15:34"
|
||||||
},
|
},
|
||||||
"text": {
|
"text": {
|
||||||
"nwd": [
|
"nwd": [
|
||||||
[
|
[
|
||||||
"#! Lorem Ipsum",
|
"#! Lorem Ipsum",
|
||||||
"",
|
"",
|
||||||
"**By lipsum.com**",
|
">> **By lipsum.com** <<",
|
||||||
"",
|
"",
|
||||||
"\u201cNeque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit\u2026\u201d",
|
">> \u201cNeque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit\u2026\u201d <<",
|
||||||
"",
|
"",
|
||||||
"\u201cThere is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain\u2026\u201d"
|
">> \u201cThere is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain\u2026\u201d <<"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"",
|
"",
|
||||||
@@ -36,7 +36,7 @@
|
|||||||
[
|
[
|
||||||
"# Act One",
|
"# Act One",
|
||||||
"",
|
"",
|
||||||
"\u201cFusce maximus felis libero\u201d"
|
">> \u201cFusce maximus felis libero\u201d <<"
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"## Chapter One",
|
"## Chapter One",
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
#! Lorem Ipsum
|
#! Lorem Ipsum
|
||||||
|
|
||||||
**By lipsum.com**
|
>> **By lipsum.com** <<
|
||||||
|
|
||||||
“Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…”
|
>> “Neque porro quisquam est qui dolorem ipsum quia dolor sit amet, consectetur, adipisci velit…” <<
|
||||||
|
|
||||||
“There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…”
|
>> “There is no one who loves pain itself, who seeks after it and wants to have it, simply because it is pain…” <<
|
||||||
|
|
||||||
|
|
||||||
% Exctracted from the lipsum.com website.
|
% Exctracted from the lipsum.com website.
|
||||||
@@ -23,7 +23,7 @@ _Lorem Ipsum_ is simply dummy text[footnote:f9kgf] of the printing and typesetti
|
|||||||
|
|
||||||
# Act One
|
# Act One
|
||||||
|
|
||||||
“Fusce maximus felis libero”
|
>> “Fusce maximus felis libero” <<
|
||||||
|
|
||||||
## Chapter One
|
## Chapter One
|
||||||
|
|
||||||
|
|||||||
@@ -412,7 +412,7 @@ def testCoreDocBuild_Custom(mockGUI, fncPath: Path):
|
|||||||
assert error == []
|
assert error == []
|
||||||
assert docFile.read_text(encoding="utf-8") == (
|
assert docFile.read_text(encoding="utf-8") == (
|
||||||
"#! New Novel\n\n"
|
"#! New Novel\n\n"
|
||||||
"By Jane Doe\n\n"
|
">> By Jane Doe <<\n\n"
|
||||||
"## New Chapter\n\n\n"
|
"## New Chapter\n\n\n"
|
||||||
"### New Scene\n\n\n"
|
"### New Scene\n\n\n"
|
||||||
)
|
)
|
||||||
@@ -442,7 +442,7 @@ def testCoreDocBuild_Custom(mockGUI, fncPath: Path):
|
|||||||
assert error == []
|
assert error == []
|
||||||
assert docFile.read_text(encoding="utf-8") == (
|
assert docFile.read_text(encoding="utf-8") == (
|
||||||
"#! New Novel\n\n"
|
"#! New Novel\n\n"
|
||||||
"By Jane Doe\n\n"
|
">> By Jane Doe <<\n\n"
|
||||||
"## New Chapter\n\n\n"
|
"## New Chapter\n\n\n"
|
||||||
"### New Scene\n\n\n"
|
"### New Scene\n\n\n"
|
||||||
)
|
)
|
||||||
@@ -566,7 +566,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
|
|||||||
assert isinstance(docBuild.lastBuild, ToRaw)
|
assert isinstance(docBuild.lastBuild, ToRaw)
|
||||||
assert docFile.read_text(encoding="utf-8") == (
|
assert docFile.read_text(encoding="utf-8") == (
|
||||||
"#! New Novel\n\n"
|
"#! New Novel\n\n"
|
||||||
"By Jane Doe\n\n"
|
">> By Jane Doe <<\n\n"
|
||||||
"## New Chapter\n\n\n"
|
"## New Chapter\n\n\n"
|
||||||
"### New Scene\n\n\n"
|
"### New Scene\n\n\n"
|
||||||
"#! Notes: Plot\n\n"
|
"#! Notes: Plot\n\n"
|
||||||
|
|||||||
@@ -32,10 +32,7 @@ from novelwriter.core.docbuild import NWBuildDocument
|
|||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.enum import nwBuildFmt
|
from novelwriter.enum import nwBuildFmt
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||||||
from novelwriter.formats.todocx import (
|
from novelwriter.formats.todocx import ToDocX, _mkTag, _wTag
|
||||||
S_FNOTE, S_HEAD1, S_HEAD2, S_HEAD3, S_HEAD4, S_META, S_NORM, S_SEP,
|
|
||||||
S_TITLE, ToDocX, _mkTag, _wTag
|
|
||||||
)
|
|
||||||
|
|
||||||
from tests.tools import DOCX_IGNORE, cmpFiles
|
from tests.tools import DOCX_IGNORE, cmpFiles
|
||||||
|
|
||||||
@@ -59,6 +56,120 @@ def xmlToText(xElem):
|
|||||||
return rTxt
|
return rTxt
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToDocX_HeadingStyles(mockGUI):
|
||||||
|
"""Test formatting of headings."""
|
||||||
|
project = NWProject()
|
||||||
|
doc = ToDocX(project)
|
||||||
|
doc._isNovel = True
|
||||||
|
doc.initDocument()
|
||||||
|
|
||||||
|
# Title
|
||||||
|
# =====
|
||||||
|
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc._text = "#! Hello World"
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Title" /><w:jc w:val="center" /></w:pPr>'
|
||||||
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Heading Level 1
|
||||||
|
# ===============
|
||||||
|
doc._text = "# Hello World"
|
||||||
|
|
||||||
|
# Plain
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading1" /><w:jc w:val="center" /></w:pPr>'
|
||||||
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Formatted
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.setPartitionFormat(f"Part{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading1" /><w:jc w:val="center" /></w:pPr>'
|
||||||
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>Part</w:t><w:br /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Heading Level 2
|
||||||
|
# ===============
|
||||||
|
doc._text = "## Hello World"
|
||||||
|
|
||||||
|
# Plain
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading2" /></w:pPr>'
|
||||||
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Formatted
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.setChapterFormat(f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading2" /></w:pPr>'
|
||||||
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
|
'<w:r><w:rPr /><w:t>Chapter 2</w:t><w:br /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Heading Level 3
|
||||||
|
# ===============
|
||||||
|
doc._text = "### Hello World"
|
||||||
|
|
||||||
|
# Plain
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading3" /></w:pPr><w:r><w:rPr />'
|
||||||
|
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Formatted
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading3" /></w:pPr>'
|
||||||
|
'<w:r><w:rPr /><w:t>Scene 2</w:t><w:br /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
# Heading Level 4
|
||||||
|
# ===============
|
||||||
|
doc._text = "#### Hello World"
|
||||||
|
|
||||||
|
xTest = ET.Element(_wTag("body"))
|
||||||
|
doc.tokenizeText()
|
||||||
|
doc.doConvert()
|
||||||
|
doc._pars[-1].toXml(xTest)
|
||||||
|
assert xmlToText(xTest) == (
|
||||||
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Heading4" /></w:pPr><w:r><w:rPr />'
|
||||||
|
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToDocX_ParagraphStyles(mockGUI):
|
def testFmtToDocX_ParagraphStyles(mockGUI):
|
||||||
"""Test formatting of paragraphs."""
|
"""Test formatting of paragraphs."""
|
||||||
@@ -71,61 +182,12 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
|
|
||||||
# Normal Text
|
# Normal Text
|
||||||
xTest = ET.Element(_wTag("body"))
|
xTest = ET.Element(_wTag("body"))
|
||||||
doc._blocks = [(BlockTyp.TEXT, "", "Hello World", [], BlockFmt.NONE)]
|
doc._text = "Hello World"
|
||||||
|
doc.tokenizeText()
|
||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr><w:r><w:rPr />'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr><w:r><w:rPr />'
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Title
|
|
||||||
xTest = ET.Element(_wTag("body"))
|
|
||||||
doc._blocks = [(BlockTyp.TITLE, "", "Hello World", [], BlockFmt.NONE)]
|
|
||||||
doc.doConvert()
|
|
||||||
doc._pars[-1].toXml(xTest)
|
|
||||||
assert xmlToText(xTest) == (
|
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_TITLE}" /></w:pPr><w:r><w:rPr />'
|
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Heading Level 1
|
|
||||||
xTest = ET.Element(_wTag("body"))
|
|
||||||
doc._blocks = [(BlockTyp.HEAD1, "", "Hello World", [], BlockFmt.NONE)]
|
|
||||||
doc.doConvert()
|
|
||||||
doc._pars[-1].toXml(xTest)
|
|
||||||
assert xmlToText(xTest) == (
|
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_HEAD1}" /></w:pPr><w:r><w:rPr />'
|
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Heading Level 2
|
|
||||||
xTest = ET.Element(_wTag("body"))
|
|
||||||
doc._blocks = [(BlockTyp.HEAD2, "", "Hello World", [], BlockFmt.NONE)]
|
|
||||||
doc.doConvert()
|
|
||||||
doc._pars[-1].toXml(xTest)
|
|
||||||
assert xmlToText(xTest) == (
|
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_HEAD2}" /></w:pPr><w:r><w:rPr />'
|
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Heading Level 3
|
|
||||||
xTest = ET.Element(_wTag("body"))
|
|
||||||
doc._blocks = [(BlockTyp.HEAD3, "", "Hello World", [], BlockFmt.NONE)]
|
|
||||||
doc.doConvert()
|
|
||||||
doc._pars[-1].toXml(xTest)
|
|
||||||
assert xmlToText(xTest) == (
|
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_HEAD3}" /></w:pPr><w:r><w:rPr />'
|
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Heading Level 4
|
|
||||||
xTest = ET.Element(_wTag("body"))
|
|
||||||
doc._blocks = [(BlockTyp.HEAD4, "", "Hello World", [], BlockFmt.NONE)]
|
|
||||||
doc.doConvert()
|
|
||||||
doc._pars[-1].toXml(xTest)
|
|
||||||
assert xmlToText(xTest) == (
|
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_HEAD4}" /></w:pPr><w:r><w:rPr />'
|
|
||||||
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -135,7 +197,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_SEP}" /></w:pPr><w:r><w:rPr />'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Separator" /></w:pPr><w:r><w:rPr />'
|
||||||
'<w:t>* * *</w:t></w:r></w:p></w:body>'
|
'<w:t>* * *</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -145,7 +207,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr></w:p></w:body>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Synopsis
|
# Synopsis
|
||||||
@@ -155,7 +217,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_META}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="MetaText" /></w:pPr>'
|
||||||
'<w:r><w:rPr><w:b /><w:color w:val="813709" /></w:rPr><w:t>Synopsis:</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:color w:val="813709" /></w:rPr><w:t>Synopsis:</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:color w:val="813709" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
'<w:r><w:rPr><w:color w:val="813709" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
||||||
@@ -169,7 +231,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_META}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="MetaText" /></w:pPr>'
|
||||||
'<w:r><w:rPr><w:b /><w:color w:val="813709" /></w:rPr><w:t>Short Description:</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:color w:val="813709" /></w:rPr><w:t>Short Description:</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:color w:val="813709" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
'<w:r><w:rPr><w:color w:val="813709" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
||||||
@@ -183,7 +245,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_META}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="MetaText" /></w:pPr>'
|
||||||
'<w:r><w:rPr><w:b /><w:color w:val="646464" /></w:rPr><w:t>Comment:</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:color w:val="646464" /></w:rPr><w:t>Comment:</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:color w:val="646464" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
'<w:r><w:rPr><w:color w:val="646464" /></w:rPr><w:t>Hello World</w:t></w:r>'
|
||||||
@@ -197,7 +259,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_META}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="MetaText" /></w:pPr>'
|
||||||
'<w:r><w:rPr><w:b /><w:color w:val="f5871f" /></w:rPr><w:t>Tag:</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:color w:val="f5871f" /></w:rPr><w:t>Tag:</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:color w:val="4271ae" /></w:rPr><w:t>Stuff</w:t></w:r>'
|
'<w:r><w:rPr><w:color w:val="4271ae" /></w:rPr><w:t>Stuff</w:t></w:r>'
|
||||||
@@ -211,7 +273,7 @@ def testFmtToDocX_ParagraphStyles(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_META}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="MetaText" /></w:pPr>'
|
||||||
'<w:r><w:rPr><w:b /><w:color w:val="f5871f" /></w:rPr><w:t>Characters:</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:color w:val="f5871f" /></w:rPr><w:t>Characters:</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:color w:val="4271ae" /></w:rPr><w:t>Jane</w:t></w:r>'
|
'<w:r><w:rPr><w:color w:val="4271ae" /></w:rPr><w:t>Jane</w:t></w:r>'
|
||||||
@@ -245,7 +307,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /><w:jc w:val="left" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /><w:jc w:val="left" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -255,7 +317,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /><w:jc w:val="right" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /><w:jc w:val="right" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -265,7 +327,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /><w:jc w:val="center" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /><w:jc w:val="center" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -275,7 +337,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /><w:jc w:val="both" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /><w:jc w:val="both" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -285,7 +347,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:br w:type="page" /></w:r>'
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r>'
|
||||||
'</w:p></w:body>'
|
'</w:p></w:body>'
|
||||||
@@ -297,7 +359,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r>'
|
||||||
'<w:r><w:br w:type="page" /></w:r>'
|
'<w:r><w:br w:type="page" /></w:r>'
|
||||||
'</w:p></w:body>'
|
'</w:p></w:body>'
|
||||||
@@ -309,7 +371,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" />'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" />'
|
||||||
'<w:spacing w:before="0" w:after="0" w:line="252" /></w:pPr>'
|
'<w:spacing w:before="0" w:after="0" w:line="252" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
@@ -320,7 +382,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" />'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" />'
|
||||||
'<w:ind w:left="880" w:right="880" /></w:pPr>'
|
'<w:ind w:left="880" w:right="880" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
@@ -331,7 +393,7 @@ def testFmtToDocX_ParagraphFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" />'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" />'
|
||||||
'<w:ind w:firstLine="308" /></w:pPr>'
|
'<w:ind w:firstLine="308" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
'<w:r><w:rPr /><w:t>Hello World</w:t></w:r></w:p></w:body>'
|
||||||
)
|
)
|
||||||
@@ -351,7 +413,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve">Text </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve">Text </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:b /></w:rPr><w:t>bold</w:t></w:r>'
|
'<w:r><w:rPr><w:b /></w:rPr><w:t>bold</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve">, </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve">, </w:t></w:r>'
|
||||||
@@ -369,7 +431,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:strike /></w:rPr><w:t xml:space="preserve">nested </w:t></w:r>'
|
'<w:r><w:rPr><w:strike /></w:rPr><w:t xml:space="preserve">nested </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:b /><w:strike /></w:rPr><w:t>bold</w:t></w:r>'
|
'<w:r><w:rPr><w:b /><w:strike /></w:rPr><w:t>bold</w:t></w:r>'
|
||||||
@@ -389,7 +451,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Some super</w:t></w:r>'
|
'<w:r><w:rPr /><w:t>Some super</w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:vertAlign w:val="superscript" /></w:rPr><w:t>script</w:t></w:r>'
|
'<w:r><w:rPr><w:vertAlign w:val="superscript" /></w:rPr><w:t>script</w:t></w:r>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve"> and sub</w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve"> and sub</w:t></w:r>'
|
||||||
@@ -405,7 +467,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:u w:val="single" /></w:rPr>'
|
'<w:r><w:rPr><w:u w:val="single" /></w:rPr>'
|
||||||
'<w:t xml:space="preserve">underlined and </w:t></w:r>'
|
'<w:t xml:space="preserve">underlined and </w:t></w:r>'
|
||||||
@@ -422,7 +484,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Some text.</w:t><w:br /><w:t>Next line</w:t></w:r>'
|
'<w:r><w:rPr /><w:t>Some text.</w:t><w:br /><w:t>Next line</w:t></w:r>'
|
||||||
'</w:p></w:body>'
|
'</w:p></w:body>'
|
||||||
)
|
)
|
||||||
@@ -434,7 +496,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:tab /><w:t>Item 1</w:t><w:tab /><w:t>Item 2</w:t></w:r>'
|
'<w:r><w:rPr /><w:tab /><w:t>Item 1</w:t><w:tab /><w:t>Item 2</w:t></w:r>'
|
||||||
'</w:p></w:body>'
|
'</w:p></w:body>'
|
||||||
)
|
)
|
||||||
@@ -446,7 +508,7 @@ def testFmtToDocX_TextFormatting(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
'<w:r><w:rPr /><w:t xml:space="preserve">Some </w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:b /></w:rPr><w:t>bold</w:t><w:tab /><w:t>text</w:t></w:r>'
|
'<w:r><w:rPr><w:b /></w:rPr><w:t>bold</w:t><w:tab /><w:t>text</w:t></w:r>'
|
||||||
'</w:p></w:body>'
|
'</w:p></w:body>'
|
||||||
@@ -474,7 +536,7 @@ def testFmtToDocX_Footnotes(mockGUI):
|
|||||||
doc.doConvert()
|
doc.doConvert()
|
||||||
doc._pars[-1].toXml(xTest)
|
doc._pars[-1].toXml(xTest)
|
||||||
assert xmlToText(xTest) == (
|
assert xmlToText(xTest) == (
|
||||||
f'<w:body><w:p><w:pPr><w:pStyle w:val="{S_NORM}" /></w:pPr>'
|
'<w:body><w:p><w:pPr><w:pStyle w:val="Normal" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Text with one</w:t></w:r>'
|
'<w:r><w:rPr /><w:t>Text with one</w:t></w:r>'
|
||||||
'<w:r><w:rPr><w:vertAlign w:val="superscript" /></w:rPr>'
|
'<w:r><w:rPr><w:vertAlign w:val="superscript" /></w:rPr>'
|
||||||
'<w:footnoteReference w:id="1" /></w:r>'
|
'<w:footnoteReference w:id="1" /></w:r>'
|
||||||
@@ -493,11 +555,11 @@ def testFmtToDocX_Footnotes(mockGUI):
|
|||||||
doc._footnotesXml()
|
doc._footnotesXml()
|
||||||
assert xmlToText(doc._files["footnotes.xml"].xml) == (
|
assert xmlToText(doc._files["footnotes.xml"].xml) == (
|
||||||
'<w:footnotes>'
|
'<w:footnotes>'
|
||||||
f'<w:footnote w:id="1"><w:p><w:pPr><w:pStyle w:val="{S_FNOTE}" /></w:pPr>'
|
'<w:footnote w:id="1"><w:p><w:pPr><w:pStyle w:val="FootnoteText" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Footnote text A.</w:t></w:r></w:p></w:footnote>'
|
'<w:r><w:rPr /><w:t>Footnote text A.</w:t></w:r></w:p></w:footnote>'
|
||||||
f'<w:footnote w:id="2"><w:p><w:pPr><w:pStyle w:val="{S_FNOTE}" /></w:pPr>'
|
'<w:footnote w:id="2"><w:p><w:pPr><w:pStyle w:val="FootnoteText" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Another footnote.</w:t></w:r></w:p></w:footnote>'
|
'<w:r><w:rPr /><w:t>Another footnote.</w:t></w:r></w:p></w:footnote>'
|
||||||
f'<w:footnote w:id="3"><w:p><w:pPr><w:pStyle w:val="{S_FNOTE}" /></w:pPr>'
|
'<w:footnote w:id="3"><w:p><w:pPr><w:pStyle w:val="FootnoteText" /></w:pPr>'
|
||||||
'<w:r><w:rPr /><w:t>Again?</w:t></w:r></w:p></w:footnote>'
|
'<w:r><w:rPr /><w:t>Again?</w:t></w:r></w:p></w:footnote>'
|
||||||
'</w:footnotes>'
|
'</w:footnotes>'
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
from novelwriter import CONFIG
|
from novelwriter import CONFIG
|
||||||
|
from novelwriter.constants import nwHeadFmt
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||||||
from novelwriter.formats.tohtml import ToHtml
|
from novelwriter.formats.tohtml import ToHtml
|
||||||
@@ -44,32 +45,35 @@ def testFmtToHtml_ConvertHeaders(mockGUI):
|
|||||||
html._isFirst = True
|
html._isFirst = True
|
||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
html._text = "# Partition\n"
|
html._text = "# Title\n"
|
||||||
|
html.setPartitionFormat(f"Part{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
html.tokenizeText()
|
html.tokenizeText()
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html._pages[-1] == (
|
assert html._pages[-1] == (
|
||||||
"<h1 class='title' style='text-align: center;'>Partition</h1>\n"
|
"<h1 class='title' style='text-align: center;'>Part<br>Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
html._text = "## Chapter Title\n"
|
html._text = "## Title\n"
|
||||||
|
html.setChapterFormat(f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
html.tokenizeText()
|
html.tokenizeText()
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html._pages[-1] == (
|
assert html._pages[-1] == (
|
||||||
"<h1 style='page-break-before: always;'>Chapter Title</h1>\n"
|
"<h1 style='page-break-before: always;'>Chapter 1<br>Title</h1>\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
html._text = "### Scene Title\n"
|
html._text = "### Title\n"
|
||||||
|
html.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
html.tokenizeText()
|
html.tokenizeText()
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html._pages[-1] == "<h2>Scene Title</h2>\n"
|
assert html._pages[-1] == "<h2>Scene 1<br>Title</h2>\n"
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
html._text = "#### Section Title\n"
|
html._text = "#### Title\n"
|
||||||
html.tokenizeText()
|
html.tokenizeText()
|
||||||
html.doConvert()
|
html.doConvert()
|
||||||
assert html._pages[-1] == "<h3>Section Title</h3>\n"
|
assert html._pages[-1] == "<h3>Title</h3>\n"
|
||||||
|
|
||||||
# Title
|
# Title
|
||||||
html._text = "#! Title\n"
|
html._text = "#! Title\n"
|
||||||
|
|||||||
@@ -421,8 +421,8 @@ def testFmtToken_HeaderFormat(mockGUI):
|
|||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToken_HeaderStyle(mockGUI):
|
def testFmtToken_HeaderStyleNone(mockGUI):
|
||||||
"""Test the styling of headers in the Tokenizer class."""
|
"""Test header styling disabled."""
|
||||||
project = NWProject()
|
project = NWProject()
|
||||||
tokens = BareTokenizer(project)
|
tokens = BareTokenizer(project)
|
||||||
|
|
||||||
@@ -432,13 +432,12 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
return tokens._blocks[0][4]
|
return tokens._blocks[0][4]
|
||||||
|
|
||||||
# No Styles
|
tokens.setTitleStyle(False, False)
|
||||||
# =========
|
|
||||||
|
|
||||||
tokens.setPartitionStyle(False, False)
|
tokens.setPartitionStyle(False, False)
|
||||||
tokens.setChapterStyle(False, False)
|
tokens.setChapterStyle(False, False)
|
||||||
tokens.setSceneStyle(False, False)
|
tokens.setSceneStyle(False, False)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.NONE
|
||||||
assert tokens._partStyle == BlockFmt.NONE
|
assert tokens._partStyle == BlockFmt.NONE
|
||||||
assert tokens._chapterStyle == BlockFmt.NONE
|
assert tokens._chapterStyle == BlockFmt.NONE
|
||||||
assert tokens._sceneStyle == BlockFmt.NONE
|
assert tokens._sceneStyle == BlockFmt.NONE
|
||||||
@@ -451,7 +450,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -459,7 +458,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Note Docs
|
# Note Docs
|
||||||
@@ -470,7 +469,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -478,16 +477,28 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Center Headers
|
|
||||||
# ==============
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToken_HeaderStyleCenter(mockGUI):
|
||||||
|
"""Test header styling centred."""
|
||||||
|
project = NWProject()
|
||||||
|
tokens = BareTokenizer(project)
|
||||||
|
|
||||||
|
def processStyle(text: str, first: bool) -> BlockFmt:
|
||||||
|
tokens._text = text
|
||||||
|
tokens._isFirst = first
|
||||||
|
tokens.tokenizeText()
|
||||||
|
return tokens._blocks[0][4]
|
||||||
|
|
||||||
|
tokens.setTitleStyle(True, False)
|
||||||
tokens.setPartitionStyle(True, False)
|
tokens.setPartitionStyle(True, False)
|
||||||
tokens.setChapterStyle(True, False)
|
tokens.setChapterStyle(True, False)
|
||||||
tokens.setSceneStyle(True, False)
|
tokens.setSceneStyle(True, False)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.CENTRE
|
||||||
assert tokens._partStyle == BlockFmt.CENTRE
|
assert tokens._partStyle == BlockFmt.CENTRE
|
||||||
assert tokens._chapterStyle == BlockFmt.CENTRE
|
assert tokens._chapterStyle == BlockFmt.CENTRE
|
||||||
assert tokens._sceneStyle == BlockFmt.CENTRE
|
assert tokens._sceneStyle == BlockFmt.CENTRE
|
||||||
@@ -500,7 +511,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.CENTRE
|
assert processStyle("## Chapter\n", False) == BlockFmt.CENTRE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.CENTRE
|
assert processStyle("### Scene\n", False) == BlockFmt.CENTRE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.CENTRE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.CENTRE
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -519,7 +530,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -530,13 +541,25 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Page Break Headers
|
|
||||||
# ==================
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToken_HeaderStylePageBreak(mockGUI):
|
||||||
|
"""Test header styling page break."""
|
||||||
|
project = NWProject()
|
||||||
|
tokens = BareTokenizer(project)
|
||||||
|
|
||||||
|
def processStyle(text: str, first: bool) -> BlockFmt:
|
||||||
|
tokens._text = text
|
||||||
|
tokens._isFirst = first
|
||||||
|
tokens.tokenizeText()
|
||||||
|
return tokens._blocks[0][4]
|
||||||
|
|
||||||
|
tokens.setTitleStyle(False, True)
|
||||||
tokens.setPartitionStyle(False, True)
|
tokens.setPartitionStyle(False, True)
|
||||||
tokens.setChapterStyle(False, True)
|
tokens.setChapterStyle(False, True)
|
||||||
tokens.setSceneStyle(False, True)
|
tokens.setSceneStyle(False, True)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.PBB
|
||||||
assert tokens._partStyle == BlockFmt.PBB
|
assert tokens._partStyle == BlockFmt.PBB
|
||||||
assert tokens._chapterStyle == BlockFmt.PBB
|
assert tokens._chapterStyle == BlockFmt.PBB
|
||||||
assert tokens._sceneStyle == BlockFmt.PBB
|
assert tokens._sceneStyle == BlockFmt.PBB
|
||||||
@@ -549,7 +572,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.PBB
|
assert processStyle("## Chapter\n", False) == BlockFmt.PBB
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.PBB
|
assert processStyle("### Scene\n", False) == BlockFmt.PBB
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.PBB
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.PBB
|
assert processStyle("##! Prologue\n", False) == BlockFmt.PBB
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -557,7 +580,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Note Docs
|
# Note Docs
|
||||||
@@ -568,7 +591,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.PBB
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
# First Document is True
|
# First Document is True
|
||||||
@@ -576,16 +599,28 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
assert processStyle("### Scene\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
assert processStyle("#### Section\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Page Break and Centre Headers
|
|
||||||
# =============================
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToken_HeaderStylePageBreakCenter(mockGUI):
|
||||||
|
"""Test header styling page break and centred."""
|
||||||
|
project = NWProject()
|
||||||
|
tokens = BareTokenizer(project)
|
||||||
|
|
||||||
|
def processStyle(text: str, first: bool) -> BlockFmt:
|
||||||
|
tokens._text = text
|
||||||
|
tokens._isFirst = first
|
||||||
|
tokens.tokenizeText()
|
||||||
|
return tokens._blocks[0][4]
|
||||||
|
|
||||||
|
tokens.setTitleStyle(True, True)
|
||||||
tokens.setPartitionStyle(True, True)
|
tokens.setPartitionStyle(True, True)
|
||||||
tokens.setChapterStyle(True, True)
|
tokens.setChapterStyle(True, True)
|
||||||
tokens.setSceneStyle(True, True)
|
tokens.setSceneStyle(True, True)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert tokens._partStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._partStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert tokens._chapterStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._chapterStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert tokens._sceneStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._sceneStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
@@ -628,15 +663,46 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
assert processStyle("#! My Novel\n", True) == BlockFmt.CENTRE
|
||||||
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", True) == BlockFmt.NONE
|
||||||
|
|
||||||
# Check Separation
|
|
||||||
# ================
|
@pytest.mark.core
|
||||||
|
def testFmtToken_HeaderStyleSeparation(mockGUI):
|
||||||
|
"""Test header styling separation."""
|
||||||
|
project = NWProject()
|
||||||
|
tokens = BareTokenizer(project)
|
||||||
|
|
||||||
|
def processStyle(text: str, first: bool) -> BlockFmt:
|
||||||
|
tokens._text = text
|
||||||
|
tokens._isFirst = first
|
||||||
|
tokens.tokenizeText()
|
||||||
|
return tokens._blocks[0][4]
|
||||||
|
|
||||||
tokens._isNovel = True
|
tokens._isNovel = True
|
||||||
|
|
||||||
# Title Styles
|
# Title Styles
|
||||||
|
tokens.setTitleStyle(True, True)
|
||||||
|
tokens.setPartitionStyle(False, False)
|
||||||
|
tokens.setChapterStyle(False, False)
|
||||||
|
tokens.setSceneStyle(False, False)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
|
assert tokens._partStyle == BlockFmt.NONE
|
||||||
|
assert tokens._chapterStyle == BlockFmt.NONE
|
||||||
|
assert tokens._sceneStyle == BlockFmt.NONE
|
||||||
|
|
||||||
|
assert processStyle("# Title\n", False) == BlockFmt.NONE
|
||||||
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
|
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
|
# Partition Styles
|
||||||
|
tokens.setTitleStyle(False, False)
|
||||||
tokens.setPartitionStyle(True, True)
|
tokens.setPartitionStyle(True, True)
|
||||||
tokens.setChapterStyle(False, False)
|
tokens.setChapterStyle(False, False)
|
||||||
tokens.setSceneStyle(False, False)
|
tokens.setSceneStyle(False, False)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.NONE
|
||||||
assert tokens._partStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._partStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert tokens._chapterStyle == BlockFmt.NONE
|
assert tokens._chapterStyle == BlockFmt.NONE
|
||||||
assert tokens._sceneStyle == BlockFmt.NONE
|
assert tokens._sceneStyle == BlockFmt.NONE
|
||||||
@@ -645,14 +711,16 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
# Chapter Styles
|
# Chapter Styles
|
||||||
|
tokens.setTitleStyle(False, False)
|
||||||
tokens.setPartitionStyle(False, False)
|
tokens.setPartitionStyle(False, False)
|
||||||
tokens.setChapterStyle(True, True)
|
tokens.setChapterStyle(True, True)
|
||||||
tokens.setSceneStyle(False, False)
|
tokens.setSceneStyle(False, False)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.NONE
|
||||||
assert tokens._partStyle == BlockFmt.NONE
|
assert tokens._partStyle == BlockFmt.NONE
|
||||||
assert tokens._chapterStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._chapterStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert tokens._sceneStyle == BlockFmt.NONE
|
assert tokens._sceneStyle == BlockFmt.NONE
|
||||||
@@ -661,14 +729,16 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("## Chapter\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
assert processStyle("### Scene\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("##! Prologue\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
|
|
||||||
# Scene Styles
|
# Scene Styles
|
||||||
|
tokens.setTitleStyle(False, False)
|
||||||
tokens.setPartitionStyle(False, False)
|
tokens.setPartitionStyle(False, False)
|
||||||
tokens.setChapterStyle(False, False)
|
tokens.setChapterStyle(False, False)
|
||||||
tokens.setSceneStyle(True, True)
|
tokens.setSceneStyle(True, True)
|
||||||
|
|
||||||
|
assert tokens._titleStyle == BlockFmt.NONE
|
||||||
assert tokens._partStyle == BlockFmt.NONE
|
assert tokens._partStyle == BlockFmt.NONE
|
||||||
assert tokens._chapterStyle == BlockFmt.NONE
|
assert tokens._chapterStyle == BlockFmt.NONE
|
||||||
assert tokens._sceneStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
assert tokens._sceneStyle == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
@@ -677,7 +747,7 @@ def testFmtToken_HeaderStyle(mockGUI):
|
|||||||
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
assert processStyle("## Chapter\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("### Scene\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("### Scene\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
||||||
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
assert processStyle("#### Section\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("#! My Novel\n", False) == BlockFmt.CENTRE | BlockFmt.PBB
|
assert processStyle("#! My Novel\n", False) == BlockFmt.NONE
|
||||||
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
assert processStyle("##! Prologue\n", False) == BlockFmt.NONE
|
||||||
|
|
||||||
|
|
||||||
@@ -842,13 +912,13 @@ def testFmtToken_MarginFormat(mockGUI):
|
|||||||
]
|
]
|
||||||
assert tokens._raw[-1] == (
|
assert tokens._raw[-1] == (
|
||||||
"Some regular text\n\n"
|
"Some regular text\n\n"
|
||||||
"Some left-aligned text\n\n"
|
"Some left-aligned text <<\n\n"
|
||||||
"Some right-aligned text\n\n"
|
">> Some right-aligned text\n\n"
|
||||||
"Some centered text\n\n"
|
">> Some centered text <<\n\n"
|
||||||
"Left-indented block\n\n"
|
"> Left-indented block\n\n"
|
||||||
"Right-indented block\n\n"
|
"Right-indented block <\n\n"
|
||||||
"Double-indented block\n\n"
|
"> Double-indented block <\n\n"
|
||||||
"Right-indent, right-aligned\n\n\n"
|
">> Right-indent, right-aligned <\n\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1056,72 +1126,121 @@ def testFmtToken_TextFormat(mockGUI):
|
|||||||
tokens._text = "Some **bolded text** on this lines\n"
|
tokens._text = "Some **bolded text** on this lines\n"
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [(
|
assert tokens._blocks == [(
|
||||||
BlockTyp.TEXT, "", "Some bolded text on this lines",
|
BlockTyp.TEXT, "", "Some bolded text on this lines", [
|
||||||
[
|
|
||||||
(5, TextFmt.B_B, ""),
|
(5, TextFmt.B_B, ""),
|
||||||
(16, TextFmt.B_E, ""),
|
(16, TextFmt.B_E, ""),
|
||||||
],
|
], BlockFmt.NONE
|
||||||
BlockFmt.NONE
|
|
||||||
)]
|
)]
|
||||||
assert tokens._raw[-1] == "Some **bolded text** on this lines\n\n"
|
assert tokens._raw[-1] == "Some **bolded text** on this lines\n\n"
|
||||||
|
|
||||||
tokens._text = "Some _italic text_ on this lines\n"
|
tokens._text = "Some _italic text_ on this lines\n"
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [(
|
assert tokens._blocks == [(
|
||||||
BlockTyp.TEXT, "", "Some italic text on this lines",
|
BlockTyp.TEXT, "", "Some italic text on this lines", [
|
||||||
[
|
|
||||||
(5, TextFmt.I_B, ""),
|
(5, TextFmt.I_B, ""),
|
||||||
(16, TextFmt.I_E, ""),
|
(16, TextFmt.I_E, ""),
|
||||||
],
|
], BlockFmt.NONE
|
||||||
BlockFmt.NONE
|
|
||||||
)]
|
)]
|
||||||
assert tokens._raw[-1] == "Some _italic text_ on this lines\n\n"
|
assert tokens._raw[-1] == "Some _italic text_ on this lines\n\n"
|
||||||
|
|
||||||
tokens._text = "Some **_bold italic text_** on this lines\n"
|
tokens._text = "Some **_bold italic text_** on this lines\n"
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [(
|
assert tokens._blocks == [(
|
||||||
BlockTyp.TEXT, "", "Some bold italic text on this lines",
|
BlockTyp.TEXT, "", "Some bold italic text on this lines", [
|
||||||
[
|
|
||||||
(5, TextFmt.B_B, ""),
|
(5, TextFmt.B_B, ""),
|
||||||
(5, TextFmt.I_B, ""),
|
(5, TextFmt.I_B, ""),
|
||||||
(21, TextFmt.I_E, ""),
|
(21, TextFmt.I_E, ""),
|
||||||
(21, TextFmt.B_E, ""),
|
(21, TextFmt.B_E, ""),
|
||||||
],
|
], BlockFmt.NONE
|
||||||
BlockFmt.NONE
|
|
||||||
)]
|
)]
|
||||||
assert tokens._raw[-1] == "Some **_bold italic text_** on this lines\n\n"
|
assert tokens._raw[-1] == "Some **_bold italic text_** on this lines\n\n"
|
||||||
|
|
||||||
tokens._text = "Some ~~strikethrough text~~ on this lines\n"
|
tokens._text = "Some ~~strikethrough text~~ on this lines\n"
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [(
|
assert tokens._blocks == [(
|
||||||
BlockTyp.TEXT, "", "Some strikethrough text on this lines",
|
BlockTyp.TEXT, "", "Some strikethrough text on this lines", [
|
||||||
[
|
|
||||||
(5, TextFmt.D_B, ""),
|
(5, TextFmt.D_B, ""),
|
||||||
(23, TextFmt.D_E, ""),
|
(23, TextFmt.D_E, ""),
|
||||||
],
|
], BlockFmt.NONE
|
||||||
BlockFmt.NONE
|
|
||||||
)]
|
)]
|
||||||
assert tokens._raw[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
assert tokens._raw[-1] == "Some ~~strikethrough text~~ on this lines\n\n"
|
||||||
|
|
||||||
tokens._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
tokens._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
|
||||||
tokens.tokenizeText()
|
tokens.tokenizeText()
|
||||||
assert tokens._blocks == [(
|
assert tokens._blocks == [(
|
||||||
BlockTyp.TEXT, "", "Some nested bold and italic and strikethrough text here",
|
BlockTyp.TEXT, "", "Some nested bold and italic and strikethrough text here", [
|
||||||
[
|
|
||||||
(5, TextFmt.B_B, ""),
|
(5, TextFmt.B_B, ""),
|
||||||
(21, TextFmt.I_B, ""),
|
(21, TextFmt.I_B, ""),
|
||||||
(27, TextFmt.I_E, ""),
|
(27, TextFmt.I_E, ""),
|
||||||
(32, TextFmt.D_B, ""),
|
(32, TextFmt.D_B, ""),
|
||||||
(45, TextFmt.D_E, ""),
|
(45, TextFmt.D_E, ""),
|
||||||
(50, TextFmt.B_E, ""),
|
(50, TextFmt.B_E, ""),
|
||||||
],
|
], BlockFmt.NONE
|
||||||
BlockFmt.NONE
|
|
||||||
)]
|
)]
|
||||||
assert tokens._raw[-1] == (
|
assert tokens._raw[-1] == (
|
||||||
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
"Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.core
|
||||||
|
def testFmtToken_LineBreak(mockGUI):
|
||||||
|
"""Test processing of forced line breaks in the Tokenizer class."""
|
||||||
|
project = NWProject()
|
||||||
|
tokens = BareTokenizer(project)
|
||||||
|
tokens._handle = TMH
|
||||||
|
tokens.setComments(True)
|
||||||
|
|
||||||
|
# They are stripped in headers
|
||||||
|
tokens._text = "## Hello[br] World"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [
|
||||||
|
(BlockTyp.HEAD2, TM1, "Hello World", [], BlockFmt.NONE)
|
||||||
|
]
|
||||||
|
|
||||||
|
# They are stripped in comments
|
||||||
|
tokens._text = "% Hello[br] World"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [(
|
||||||
|
BlockTyp.COMMENT, "", "Comment: Hello World", [
|
||||||
|
(0, TextFmt.B_B, ""), (0, TextFmt.COL_B, "comment"),
|
||||||
|
(8, TextFmt.COL_E, ""), (8, TextFmt.B_E, ""),
|
||||||
|
(9, TextFmt.COL_B, "comment"), (20, TextFmt.COL_E, ""),
|
||||||
|
], BlockFmt.NONE
|
||||||
|
)]
|
||||||
|
|
||||||
|
# They are used in text, with breaks enabled
|
||||||
|
tokens.setKeepLineBreaks(True)
|
||||||
|
tokens._text = "Hello[br]\nWorld"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [
|
||||||
|
(BlockTyp.TEXT, "", "Hello\nWorld", [], BlockFmt.NONE)
|
||||||
|
]
|
||||||
|
|
||||||
|
# They are used in text, with breaks disabled
|
||||||
|
tokens.setKeepLineBreaks(False)
|
||||||
|
tokens._text = "Hello[br]\nWorld"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [
|
||||||
|
(BlockTyp.TEXT, "", "Hello\nWorld", [], BlockFmt.NONE)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Without forced breaks, they are preserved with breaks enabled
|
||||||
|
tokens.setKeepLineBreaks(True)
|
||||||
|
tokens._text = "Hello\nWorld"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [
|
||||||
|
(BlockTyp.TEXT, "", "Hello\nWorld", [], BlockFmt.NONE)
|
||||||
|
]
|
||||||
|
|
||||||
|
# Without forced breaks, they are not preserved with breaks disabled
|
||||||
|
tokens.setKeepLineBreaks(False)
|
||||||
|
tokens._text = "Hello\nWorld"
|
||||||
|
tokens.tokenizeText()
|
||||||
|
assert tokens._blocks == [
|
||||||
|
(BlockTyp.TEXT, "", "Hello World", [], BlockFmt.NONE)
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testFmtToken_Dialogue(mockGUI):
|
def testFmtToken_Dialogue(mockGUI):
|
||||||
"""Test the tokenization of dialogue in the Tokenizer class."""
|
"""Test the tokenization of dialogue in the Tokenizer class."""
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
from novelwriter.constants import nwHeadFmt
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
from novelwriter.formats.shared import BlockFmt, BlockTyp
|
||||||
from novelwriter.formats.tomarkdown import ToMarkdown
|
from novelwriter.formats.tomarkdown import ToMarkdown
|
||||||
@@ -37,22 +38,25 @@ def testFmtToMarkdown_ConvertHeaders(mockGUI):
|
|||||||
md._isFirst = True
|
md._isFirst = True
|
||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
md._text = "# Partition\n"
|
md._text = "# Title\n"
|
||||||
|
md.setPartitionFormat(f"Part{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
md.tokenizeText()
|
md.tokenizeText()
|
||||||
md.doConvert()
|
md.doConvert()
|
||||||
assert md._pages[-1] == "# Partition\n\n"
|
assert md._pages[-1] == "# Part - Title\n\n"
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
md._text = "## Chapter Title\n"
|
md._text = "## Title\n"
|
||||||
|
md.setChapterFormat(f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
md.tokenizeText()
|
md.tokenizeText()
|
||||||
md.doConvert()
|
md.doConvert()
|
||||||
assert md._pages[-1] == "## Chapter Title\n\n"
|
assert md._pages[-1] == "## Chapter 1 - Title\n\n"
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
md._text = "### Scene Title\n"
|
md._text = "### Title\n"
|
||||||
|
md.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
md.tokenizeText()
|
md.tokenizeText()
|
||||||
md.doConvert()
|
md.doConvert()
|
||||||
assert md._pages[-1] == "### Scene Title\n\n"
|
assert md._pages[-1] == "### Scene 1 - Title\n\n"
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
md._text = "#### Section Title\n"
|
md._text = "#### Section Title\n"
|
||||||
|
|||||||
@@ -281,6 +281,7 @@ def testFmtToOdt_ConvertHeaders(mockGUI):
|
|||||||
|
|
||||||
# Header 1
|
# Header 1
|
||||||
odt._text = "# Title\n"
|
odt._text = "# Title\n"
|
||||||
|
odt.setPartitionFormat(f"Part{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
@@ -288,12 +289,14 @@ def testFmtToOdt_ConvertHeaders(mockGUI):
|
|||||||
assert odt.errData == []
|
assert odt.errData == []
|
||||||
assert xmlToText(odt._xText) == (
|
assert xmlToText(odt._xText) == (
|
||||||
'<office:text>'
|
'<office:text>'
|
||||||
'<text:h text:style-name="P1" text:outline-level="1">Title</text:h>'
|
'<text:h text:style-name="P1" text:outline-level="1">Part'
|
||||||
|
'<text:line-break />Title</text:h>'
|
||||||
'</office:text>'
|
'</office:text>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 2
|
# Header 2
|
||||||
odt._text = "## Chapter\n"
|
odt._text = "## Title\n"
|
||||||
|
odt.setChapterFormat(f"Chapter {nwHeadFmt.CH_NUM}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
@@ -301,12 +304,14 @@ def testFmtToOdt_ConvertHeaders(mockGUI):
|
|||||||
assert odt.errData == []
|
assert odt.errData == []
|
||||||
assert xmlToText(odt._xText) == (
|
assert xmlToText(odt._xText) == (
|
||||||
'<office:text>'
|
'<office:text>'
|
||||||
'<text:h text:style-name="P2" text:outline-level="2">Chapter</text:h>'
|
'<text:h text:style-name="P2" text:outline-level="2">Chapter 1'
|
||||||
|
'<text:line-break />Title</text:h>'
|
||||||
'</office:text>'
|
'</office:text>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 3
|
# Header 3
|
||||||
odt._text = "### Scene\n"
|
odt._text = "### Title\n"
|
||||||
|
odt.setSceneFormat(f"Scene {nwHeadFmt.SC_ABS}{nwHeadFmt.BR}{nwHeadFmt.TITLE}")
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
@@ -314,12 +319,13 @@ def testFmtToOdt_ConvertHeaders(mockGUI):
|
|||||||
assert odt.errData == []
|
assert odt.errData == []
|
||||||
assert xmlToText(odt._xText) == (
|
assert xmlToText(odt._xText) == (
|
||||||
'<office:text>'
|
'<office:text>'
|
||||||
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene</text:h>'
|
'<text:h text:style-name="Heading_20_3" text:outline-level="3">Scene 1'
|
||||||
|
'<text:line-break />Title</text:h>'
|
||||||
'</office:text>'
|
'</office:text>'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Header 4
|
# Header 4
|
||||||
odt._text = "#### Section\n"
|
odt._text = "#### Title\n"
|
||||||
odt.tokenizeText()
|
odt.tokenizeText()
|
||||||
odt.initDocument()
|
odt.initDocument()
|
||||||
odt.doConvert()
|
odt.doConvert()
|
||||||
@@ -327,7 +333,7 @@ def testFmtToOdt_ConvertHeaders(mockGUI):
|
|||||||
assert odt.errData == []
|
assert odt.errData == []
|
||||||
assert xmlToText(odt._xText) == (
|
assert xmlToText(odt._xText) == (
|
||||||
'<office:text>'
|
'<office:text>'
|
||||||
'<text:h text:style-name="Heading_20_4" text:outline-level="4">Section</text:h>'
|
'<text:h text:style-name="Heading_20_4" text:outline-level="4">Title</text:h>'
|
||||||
'</office:text>'
|
'</office:text>'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -522,13 +522,18 @@ def testGuiMainMenu_Insert(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd
|
|||||||
nwGUI.mainMenu.aInsShort.activate(QAction.ActionEvent.Trigger)
|
nwGUI.mainMenu.aInsShort.activate(QAction.ActionEvent.Trigger)
|
||||||
assert nwGUI.docEditor.getText() == "Stuff\n%Short: \n"
|
assert nwGUI.docEditor.getText() == "Stuff\n%Short: \n"
|
||||||
|
|
||||||
# Insert Break or Space
|
# Breaks and Vertical Space
|
||||||
# =====================
|
# =========================
|
||||||
|
|
||||||
nwGUI.docEditor.setPlainText("### Stuff\n")
|
nwGUI.docEditor.setPlainText("### Stuff\n")
|
||||||
nwGUI.mainMenu.aInsNewPage.activate(QAction.ActionEvent.Trigger)
|
nwGUI.mainMenu.aInsNewPage.activate(QAction.ActionEvent.Trigger)
|
||||||
assert nwGUI.docEditor.getText() == "[newpage]\n### Stuff\n"
|
assert nwGUI.docEditor.getText() == "[newpage]\n### Stuff\n"
|
||||||
|
|
||||||
|
nwGUI.docEditor.setPlainText("Line OneLine Two\n")
|
||||||
|
nwGUI.docEditor.setCursorPosition(8)
|
||||||
|
nwGUI.mainMenu.aInsLineBreak.activate(QAction.ActionEvent.Trigger)
|
||||||
|
assert nwGUI.docEditor.getText() == "Line One[br]Line Two\n"
|
||||||
|
|
||||||
nwGUI.docEditor.setPlainText("### Stuff\n")
|
nwGUI.docEditor.setPlainText("### Stuff\n")
|
||||||
nwGUI.mainMenu.aInsVSpaceS.activate(QAction.ActionEvent.Trigger)
|
nwGUI.mainMenu.aInsVSpaceS.activate(QAction.ActionEvent.Trigger)
|
||||||
assert nwGUI.docEditor.getText() == "[vspace]\n### Stuff\n"
|
assert nwGUI.docEditor.getText() == "[vspace]\n### Stuff\n"
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ def testTextCounting_preProcessText():
|
|||||||
"[vspace:3]\n\n"
|
"[vspace:3]\n\n"
|
||||||
"[New Page]\n\n"
|
"[New Page]\n\n"
|
||||||
"[footnote:abcd]\n\n"
|
"[footnote:abcd]\n\n"
|
||||||
|
"[br]\n\n"
|
||||||
"Dashes\u2013and even longer\u2014dashes.\n\n"
|
"Dashes\u2013and even longer\u2014dashes.\n\n"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ def testTextCounting_preProcessText():
|
|||||||
"#### Heading Four",
|
"#### Heading Four",
|
||||||
"", "", "",
|
"", "", "",
|
||||||
"A paragraph.", "",
|
"A paragraph.", "",
|
||||||
"", "", "", "", "", "",
|
"", "", "", "", "", "", "", "",
|
||||||
"Dashes and even longer dashes.", ""
|
"Dashes and even longer dashes.", ""
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -68,7 +69,7 @@ def testTextCounting_preProcessText():
|
|||||||
assert preProcessText(text, keepHeaders=False) == [
|
assert preProcessText(text, keepHeaders=False) == [
|
||||||
"", "", "",
|
"", "", "",
|
||||||
"A paragraph.", "",
|
"A paragraph.", "",
|
||||||
"", "", "", "", "", "",
|
"", "", "", "", "", "", "", "",
|
||||||
"Dashes and even longer dashes.", ""
|
"Dashes and even longer dashes.", ""
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -198,6 +198,17 @@ def testTextPatterns_ShortcodesPlain():
|
|||||||
|
|
||||||
assert allMatches(regEx, "one [x]two[/x] three") == []
|
assert allMatches(regEx, "one [x]two[/x] three") == []
|
||||||
|
|
||||||
|
# Line Break Substitution
|
||||||
|
# =======================
|
||||||
|
regEx = REGEX_PATTERNS.lineBreak
|
||||||
|
|
||||||
|
assert regEx.sub("\n", "one[br]two") == "one\ntwo"
|
||||||
|
assert regEx.sub("\n", "one[br]\ntwo") == "one\ntwo"
|
||||||
|
assert regEx.sub("\n", "one[br]\n\ntwo") == "one\n\ntwo"
|
||||||
|
assert regEx.sub("\n", "one[BR]two") == "one\ntwo"
|
||||||
|
assert regEx.sub("\n", "one[BR]\ntwo") == "one\ntwo"
|
||||||
|
assert regEx.sub("\n", "one[BR]\n\ntwo") == "one\n\ntwo"
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testTextPatterns_ShortcodesValue():
|
def testTextPatterns_ShortcodesValue():
|
||||||
|
|||||||
Reference in New Issue
Block a user