novelWriter 2.6b1__py3-none-any.whl → 2.6rc1__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 (114) hide show
  1. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/METADATA +4 -4
  2. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/RECORD +114 -98
  3. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/WHEEL +1 -1
  4. novelwriter/__init__.py +50 -11
  5. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  6. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  7. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  8. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  9. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  10. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  11. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  12. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  13. novelwriter/assets/i18n/nw_pl_PL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_ru_RU.qm +0 -0
  16. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  17. novelwriter/assets/i18n/project_de_DE.json +2 -2
  18. novelwriter/assets/i18n/project_ru_RU.json +11 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +7 -0
  20. novelwriter/assets/icons/typicons_dark/mixed_margin-bottom.svg +6 -0
  21. novelwriter/assets/icons/typicons_dark/mixed_margin-left.svg +6 -0
  22. novelwriter/assets/icons/typicons_dark/mixed_margin-right.svg +6 -0
  23. novelwriter/assets/icons/typicons_dark/mixed_margin-top.svg +6 -0
  24. novelwriter/assets/icons/typicons_dark/mixed_size-height.svg +6 -0
  25. novelwriter/assets/icons/typicons_dark/mixed_size-width.svg +6 -0
  26. novelwriter/assets/icons/typicons_dark/nw_toolbar.svg +5 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +7 -0
  28. novelwriter/assets/icons/typicons_light/mixed_margin-bottom.svg +6 -0
  29. novelwriter/assets/icons/typicons_light/mixed_margin-left.svg +6 -0
  30. novelwriter/assets/icons/typicons_light/mixed_margin-right.svg +6 -0
  31. novelwriter/assets/icons/typicons_light/mixed_margin-top.svg +6 -0
  32. novelwriter/assets/icons/typicons_light/mixed_size-height.svg +6 -0
  33. novelwriter/assets/icons/typicons_light/mixed_size-width.svg +6 -0
  34. novelwriter/assets/icons/typicons_light/nw_toolbar.svg +5 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/text/credits_en.htm +1 -0
  38. novelwriter/common.py +38 -3
  39. novelwriter/config.py +19 -13
  40. novelwriter/constants.py +60 -45
  41. novelwriter/core/buildsettings.py +1 -1
  42. novelwriter/core/coretools.py +112 -126
  43. novelwriter/core/docbuild.py +4 -3
  44. novelwriter/core/document.py +1 -1
  45. novelwriter/core/index.py +10 -20
  46. novelwriter/core/item.py +40 -7
  47. novelwriter/core/itemmodel.py +518 -0
  48. novelwriter/core/options.py +1 -1
  49. novelwriter/core/project.py +68 -90
  50. novelwriter/core/projectdata.py +8 -2
  51. novelwriter/core/projectxml.py +1 -1
  52. novelwriter/core/sessions.py +1 -1
  53. novelwriter/core/spellcheck.py +1 -1
  54. novelwriter/core/status.py +24 -8
  55. novelwriter/core/storage.py +1 -1
  56. novelwriter/core/tree.py +269 -288
  57. novelwriter/dialogs/about.py +1 -1
  58. novelwriter/dialogs/docmerge.py +8 -18
  59. novelwriter/dialogs/docsplit.py +1 -1
  60. novelwriter/dialogs/editlabel.py +1 -1
  61. novelwriter/dialogs/preferences.py +4 -4
  62. novelwriter/dialogs/projectsettings.py +148 -98
  63. novelwriter/dialogs/quotes.py +1 -1
  64. novelwriter/dialogs/wordlist.py +11 -10
  65. novelwriter/enum.py +8 -1
  66. novelwriter/error.py +2 -2
  67. novelwriter/extensions/configlayout.py +7 -5
  68. novelwriter/extensions/eventfilters.py +1 -1
  69. novelwriter/extensions/modified.py +17 -5
  70. novelwriter/extensions/novelselector.py +1 -1
  71. novelwriter/extensions/pagedsidebar.py +4 -4
  72. novelwriter/extensions/progressbars.py +4 -4
  73. novelwriter/extensions/statusled.py +3 -3
  74. novelwriter/extensions/switch.py +3 -3
  75. novelwriter/extensions/switchbox.py +1 -1
  76. novelwriter/extensions/versioninfo.py +1 -1
  77. novelwriter/formats/shared.py +1 -1
  78. novelwriter/formats/todocx.py +35 -39
  79. novelwriter/formats/tohtml.py +15 -16
  80. novelwriter/formats/tokenizer.py +26 -22
  81. novelwriter/formats/tomarkdown.py +1 -1
  82. novelwriter/formats/toodt.py +54 -125
  83. novelwriter/formats/toqdoc.py +93 -45
  84. novelwriter/formats/toraw.py +1 -1
  85. novelwriter/gui/doceditor.py +233 -220
  86. novelwriter/gui/dochighlight.py +1 -1
  87. novelwriter/gui/docviewer.py +39 -10
  88. novelwriter/gui/docviewerpanel.py +15 -23
  89. novelwriter/gui/editordocument.py +1 -1
  90. novelwriter/gui/itemdetails.py +20 -27
  91. novelwriter/gui/mainmenu.py +14 -9
  92. novelwriter/gui/noveltree.py +13 -13
  93. novelwriter/gui/outline.py +18 -20
  94. novelwriter/gui/projtree.py +545 -1201
  95. novelwriter/gui/search.py +11 -19
  96. novelwriter/gui/sidebar.py +1 -1
  97. novelwriter/gui/statusbar.py +20 -3
  98. novelwriter/gui/theme.py +8 -4
  99. novelwriter/guimain.py +60 -48
  100. novelwriter/shared.py +53 -24
  101. novelwriter/text/counting.py +1 -1
  102. novelwriter/text/patterns.py +18 -6
  103. novelwriter/tools/dictionaries.py +1 -1
  104. novelwriter/tools/lipsum.py +1 -1
  105. novelwriter/tools/manusbuild.py +14 -12
  106. novelwriter/tools/manuscript.py +7 -7
  107. novelwriter/tools/manussettings.py +43 -53
  108. novelwriter/tools/noveldetails.py +1 -1
  109. novelwriter/tools/welcome.py +1 -1
  110. novelwriter/tools/writingstats.py +1 -1
  111. novelwriter/types.py +9 -3
  112. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/LICENSE.md +0 -0
  113. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/entry_points.txt +0 -0
  114. {novelWriter-2.6b1.dist-info → novelWriter-2.6rc1.dist-info}/top_level.txt +0 -0
