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
@@ -43,7 +43,7 @@ from novelwriter.constants import nwHeadFmt, nwStyles
43
43
  from novelwriter.core.project import NWProject
44
44
  from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape
45
45
  from novelwriter.formats.tokenizer import Tokenizer
46
- from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb
46
+ from novelwriter.types import FONT_STYLE, QtHexRgb
47
47
 
48
48
  logger = logging.getLogger(__name__)
49
49
 
@@ -173,44 +173,10 @@ class ToOdt(Tokenizer):
173
173
  self._fontStyle = "normal"
174
174
  self._fontPitch = "variable"
175
175
  self._fontBold = "bold"
176
- self._fSizeTitle = "30pt"
177
- self._fSizeHead1 = "24pt"
178
- self._fSizeHead2 = "20pt"
179
- self._fSizeHead3 = "16pt"
180
- self._fSizeHead4 = "14pt"
181
- self._fSizeHead = "14pt"
182
- self._fSizeText = "12pt"
183
- self._fSizeFoot = "10pt"
184
- self._fLineHeight = "115%"
185
176
  self._fBlockIndent = "1.693cm"
186
- self._fTextIndent = "0.499cm"
187
177
  self._dLanguage = "en"
188
178
  self._dCountry = "GB"
189
179
 
190
- # Text Margins
191
- self._mTopTitle = "0.423cm"
192
- self._mTopHead1 = "0.423cm"
193
- self._mTopHead2 = "0.353cm"
194
- self._mTopHead3 = "0.247cm"
195
- self._mTopHead4 = "0.247cm"
196
- self._mTopHead = "0.423cm"
197
- self._mTopText = "0.000cm"
198
- self._mTopMeta = "0.000cm"
199
- self._mTopSep = "0.247cm"
200
-
201
- self._mBotTitle = "0.212cm"
202
- self._mBotHead1 = "0.212cm"
203
- self._mBotHead2 = "0.212cm"
204
- self._mBotHead3 = "0.212cm"
205
- self._mBotHead4 = "0.212cm"
206
- self._mBotHead = "0.212cm"
207
- self._mBotText = "0.247cm"
208
- self._mBotMeta = "0.106cm"
209
- self._mBotSep = "0.247cm"
210
-
211
- self._mBotFoot = "0.106cm"
212
- self._mLeftFoot = "0.600cm"
213
-
214
180
  # Document Size and Margins
215
181
  self._mDocWidth = "21.0cm"
216
182
  self._mDocHeight = "29.7cm"
@@ -254,60 +220,16 @@ class ToOdt(Tokenizer):
254
220
  # Initialise Variables
255
221
  # ====================
256
222
 
257
- intWeight = FONT_WEIGHTS.get(self._textFont.weight(), 400)
258
- fontWeight = str(intWeight)
259
- fontBold = str(min(intWeight + 300, 900))
260
-
261
223
  lang, _, country = self._dLocale.name().partition("_")
262
224
  self._dLanguage = lang or self._dLanguage
263
225
  self._dCountry = country or self._dCountry
264
226
 
