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