@@ -10,7 +10,7 @@ Created: 2024-01-26 [2.3b1] NFixedPage
10
10
  Created: 2024-03-12 [2.4b1] NWrappedWidgetBox
11
11
 
12
12
  This file is a part of novelWriter
13
- Copyright 2018–2024, Veronica Berglyd Olsen
13
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
14
14
 
15
15
  This program is free software: you can redistribute it and/or modify
16
16
  it under the terms of the GNU General Public License as published by
@@ -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)
@@ -7,7 +7,7 @@ Created: 2023-08-31 [2.1rc1] WheelEventFilter
7
7
  Created: 2023-11-28 [2.2] StatusTipFilter
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -10,7 +10,7 @@ Created: 2024-05-01 [2.5b1] NToolDialog
10
10
  Created: 2024-05-01 [2.5b1] NNonBlockingDialog
11
11
 
12
12
  This file is a part of novelWriter
13
- Copyright 2018–2024, Veronica Berglyd Olsen
13
+ Copyright (C) 2024 Veronica Berglyd Olsen and novelWriter contributors
14
14
 
15
15
  This program is free software: you can redistribute it and/or modify
16
16
  it under the terms of the GNU General Public License as published by
@@ -30,14 +30,15 @@ from __future__ import annotations
30
30
  from enum import Enum
31
31
  from typing import TYPE_CHECKING
32
32
 
33
- from PyQt5.QtCore import QSize, Qt, pyqtSlot
34
- from PyQt5.QtGui import QWheelEvent
33
+ from PyQt5.QtCore import QSize, Qt, pyqtSignal, pyqtSlot
34
+ from PyQt5.QtGui import QMouseEvent, QWheelEvent
35
35
  from PyQt5.QtWidgets import (
36
- QApplication, QComboBox, QDialog, QDoubleSpinBox, QSpinBox, QToolButton,
37
- QWidget
36
+ QApplication, QComboBox, QDialog, QDoubleSpinBox, QLabel, QSpinBox,
37
+ QToolButton, QWidget
38
38
  )
39
39
 
40
40
  from novelwriter import CONFIG, SHARED
41
+ from novelwriter.types import QtMouseLeft
41
42
 
42
43
  if TYPE_CHECKING: # pragma: no cover
43
44
  from novelwriter.guimain import GuiMain
@@ -196,3 +197,14 @@ class NIconToggleButton(QToolButton):
196
197
  iconSize = self.iconSize()
197
198
  self.setIcon(SHARED.theme.getToggleIcon(iconKey, (iconSize.width(), iconSize.height())))
198
199
  return
