novelWriter 2.3rc1__py3-none-any.whl → 2.4__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 (125) hide show
  1. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/METADATA +5 -6
  2. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/RECORD +119 -109
  3. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/WHEEL +1 -1
  4. novelWriter-2.4.dist-info/entry_points.txt +2 -0
  5. novelwriter/__init__.py +17 -10
  6. novelwriter/assets/i18n/nw_de_DE.qm +0 -0
  7. novelwriter/assets/i18n/nw_en_US.qm +0 -0
  8. novelwriter/assets/i18n/nw_es_419.qm +0 -0
  9. novelwriter/assets/i18n/nw_fr_FR.qm +0 -0
  10. novelwriter/assets/i18n/nw_it_IT.qm +0 -0
  11. novelwriter/assets/i18n/nw_ja_JP.qm +0 -0
  12. novelwriter/assets/i18n/nw_nb_NO.qm +0 -0
  13. novelwriter/assets/i18n/nw_nl_NL.qm +0 -0
  14. novelwriter/assets/i18n/nw_pt_BR.qm +0 -0
  15. novelwriter/assets/i18n/nw_zh_CN.qm +0 -0
  16. novelwriter/assets/i18n/project_nl_NL.json +11 -0
  17. novelwriter/assets/i18n/project_pt_BR.json +11 -0
  18. novelwriter/assets/icons/none.svg +4 -0
  19. novelwriter/assets/icons/typicons_dark/icons.conf +4 -0
  20. novelwriter/assets/icons/typicons_dark/nw_tb-mark.svg +7 -0
  21. novelwriter/assets/icons/typicons_dark/typ_refresh-flipped.svg +1 -1
  22. novelwriter/assets/icons/typicons_dark/typ_refresh.svg +1 -1
  23. novelwriter/assets/icons/typicons_dark/typ_search-grey.svg +4 -0
  24. novelwriter/assets/icons/typicons_dark/typ_times.svg +1 -1
  25. novelwriter/assets/icons/typicons_dark/typ_unfold-hidden.svg +4 -0
  26. novelwriter/assets/icons/typicons_dark/typ_unfold-visible.svg +4 -0
  27. novelwriter/assets/icons/typicons_light/icons.conf +4 -0
  28. novelwriter/assets/icons/typicons_light/nw_tb-mark.svg +7 -0
  29. novelwriter/assets/icons/typicons_light/typ_refresh-flipped.svg +1 -1
  30. novelwriter/assets/icons/typicons_light/typ_refresh.svg +1 -1
  31. novelwriter/assets/icons/typicons_light/typ_search-grey.svg +4 -0
  32. novelwriter/assets/icons/typicons_light/typ_times.svg +1 -1
  33. novelwriter/assets/icons/typicons_light/typ_unfold-hidden.svg +4 -0
  34. novelwriter/assets/icons/typicons_light/typ_unfold-visible.svg +4 -0
  35. novelwriter/assets/manual.pdf +0 -0
  36. novelwriter/assets/sample.zip +0 -0
  37. novelwriter/assets/syntax/cyberpunk_night.conf +26 -0
  38. novelwriter/assets/syntax/default_dark.conf +1 -0
  39. novelwriter/assets/syntax/default_light.conf +1 -0
  40. novelwriter/assets/syntax/grey_dark.conf +1 -0
  41. novelwriter/assets/syntax/grey_light.conf +1 -0
  42. novelwriter/assets/syntax/light_owl.conf +1 -0
  43. novelwriter/assets/syntax/night_owl.conf +1 -0
  44. novelwriter/assets/syntax/solarized_dark.conf +1 -0
  45. novelwriter/assets/syntax/solarized_light.conf +1 -0
  46. novelwriter/assets/syntax/tango.conf +23 -0
  47. novelwriter/assets/syntax/tomorrow.conf +1 -0
  48. novelwriter/assets/syntax/tomorrow_night.conf +1 -0
  49. novelwriter/assets/syntax/tomorrow_night_blue.conf +1 -0
  50. novelwriter/assets/syntax/tomorrow_night_bright.conf +1 -0
  51. novelwriter/assets/syntax/tomorrow_night_eighties.conf +1 -0
  52. novelwriter/assets/text/credits_en.htm +25 -23
  53. novelwriter/assets/themes/cyberpunk_night.conf +29 -0
  54. novelwriter/common.py +12 -4
  55. novelwriter/config.py +47 -16
  56. novelwriter/constants.py +5 -6
  57. novelwriter/core/buildsettings.py +64 -44
  58. novelwriter/core/coretools.py +97 -13
  59. novelwriter/core/docbuild.py +74 -7
  60. novelwriter/core/document.py +24 -3
  61. novelwriter/core/index.py +31 -112
  62. novelwriter/core/project.py +11 -15
  63. novelwriter/core/projectxml.py +3 -2
  64. novelwriter/core/sessions.py +2 -2
  65. novelwriter/core/spellcheck.py +3 -3
  66. novelwriter/core/status.py +6 -5
  67. novelwriter/core/storage.py +16 -6
  68. novelwriter/core/tohtml.py +22 -25
  69. novelwriter/core/tokenizer.py +417 -237
  70. novelwriter/core/tomd.py +17 -8
  71. novelwriter/core/toodt.py +386 -351
  72. novelwriter/core/tree.py +8 -8
  73. novelwriter/dialogs/about.py +10 -12
  74. novelwriter/dialogs/docmerge.py +17 -14
  75. novelwriter/dialogs/docsplit.py +20 -19
  76. novelwriter/dialogs/editlabel.py +5 -4
  77. novelwriter/dialogs/preferences.py +32 -40
  78. novelwriter/dialogs/projectsettings.py +31 -28
  79. novelwriter/dialogs/quotes.py +10 -9
  80. novelwriter/dialogs/wordlist.py +18 -15
  81. novelwriter/enum.py +17 -14
  82. novelwriter/error.py +14 -12
  83. novelwriter/extensions/circularprogress.py +12 -8
  84. novelwriter/extensions/configlayout.py +23 -3
  85. novelwriter/extensions/modified.py +51 -2
  86. novelwriter/extensions/pagedsidebar.py +16 -14
  87. novelwriter/extensions/simpleprogress.py +3 -1
  88. novelwriter/extensions/statusled.py +3 -1
  89. novelwriter/extensions/switch.py +10 -9
  90. novelwriter/extensions/switchbox.py +14 -13
  91. novelwriter/extensions/versioninfo.py +1 -1
  92. novelwriter/gui/doceditor.py +433 -496
  93. novelwriter/gui/dochighlight.py +54 -33
  94. novelwriter/gui/docviewer.py +162 -175
  95. novelwriter/gui/docviewerpanel.py +20 -37
  96. novelwriter/gui/editordocument.py +15 -4
  97. novelwriter/gui/itemdetails.py +51 -54
  98. novelwriter/gui/mainmenu.py +37 -17
  99. novelwriter/gui/noveltree.py +31 -37
  100. novelwriter/gui/outline.py +120 -98
  101. novelwriter/gui/projtree.py +114 -112
  102. novelwriter/gui/search.py +362 -0
  103. novelwriter/gui/sidebar.py +36 -45
  104. novelwriter/gui/statusbar.py +14 -14
  105. novelwriter/gui/theme.py +116 -34
  106. novelwriter/guimain.py +216 -207
  107. novelwriter/shared.py +31 -6
  108. novelwriter/text/counting.py +138 -0
  109. novelwriter/tools/dictionaries.py +15 -14
  110. novelwriter/tools/lipsum.py +20 -17
  111. novelwriter/tools/manusbuild.py +43 -35
  112. novelwriter/tools/manuscript.py +381 -104
  113. novelwriter/tools/manussettings.py +263 -125
  114. novelwriter/tools/noveldetails.py +21 -19
  115. novelwriter/tools/welcome.py +59 -57
  116. novelwriter/tools/writingstats.py +61 -55
  117. novelwriter/types.py +90 -0
  118. novelWriter-2.3rc1.dist-info/entry_points.txt +0 -5
  119. novelwriter/core/__init__.py +0 -3
  120. novelwriter/dialogs/__init__.py +0 -3
  121. novelwriter/extensions/__init__.py +0 -3
  122. novelwriter/gui/__init__.py +0 -3
  123. novelwriter/tools/__init__.py +0 -3
  124. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/LICENSE.md +0 -0
  125. {novelWriter-2.3rc1.dist-info → novelWriter-2.4.dist-info}/top_level.txt +0 -0
