1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2001, Sven2
6 * Copyright (c) 2017-2022, The LegacyClonk Team and contributors
7 *
8 * Distributed under the terms of the ISC license; see accompanying file
9 * "COPYING" for details.
10 *
11 * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12 * See accompanying file "TRADEMARK" for details.
13 *
14 * To redistribute this file separately, substitute the full license texts
15 * for the above references.
16 */
17
18// markup tags for fonts
19
20#include <StdMarkup.h>
21#include <StdColors.h>
22#include <StdDDraw2.h>
23
24void CMarkupTagItalic::Apply(CBltTransform &rBltTrf, bool fDoClr, uint32_t &dwClr)
25{
26 // do sheering
27 rBltTrf.mat[1] -= 0.3f;
28}
29
30void CMarkupTagColor::Apply(CBltTransform &rBltTrf, bool fDoClr, uint32_t &dwClr)
31{
32 // set color
33 if (fDoClr) dwClr = this->dwClr;
34}
35
36bool CMarkup::Read(const char **ppText, bool fSkip)
37{
38 char Tag[50]; CMarkupTag *pNewTag = nullptr;
39 // get tag
40 if (!SCopyEnclosed(szSource: *ppText, cOpen: '<', cClose: '>', sTarget: Tag, iSize: 49)) return false;
41 const auto iTagLen = SLen(sptr: Tag);
42 // split tag to name and pars
43 char *szPars = nullptr; int iSPos;
44 if ((iSPos = SCharPos(cTarget: ' ', szInStr: Tag)) > -1)
45 {
46 Tag[iSPos] = 0;
47 szPars = Tag + iSPos + 1;
48 }
49 // closing tag?
50 if (Tag[0] == '/')
51 {
52 // no parameters
53 if (szPars) return false;
54 if (!fSkip)
55 {
56 // is this the tag to be closed?
57 if (!pLast) return false;
58 if (!SEqual(szStr1: pLast->TagName(), szStr2: Tag + 1)) return false;
59 // close it
60 delete Pop();
61 }
62 }
63 // italic
64 else if (SEqual(szStr1: Tag, szStr2: "i"))
65 {
66 // no parameters
67 if (szPars) return false;
68 // create italic tag
69 if (!fSkip) pNewTag = new CMarkupTagItalic();
70 }
71 // colored
72 else if (SEqual(szStr1: Tag, szStr2: "c"))
73 {
74 // no parameters?
75 if (!szPars) return false;
76 if (const auto iParLen = SLen(sptr: szPars); iParLen > 8)
77 {
78 return false;
79 }
80 else if (!fSkip)
81 {
82 // get color value by parameter
83 uint32_t dwClr = 0;
84 for (int i = 0; i < iParLen; ++i)
85 {
86 uint8_t b;
87 if (szPars[i] >= '0' && szPars[i] <= '9') b = szPars[i] - '0';
88 else if (szPars[i] >= 'a' && szPars[i] <= 'f') b = szPars[i] - 'a' + 10;
89 else return false;
90 dwClr |= (b << ((iParLen - i - 1) * 4));
91 }
92 // adjust alpha if not given
93 if (iParLen <= 6) dwClr |= 0xff000000;
94 dwClr = InvertRGBAAlpha(dwFromClr: dwClr);
95 // create color tag
96 pNewTag = new CMarkupTagColor(dwClr);
97 }
98 }
99 // unknown tag
100 else return false;
101 // add created tag
102 if (pNewTag) Push(pTag: pNewTag);
103 // advance past tag
104 *ppText += iTagLen + 2;
105 // success
106 return true;
107}
108
109bool CMarkup::SkipTags(const char **ppText)
110{
111 // read tags as long as found
112 while (**ppText == '<') if (!Read(ppText, fSkip: true)) break;
113 // return whether end is reached
114 return !**ppText;
115}
116
117std::string CMarkup::ToMarkup()
118{
119 std::string result;
120 for (CMarkupTag *pTag = pTags; pTag; pTag = pTag->pNext) result += pTag->ToMarkup();
121 return result;
122}
123
124std::string CMarkup::ToCloseMarkup()
125{
126 std::string result;
127 for (CMarkupTag *pTag = pLast; pTag; pTag = pTag->pPrev) result += std::format(fmt: "</{}>", args: pTag->TagName());
128 return result;
129}
130
131bool CMarkup::StripMarkup(char *szText)
132{
133 // skip any tags and inline-images
134 CMarkup mkup(false);
135 const char *szRead = szText, *szPos2;
136 do
137 {
138 mkup.SkipTags(ppText: &szRead);
139 if (szRead[0] == '{' && szRead[1] == '{' && szRead[2] != '{') // skip at {{{, because {{{id}} should be parsed as { {{id}} }.
140 {
141 if (szPos2 = SSearch(szString: szRead + 2, szIndex: "}}"))
142 // valid {{blub}}-tag
143 szRead = szPos2;
144 else
145 // invalid {{-tag
146 szRead += 2;
147 }
148 else if (szRead[0] == '}' && szRead[1] == '}')
149 // invalid }}-tag
150 szRead += 2;
151 } while (*szText++ = *szRead++);
152 return szText != szRead;
153}
154
155bool CMarkup::StripMarkup(StdStrBuf *sText)
156{
157 // strip any markup codes from given text buffer
158 char *buf = sText->GrabPointer();
159 if (!buf) return false;
160 bool fSuccess = StripMarkup(szText: buf);
161 sText->Take(pnData: buf);
162 return fSuccess;
163}
164