From c8017a1f42a997d613c6cc559d98fb9dcb3a9716 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 23 May 2024 19:43:32 +0200 Subject: [PATCH] Add text paragraph formatting --- .../core/{toqdocument.py => toqdoc.py} | 88 ++++++++++++++----- novelwriter/gui/docviewer.py | 2 +- novelwriter/types.py | 6 +- 3 files changed, 72 insertions(+), 24 deletions(-) rename novelwriter/core/{toqdocument.py => toqdoc.py} (75%) diff --git a/novelwriter/core/toqdocument.py b/novelwriter/core/toqdoc.py similarity index 75% rename from novelwriter/core/toqdocument.py rename to novelwriter/core/toqdoc.py index 9ed22ed3..dac1b532 100644 --- a/novelwriter/core/toqdocument.py +++ b/novelwriter/core/toqdoc.py @@ -26,8 +26,8 @@ from __future__ import annotations import logging from PyQt5.QtGui import ( - QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat, QTextCursor, - QTextDocument + QColor, QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat, + QTextCursor, QTextDocument ) from novelwriter import SHARED @@ -36,13 +36,13 @@ from novelwriter.core.project import NWProject from novelwriter.core.tokenizer import T_Formats, Tokenizer from novelwriter.types import ( QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight, QtPageBreakAfter, - QtPageBreakBefore + QtPageBreakBefore, QtTransparent, QtVAlignNormal, QtVAlignSub, + QtVAlignSuper ) logger = logging.getLogger(__name__) T_TextStyle = tuple[QTextBlockFormat, QTextCharFormat] -T_TextParFormat = tuple[str, QTextCharFormat] class ToQTextDocument(Tokenizer): @@ -58,7 +58,11 @@ class ToQTextDocument(Tokenizer): self._document.setUndoRedoEnabled(False) self._styles: dict[int, T_TextStyle] = {} + self._usedNotes: dict[str, int] = {} + self._init = False + self._bold = QFont.Weight.Bold + self._normal = QFont.Weight.Normal return @@ -166,8 +170,7 @@ class ToQTextDocument(Tokenizer): if tType == self.T_TEXT: newBlock(bFmt) - tTemp, cFmt = self._formatText(tText, tFormat, self._defaultChar) - cursor.insertText(tTemp, cFmt) + self._insertFragments(tText, tFormat, cursor, self._defaultChar) elif tType in self.L_HEADINGS: bFmt, cFmt = self._genHeadStyle(tType, bFmt) @@ -222,23 +225,64 @@ class ToQTextDocument(Tokenizer): # Internal Functions ## - def _formatText(self, text: str, tFmt: T_Formats, dFmt: QTextCharFormat) -> T_TextParFormat: + def _insertFragments( + self, text: str, tFmt: T_Formats, cursor: QTextCursor, + dFmt: QTextCharFormat, bgCol: QColor = QtTransparent + ) -> None: """Apply formatting tags to text.""" - temp = text - fmt = QTextCharFormat(dFmt) - # for pos, fmt, data in reversed(tFmt): - # md = "" - # if fmt == self.FMT_FNOTE: - # if data in self._footnotes: - # index = len(self._usedNotes) + 1 - # self._usedNotes[data] = index - # md = f"[{index}]" - # else: - # md = "[ERR]" - # else: - # md = tags.get(fmt, "") - # temp = f"{temp[:pos]}{md}{temp[pos:]}" - return temp, fmt + cFmt = QTextCharFormat(dFmt) + start = 0 + for pos, fmt, data in tFmt: + + # Insert buffer with previous format + cursor.insertText(text[start:pos], cFmt) + + # Construct next format + if fmt == self.FMT_B_B: + cFmt.setFontWeight(self._bold) + elif fmt == self.FMT_B_E: + cFmt.setFontWeight(self._normal) + elif fmt == self.FMT_I_B: + cFmt.setFontItalic(True) + elif fmt == self.FMT_I_E: + cFmt.setFontItalic(False) + elif fmt == self.FMT_D_B: + cFmt.setFontStrikeOut(True) + elif fmt == self.FMT_D_E: + cFmt.setFontStrikeOut(False) + elif fmt == self.FMT_U_B: + cFmt.setFontUnderline(True) + elif fmt == self.FMT_U_E: + cFmt.setFontUnderline(False) + elif fmt == self.FMT_M_B: + cFmt.setBackground(SHARED.theme.colMark) + elif fmt == self.FMT_M_E: + cFmt.setBackground(bgCol) + elif fmt == self.FMT_SUP_B: + cFmt.setVerticalAlignment(QtVAlignSuper) + elif fmt == self.FMT_SUP_E: + cFmt.setVerticalAlignment(QtVAlignNormal) + elif fmt == self.FMT_SUB_B: + cFmt.setVerticalAlignment(QtVAlignSub) + elif fmt == self.FMT_SUB_E: + cFmt.setVerticalAlignment(QtVAlignNormal) + elif fmt == self.FMT_FNOTE: + cFmt.setVerticalAlignment(QtVAlignSuper) + if data in self._footnotes: + index = len(self._usedNotes) + 1 + self._usedNotes[data] = index + cursor.insertText(f"[{index}]", cFmt) + else: + cursor.insertText("[ERR]", cFmt) + cFmt.setVerticalAlignment(QtVAlignNormal) + + # Move pos for next pass + start = pos + + # Insert whatever is left in the buffer + cursor.insertText(text[start:], cFmt) + + return def _formatKeywords(self, text: str, style: int) -> str: """Apply Markdown formatting to keywords.""" diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py index 093aac3a..e3eb6887 100644 --- a/novelwriter/gui/docviewer.py +++ b/novelwriter/gui/docviewer.py @@ -40,7 +40,7 @@ from PyQt5.QtWidgets import ( from novelwriter import CONFIG, SHARED from novelwriter.common import cssCol from novelwriter.constants import nwHeaders, nwUnicode -from novelwriter.core.toqdocument import ToQTextDocument +from novelwriter.core.toqdoc import ToQTextDocument from novelwriter.enum import nwDocAction, nwDocMode, nwItemType from novelwriter.error import logException from novelwriter.extensions.eventfilters import WheelEventFilter diff --git a/novelwriter/types.py b/novelwriter/types.py index c70d2893..c45d79e0 100644 --- a/novelwriter/types.py +++ b/novelwriter/types.py @@ -24,7 +24,7 @@ along with this program. If not, see . from __future__ import annotations from PyQt5.QtCore import QRegularExpression, Qt -from PyQt5.QtGui import QColor, QFont, QPainter, QTextCursor, QTextFormat +from PyQt5.QtGui import QColor, QFont, QPainter, QTextCharFormat, QTextCursor, QTextFormat from PyQt5.QtWidgets import QDialogButtonBox, QSizePolicy, QStyle # Qt Alignment Flags @@ -44,6 +44,10 @@ QtAlignRightMiddle = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter QtAlignRightTop = Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignTop QtAlignTop = Qt.AlignmentFlag.AlignTop +QtVAlignNormal = QTextCharFormat.VerticalAlignment.AlignNormal +QtVAlignSub = QTextCharFormat.VerticalAlignment.AlignSubScript +QtVAlignSuper = QTextCharFormat.VerticalAlignment.AlignSuperScript + # Qt Page Break QtPageBreakBefore = QTextFormat.PageBreakFlag.PageBreak_AlwaysBefore