1 /**
2 * \file texstream.cpp
3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
5 *
6 * \author Enrico Forestieri
7 *
8 * Full author contact details are available in file CREDITS.
9 */
10
11 #include <config.h>
12
13 #include "texstream.h"
14
15 #include "TexRow.h"
16
17 #include "support/lstrings.h"
18 #include "support/unicode.h"
19
20 #include <algorithm>
21 #include <cerrno>
22 #include <cstdio>
23 #include <cstring>
24 #include <iconv.h>
25 #include <locale>
26
27 using namespace std;
28
29 using lyx::support::contains;
30 using lyx::support::split;
31
32
33 namespace lyx {
34
35
otexrowstream(odocstream & os)36 otexrowstream::otexrowstream(odocstream & os)
37 : os_(os), texrow_(make_unique<TexRow>())
38 {}
39
40
41 otexrowstream::~otexrowstream() = default;
42
43
releaseTexRow()44 unique_ptr<TexRow> otexrowstream::releaseTexRow()
45 {
46 auto p = make_unique<TexRow>();
47 swap(texrow_, p);
48 return p;
49 }
50
51
put(char_type const & c)52 void otexrowstream::put(char_type const & c)
53 {
54 os_.put(c);
55 if (c == '\n')
56 texrow_->newline();
57 }
58
59
put(char_type const & c)60 void otexstream::put(char_type const & c)
61 {
62 bool isprotected = false;
63 if (protectspace_) {
64 if (!canbreakline_ && c == ' ') {
65 os() << "{}";
66 isprotected = true;
67 }
68 protectspace_ = false;
69 }
70 if (terminate_command_) {
71 if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
72 // A space or line break follows. Terminate with brackets.
73 os() << "{}";
74 else if (c != '\\' && c != '{' && c != '}')
75 // Non-terminating character follows. Terminate with space.
76 os() << " ";
77 terminate_command_ = false;
78 }
79 otexrowstream::put(c);
80 lastChar(c);
81 }
82
83
length()84 size_t otexstringstream::length()
85 {
86 auto pos = ods_.tellp();
87 return (pos >= 0) ? size_t(pos) : 0;
88 }
89
90
release()91 TexString otexstringstream::release()
92 {
93 TexString ts(ods_.str(), move(texrow()));
94 // reset this
95 texrow() = TexRow();
96 ods_.clear();
97 ods_.str(docstring());
98 return ts;
99 }
100
101
102 BreakLine breakln;
103 SafeBreakLine safebreakln;
104 TerminateCommand termcmd;
105
106
operator <<(otexstream & ots,BreakLine)107 otexstream & operator<<(otexstream & ots, BreakLine)
108 {
109 if (ots.canBreakLine()) {
110 if (ots.terminateCommand())
111 ots << "{}";
112 ots.otexrowstream::put('\n');
113 ots.lastChar('\n');
114 }
115 ots.protectSpace(false);
116 ots.terminateCommand(false);
117 return ots;
118 }
119
120
operator <<(otexstream & ots,SafeBreakLine)121 otexstream & operator<<(otexstream & ots, SafeBreakLine)
122 {
123 otexrowstream & otrs = ots;
124 if (ots.canBreakLine()) {
125 if (ots.terminateCommand())
126 otrs << "{}";
127 otrs << "%\n";
128 ots.lastChar('\n');
129 }
130 ots.protectSpace(false);
131 ots.terminateCommand(false);
132 return ots;
133 }
134
135
operator <<(otexstream & ots,TerminateCommand)136 otexstream & operator<<(otexstream & ots, TerminateCommand)
137 {
138 ots.terminateCommand(true);
139 return ots;
140 }
141
142
operator <<(otexrowstream & ots,odocstream_manip pf)143 otexrowstream & operator<<(otexrowstream & ots, odocstream_manip pf)
144 {
145 ots.os() << pf;
146 if (pf == static_cast<odocstream_manip>(endl)) {
147 ots.texrow().newline();
148 }
149 return ots;
150 }
151
152
operator <<(otexstream & ots,odocstream_manip pf)153 otexstream & operator<<(otexstream & ots, odocstream_manip pf)
154 {
155 otexrowstream & otrs = ots;
156 otrs << pf;
157 if (pf == static_cast<odocstream_manip>(endl)) {
158 ots.lastChar('\n');
159 }
160 return ots;
161 }
162
163
operator <<(otexrowstream & ots,TexString ts)164 otexrowstream & operator<<(otexrowstream & ots, TexString ts)
165 {
166 ts.validate();
167 ots.os() << move(ts.str);
168 ots.texrow().append(move(ts.texrow));
169 return ots;
170 }
171
172
operator <<(otexstream & ots,TexString ts)173 otexstream & operator<<(otexstream & ots, TexString ts)
174 {
175 size_t const len = ts.str.length();
176 // Check whether there is something to output
177 if (len == 0)
178 return ots;
179
180 otexrowstream & otrs = ots;
181 bool isprotected = false;
182 char_type const c = ts.str[0];
183 if (ots.protectSpace()) {
184 if (!ots.canBreakLine() && c == ' ') {
185 otrs << "{}";
186 isprotected = true;
187 }
188 ots.protectSpace(false);
189 }
190 if (ots.terminateCommand()) {
191 if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
192 // A space or line break follows. Terminate with brackets.
193 otrs << "{}";
194 else if (c != '\\' && c != '{' && c != '}')
195 // Non-terminating character follows. Terminate with space.
196 otrs << " ";
197 ots.terminateCommand(false);
198 }
199
200 if (len > 1)
201 ots.canBreakLine(ts.str[len - 2] != '\n');
202 ots.lastChar(ts.str[len - 1]);
203
204 otrs << move(ts);
205 return ots;
206 }
207
208
operator <<(otexrowstream & ots,docstring const & s)209 otexrowstream & operator<<(otexrowstream & ots, docstring const & s)
210 {
211 ots.os() << s;
212 ots.texrow().newlines(count(s.begin(), s.end(), '\n'));
213 return ots;
214 }
215
216
operator <<(otexstream & ots,docstring const & s)217 otexstream & operator<<(otexstream & ots, docstring const & s)
218 {
219 size_t const len = s.length();
220
221 // Check whether there's something to output
222 if (len == 0)
223 return ots;
224 otexrowstream & otrs = ots;
225 bool isprotected = false;
226 char_type const c = s[0];
227 if (ots.protectSpace()) {
228 if (!ots.canBreakLine() && c == ' ') {
229 otrs << "{}";
230 isprotected = true;
231 }
232 ots.protectSpace(false);
233 }
234 if (ots.terminateCommand()) {
235 if ((c == ' ' || c == '\0' || c == '\n') && !isprotected)
236 // A space or line break follows. Terminate with brackets.
237 otrs << "{}";
238 else if (c != '\\' && c != '{' && c != '}')
239 // Non-terminating character follows. Terminate with space.
240 otrs << " ";
241 ots.terminateCommand(false);
242 }
243
244 if (contains(s, 0xF0000)) {
245 // Some encoding changes for the underlying stream are embedded
246 // in the docstring. The encoding names to be used are enclosed
247 // between the code points 0xF0000 and 0xF0001, the first two
248 // characters of plane 15, which is a Private Use Area whose
249 // codepoints don't have any associated glyph.
250 docstring s1;
251 docstring s2 = split(s, s1, 0xF0000);
252 while (true) {
253 if (!s1.empty())
254 otrs << s1;
255 if (s2.empty())
256 break;
257 docstring enc;
258 docstring const s3 = split(s2, enc, 0xF0001);
259 if (!contains(s2, 0xF0001))
260 s2 = split(enc, s1, 0xF0000);
261 else {
262 otrs << setEncoding(to_ascii(enc));
263 s2 = split(s3, s1, 0xF0000);
264 }
265 }
266 } else
267 otrs << s;
268
269 if (len > 1)
270 ots.canBreakLine(s[len - 2] != '\n');
271 ots.lastChar(s[len - 1]);
272 return ots;
273 }
274
275
operator <<(otexrowstream & ots,string const & s)276 otexrowstream & operator<<(otexrowstream & ots, string const & s)
277 {
278 ots << from_utf8(s);
279 return ots;
280 }
281
282
operator <<(otexstream & ots,string const & s)283 otexstream & operator<<(otexstream & ots, string const & s)
284 {
285 ots << from_utf8(s);
286 return ots;
287 }
288
289
operator <<(otexrowstream & ots,char const * s)290 otexrowstream & operator<<(otexrowstream & ots, char const * s)
291 {
292 ots << from_utf8(s);
293 return ots;
294 }
295
296
operator <<(otexstream & ots,char const * s)297 otexstream & operator<<(otexstream & ots, char const * s)
298 {
299 ots << from_utf8(s);
300 return ots;
301 }
302
303
operator <<(otexrowstream & ots,char c)304 otexrowstream & operator<<(otexrowstream & ots, char c)
305 {
306 ots.put(c);
307 return ots;
308 }
309
310
operator <<(otexstream & ots,char c)311 otexstream & operator<<(otexstream & ots, char c)
312 {
313 ots.put(c);
314 return ots;
315 }
316
317
318 template <typename Type>
operator <<(otexrowstream & ots,Type value)319 otexrowstream & operator<<(otexrowstream & ots, Type value)
320 {
321 ots.os() << value;
322 return ots;
323 }
324
325 template otexrowstream & operator<< <SetEnc>(otexrowstream & os, SetEnc);
326 template otexrowstream & operator<< <double>(otexrowstream &, double);
327 template otexrowstream & operator<< <int>(otexrowstream &, int);
328 template otexrowstream & operator<< <unsigned int>(otexrowstream &,
329 unsigned int);
330 template otexrowstream & operator<< <unsigned long>(otexrowstream &,
331 unsigned long);
332
333 #ifdef LYX_USE_LONG_LONG
334 template otexrowstream & operator<< <unsigned long long>(otexrowstream &,
335 unsigned long long);
336 #endif
337
338
339 template <typename Type>
340 otexstream & operator<<(otexstream & ots, Type value)
341 {
342 ots.os() << value;
343 ots.lastChar(0);
344 ots.protectSpace(false);
345 ots.terminateCommand(false);
346 return ots;
347 }
348
349 template otexstream & operator<< <SetEnc>(otexstream & os, SetEnc);
350 template otexstream & operator<< <double>(otexstream &, double);
351 template otexstream & operator<< <int>(otexstream &, int);
352 template otexstream & operator<< <unsigned int>(otexstream &, unsigned int);
353 template otexstream & operator<< <unsigned long>(otexstream &, unsigned long);
354 #ifdef LYX_USE_LONG_LONG
355 template otexstream & operator<< <unsigned long long>(otexstream &, unsigned long long);
356 #endif
357
358 } // namespace lyx
359