200
+
201
+
202
+ class NClickableLabel(QLabel):
203
+
204
+ mouseClicked = pyqtSignal()
205
+
206
+ def mousePressEvent(self, event: QMouseEvent) -> None:
207
+ """Capture a left mouse click and emit its signal."""
208
+ if event.button() == QtMouseLeft:
209
+ self.mouseClicked.emit()
210
+ return super().mousePressEvent(event)
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2022-11-17 [2.0] NovelSelector
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2022 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -8,7 +8,7 @@ Created: 2023-02-21 [2.1b1] NPagedToolButton
8
8
  Created: 2023-02-21 [2.1b1] NPagedToolLabel
9
9
 
10
10
  This file is a part of novelWriter
11
- Copyright 2018–2024, Veronica Berglyd Olsen
11
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
12
12
 
13
13
  This program is free software: you can redistribute it and/or modify
14
14
  it under the terms of the GNU General Public License as published by
@@ -33,7 +33,7 @@ from PyQt5.QtWidgets import (
33
33
  )
34
34
 
35
35
  from novelwriter.types import (
36
- QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen, QtPaintAnitAlias,
36
+ QtAlignLeft, QtMouseOver, QtNoBrush, QtNoPen, QtPaintAntiAlias,
37
37
  QtSizeExpanding, QtSizeFixed
38
38
  )
39
39
 
@@ -146,7 +146,7 @@ class _PagedToolButton(QToolButton):
146
146
  opt.initFrom(self)
147
147
 
148
148
  paint = QPainter(self)
149
- paint.setRenderHint(QtPaintAnitAlias, True)
149
+ paint.setRenderHint(QtPaintAntiAlias, True)
150
150
  paint.setPen(QtNoPen)
151
151
  paint.setBrush(QtNoBrush)
152
152
 
@@ -213,7 +213,7 @@ class _NPagedToolLabel(QLabel):
213
213
  label that matches the button style.
