1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2011-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // markup tags for fonts
17 
18 #include "C4Include.h"
19 #include "lib/C4Markup.h"
20 #include "graphics/C4BltTransform.h"
21 
22 using namespace std::string_literals;
23 
OpeningTag() const24 std::string C4MarkupTag::OpeningTag() const
25 {
26 	return "<"s + TagName() + ">";
27 }
28 
ClosingTag() const29 std::string C4MarkupTag::ClosingTag() const
30 {
31 	return "</"s + TagName() + ">";
32 }
33 
Apply(C4BltTransform & rBltTrf,bool fDoClr,DWORD & dwClr)34 void C4MarkupTagItalic::Apply(C4BltTransform &rBltTrf, bool fDoClr, DWORD &dwClr)
35 {
36 	// do sheering
37 	rBltTrf.mat[1]-=0.3f;
38 }
39 
Apply(C4BltTransform & rBltTrf,bool fDoClr,DWORD & dwClr)40 void C4MarkupTagColor::Apply(C4BltTransform &rBltTrf, bool fDoClr, DWORD &dwClr)
41 {
42 	// set color
43 	if (fDoClr) dwClr = this->dwClr;
44 }
45 
46 
OpeningTag() const47 std::string C4MarkupTagColor::OpeningTag() const
48 {
49 	return "<c "s + FormatString("%x", dwClr).getData() + ">";
50 }
51 
Read(const char ** ppText,bool fSkip)52 bool C4Markup::Read(const char **ppText, bool fSkip)
53 {
54 	char Tag[50]; C4MarkupTag *pNewTag=nullptr; int iTagLen,iParLen;
55 	// get tag
56 	if (!SCopyEnclosed(*ppText, '<', '>', Tag, 49)) return false;
57 	iTagLen=SLen(Tag);
58 	// split tag to name and pars
59 	char *szPars=nullptr; int iSPos;
60 	if ((iSPos=SCharPos(' ', Tag))>-1)
61 	{
62 		Tag[iSPos]=0;
63 		szPars=Tag+iSPos+1;
64 	}
65 	// closing tag?
66 	if (Tag[0]=='/')
67 	{
68 		// no parameters
69 		if (szPars) return false;
70 		if (!fSkip)
71 		{
72 			// is this the tag to be closed?
73 			if (!pLast) return false;
74 			if (!SEqual(pLast->TagName(), Tag+1)) return false;
75 			// close it
76 			delete Pop();
77 		}
78 	}
79 	// italic
80 	else if (SEqual(Tag, "i"))
81 	{
82 		// no parameters
83 		if (szPars) return false;
84 		// create italic tag
85 		if (!fSkip) pNewTag=new C4MarkupTagItalic();
86 	}
87 	// colored
88 	else if (SEqual(Tag, "c"))
89 	{
90 		// no parameters?
91 		if (!szPars) return false;
92 		if ((iParLen=SLen(szPars))>8) return false;
93 		if (!fSkip)
94 		{
95 			// get color value by parameter
96 			DWORD dwClr=0;
97 			for (int i=0; i<iParLen; ++i)
98 			{
99 				BYTE b;
100 				if (szPars[i]>='0' && szPars[i]<='9') b=szPars[i]-'0';
101 				else if (szPars[i]>='a' && szPars[i]<='f') b=szPars[i]-'a'+10;
102 				else return false;
103 				dwClr|=(b<<((iParLen-i-1)*4));
104 			}
105 			// adjust alpha if not given
106 			if (iParLen<=6) dwClr|=0xff000000;
107 			// create color tag
108 			pNewTag=new C4MarkupTagColor(dwClr);
109 		}
110 	}
111 	// unknown tag
112 	else return false;
113 	// add created tag
114 	if (pNewTag) Push(pNewTag);
115 	// advance past tag
116 	*ppText+=iTagLen+2;
117 	// success
118 	return true;
119 }
120 
121 
122 
SkipTags(const char ** ppText)123 bool C4Markup::SkipTags(const char **ppText)
124 {
125 	// read tags as long as found
126 	while (**ppText=='<') if (!Read(ppText, true)) break;
127 	// return whether end is reached
128 	return !**ppText;
129 }
130 
131 
ClosingTags() const132 std::string C4Markup::ClosingTags() const
133 {
134 	std::string result;
135 	for (auto tag = pLast; tag; tag = tag->pPrev)
136 		result += tag->ClosingTag();
137 	return result;
138 }
139 
OpeningTags() const140 std::string C4Markup::OpeningTags() const
141 {
142 	std::string result;
143 	for (auto tag = pTags; tag; tag = tag->pNext)
144 		result += tag->OpeningTag();
145 	return result;
146 }
147 
StripMarkup(char * szText)148 bool C4Markup::StripMarkup(char *szText)
149 {
150 	// skip any tags and inline-images
151 	C4Markup mkup(false);
152 	const char *szRead = szText, *szPos2;
153 	do
154 	{
155 		mkup.SkipTags(&szRead);
156 		if (szRead[0] == '{' && szRead[1] == '{' && szRead[2] != '{') // skip at {{{, because {{{id}} should be parsed as { {{id}} }.
157 		{
158 			if ((szPos2 = SSearch(szRead+2, "}}")))
159 				// valid {{blub}}-tag
160 				szRead = szPos2;
161 			else
162 				// invalid {{-tag
163 				szRead += 2;
164 		}
165 		else if (szRead[0] == '}' && szRead[1] == '}')
166 			// invalid }}-tag
167 			szRead += 2;
168 	}
169 	while ((*szText++ = *szRead++));
170 	return szText != szRead;
171 }
172 
StripMarkup(StdStrBuf * sText)173 bool C4Markup::StripMarkup(StdStrBuf *sText)
174 {
175 	// strip any markup codes from given text buffer
176 	char *buf = sText->GrabPointer();
177 	if (!buf) return false;
178 	bool fSuccess = StripMarkup(buf);
179 	sText->Take(buf);
180 	return fSuccess;
181 }
182