novelwriter/core/toodt.py CHANGED
@@ -60,8 +60,7 @@ for ns, uri in XML_NS.items():
60
60
 
61
61
  def _mkTag(ns: str, tag: str) -> str:
62
62
  """Assemble namespace and tag name."""
63
- uri = XML_NS.get(ns, "")
64
- if uri:
63
+ if uri := XML_NS.get(ns, ""):
65
64
  return f"{{{uri}}}{tag}"
66
65
  logger.warning("Missing xml namespace '%s'", ns)
67
66
  return tag
@@ -84,14 +83,16 @@ X_BLD = 0x01 # Bold format
84
83
  X_ITA = 0x02 # Italic format
85
84
  X_DEL = 0x04 # Strikethrough format
86
85
  X_UND = 0x08 # Underline format
87
- X_SUP = 0x10 # Superscript
88
- X_SUB = 0x20 # Subscript
86
+ X_MRK = 0x10 # Marked format
87
+ X_SUP = 0x20 # Superscript
88
+ X_SUB = 0x40 # Subscript
89
89
 
90
90
  # Formatting Masks
91
91
  M_BLD = ~X_BLD
92
92
  M_ITA = ~X_ITA
93
93
  M_DEL = ~X_DEL
94
94
  M_UND = ~X_UND
95
+ M_MRK = ~X_MRK
95
96
  M_SUP = ~X_SUP
96
97
  M_SUB = ~X_SUB
97
98
 
@@ -125,9 +126,9 @@ class ToOdt(Tokenizer):
125
126
  self._xBody = ET.Element("") # Office body root
126
127
  self._xText = ET.Element("") # Office text root
127
128
 
128
- self._mainPara = {} # User-accessible paragraph styles
129
- self._autoPara = {} # Auto-generated paragraph styles
130
- self._autoText = {} # Auto-generated text styles
129
+ self._mainPara: dict[str, ODTParagraphStyle] = {} # User-accessible paragraph styles
130
+ self._autoPara: dict[str, ODTParagraphStyle] = {} # Auto-generated paragraph styles
131
+ self._autoText: dict[int, ODTTextStyle] = {} # Auto-generated text styles
131
132
 
132
133
  self._errData = [] # List of errors encountered
133
134
 
@@ -136,6 +137,7 @@ class ToOdt(Tokenizer):
136
137
  self._textSize = 12
137
138
  self._textFixed = False
138
139
  self._colourHead = False
140
+ self._firstIndent = False
139
141
  self._headerFormat = ""
140
142
  self._pageOffset = 0
141
143
 
@@ -151,6 +153,7 @@ class ToOdt(Tokenizer):
151
153
  self._fSizeText = "12pt"
152
154
  self._fLineHeight = "115%"
153
155
  self._fBlockIndent = "1.693cm"
156
+ self._fTextIndent = "0.499cm"
154
157
  self._textAlign = "left"
155
158
  self._dLanguage = "en"
156
159
  self._dCountry = "GB"
@@ -189,6 +192,7 @@ class ToOdt(Tokenizer):
189
192
  self._opaHead34 = None
190
193
  self._colMetaTx = None
191
194
  self._opaMetaTx = None
195
+ self._markText = "#ffffa6"
192
196
 
193
197
  return
194
198
 
@@ -199,10 +203,9 @@ class ToOdt(Tokenizer):
199
203
  def setLanguage(self, language: str | None) -> None:
200
204
  """Set language for the document."""
201
205
  if language:
202
- langBits = language.split("_")
203
- self._dLanguage = langBits[0]
204
- if len(langBits) > 1:
205
- self._dCountry = langBits[1]
206
+ lang, _, country = language.partition("_")
207
+ self._dLanguage = lang or self._dLanguage
208
+ self._dCountry = country or self._dCountry
206
209
  return
207
210
 
208
211
  def setColourHeaders(self, state: bool) -> None:
@@ -229,6 +232,11 @@ class ToOdt(Tokenizer):
229
232
  self._pageOffset = offset
230
233
  return
231
234
 
235
+ def setFirstLineIndent(self, state: bool) -> None:
236
+ """Enable or disable first line indent."""
237
+ self._firstIndent = state
238
+ return
239
+
232
240
  ##
233
241
  # Class Methods
234
242
  ##
@@ -394,10 +402,11 @@ class ToOdt(Tokenizer):
394
402
  pFmt = []
395
403
  pText = []
396
404
  pStyle = None
405
+ pIndent = True
397
406
  for tType, _, tText, tFormat, tStyle in self._tokens:
398
407
 
399
408
  # Styles
400
- oStyle = ODTParagraphStyle()
409
+ oStyle = ODTParagraphStyle("New")
401
410
  if tStyle is not None:
402
411
  if tStyle & self.A_LEFT:
403
412
  oStyle.setTextAlign("left")
@@ -424,6 +433,9 @@ class ToOdt(Tokenizer):
424
433
  if tStyle & self.A_IND_R:
425
434
  oStyle.setMarginRight(self._fBlockIndent)
426
435
 
436
+ if tType not in (self.T_EMPTY, self.T_TEXT):
437
+ pIndent = False
438
+
427
439
  # Process Text Types
428
440
  if tType == self.T_EMPTY:
429
441
  if len(pText) > 1 and pStyle is not None:
@@ -437,7 +449,14 @@ class ToOdt(Tokenizer):
437
449
  tLen = len(tTxt)
438
450
  tTxt += f"{nText}\n"
439
451
  tFmt.extend((p+tLen, fmt) for p, fmt in nFmt)
440
- self._addTextPar("Text_20_body", pStyle, tTxt.rstrip(), tFmt=tFmt)
452
+
453
+ # Don't indent a paragraph if it has alignment set
454
+ tIndent = self._firstIndent and pIndent and pStyle.isUnaligned()
455
+ self._addTextPar(
456
+ "First_20_line_20_indent" if tIndent else "Text_20_body",
457
+ pStyle, tTxt.rstrip(), tFmt=tFmt
458
+ )
459
+ pIndent = True
441
460
 
442
461
  pFmt = []
443
462
  pText = []
@@ -447,10 +466,6 @@ class ToOdt(Tokenizer):
447
466
  tHead = tText.replace(nwHeadFmt.BR, "\n")
448
467
  self._addTextPar("Title", oStyle, tHead, isHead=False) # Title must be text:p
449
468
 
450
- elif tType == self.T_UNNUM:
451
- tHead = tText.replace(nwHeadFmt.BR, "\n")
452
- self._addTextPar("Heading_20_2", oStyle, tHead, isHead=True, oLevel="2")
453
-
454
469
  elif tType == self.T_HEAD1:
455
470
  tHead = tText.replace(nwHeadFmt.BR, "\n")
456
471
  self._addTextPar("Heading_20_1", oStyle, tHead, isHead=True, oLevel="1")
@@ -498,12 +513,11 @@ class ToOdt(Tokenizer):
498
513
  return
499
514
 
500
515
  def closeDocument(self) -> None:
501
- """Pack the styles of the XML document."""
502
- # Build the auto-generated styles
503
- for styleName, styleObj in self._autoPara.values():
504
- styleObj.packXML(self._xAuto, styleName)
505
- for styleName, styleObj in self._autoText.values():
506
- styleObj.packXML(self._xAuto, styleName)
516
+ """Pack the automatic styles of the XML document."""
517
+ for style in self._autoPara.values():
518
+ style.packXML(self._xAuto)
519
+ for style in self._autoText.values():
520
+ style.packXML(self._xAuto)
507
521
  return
