Skip to content

Commit 165b088

Browse files
fix: add support for <sub> tag in SVG publication string rendering (#4052)
* fix: add support for <sub> tag in SVG publication string rendering close #4049 * refactor: publication string format
1 parent cc55619 commit 165b088

1 file changed

Lines changed: 51 additions & 34 deletions

File tree

src/component/1d/FloatPublicationString.tsx

Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ import { useACSSettings } from '../hooks/use_acs_settings.js';
1919
import { usePublicationStrings } from '../hooks/use_publication_strings.js';
2020
import { PublicationStringModal } from '../modal/PublicationStringModal.js';
2121

22+
const MARKERS: Record<string, React.SVGProps<SVGTSpanElement>> = {
23+
'++': { baselineShift: 'super' },
24+
'--': { baselineShift: 'sub' },
25+
'**': { fontStyle: 'italic' },
26+
};
27+
28+
function getMarker(word: string) {
29+
return Object.keys(MARKERS).find(
30+
(marker) => word.startsWith(marker) && word.endsWith(marker),
31+
);
32+
}
33+
2234
const ReactRnd = styled(Rnd)`
2335
border: 1px solid transparent;
2436
@@ -51,10 +63,11 @@ function useWrapSVGText(params: UseWrapSVGTextParams) {
5163
labelWeight: style.fontWeight,
5264
debugCanvasWidth: debugCanvas ? width : undefined,
5365
});
54-
5566
const formattedText = text
56-
.replaceAll(/<sup>(?<n>.*?)<\/sup>/g, '++$1++ ')
57-
.replaceAll(/<i>(?<j>.*?)<\/i>/g, '**$1**');
67+
.replaceAll(/<sup>(?<n>.*?)<\/sup>/g, ' ++$1++ ')
68+
.replaceAll(/<sub>(?<k>.*?)<\/sub>/g, ' --$1-- ')
69+
.replaceAll(/<i>(?<j>.*?)<\/i>/g, ' **$1** ')
70+
.trim();
5871

5972
const lineHeight = labelSize * 1.6;
6073

@@ -63,10 +76,12 @@ function useWrapSVGText(params: UseWrapSVGTextParams) {
6376
let lineWidth = 0;
6477

6578
const spaceWidth = getTextWidth(' ');
66-
const words = formattedText.split(' ');
79+
const words = formattedText.split(/\s+/);
6780

6881
for (const word of words) {
69-
const wordWidth = getTextWidth(word);
82+
const marker = getMarker(word);
83+
const cleanWord = marker ? word.replaceAll(marker, '') : word;
84+
const wordWidth = getTextWidth(cleanWord);
7085
if (lineWidth + wordWidth > width) {
7186
lines.push(line);
7287
line = [word];
@@ -87,27 +102,22 @@ function useWrapSVGText(params: UseWrapSVGTextParams) {
87102
for (const line of lines) {
88103
let x = 0;
89104
for (const word of line) {
90-
const isSuper = word.startsWith('++') && word.endsWith('++');
91-
const isItalic = word.startsWith('**') && word.endsWith('**');
92-
const baseLine = ctx.textBaseline;
93-
94-
let finalWord = `${word} `;
95-
if (isSuper) {
96-
finalWord = word.replaceAll('++', '');
97-
98-
ctx.textBaseline = 'bottom';
99-
} else if (isItalic) {
100-
finalWord = word.replaceAll('**', '');
105+
const marker = getMarker(word);
106+
const finalWord = marker ? word.replaceAll(marker, '') : word;
107+
108+
if (marker === '++') {
109+
ctx.fillText(finalWord, x, y - lineHeight * 0.3);
110+
} else if (marker === '--') {
111+
ctx.fillText(finalWord, x, y + lineHeight * 0.3);
112+
} else {
113+
ctx.fillText(finalWord, x, y);
101114
}
102115

103-
ctx?.fillText(finalWord, x, y);
104116
x += getTextWidth(finalWord);
105-
106-
ctx.textBaseline = baseLine;
107117
}
108-
109118
y += lineHeight;
110119
}
120+
ctx.fillStyle = 'black';
111121
});
112122

113123
return { lines, lineHeight };
@@ -146,28 +156,35 @@ function PublicationText(props: PublicationTextProps) {
146156
dominantBaseline="hanging"
147157
>
148158
{line.map((word, wordIndex) => {
149-
if (word.startsWith('++') && word.endsWith('++')) {
159+
const marker = getMarker(word);
160+
161+
if (marker) {
162+
const props = MARKERS[marker];
163+
const isBaselineShift = 'baselineShift' in props;
164+
const fontSize = isBaselineShift
165+
? Math.floor((5 / 6) * textStyleWithSize.fontSize)
166+
: undefined;
167+
150168
return (
151169
<tspan
152170
// eslint-disable-next-line react/no-array-index-key
153171
key={wordIndex}
154-
baselineShift="super"
155-
fontSize={Math.floor((5 / 6) * textStyleWithSize.fontSize)}
172+
fontSize={fontSize}
173+
{...props}
156174
>
157-
{word.replaceAll('++', '')}
158-
</tspan>
159-
);
160-
} else if (word.startsWith('**') && word.endsWith('**')) {
161-
return (
162-
// eslint-disable-next-line react/no-array-index-key
163-
<tspan key={wordIndex} fontStyle="italic">
164-
{word.replaceAll('**', '')}
175+
{word.replaceAll(marker, '')}
165176
</tspan>
166177
);
167-
} else {
168-
// eslint-disable-next-line react/no-array-index-key
169-
return <tspan key={wordIndex}>{word} </tspan>;
170178
}
179+
180+
const addSpace =
181+
!line[wordIndex + 1]?.startsWith('--') ||
182+
/\s$/.test(word) ||
183+
/[^a-zA-Z0-9]$/.test(word);
184+
return (
185+
// eslint-disable-next-line react/no-array-index-key
186+
<tspan key={wordIndex}>{addSpace ? `${word} ` : word}</tspan>
187+
);
171188
})}
172189
</SVGStyledText>
173190
))}

0 commit comments

Comments
 (0)