1 /***************************************************************************
2 rtfcode.cpp - description
3 -------------------
4 begin : Die Jul 9 2002
5 copyright : (C) 2002-2021 by Andre Simon
6 email : a.simon@mailbox.org
7 ***************************************************************************/
8
9
10 /*
11 This file is part of Highlight.
12
13 Highlight is free software: you can redistribute it and/or modify
14 it under the terms of the GNU General Public License as published by
15 the Free Software Foundation, either version 3 of the License, or
16 (at your option) any later version.
17
18 Highlight is distributed in the hope that it will be useful,
19 but WITHOUT ANY WARRANTY; without even the implied warranty of
20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 GNU General Public License for more details.
22
23 You should have received a copy of the GNU General Public License
24 along with Highlight. If not, see <http://www.gnu.org/licenses/>.
25 */
26
27
28 #include <sstream>
29 #include <string>
30
31 #include "charcodes.h"
32 #include "version.h"
33 #include "rtfgenerator.h"
34
35 namespace highlight
36 {
37
RtfGenerator()38 RtfGenerator::RtfGenerator()
39 : CodeGenerator ( RTF ),
40 pageSize ( "a4" ), // Default: DIN A4
41 addCharStyles ( false ),
42 addPageColor(false),
43 isUtf8(false),
44 utf16Char(0),
45 utf8SeqLen(0)
46 {
47 newLineTag = "}\\par\\pard\n\\cbpat1{";
48 spacer = initialSpacer = " ";
49
50 // Page dimensions
51 psMap["a3"] = PageSize ( 16837,23811 );
52 psMap["a4"] = PageSize ( 11905,16837 );
53 psMap["a5"] = PageSize ( 8390,11905 );
54
55 psMap["b4"] = PageSize ( 14173,20012 );
56 psMap["b5"] = PageSize ( 9977,14173 );
57 psMap["b6"] = PageSize ( 7086,9977 );
58
59 psMap["letter"] = PageSize ( 12240,15840 );
60 psMap["legal"] = PageSize ( 12240,20163 );
61 }
62
~RtfGenerator()63 RtfGenerator::~RtfGenerator()
64 {}
65
getHeader()66 string RtfGenerator::getHeader()
67 {
68 return string();
69 }
70
getAttributes(const ElementStyle & col)71 string RtfGenerator::getAttributes ( const ElementStyle & col )
72 {
73 stringstream s;
74 s << "\\red"<< col.getColour().getRed ( RTF )
75 << "\\green"<<col.getColour().getGreen ( RTF )
76 << "\\blue"<<col.getColour().getBlue ( RTF )
77 << ";";
78 return s.str();
79 }
80
getOpenTag(int styleNumber,const ElementStyle & elem)81 string RtfGenerator::getOpenTag ( int styleNumber,const ElementStyle & elem )
82 {
83 ostringstream s;
84 s << "{";
85 if ( addCharStyles ) {
86 s<<"\\*\\cs"<< ( styleNumber+2 );
87 }
88 s << "\\cf"<< ( styleNumber+2 ) <<"{";
89
90 if ( elem.isBold() ) s << "\\b ";
91 if ( elem.isItalic() ) s << "\\i ";
92 if ( elem.isUnderline() ) s << "\\ul ";
93 return s.str();
94 }
95
96
getCloseTag(const ElementStyle & elem)97 string RtfGenerator::getCloseTag ( const ElementStyle &elem )
98 {
99 ostringstream s;
100 if ( elem.isBold() ) s << "\\b0 ";
101 if ( elem.isItalic() ) s << "\\i0 ";
102 if ( elem.isUnderline() ) s << "\\ul0 ";
103 s << "}}";
104 return s.str();
105 }
106
getCharStyle(int styleNumber,const ElementStyle & elem,const string & styleName)107 string RtfGenerator::getCharStyle ( int styleNumber,const ElementStyle &elem,
108 const string&styleName )
109 {
110 ostringstream s;
111 s << "{\\*\\cs"<< ( styleNumber+2 ) <<"\\additive\\cf"<< ( styleNumber+2 )
112 << "\\f1\\fs";
113 int fontSize=0;
114 StringTools::str2num<int> ( fontSize, this->getBaseFontSize(), std::dec );
115 s << ( ( fontSize ) ? fontSize*2: 20 ); // RTF needs double amount
116 if ( elem.isBold() ) s << "\\b";
117 if ( elem.isItalic() ) s << "\\i";
118 if ( elem.isUnderline() ) s << "\\ul";
119 s << "\\sbasedon222\\snext0 "<< styleName << ";}\n";
120 return s.str();
121 }
122 // {\*\cs2\additive\cf2\f1\fs20\sbasedon222\snext0 HL Default;}
123
124
printBody()125 void RtfGenerator::printBody()
126 {
127 isUtf8 = StringTools::change_case ( encoding ) == "utf-8";
128
129 *out << "{\\rtf1\\ansi \\deff1" // drop \\uc0 because of unicode output
130 << "{\\fonttbl{\\f1\\fmodern\\fprq1\\fcharset0 " ;
131 *out << this->getBaseFont() ;
132 *out << ";}}"
133 << "{\\colortbl;";
134
135 *out << "\\red" << ( docStyle.getBgColour().getRed ( RTF ) );
136 *out << "\\green" << ( docStyle.getBgColour().getGreen ( RTF ) );
137 *out << "\\blue" << ( docStyle.getBgColour().getBlue ( RTF ) );
138 *out << ";";
139
140 *out << getAttributes ( docStyle.getDefaultStyle() );
141
142 *out << getAttributes ( docStyle.getStringStyle() );
143 *out << getAttributes ( docStyle.getNumberStyle() );
144 *out << getAttributes ( docStyle.getSingleLineCommentStyle() );
145
146 *out << getAttributes ( docStyle.getCommentStyle() );
147 *out << getAttributes ( docStyle.getEscapeCharStyle() );
148 *out << getAttributes ( docStyle.getPreProcessorStyle() );
149
150 *out << getAttributes ( docStyle.getPreProcStringStyle() );
151 *out << getAttributes ( docStyle.getLineStyle() );
152 *out << getAttributes ( docStyle.getOperatorStyle() );
153 *out << getAttributes ( docStyle.getInterpolationStyle() );
154
155 *out << getAttributes ( docStyle.getErrorStyle() );
156 *out << getAttributes ( docStyle.getErrorMessageStyle() );
157
158 /* For output formats which can refer to external styles it is more safe
159 to use the colour theme's keyword class names, since the language
160 definitions (which may change during a batch conversion) do not have to define
161 all keyword classes, that are needed to highlight all input files correctly.
162 It is ok for RTF to use the language definition's class names, because RTF
163 does not refer to external styles.
164 We cannot use the theme's class names, because KSIterator returns an
165 alphabetically ordered list, which is not good because RTF is dependent
166 on the order. We access the keyword style with an ID, which is calculated
167 ignoring the alphabetic order.
168 */
169
170 ///FIXME: nested syntax may be highlighted incorrectly if it contains more
171 // keyword definitions than the hosting syntax.
172 // Workaround: Add keyword group to hosting syntax (see html.lang)
173 vector<string> keywordClasses = currentSyntax->getKeywordClasses();
174
175 for ( unsigned int i=0; i<keywordClasses.size(); i++ ) {
176 *out << getAttributes ( docStyle.getKeywordStyle ( keywordClasses[i] ) );
177 }
178
179 *out << "}\n";
180
181 if ( addCharStyles ) {
182 *out << "{\\stylesheet{\n";
183 *out << getCharStyle ( STANDARD, docStyle.getDefaultStyle(), "HL Default" );
184 *out << getCharStyle ( STRING, docStyle.getStringStyle(), "HL String" );
185 *out << getCharStyle ( NUMBER, docStyle.getNumberStyle(), "HL Number" );
186 *out << getCharStyle ( SL_COMMENT, docStyle.getSingleLineCommentStyle(), "HL SL Comment" );
187 *out << getCharStyle ( ML_COMMENT, docStyle.getCommentStyle(), "HL ML Comment" );
188 *out << getCharStyle ( ESC_CHAR, docStyle.getEscapeCharStyle(), "HL Escape Character" );
189 *out << getCharStyle ( DIRECTIVE, docStyle.getPreProcessorStyle(), "HL Directive" );
190 *out << getCharStyle ( DIRECTIVE_STRING, docStyle.getPreProcStringStyle(), "HL Directive String" );
191 *out << getCharStyle ( LINENUMBER, docStyle.getLineStyle(), "HL Line" );
192 *out << getCharStyle ( SYMBOL, docStyle.getOperatorStyle(), "HL Operator" );
193 *out << getCharStyle ( STRING_INTERPOLATION, docStyle.getInterpolationStyle(), "HL Interpolation" );
194
195 *out << getCharStyle ( SYNTAX_ERROR, docStyle.getErrorStyle(), "HL Error" );
196 *out << getCharStyle ( SYNTAX_ERROR_MSG, docStyle.getErrorMessageStyle(), "HL Warning" );
197
198 char styleName[20];
199 for ( unsigned int i=0; i<keywordClasses.size(); i++ ) {
200 sprintf ( styleName, "HL Keyword %c", 'A'+i ); //maybe better simple numbering
201 *out << getCharStyle ( KEYWORD+i, docStyle.getKeywordStyle ( keywordClasses[i] ), string ( styleName ) );
202 }
203 *out << "}}\n";
204 }
205
206 if (addPageColor) {
207 long svVal = docStyle.getBgColour().getRed() +
208 docStyle.getBgColour().getGreen() * 256+
209 docStyle.getBgColour().getBlue() * 256 * 256;
210 *out<<"\\viewbksp1\\ilfomacatclnup0{\\*\\background{\\shp{{\\sp{\\sn fillColor}{\\sv "<<svVal<<"}}}}}\n";
211 }
212
213 *out << "\\paperw"<< psMap[pageSize].width <<"\\paperh"<< psMap[pageSize].height
214 << "\\margl1134\\margr1134\\margt1134\\margb1134\\sectd" // page margins
215 << "\\plain\\f1\\fs" ; // Font formatting
216 int fontSize=0;
217 StringTools::str2num<int> ( fontSize, this->getBaseFontSize(), std::dec );
218 *out << ( ( fontSize ) ? fontSize*2: 20 ); // RTF needs double amount
219 *out << "\n\\pard \\cbpat1{";
220
221 processRootState();
222
223 *out << "}}"<<endl;
224 }
225
getFooter()226 string RtfGenerator::getFooter()
227 {
228 return string();
229 }
230
initOutputTags()231 void RtfGenerator::initOutputTags ( )
232 {
233 openTags.push_back ( getOpenTag ( STANDARD, docStyle.getDefaultStyle() ) );
234 openTags.push_back ( getOpenTag ( STRING, docStyle.getStringStyle() ) );
235 openTags.push_back ( getOpenTag ( NUMBER, docStyle.getNumberStyle() ) );
236 openTags.push_back ( getOpenTag ( SL_COMMENT, docStyle.getSingleLineCommentStyle() ) );
237 openTags.push_back ( getOpenTag ( ML_COMMENT,docStyle.getCommentStyle() ) );
238 openTags.push_back ( getOpenTag ( ESC_CHAR, docStyle.getEscapeCharStyle() ) );
239 openTags.push_back ( getOpenTag ( DIRECTIVE, docStyle.getPreProcessorStyle() ) );
240 openTags.push_back ( getOpenTag ( DIRECTIVE_STRING, docStyle.getPreProcStringStyle() ) );
241 openTags.push_back ( getOpenTag ( LINENUMBER, docStyle.getLineStyle() ) );
242 openTags.push_back ( getOpenTag ( SYMBOL, docStyle.getOperatorStyle() ) );
243 openTags.push_back ( getOpenTag ( STRING_INTERPOLATION, docStyle.getInterpolationStyle()) );
244
245 openTags.push_back ( getOpenTag ( SYNTAX_ERROR, docStyle.getErrorStyle() ) );
246 openTags.push_back ( getOpenTag ( SYNTAX_ERROR_MSG, docStyle.getErrorMessageStyle()) );
247
248 closeTags.push_back ( getCloseTag ( docStyle.getDefaultStyle() ) );
249 closeTags.push_back ( getCloseTag ( docStyle.getStringStyle() ) );
250 closeTags.push_back ( getCloseTag ( docStyle.getNumberStyle() ) );
251 closeTags.push_back ( getCloseTag ( docStyle.getSingleLineCommentStyle() ) );
252 closeTags.push_back ( getCloseTag ( docStyle.getCommentStyle() ) );
253 closeTags.push_back ( getCloseTag ( docStyle.getEscapeCharStyle() ) );
254 closeTags.push_back ( getCloseTag ( docStyle.getPreProcessorStyle() ) );
255 closeTags.push_back ( getCloseTag ( docStyle.getPreProcStringStyle() ) );
256 closeTags.push_back ( getCloseTag ( docStyle.getLineStyle() ) );
257 closeTags.push_back ( getCloseTag ( docStyle.getOperatorStyle() ) );
258 closeTags.push_back ( getCloseTag ( docStyle.getInterpolationStyle() ) );
259
260 closeTags.push_back ( getCloseTag ( docStyle.getErrorStyle()) );
261 closeTags.push_back ( getCloseTag ( docStyle.getErrorMessageStyle()) );
262 }
263
maskCharacter(unsigned char c)264 string RtfGenerator::maskCharacter ( unsigned char c )
265 {
266 if (isUtf8 && c > 0x7f && utf8SeqLen==0) {
267
268 //http://stackoverflow.com/questions/7153935/how-to-convert-utf-8-stdstring-to-utf-16-stdwstring
269
270 if (c <= 0xDF) {
271 utf16Char = c&0x1F;
272 utf8SeqLen = 1;
273 } else if (c <= 0xEF) {
274 utf16Char = c&0x0F;
275 utf8SeqLen = 2;
276 } else if (c <= 0xF7) {
277 utf16Char = c&0x07;
278 utf8SeqLen = 3;
279 } else {
280 utf8SeqLen = 0;
281 }
282 return "";
283 }
284
285 if (utf8SeqLen) {
286 utf16Char <<= 6;
287 utf16Char += c & 0x3f;
288 --utf8SeqLen;
289
290 if (!utf8SeqLen) {
291 string m ( "\\u" );
292 m += to_string(utf16Char);
293 m += '?';
294 utf16Char=0L;
295 return m;
296 } else {
297 return "";
298 }
299 }
300
301 switch ( c ) {
302 case '}' :
303 case '{' :
304 case '\\' : {
305 string m ( "\\" );
306 return m += c;
307 }
308 break;
309 case '0':
310 case '1':
311 case '2':
312 case '3':
313 case '4':
314 case '5':
315 case '6':
316 case '7':
317 case '8':
318 case '9': {
319 string m ( 1, '{' );
320 m += c;
321 m += '}';
322 return m;
323 }
324 break;
325
326 case AUML_LC:
327 return "\\'e4";
328 break;
329 case OUML_LC:
330 return "\\'f6";
331 break;
332 case UUML_LC:
333 return "\\'fc";
334 break;
335 case AUML_UC:
336 return "\\'c4";
337 break;
338 case OUML_UC:
339 return "\\'d6";
340 break;
341 case UUML_UC:
342 return "\\'dc";
343 break;
344
345 case AACUTE_LC:
346 return "\\'e1";
347 break;
348 case EACUTE_LC:
349 return "\\'e9";
350 break;
351 case OACUTE_LC:
352 return "\\'f3";
353 break;
354 case UACUTE_LC:
355 return "\\'fa";
356 break;
357
358 case AGRAVE_LC:
359 return "\\'e0";
360 break;
361 case EGRAVE_LC:
362 return "\\'e8";
363 break;
364 case OGRAVE_LC:
365 return "\\'f2";
366 break;
367 case UGRAVE_LC:
368 return "\\'f9";
369 break;
370
371 case AACUTE_UC:
372 return "\\'c1";
373 break;
374 case EACUTE_UC:
375 return "\\'c9";
376 break;
377 case OACUTE_UC:
378 return "\\'d3";
379 break;
380 case UACUTE_UC:
381 return "\\'da";
382 break;
383 case AGRAVE_UC:
384 return "\\'c0";
385 break;
386 case EGRAVE_UC:
387 return "\\'c8";
388 break;
389 case OGRAVE_UC:
390 return "\\'d2";
391 break;
392 case UGRAVE_UC:
393 return "\\'d9";
394 break;
395
396 case SZLIG:
397 return "\\'df";
398 break;
399
400 default :
401 return string ( 1, c );
402 }
403 }
404
getKeywordOpenTag(unsigned int styleID)405 string RtfGenerator::getKeywordOpenTag ( unsigned int styleID )
406 {
407 return getOpenTag ( KEYWORD+styleID,
408 docStyle.getKeywordStyle ( currentSyntax->getKeywordClasses() [styleID] ) );
409 }
410
getKeywordCloseTag(unsigned int styleID)411 string RtfGenerator::getKeywordCloseTag ( unsigned int styleID )
412 {
413 return getCloseTag ( docStyle.getKeywordStyle ( currentSyntax->getKeywordClasses() [styleID] ) );
414 }
415
setRTFPageSize(const string & ps)416 void RtfGenerator::setRTFPageSize ( const string & ps )
417 {
418 if ( psMap.count ( ps ) ) pageSize = ps;
419 }
420
setRTFCharStyles(bool cs)421 void RtfGenerator::setRTFCharStyles ( bool cs )
422 {
423 addCharStyles = cs;
424 }
425
setRTFPageColor(bool pc)426 void RtfGenerator::setRTFPageColor ( bool pc )
427 {
428 addPageColor = pc;
429 }
430
431
432 }
433