508
522
 
509
523
  def saveFlatXML(self, path: str | Path) -> None:
@@ -534,7 +548,7 @@ class ToOdt(Tokenizer):
534
548
  oVers = _mkTag("office", "version")
535
549
  xSett = ET.Element(oRoot, attrib={oVers: X_VERS})
536
550
 
537
- def putInZip(name, xObj, zipObj):
551
+ def putInZip(name: str, xObj: ET.Element, zipObj: ZipFile) -> None:
538
552
  with zipObj.open(name, mode="w") as fObj:
539
553
  xml = ET.ElementTree(xObj)
540
554
  xml.write(fObj, encoding="utf-8", xml_declaration=True)
@@ -557,10 +571,7 @@ class ToOdt(Tokenizer):
557
571
 
558
572
  def _formatSynopsis(self, text: str, synopsis: bool) -> tuple[str, list[tuple[int, int]]]:
559
573
  """Apply formatting to synopsis lines."""
560
- if synopsis:
561
- name = self._localLookup("Synopsis")
562
- else:
563
- name = self._localLookup("Short Description")
574
+ name = self._localLookup("Synopsis" if synopsis else "Short Description")
564
575
  rTxt = f"{name}: {text}"
565
576
  rFmt = [(0, self.FMT_B_B), (len(name) + 1, self.FMT_B_E)]
566
577
  return rTxt, rFmt
@@ -593,8 +604,7 @@ class ToOdt(Tokenizer):
593
604
  tFmt: Sequence[tuple[int, int]] = [], isHead: bool = False, oLevel: str | None = None
594
605
  ) -> None:
595
606
  """Add a text paragraph to the text XML element."""
596
- tAttr = {}
597
- tAttr[_mkTag("text", "style-name")] = self._paraStyle(styleName, oStyle)
607
+ tAttr = {_mkTag("text", "style-name"): self._paraStyle(styleName, oStyle)}
598
608
  if oLevel is not None:
599
609
  tAttr[_mkTag("text", "outline-level")] = oLevel
600
610
 
@@ -643,6 +653,10 @@ class ToOdt(Tokenizer):
643
653
  xFmt |= X_UND
644
654
  elif fFmt == self.FMT_U_E:
645
655
  xFmt &= M_UND
656
+ elif fFmt == self.FMT_M_B:
657
+ xFmt |= X_MRK
658
+ elif fFmt == self.FMT_M_E:
659
+ xFmt &= M_MRK
646
660
  elif fFmt == self.FMT_SUP_B:
647
661
  xFmt |= X_SUP
648
662
  elif fFmt == self.FMT_SUP_E:
@@ -672,52 +686,54 @@ class ToOdt(Tokenizer):
672
686
 
673
687
  return
674
688
 
675
- def _paraStyle(self, parName: str, oStyle: ODTParagraphStyle) -> str:
689
+ def _paraStyle(self, mainName: str, modStyle: ODTParagraphStyle) -> str:
676
690
  """Return a name for a style object."""
677
- refStyle = self._mainPara.get(parName, None)
678
- if refStyle is None:
679
- logger.error("Unknown paragraph style '%s'", parName)
691
+ if not (refStyle := self._mainPara.get(mainName)):
692
+ logger.error("Unknown main paragraph style '%s'", mainName)
680
693
  return "Standard"
681
694
 
682
- if not refStyle.checkNew(oStyle):
683
- return parName
695
+ if not refStyle.checkNew(modStyle):
696
+ # The style is unmodified, so we return the main style
697
+ return mainName
684
698
 
685
- oStyle.setParentStyleName(parName)
686
- pID = oStyle.getID()
687
- if pID in self._autoPara:
688
- return self._autoPara[pID][0]
699
+ # The style is modified, so we check if there already is an
700
+ # identical style with the same parent we can use instead
701
+ modStyle.setParentStyleName(mainName)
702
+ if (pID := modStyle.getID()) in self._autoPara:
703
+ return self._autoPara[pID].name
689
704
 
690
- newName = "P%d" % (len(self._autoPara) + 1)
691
- self._autoPara[pID] = (newName, oStyle)
705
+ # If neither of the above hold, we store it as a new style
706
+ modStyle.setName(f"P{len(self._autoPara)+1:d}")
707
+ self._autoPara[pID] = modStyle
692
708
 
693
- return newName
709
+ return modStyle.name
694
710
 
695
711
  def _textStyle(self, hFmt: int) -> str:
696
712
  """Return a text style for a given style code."""
697
713
  if hFmt in self._autoText:
698
- return self._autoText[hFmt][0]
714
+ return self._autoText[hFmt].name
699
715
 
700
- newName = "T%d" % (len(self._autoText) + 1)
701
- newStyle = ODTTextStyle()
716
+ style = ODTTextStyle(f"T{len(self._autoText)+1:d}")
702
717
  if hFmt & X_BLD:
703
- newStyle.setFontWeight("bold")
718
+ style.setFontWeight("bold")
704
719
  if hFmt & X_ITA:
705
- newStyle.setFontStyle("italic")
720
+ style.setFontStyle("italic")
706
721
  if hFmt & X_DEL:
707
- newStyle.setStrikeStyle("solid")
708
- newStyle.setStrikeType("single")
722
+ style.setStrikeStyle("solid")
723
+ style.setStrikeType("single")
709
724
  if hFmt & X_UND:
710
- newStyle.setUnderlineStyle("solid")
711
- newStyle.setUnderlineWidth("auto")
712
- newStyle.setUnderlineColour("font-color")
725
+ style.setUnderlineStyle("solid")
726
+ style.setUnderlineWidth("auto")
727
+ style.setUnderlineColour("font-color")
728
+ if hFmt & X_MRK:
729
+ style.setBackgroundColour(self._markText)
713
730
  if hFmt & X_SUP:
714
- newStyle.setTextPosition("super")
731
+ style.setTextPosition("super")
715
732
  if hFmt & X_SUB:
716
- newStyle.setTextPosition("sub")
733
+ style.setTextPosition("sub")
734
+ self._autoText[hFmt] = style
717
735
 
718
- self._autoText[hFmt] = (newName, newStyle)
719
-
720
- return newName
736
+ return style.name
721
737
 
722
738
  def _emToCm(self, value: float) -> str:
723
739
  """Converts an em value to centimetres."""
@@ -729,286 +745,254 @@ class ToOdt(Tokenizer):
729
745
 
730
746
  def _pageStyles(self) -> None:
731
747
  """Set the default page style."""
732
- tAttr = {}
733
- tAttr[_mkTag("style", "name")] = "PM1"
734
- if self._isFlat:
735
- xPage = ET.SubElement(self._xAuto, _mkTag("style", "page-layout"), attrib=tAttr)
736
- else:
737
- xPage = ET.SubElement(self._xAut2, _mkTag("style", "page-layout"), attrib=tAttr)
738
-
739
- tAttr = {}
740
- tAttr[_mkTag("fo", "page-width")] = self._mDocWidth
741
- tAttr[_mkTag("fo", "page-height")] = self._mDocHeight
742
- tAttr[_mkTag("fo", "margin-top")] = self._mDocTop
743
- tAttr[_mkTag("fo", "margin-bottom")] = self._mDocBtm
744
- tAttr[_mkTag("fo", "margin-left")] = self._mDocLeft
745
- tAttr[_mkTag("fo", "margin-right")] = self._mDocRight
746
- tAttr[_mkTag("fo", "print-orientation")] = "portrait"
747
- ET.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib=tAttr)
748
+ xPage = ET.SubElement(
749
+ self._xAuto if self._isFlat else self._xAut2,
750
+ _mkTag("style", "page-layout"),
751
+ attrib={_mkTag("style", "name"): "PM1"}
752
+ )
753
+ ET.SubElement(xPage, _mkTag("style", "page-layout-properties"), attrib={
754
+ _mkTag("fo", "page-width"): self._mDocWidth,
755
+ _mkTag("fo", "page-height"): self._mDocHeight,
756
+ _mkTag("fo", "margin-top"): self._mDocTop,
757
+ _mkTag("fo", "margin-bottom"): self._mDocBtm,
758
+ _mkTag("fo", "margin-left"): self._mDocLeft,
759
+ _mkTag("fo", "margin-right"): self._mDocRight,
760
+ _mkTag("fo", "print-orientation"): "portrait",
761
+ })
748
762
 
