novelWriter 2.6b1__py3-none-any.whl → 2.6b2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/METADATA +3 -3
  2. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/RECORD +68 -52
  3. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +49 -10
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  7. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  8. novelwriter/assets/i18n/project_de_DE.json +2 -2
  9. novelwriter/assets/i18n/project_ru_RU.json +11 -0
  10. novelwriter/assets/icons/typicons_dark/icons.conf +7 -0
  11. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
  12. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
  13. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
  14. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
  15. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
  16. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
  17. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
  18. novelwriter/assets/icons/typicons_light/icons.conf +7 -0
  19. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
  20. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
  21. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
  22. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
  23. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
  24. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
  25. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
  26. novelwriter/assets/manual.pdf +0 -0
  27. novelwriter/assets/sample.zip +0 -0
  28. novelwriter/assets/text/credits_en.htm +1 -0
  29. novelwriter/common.py +37 -2
  30. novelwriter/config.py +15 -12
  31. novelwriter/constants.py +24 -9
  32. novelwriter/core/coretools.py +111 -125
  33. novelwriter/core/docbuild.py +3 -2
  34. novelwriter/core/index.py +9 -19
  35. novelwriter/core/item.py +39 -6
  36. novelwriter/core/itemmodel.py +518 -0
  37. novelwriter/core/project.py +67 -89
  38. novelwriter/core/status.py +7 -5
  39. novelwriter/core/tree.py +268 -287
  40. novelwriter/dialogs/docmerge.py +7 -17
  41. novelwriter/dialogs/preferences.py +3 -3
  42. novelwriter/dialogs/projectsettings.py +2 -2
  43. novelwriter/enum.py +7 -0
  44. novelwriter/extensions/configlayout.py +6 -4
  45. novelwriter/formats/todocx.py +34 -38
  46. novelwriter/formats/tohtml.py +14 -15
  47. novelwriter/formats/tokenizer.py +21 -17
  48. novelwriter/formats/toodt.py +53 -124
  49. novelwriter/formats/toqdoc.py +92 -44
  50. novelwriter/gui/doceditor.py +230 -219
  51. novelwriter/gui/docviewer.py +38 -9
  52. novelwriter/gui/docviewerpanel.py +14 -22
  53. novelwriter/gui/itemdetails.py +17 -24
  54. novelwriter/gui/mainmenu.py +13 -8
  55. novelwriter/gui/noveltree.py +12 -12
  56. novelwriter/gui/outline.py +10 -11
  57. novelwriter/gui/projtree.py +548 -1202
  58. novelwriter/gui/search.py +9 -10
  59. novelwriter/gui/theme.py +7 -3
  60. novelwriter/guimain.py +59 -43
  61. novelwriter/shared.py +52 -23
  62. novelwriter/text/patterns.py +17 -5
  63. novelwriter/tools/manusbuild.py +13 -11
  64. novelwriter/tools/manussettings.py +42 -52
  65. novelwriter/types.py +7 -1
  66. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/LICENSE.md +0 -0
  67. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/entry_points.txt +0 -0
  68. {novelWriter-2.6b1.dist-info → novelWriter-2.6b2.dist-info}/top_level.txt +0 -0
@@ -162,23 +162,13 @@ class GuiDocMerge(NDialog):
162
162
  self._data = {}
163
163
  self._data["sHandle"] = sHandle
164
164
  self._data["origItems"] = itemList
165
-
166
165
  self.listBox.clear()
167
166
  for tHandle in itemList:
168
- nwItem = SHARED.project.tree[tHandle]
169
- if nwItem is None or not nwItem.isFileType():
170
- continue
171
-
172
- itemIcon = SHARED.theme.getItemIcon(
173
- nwItem.itemType, nwItem.itemClass, nwItem.itemLayout, nwItem.mainHeading
174
- )
175
-
176
- newItem = QListWidgetItem()
177
- newItem.setIcon(itemIcon)
178
- newItem.setText(nwItem.itemName)
179
- newItem.setData(self.D_HANDLE, tHandle)
180
- newItem.setCheckState(Qt.CheckState.Checked)
181
-
182
- self.listBox.addItem(newItem)
183
-
167
+ if (nwItem := SHARED.project.tree[tHandle]) and nwItem.isFileType():
168
+ item = QListWidgetItem()
169
+ item.setIcon(nwItem.getMainIcon())
170
+ item.setText(nwItem.itemName)
171
+ item.setData(self.D_HANDLE, tHandle)
172
+ item.setCheckState(Qt.CheckState.Checked)
173
+ self.listBox.addItem(item)
184
174
  return