265
- self._fontFamily = self._textFont.family()
266
- self._fontSize = self._textFont.pointSize()
267
- self._fontWeight = FONT_WEIGHT_MAP.get(fontWeight, fontWeight)
268
- self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal")
269
- self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable"
270
- self._fontBold = FONT_WEIGHT_MAP.get(fontBold, fontBold)
271
- self._headWeight = self._fontBold if self._boldHeads else None
272
-
273
- hScale = self._scaleHeads
274
- self._fSizeTitle = self._emToPt(nwStyles.H_SIZES[0] if hScale else 1.0) # Was 2.50
275
- self._fSizeHead1 = self._emToPt(nwStyles.H_SIZES[1] if hScale else 1.0) # Was 2.00
276
- self._fSizeHead2 = self._emToPt(nwStyles.H_SIZES[2] if hScale else 1.0) # Was 1.60
277
- self._fSizeHead3 = self._emToPt(nwStyles.H_SIZES[3] if hScale else 1.0) # Was 1.30
278
- self._fSizeHead4 = self._emToPt(nwStyles.H_SIZES[4] if hScale else 1.0) # Was 1.15
279
- self._fSizeHead = self._emToPt(nwStyles.H_SIZES[4] if hScale else 1.0) # Was 1.15
280
- self._fSizeText = self._emToPt(1.0)
281
- self._fSizeFoot = self._emToPt(0.8)
282
-
283
- mScale = self._lineHeight/1.15
284
-
285
- self._mTopTitle = self._emToCm(mScale * self._marginTitle[0])
286
- self._mTopHead1 = self._emToCm(mScale * self._marginHead1[0])
287
- self._mTopHead2 = self._emToCm(mScale * self._marginHead2[0])
288
- self._mTopHead3 = self._emToCm(mScale * self._marginHead3[0])
289
- self._mTopHead4 = self._emToCm(mScale * self._marginHead4[0])
290
- self._mTopHead = self._emToCm(mScale * self._marginHead4[0])
291
- self._mTopText = self._emToCm(mScale * self._marginText[0])
292
- self._mTopMeta = self._emToCm(mScale * self._marginMeta[0])
293
- self._mTopSep = self._emToCm(mScale * self._marginSep[0])
294
-
295
- self._mBotTitle = self._emToCm(mScale * self._marginTitle[1])
296
- self._mBotHead1 = self._emToCm(mScale * self._marginHead1[1])
297
- self._mBotHead2 = self._emToCm(mScale * self._marginHead2[1])
298
- self._mBotHead3 = self._emToCm(mScale * self._marginHead3[1])
299
- self._mBotHead4 = self._emToCm(mScale * self._marginHead4[1])
300
- self._mBotHead = self._emToCm(mScale * self._marginHead4[1])
301
- self._mBotText = self._emToCm(mScale * self._marginText[1])
302
- self._mBotMeta = self._emToCm(mScale * self._marginMeta[1])
303
- self._mBotSep = self._emToCm(mScale * self._marginSep[1])
304
-
305
- self._mLeftFoot = self._emToCm(self._marginFoot[0])
306
- self._mBotFoot = self._emToCm(self._marginFoot[1])
307
-
308
- self._fLineHeight = f"{round(100 * self._lineHeight):d}%"
227
+ self._fontFamily = self._textFont.family()
228
+ self._fontSize = self._textFont.pointSize()
229
+ self._fontStyle = FONT_STYLE.get(self._textFont.style(), "normal")
230
+ self._fontPitch = "fixed" if self._textFont.fixedPitch() else "variable"
231
+ self._headWeight = self._fontBold if self._boldHeads else None
309
232
  self._fBlockIndent = self._emToCm(self._blockIndent)
310
- self._fTextIndent = self._emToCm(self._firstWidth)
311
233
 
312
234
  # Clear Errors
313
235
  self._errData = []
@@ -744,7 +666,7 @@ class ToOdt(Tokenizer):
744
666
 
745
667
  def _emToCm(self, value: float) -> str:
746
668
  """Converts an em value to centimetres."""
747
- return f"{value*2.54/72*self._fontSize:.3f}cm"
669
+ return f"{value*self._fontSize*2.54/72.0:.3f}cm"
748
670
 
749
671
  def _emToPt(self, scale: float) -> str:
750
672
  """Compute relative font size in points."""
@@ -772,16 +694,19 @@ class ToOdt(Tokenizer):
772
694
 
773
695
  xHead = ET.SubElement(xPage, _mkTag("style", "header-style"))
774
696
  ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib={
775
- _mkTag("fo", "min-height"): "0.600cm",
697
+ _mkTag("fo", "min-height"): self._emToCm(1.5),
776
698
  _mkTag("fo", "margin-left"): "0.000cm",
777
699
  _mkTag("fo", "margin-right"): "0.000cm",
778
- _mkTag("fo", "margin-bottom"): "0.500cm",
700
+ _mkTag("fo", "margin-bottom"): self._emToCm(0.5),
779
701
  })
780
702
 
781
703
  return
782
704
 
783
705
  def _defaultStyles(self) -> None:
784
706
  """Set the default styles."""
707
+ hScale = self._scaleHeads
708
+ textSize = self._emToPt(nwStyles.T_NORMAL)
709
+
785
710
  # Add Paragraph Family Style
