1 /*
2 * RichEdit - RTF writer module
3 *
4 * Copyright 2005 by Phil Krylov
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 */
20
21 #define NONAMELESSUNION
22
23 #include "editor.h"
24 #include "rtf.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
27
28 #define STREAMOUT_BUFFER_SIZE 4096
29 #define STREAMOUT_FONTTBL_SIZE 8192
30 #define STREAMOUT_COLORTBL_SIZE 1024
31
32 typedef struct tagME_OutStream
33 {
34 EDITSTREAM *stream;
35 char buffer[STREAMOUT_BUFFER_SIZE];
36 UINT pos, written;
37 UINT nCodePage;
38 UINT nFontTblLen;
39 ME_FontTableItem fonttbl[STREAMOUT_FONTTBL_SIZE];
40 UINT nColorTblLen;
41 COLORREF colortbl[STREAMOUT_COLORTBL_SIZE];
42 UINT nDefaultFont;
43 UINT nDefaultCodePage;
44 /* nNestingLevel = 0 means we aren't in a cell, 1 means we are in a cell,
45 * an greater numbers mean we are in a cell nested within a cell. */
46 UINT nNestingLevel;
47 CHARFORMAT2W cur_fmt; /* current character format */
48 } ME_OutStream;
49
50 static BOOL
51 ME_StreamOutRTFText(ME_OutStream *pStream, const WCHAR *text, LONG nChars);
52
53
54 static ME_OutStream*
ME_StreamOutInit(ME_TextEditor * editor,EDITSTREAM * stream)55 ME_StreamOutInit(ME_TextEditor *editor, EDITSTREAM *stream)
56 {
57 ME_OutStream *pStream = heap_alloc_zero(sizeof(*pStream));
58
59 pStream->stream = stream;
60 pStream->stream->dwError = 0;
61 pStream->nColorTblLen = 1;
62 pStream->cur_fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
63 pStream->cur_fmt.bUnderlineType = CFU_UNDERLINE;
64 return pStream;
65 }
66
67
68 static BOOL
ME_StreamOutFlush(ME_OutStream * pStream)69 ME_StreamOutFlush(ME_OutStream *pStream)
70 {
71 LONG nWritten = 0;
72 EDITSTREAM *stream = pStream->stream;
73
74 if (pStream->pos) {
75 TRACE("sending %u bytes\n", pStream->pos);
76 nWritten = pStream->pos;
77 stream->dwError = stream->pfnCallback(stream->dwCookie, (LPBYTE)pStream->buffer,
78 pStream->pos, &nWritten);
79 TRACE("error=%u written=%u\n", stream->dwError, nWritten);
80 if (nWritten == 0 || stream->dwError)
81 return FALSE;
82 /* Don't resend partial chunks if nWritten < pStream->pos */
83 }
84 if (nWritten == pStream->pos)
85 pStream->written += nWritten;
86 pStream->pos = 0;
87 return TRUE;
88 }
89
90
91 static LONG
ME_StreamOutFree(ME_OutStream * pStream)92 ME_StreamOutFree(ME_OutStream *pStream)
93 {
94 LONG written = pStream->written;
95 TRACE("total length = %u\n", written);
96
97 heap_free(pStream);
98 return written;
99 }
100
101
102 static BOOL
ME_StreamOutMove(ME_OutStream * pStream,const char * buffer,int len)103 ME_StreamOutMove(ME_OutStream *pStream, const char *buffer, int len)
104 {
105 while (len) {
106 int space = STREAMOUT_BUFFER_SIZE - pStream->pos;
107 int fit = min(space, len);
108
109 TRACE("%u:%u:%s\n", pStream->pos, fit, debugstr_an(buffer,fit));
110 memmove(pStream->buffer + pStream->pos, buffer, fit);
111 len -= fit;
112 buffer += fit;
113 pStream->pos += fit;
114 if (pStream->pos == STREAMOUT_BUFFER_SIZE) {
115 if (!ME_StreamOutFlush(pStream))
116 return FALSE;
117 }
118 }
119 return TRUE;
120 }
121
122
123 static BOOL WINAPIV
ME_StreamOutPrint(ME_OutStream * pStream,const char * format,...)124 ME_StreamOutPrint(ME_OutStream *pStream, const char *format, ...)
125 {
126 char string[STREAMOUT_BUFFER_SIZE]; /* This is going to be enough */
127 int len;
128 __ms_va_list valist;
129
130 __ms_va_start(valist, format);
131 len = vsnprintf(string, sizeof(string), format, valist);
132 __ms_va_end(valist);
133
134 return ME_StreamOutMove(pStream, string, len);
135 }
136
137 #define HEX_BYTES_PER_LINE 40
138
139 static BOOL
ME_StreamOutHexData(ME_OutStream * stream,const BYTE * data,UINT len)140 ME_StreamOutHexData(ME_OutStream *stream, const BYTE *data, UINT len)
141 {
142
143 char line[HEX_BYTES_PER_LINE * 2 + 1];
144 UINT size, i;
145 static const char hex[] = "0123456789abcdef";
146
147 while (len)
148 {
149 size = min( len, HEX_BYTES_PER_LINE );
150 for (i = 0; i < size; i++)
151 {
152 line[i * 2] = hex[(*data >> 4) & 0xf];
153 line[i * 2 + 1] = hex[*data & 0xf];
154 data++;
155 }
156 line[size * 2] = '\n';
157 if (!ME_StreamOutMove( stream, line, size * 2 + 1 ))
158 return FALSE;
159 len -= size;
160 }
161 return TRUE;
162 }
163
164 static BOOL
ME_StreamOutRTFHeader(ME_OutStream * pStream,int dwFormat)165 ME_StreamOutRTFHeader(ME_OutStream *pStream, int dwFormat)
166 {
167 const char *cCharSet = NULL;
168 UINT nCodePage;
169 LANGID language;
170 BOOL success;
171
172 if (dwFormat & SF_USECODEPAGE) {
173 CPINFOEXW info;
174
175 switch (HIWORD(dwFormat)) {
176 case CP_ACP:
177 cCharSet = "ansi";
178 nCodePage = GetACP();
179 break;
180 case CP_OEMCP:
181 nCodePage = GetOEMCP();
182 if (nCodePage == 437)
183 cCharSet = "pc";
184 else if (nCodePage == 850)
185 cCharSet = "pca";
186 else
187 cCharSet = "ansi";
188 break;
189 case CP_UTF8:
190 nCodePage = CP_UTF8;
191 break;
192 default:
193 if (HIWORD(dwFormat) == CP_MACCP) {
194 cCharSet = "mac";
195 nCodePage = 10000; /* MacRoman */
196 } else {
197 cCharSet = "ansi";
198 nCodePage = 1252; /* Latin-1 */
199 }
200 if (GetCPInfoExW(HIWORD(dwFormat), 0, &info))
201 nCodePage = info.CodePage;
202 }
203 } else {
204 cCharSet = "ansi";
205 /* TODO: If the original document contained an \ansicpg value, retain it.
206 * Otherwise, M$ richedit emits a codepage number determined from the
207 * charset of the default font here. Anyway, this value is not used by
208 * the reader... */
209 nCodePage = GetACP();
210 }
211 if (nCodePage == CP_UTF8)
212 success = ME_StreamOutPrint(pStream, "{\\urtf");
213 else
214 success = ME_StreamOutPrint(pStream, "{\\rtf1\\%s\\ansicpg%u\\uc1", cCharSet, nCodePage);
215
216 if (!success)
217 return FALSE;
218
219 pStream->nDefaultCodePage = nCodePage;
220
221 /* FIXME: This should be a document property */
222 /* TODO: handle SFF_PLAINRTF */
223 language = GetUserDefaultLangID();
224 if (!ME_StreamOutPrint(pStream, "\\deff0\\deflang%u\\deflangfe%u", language, language))
225 return FALSE;
226
227 /* FIXME: This should be a document property */
228 pStream->nDefaultFont = 0;
229
230 return TRUE;
231 }
232
add_font_to_fonttbl(ME_OutStream * stream,ME_Style * style)233 static void add_font_to_fonttbl( ME_OutStream *stream, ME_Style *style )
234 {
235 ME_FontTableItem *table = stream->fonttbl;
236 CHARFORMAT2W *fmt = &style->fmt;
237 WCHAR *face = fmt->szFaceName;
238 BYTE charset = (fmt->dwMask & CFM_CHARSET) ? fmt->bCharSet : DEFAULT_CHARSET;
239 int i;
240
241 if (fmt->dwMask & CFM_FACE)
242 {
243 for (i = 0; i < stream->nFontTblLen; i++)
244 if (table[i].bCharSet == charset
245 && (table[i].szFaceName == face || !wcscmp(table[i].szFaceName, face)))
246 break;
247
248 if (i == stream->nFontTblLen && i < STREAMOUT_FONTTBL_SIZE)
249 {
250 table[i].bCharSet = charset;
251 table[i].szFaceName = face;
252 stream->nFontTblLen++;
253 }
254 }
255 }
256
find_font_in_fonttbl(ME_OutStream * stream,CHARFORMAT2W * fmt,unsigned int * idx)257 static BOOL find_font_in_fonttbl( ME_OutStream *stream, CHARFORMAT2W *fmt, unsigned int *idx )
258 {
259 WCHAR *facename;
260 int i;
261
262 *idx = 0;
263 if (fmt->dwMask & CFM_FACE)
264 facename = fmt->szFaceName;
265 else
266 facename = stream->fonttbl[0].szFaceName;
267 for (i = 0; i < stream->nFontTblLen; i++)
268 {
269 if (facename == stream->fonttbl[i].szFaceName
270 || !wcscmp(facename, stream->fonttbl[i].szFaceName))
271 if (!(fmt->dwMask & CFM_CHARSET)
272 || fmt->bCharSet == stream->fonttbl[i].bCharSet)
273 {
274 *idx = i;
275 break;
276 }
277 }
278
279 return i < stream->nFontTblLen;
280 }
281
add_color_to_colortbl(ME_OutStream * stream,COLORREF color)282 static void add_color_to_colortbl( ME_OutStream *stream, COLORREF color )
283 {
284 int i;
285
286 for (i = 1; i < stream->nColorTblLen; i++)
287 if (stream->colortbl[i] == color)
288 break;
289
290 if (i == stream->nColorTblLen && i < STREAMOUT_COLORTBL_SIZE)
291 {
292 stream->colortbl[i] = color;
293 stream->nColorTblLen++;
294 }
295 }
296
find_color_in_colortbl(ME_OutStream * stream,COLORREF color,unsigned int * idx)297 static BOOL find_color_in_colortbl( ME_OutStream *stream, COLORREF color, unsigned int *idx )
298 {
299 int i;
300
301 *idx = 0;
302 for (i = 1; i < stream->nColorTblLen; i++)
303 {
304 if (stream->colortbl[i] == color)
305 {
306 *idx = i;
307 break;
308 }
309 }
310
311 return i < stream->nFontTblLen;
312 }
313
314 static BOOL
ME_StreamOutRTFFontAndColorTbl(ME_OutStream * pStream,ME_DisplayItem * pFirstRun,ME_DisplayItem * pLastRun)315 ME_StreamOutRTFFontAndColorTbl(ME_OutStream *pStream, ME_DisplayItem *pFirstRun,
316 ME_DisplayItem *pLastRun)
317 {
318 ME_DisplayItem *item = pFirstRun;
319 ME_FontTableItem *table = pStream->fonttbl;
320 unsigned int i;
321 ME_DisplayItem *pCell = NULL;
322 ME_Paragraph *prev_para = NULL;
323
324 do {
325 CHARFORMAT2W *fmt = &item->member.run.style->fmt;
326
327 add_font_to_fonttbl( pStream, item->member.run.style );
328
329 if (fmt->dwMask & CFM_COLOR && !(fmt->dwEffects & CFE_AUTOCOLOR))
330 add_color_to_colortbl( pStream, fmt->crTextColor );
331 if (fmt->dwMask & CFM_BACKCOLOR && !(fmt->dwEffects & CFE_AUTOBACKCOLOR))
332 add_color_to_colortbl( pStream, fmt->crBackColor );
333
334 if (item->member.run.para != prev_para)
335 {
336 /* check for any para numbering text */
337 if (item->member.run.para->fmt.wNumbering)
338 add_font_to_fonttbl( pStream, item->member.run.para->para_num.style );
339
340 if ((pCell = item->member.para.pCell))
341 {
342 ME_Border* borders[4] = { &pCell->member.cell.border.top,
343 &pCell->member.cell.border.left,
344 &pCell->member.cell.border.bottom,
345 &pCell->member.cell.border.right };
346 for (i = 0; i < 4; i++)
347 if (borders[i]->width > 0)
348 add_color_to_colortbl( pStream, borders[i]->colorRef );
349 }
350
351 prev_para = item->member.run.para;
352 }
353
354 if (item == pLastRun)
355 break;
356 item = ME_FindItemFwd(item, diRun);
357 } while (item);
358
359 if (!ME_StreamOutPrint(pStream, "{\\fonttbl"))
360 return FALSE;
361
362 for (i = 0; i < pStream->nFontTblLen; i++) {
363 if (table[i].bCharSet != DEFAULT_CHARSET) {
364 if (!ME_StreamOutPrint(pStream, "{\\f%u\\fcharset%u ", i, table[i].bCharSet))
365 return FALSE;
366 } else {
367 if (!ME_StreamOutPrint(pStream, "{\\f%u ", i))
368 return FALSE;
369 }
370 if (!ME_StreamOutRTFText(pStream, table[i].szFaceName, -1))
371 return FALSE;
372 if (!ME_StreamOutPrint(pStream, ";}"))
373 return FALSE;
374 }
375 if (!ME_StreamOutPrint(pStream, "}\r\n"))
376 return FALSE;
377
378 /* Output the color table */
379 if (!ME_StreamOutPrint(pStream, "{\\colortbl;")) return FALSE; /* first entry is auto-color */
380 for (i = 1; i < pStream->nColorTblLen; i++)
381 {
382 if (!ME_StreamOutPrint(pStream, "\\red%u\\green%u\\blue%u;", pStream->colortbl[i] & 0xFF,
383 (pStream->colortbl[i] >> 8) & 0xFF, (pStream->colortbl[i] >> 16) & 0xFF))
384 return FALSE;
385 }
386 if (!ME_StreamOutPrint(pStream, "}\r\n")) return FALSE;
387
388 return TRUE;
389 }
390
391 static BOOL
ME_StreamOutRTFTableProps(ME_TextEditor * editor,ME_OutStream * pStream,ME_DisplayItem * para)392 ME_StreamOutRTFTableProps(ME_TextEditor *editor, ME_OutStream *pStream,
393 ME_DisplayItem *para)
394 {
395 ME_DisplayItem *cell;
396 char props[STREAMOUT_BUFFER_SIZE] = "";
397 int i;
398 const char sideChar[4] = {'t','l','b','r'};
399
400 if (!ME_StreamOutPrint(pStream, "\\trowd"))
401 return FALSE;
402 if (!editor->bEmulateVersion10) { /* v4.1 */
403 PARAFORMAT2 *pFmt = &ME_GetTableRowEnd(para)->member.para.fmt;
404 para = ME_GetTableRowStart(para);
405 cell = para->member.para.next_para->member.para.pCell;
406 assert(cell);
407 if (pFmt->dxOffset)
408 sprintf(props + strlen(props), "\\trgaph%d", pFmt->dxOffset);
409 if (pFmt->dxStartIndent)
410 sprintf(props + strlen(props), "\\trleft%d", pFmt->dxStartIndent);
411 do {
412 ME_Border* borders[4] = { &cell->member.cell.border.top,
413 &cell->member.cell.border.left,
414 &cell->member.cell.border.bottom,
415 &cell->member.cell.border.right };
416 for (i = 0; i < 4; i++)
417 {
418 if (borders[i]->width)
419 {
420 unsigned int idx;
421 COLORREF crColor = borders[i]->colorRef;
422 sprintf(props + strlen(props), "\\clbrdr%c", sideChar[i]);
423 sprintf(props + strlen(props), "\\brdrs");
424 sprintf(props + strlen(props), "\\brdrw%d", borders[i]->width);
425 if (find_color_in_colortbl( pStream, crColor, &idx ))
426 sprintf(props + strlen(props), "\\brdrcf%u", idx);
427 }
428 }
429 sprintf(props + strlen(props), "\\cellx%d", cell->member.cell.nRightBoundary);
430 cell = cell->member.cell.next_cell;
431 } while (cell->member.cell.next_cell);
432 } else { /* v1.0 - 3.0 */
433 const ME_Border* borders[4] = { ¶->member.para.border.top,
434 ¶->member.para.border.left,
435 ¶->member.para.border.bottom,
436 ¶->member.para.border.right };
437 PARAFORMAT2 *pFmt = ¶->member.para.fmt;
438
439 assert(!(para->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL)));
440 if (pFmt->dxOffset)
441 sprintf(props + strlen(props), "\\trgaph%d", pFmt->dxOffset);
442 if (pFmt->dxStartIndent)
443 sprintf(props + strlen(props), "\\trleft%d", pFmt->dxStartIndent);
444 for (i = 0; i < 4; i++)
445 {
446 if (borders[i]->width)
447 {
448 unsigned int idx;
449 COLORREF crColor = borders[i]->colorRef;
450 sprintf(props + strlen(props), "\\trbrdr%c", sideChar[i]);
451 sprintf(props + strlen(props), "\\brdrs");
452 sprintf(props + strlen(props), "\\brdrw%d", borders[i]->width);
453 if (find_color_in_colortbl( pStream, crColor, &idx ))
454 sprintf(props + strlen(props), "\\brdrcf%u", idx);
455 }
456 }
457 for (i = 0; i < pFmt->cTabCount; i++)
458 {
459 sprintf(props + strlen(props), "\\cellx%d", pFmt->rgxTabs[i] & 0x00FFFFFF);
460 }
461 }
462 if (!ME_StreamOutPrint(pStream, props))
463 return FALSE;
464 props[0] = '\0';
465 return TRUE;
466 }
467
stream_out_para_num(ME_OutStream * stream,ME_Paragraph * para,BOOL pn_dest)468 static BOOL stream_out_para_num( ME_OutStream *stream, ME_Paragraph *para, BOOL pn_dest )
469 {
470 static const char fmt_label[] = "{\\*\\pn\\pnlvlbody\\pnf%u\\pnindent%d\\pnstart%d%s%s}";
471 static const char fmt_bullet[] = "{\\*\\pn\\pnlvlblt\\pnf%u\\pnindent%d{\\pntxtb\\'b7}}";
472 static const char dec[] = "\\pndec";
473 static const char lcltr[] = "\\pnlcltr";
474 static const char ucltr[] = "\\pnucltr";
475 static const char lcrm[] = "\\pnlcrm";
476 static const char ucrm[] = "\\pnucrm";
477 static const char period[] = "{\\pntxta.}";
478 static const char paren[] = "{\\pntxta)}";
479 static const char parens[] = "{\\pntxtb(}{\\pntxta)}";
480 const char *type, *style = "";
481 unsigned int idx;
482
483 find_font_in_fonttbl( stream, ¶->para_num.style->fmt, &idx );
484
485 if (!ME_StreamOutPrint( stream, "{\\pntext\\f%u ", idx )) return FALSE;
486 if (!ME_StreamOutRTFText( stream, para->para_num.text->szData, para->para_num.text->nLen ))
487 return FALSE;
488 if (!ME_StreamOutPrint( stream, "\\tab}" )) return FALSE;
489
490 if (!pn_dest) return TRUE;
491
492 if (para->fmt.wNumbering == PFN_BULLET)
493 {
494 if (!ME_StreamOutPrint( stream, fmt_bullet, idx, para->fmt.wNumberingTab ))
495 return FALSE;
496 }
497 else
498 {
499 switch (para->fmt.wNumbering)
500 {
501 case PFN_ARABIC:
502 default:
503 type = dec;
504 break;
505 case PFN_LCLETTER:
506 type = lcltr;
507 break;
508 case PFN_UCLETTER:
509 type = ucltr;
510 break;
511 case PFN_LCROMAN:
512 type = lcrm;
513 break;
514 case PFN_UCROMAN:
515 type = ucrm;
516 break;
517 }
518 switch (para->fmt.wNumberingStyle & 0xf00)
519 {
520 case PFNS_PERIOD:
521 style = period;
522 break;
523 case PFNS_PAREN:
524 style = paren;
525 break;
526 case PFNS_PARENS:
527 style = parens;
528 break;
529 }
530
531 if (!ME_StreamOutPrint( stream, fmt_label, idx, para->fmt.wNumberingTab,
532 para->fmt.wNumberingStart, type, style ))
533 return FALSE;
534 }
535 return TRUE;
536 }
537
538 static BOOL
ME_StreamOutRTFParaProps(ME_TextEditor * editor,ME_OutStream * pStream,ME_DisplayItem * para)539 ME_StreamOutRTFParaProps(ME_TextEditor *editor, ME_OutStream *pStream,
540 ME_DisplayItem *para)
541 {
542 PARAFORMAT2 *fmt = ¶->member.para.fmt;
543 char props[STREAMOUT_BUFFER_SIZE] = "";
544 int i;
545 ME_Paragraph *prev_para = NULL;
546
547 if (para->member.para.prev_para->type == diParagraph)
548 prev_para = ¶->member.para.prev_para->member.para;
549
550 if (!editor->bEmulateVersion10) { /* v4.1 */
551 if (para->member.para.nFlags & MEPF_ROWSTART) {
552 pStream->nNestingLevel++;
553 if (pStream->nNestingLevel == 1) {
554 if (!ME_StreamOutRTFTableProps(editor, pStream, para))
555 return FALSE;
556 }
557 return TRUE;
558 } else if (para->member.para.nFlags & MEPF_ROWEND) {
559 pStream->nNestingLevel--;
560 if (pStream->nNestingLevel >= 1) {
561 if (!ME_StreamOutPrint(pStream, "{\\*\\nesttableprops"))
562 return FALSE;
563 if (!ME_StreamOutRTFTableProps(editor, pStream, para))
564 return FALSE;
565 if (!ME_StreamOutPrint(pStream, "\\nestrow}{\\nonesttables\\par}\r\n"))
566 return FALSE;
567 } else {
568 if (!ME_StreamOutPrint(pStream, "\\row\r\n"))
569 return FALSE;
570 }
571 return TRUE;
572 }
573 } else { /* v1.0 - 3.0 */
574 if (para->member.para.fmt.dwMask & PFM_TABLE &&
575 para->member.para.fmt.wEffects & PFE_TABLE)
576 {
577 if (!ME_StreamOutRTFTableProps(editor, pStream, para))
578 return FALSE;
579 }
580 }
581
582 if (prev_para && !memcmp( fmt, &prev_para->fmt, sizeof(*fmt) ))
583 {
584 if (fmt->wNumbering)
585 return stream_out_para_num( pStream, ¶->member.para, FALSE );
586 return TRUE;
587 }
588
589 if (!ME_StreamOutPrint(pStream, "\\pard"))
590 return FALSE;
591
592 if (fmt->wNumbering)
593 if (!stream_out_para_num( pStream, ¶->member.para, TRUE )) return FALSE;
594
595 if (!editor->bEmulateVersion10) { /* v4.1 */
596 if (pStream->nNestingLevel > 0)
597 strcat(props, "\\intbl");
598 if (pStream->nNestingLevel > 1)
599 sprintf(props + strlen(props), "\\itap%d", pStream->nNestingLevel);
600 } else { /* v1.0 - 3.0 */
601 if (fmt->dwMask & PFM_TABLE && fmt->wEffects & PFE_TABLE)
602 strcat(props, "\\intbl");
603 }
604
605 /* TODO: PFM_BORDER. M$ does not emit any keywords for these properties, and
606 * when streaming border keywords in, PFM_BORDER is set, but wBorder field is
607 * set very different from the documentation.
608 * (Tested with RichEdit 5.50.25.0601) */
609
610 if (fmt->dwMask & PFM_ALIGNMENT) {
611 switch (fmt->wAlignment) {
612 case PFA_LEFT:
613 /* Default alignment: not emitted */
614 break;
615 case PFA_RIGHT:
616 strcat(props, "\\qr");
617 break;
618 case PFA_CENTER:
619 strcat(props, "\\qc");
620 break;
621 case PFA_JUSTIFY:
622 strcat(props, "\\qj");
623 break;
624 }
625 }
626
627 if (fmt->dwMask & PFM_LINESPACING) {
628 /* FIXME: MSDN says that the bLineSpacingRule field is controlled by the
629 * PFM_SPACEAFTER flag. Is that true? I don't believe so. */
630 switch (fmt->bLineSpacingRule) {
631 case 0: /* Single spacing */
632 strcat(props, "\\sl-240\\slmult1");
633 break;
634 case 1: /* 1.5 spacing */
635 strcat(props, "\\sl-360\\slmult1");
636 break;
637 case 2: /* Double spacing */
638 strcat(props, "\\sl-480\\slmult1");
639 break;
640 case 3:
641 sprintf(props + strlen(props), "\\sl%d\\slmult0", fmt->dyLineSpacing);
642 break;
643 case 4:
644 sprintf(props + strlen(props), "\\sl-%d\\slmult0", fmt->dyLineSpacing);
645 break;
646 case 5:
647 sprintf(props + strlen(props), "\\sl-%d\\slmult1", fmt->dyLineSpacing * 240 / 20);
648 break;
649 }
650 }
651
652 if (fmt->dwMask & PFM_DONOTHYPHEN && fmt->wEffects & PFE_DONOTHYPHEN)
653 strcat(props, "\\hyph0");
654 if (fmt->dwMask & PFM_KEEP && fmt->wEffects & PFE_KEEP)
655 strcat(props, "\\keep");
656 if (fmt->dwMask & PFM_KEEPNEXT && fmt->wEffects & PFE_KEEPNEXT)
657 strcat(props, "\\keepn");
658 if (fmt->dwMask & PFM_NOLINENUMBER && fmt->wEffects & PFE_NOLINENUMBER)
659 strcat(props, "\\noline");
660 if (fmt->dwMask & PFM_NOWIDOWCONTROL && fmt->wEffects & PFE_NOWIDOWCONTROL)
661 strcat(props, "\\nowidctlpar");
662 if (fmt->dwMask & PFM_PAGEBREAKBEFORE && fmt->wEffects & PFE_PAGEBREAKBEFORE)
663 strcat(props, "\\pagebb");
664 if (fmt->dwMask & PFM_RTLPARA && fmt->wEffects & PFE_RTLPARA)
665 strcat(props, "\\rtlpar");
666 if (fmt->dwMask & PFM_SIDEBYSIDE && fmt->wEffects & PFE_SIDEBYSIDE)
667 strcat(props, "\\sbys");
668
669 if (!(editor->bEmulateVersion10 && /* v1.0 - 3.0 */
670 fmt->dwMask & PFM_TABLE && fmt->wEffects & PFE_TABLE))
671 {
672 if (fmt->dxOffset)
673 sprintf(props + strlen(props), "\\li%d", fmt->dxOffset);
674 if (fmt->dxStartIndent)
675 sprintf(props + strlen(props), "\\fi%d", fmt->dxStartIndent);
676 if (fmt->dxRightIndent)
677 sprintf(props + strlen(props), "\\ri%d", fmt->dxRightIndent);
678 if (fmt->dwMask & PFM_TABSTOPS) {
679 static const char * const leader[6] = { "", "\\tldot", "\\tlhyph", "\\tlul", "\\tlth", "\\tleq" };
680
681 for (i = 0; i < fmt->cTabCount; i++) {
682 switch ((fmt->rgxTabs[i] >> 24) & 0xF) {
683 case 1:
684 strcat(props, "\\tqc");
685 break;
686 case 2:
687 strcat(props, "\\tqr");
688 break;
689 case 3:
690 strcat(props, "\\tqdec");
691 break;
692 case 4:
693 /* Word bar tab (vertical bar). Handled below */
694 break;
695 }
696 if (fmt->rgxTabs[i] >> 28 <= 5)
697 strcat(props, leader[fmt->rgxTabs[i] >> 28]);
698 sprintf(props+strlen(props), "\\tx%d", fmt->rgxTabs[i]&0x00FFFFFF);
699 }
700 }
701 }
702 if (fmt->dySpaceAfter)
703 sprintf(props + strlen(props), "\\sa%d", fmt->dySpaceAfter);
704 if (fmt->dySpaceBefore)
705 sprintf(props + strlen(props), "\\sb%d", fmt->dySpaceBefore);
706 if (fmt->sStyle != -1)
707 sprintf(props + strlen(props), "\\s%d", fmt->sStyle);
708
709 if (fmt->dwMask & PFM_SHADING) {
710 static const char * const style[16] = { "", "\\bgdkhoriz", "\\bgdkvert", "\\bgdkfdiag",
711 "\\bgdkbdiag", "\\bgdkcross", "\\bgdkdcross",
712 "\\bghoriz", "\\bgvert", "\\bgfdiag",
713 "\\bgbdiag", "\\bgcross", "\\bgdcross",
714 "", "", "" };
715 if (fmt->wShadingWeight)
716 sprintf(props + strlen(props), "\\shading%d", fmt->wShadingWeight);
717 if (fmt->wShadingStyle & 0xF)
718 strcat(props, style[fmt->wShadingStyle & 0xF]);
719 if ((fmt->wShadingStyle >> 4) & 0xf)
720 sprintf(props + strlen(props), "\\cfpat%d", (fmt->wShadingStyle >> 4) & 0xf);
721 if ((fmt->wShadingStyle >> 8) & 0xf)
722 sprintf(props + strlen(props), "\\cbpat%d", (fmt->wShadingStyle >> 8) & 0xf);
723 }
724 if (*props)
725 strcat(props, " ");
726
727 if (*props && !ME_StreamOutPrint(pStream, props))
728 return FALSE;
729
730 return TRUE;
731 }
732
733
734 static BOOL
ME_StreamOutRTFCharProps(ME_OutStream * pStream,CHARFORMAT2W * fmt)735 ME_StreamOutRTFCharProps(ME_OutStream *pStream, CHARFORMAT2W *fmt)
736 {
737 char props[STREAMOUT_BUFFER_SIZE] = "";
738 unsigned int i;
739 CHARFORMAT2W *old_fmt = &pStream->cur_fmt;
740 static const struct
741 {
742 DWORD effect;
743 const char *on, *off;
744 } effects[] =
745 {
746 { CFE_ALLCAPS, "\\caps", "\\caps0" },
747 { CFE_BOLD, "\\b", "\\b0" },
748 { CFE_DISABLED, "\\disabled", "\\disabled0" },
749 { CFE_EMBOSS, "\\embo", "\\embo0" },
750 { CFE_HIDDEN, "\\v", "\\v0" },
751 { CFE_IMPRINT, "\\impr", "\\impr0" },
752 { CFE_ITALIC, "\\i", "\\i0" },
753 { CFE_OUTLINE, "\\outl", "\\outl0" },
754 { CFE_PROTECTED, "\\protect", "\\protect0" },
755 { CFE_SHADOW, "\\shad", "\\shad0" },
756 { CFE_SMALLCAPS, "\\scaps", "\\scaps0" },
757 { CFE_STRIKEOUT, "\\strike", "\\strike0" },
758 };
759
760 for (i = 0; i < ARRAY_SIZE( effects ); i++)
761 {
762 if ((old_fmt->dwEffects ^ fmt->dwEffects) & effects[i].effect)
763 strcat( props, fmt->dwEffects & effects[i].effect ? effects[i].on : effects[i].off );
764 }
765
766 if ((old_fmt->dwEffects ^ fmt->dwEffects) & CFE_AUTOBACKCOLOR ||
767 (!(fmt->dwEffects & CFE_AUTOBACKCOLOR) && old_fmt->crBackColor != fmt->crBackColor))
768 {
769 if (fmt->dwEffects & CFE_AUTOBACKCOLOR) i = 0;
770 else find_color_in_colortbl( pStream, fmt->crBackColor, &i );
771 sprintf(props + strlen(props), "\\highlight%u", i);
772 }
773 if ((old_fmt->dwEffects ^ fmt->dwEffects) & CFE_AUTOCOLOR ||
774 (!(fmt->dwEffects & CFE_AUTOCOLOR) && old_fmt->crTextColor != fmt->crTextColor))
775 {
776 if (fmt->dwEffects & CFE_AUTOCOLOR) i = 0;
777 else find_color_in_colortbl( pStream, fmt->crTextColor, &i );
778 sprintf(props + strlen(props), "\\cf%u", i);
779 }
780
781 if (old_fmt->bAnimation != fmt->bAnimation)
782 sprintf(props + strlen(props), "\\animtext%u", fmt->bAnimation);
783 if (old_fmt->wKerning != fmt->wKerning)
784 sprintf(props + strlen(props), "\\kerning%u", fmt->wKerning);
785
786 if (old_fmt->lcid != fmt->lcid)
787 {
788 /* TODO: handle SFF_PLAINRTF */
789 if (LOWORD(fmt->lcid) == 1024)
790 strcat(props, "\\noproof\\lang1024\\langnp1024\\langfe1024\\langfenp1024");
791 else
792 sprintf(props + strlen(props), "\\lang%u", LOWORD(fmt->lcid));
793 }
794
795 if (old_fmt->yOffset != fmt->yOffset)
796 {
797 if (fmt->yOffset >= 0)
798 sprintf(props + strlen(props), "\\up%d", fmt->yOffset);
799 else
800 sprintf(props + strlen(props), "\\dn%d", -fmt->yOffset);
801 }
802 if (old_fmt->yHeight != fmt->yHeight)
803 sprintf(props + strlen(props), "\\fs%d", fmt->yHeight / 10);
804 if (old_fmt->sSpacing != fmt->sSpacing)
805 sprintf(props + strlen(props), "\\expnd%u\\expndtw%u", fmt->sSpacing / 5, fmt->sSpacing);
806 if ((old_fmt->dwEffects ^ fmt->dwEffects) & (CFM_SUBSCRIPT | CFM_SUPERSCRIPT))
807 {
808 if (fmt->dwEffects & CFE_SUBSCRIPT)
809 strcat(props, "\\sub");
810 else if (fmt->dwEffects & CFE_SUPERSCRIPT)
811 strcat(props, "\\super");
812 else
813 strcat(props, "\\nosupersub");
814 }
815 if ((old_fmt->dwEffects ^ fmt->dwEffects) & CFE_UNDERLINE ||
816 old_fmt->bUnderlineType != fmt->bUnderlineType)
817 {
818 BYTE type = (fmt->dwEffects & CFE_UNDERLINE) ? fmt->bUnderlineType : CFU_UNDERLINENONE;
819 switch (type)
820 {
821 case CFU_UNDERLINE:
822 strcat(props, "\\ul");
823 break;
824 case CFU_UNDERLINEDOTTED:
825 strcat(props, "\\uld");
826 break;
827 case CFU_UNDERLINEDOUBLE:
828 strcat(props, "\\uldb");
829 break;
830 case CFU_UNDERLINEWORD:
831 strcat(props, "\\ulw");
832 break;
833 case CFU_CF1UNDERLINE:
834 case CFU_UNDERLINENONE:
835 default:
836 strcat(props, "\\ulnone");
837 break;
838 }
839 }
840
841 if (wcscmp(old_fmt->szFaceName, fmt->szFaceName) ||
842 old_fmt->bCharSet != fmt->bCharSet)
843 {
844 if (find_font_in_fonttbl( pStream, fmt, &i ))
845 {
846 sprintf(props + strlen(props), "\\f%u", i);
847
848 /* In UTF-8 mode, charsets/codepages are not used */
849 if (pStream->nDefaultCodePage != CP_UTF8)
850 {
851 if (pStream->fonttbl[i].bCharSet == DEFAULT_CHARSET)
852 pStream->nCodePage = pStream->nDefaultCodePage;
853 else
854 pStream->nCodePage = RTFCharSetToCodePage(NULL, pStream->fonttbl[i].bCharSet);
855 }
856 }
857 }
858 if (*props)
859 strcat(props, " ");
860 if (!ME_StreamOutPrint(pStream, props))
861 return FALSE;
862 *old_fmt = *fmt;
863 return TRUE;
864 }
865
866
867 static BOOL
ME_StreamOutRTFText(ME_OutStream * pStream,const WCHAR * text,LONG nChars)868 ME_StreamOutRTFText(ME_OutStream *pStream, const WCHAR *text, LONG nChars)
869 {
870 char buffer[STREAMOUT_BUFFER_SIZE];
871 int pos = 0;
872 int fit, nBytes, i;
873
874 if (nChars == -1)
875 nChars = lstrlenW(text);
876
877 while (nChars) {
878 /* In UTF-8 mode, font charsets are not used. */
879 if (pStream->nDefaultCodePage == CP_UTF8) {
880 /* 6 is the maximum character length in UTF-8 */
881 fit = min(nChars, STREAMOUT_BUFFER_SIZE / 6);
882 nBytes = WideCharToMultiByte(CP_UTF8, 0, text, fit, buffer,
883 STREAMOUT_BUFFER_SIZE, NULL, NULL);
884 nChars -= fit;
885 text += fit;
886 for (i = 0; i < nBytes; i++)
887 if (buffer[i] == '{' || buffer[i] == '}' || buffer[i] == '\\') {
888 if (!ME_StreamOutPrint(pStream, "%.*s\\", i - pos, buffer + pos))
889 return FALSE;
890 pos = i;
891 }
892 if (pos < nBytes)
893 if (!ME_StreamOutMove(pStream, buffer + pos, nBytes - pos))
894 return FALSE;
895 pos = 0;
896 } else if (*text < 128) {
897 if (*text == '{' || *text == '}' || *text == '\\')
898 buffer[pos++] = '\\';
899 buffer[pos++] = (char)(*text++);
900 nChars--;
901 } else {
902 BOOL unknown = FALSE;
903 char letter[3];
904
905 /* FIXME: In the MS docs for WideCharToMultiByte there is a big list of
906 * codepages including CP_SYMBOL for which the last parameter must be set
907 * to NULL for the function to succeed. But in Wine we need to care only
908 * about CP_SYMBOL */
909 nBytes = WideCharToMultiByte(pStream->nCodePage, 0, text, 1,
910 letter, 3, NULL,
911 (pStream->nCodePage == CP_SYMBOL) ? NULL : &unknown);
912 if (unknown)
913 pos += sprintf(buffer + pos, "\\u%d?", (short)*text);
914 else if ((BYTE)*letter < 128) {
915 if (*letter == '{' || *letter == '}' || *letter == '\\')
916 buffer[pos++] = '\\';
917 buffer[pos++] = *letter;
918 } else {
919 for (i = 0; i < nBytes; i++)
920 pos += sprintf(buffer + pos, "\\'%02x", (BYTE)letter[i]);
921 }
922 text++;
923 nChars--;
924 }
925 if (pos >= STREAMOUT_BUFFER_SIZE - 11) {
926 if (!ME_StreamOutMove(pStream, buffer, pos))
927 return FALSE;
928 pos = 0;
929 }
930 }
931 return ME_StreamOutMove(pStream, buffer, pos);
932 }
933
stream_out_graphics(ME_TextEditor * editor,ME_OutStream * stream,ME_Run * run)934 static BOOL stream_out_graphics( ME_TextEditor *editor, ME_OutStream *stream,
935 ME_Run *run )
936 {
937 IDataObject *data;
938 HRESULT hr;
939 FORMATETC fmt = { CF_ENHMETAFILE, NULL, DVASPECT_CONTENT, -1, TYMED_ENHMF };
940 STGMEDIUM med = { TYMED_NULL };
941 BOOL ret = FALSE;
942 ENHMETAHEADER *emf_bits = NULL;
943 UINT size;
944 SIZE goal, pic;
945 ME_Context c;
946
947 hr = IOleObject_QueryInterface( run->reobj->obj.poleobj, &IID_IDataObject, (void **)&data );
948 if (FAILED(hr)) return FALSE;
949
950 ME_InitContext( &c, editor, ITextHost_TxGetDC( editor->texthost ) );
951 hr = IDataObject_QueryGetData( data, &fmt );
952 if (hr != S_OK) goto done;
953
954 hr = IDataObject_GetData( data, &fmt, &med );
955 if (FAILED(hr)) goto done;
956 if (med.tymed != TYMED_ENHMF) goto done;
957
958 size = GetEnhMetaFileBits( med.u.hEnhMetaFile, 0, NULL );
959 if (size < FIELD_OFFSET(ENHMETAHEADER, cbPixelFormat)) goto done;
960
961 emf_bits = HeapAlloc( GetProcessHeap(), 0, size );
962 if (!emf_bits) goto done;
963
964 size = GetEnhMetaFileBits( med.u.hEnhMetaFile, size, (BYTE *)emf_bits );
965 if (size < FIELD_OFFSET(ENHMETAHEADER, cbPixelFormat)) goto done;
966
967 /* size_in_pixels = (frame_size / 100) * szlDevice / szlMillimeters
968 pic = size_in_pixels * 2540 / dpi */
969 pic.cx = MulDiv( emf_bits->rclFrame.right - emf_bits->rclFrame.left, emf_bits->szlDevice.cx * 254,
970 emf_bits->szlMillimeters.cx * c.dpi.cx * 10 );
971 pic.cy = MulDiv( emf_bits->rclFrame.bottom - emf_bits->rclFrame.top, emf_bits->szlDevice.cy * 254,
972 emf_bits->szlMillimeters.cy * c.dpi.cy * 10 );
973
974 /* convert goal size to twips */
975 goal.cx = MulDiv( run->reobj->obj.sizel.cx, 144, 254 );
976 goal.cy = MulDiv( run->reobj->obj.sizel.cy, 144, 254 );
977
978 if (!ME_StreamOutPrint( stream, "{\\*\\shppict{\\pict\\emfblip\\picw%d\\pich%d\\picwgoal%d\\pichgoal%d\n",
979 pic.cx, pic.cy, goal.cx, goal.cy ))
980 goto done;
981
982 if (!ME_StreamOutHexData( stream, (BYTE *)emf_bits, size ))
983 goto done;
984
985 if (!ME_StreamOutPrint( stream, "}}\n" ))
986 goto done;
987
988 ret = TRUE;
989
990 done:
991 ME_DestroyContext( &c );
992 HeapFree( GetProcessHeap(), 0, emf_bits );
993 ReleaseStgMedium( &med );
994 IDataObject_Release( data );
995 return ret;
996 }
997
ME_StreamOutRTF(ME_TextEditor * editor,ME_OutStream * pStream,const ME_Cursor * start,int nChars,int dwFormat)998 static BOOL ME_StreamOutRTF(ME_TextEditor *editor, ME_OutStream *pStream,
999 const ME_Cursor *start, int nChars, int dwFormat)
1000 {
1001 ME_Cursor cursor = *start;
1002 ME_DisplayItem *prev_para = NULL;
1003 ME_Cursor endCur = cursor;
1004
1005 ME_MoveCursorChars(editor, &endCur, nChars, TRUE);
1006
1007 if (!ME_StreamOutRTFHeader(pStream, dwFormat))
1008 return FALSE;
1009
1010 if (!ME_StreamOutRTFFontAndColorTbl(pStream, cursor.pRun, endCur.pRun))
1011 return FALSE;
1012
1013 /* TODO: stylesheet table */
1014
1015 if (!ME_StreamOutPrint(pStream, "{\\*\\generator Wine Riched20 2.0;}\r\n"))
1016 return FALSE;
1017
1018 /* TODO: information group */
1019
1020 /* TODO: document formatting properties */
1021
1022 /* FIXME: We have only one document section */
1023
1024 /* TODO: section formatting properties */
1025
1026 do {
1027 if (cursor.pPara != prev_para)
1028 {
1029 prev_para = cursor.pPara;
1030 if (!ME_StreamOutRTFParaProps(editor, pStream, cursor.pPara))
1031 return FALSE;
1032 }
1033
1034 if (cursor.pRun == endCur.pRun && !endCur.nOffset)
1035 break;
1036 TRACE("flags %xh\n", cursor.pRun->member.run.nFlags);
1037 /* TODO: emit embedded objects */
1038 if (cursor.pPara->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND))
1039 continue;
1040 if (cursor.pRun->member.run.nFlags & MERF_GRAPHICS) {
1041 if (!stream_out_graphics(editor, pStream, &cursor.pRun->member.run))
1042 return FALSE;
1043 } else if (cursor.pRun->member.run.nFlags & MERF_TAB) {
1044 if (editor->bEmulateVersion10 && /* v1.0 - 3.0 */
1045 cursor.pPara->member.para.fmt.dwMask & PFM_TABLE &&
1046 cursor.pPara->member.para.fmt.wEffects & PFE_TABLE)
1047 {
1048 if (!ME_StreamOutPrint(pStream, "\\cell "))
1049 return FALSE;
1050 } else {
1051 if (!ME_StreamOutPrint(pStream, "\\tab "))
1052 return FALSE;
1053 }
1054 } else if (cursor.pRun->member.run.nFlags & MERF_ENDCELL) {
1055 if (pStream->nNestingLevel > 1) {
1056 if (!ME_StreamOutPrint(pStream, "\\nestcell "))
1057 return FALSE;
1058 } else {
1059 if (!ME_StreamOutPrint(pStream, "\\cell "))
1060 return FALSE;
1061 }
1062 nChars--;
1063 } else if (cursor.pRun->member.run.nFlags & MERF_ENDPARA) {
1064 if (!ME_StreamOutRTFCharProps(pStream, &cursor.pRun->member.run.style->fmt))
1065 return FALSE;
1066
1067 if (cursor.pPara->member.para.fmt.dwMask & PFM_TABLE &&
1068 cursor.pPara->member.para.fmt.wEffects & PFE_TABLE &&
1069 !(cursor.pPara->member.para.nFlags & (MEPF_ROWSTART|MEPF_ROWEND|MEPF_CELL)))
1070 {
1071 if (!ME_StreamOutPrint(pStream, "\\row\r\n"))
1072 return FALSE;
1073 } else {
1074 if (!ME_StreamOutPrint(pStream, "\\par\r\n"))
1075 return FALSE;
1076 }
1077 /* Skip as many characters as required by current line break */
1078 nChars = max(0, nChars - cursor.pRun->member.run.len);
1079 } else if (cursor.pRun->member.run.nFlags & MERF_ENDROW) {
1080 if (!ME_StreamOutPrint(pStream, "\\line\r\n"))
1081 return FALSE;
1082 nChars--;
1083 } else {
1084 int nEnd;
1085
1086 TRACE("style %p\n", cursor.pRun->member.run.style);
1087 if (!ME_StreamOutRTFCharProps(pStream, &cursor.pRun->member.run.style->fmt))
1088 return FALSE;
1089
1090 nEnd = (cursor.pRun == endCur.pRun) ? endCur.nOffset : cursor.pRun->member.run.len;
1091 if (!ME_StreamOutRTFText(pStream, get_text( &cursor.pRun->member.run, cursor.nOffset ),
1092 nEnd - cursor.nOffset))
1093 return FALSE;
1094 cursor.nOffset = 0;
1095 }
1096 } while (cursor.pRun != endCur.pRun && ME_NextRun(&cursor.pPara, &cursor.pRun, TRUE));
1097
1098 if (!ME_StreamOutMove(pStream, "}\0", 2))
1099 return FALSE;
1100 return TRUE;
1101 }
1102
1103
ME_StreamOutText(ME_TextEditor * editor,ME_OutStream * pStream,const ME_Cursor * start,int nChars,DWORD dwFormat)1104 static BOOL ME_StreamOutText(ME_TextEditor *editor, ME_OutStream *pStream,
1105 const ME_Cursor *start, int nChars, DWORD dwFormat)
1106 {
1107 ME_Cursor cursor = *start;
1108 int nLen;
1109 UINT nCodePage = CP_ACP;
1110 char *buffer = NULL;
1111 int nBufLen = 0;
1112 BOOL success = TRUE;
1113
1114 if (!cursor.pRun)
1115 return FALSE;
1116
1117 if (dwFormat & SF_USECODEPAGE)
1118 nCodePage = HIWORD(dwFormat);
1119
1120 /* TODO: Handle SF_TEXTIZED */
1121
1122 while (success && nChars && cursor.pRun) {
1123 nLen = min(nChars, cursor.pRun->member.run.len - cursor.nOffset);
1124
1125 if (!editor->bEmulateVersion10 && cursor.pRun->member.run.nFlags & MERF_ENDPARA)
1126 {
1127 static const WCHAR szEOL[] = { '\r', '\n' };
1128
1129 /* richedit 2.0 - all line breaks are \r\n */
1130 if (dwFormat & SF_UNICODE)
1131 success = ME_StreamOutMove(pStream, (const char *)szEOL, sizeof(szEOL));
1132 else
1133 success = ME_StreamOutMove(pStream, "\r\n", 2);
1134 } else {
1135 if (dwFormat & SF_UNICODE)
1136 success = ME_StreamOutMove(pStream, (const char *)(get_text( &cursor.pRun->member.run, cursor.nOffset )),
1137 sizeof(WCHAR) * nLen);
1138 else {
1139 int nSize;
1140
1141 nSize = WideCharToMultiByte(nCodePage, 0, get_text( &cursor.pRun->member.run, cursor.nOffset ),
1142 nLen, NULL, 0, NULL, NULL);
1143 if (nSize > nBufLen) {
1144 buffer = heap_realloc(buffer, nSize);
1145 nBufLen = nSize;
1146 }
1147 WideCharToMultiByte(nCodePage, 0, get_text( &cursor.pRun->member.run, cursor.nOffset ),
1148 nLen, buffer, nSize, NULL, NULL);
1149 success = ME_StreamOutMove(pStream, buffer, nSize);
1150 }
1151 }
1152
1153 nChars -= nLen;
1154 cursor.nOffset = 0;
1155 cursor.pRun = ME_FindItemFwd(cursor.pRun, diRun);
1156 }
1157
1158 heap_free(buffer);
1159 return success;
1160 }
1161
1162
ME_StreamOutRange(ME_TextEditor * editor,DWORD dwFormat,const ME_Cursor * start,int nChars,EDITSTREAM * stream)1163 LRESULT ME_StreamOutRange(ME_TextEditor *editor, DWORD dwFormat,
1164 const ME_Cursor *start,
1165 int nChars, EDITSTREAM *stream)
1166 {
1167 ME_OutStream *pStream = ME_StreamOutInit(editor, stream);
1168
1169 if (dwFormat & SF_RTF)
1170 ME_StreamOutRTF(editor, pStream, start, nChars, dwFormat);
1171 else if (dwFormat & SF_TEXT || dwFormat & SF_TEXTIZED)
1172 ME_StreamOutText(editor, pStream, start, nChars, dwFormat);
1173 if (!pStream->stream->dwError)
1174 ME_StreamOutFlush(pStream);
1175 return ME_StreamOutFree(pStream);
1176 }
1177
1178 LRESULT
ME_StreamOut(ME_TextEditor * editor,DWORD dwFormat,EDITSTREAM * stream)1179 ME_StreamOut(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream)
1180 {
1181 ME_Cursor start;
1182 int nChars;
1183
1184 if (dwFormat & SFF_SELECTION) {
1185 int nStart, nTo;
1186 start = editor->pCursors[ME_GetSelectionOfs(editor, &nStart, &nTo)];
1187 nChars = nTo - nStart;
1188 } else {
1189 ME_SetCursorToStart(editor, &start);
1190 nChars = ME_GetTextLength(editor);
1191 /* Generate an end-of-paragraph at the end of SCF_ALL RTF output */
1192 if (dwFormat & SF_RTF)
1193 nChars++;
1194 }
1195 return ME_StreamOutRange(editor, dwFormat, &start, nChars, stream);
1196 }
1197