@@ -574,7 +574,7 @@ class GuiPreferences(NDialog):
574
574
  self.dialogLine.setText(CONFIG.dialogLine)
575
575
  self.mainForm.addRow(
576
576
  self.tr("Dialogue line symbols"), self.dialogLine,
577
- self.tr("Lines starting with these symbols are always dialogue.")
577
+ self.tr("Lines starting with any of these symbols are dialogue.")
578
578
  )
579
579
 
580
580
  self.narratorBreak = QLineEdit(self)
@@ -583,8 +583,8 @@ class GuiPreferences(NDialog):
583
583
  self.narratorBreak.setAlignment(QtAlignCenter)
584
584
  self.narratorBreak.setText(CONFIG.narratorBreak)
585
585
  self.mainForm.addRow(
586
- self.tr("Dialogue narrator break symbol"), self.narratorBreak,
587
- self.tr("Symbol to indicate injected narrator break in dialogue")
586
+ self.tr("Narrator break symbol"), self.narratorBreak,
587
+ self.tr("Symbol to indicate a narrator break in dialogue")
588
588
  )
589
589
 
590
590
  self.narratorDialog = QLineEdit(self)
@@ -185,11 +185,11 @@ class GuiProjectSettings(NDialog):
185
185
 
186
186
  if self.statusPage.changed:
187
187
  logger.debug("Updating status labels")
188
- project.data.itemStatus.update(self.statusPage.getNewList())
188
+ project.updateStatus("s", self.statusPage.getNewList())
189
189
 
190
190
  if self.importPage.changed:
191
191
  logger.debug("Updating importance labels")
192
- project.data.itemImport.update(self.importPage.getNewList())
192
+ project.updateStatus("i", self.importPage.getNewList())
193
193
 
194
194
  if self.replacePage.changed:
195
195
  logger.debug("Updating auto-replace settings")
novelwriter/enum.py CHANGED
@@ -75,6 +75,13 @@ class nwTrinary(Enum):
75
75
  POSITIVE = 1
76
76
 
77
77
 
78
+ class nwChange(Enum):
79
+
80
+ CREATE = 0
81
+ UPDATE = 1
82
+ DELETE = 2
83
+
84
+
78
85
  class nwDocMode(Enum):
79
86
 
80
87
  VIEW = 0