786
711
  xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib={
787
712
  _mkTag("style", "family"): "paragraph",
@@ -796,7 +721,7 @@ class ToOdt(Tokenizer):
796
721
  _mkTag("fo", "font-family"): self._fontFamily,
797
722
  _mkTag("fo", "font-weight"): self._fontWeight,
798
723
  _mkTag("fo", "font-style"): self._fontStyle,
799
- _mkTag("fo", "font-size"): self._fSizeText,
724
+ _mkTag("fo", "font-size"): textSize,
800
725
  _mkTag("fo", "language"): self._dLanguage,
801
726
  _mkTag("fo", "country"): self._dCountry,
802
727
  })
@@ -812,7 +737,7 @@ class ToOdt(Tokenizer):
812
737
  _mkTag("fo", "font-family"): self._fontFamily,
813
738
  _mkTag("fo", "font-weight"): self._fontWeight,
814
739
  _mkTag("fo", "font-style"): self._fontStyle,
815
- _mkTag("fo", "font-size"): self._fSizeText,
740
+ _mkTag("fo", "font-size"): textSize,
816
741
  })
817
742
 
818
743
  # Add Default Heading Style
@@ -824,8 +749,8 @@ class ToOdt(Tokenizer):
824
749
  _mkTag("style", "class"): "text",
825
750
  })
826
751
  ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib={
827
- _mkTag("fo", "margin-top"): self._mTopHead,
828
- _mkTag("fo", "margin-bottom"): self._mBotHead,
752
+ _mkTag("fo", "margin-top"): self._emToCm(self._marginHead4[0]),
753
+ _mkTag("fo", "margin-bottom"): self._emToCm(self._marginHead4[1]),
829
754
  _mkTag("fo", "keep-with-next"): "always",
830
755
  })
831
756
  ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={
@@ -833,7 +758,7 @@ class ToOdt(Tokenizer):
833
758
  _mkTag("fo", "font-family"): self._fontFamily,
834
759
  _mkTag("fo", "font-weight"): self._fontWeight,
835
760
  _mkTag("fo", "font-style"): self._fontStyle,
836
- _mkTag("fo", "font-size"): self._fSizeHead,
761
+ _mkTag("fo", "font-size"): self._emToPt(nwStyles.H_SIZES[4] if hScale else 1.0),
837
762
  })
838
763
 
839
764
  # Add Header and Footer Styles
@@ -857,20 +782,24 @@ class ToOdt(Tokenizer):
857
782
 
858
783
  def _useableStyles(self) -> None:
859
784
  """Set the usable styles."""
785
+ hScale = self._scaleHeads
860
786
  hColor = self._theme.head if self._colorHeads else None
861
787
 
788
+ textSize = self._emToPt(nwStyles.T_NORMAL)
789
+ lineHeight = f"{round(100 * self._lineHeight):d}%"
790
+
862
791
  # Add Text Body Style
863
792
  style = ODTParagraphStyle(S_TEXT)
864
793
  style.setDisplayName("Text body")
865
794
  style.setParentStyleName("Standard")
866
795
  style.setClass("text")
867
- style.setMarginTop(self._mTopText)
868
- style.setMarginBottom(self._mBotText)
869
- style.setLineHeight(self._fLineHeight)
796
+ style.setMarginTop(self._emToCm(self._marginText[0]))
797
+ style.setMarginBottom(self._emToCm(self._marginText[1]))
798
+ style.setLineHeight(lineHeight)
870
799
  style.setTextAlign(self._defaultAlign)
871
800
  style.setFontName(self._fontFamily)
872
801
  style.setFontFamily(self._fontFamily)
873
- style.setFontSize(self._fSizeText)
802
+ style.setFontSize(textSize)
874
803
  style.setFontWeight(self._fontWeight)
875
804
  style.packXML(self._xStyl)
876
805
  self._mainPara[style.name] = style
@@ -880,7 +809,7 @@ class ToOdt(Tokenizer):
880
809
  style.setDisplayName("First line indent")
881
810
  style.setParentStyleName(S_TEXT)
882
811
  style.setClass("text")
883
- style.setTextIndent(self._fTextIndent)
812
+ style.setTextIndent(self._emToCm(self._firstWidth))
884
813
  style.packXML(self._xStyl)
885
814
  self._mainPara[style.name] = style
886
815
 
@@ -889,12 +818,12 @@ class ToOdt(Tokenizer):
889
818
  style.setDisplayName("Text Meta")
890
819
  style.setParentStyleName("Standard")
891
820
  style.setClass("text")
