1 /* Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
2    file Copyright.txt or https://cmake.org/licensing for details.  */
3 #include "cmWIXRichTextFormatWriter.h"
4 
5 #include "cmVersion.h"
6 
cmWIXRichTextFormatWriter(std::string const & filename)7 cmWIXRichTextFormatWriter::cmWIXRichTextFormatWriter(
8   std::string const& filename)
9   : File(filename.c_str(), std::ios::binary)
10 {
11   StartGroup();
12   WriteHeader();
13   WriteDocumentPrefix();
14 }
15 
~cmWIXRichTextFormatWriter()16 cmWIXRichTextFormatWriter::~cmWIXRichTextFormatWriter()
17 {
18   EndGroup();
19 
20   /* I haven't seen this in the RTF spec but
21    *  wordpad terminates its RTF like this */
22   File << "\r\n";
23   File.put(0);
24 }
25 
AddText(std::string const & text)26 void cmWIXRichTextFormatWriter::AddText(std::string const& text)
27 {
28   using rtf_byte_t = unsigned char;
29 
30   for (size_t i = 0; i < text.size(); ++i) {
31     rtf_byte_t c = rtf_byte_t(text[i]);
32 
33     switch (c) {
34       case '\\':
35         File << "\\\\";
36         break;
37       case '{':
38         File << "\\{";
39         break;
40       case '}':
41         File << "\\}";
42         break;
43       case '\n':
44         File << "\\par\r\n";
45         break;
46       case '\r':
47         continue;
48       default: {
49         if (c <= 0x7F) {
50           File << c;
51         } else {
52           if (c <= 0xC0) {
53             EmitInvalidCodepoint(c);
54           } else if (c < 0xE0 && i + 1 < text.size()) {
55             EmitUnicodeCodepoint((text[i + 1] & 0x3F) | ((c & 0x1F) << 6));
56             i += 1;
57           } else if (c < 0xF0 && i + 2 < text.size()) {
58             EmitUnicodeCodepoint((text[i + 2] & 0x3F) |
59                                  ((text[i + 1] & 0x3F) << 6) |
60                                  ((c & 0xF) << 12));
61             i += 2;
62           } else if (c < 0xF8 && i + 3 < text.size()) {
63             EmitUnicodeCodepoint(
64               (text[i + 3] & 0x3F) | ((text[i + 2] & 0x3F) << 6) |
65               ((text[i + 1] & 0x3F) << 12) | ((c & 0x7) << 18));
66             i += 3;
67           } else {
68             EmitInvalidCodepoint(c);
69           }
70         }
71       } break;
72     }
73   }
74 }
75 
WriteHeader()76 void cmWIXRichTextFormatWriter::WriteHeader()
77 {
78   ControlWord("rtf1");
79   ControlWord("ansi");
80   ControlWord("ansicpg1252");
81   ControlWord("deff0");
82   ControlWord("deflang1033");
83 
84   WriteFontTable();
85   WriteColorTable();
86   WriteGenerator();
87 }
88 
WriteFontTable()89 void cmWIXRichTextFormatWriter::WriteFontTable()
90 {
91   StartGroup();
92   ControlWord("fonttbl");
93 
94   StartGroup();
95   ControlWord("f0");
96   ControlWord("fswiss");
97   ControlWord("fcharset0 Consolas;");
98   EndGroup();
99 
100   EndGroup();
101 }
102 
WriteColorTable()103 void cmWIXRichTextFormatWriter::WriteColorTable()
104 {
105   StartGroup();
106   ControlWord("colortbl ;");
107   ControlWord("red255");
108   ControlWord("green0");
109   ControlWord("blue0;");
110   ControlWord("red0");
111   ControlWord("green255");
112   ControlWord("blue0;");
113   ControlWord("red0");
114   ControlWord("green0");
115   ControlWord("blue255;");
116   EndGroup();
117 }
118 
WriteGenerator()119 void cmWIXRichTextFormatWriter::WriteGenerator()
120 {
121   StartGroup();
122   NewControlWord("generator");
123   File << " CPack WiX Generator (" << cmVersion::GetCMakeVersion() << ");";
124   EndGroup();
125 }
126 
WriteDocumentPrefix()127 void cmWIXRichTextFormatWriter::WriteDocumentPrefix()
128 {
129   ControlWord("viewkind4");
130   ControlWord("uc1");
131   ControlWord("pard");
132   ControlWord("f0");
133   ControlWord("fs14");
134 }
135 
ControlWord(std::string const & keyword)136 void cmWIXRichTextFormatWriter::ControlWord(std::string const& keyword)
137 {
138   File << "\\" << keyword;
139 }
140 
NewControlWord(std::string const & keyword)141 void cmWIXRichTextFormatWriter::NewControlWord(std::string const& keyword)
142 {
143   File << "\\*\\" << keyword;
144 }
145 
StartGroup()146 void cmWIXRichTextFormatWriter::StartGroup()
147 {
148   File.put('{');
149 }
150 
EndGroup()151 void cmWIXRichTextFormatWriter::EndGroup()
152 {
153   File.put('}');
154 }
155 
EmitUnicodeCodepoint(int c)156 void cmWIXRichTextFormatWriter::EmitUnicodeCodepoint(int c)
157 {
158   // Do not emit byte order mark (BOM)
159   if (c == 0xFEFF) {
160     return;
161   } else if (c <= 0xFFFF) {
162     EmitUnicodeSurrogate(c);
163   } else {
164     c -= 0x10000;
165     EmitUnicodeSurrogate(((c >> 10) & 0x3FF) + 0xD800);
166     EmitUnicodeSurrogate((c & 0x3FF) + 0xDC00);
167   }
168 }
169 
EmitUnicodeSurrogate(int c)170 void cmWIXRichTextFormatWriter::EmitUnicodeSurrogate(int c)
171 {
172   ControlWord("u");
173   if (c <= 32767) {
174     File << c;
175   } else {
176     File << (c - 65536);
177   }
178   File << "?";
179 }
180 
EmitInvalidCodepoint(int c)181 void cmWIXRichTextFormatWriter::EmitInvalidCodepoint(int c)
182 {
183   ControlWord("cf1 ");
184   File << "[INVALID-BYTE-" << int(c) << "]";
185   ControlWord("cf0 ");
186 }
187