749
763
  xHead = ET.SubElement(xPage, _mkTag("style", "header-style"))
750
-
751
- tAttr = {}
752
- tAttr[_mkTag("fo", "min-height")] = "0.600cm"
753
- tAttr[_mkTag("fo", "margin-left")] = "0.000cm"
754
- tAttr[_mkTag("fo", "margin-right")] = "0.000cm"
755
- tAttr[_mkTag("fo", "margin-bottom")] = "0.500cm"
756
- ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib=tAttr)
764
+ ET.SubElement(xHead, _mkTag("style", "header-footer-properties"), attrib={
765
+ _mkTag("fo", "min-height"): "0.600cm",
766
+ _mkTag("fo", "margin-left"): "0.000cm",
767
+ _mkTag("fo", "margin-right"): "0.000cm",
768
+ _mkTag("fo", "margin-bottom"): "0.500cm",
769
+ })
757
770
 
758
771
  return
759
772
 
760
773
  def _defaultStyles(self) -> None:
761
774
  """Set the default styles."""
762
775
  # Add Paragraph Family Style
763
- # ==========================
764
-
765
- tAttr = {}
766
- tAttr[_mkTag("style", "family")] = "paragraph"
767
- xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib=tAttr)
768
-
769
- tAttr = {}
770
- tAttr[_mkTag("style", "line-break")] = "strict"
771
- tAttr[_mkTag("style", "tab-stop-distance")] = "1.251cm"
772
- tAttr[_mkTag("style", "writing-mode")] = "page"
773
- ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=tAttr)
774
-
775
- tAttr = {}
776
- tAttr[_mkTag("style", "font-name")] = self._textFont
777
- tAttr[_mkTag("fo", "font-family")] = self._fontFamily
778
- tAttr[_mkTag("fo", "font-size")] = self._fSizeText
779
- tAttr[_mkTag("fo", "language")] = self._dLanguage
780
- tAttr[_mkTag("fo", "country")] = self._dCountry
781
- ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
776
+ xStyl = ET.SubElement(self._xStyl, _mkTag("style", "default-style"), attrib={
777
+ _mkTag("style", "family"): "paragraph",
778
+ })
779
+ ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib={
780
+ _mkTag("style", "line-break"): "strict",
781
+ _mkTag("style", "tab-stop-distance"): "1.251cm",
782
+ _mkTag("style", "writing-mode"): "page",
783
+ })
784
+ ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={
785
+ _mkTag("style", "font-name"): self._textFont,
786
+ _mkTag("fo", "font-family"): self._fontFamily,
787
+ _mkTag("fo", "font-size"): self._fSizeText,
788
+ _mkTag("fo", "language"): self._dLanguage,
789
+ _mkTag("fo", "country"): self._dCountry,
790
+ })
782
791
 
783
792
  # Add Standard Paragraph Style
784
- # ============================
785
-
786
- tAttr = {}
787
- tAttr[_mkTag("style", "name")] = "Standard"
788
- tAttr[_mkTag("style", "family")] = "paragraph"
789
- tAttr[_mkTag("style", "class")] = "text"
790
- xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
791
-
792
- tAttr = {}
793
- tAttr[_mkTag("style", "font-name")] = self._textFont
794
- tAttr[_mkTag("fo", "font-family")] = self._fontFamily
795
- tAttr[_mkTag("fo", "font-size")] = self._fSizeText
796
- ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
793
+ xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib={
794
+ _mkTag("style", "name"): "Standard",
795
+ _mkTag("style", "family"): "paragraph",
796
+ _mkTag("style", "class"): "text",
797
+ })
798
+ ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={
799
+ _mkTag("style", "font-name"): self._textFont,
800
+ _mkTag("fo", "font-family"): self._fontFamily,
801
+ _mkTag("fo", "font-size"): self._fSizeText,
802
+ })
797
803
 
798
804
  # Add Default Heading Style
799
- # =========================
800
-
801
- tAttr = {}
802
- tAttr[_mkTag("style", "name")] = "Heading"
803
- tAttr[_mkTag("style", "family")] = "paragraph"
804
- tAttr[_mkTag("style", "parent-style-name")] = "Standard"
805
- tAttr[_mkTag("style", "next-style-name")] = "Text_20_body"
806
- tAttr[_mkTag("style", "class")] = "text"
807
- xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
808
-
809
- tAttr = {}
810
- tAttr[_mkTag("fo", "margin-top")] = self._mTopHead
811
- tAttr[_mkTag("fo", "margin-bottom")] = self._mBotHead
812
- tAttr[_mkTag("fo", "keep-with-next")] = "always"
813
- ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=tAttr)
814
-
815
- tAttr = {}
816
- tAttr[_mkTag("style", "font-name")] = self._textFont
817
- tAttr[_mkTag("fo", "font-family")] = self._fontFamily
818
- tAttr[_mkTag("fo", "font-size")] = self._fSizeHead
819
- ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=tAttr)
805
+ xStyl = ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib={
806
+ _mkTag("style", "name"): "Heading",
807
+ _mkTag("style", "family"): "paragraph",
808
+ _mkTag("style", "parent-style-name"): "Standard",
809
+ _mkTag("style", "next-style-name"): "Text_20_body",
810
+ _mkTag("style", "class"): "text",
811
+ })
812
+ ET.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib={
813
+ _mkTag("fo", "margin-top"): self._mTopHead,
814
+ _mkTag("fo", "margin-bottom"): self._mBotHead,
815
+ _mkTag("fo", "keep-with-next"): "always",
816
+ })
817
+ ET.SubElement(xStyl, _mkTag("style", "text-properties"), attrib={
818
+ _mkTag("style", "font-name"): self._textFont,
819
+ _mkTag("fo", "font-family"): self._fontFamily,
820
+ _mkTag("fo", "font-size"): self._fSizeHead,
821
+ })
820
822
 
821
823
  # Add Header and Footer Styles
822
- # ============================
823
- tAttr = {}
824
- tAttr[_mkTag("style", "name")] = "Header_20_and_20_Footer"
825
- tAttr[_mkTag("style", "display-name")] = "Header and Footer"
826
- tAttr[_mkTag("style", "family")] = "paragraph"
827
- tAttr[_mkTag("style", "parent-style-name")] = "Standard"
828
- tAttr[_mkTag("style", "class")] = "extra"
829
- ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib=tAttr)
824
+ ET.SubElement(self._xStyl, _mkTag("style", "style"), attrib={
825
+ _mkTag("style", "name"): "Header_20_and_20_Footer",
826
+ _mkTag("style", "display-name"): "Header and Footer",
827
+ _mkTag("style", "family"): "paragraph",
828
+ _mkTag("style", "parent-style-name"): "Standard",
829
+ _mkTag("style", "class"): "extra",
830
+ })
830
831
 
831
832
  return
832
833
 
833
834
  def _useableStyles(self) -> None:
834
835
  """Set the usable styles."""
835
836
  # Add Text Body Style