892
- style.setMarginTop(self._mTopMeta)
893
- style.setMarginBottom(self._mBotMeta)
894
- style.setLineHeight(self._fLineHeight)
821
+ style.setMarginTop(self._emToCm(self._marginMeta[0]))
822
+ style.setMarginBottom(self._emToCm(self._marginMeta[1]))
823
+ style.setLineHeight(lineHeight)
895
824
  style.setFontName(self._fontFamily)
896
825
  style.setFontFamily(self._fontFamily)
897
- style.setFontSize(self._fSizeText)
826
+ style.setFontSize(textSize)
898
827
  style.setFontWeight(self._fontWeight)
899
828
  style.packXML(self._xStyl)
900
829
  self._mainPara[style.name] = style
@@ -905,12 +834,12 @@ class ToOdt(Tokenizer):
905
834
  style.setParentStyleName("Heading")
906
835
  style.setNextStyleName(S_TEXT)
907
836
  style.setClass("chapter")
908
- style.setMarginTop(self._mTopTitle)
909
- style.setMarginBottom(self._mBotTitle)
837
+ style.setMarginTop(self._emToCm(self._marginTitle[0]))
838
+ style.setMarginBottom(self._emToCm(self._marginTitle[1]))
910
839
  style.setTextAlign("center")
911
840
  style.setFontName(self._fontFamily)
912
841
  style.setFontFamily(self._fontFamily)
913
- style.setFontSize(self._fSizeTitle)
842
+ style.setFontSize(self._emToPt(nwStyles.H_SIZES[0] if hScale else 1.0))
914
843
  style.setFontWeight(self._headWeight)
915
844
  style.packXML(self._xStyl)
916
845
  self._mainPara[style.name] = style
@@ -921,13 +850,13 @@ class ToOdt(Tokenizer):
921
850
  style.setParentStyleName("Standard")
922
851
  style.setNextStyleName(S_TEXT)
923
852
  style.setClass("text")
924
- style.setMarginTop(self._mTopSep)
925
- style.setMarginBottom(self._mBotSep)
926
- style.setLineHeight(self._fLineHeight)
853
+ style.setMarginTop(self._emToCm(self._marginSep[0]))
854
+ style.setMarginBottom(self._emToCm(self._marginSep[1]))
855
+ style.setLineHeight(lineHeight)
927
856
  style.setTextAlign("center")
928
857
  style.setFontName(self._fontFamily)
929
858
  style.setFontFamily(self._fontFamily)
930
- style.setFontSize(self._fSizeText)
859
+ style.setFontSize(textSize)
931
860
  style.setFontWeight(self._fontWeight)
932
861
  style.packXML(self._xStyl)
933
862
  self._mainPara[style.name] = style
@@ -939,11 +868,11 @@ class ToOdt(Tokenizer):
939
868
  style.setNextStyleName(S_TEXT)
940
869
  style.setOutlineLevel("1")
941
870
  style.setClass("text")
942
- style.setMarginTop(self._mTopHead1)
943
- style.setMarginBottom(self._mBotHead1)
871
+ style.setMarginTop(self._emToCm(self._marginHead1[0]))
872
+ style.setMarginBottom(self._emToCm(self._marginHead1[1]))
944
873
  style.setFontName(self._fontFamily)
945
874
  style.setFontFamily(self._fontFamily)
946
- style.setFontSize(self._fSizeHead1)
875
+ style.setFontSize(self._emToPt(nwStyles.H_SIZES[1] if hScale else 1.0))
947
876
  style.setFontWeight(self._headWeight)
948
877
  style.setColor(hColor)
949
878
  style.packXML(self._xStyl)
@@ -956,11 +885,11 @@ class ToOdt(Tokenizer):
956
885
  style.setNextStyleName(S_TEXT)
957
886
  style.setOutlineLevel("2")
958
887
  style.setClass("text")
959
- style.setMarginTop(self._mTopHead2)
960
- style.setMarginBottom(self._mBotHead2)
888
+ style.setMarginTop(self._emToCm(self._marginHead2[0]))
889
+ style.setMarginBottom(self._emToCm(self._marginHead2[1]))
961
890
  style.setFontName(self._fontFamily)
962
891
  style.setFontFamily(self._fontFamily)
963
- style.setFontSize(self._fSizeHead2)
892
+ style.setFontSize(self._emToPt(nwStyles.H_SIZES[2] if hScale else 1.0))
964
893
  style.setFontWeight(self._headWeight)