214
214
  """
215
215
  paint = QPainter(self)
216
- paint.setRenderHint(QtPaintAnitAlias, True)
216
+ paint.setRenderHint(QtPaintAntiAlias, True)
217
217
  paint.setPen(QtNoPen)
218
218
 
219
219
  width = self.width()
@@ -7,7 +7,7 @@ Created: 2023-06-07 [2.1b1] NProgressCircle
7
7
  Created: 2023-06-09 [2.1b1] NProgressSimple
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -31,7 +31,7 @@ from PyQt5.QtGui import QBrush, QColor, QPainter, QPaintEvent, QPen
31
31
  from PyQt5.QtWidgets import QProgressBar, QWidget
32
32
 
33
33
  from novelwriter.types import (
34
- QtAlignCenter, QtPaintAnitAlias, QtRoundCap, QtSizeFixed, QtSolidLine,
34
+ QtAlignCenter, QtPaintAntiAlias, QtRoundCap, QtSizeFixed, QtSolidLine,
35
35
  QtTransparent
36
36
  )
37
37
 
@@ -91,7 +91,7 @@ class NProgressCircle(QProgressBar):
91
91
  progress = 100.0*self.value()/self.maximum()
92
92
  angle = ceil(16*3.6*progress)
93
93
  painter = QPainter(self)
94
- painter.setRenderHint(QtPaintAnitAlias, True)
94
+ painter.setRenderHint(QtPaintAntiAlias, True)
95
95
  painter.setPen(self._dPen)
96
96
  painter.setBrush(self._dBrush)
97
97
  painter.drawEllipse(self._dRect)
@@ -119,7 +119,7 @@ class NProgressSimple(QProgressBar):
119
119
  if (value := self.value()) > 0:
120
120
  progress = ceil(self.width()*float(value)/self.maximum())
121
121
  painter = QPainter(self)
122
- painter.setRenderHint(QtPaintAnitAlias, True)
122
+ painter.setRenderHint(QtPaintAntiAlias, True)
123
123
  painter.setPen(self.palette().highlight().color())
124
124
  painter.setBrush(self.palette().highlight())
125
125
  painter.drawRect(0, 0, progress, self.height())
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2020-05-17 [0.5.1]
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -30,7 +30,7 @@ from PyQt5.QtWidgets import QAbstractButton, QWidget
30
30
 
31
31
  from novelwriter import CONFIG
32
32
  from novelwriter.enum import nwTrinary
33
- from novelwriter.types import QtBlack, QtPaintAnitAlias
33
+ from novelwriter.types import QtBlack, QtPaintAntiAlias
34
34
 
35
35
  logger = logging.getLogger(__name__)
36
36
 
@@ -81,7 +81,7 @@ class StatusLED(QAbstractButton):
81
81
  def paintEvent(self, event: QPaintEvent) -> None:
82
82
  """Draw the LED."""
83
83
  painter = QPainter(self)
84
- painter.setRenderHint(QtPaintAnitAlias, True)
84
+ painter.setRenderHint(QtPaintAntiAlias, True)
85
85
  painter.setPen(self.palette().windowText().color())
86
86
  painter.setBrush(self._color)
87
87
  painter.setOpacity(1.0)
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2020-05-03 [0.4.5]
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -28,7 +28,7 @@ from PyQt5.QtGui import QMouseEvent, QPainter, QPaintEvent, QResizeEvent
28
28
  from PyQt5.QtWidgets import QAbstractButton, QWidget
29
29
 
30
30
  from novelwriter import CONFIG, SHARED
31
- from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAnitAlias, QtSizeFixed
31
+ from novelwriter.types import QtMouseLeft, QtNoPen, QtPaintAntiAlias, QtSizeFixed
32
32
 
33
33
 
34
34
  class NSwitch(QAbstractButton):
@@ -90,7 +90,7 @@ class NSwitch(QAbstractButton):
90
90
  def paintEvent(self, event: QPaintEvent) -> None:
91
91
  """Drawing the switch itself."""
92
92
  painter = QPainter(self)
93
- painter.setRenderHint(QtPaintAnitAlias, True)
93
+ painter.setRenderHint(QtPaintAntiAlias, True)
94
94
  painter.setPen(QtNoPen)
95
95
 
96
96
  palette = self.palette()
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2023-04-16 [2.1b1]
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2023 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2024-02-14 [2.3b1] VersionInfoWidget
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2024 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2024-10-21 [2.6b1]
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2024 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -7,7 +7,7 @@ Created: 2024-10-18 [2.6b1] ToDocX
7
7
  Created: 2024-10-18 [2.6b1] DocXParagraph
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2024 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -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)
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2019-05-07 [0.0.1] ToHtml
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by
@@ -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()
@@ -7,7 +7,7 @@ Created: 2019-05-05 [0.0.1] Tokenizer
7
7
  Created: 2023-05-23 [2.1b1] HeadingFormatter
8
8
 
9
9
  This file is a part of novelWriter
10
- Copyright 2018–2024, Veronica Berglyd Olsen
10
+ Copyright (C) 2019 Veronica Berglyd Olsen and novelWriter contributors
11
11
 
12
12
  This program is free software: you can redistribute it and/or modify
13
13
  it under the terms of the GNU General Public License as published by
@@ -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
  ))
@@ -942,11 +946,11 @@ class Tokenizer(ABC):
942
946
  titleCount = self._counts.get(nwStats.TITLES, 0)
943
947
  paragraphCount = self._counts.get(nwStats.PARAGRAPHS, 0)
944
948
 
945
- allWords = self._counts.get(nwStats.WORDS_ALL, 0)
949
+ allWords = self._counts.get(nwStats.WORDS, 0)
946
950
  textWords = self._counts.get(nwStats.WORDS_TEXT, 0)
947
951
  titleWords = self._counts.get(nwStats.WORDS_TITLE, 0)
948
952
 
949
- allChars = self._counts.get(nwStats.CHARS_ALL, 0)
953
+ allChars = self._counts.get(nwStats.CHARS, 0)
950
954
  textChars = self._counts.get(nwStats.CHARS_TEXT, 0)
951
955
  titleChars = self._counts.get(nwStats.CHARS_TITLE, 0)
952
956
 
@@ -1000,11 +1004,11 @@ class Tokenizer(ABC):
1000
1004
  self._counts[nwStats.TITLES] = titleCount
1001
1005
  self._counts[nwStats.PARAGRAPHS] = paragraphCount
1002
1006
 
1003
- self._counts[nwStats.WORDS_ALL] = allWords
1007
+ self._counts[nwStats.WORDS] = allWords
1004
1008
  self._counts[nwStats.WORDS_TEXT] = textWords
1005
1009
  self._counts[nwStats.WORDS_TITLE] = titleWords
1006
1010
 
1007
- self._counts[nwStats.CHARS_ALL] = allChars
1011
+ self._counts[nwStats.CHARS] = allChars
1008
1012
  self._counts[nwStats.CHARS_TEXT] = textChars
1009
1013
  self._counts[nwStats.CHARS_TITLE] = titleChars
1010
1014
 
@@ -6,7 +6,7 @@ File History:
6
6
  Created: 2021-02-06 [1.2b1] ToMarkdown
7
7
 
8
8
  This file is a part of novelWriter
9
- Copyright 2018–2024, Veronica Berglyd Olsen
9
+ Copyright (C) 2021 Veronica Berglyd Olsen and novelWriter contributors
10
10
 
11
11
  This program is free software: you can redistribute it and/or modify
12
12
  it under the terms of the GNU General Public License as published by