836
- # ===================
837
-
838
- oStyle = ODTParagraphStyle()
839
- oStyle.setDisplayName("Text body")
840
- oStyle.setParentStyleName("Standard")
841
- oStyle.setClass("text")
842
- oStyle.setMarginTop(self._mTopText)
843
- oStyle.setMarginBottom(self._mBotText)
844
- oStyle.setLineHeight(self._fLineHeight)
845
- oStyle.setFontName(self._textFont)
846
- oStyle.setFontFamily(self._fontFamily)
847
- oStyle.setFontSize(self._fSizeText)
848
- oStyle.setTextAlign(self._textAlign)
849
- oStyle.packXML(self._xStyl, "Text_20_body")
850
-
851
- self._mainPara["Text_20_body"] = oStyle
837
+ style = ODTParagraphStyle("Text_20_body")
838
+ style.setDisplayName("Text body")
839
+ style.setParentStyleName("Standard")
840
+ style.setClass("text")
841
+ style.setMarginTop(self._mTopText)
842
+ style.setMarginBottom(self._mBotText)
843
+ style.setLineHeight(self._fLineHeight)
844
+ style.setTextAlign(self._textAlign)
845
+ style.setFontName(self._textFont)
846
+ style.setFontFamily(self._fontFamily)
847
+ style.setFontSize(self._fSizeText)
848
+ style.packXML(self._xStyl)
849
+ self._mainPara[style.name] = style
850
+
851
+ # Add First Line Indent Style
852
+ style = ODTParagraphStyle("First_20_line_20_indent")
853
+ style.setDisplayName("First line indent")
854
+ style.setParentStyleName("Text_20_body")
855
+ style.setClass("text")
856
+ style.setTextIndent(self._fTextIndent)
857
+ style.packXML(self._xStyl)
858
+ self._mainPara[style.name] = style
852
859
 
853
860
  # Add Text Meta Style
854
- # ===================
855
-
856
- oStyle = ODTParagraphStyle()
857
- oStyle.setDisplayName("Text Meta")
858
- oStyle.setParentStyleName("Standard")
859
- oStyle.setClass("text")
860
- oStyle.setMarginTop(self._mTopMeta)
861
- oStyle.setMarginBottom(self._mBotMeta)
862
- oStyle.setLineHeight(self._fLineHeight)
863
- oStyle.setFontName(self._textFont)
864
- oStyle.setFontFamily(self._fontFamily)
865
- oStyle.setFontSize(self._fSizeText)
866
- oStyle.setColor(self._colMetaTx)
867
- oStyle.setOpacity(self._opaMetaTx)
868
- oStyle.packXML(self._xStyl, "Text_20_Meta")
869
-
870
- self._mainPara["Text_20_Meta"] = oStyle
861
+ style = ODTParagraphStyle("Text_20_Meta")
862
+ style.setDisplayName("Text Meta")
863
+ style.setParentStyleName("Standard")
864
+ style.setClass("text")
865
+ style.setMarginTop(self._mTopMeta)
866
+ style.setMarginBottom(self._mBotMeta)
867
+ style.setLineHeight(self._fLineHeight)
868
+ style.setFontName(self._textFont)
869
+ style.setFontFamily(self._fontFamily)
870
+ style.setFontSize(self._fSizeText)
871
+ style.setColour(self._colMetaTx)
872
+ style.setOpacity(self._opaMetaTx)
873
+ style.packXML(self._xStyl)
874
+ self._mainPara[style.name] = style
871
875
 
872
876
  # Add Title Style
873
- # ===============
874
-
875
- oStyle = ODTParagraphStyle()
876
- oStyle.setDisplayName("Title")
877
- oStyle.setParentStyleName("Heading")
878
- oStyle.setNextStyleName("Text_20_body")
879
- oStyle.setClass("chapter")
880
- oStyle.setTextAlign("center")
881
- oStyle.setMarginTop(self._mTopTitle)
882
- oStyle.setMarginBottom(self._mBotTitle)
883
- oStyle.setFontName(self._textFont)
884
- oStyle.setFontFamily(self._fontFamily)
885
- oStyle.setFontSize(self._fSizeTitle)
886
- oStyle.setFontWeight("bold")
887
- oStyle.packXML(self._xStyl, "Title")
888
-
889
- self._mainPara["Title"] = oStyle
877
+ style = ODTParagraphStyle("Title")
878
+ style.setDisplayName("Title")
879
+ style.setParentStyleName("Heading")
880
+ style.setNextStyleName("Text_20_body")
881
+ style.setClass("chapter")
882
+ style.setMarginTop(self._mTopTitle)
883
+ style.setMarginBottom(self._mBotTitle)
884
+ style.setTextAlign("center")
885
+ style.setFontName(self._textFont)
886
+ style.setFontFamily(self._fontFamily)
887
+ style.setFontSize(self._fSizeTitle)
888
+ style.setFontWeight("bold")
889
+ style.packXML(self._xStyl)
890
+ self._mainPara[style.name] = style
890
891
 
891
892
  # Add Separator Style
892
- # ===================
893
-
894
- oStyle = ODTParagraphStyle()
895
- oStyle.setDisplayName("Separator")
896
- oStyle.setParentStyleName("Standard")
897
- oStyle.setNextStyleName("Text_20_body")
898
- oStyle.setClass("text")
899
- oStyle.setTextAlign("center")
900
- oStyle.setMarginTop(self._mTopText)
901
- oStyle.setMarginBottom(self._mBotText)
902
- oStyle.setLineHeight(self._fLineHeight)
903
- oStyle.setFontName(self._textFont)
904
- oStyle.setFontFamily(self._fontFamily)
905
- oStyle.setFontSize(self._fSizeText)
906
- oStyle.packXML(self._xStyl, "Separator")
907
-
908
- self._mainPara["Separator"] = oStyle
893
+ style = ODTParagraphStyle("Separator")
894
+ style.setDisplayName("Separator")
895
+ style.setParentStyleName("Standard")
896
+ style.setNextStyleName("Text_20_body")
897
+ style.setClass("text")
898
+ style.setMarginTop(self._mTopText)
899
+ style.setMarginBottom(self._mBotText)
900
+ style.setLineHeight(self._fLineHeight)
901
+ style.setTextAlign("center")
902
+ style.setFontName(self._textFont)
903
+ style.setFontFamily(self._fontFamily)
904
+ style.setFontSize(self._fSizeText)
905
+ style.packXML(self._xStyl)
906
+ self._mainPara[style.name] = style
909
907
 
910
908
  # Add Heading 1 Style
911
- # ===================
912
-
913
- oStyle = ODTParagraphStyle()
914
- oStyle.setDisplayName("Heading 1")
915
- oStyle.setParentStyleName("Heading")
916
- oStyle.setNextStyleName("Text_20_body")
917
- oStyle.setOutlineLevel("1")
918
- oStyle.setClass("text")
919
- oStyle.setMarginTop(self._mTopHead1)
920
- oStyle.setMarginBottom(self._mBotHead1)
921
- oStyle.setFontName(self._textFont)
922
- oStyle.setFontFamily(self._fontFamily)
923
- oStyle.setFontSize(self._fSizeHead1)
924
- oStyle.setColor(self._colHead12)
925
- oStyle.setOpacity(self._opaHead12)
926
- oStyle.setFontWeight("bold")
927
- oStyle.packXML(self._xStyl, "Heading_20_1")
928
-
929
- self._mainPara["Heading_20_1"] = oStyle
909
+ style = ODTParagraphStyle("Heading_20_1")
910
+ style.setDisplayName("Heading 1")
911
+ style.setParentStyleName("Heading")
912
+ style.setNextStyleName("Text_20_body")
913
+ style.setOutlineLevel("1")
914
+ style.setClass("text")
915
+ style.setMarginTop(self._mTopHead1)
916
+ style.setMarginBottom(self._mBotHead1)
917
+ style.setFontName(self._textFont)
918
+ style.setFontFamily(self._fontFamily)
919
+ style.setFontSize(self._fSizeHead1)
920
+ style.setFontWeight("bold")
921
+ style.setColour(self._colHead12)
922
+ style.setOpacity(self._opaHead12)
923
+ style.packXML(self._xStyl)
924
+ self._mainPara[style.name] = style
930
925
 