965
894
  style.setColor(hColor)
966
895
  style.packXML(self._xStyl)
@@ -973,11 +902,11 @@ class ToOdt(Tokenizer):
973
902
  style.setNextStyleName(S_TEXT)
974
903
  style.setOutlineLevel("3")
975
904
  style.setClass("text")
976
- style.setMarginTop(self._mTopHead3)
977
- style.setMarginBottom(self._mBotHead3)
905
+ style.setMarginTop(self._emToCm(self._marginHead3[0]))
906
+ style.setMarginBottom(self._emToCm(self._marginHead3[1]))
978
907
  style.setFontName(self._fontFamily)
979
908
  style.setFontFamily(self._fontFamily)
980
- style.setFontSize(self._fSizeHead3)
909
+ style.setFontSize(self._emToPt(nwStyles.H_SIZES[3] if hScale else 1.0))
981
910
  style.setFontWeight(self._headWeight)
982
911
  style.setColor(hColor)
983
912
  style.packXML(self._xStyl)
@@ -990,11 +919,11 @@ class ToOdt(Tokenizer):
990
919
  style.setNextStyleName(S_TEXT)
991
920
  style.setOutlineLevel("4")
992
921
  style.setClass("text")
993
- style.setMarginTop(self._mTopHead4)
994
- style.setMarginBottom(self._mBotHead4)
922
+ style.setMarginTop(self._emToCm(self._marginHead4[0]))
923
+ style.setMarginBottom(self._emToCm(self._marginHead4[1]))
995
924
  style.setFontName(self._fontFamily)
996
925
  style.setFontFamily(self._fontFamily)
997
- style.setFontSize(self._fSizeHead4)
926
+ style.setFontSize(self._emToPt(nwStyles.H_SIZES[4] if hScale else 1.0))
998
927
  style.setFontWeight(self._headWeight)
999
928
  style.setColor(hColor)
1000
929
  style.packXML(self._xStyl)
@@ -1013,10 +942,10 @@ class ToOdt(Tokenizer):
1013
942
  style.setDisplayName("Footnote")
1014
943
  style.setParentStyleName("Standard")
1015
944
  style.setClass("extra")
1016
- style.setMarginLeft(self._mLeftFoot)
1017
- style.setMarginBottom(self._mBotFoot)
1018
- style.setTextIndent("-"+self._mLeftFoot)
1019
- style.setFontSize(self._fSizeFoot)
945
+ style.setMarginLeft(self._emToCm(self._marginFoot[0]))
946
+ style.setMarginBottom(self._emToCm(self._marginFoot[1]))
947
+ style.setTextIndent("-"+self._emToCm(self._marginFoot[0]))
948
+ style.setFontSize(self._emToPt(nwStyles.T_SMALL))
1020
949
  style.packXML(self._xStyl)
1021
950
  self._mainPara[style.name] = style
1022
951
 
@@ -29,11 +29,12 @@ from pathlib import Path
29
29
 
30
30
  from PyQt5.QtCore import QMarginsF, QSizeF
31
31
  from PyQt5.QtGui import (
32
- QColor, QFont, QFontMetricsF, QPageSize, QTextBlockFormat, QTextCharFormat,
33
- QTextCursor, QTextDocument
32
+ QColor, QFont, QFontDatabase, QPageSize, QTextBlockFormat, QTextCharFormat,
33
+ QTextCursor, QTextDocument, QTextFrameFormat
34
34
  )
35
35
  from PyQt5.QtPrintSupport import QPrinter
36
36
 
37
+ from novelwriter import __version__
37
38
  from novelwriter.constants import nwStyles, nwUnicode
38
39
  from novelwriter.core.project import NWProject
39
40
  from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
@@ -67,16 +68,22 @@ class ToQTextDocument(Tokenizer):
67
68
  super().__init__(project)
68
69
  self._document = QTextDocument()
69
70
  self._document.setUndoRedoEnabled(False)
70
- self._document.setDocumentMargin(0)
71
+ self._document.setDocumentMargin(0.0)
71
72
 
72
73
  self._usedNotes: dict[str, int] = {}
73
74
  self._usedFields: list[tuple[int, str]] = []
74
75
 
75
76
  self._init = False