@@ -27,7 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
27
27
  """
28
28
  from __future__ import annotations
29
29
 
30
- from PyQt5.QtGui import QColor, QFont, QPalette
30
+ from PyQt5.QtGui import QColor, QFont, QPalette, QPixmap
31
31
  from PyQt5.QtWidgets import (
32
32
  QAbstractButton, QFrame, QHBoxLayout, QLabel, QLayout, QScrollArea,
33
33
  QVBoxLayout, QWidget
@@ -183,7 +183,7 @@ class NScrollableForm(QScrollArea):
183
183
  def addRow(
184
184
  self,
185
185
  label: str | None,
186
- widget: QWidget | list[QWidget | str | int],
186
+ widget: QWidget | list[QWidget | QPixmap | int],
187
187
  helpText: str = "",
188
188
  unit: str | None = None,
189
189
  button: QWidget | None = None,
@@ -200,8 +200,10 @@ class NScrollableForm(QScrollArea):
200
200
  for item in widget:
201
201
  if isinstance(item, QWidget):
202
202
  wBox.addWidget(item)
203
- elif isinstance(item, str):
204
- wBox.addWidget(QLabel(item, self))
203
+ elif isinstance(item, QPixmap):
204
+ icon = QLabel(self)
205
+ icon.setPixmap(item)
206
+ wBox.addWidget(icon)
205
207
  elif isinstance(item, int):
206
208
  wBox.addSpacing(CONFIG.pxInt(item))
207
209
  qWidget = QWidget(self)
@@ -33,7 +33,7 @@ from pathlib import Path
33
33
  from typing import NamedTuple
34
34
  from zipfile import ZIP_DEFLATED, ZipFile
35
35
 
36
- from PyQt5.QtCore import QMarginsF, QSizeF
36
+ from PyQt5.QtCore import QMargins, QSize
37
37
  from PyQt5.QtGui import QColor
38
38
 
39
39
  from novelwriter import __version__
@@ -95,6 +95,11 @@ def _wText(parent: ET.Element, text: str) -> ET.Element:
95
95
  return xmlSubElem(parent, _wTag("t"), text, attrib=attrib)
96
96
 
97
97
 
98
+ def _mmToSz(value: float) -> int:
99
+ """Convert millimetres to internal margin size units"""
100
+ return int(value*20.0*72.0/25.4)
101
+
102
+
98
103
  # Cached
99
104
  W_VAL = _wTag("val")
100
105
 
@@ -182,8 +187,8 @@ class ToDocX(Tokenizer):
182
187
  # Internal
183
188
  self._fontFamily = "Liberation Serif"
184
189
  self._fontSize = 12.0
185
- self._pageSize = QSizeF(210.0, 297.0)
186
- self._pageMargins = QMarginsF(20.0, 20.0, 20.0, 20.0)
190
+ self._pageSize = QSize(_mmToSz(210.0), _mmToSz(297.0))
191
+ self._pageMargins = QMargins(_mmToSz(20.0), _mmToSz(20.0), _mmToSz(20.0), _mmToSz(20.0))
187
192
 
188
193
  # Data Variables
189
194
  self._pars: list[DocXParagraph] = []
@@ -203,8 +208,8 @@ class ToDocX(Tokenizer):
203
208
  self, width: float, height: float, top: float, bottom: float, left: float, right: float
204
209
  ) -> None:
205
210
  """Set the document page size and margins in millimetres."""
206
- self._pageSize = QSizeF(width, height)
207
- self._pageMargins = QMarginsF(left, top, right, bottom)
211
+ self._pageSize = QSize(_mmToSz(width), _mmToSz(height))
212
+ self._pageMargins = QMargins(_mmToSz(left), _mmToSz(top), _mmToSz(right), _mmToSz(bottom))
208
213
  return
209
214
 
210
215
  def setHeaderFormat(self, format: str, offset: int) -> None:
@@ -521,12 +526,6 @@ class ToDocX(Tokenizer):
521
526
  hScale = self._scaleHeads
522
527
  hColor = _docXCol(self._theme.head) if self._colorHeads else None
523
528
  fSz = self._fontSize
524
- fnSz = 0.8 * self._fontSize
525
- fSz0 = (nwStyles.H_SIZES[0] * fSz) if hScale else fSz
526
- fSz1 = (nwStyles.H_SIZES[1] * fSz) if hScale else fSz
527
- fSz2 = (nwStyles.H_SIZES[2] * fSz) if hScale else fSz
528
- fSz3 = (nwStyles.H_SIZES[3] * fSz) if hScale else fSz
529
- fSz4 = (nwStyles.H_SIZES[4] * fSz) if hScale else fSz
530
529
 
531
530
  # Add Normal Style
532
531
  styles.append(DocXParStyle(
@@ -545,12 +544,12 @@ class ToDocX(Tokenizer):
545
544
  styles.append(DocXParStyle(
546
545
  name="Title",
547
546
  styleId=S_TITLE,
548
- size=fSz0,
547
+ size=(nwStyles.H_SIZES[0] * fSz) if hScale else fSz,
549
548
  basedOn=S_NORM,
550
549
  nextStyle=S_NORM,
551
550
  before=fSz * self._marginTitle[0],
552
551
  after=fSz * self._marginTitle[1],
553
- line=fSz0 * self._lineHeight,
552
+ line=fSz * self._lineHeight,
554
553
  level=0,
555
554
  bold=self._boldHeads,
556
555
  ))
@@ -559,12 +558,12 @@ class ToDocX(Tokenizer):
559
558
  styles.append(DocXParStyle(
560
559
  name="Heading 1",
561
560
  styleId=S_HEAD1,
562
- size=fSz1,
561
+ size=(nwStyles.H_SIZES[1] * fSz) if hScale else fSz,
563
562
  basedOn=S_NORM,
564
563
  nextStyle=S_NORM,
565
564
  before=fSz * self._marginHead1[0],
566
565
  after=fSz * self._marginHead1[1],
567
- line=fSz1 * self._lineHeight,
566
+ line=fSz * self._lineHeight,
568
567
  level=0,
569
568
  color=hColor,
570
569
  bold=self._boldHeads,
@@ -574,12 +573,12 @@ class ToDocX(Tokenizer):
574
573
  styles.append(DocXParStyle(
575
574
  name="Heading 2",
576
575
  styleId=S_HEAD2,
577
- size=fSz2,
576
+ size=(nwStyles.H_SIZES[2] * fSz) if hScale else fSz,
578
577
  basedOn=S_NORM,
579
578
  nextStyle=S_NORM,
580
579
  before=fSz * self._marginHead2[0],
581
580
  after=fSz * self._marginHead2[1],
582
- line=fSz2 * self._lineHeight,
581
+ line=fSz * self._lineHeight,
583
582
  level=1,
584
583
  color=hColor,
585
584
  bold=self._boldHeads,
@@ -589,12 +588,12 @@ class ToDocX(Tokenizer):
589
588
  styles.append(DocXParStyle(
590
589
  name="Heading 3",
591
590
  styleId=S_HEAD3,
592
- size=fSz3,
591
+ size=(nwStyles.H_SIZES[3] * fSz) if hScale else fSz,
593
592
  basedOn=S_NORM,
594
593
  nextStyle=S_NORM,
595
594
  before=fSz * self._marginHead3[0],
596
595
  after=fSz * self._marginHead3[1],
597
- line=fSz3 * self._lineHeight,
596
+ line=fSz * self._lineHeight,
598
597
  level=1,
599
598
  color=hColor,
600
599
  bold=self._boldHeads,
@@ -604,12 +603,12 @@ class ToDocX(Tokenizer):
604
603
  styles.append(DocXParStyle(
605
604
  name="Heading 4",
606
605
  styleId=S_HEAD4,
607
- size=fSz4,
606
+ size=(nwStyles.H_SIZES[4] * fSz) if hScale else fSz,
608
607
  basedOn=S_NORM,
609
608
  nextStyle=S_NORM,
610
609
  before=fSz * self._marginHead4[0],
611
610
  after=fSz * self._marginHead4[1],
612
- line=fSz4 * self._lineHeight,
611
+ line=fSz * self._lineHeight,
613
612
  level=1,
614
613
  color=hColor,
615
614
  bold=self._boldHeads,
@@ -653,12 +652,12 @@ class ToDocX(Tokenizer):
653
652
  styles.append(DocXParStyle(
654
653
  name="Footnote Text",
655
654
  styleId=S_FNOTE,
656
- size=fnSz,
655
+ size=nwStyles.T_SMALL * fSz,
657
656
  basedOn=S_NORM,
658
657
  before=0.0,
659
- after=fnSz * self._marginFoot[1],
660
- left=fnSz * self._marginFoot[0],
661
- line=fnSz * self._lineHeight,
658
+ after=fSz * self._marginFoot[1],
659
+ left=fSz * self._marginFoot[0],
660
+ line=fSz * self._lineHeight,
662
661
  ))
663
662
 
664
663
  # Add to Cache
@@ -795,6 +794,7 @@ class ToDocX(Tokenizer):
795
794
  _wTag("before"): str(int(20.0 * firstFloat(style.before))),
796
795
  _wTag("after"): str(int(20.0 * firstFloat(style.after))),
797
796
  _wTag("line"): str(int(20.0 * firstFloat(style.line, size))),
797
+ _wTag("lineRule"): "auto",
798
798
  })
799
799
  if style.left is not None:
800
800
  xmlSubElem(pPr, _wTag("ind"), attrib={
@@ -860,12 +860,10 @@ class ToDocX(Tokenizer):
860
860
  xR = xmlSubElem(xP, _wTag("r"))
861
861
  xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "begin"})
862
862
  xR = xmlSubElem(xP, _wTag("r"))
863
- _wText(xR, " PAGE ")
863
+ xmlSubElem(xR, _wTag("instrText"), "PAGE", attrib={_mkTag("xml", "space"): "preserve"})
864
864
  xR = xmlSubElem(xP, _wTag("r"))
865
865
  xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "separate"})
866
866
  xR = xmlSubElem(xP, _wTag("r"))
867
- _wText(xR, "0")
868
- xR = xmlSubElem(xP, _wTag("r"))
869
867
  xmlSubElem(xR, _wTag("fldChar"), attrib={wFldCT: "end"})
870
868
  if post:
871
869
  xR = xmlSubElem(xP, _wTag("r"))
@@ -933,9 +931,6 @@ class ToDocX(Tokenizer):
933
931
  for par in pars:
934
932
  par.toXml(xBody)
935
933
 
936
- def szScale(value: float) -> str:
937
- return str(int(value*2.0*72.0/2.54))
938
-
939
934
  # Write Settings
940
935
  xSect = xmlSubElem(xBody, _wTag("sectPr"))
941
936
  if hFirst and hDefault:
@@ -952,16 +947,16 @@ class ToDocX(Tokenizer):
952
947
  })
953
948
 
954
949
  xmlSubElem(xSect, _wTag("pgSz"), attrib={
955
- _wTag("w"): szScale(self._pageSize.width()),
956
- _wTag("h"): szScale(self._pageSize.height()),
950
+ _wTag("w"): str(self._pageSize.width()),
951
+ _wTag("h"): str(self._pageSize.height()),
957
952
  _wTag("orient"): "portrait",
958
953
  })
959
954
  xmlSubElem(xSect, _wTag("pgMar"), attrib={
960
- _wTag("top"): szScale(self._pageMargins.top()),
961
- _wTag("right"): szScale(self._pageMargins.right()),
962
- _wTag("bottom"): szScale(self._pageMargins.bottom()),
963
- _wTag("left"): szScale(self._pageMargins.left()),
964
- _wTag("header"): szScale(self._pageMargins.top()/2.0),
955
+ _wTag("top"): str(self._pageMargins.top()),
956
+ _wTag("right"): str(self._pageMargins.right()),
957
+ _wTag("bottom"): str(self._pageMargins.bottom()),
958
+ _wTag("left"): str(self._pageMargins.left()),
959
+ _wTag("header"): str(self._pageMargins.top() - int(35.0*self._fontSize)),
965
960
  _wTag("footer"): "0",
966
961
  _wTag("gutter"): "0",
967
962
  })
@@ -1171,6 +1166,7 @@ class DocXParagraph:
1171
1166
  _wTag("before"): str(int(20.0 * firstFloat(self._topMargin, style.before))),
1172
1167
  _wTag("after"): str(int(20.0 * firstFloat(self._bottomMargin, style.after))),
1173
1168
  _wTag("line"): str(int(20.0 * firstFloat(style.line, style.size))),
1169
+ _wTag("lineRule"): "auto",
1174
1170
  })
1175
1171
  if indent:
1176
1172
  xmlSubElem(pPr, _wTag("ind"), attrib=indent)
@@ -339,26 +339,25 @@ class ToHtml(Tokenizer):
339
339
  if not self._cssStyles:
340
340
  return []
341
341
 
342
- mScale = self._lineHeight/1.15
343
342
  tColor = self._theme.text.name(QtHexRgb)
344
343
  hColor = self._theme.head.name(QtHexRgb) if self._colorHeads else tColor
345
344
  lColor = self._theme.head.name(QtHexRgb)
346
345
  mColor = self._theme.highlight.name(QtHexRgb)
347
346
 
348
- mtH0 = mScale * self._marginTitle[0]
349
- mbH0 = mScale * self._marginTitle[1]
350
- mtH1 = mScale * self._marginHead1[0]
351
- mbH1 = mScale * self._marginHead1[1]
352
- mtH2 = mScale * self._marginHead2[0]
353
- mbH2 = mScale * self._marginHead2[1]
354
- mtH3 = mScale * self._marginHead3[0]
355
- mbH3 = mScale * self._marginHead3[1]
356
- mtH4 = mScale * self._marginHead4[0]
357
- mbH4 = mScale * self._marginHead4[1]
358
- mtTT = mScale * self._marginText[0]
359
- mbTT = mScale * self._marginText[1]
360
- mtSP = mScale * self._marginSep[0]
361
- mbSP = mScale * self._marginSep[1]
347
+ mtH0 = self._marginTitle[0]
348
+ mbH0 = self._marginTitle[1]
349
+ mtH1 = self._marginHead1[0]
350
+ mbH1 = self._marginHead1[1]
351
+ mtH2 = self._marginHead2[0]
352
+ mbH2 = self._marginHead2[1]
353
+ mtH3 = self._marginHead3[0]
354
+ mbH3 = self._marginHead3[1]
355
+ mtH4 = self._marginHead4[0]
356
+ mbH4 = self._marginHead4[1]
357
+ mtTT = self._marginText[0]
358
+ mbTT = self._marginText[1]
359
+ mtSP = self._marginSep[0]
360
+ mbSP = self._marginSep[1]
362
361
 
363
362
  font = self._textFont
364
363
  fFam = font.family()
@@ -35,7 +35,7 @@ from PyQt5.QtCore import QLocale
35
35
  from PyQt5.QtGui import QColor, QFont
36
36
 
37
37
  from novelwriter import CONFIG
38
- from novelwriter.common import checkInt, numberToRoman
38
+ from novelwriter.common import checkInt, fontMatcher, numberToRoman
39
39
  from novelwriter.constants import (
40
40
  nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
41
41
  trConst
@@ -302,9 +302,9 @@ class Tokenizer(ABC):
302
302
  self._sceneStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE
303
303
  return
304
304
 
305
- def setFont(self, font: QFont) -> None:
305
+ def setTextFont(self, font: QFont) -> None:
306
306
  """Set the build font."""
307
- self._textFont = font
307
+ self._textFont = fontMatcher(font)
308
308
  return
309
309
 
310
310
  def setLineHeight(self, height: float) -> None:
@@ -490,22 +490,14 @@ class Tokenizer(ABC):
490
490
  return
491
491
 
492
492
  def doPreProcessing(self) -> None:
493
- """Run trough the various replace dictionaries."""
493
+ """Run pre-processing jobs before the text is tokenized."""
494
494
  # Process the user's auto-replace dictionary
495
- autoReplace = self._project.data.autoReplace
496
- if len(autoReplace) > 0:
495
+ if autoReplace := self._project.data.autoReplace:
497
496
  repDict = {}
498
497
  for aKey, aVal in autoReplace.items():
499
498
  repDict[f"<{aKey}>"] = aVal
500
499
  xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL)
501
500
  self._text = xRep.sub(lambda x: repDict[x.group(0)], self._text)
502
-
503
- # Process the translation map for placeholder characters
504
- self._text = self._text.translate(str.maketrans({
505
- nwUnicode.U_MAPOS: nwUnicode.U_RSQUO,
506
- nwUnicode.U_HBAR: nwUnicode.U_EMDASH,
507
- }))
508
-
509
501
  return
510
502
 
511
503
  def tokenizeText(self) -> None:
@@ -538,13 +530,25 @@ class Tokenizer(ABC):
538
530
  firstIndent = self._firstIndent
539
531
 
540
532
  # Replace all instances of [br] with a placeholder character
541
- text = REGEX_PATTERNS.lineBreak.sub("\uffff", self._text)
533
+ text = REGEX_PATTERNS.lineBreak.sub(nwUnicode.U_NAC2, self._text)
534
+
535
+ # Translation Maps
536
+ transMapA = str.maketrans({
537
+ nwUnicode.U_NAC2: "", # Used when [br] is ignored
538
+ nwUnicode.U_MAPOS: nwUnicode.U_RSQUO,
539
+ nwUnicode.U_HBAR: nwUnicode.U_EMDASH,
540
+ })
541
+ transMapB = str.maketrans({
542
+ nwUnicode.U_NAC2: "\n", # Used when [br] is not ignored
543
+ nwUnicode.U_MAPOS: nwUnicode.U_RSQUO,
544
+ nwUnicode.U_HBAR: nwUnicode.U_EMDASH,
545
+ })
542
546
 
543
547
  nHead = 0
544
548
  tHandle = self._handle or ""
545
549
  tBlocks: list[T_Block] = [B_EMPTY]
546
550
  for bLine in text.splitlines():
547
- aLine = bLine.replace("\uffff", "") # Remove placeholder characters
551
+ aLine = bLine.translate(transMapA)
548
552
  sLine = aLine.strip().lower()
549
553
 
550
554
  # Check for blank lines
@@ -884,7 +888,7 @@ class Tokenizer(ABC):
884
888
  if doJustify and not cStyle & BlockFmt.ALIGNED:
885
889
  cStyle |= BlockFmt.JUSTIFY
886
890
 
887
- pTxt = pLines[0][2].replace("\uffff", "\n")
891
+ pTxt = pLines[0][2].translate(transMapB)
888
892
  sBlocks.append((
889
893
  BlockTyp.TEXT, pLines[0][1], pTxt, pLines[0][3], cStyle
890
894
  ))
@@ -901,7 +905,7 @@ class Tokenizer(ABC):
901
905
  tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aBlock[3])
902
906
  cStyle |= aBlock[4]
903
907
 
904
- pTxt = tTxt[:-1].replace("\uffff", "\n")
908
+ pTxt = tTxt[:-1].translate(transMapB)
905
909
  sBlocks.append((
906
910
  BlockTyp.TEXT, pLines[0][1], pTxt, tFmt, cStyle
907
911
  ))