931
926
  # Add Heading 2 Style
932
- # ===================
933
-
934
- oStyle = ODTParagraphStyle()
935
- oStyle.setDisplayName("Heading 2")
936
- oStyle.setParentStyleName("Heading")
937
- oStyle.setNextStyleName("Text_20_body")
938
- oStyle.setOutlineLevel("2")
939
- oStyle.setClass("text")
940
- oStyle.setMarginTop(self._mTopHead2)
941
- oStyle.setMarginBottom(self._mBotHead2)
942
- oStyle.setFontName(self._textFont)
943
- oStyle.setFontFamily(self._fontFamily)
944
- oStyle.setFontSize(self._fSizeHead2)
945
- oStyle.setColor(self._colHead12)
946
- oStyle.setOpacity(self._opaHead12)
947
- oStyle.setFontWeight("bold")
948
- oStyle.packXML(self._xStyl, "Heading_20_2")
949
-
950
- self._mainPara["Heading_20_2"] = oStyle
927
+ style = ODTParagraphStyle("Heading_20_2")
928
+ style.setDisplayName("Heading 2")
929
+ style.setParentStyleName("Heading")
930
+ style.setNextStyleName("Text_20_body")
931
+ style.setOutlineLevel("2")
932
+ style.setClass("text")
933
+ style.setMarginTop(self._mTopHead2)
934
+ style.setMarginBottom(self._mBotHead2)
935
+ style.setFontName(self._textFont)
936
+ style.setFontFamily(self._fontFamily)
937
+ style.setFontSize(self._fSizeHead2)
938
+ style.setFontWeight("bold")
939
+ style.setColour(self._colHead12)
940
+ style.setOpacity(self._opaHead12)
941
+ style.packXML(self._xStyl)
942
+ self._mainPara[style.name] = style
951
943
 
952
944
  # Add Heading 3 Style
953
- # ===================
954
-
955
- oStyle = ODTParagraphStyle()
956
- oStyle.setDisplayName("Heading 3")
957
- oStyle.setParentStyleName("Heading")
958
- oStyle.setNextStyleName("Text_20_body")
959
- oStyle.setOutlineLevel("3")
960
- oStyle.setClass("text")
961
- oStyle.setMarginTop(self._mTopHead3)
962
- oStyle.setMarginBottom(self._mBotHead3)
963
- oStyle.setFontName(self._textFont)
964
- oStyle.setFontFamily(self._fontFamily)
965
- oStyle.setFontSize(self._fSizeHead3)
966
- oStyle.setColor(self._colHead34)
967
- oStyle.setOpacity(self._opaHead34)
968
- oStyle.setFontWeight("bold")
969
- oStyle.packXML(self._xStyl, "Heading_20_3")
970
-
971
- self._mainPara["Heading_20_3"] = oStyle
945
+ style = ODTParagraphStyle("Heading_20_3")
946
+ style.setDisplayName("Heading 3")
947
+ style.setParentStyleName("Heading")
948
+ style.setNextStyleName("Text_20_body")
949
+ style.setOutlineLevel("3")
950
+ style.setClass("text")
951
+ style.setMarginTop(self._mTopHead3)
952
+ style.setMarginBottom(self._mBotHead3)
953
+ style.setFontName(self._textFont)
954
+ style.setFontFamily(self._fontFamily)
955
+ style.setFontSize(self._fSizeHead3)
956
+ style.setFontWeight("bold")
957
+ style.setColour(self._colHead34)
958
+ style.setOpacity(self._opaHead34)
959
+ style.packXML(self._xStyl)
960
+ self._mainPara[style.name] = style
972
961
 
973
962
  # Add Heading 4 Style
974
- # ===================
975
-
976
- oStyle = ODTParagraphStyle()
977
- oStyle.setDisplayName("Heading 4")
978
- oStyle.setParentStyleName("Heading")
979
- oStyle.setNextStyleName("Text_20_body")
980
- oStyle.setOutlineLevel("4")
981
- oStyle.setClass("text")
982
- oStyle.setMarginTop(self._mTopHead4)
983
- oStyle.setMarginBottom(self._mBotHead4)
984
- oStyle.setFontName(self._textFont)
985
- oStyle.setFontFamily(self._fontFamily)
986
- oStyle.setFontSize(self._fSizeHead4)
987
- oStyle.setColor(self._colHead34)
988
- oStyle.setOpacity(self._opaHead34)
989
- oStyle.setFontWeight("bold")
990
- oStyle.packXML(self._xStyl, "Heading_20_4")
991
-
992
- self._mainPara["Heading_20_4"] = oStyle
963
+ style = ODTParagraphStyle("Heading_20_4")
964
+ style.setDisplayName("Heading 4")
965
+ style.setParentStyleName("Heading")
966
+ style.setNextStyleName("Text_20_body")
967
+ style.setOutlineLevel("4")
968
+ style.setClass("text")
969
+ style.setMarginTop(self._mTopHead4)
970
+ style.setMarginBottom(self._mBotHead4)
971
+ style.setFontName(self._textFont)
972
+ style.setFontFamily(self._fontFamily)
973
+ style.setFontSize(self._fSizeHead4)
974
+ style.setFontWeight("bold")
975
+ style.setColour(self._colHead34)
976
+ style.setOpacity(self._opaHead34)
977
+ style.packXML(self._xStyl)
978
+ self._mainPara[style.name] = style
993
979
 
994
980
  # Add Header Style
995
- # ================
996
- oStyle = ODTParagraphStyle()
997
- oStyle.setDisplayName("Header")
998
- oStyle.setParentStyleName("Header_20_and_20_Footer")
999
- oStyle.setTextAlign("right")
1000
- oStyle.packXML(self._xStyl, "Header")
1001
-
1002
- self._mainPara["Header"] = oStyle
981
+ style = ODTParagraphStyle("Header")
982
+ style.setDisplayName("Header")
983
+ style.setParentStyleName("Header_20_and_20_Footer")
984
+ style.setTextAlign("right")
985
+ style.packXML(self._xStyl)
986
+ self._mainPara[style.name] = style
1003
987
 
1004
988
  return
1005
989
 
1006
990
  def _writeHeader(self) -> None:
1007
991
  """Write the header elements."""
1008
- tAttr = {}
1009
- tAttr[_mkTag("style", "name")] = "Standard"
1010
- tAttr[_mkTag("style", "page-layout-name")] = "PM1"
1011
- xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib=tAttr)
992
+ xPage = ET.SubElement(self._xMast, _mkTag("style", "master-page"), attrib={
993
+ _mkTag("style", "name"): "Standard",
994
+ _mkTag("style", "page-layout-name"): "PM1",
995
+ })
1012
996
 
1013
997
  # Standard Page Header
1014
998
  if self._headerFormat:
@@ -1025,10 +1009,10 @@ class ToOdt(Tokenizer):
1025
1009
  })
1026
1010
  xPar.text = pre
1027
1011
  if page:
1028
- attrib = {_mkTag("text", "select-page"): "current"}
1012
+ attr = {_mkTag("text", "select-page"): "current"}
1029
1013
  if self._pageOffset > 0:
1030
- attrib = {_mkTag("text", "page-adjust"): str(0 - self._pageOffset)}
1031
- xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib=attrib)
1014
+ attr = {_mkTag("text", "page-adjust"): str(0 - self._pageOffset)}
1015
+ xTail = ET.SubElement(xPar, _mkTag("text", "page-number"), attrib=attr)
1032
1016
  xTail.text = "2"
1033
1017
  xTail.tail = post
1034
1018
  else:
@@ -1060,7 +1044,9 @@ class ODTParagraphStyle:
1060
1044
  VALID_CLASS = ["text", "chapter"]
1061
1045
  VALID_WEIGHT = ["normal", "inherit", "bold"]
1062
1046
 
1063
- def __init__(self) -> None:
1047
+ def __init__(self, name: str) -> None:
1048
+
1049
+ self._name = name
1064
1050
 