76
- self._bold = QFont.Weight.Bold
77
- self._normal = QFont.Weight.Normal
78
77
  self._newPage = False
78
+ self._anchors = True
79
79
 
80
+ self._hWeight = QFont.Weight.Bold
81
+ self._dWeight = QFont.Weight.Normal
82
+ self._dItalic = False
83
+ self._dStrike = False
84
+ self._dUnderline = False
85
+
86
+ self._dpi = 96
80
87
  self._pageSize = QPageSize(QPageSize.PageSizeId.A4)
81
88
  self._pageMargins = QMarginsF(20.0, 20.0, 20.0, 20.0)
82
89
 
@@ -108,32 +115,52 @@ class ToQTextDocument(Tokenizer):
108
115
  self._newPage = state
109
116
  return
110
117
 
118
+ def disableAnchors(self) -> None:
119
+ """Disable anchors for when writing to file."""
120
+ self._anchors = False
121
+ return
122
+
111
123
  ##
112
124
  # Class Methods
113
125
  ##
114
126
 
115
- def initDocument(self) -> None:
127
+ def initDocument(self, pdf: bool = False) -> None:
116
128
  """Initialise all computed values of the document."""
117
129
  super().initDocument()
118
130
 
131
+ if pdf:
132
+ fontDB = QFontDatabase()
133
+ family = self._textFont.family()
134
+ style = self._textFont.styleName()
135
+ self._dpi = 1200 if fontDB.isScalable(family, style) else 72
136
+
119
137
  self._document.setUndoRedoEnabled(False)
120
138
  self._document.blockSignals(True)
121
139
  self._document.clear()
122
140
  self._document.setDefaultFont(self._textFont)
123
141
 
124
- qMetric = QFontMetricsF(self._textFont)
125
- mPx = qMetric.ascent() # 1 em in pixels
126
- fPt = self._textFont.pointSizeF()
142
+ # Default Styles
143
+ self._dWeight = self._textFont.weight()
144
+ self._dItalic = self._textFont.italic()
145
+ self._dStrike = self._textFont.strikeOut()
146
+ self._dUnderline = self._textFont.underline()
147
+
148
+ # Header Weight
149
+ self._hWeight = QFont.Weight.Bold if self._boldHeads else self._dWeight
127
150
 
128
151
  # Scaled Sizes
129
152
  # ============
130
153
 
154
+ fPt = self._textFont.pointSizeF()
155
+ fPx = fPt*96.0/72.0 # 1 em in pixels
156
+ mPx = fPx * self._dpi/96.0
157
+
131
158
  self._mHead = {
132
- BlockTyp.TITLE: (mPx * self._marginTitle[0], mPx * self._marginTitle[1]),
133
- BlockTyp.HEAD1: (mPx * self._marginHead1[0], mPx * self._marginHead1[1]),
134
- BlockTyp.HEAD2: (mPx * self._marginHead2[0], mPx * self._marginHead2[1]),
135
- BlockTyp.HEAD3: (mPx * self._marginHead3[0], mPx * self._marginHead3[1]),
136
- BlockTyp.HEAD4: (mPx * self._marginHead4[0], mPx * self._marginHead4[1]),
159
+ BlockTyp.TITLE: (fPx * self._marginTitle[0], fPx * self._marginTitle[1]),
160
+ BlockTyp.HEAD1: (fPx * self._marginHead1[0], fPx * self._marginHead1[1]),
161
+ BlockTyp.HEAD2: (fPx * self._marginHead2[0], fPx * self._marginHead2[1]),
162
+ BlockTyp.HEAD3: (fPx * self._marginHead3[0], fPx * self._marginHead3[1]),
163
+ BlockTyp.HEAD4: (fPx * self._marginHead4[0], fPx * self._marginHead4[1]),
137
164
  }
138
165
 
139
166
  hScale = self._scaleHeads
@@ -145,9 +172,9 @@ class ToQTextDocument(Tokenizer):
145
172
  BlockTyp.HEAD4: (nwStyles.H_SIZES.get(4, 1.0) * fPt) if hScale else fPt,
146
173
  }
147
174
 
