Replace heading field with a general meta data field in the text block tuple

This commit is contained in:
Veronica Berglyd Olsen
2024-10-23 16:20:50 +02:00
parent f4a941cd93
commit 0ddc1131dc
15 changed files with 346 additions and 336 deletions
+16 -13
View File
@@ -119,18 +119,21 @@ class BlockFmt(Flag):
An enum of flags that can be combined to format a text block.
"""
NONE = 0x0000 # No special style
LEFT = 0x0001 # Left aligned
RIGHT = 0x0002 # Right aligned
CENTRE = 0x0004 # Centred
JUSTIFY = 0x0008 # Justified
PBB = 0x0010 # Page break before
PBA = 0x0020 # Page break after
Z_TOPMRG = 0x0040 # Zero top margin
Z_BTMMRG = 0x0080 # Zero bottom margin
IND_L = 0x0100 # Left indentation
IND_R = 0x0200 # Right indentation
IND_T = 0x0400 # Text indentation
NONE = 0x0000 # No special style
LEFT = 0x0001 # Left aligned
RIGHT = 0x0002 # Right aligned
CENTRE = 0x0004 # Centred
JUSTIFY = 0x0008 # Justified
PBB = 0x0010 # Page break before
PBA = 0x0020 # Page break after
Z_TOP = 0x0040 # Zero top margin
Z_BTM = 0x0080 # Zero bottom margin
IND_L = 0x0100 # Left indentation
IND_R = 0x0200 # Right indentation
IND_T = 0x0400 # Text indentation
# Masks
ALIGNED = LEFT | RIGHT | CENTRE | JUSTIFY
# Types
@@ -145,4 +148,4 @@ T_Note = tuple[str, T_Formats]
# A tokenized text block, consisting of:
# type, header number, text, text formats, and block format
T_Block = tuple[BlockTyp, int, str, T_Formats, BlockFmt]
T_Block = tuple[BlockTyp, str, str, T_Formats, BlockFmt]
+2 -2
View File
@@ -240,9 +240,9 @@ class ToDocX(Tokenizer):
if tStyle & BlockFmt.PBA:
par.setPageBreakAfter(True)
if tStyle & BlockFmt.Z_BTMMRG:
if tStyle & BlockFmt.Z_BTM:
par.setMarginBottom(0.0)
if tStyle & BlockFmt.Z_TOPMRG:
if tStyle & BlockFmt.Z_TOP:
par.setMarginTop(0.0)
if tStyle & BlockFmt.IND_T:
+7 -8
View File
@@ -157,9 +157,7 @@ class ToHtml(Tokenizer):
h4 = "h4"
lines = []
tHandle = self._handle
for tType, nHead, tText, tFmt, tStyle in self._blocks:
for tType, tMeta, tText, tFmt, tStyle in self._blocks:
# Replace < and > with HTML entities
if tFmt:
@@ -200,9 +198,9 @@ class ToHtml(Tokenizer):
if tStyle & BlockFmt.PBA:
aStyle.append("page-break-after: always;")
if tStyle & BlockFmt.Z_BTMMRG:
if tStyle & BlockFmt.Z_BTM:
aStyle.append("margin-bottom: 0;")
if tStyle & BlockFmt.Z_TOPMRG:
if tStyle & BlockFmt.Z_TOP:
aStyle.append("margin-top: 0;")
if tStyle & BlockFmt.IND_L:
@@ -218,8 +216,8 @@ class ToHtml(Tokenizer):
else:
hStyle = ""
if self._linkHeadings and tHandle:
aNm = f"<a name='{tHandle}:T{nHead:04d}'></a>"
if self._linkHeadings and tMeta:
aNm = f"<a name='{tMeta}'></a>"
else:
aNm = ""
@@ -257,7 +255,8 @@ class ToHtml(Tokenizer):
lines.append(f"<p class='comment'{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
elif tType == BlockTyp.KEYWORD:
lines.append(f"<p class='meta'{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
tClass = f"meta meta-{tMeta}"
lines.append(f"<p class='{tClass}'{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
self._result = "".join(lines)
self._fullHTML.append(self._result)
+87 -93
View File
@@ -60,6 +60,7 @@ class ComStyle(NamedTuple):
textClass: str = ""
B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
COMMENT_STYLE = {
nwComment.PLAIN: ComStyle("Comment", "comment", "comment"),
nwComment.IGNORE: ComStyle(),
@@ -71,6 +72,13 @@ COMMENT_STYLE = {
nwComment.STORY: ComStyle("", "modifier", "note"),
}
# Lookups
HEADINGS = [BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4]
SKIP_INDENT = [
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
]
class Tokenizer(ABC):
"""Core: Text Tokenizer Abstract Base Class
@@ -81,18 +89,6 @@ class Tokenizer(ABC):
subclasses.
"""
# Masks
M_ALIGNED = BlockFmt.LEFT | BlockFmt.RIGHT | BlockFmt.CENTRE | BlockFmt.JUSTIFY
# Lookups
L_HEADINGS = [
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
]
L_SKIP_INDENT = [
BlockTyp.TITLE, BlockTyp.HEAD1, BlockTyp.HEAD2, BlockTyp.HEAD2, BlockTyp.HEAD3,
BlockTyp.HEAD4, BlockTyp.SEP, BlockTyp.SKIP,
]
def __init__(self, project: NWProject) -> None:
self._project = project
@@ -487,7 +483,7 @@ class Tokenizer(ABC):
title = f"{trNotes}: {tItem.itemName}"
self._blocks = []
self._blocks.append((
BlockTyp.TITLE, 1, title, [], textAlign
BlockTyp.TITLE, f"{self._handle}:T0001", title, [], textAlign
))
if self._keepRaw:
self._markdown.append(f"#! {title}\n\n")
@@ -538,6 +534,13 @@ class Tokenizer(ABC):
4: The internal formatting map of the text, TxtFmt.*
5: The formats of the block, BlockFmt.*
"""
# Cache Flags
isNovel = self._isNovel
keepRaw = self._keepRaw
doJustify = self._doJustify
indentFirst = self._indentFirst
firstIndent = self._firstIndent
if self._isNovel:
self._hFormatter.setHandle(self._handle)
@@ -545,18 +548,15 @@ class Tokenizer(ABC):
breakNext = False
tmpMarkdown = []
tHandle = self._handle or ""
blocks: list[T_Block] = []
tBlocks: list[T_Block] = [B_EMPTY]
for aLine in self._text.splitlines():
sLine = aLine.strip().lower()
# Check for blank lines
if len(sLine) == 0:
blocks.append((
BlockTyp.EMPTY, nHead, "", [], BlockFmt.NONE
))
if self._keepRaw:
if not sLine:
tBlocks.append(B_EMPTY)
if keepRaw:
tmpMarkdown.append("\n")
continue
if breakNext:
@@ -580,20 +580,20 @@ class Tokenizer(ABC):
continue
elif sLine == "[vspace]":
blocks.append(
(BlockTyp.SKIP, nHead, "", [], sAlign)
tBlocks.append(
(BlockTyp.SKIP, "", "", [], sAlign)
)
continue
elif sLine.startswith("[vspace:") and sLine.endswith("]"):
nSkip = checkInt(sLine[8:-1], 0)
if nSkip >= 1:
blocks.append(
(BlockTyp.SKIP, nHead, "", [], sAlign)
tBlocks.append(
(BlockTyp.SKIP, "", "", [], sAlign)
)
if nSkip > 1:
blocks += (nSkip - 1) * [
(BlockTyp.SKIP, nHead, "", [], BlockFmt.NONE)
tBlocks += (nSkip - 1) * [
(BlockTyp.SKIP, "", "", [], BlockFmt.NONE)
]
continue
@@ -612,22 +612,22 @@ class Tokenizer(ABC):
if cStyle == nwComment.PLAIN and not self._doComments:
continue
if self._doJustify and not sAlign & self.M_ALIGNED:
if doJustify and not sAlign & BlockFmt.ALIGNED:
sAlign |= BlockFmt.JUSTIFY
if cStyle in (nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN):
bStyle = COMMENT_STYLE[cStyle]
tLine, tFmt = self._formatComment(bStyle, cKey, cText)
blocks.append((
BlockTyp.COMMENT, nHead, tLine, tFmt, sAlign
tBlocks.append((
BlockTyp.COMMENT, "", tLine, tFmt, sAlign
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.FOOTNOTE:
tLine, tFmt = self._extractFormats(cText, skip=TextFmt.FNOTE)
self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("@"):
@@ -637,12 +637,12 @@ class Tokenizer(ABC):
# are automatically skipped.
if self._doKeywords:
tLine, tFmt = self._formatMeta(aLine)
tTag, tLine, tFmt = self._formatMeta(aLine)
if tLine:
blocks.append((
BlockTyp.KEYWORD, nHead, tLine, tFmt, sAlign
tBlocks.append((
BlockTyp.KEYWORD, tTag[1:], tLine, tFmt, sAlign
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("# ", "#! ")):
@@ -661,7 +661,7 @@ class Tokenizer(ABC):
tType = BlockTyp.HEAD1 if isPlain else BlockTyp.TITLE
tStyle = BlockFmt.NONE if isPlain else self._titleStyle
sHide = self._hidePart if isPlain else False
if self._isNovel:
if isNovel:
if sHide:
tText = ""
tType = BlockTyp.EMPTY
@@ -675,10 +675,10 @@ class Tokenizer(ABC):
self._hFormatter.resetAll()
self._noSep = True
blocks.append((
tType, nHead, tText, [], tStyle
tBlocks.append((
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("## ", "##! ")):
@@ -698,7 +698,7 @@ class Tokenizer(ABC):
tStyle = BlockFmt.NONE
sHide = self._hideChapter if isPlain else self._hideUnNum
tFormat = self._fmtChapter if isPlain else self._fmtUnNum
if self._isNovel:
if isNovel:
if isPlain:
self._hFormatter.incChapter()
if sHide:
@@ -710,10 +710,10 @@ class Tokenizer(ABC):
self._hFormatter.resetScene()
self._noSep = True
blocks.append((
tType, nHead, tText, [], tStyle
tBlocks.append((
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("### ", "###! ")):
@@ -735,7 +735,7 @@ class Tokenizer(ABC):
tStyle = BlockFmt.NONE
sHide = self._hideScene if isPlain else self._hideHScene
tFormat = self._fmtScene if isPlain else self._fmtHScene
if self._isNovel:
if isNovel:
self._hFormatter.incScene()
if sHide:
tText = ""
@@ -751,10 +751,10 @@ class Tokenizer(ABC):
tStyle = BlockFmt.NONE if self._noSep else BlockFmt.CENTRE
self._noSep = False
blocks.append((
tType, nHead, tText, [], tStyle
tBlocks.append((
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("#### "):
@@ -769,7 +769,7 @@ class Tokenizer(ABC):
tText = aLine[5:].strip()
tType = BlockTyp.HEAD4
tStyle = BlockFmt.NONE
if self._isNovel:
if isNovel:
if self._hideSection:
tText = ""
tType = BlockTyp.EMPTY
@@ -781,10 +781,10 @@ class Tokenizer(ABC):
tType = BlockTyp.SEP
tStyle = BlockFmt.CENTRE
blocks.append((
tType, nHead, tText, [], tStyle
tBlocks.append((
tType, f"{tHandle}:T{nHead:04d}", tText, [], tStyle
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
else:
@@ -828,30 +828,28 @@ class Tokenizer(ABC):
sAlign |= BlockFmt.IND_R
# Process formats
tLine, tFmt = self._extractFormats(aLine, hDialog=self._isNovel)
blocks.append((
BlockTyp.TEXT, nHead, tLine, tFmt, sAlign
tLine, tFmt = self._extractFormats(aLine, hDialog=isNovel)
tBlocks.append((
BlockTyp.TEXT, "", tLine, tFmt, sAlign
))
if self._keepRaw:
if keepRaw:
tmpMarkdown.append(f"{aLine}\n")
# If we have content, turn off the first page flag
if self._isFirst and blocks:
if self._isFirst and len(tBlocks) > 1:
self._isFirst = False # First document has been processed
# Make sure the blocks array doesn't start with a page break
# on the very first page, adding a blank first page.
if blocks[0][4] & BlockFmt.PBB:
cBlock = blocks[0]
blocks[0] = (
if tBlocks[1][4] & BlockFmt.PBB:
cBlock = tBlocks[1]
tBlocks[1] = (
cBlock[0], cBlock[1], cBlock[2], cBlock[3], cBlock[4] & ~BlockFmt.PBB
)
# Always add an empty line at the end of the file
blocks.append((
BlockTyp.EMPTY, nHead, "", [], BlockFmt.NONE
))
if self._keepRaw:
tBlocks.append(B_EMPTY)
if keepRaw:
tmpMarkdown.append("\n")
self._markdown.append("".join(tmpMarkdown))
@@ -860,24 +858,18 @@ class Tokenizer(ABC):
# This second pass strips away consecutive blank lines, and
# combines consecutive text lines into the same paragraph.
# It also ensures that there isn't paragraph spacing between
# meta data lines for formats that has spacing.
self._blocks = []
pBlock: T_Block = (BlockTyp.EMPTY, 0, "", [], BlockFmt.NONE)
nBlock: T_Block = (BlockTyp.EMPTY, 0, "", [], BlockFmt.NONE)
# meta data lines for formats that have spacing.
lineSep = "\n" if self._keepBreaks else " "
pLines: list[T_Block] = []
sBlocks: list[T_Block] = []
for n, cBlock in enumerate(tBlocks[1:-1], 1):
tCount = len(blocks)
for n, cBlock in enumerate(blocks):
pBlock = tBlocks[n-1] # Look behind
nBlock = tBlocks[n+1] # Look ahead
if n > 0:
pBlock = blocks[n-1] # Look behind
if n < tCount - 1:
nBlock = blocks[n+1] # Look ahead
if cBlock[0] in self.L_SKIP_INDENT and not self._indentFirst:
if cBlock[0] in SKIP_INDENT and not indentFirst:
# Unless the indentFirst flag is set, we set up the next
# paragraph to not be indented if we see a block of a
# specific type
@@ -891,10 +883,10 @@ class Tokenizer(ABC):
# Adjust margins for lines in a list of keyword lines
aStyle = cBlock[4]
if pBlock[0] == BlockTyp.KEYWORD:
aStyle |= BlockFmt.Z_TOPMRG
aStyle |= BlockFmt.Z_TOP
if nBlock[0] == BlockTyp.KEYWORD:
aStyle |= BlockFmt.Z_BTMMRG
self._blocks.append((
aStyle |= BlockFmt.Z_BTM
sBlocks.append((
cBlock[0], cBlock[1], cBlock[2], cBlock[3], aStyle
))
@@ -906,7 +898,7 @@ class Tokenizer(ABC):
# Next block is not text, so we add the buffer to blocks
nLines = len(pLines)
cStyle = pLines[0][4]
if self._firstIndent and not (self._noIndent or cStyle & self.M_ALIGNED):
if firstIndent and not (self._noIndent or cStyle & BlockFmt.ALIGNED):
# If paragraph indentation is enabled, not temporarily
# turned off, and the block is not aligned, we add the
# text indentation flag
@@ -916,9 +908,9 @@ class Tokenizer(ABC):
# The paragraph contains a single line, so we just save
# that directly to the blocks list. If justify is
# enabled, and there is no alignment, we apply it.
if self._doJustify and not cStyle & self.M_ALIGNED:
if doJustify and not cStyle & BlockFmt.ALIGNED:
cStyle |= BlockFmt.JUSTIFY
self._blocks.append((
sBlocks.append((
BlockTyp.TEXT, pLines[0][1], pLines[0][2], pLines[0][3], cStyle
))
elif nLines > 1:
@@ -931,7 +923,7 @@ class Tokenizer(ABC):
tLen = len(tTxt)
tTxt += f"{aBlock[2]}{lineSep}"
tFmt.extend((p+tLen, fmt, key) for p, fmt, key in aBlock[3])
self._blocks.append((
sBlocks.append((
BlockTyp.TEXT, pLines[0][1], tTxt[:-1], tFmt, cStyle
))
@@ -940,15 +932,16 @@ class Tokenizer(ABC):
self._noIndent = False
else:
self._blocks.append(cBlock)
sBlocks.append(cBlock)
self._blocks = sBlocks
return
def buildOutline(self) -> None:
"""Build an outline of the text up to level 3 headings."""
tHandle = self._handle or ""
isNovel = self._isNovel
for tType, nHead, tText, _, _ in self._blocks:
for tType, tKey, tText, _, _ in self._blocks:
if tType == BlockTyp.TITLE:
prefix = "TT"
elif tType == BlockTyp.HEAD1:
@@ -960,9 +953,8 @@ class Tokenizer(ABC):
else:
continue
key = f"{tHandle}:T{nHead:04d}"
text = tText.replace(nwHeadFmt.BR, " ").replace("&amp;", "&")
self._outline[key] = f"{prefix}|{text}"
self._outline[tKey] = f"{prefix}|{text}"
return
@@ -1006,7 +998,7 @@ class Tokenizer(ABC):
allWordChars += nPWChars
textWordChars += nPWChars
elif tType in self.L_HEADINGS:
elif tType in HEADINGS:
titleCount += 1
allWords += nWords
titleWords += nWords
@@ -1089,14 +1081,16 @@ class Tokenizer(ABC):
rFmt.extend((p + shift, f, d) for p, f, d in tFmt)
return tTxt, rFmt
def _formatMeta(self, text: str) -> tuple[str, T_Formats]:
"""Parse a meta line into a """
def _formatMeta(self, text: str) -> tuple[str, str, T_Formats]:
"""Apply formatting to a meta data line."""
tag = ""
txt = []
fmt = []
valid, bits, _ = self._project.index.scanThis(text)
if valid and bits and bits[0] in nwLabels.KEY_NAME and bits[0] not in self._skipKeywords:
tag = bits[0]
pos = 0
lbl = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:"
lbl = f"{self._localLookup(nwLabels.KEY_NAME[tag])}:"
end = len(lbl)
fmt = [
(pos, TextFmt.B_B, ""),
@@ -1138,7 +1132,7 @@ class Tokenizer(ABC):
txt.append(", ")
pos += 2
return "".join(txt), fmt
return tag, "".join(txt), fmt
def _extractFormats(
self, text: str, skip: int = 0, hDialog: bool = False
+1 -1
View File
@@ -154,7 +154,7 @@ class ToMarkdown(Tokenizer):
lines.append(f"{self._formatText(tText, tFormat, mTags)}\n\n")
elif tType == BlockTyp.KEYWORD:
end = " \n" if tStyle & BlockFmt.Z_BTMMRG else "\n\n"
end = " \n" if tStyle & BlockFmt.Z_BTM else "\n\n"
lines.append(f"{self._formatText(tText, tFormat, mTags)}{end}")
self._result = "".join(lines)
+2 -2
View File
@@ -423,9 +423,9 @@ class ToOdt(Tokenizer):
if tStyle & BlockFmt.PBA:
oStyle.setBreakAfter("page")
if tStyle & BlockFmt.Z_BTMMRG:
if tStyle & BlockFmt.Z_BTM:
oStyle.setMarginBottom("0.000cm")
if tStyle & BlockFmt.Z_TOPMRG:
if tStyle & BlockFmt.Z_TOP:
oStyle.setMarginTop("0.000cm")
if tStyle & BlockFmt.IND_L:
+10 -10
View File
@@ -37,7 +37,7 @@ from PyQt5.QtPrintSupport import QPrinter
from novelwriter.constants import nwHeadFmt, nwStyles, nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.formats.tokenizer import HEADINGS, Tokenizer
from novelwriter.types import (
QtAlignAbsolute, QtAlignCenter, QtAlignJustify, QtAlignLeft, QtAlignRight,
QtPageBreakAfter, QtPageBreakBefore, QtTransparent, QtVAlignNormal,
@@ -173,7 +173,7 @@ class ToQTextDocument(Tokenizer):
cursor = QTextCursor(self._document)
cursor.movePosition(QTextCursor.MoveOperation.End)
for tType, nHead, tText, tFormat, tStyle in self._blocks:
for tType, tMeta, tText, tFormat, tStyle in self._blocks:
bFmt = QTextBlockFormat(self._blockFmt)
if tType in (BlockTyp.COMMENT, BlockTyp.KEYWORD):
@@ -198,9 +198,9 @@ class ToQTextDocument(Tokenizer):
if tStyle & BlockFmt.PBA:
bFmt.setPageBreakPolicy(QtPageBreakAfter)
if tStyle & BlockFmt.Z_BTMMRG:
if tStyle & BlockFmt.Z_BTM:
bFmt.setBottomMargin(0.0)
if tStyle & BlockFmt.Z_TOPMRG:
if tStyle & BlockFmt.Z_TOP:
bFmt.setTopMargin(0.0)
if tStyle & BlockFmt.IND_L:
@@ -214,8 +214,8 @@ class ToQTextDocument(Tokenizer):
newBlock(cursor, bFmt)
self._insertFragments(tText, tFormat, cursor, self._charFmt)
elif tType in self.L_HEADINGS:
bFmt, cFmt = self._genHeadStyle(tType, nHead, bFmt)
elif tType in HEADINGS:
bFmt, cFmt = self._genHeadStyle(tType, tMeta, bFmt)
newBlock(cursor, bFmt)
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
@@ -254,7 +254,7 @@ class ToQTextDocument(Tokenizer):
cursor = QTextCursor(self._document)
cursor.movePosition(QTextCursor.MoveOperation.End)
bFmt, cFmt = self._genHeadStyle(BlockTyp.HEAD4, -1, self._blockFmt)
bFmt, cFmt = self._genHeadStyle(BlockTyp.HEAD4, "", self._blockFmt)
newBlock(cursor, bFmt)
cursor.insertText(self._localLookup("Footnotes"), cFmt)
@@ -356,7 +356,7 @@ class ToQTextDocument(Tokenizer):
return
def _genHeadStyle(self, hType: BlockTyp, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
def _genHeadStyle(self, hType: BlockTyp, hKey: str, rFmt: QTextBlockFormat) -> T_TextStyle:
"""Generate a heading style set."""
mTop, mBottom = self._mHead.get(hType, (0.0, 0.0))
@@ -369,8 +369,8 @@ class ToQTextDocument(Tokenizer):
cFmt.setForeground(self._theme.head if hCol else self._theme.text)
cFmt.setFontWeight(self._bold if self._boldHeads else self._normal)
cFmt.setFontPointSize(self._sHead.get(hType, 1.0))
if nHead >= 0:
cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"])
if hKey:
cFmt.setAnchorNames([hKey])
cFmt.setAnchor(True)
return bFmt, cFmt