1065
1051
  # Attributes
1066
1052
  self._mAttr = {
@@ -1077,6 +1063,7 @@ class ODTParagraphStyle:
1077
1063
  "margin-bottom": ["fo", None],
1078
1064
  "margin-left": ["fo", None],
1079
1065
  "margin-right": ["fo", None],
1066
+ "text-indent": ["fo", None],
1080
1067
  "line-height": ["fo", None],
1081
1068
  "text-align": ["fo", None],
1082
1069
  "break-before": ["fo", None],
@@ -1095,23 +1082,50 @@ class ODTParagraphStyle:
1095
1082
 
1096
1083
  return
1097
1084
 
1085
+ @property
1086
+ def name(self) -> str:
1087
+ return self._name
1088
+
1089
+ ##
1090
+ # Checkers
1091
+ ##
1092
+
1093
+ def isUnaligned(self) -> bool:
1094
+ """Check if paragraph has any sort of alignment or margins."""
1095
+ return all(
1096
+ self._pAttr[n][1] is None for n in ["text-align", "margin-left", "margin-right"]
1097
+ )
1098
+
1099
+ ##
1100
+ # Setters
1101
+ ##
1102
+
1103
+ def setName(self, name: str) -> None:
1104
+ """Set the paragraph style name."""
1105
+ self._name = name
1106
+ return
1107
+
1098
1108
  ##
1099
1109
  # Attribute Setters
1100
1110
  ##
1101
1111
 
1102
1112
  def setDisplayName(self, value: str | None) -> None:
1113
+ """Set style display name."""
1103
1114
  self._mAttr["display-name"][1] = value
1104
1115
  return
1105
1116
 
1106
1117
  def setParentStyleName(self, value: str | None) -> None:
1118
+ """Set parent style name."""
1107
1119
  self._mAttr["parent-style-name"][1] = value
1108
1120
  return
1109
1121
 
1110
1122
  def setNextStyleName(self, value: str | None) -> None:
1123
+ """Set next style name."""
1111
1124
  self._mAttr["next-style-name"][1] = value
1112
1125
  return
1113
1126
 
1114
1127
  def setOutlineLevel(self, value: str | None) -> None:
1128
+ """Set paragraph outline level."""
1115
1129
  if value in self.VALID_LEVEL:
1116
1130
  self._mAttr["default-outline-level"][1] = value
1117
1131
  else:
@@ -1119,6 +1133,7 @@ class ODTParagraphStyle:
1119
1133
  return
1120
1134
 
1121
1135
  def setClass(self, value: str | None) -> None:
1136
+ """Set paragraph class."""
1122
1137
  if value in self.VALID_CLASS:
1123
1138
  self._mAttr["class"][1] = value
1124
1139
  else:
@@ -1130,26 +1145,37 @@ class ODTParagraphStyle:
1130
1145
  ##
1131
1146
 
1132
1147
  def setMarginTop(self, value: str | None) -> None:
1148
+ """Set paragraph top margin."""
1133
1149
  self._pAttr["margin-top"][1] = value
1134
1150
  return
1135
1151
 
1136
1152
  def setMarginBottom(self, value: str | None) -> None:
1153
+ """Set paragraph bottom margin."""
1137
1154
  self._pAttr["margin-bottom"][1] = value
1138
1155
  return
1139
1156
 
1140
1157
  def setMarginLeft(self, value: str | None) -> None:
1158
+ """Set paragraph left margin."""
1141
1159
  self._pAttr["margin-left"][1] = value
1142
1160
  return
1143
1161
 
1144
1162
  def setMarginRight(self, value: str | None) -> None:
1163
+ """Set paragraph right margin."""
1145
1164
  self._pAttr["margin-right"][1] = value
1146
1165
  return
1147
1166
 
1167
+ def setTextIndent(self, value: str | None) -> None:
1168
+ """Set text indentation."""
1169
+ self._pAttr["text-indent"][1] = value
1170
+ return
1171
+
1148
1172
  def setLineHeight(self, value: str | None) -> None:
1173
+ """Set line height."""
1149
1174
  self._pAttr["line-height"][1] = value
1150
1175
  return
1151
1176
 
1152
1177
  def setTextAlign(self, value: str | None) -> None:
1178
+ """Set paragraph text alignment."""
1153
1179
  if value in self.VALID_ALIGN:
1154
1180
  self._pAttr["text-align"][1] = value
1155
1181
  else:
@@ -1157,6 +1183,7 @@ class ODTParagraphStyle:
1157
1183
  return
1158
1184
 
1159
1185
  def setBreakBefore(self, value: str | None) -> None:
1186
+ """Set page break before policy."""
1160
1187
  if value in self.VALID_BREAK:
1161
1188
  self._pAttr["break-before"][1] = value
1162
1189
  else:
@@ -1164,6 +1191,7 @@ class ODTParagraphStyle:
1164
1191
  return
1165
1192
 
1166
1193
  def setBreakAfter(self, value: str | None) -> None:
1194
+ """Set page break after policy."""
1167
1195
  if value in self.VALID_BREAK:
1168
1196
  self._pAttr["break-after"][1] = value
1169
1197
  else:
@@ -1175,29 +1203,35 @@ class ODTParagraphStyle:
1175
1203
  ##
1176
1204
 
1177
1205
  def setFontName(self, value: str | None) -> None:
1206
+ """Set font name."""
1178
1207
  self._tAttr["font-name"][1] = value
1179
1208
  return
1180
1209
 
1181
1210
  def setFontFamily(self, value: str | None) -> None:
1211
+ """Set font family."""
1182
1212
  self._tAttr["font-family"][1] = value
1183
1213
  return
1184
1214
 
1185
1215
  def setFontSize(self, value: str | None) -> None:
1216
+ """Set font size."""
1186
1217
  self._tAttr["font-size"][1] = value
1187
1218
  return
1188
1219
 
1189
1220
  def setFontWeight(self, value: str | None) -> None:
1221
+ """Set font weight."""
1190
1222
  if value in self.VALID_WEIGHT:
1191
1223
  self._tAttr["font-weight"][1] = value
1192
1224
  else:
1193
1225
  self._tAttr["font-weight"][1] = None
1194
1226
  return
1195
1227
 
1196
- def setColor(self, value: str | None) -> None:
1228
+ def setColour(self, value: str | None) -> None:
1229
+ """Set text colour."""
1197
1230
  self._tAttr["color"][1] = value
1198
1231
  return
1199
1232
 
1200
1233
  def setOpacity(self, value: str | None) -> None:
1234
+ """Set text opacity."""
1201
1235
  self._tAttr["opacity"][1] = value
1202
1236
  return
1203
1237
 
@@ -1206,54 +1240,41 @@ class ODTParagraphStyle:
1206
1240
  ##
1207
1241
 
1208
1242
  def checkNew(self, style: ODTParagraphStyle) -> bool:
1209
- """Check if there are new settings in refStyle that differ from
1210
- those in the current object.
1243
+ """Check if there are new settings in style that differ from
1244
+ those in this object. Unset styles are ignored as they can be
1245
+ inherited from the parent style.
1211
1246
  """
1212
- for name, (_, aVal) in style._mAttr.items():
1213
- if aVal is not None and aVal != self._mAttr[name][1]:
1214
- return True
1215
- for name, (_, aVal) in style._pAttr.items():
1216
- if aVal is not None and aVal != self._pAttr[name][1]:
1217
- return True
1218
- for name, (_, aVal) in style._tAttr.items():
1219
- if aVal is not None and aVal != self._tAttr[name][1]:
1220
- return True
1247
+ if any(v and v != self._mAttr[m][1] for m, (_, v) in style._mAttr.items()):
1248
+ return True
1249
+ if any(v and v != self._pAttr[m][1] for m, (_, v) in style._pAttr.items()):
1250
+ return True
1251
+ if any(v and v != self._tAttr[m][1] for m, (_, v) in style._tAttr.items()):
1252
+ return True
1221
1253
  return False
1222
1254
 
1223
1255
  def getID(self) -> str:
1224
1256
  """Generate a unique ID from the settings."""
1225
- string = (
1257
+ return sha256((
1226
1258
  f"Paragraph:Main:{str(self._mAttr)}:"
1227
1259
  f"Paragraph:Para:{str(self._pAttr)}:"
1228
1260
  f"Paragraph:Text:{str(self._tAttr)}:"
1229
- )
1230
- return sha256(string.encode()).hexdigest()
1261
+ ).encode()).hexdigest()
1231
1262
 
1232
- def packXML(self, xParent: ET.Element, name: str) -> None:
1263
+ def packXML(self, xParent: ET.Element) -> None:
1233
1264
  """Pack the content into an xml element."""
1234
- attr = {}
1235
- attr[_mkTag("style", "name")] = name
1236
- attr[_mkTag("style", "family")] = "paragraph"
1237
- for aName, (aNm, aVal) in self._mAttr.items():
1238
- if aVal is not None:
1239
- attr[_mkTag(aNm, aName)] = aVal
1240
-
1265
+ attr = {
1266
+ _mkTag("style", "name"): self._name,
1267
+ _mkTag("style", "family"): "paragraph",
1268
+ }
1269
+ attr.update(
1270
+ {_mkTag(n, m): v for m, (n, v) in self._mAttr.items() if v}
1271
+ )
1241
1272
  xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=attr)
1242
1273
 
1243
- attr = {}
1244
- for aName, (aNm, aVal) in self._pAttr.items():
1245
- if aVal is not None:
1246
- attr[_mkTag(aNm, aName)] = aVal
1247
-
1248
- if attr:
1274
+ if attr := {_mkTag(n, m): v for m, (n, v) in self._pAttr.items() if v}:
1249
1275
  ET.SubElement(xEntry, _mkTag("style", "paragraph-properties"), attrib=attr)
1250
1276
 
1251
- attr = {}
1252
- for aName, (aNm, aVal) in self._tAttr.items():
1253
- if aVal is not None:
1254
- attr[_mkTag(aNm, aName)] = aVal
1255
-
1256
- if attr:
1277
+ if attr := {_mkTag(n, m): v for m, (n, v) in self._tAttr.items() if v}:
1257
1278
  ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=attr)
1258
1279
 
1259
1280
  return
@@ -1274,11 +1295,12 @@ class ODTTextStyle:
1274
1295
  VALID_LWIDTH = ["auto"]
1275
1296
  VALID_LCOL = ["font-color"]
1276
1297
 
1277
- def __init__(self) -> None:
1278
- # Text Attributes
1298
+ def __init__(self, name: str) -> None:
1299
+ self._name = name
1279
1300
  self._tAttr = {
1280
1301
  "font-weight": ["fo", None],
1281
1302
  "font-style": ["fo", None],
1303
+ "background-color": ["fo", None],
1282
1304
  "text-position": ["style", None],
1283
1305
  "text-line-through-style": ["style", None],
1284
1306
  "text-line-through-type": ["style", None],
@@ -1288,11 +1310,16 @@ class ODTTextStyle:
1288
1310
  }
1289
1311
  return
1290
1312
 
1313
+ @property
1314
+ def name(self) -> str:
1315
+ return self._name
1316
+
1291
1317
  ##
1292
1318
  # Setters
1293
1319
  ##
1294
1320
 
1295
1321
  def setFontWeight(self, value: str | None) -> None:
1322
+ """Set text font weight."""
1296
1323
  if value in self.VALID_WEIGHT:
1297
1324
  self._tAttr["font-weight"][1] = value
1298
1325
  else:
@@ -1300,13 +1327,23 @@ class ODTTextStyle:
1300
1327
  return
1301
1328
 
1302
1329
  def setFontStyle(self, value: str | None) -> None:
1330
+ """Set text font style."""
1303
1331
  if value in self.VALID_STYLE:
1304
1332
  self._tAttr["font-style"][1] = value
1305
1333
  else:
1306
1334
  self._tAttr["font-style"][1] = None
1307
1335
  return
1308
1336
 
1337
+ def setBackgroundColour(self, value: str | None) -> None:
1338
+ """Set text background colour."""
1339
+ if value and len(value) == 7 and value[0] == "#":
1340
+ self._tAttr["background-color"][1] = value
1341
+ else:
1342
+ self._tAttr["background-color"][1] = None
1343
+ return
1344
+
1309
1345
  def setTextPosition(self, value: str | None) -> None:
1346
+ """Set text vertical position."""
1310
1347
  if value in self.VALID_POS:
1311
1348
  self._tAttr["text-position"][1] = f"{value} 58%"
1312
1349
  else:
@@ -1314,6 +1351,7 @@ class ODTTextStyle:
1314
1351
  return
1315
1352
 
1316
1353
  def setStrikeStyle(self, value: str | None) -> None:
1354
+ """Set text line-trough style."""
1317
1355
  if value in self.VALID_LSTYLE:
1318
1356
  self._tAttr["text-line-through-style"][1] = value
1319
1357
  else:
@@ -1321,6 +1359,7 @@ class ODTTextStyle:
1321
1359
  return
1322
1360
 
1323
1361
  def setStrikeType(self, value: str | None) -> None:
1362
+ """Set text line-through type."""
1324
1363
  if value in self.VALID_LTYPE:
1325
1364
  self._tAttr["text-line-through-type"][1] = value
1326
1365
  else:
@@ -1328,6 +1367,7 @@ class ODTTextStyle:
1328
1367
  return
1329
1368
 
1330
1369
  def setUnderlineStyle(self, value: str | None) -> None:
1370
+ """Set text underline style."""
1331
1371
  if value in self.VALID_LSTYLE:
1332
1372
  self._tAttr["text-underline-style"][1] = value
1333
1373
  else:
@@ -1335,6 +1375,7 @@ class ODTTextStyle:
1335
1375
  return
1336
1376
 
1337
1377
  def setUnderlineWidth(self, value: str | None) -> None:
1378
+ """Set text underline width."""
1338
1379
  if value in self.VALID_LWIDTH:
1339
1380
  self._tAttr["text-underline-width"][1] = value
1340
1381
  else:
@@ -1342,6 +1383,7 @@ class ODTTextStyle:
1342
1383
  return
1343
1384
 
1344
1385
  def setUnderlineColour(self, value: str | None) -> None:
1386
+ """Set text underline colour."""
1345
1387
  if value in self.VALID_LCOL:
1346
1388
  self._tAttr["text-underline-color"][1] = value
1347
1389
  else:
@@ -1352,21 +1394,14 @@ class ODTTextStyle:
1352
1394
  # Methods
1353
1395
  ##
1354
1396
 
1355
- def packXML(self, xParent: ET.Element, name: str) -> None:
1397
+ def packXML(self, xParent: ET.Element) -> None:
1356
1398
  """Pack the content into an xml element."""
1357
- attr = {}
1358
- attr[_mkTag("style", "name")] = name
1359
- attr[_mkTag("style", "family")] = "text"
1360
- xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib=attr)
1361
-
1362
- attr = {}
1363
- for aName, (aNm, aVal) in self._tAttr.items():
1364
- if aVal is not None:
1365
- attr[_mkTag(aNm, aName)] = aVal
1366
-
1367
- if attr:
1399
+ xEntry = ET.SubElement(xParent, _mkTag("style", "style"), attrib={
1400
+ _mkTag("style", "name"): self._name,
1401
+ _mkTag("style", "family"): "text",
1402
+ })
1403
+ if attr := {_mkTag(n, m): v for m, (n, v) in self._tAttr.items() if v}:
1368
1404
  ET.SubElement(xEntry, _mkTag("style", "text-properties"), attrib=attr)
1369
-
1370
1405
  return
1371
1406
 
1372
1407
  # END Class ODTTextStyle