148
- self._mText = (mPx * self._marginText[0], mPx * self._marginText[1])
149
- self._mMeta = (mPx * self._marginMeta[0], mPx * self._marginMeta[1])
150
- self._mSep = (mPx * self._marginSep[0], mPx * self._marginSep[1])
175
+ self._mText = (fPx * self._marginText[0], fPx * self._marginText[1])
176
+ self._mMeta = (fPx * self._marginMeta[0], fPx * self._marginMeta[1])
177
+ self._mSep = (fPx * self._marginSep[0], fPx * self._marginSep[1])
151
178
 
152
179
  self._mIndent = mPx * 2.0
153
180
  self._tIndent = mPx * self._firstWidth
@@ -242,14 +269,19 @@ class ToQTextDocument(Tokenizer):
242
269
  def saveDocument(self, path: Path) -> None:
243
270
  """Save the document as a PDF file."""
244
271
  m = self._pageMargins
272
+ logger.info("Writing PDF at %d DPI", self._dpi)
245
273
 
246
- printer = QPrinter(QPrinter.PrinterMode.PrinterResolution)
274
+ printer = QPrinter(QPrinter.PrinterMode.HighResolution)
275
+ printer.setDocName(self._project.data.name)
276
+ printer.setCreator(f"novelWriter/{__version__}")
277
+ printer.setResolution(self._dpi)
247
278
  printer.setOutputFormat(QPrinter.OutputFormat.PdfFormat)
248
279
  printer.setPageSize(self._pageSize)
249
280
  printer.setPageMargins(m.left(), m.top(), m.right(), m.bottom(), QPrinter.Unit.Millimeter)
250
281
  printer.setOutputFileName(str(path))
251
282
 
252
- self._document.setPageSize(self._pageSize.size(QPageSize.Unit.Point))
283
+ self._document.documentLayout().setPaintDevice(printer)
284
+ self._document.setPageSize(QSizeF(printer.pageRect().size()))
253
285
  self._document.print(printer)
254
286
 
255
287
  return
@@ -309,21 +341,21 @@ class ToQTextDocument(Tokenizer):
309
341
 
310
342
  # Construct next format
311
343
  if fmt == TextFmt.B_B:
312
- cFmt.setFontWeight(self._bold)
344
+ cFmt.setFontWeight(QFont.Weight.Bold)
313
345
  elif fmt == TextFmt.B_E:
314
- cFmt.setFontWeight(self._normal)
346
+ cFmt.setFontWeight(self._dWeight)
315
347
  elif fmt == TextFmt.I_B:
316
348
  cFmt.setFontItalic(True)
317
349
  elif fmt == TextFmt.I_E:
318
- cFmt.setFontItalic(False)
350
+ cFmt.setFontItalic(self._dItalic)
319
351
  elif fmt == TextFmt.D_B:
320
352
  cFmt.setFontStrikeOut(True)
321
353
  elif fmt == TextFmt.D_E:
322
- cFmt.setFontStrikeOut(False)
354
+ cFmt.setFontStrikeOut(self._dStrike)
323
355
  elif fmt == TextFmt.U_B:
324
356
  cFmt.setFontUnderline(True)
325
357
  elif fmt == TextFmt.U_E:
326
- cFmt.setFontUnderline(False)
358
+ cFmt.setFontUnderline(self._dUnderline)
327
359
  elif fmt == TextFmt.M_B:
328
360
  cFmt.setBackground(self._theme.highlight)
329
361
  elif fmt == TextFmt.M_E:
@@ -344,18 +376,22 @@ class ToQTextDocument(Tokenizer):
344
376
  cFmt.setForeground(self._theme.text)
345
377
  primary = None
346
378
  elif fmt == TextFmt.ANM_B:
347
- cFmt.setAnchor(True)
348
- cFmt.setAnchorNames([data])
379
+ if self._anchors:
380
+ cFmt.setAnchor(True)
381
+ cFmt.setAnchorNames([data])
349
382
  elif fmt == TextFmt.ANM_E:
350
- cFmt.setAnchor(False)
383
+ if self._anchors:
384
+ cFmt.setAnchor(False)
351
385
  elif fmt == TextFmt.ARF_B:
352
- cFmt.setFontUnderline(True)
353
- cFmt.setAnchor(True)
354
- cFmt.setAnchorHref(data)
386
+ if self._anchors:
387
+ cFmt.setFontUnderline(True)
388
+ cFmt.setAnchor(True)
389
+ cFmt.setAnchorHref(data)
355
390
  elif fmt == TextFmt.ARF_E:
356
- cFmt.setFontUnderline(False)
357
- cFmt.setAnchor(False)
358
- cFmt.setAnchorHref("")
391
+ if self._anchors:
392
+ cFmt.setFontUnderline(False)
393
+ cFmt.setAnchor(False)
394
+ cFmt.setAnchorHref("")
359
395
  elif fmt == TextFmt.HRF_B:
360
396
  cFmt.setForeground(self._theme.link)
361
397
  cFmt.setFontUnderline(True)
@@ -363,7 +399,7 @@ class ToQTextDocument(Tokenizer):
363
399
  cFmt.setAnchorHref(data)
364
400
  elif fmt == TextFmt.HRF_E:
365
401
  cFmt.setForeground(primary or self._theme.text)
366
- cFmt.setFontUnderline(False)
402
+ cFmt.setFontUnderline(self._dUnderline)
367
403
  cFmt.setAnchor(False)
368
404
  cFmt.setAnchorHref("")
369
405
  elif fmt == TextFmt.FNOTE:
@@ -396,24 +432,36 @@ class ToQTextDocument(Tokenizer):
396
432
  def _insertNewPageMarker(self, cursor: QTextCursor) -> None:
397
433
  """Insert a new page marker."""
398
434
  if self._newPage:
399
- cursor.insertHtml("<hr width='100%'>")
435
+ bgCol = QColor(self._theme.text)
436
+ bgCol.setAlphaF(0.1)
437
+ fgCol = QColor(self._theme.text)
438
+ fgCol.setAlphaF(0.8)
400
439
 
401
- hFmt = cursor.blockFormat()
402
- hFmt.setBottomMargin(0.0)
403
- hFmt.setLineHeight(75.0, QtPropLineHeight)
404
- cursor.setBlockFormat(hFmt)
440
+ fFmt = QTextFrameFormat()
441
+ fFmt.setBorderStyle(QTextFrameFormat.BorderStyle.BorderStyle_None)
442
+ fFmt.setBackground(bgCol)
443
+ fFmt.setTopMargin(self._mSep[0])
444
+ fFmt.setBottomMargin(self._mSep[1])
405
445
 
406
446
  bFmt = QTextBlockFormat(self._blockFmt)
407
447
  bFmt.setAlignment(QtAlignCenter)
408
448
  bFmt.setTopMargin(0.0)
409
- bFmt.setLineHeight(75.0, QtPropLineHeight)
449
+ bFmt.setBottomMargin(0.0)
450
+ bFmt.setLineHeight(100.0, QtPropLineHeight)
410
451
 
411
452
  cFmt = QTextCharFormat(self._charFmt)
453
+ cFmt.setFontItalic(False)
454
+ cFmt.setFontUnderline(False)
455
+ cFmt.setFontStrikeOut(False)
456
+ cFmt.setFontWeight(QFont.Weight.Normal)
412
457
  cFmt.setFontPointSize(0.75*self._textFont.pointSizeF())
413
- cFmt.setForeground(self._theme.comment)
458
+ cFmt.setForeground(fgCol)
414
459
 
415
- newBlock(cursor, bFmt)
460
+ cursor.insertFrame(fFmt)
461
+ cursor.setBlockFormat(bFmt)
416
462
  cursor.insertText(self._project.localLookup("New Page"), cFmt)
463
+ cursor.swap(self._document.rootFrame().lastCursorPosition())
464
+
417
465
  return
418
466
 
419
467
  def _genHeadStyle(self, hType: BlockTyp, hKey: str, rFmt: QTextBlockFormat) -> T_TextStyle:
@@ -427,9 +475,9 @@ class ToQTextDocument(Tokenizer):
427
475
  hCol = self._colorHeads and hType != BlockTyp.TITLE
428
476
  cFmt = QTextCharFormat(self._charFmt)
429
477
  cFmt.setForeground(self._theme.head if hCol else self._theme.text)
430
- cFmt.setFontWeight(self._bold if self._boldHeads else self._normal)
478
+ cFmt.setFontWeight(self._hWeight)
431
479
  cFmt.setFontPointSize(self._sHead.get(hType, 1.0))
432
- if hKey:
480
+ if hKey and self._anchors:
433
481
  cFmt.setAnchorNames([hKey])
434
482
  cFmt.setAnchor(True)
435
483