StrRetToStrNAW returns ok on NT4, though it should return FALSE.
[wine] / dlls / riched20 / writer.c
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "editor.h"
22 #include "rtf.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(richedit);
25
26
27 static BOOL
28 ME_StreamOutRTFText(ME_TextEditor *editor, WCHAR *text, LONG nChars);
29
30
31 static void
32 ME_StreamOutInit(ME_TextEditor *editor, EDITSTREAM *stream)
33 {
34   editor->pStream = ALLOC_OBJ(ME_OutStream);
35   editor->pStream->stream = stream;
36   editor->pStream->pos = 0;
37   editor->pStream->written = 0;
38   editor->pStream->nFontTblLen = 0;
39   editor->pStream->nColorTblLen = 1;
40 }
41
42
43 static BOOL
44 ME_StreamOutFlush(ME_TextEditor *editor)
45 {
46   LONG nStart = 0;
47   LONG nWritten = 0;
48   EDITSTREAM *stream = editor->pStream->stream;
49
50   do {
51     stream->dwError = stream->pfnCallback(stream->dwCookie, editor->pStream->buffer + nStart,
52                                           editor->pStream->pos - nStart, &nWritten);
53     if (nWritten == 0 || stream->dwError)
54       return FALSE;
55     editor->pStream->written += nWritten;
56     nStart += nWritten;
57   } while (nStart < editor->pStream->pos);
58   editor->pStream->pos = 0;
59   return TRUE;
60 }
61
62
63 static LONG
64 ME_StreamOutFree(ME_TextEditor *editor)
65 {
66   LONG written = editor->pStream->written;
67
68   FREE_OBJ(editor->pStream);
69   editor->pStream = NULL;
70   return written;
71 }
72
73
74 static BOOL
75 ME_StreamOutMove(ME_TextEditor *editor, BYTE *buffer, int len)
76 {
77   ME_OutStream *pStream = editor->pStream;
78   
79   while (len) {
80     int space = STREAMOUT_BUFFER_SIZE - pStream->pos;
81     int fit = min(space, len);
82
83     TRACE("%u:%u:%.*s\n", pStream->pos, fit, fit, buffer);
84     memmove(pStream->buffer + pStream->pos, buffer, fit);
85     len -= fit;
86     buffer += fit;
87     pStream->pos += fit;
88     if (pStream->pos == STREAMOUT_BUFFER_SIZE) {
89       if (!ME_StreamOutFlush(editor))
90         return FALSE;
91     }
92   }
93   return TRUE;
94 }
95
96
97 static BOOL
98 ME_StreamOutPrint(ME_TextEditor *editor, char *format, ...)
99 {
100   char string[STREAMOUT_BUFFER_SIZE]; /* This is going to be enough */
101   int len;
102   va_list valist;
103
104   va_start(valist, format);
105   len = vsnprintf(string, sizeof(string), format, valist);
106   va_end(valist);
107   
108   return ME_StreamOutMove(editor, string, len);
109 }
110
111
112 static BOOL
113 ME_StreamOutRTFHeader(ME_TextEditor *editor, int dwFormat)
114 {
115   char *cCharSet = NULL;
116   UINT nCodePage;
117   LANGID language;
118   BOOL success;
119   
120   if (dwFormat & SF_USECODEPAGE) {
121     CPINFOEXW info;
122     
123     switch (HIWORD(dwFormat)) {
124       case CP_ACP:
125         cCharSet = "ansi";
126         nCodePage = GetACP();
127         break;
128       case CP_OEMCP:
129         nCodePage = GetOEMCP();
130         if (nCodePage == 437)
131           cCharSet = "pc";
132         else if (nCodePage == 850)
133           cCharSet = "pca";
134         else
135           cCharSet = "ansi";
136         break;
137       case CP_UTF8:
138         nCodePage = CP_UTF8;
139         break;
140       default:
141         if (HIWORD(dwFormat) == CP_MACCP) {
142           cCharSet = "mac";
143           nCodePage = 10000; /* MacRoman */
144         } else {
145           cCharSet = "ansi";
146           nCodePage = 1252; /* Latin-1 */
147         }
148         if (GetCPInfoExW(HIWORD(dwFormat), 0, &info))
149           nCodePage = info.CodePage;
150     }
151   } else {
152     cCharSet = "ansi";
153     /* TODO: If the original document contained an \ansicpg value, retain it.
154      * Otherwise, M$ richedit emits a codepage number determined from the
155      * charset of the default font here. Anyway, this value is not used by
156      * the reader... */
157     nCodePage = GetACP();
158   }
159   if (nCodePage == CP_UTF8)
160     success = ME_StreamOutPrint(editor, "{\\urtf");
161   else
162     success = ME_StreamOutPrint(editor, "{\\rtf1\\%s\\ansicpg%u\\uc1", cCharSet, nCodePage);
163
164   if (!success)
165     return FALSE;
166
167   editor->pStream->nDefaultCodePage = nCodePage;
168   
169   /* FIXME: This should be a document property */
170   /* TODO: handle SFF_PLAINRTF */
171   language = GetUserDefaultLangID(); 
172   if (!ME_StreamOutPrint(editor, "\\deff0\\deflang%u\\deflangfe%u", language, language))
173     return FALSE;
174
175   /* FIXME: This should be a document property */
176   editor->pStream->nDefaultFont = 0;
177
178   return TRUE;
179 }
180
181
182 static BOOL
183 ME_StreamOutRTFFontAndColorTbl(ME_TextEditor *editor, ME_DisplayItem *pFirstRun, ME_DisplayItem *pLastRun)
184 {
185   ME_DisplayItem *item = pFirstRun;
186   ME_FontTableItem *table = editor->pStream->fonttbl;
187   int i;
188   
189   do {
190     CHARFORMAT2W *fmt = &item->member.run.style->fmt;
191     COLORREF crColor;
192
193     if (fmt->dwMask & CFM_FACE) {
194       WCHAR *face = fmt->szFaceName;
195       BYTE bCharSet = (fmt->dwMask & CFM_CHARSET) ? fmt->bCharSet : DEFAULT_CHARSET;
196   
197       for (i = 0; i < editor->pStream->nFontTblLen; i++)
198         if (table[i].bCharSet == bCharSet
199             && (table[i].szFaceName == face || !lstrcmpW(table[i].szFaceName, face)))
200           break;
201       if (i == editor->pStream->nFontTblLen) {
202         table[i].bCharSet = bCharSet;
203         table[i].szFaceName = face;
204         editor->pStream->nFontTblLen++;
205       }
206     }
207     
208     if (fmt->dwMask & CFM_COLOR && !(fmt->dwEffects & CFE_AUTOCOLOR)) {
209       crColor = fmt->crTextColor;
210       for (i = 1; i < editor->pStream->nColorTblLen; i++)
211         if (editor->pStream->colortbl[i] == crColor)
212           break;
213       if (i == editor->pStream->nColorTblLen) {
214         editor->pStream->colortbl[i] = crColor;
215         editor->pStream->nColorTblLen++;
216       }
217     }
218     if (fmt->dwMask & CFM_BACKCOLOR && !(fmt->dwEffects & CFE_AUTOBACKCOLOR)) {
219       crColor = fmt->crBackColor;
220       for (i = 1; i < editor->pStream->nColorTblLen; i++)
221         if (editor->pStream->colortbl[i] == crColor)
222           break;
223       if (i == editor->pStream->nColorTblLen) {
224         editor->pStream->colortbl[i] = crColor;
225         editor->pStream->nColorTblLen++;
226       }
227     }
228
229     if (item == pLastRun)
230       break;
231     item = ME_FindItemFwd(item, diRun);
232   } while (item);
233         
234   if (!ME_StreamOutPrint(editor, "{\\fonttbl"))
235     return FALSE;
236   
237   for (i = 0; i < editor->pStream->nFontTblLen; i++) {
238     if (table[i].bCharSet != DEFAULT_CHARSET) {
239       if (!ME_StreamOutPrint(editor, "{\\f%u\\fcharset%u ", i, table[i].bCharSet))
240         return FALSE;
241     } else {
242       if (!ME_StreamOutPrint(editor, "{\\f%u ", i))
243         return FALSE;
244     }
245     if (!ME_StreamOutRTFText(editor, table[i].szFaceName, -1))
246       return FALSE;
247     if (!ME_StreamOutPrint(editor, ";}\r\n"))
248       return FALSE;
249   }
250   if (!ME_StreamOutPrint(editor, "}"))
251     return FALSE;
252
253   /* Output colors table if not empty */
254   if (editor->pStream->nColorTblLen > 1) {
255     if (!ME_StreamOutPrint(editor, "{\\colortbl;"))
256       return FALSE;
257     for (i = 1; i < editor->pStream->nColorTblLen; i++) {
258       if (!ME_StreamOutPrint(editor, "\\red%u\\green%u\\blue%u;",
259                              editor->pStream->colortbl[i] & 0xFF,
260                              (editor->pStream->colortbl[i] >> 8) & 0xFF,
261                              (editor->pStream->colortbl[i] >> 16) & 0xFF))
262         return FALSE;
263     }
264     if (!ME_StreamOutPrint(editor, "}"))
265       return FALSE;
266   }
267
268   return TRUE;
269 }
270
271
272 static BOOL
273 ME_StreamOutRTFParaProps(ME_TextEditor *editor, ME_DisplayItem *para)
274 {
275   PARAFORMAT2 *fmt = para->member.para.pFmt;
276   char props[STREAMOUT_BUFFER_SIZE] = "";
277   int i;
278
279   /* TODO: Don't emit anything if the last PARAFORMAT2 is inherited */
280   if (!ME_StreamOutPrint(editor, "\\pard"))
281     return FALSE;
282   
283   /* TODO: PFM_BORDER. M$ does not emit any keywords for these properties, and
284    * when streaming border keywords in, PFM_BORDER is set, but wBorder field is
285    * set very different from the documentation.
286    * (Tested with RichEdit 5.50.25.0601) */
287   
288   if (fmt->dwMask & PFM_ALIGNMENT) {
289     switch (fmt->wAlignment) {
290       case PFA_LEFT:
291         /* Default alignment: not emitted */
292         break;
293       case PFA_RIGHT:
294         strcat(props, "\\qr");
295         break;
296       case PFA_CENTER:
297         strcat(props, "\\qc");
298         break;
299       case PFA_JUSTIFY:
300         strcat(props, "\\qj");
301         break;
302     }
303   }
304   
305   if (fmt->dwMask & PFM_LINESPACING) {
306     /* FIXME: MSDN says that the bLineSpacingRule field is controlled by the
307      * PFM_SPACEAFTER flag. Is that true? I don't believe so. */
308     switch (fmt->bLineSpacingRule) {
309       case 0: /* Single spacing */
310         strcat(props, "\\sl-240\\slmult1");
311         break;
312       case 1: /* 1.5 spacing */
313         strcat(props, "\\sl-360\\slmult1");
314         break;
315       case 2: /* Double spacing */
316         strcat(props, "\\sl-480\\slmult1");
317         break;
318       case 3:
319         sprintf(props + strlen(props), "\\sl%ld\\slmult0", fmt->dyLineSpacing);
320         break;
321       case 4:
322         sprintf(props + strlen(props), "\\sl-%ld\\slmult0", fmt->dyLineSpacing);
323         break;
324       case 5:
325         sprintf(props + strlen(props), "\\sl-%ld\\slmult1", fmt->dyLineSpacing * 240 / 20);
326         break;
327     }
328   }
329
330   if (fmt->dwMask & PFM_DONOTHYPHEN && fmt->wEffects & PFE_DONOTHYPHEN)
331     strcat(props, "\\hyph0");
332   if (fmt->dwMask & PFM_KEEP && fmt->wEffects & PFE_KEEP)
333     strcat(props, "\\keep");
334   if (fmt->dwMask & PFM_KEEPNEXT && fmt->wEffects & PFE_KEEPNEXT)
335     strcat(props, "\\keepn");
336   if (fmt->dwMask & PFM_NOLINENUMBER && fmt->wEffects & PFE_NOLINENUMBER)
337     strcat(props, "\\noline");
338   if (fmt->dwMask & PFM_NOWIDOWCONTROL && fmt->wEffects & PFE_NOWIDOWCONTROL)
339     strcat(props, "\\nowidctlpar");
340   if (fmt->dwMask & PFM_PAGEBREAKBEFORE && fmt->wEffects & PFE_PAGEBREAKBEFORE)
341     strcat(props, "\\pagebb");
342   if (fmt->dwMask & PFM_RTLPARA && fmt->wEffects & PFE_RTLPARA)
343     strcat(props, "\\rtlpar");
344   if (fmt->dwMask & PFM_SIDEBYSIDE && fmt->wEffects & PFE_SIDEBYSIDE)
345     strcat(props, "\\sbys");
346   if (fmt->dwMask & PFM_TABLE && fmt->dwMask & PFE_TABLE)
347     strcat(props, "\\intbl");
348   
349   if (fmt->dwMask & PFM_OFFSET)
350     sprintf(props + strlen(props), "\\li%ld", fmt->dxOffset);
351   if (fmt->dwMask & PFM_OFFSETINDENT || fmt->dwMask & PFM_STARTINDENT)
352     sprintf(props + strlen(props), "\\fi%ld", fmt->dxStartIndent);
353   if (fmt->dwMask & PFM_RIGHTINDENT)
354     sprintf(props + strlen(props), "\\ri%ld", fmt->dxRightIndent);
355   if (fmt->dwMask & PFM_SPACEAFTER)
356     sprintf(props + strlen(props), "\\sa%ld", fmt->dySpaceAfter);
357   if (fmt->dwMask & PFM_SPACEBEFORE)
358     sprintf(props + strlen(props), "\\sb%ld", fmt->dySpaceBefore);
359   if (fmt->dwMask & PFM_STYLE)
360     sprintf(props + strlen(props), "\\s%d", fmt->sStyle);
361
362   if (fmt->dwMask & PFM_TABSTOPS) {
363     static const char *leader[6] = { "", "\\tldot", "\\tlhyph", "\\tlul", "\\tlth", "\\tleq" };
364     
365     for (i = 0; i < fmt->cTabCount; i++) {
366       switch ((fmt->rgxTabs[i] >> 24) & 0xF) {
367         case 1:
368           strcat(props, "\\tqc");
369           break;
370         case 2:
371           strcat(props, "\\tqr");
372           break;
373         case 3:
374           strcat(props, "\\tqdec");
375           break;
376         case 4:
377           /* Word bar tab (vertical bar). Handled below */
378           break;
379       }
380       if (fmt->rgxTabs[i] >> 28 <= 5)
381         strcat(props, leader[fmt->rgxTabs[i] >> 28]);
382     }
383   }
384     
385   
386   if (fmt->dwMask & PFM_SHADING) {
387     static const char *style[16] = { "", "\\bgdkhoriz", "\\bgdkvert", "\\bgdkfdiag",
388                                      "\\bgdkbdiag", "\\bgdkcross", "\\bgdkdcross",
389                                      "\\bghoriz", "\\bgvert", "\\bgfdiag",
390                                      "\\bgbdiag", "\\bgcross", "\\bgdcross",
391                                      "", "", "" };
392     if (fmt->wShadingWeight)
393       sprintf(props + strlen(props), "\\shading%d", fmt->wShadingWeight);
394     if (fmt->wShadingStyle & 0xF)
395       strcat(props, style[fmt->wShadingStyle & 0xF]);
396     sprintf(props + strlen(props), "\\cfpat%d\\cbpat%d",
397             (fmt->wShadingStyle >> 4) & 0xF, (fmt->wShadingStyle >> 8) & 0xF);
398   }
399   
400   if (*props && !ME_StreamOutPrint(editor, props))
401     return FALSE;
402
403   return TRUE;
404 }
405
406
407 static BOOL
408 ME_StreamOutRTFCharProps(ME_TextEditor *editor, CHARFORMAT2W *fmt)
409 {
410   char props[STREAMOUT_BUFFER_SIZE] = "";
411   int i;
412
413   if (fmt->dwMask & CFM_ALLCAPS && fmt->dwEffects & CFE_ALLCAPS)
414     strcat(props, "\\caps");
415   if (fmt->dwMask & CFM_ANIMATION)
416     sprintf(props + strlen(props), "\\animtext%u", fmt->bAnimation);
417   if (fmt->dwMask & CFM_BACKCOLOR) {
418     if (!(fmt->dwEffects & CFE_AUTOBACKCOLOR)) {
419       for (i = 1; i < editor->pStream->nColorTblLen; i++)
420         if (editor->pStream->colortbl[i] == fmt->crBackColor) {
421           sprintf(props + strlen(props), "\\cb%u", i);
422           break;
423         }
424     }
425   }
426   if (fmt->dwMask & CFM_BOLD && fmt->dwEffects & CFE_BOLD)
427     strcat(props, "\\b");
428   if (fmt->dwMask & CFM_COLOR) {
429     if (!(fmt->dwEffects & CFE_AUTOCOLOR)) {
430       for (i = 1; i < editor->pStream->nColorTblLen; i++)
431         if (editor->pStream->colortbl[i] == fmt->crTextColor) {
432           sprintf(props + strlen(props), "\\cf%u", i);
433           break;
434         }
435     }
436   }
437   /* TODO: CFM_DISABLED */
438   if (fmt->dwMask & CFM_EMBOSS && fmt->dwEffects & CFE_EMBOSS)
439     strcat(props, "\\embo");
440   if (fmt->dwMask & CFM_HIDDEN && fmt->dwEffects & CFE_HIDDEN)
441     strcat(props, "\\v");
442   if (fmt->dwMask & CFM_IMPRINT && fmt->dwEffects & CFE_IMPRINT)
443     strcat(props, "\\impr");
444   if (fmt->dwMask & CFM_ITALIC && fmt->dwEffects & CFE_ITALIC)
445     strcat(props, "\\i");
446   if (fmt->dwMask & CFM_KERNING)
447     sprintf(props + strlen(props), "\\kerning%u", fmt->wKerning);
448   if (fmt->dwMask & CFM_LCID) {
449     /* TODO: handle SFF_PLAINRTF */
450     if (LOWORD(fmt->lcid) == 1024)
451       strcat(props, "\\noproof\\lang1024\\langnp1024\\langfe1024\\langfenp1024");
452     else
453       sprintf(props + strlen(props), "\\lang%u", LOWORD(fmt->lcid));
454   }
455   /* CFM_LINK is not streamed out by M$ */
456   if (fmt->dwMask & CFM_OFFSET) {
457     if (fmt->yOffset >= 0)
458       sprintf(props + strlen(props), "\\up%ld", fmt->yOffset);
459     else
460       sprintf(props + strlen(props), "\\dn%ld", -fmt->yOffset);
461   }
462   if (fmt->dwMask & CFM_OUTLINE && fmt->dwEffects & CFE_OUTLINE)
463     strcat(props, "\\outl");
464   if (fmt->dwMask & CFM_PROTECTED && fmt->dwEffects & CFE_PROTECTED)
465     strcat(props, "\\protect");
466   /* TODO: CFM_REVISED CFM_REVAUTHOR - probably using rsidtbl? */
467   if (fmt->dwMask & CFM_SHADOW && fmt->dwEffects & CFE_SHADOW)
468     strcat(props, "\\shad");
469   if (fmt->dwMask & CFM_SIZE)
470     sprintf(props + strlen(props), "\\fs%ld", fmt->yHeight / 10);
471   if (fmt->dwMask & CFM_SMALLCAPS && fmt->dwEffects & CFE_SMALLCAPS)
472     strcat(props, "\\scaps");
473   if (fmt->dwMask & CFM_SPACING)
474     sprintf(props + strlen(props), "\\expnd%u\\expndtw%u", fmt->sSpacing / 5, fmt->sSpacing);
475   if (fmt->dwMask & CFM_STRIKEOUT && fmt->dwEffects & CFE_STRIKEOUT)
476     strcat(props, "\\strike");
477   if (fmt->dwMask & CFM_STYLE) {
478     sprintf(props + strlen(props), "\\cs%u", fmt->sStyle);
479     /* TODO: emit style contents here */
480   }
481   if (fmt->dwMask & (CFM_SUBSCRIPT | CFM_SUPERSCRIPT)) {
482     if (fmt->dwEffects & CFE_SUBSCRIPT)
483       strcat(props, "\\sub");
484     else if (fmt->dwEffects & CFE_SUPERSCRIPT)
485       strcat(props, "\\super");
486   }
487   if (fmt->dwMask & CFM_UNDERLINE || fmt->dwMask & CFM_UNDERLINETYPE) {
488     if (fmt->dwMask & CFM_UNDERLINETYPE)
489       switch (fmt->bUnderlineType) {
490         case CFU_CF1UNDERLINE:
491         case CFU_UNDERLINE:
492           strcat(props, "\\ul");
493           break;
494         case CFU_UNDERLINEDOTTED:
495           strcat(props, "\\uld");
496           break;
497         case CFU_UNDERLINEDOUBLE:
498           strcat(props, "\\uldb");
499           break;
500         case CFU_UNDERLINEWORD:
501           strcat(props, "\\ulw");
502           break;
503         case CFU_UNDERLINENONE:
504         default:
505           strcat(props, "\\ul0");
506           break;
507       }
508     else if (fmt->dwEffects & CFE_UNDERLINE)
509       strcat(props, "\\ul");
510   }
511   /* FIXME: How to emit CFM_WEIGHT? */
512   
513   if (fmt->dwMask & CFM_FACE || fmt->dwMask & CFM_CHARSET) {
514     WCHAR *szFaceName;
515     
516     if (fmt->dwMask & CFM_FACE)
517       szFaceName = fmt->szFaceName;
518     else
519       szFaceName = editor->pStream->fonttbl[0].szFaceName;
520     for (i = 0; i < editor->pStream->nFontTblLen; i++) {
521       if (szFaceName == editor->pStream->fonttbl[i].szFaceName
522           || !lstrcmpW(szFaceName, editor->pStream->fonttbl[i].szFaceName))
523         if (!(fmt->dwMask & CFM_CHARSET)
524             || fmt->bCharSet == editor->pStream->fonttbl[i].bCharSet)
525           break;
526     }
527     if (i < editor->pStream->nFontTblLen && i != editor->pStream->nDefaultFont)
528     {
529       sprintf(props + strlen(props), "\\f%u", i);
530
531       /* In UTF-8 mode, charsets/codepages are not used */
532       if (editor->pStream->nDefaultCodePage != CP_UTF8)
533       {
534         if (editor->pStream->fonttbl[i].bCharSet == DEFAULT_CHARSET)
535           editor->pStream->nCodePage = editor->pStream->nDefaultCodePage;
536         else
537           editor->pStream->nCodePage = RTFCharSetToCodePage(NULL,
538                                                             editor->pStream->fonttbl[i].bCharSet);
539       }
540     }
541   }
542   if (*props)
543     strcat(props, " ");
544   if (!ME_StreamOutPrint(editor, props))
545     return FALSE;
546   return TRUE;
547 }
548
549
550 static BOOL
551 ME_StreamOutRTFText(ME_TextEditor *editor, WCHAR *text, LONG nChars)
552 {
553   char buffer[STREAMOUT_BUFFER_SIZE];
554   int pos = 0;
555   int fit, i;
556
557   if (nChars == -1)
558     nChars = lstrlenW(text);
559   
560   while (nChars) {
561     /* In UTF-8 mode, font charsets are not used. */
562     if (editor->pStream->nDefaultCodePage == CP_UTF8) {
563       /* 6 is the maximum character length in UTF-8 */
564       fit = min(nChars, STREAMOUT_BUFFER_SIZE / 6);
565       WideCharToMultiByte(CP_UTF8, 0, text, fit, buffer, STREAMOUT_BUFFER_SIZE,
566                           NULL, NULL);
567       nChars -= fit;
568       text += fit;
569       for (i = 0; buffer[i]; i++)
570         if (buffer[i] == '{' || buffer[i] == '}' || buffer[i] == '\\') {
571           if (!ME_StreamOutPrint(editor, "%.*s\\", i - pos, buffer + pos))
572             return FALSE;
573           pos = i;
574         }
575       if (!pos)
576         if (!ME_StreamOutPrint(editor, "%s", buffer + pos))
577           return FALSE;
578       pos = 0;
579     } else if (*text < 128) {
580       if (*text == '{' || *text == '}' || *text == '\\')
581         buffer[pos++] = '\\';
582       buffer[pos++] = (char)(*text++);
583       nChars--;
584     } else {
585       BOOL unknown;
586       BYTE letter[3];
587       int nBytes, i;
588       
589       nBytes = WideCharToMultiByte(editor->pStream->nCodePage, 0, text, 1,
590                                    letter, 3, NULL, &unknown);
591       if (unknown)
592         pos += sprintf(buffer + pos, "\\u%d?", (int)*text);
593       else if (*letter < 128) {
594         if (*letter == '{' || *letter == '}' || *letter == '\\')
595           buffer[pos++] = '\\';
596         buffer[pos++] = *letter;
597       } else {
598          for (i = 0; i < nBytes; i++)
599            pos += sprintf(buffer + pos, "\\'%02x", letter[i]);
600       }
601       text++;
602       nChars--;
603     }
604     if (pos >= STREAMOUT_BUFFER_SIZE - 11) {
605       if (!ME_StreamOutMove(editor, buffer, pos))
606         return FALSE;
607       pos = 0;
608     }
609   }
610   return ME_StreamOutMove(editor, buffer, pos);
611 }
612
613
614 static BOOL
615 ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat)
616 {
617   ME_DisplayItem *p, *pEnd;
618   int nOffset, nEndLen;
619   ME_RunOfsFromCharOfs(editor, nStart, &p, &nOffset);
620   ME_RunOfsFromCharOfs(editor, nStart+nChars, &pEnd, &nEndLen);
621   
622   if (!ME_StreamOutRTFHeader(editor, dwFormat))
623     return FALSE;
624
625   if (!ME_StreamOutRTFFontAndColorTbl(editor, p, pEnd))
626     return FALSE;
627   
628   /* TODO: stylesheet table */
629   
630   /* FIXME: maybe emit something smarter for the generator? */
631   if (!ME_StreamOutPrint(editor, "{\\*\\generator Wine Riched20 2.0.????;}"))
632     return FALSE;
633
634   /* TODO: information group */
635
636   /* TODO: document formatting properties */
637
638   /* FIXME: We have only one document section */
639
640   /* TODO: section formatting properties */
641
642   if (!ME_StreamOutRTFParaProps(editor, ME_GetParagraph(p)))
643     return FALSE;
644
645   while(1)
646   {
647     switch(p->type)
648     {
649       case diParagraph:
650         if (!ME_StreamOutRTFParaProps(editor, p))
651           return FALSE;
652         break;
653       case diRun:
654         if (p == pEnd && !nEndLen)
655           break;
656         TRACE("flags %xh\n", p->member.run.nFlags);
657         /* TODO: emit embedded objects */
658         if (p->member.run.nFlags & MERF_GRAPHICS)
659           FIXME("embedded objects are not handled\n");
660         if (p->member.run.nFlags & MERF_ENDPARA) {
661           if (!ME_StreamOutPrint(editor, "\r\n\\par"))
662             return FALSE;
663           nChars--;
664         } else {
665           int nEnd;
666           
667           if (!ME_StreamOutPrint(editor, "{"))
668             return FALSE;
669           TRACE("style %p\n", p->member.run.style);
670           if (!ME_StreamOutRTFCharProps(editor, &p->member.run.style->fmt))
671             return FALSE;
672         
673           nEnd = (p == pEnd) ? nEndLen : ME_StrLen(p->member.run.strText);
674           if (!ME_StreamOutRTFText(editor, p->member.run.strText->szData + nOffset, nEnd - nOffset))
675             return FALSE;
676           nOffset = 0;
677           if (!ME_StreamOutPrint(editor, "}"))
678             return FALSE;
679         }
680         break;
681       default: /* we missed the last item */
682         assert(0);
683     }
684     if (p == pEnd)
685       break;
686     p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
687   }
688   if (!ME_StreamOutPrint(editor, "}"))
689     return FALSE;
690   return TRUE;
691 }
692
693
694 static BOOL
695 ME_StreamOutText(ME_TextEditor *editor, int nStart, int nChars, DWORD dwFormat)
696 {
697   /* FIXME: use ME_RunOfsFromCharOfs */
698   ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart);
699   int nLen;
700   UINT nCodePage = CP_ACP;
701   BYTE *buffer = NULL;
702   int nBufLen = 0;
703   BOOL success = TRUE;
704
705   if (!item)
706     return FALSE;
707    
708   if (dwFormat & SF_USECODEPAGE)
709     nCodePage = HIWORD(dwFormat);
710
711   /* TODO: Handle SF_TEXTIZED */
712   
713   while (success && nChars && item) {
714     nLen = ME_StrLen(item->member.run.strText) - nStart;
715     if (nLen > nChars)
716       nLen = nChars;
717
718     if (item->member.run.nFlags & MERF_ENDPARA) {
719       WCHAR szEOL[] = { '\r', '\n' };
720       
721       if (dwFormat & SF_UNICODE)
722         success = ME_StreamOutMove(editor, (BYTE *)szEOL, 4);
723       else
724         success = ME_StreamOutMove(editor, "\r\n", 2);
725     } else {
726       if (dwFormat & SF_UNICODE)
727         success = ME_StreamOutMove(editor, (BYTE *)(item->member.run.strText->szData + nStart),
728                                    sizeof(WCHAR) * nLen);
729       else {
730         int nSize;
731
732         nSize = WideCharToMultiByte(nCodePage, 0, item->member.run.strText->szData + nStart,
733                                     nLen, NULL, 0, NULL, NULL);
734         if (nSize > nBufLen) {
735           if (buffer)
736             FREE_OBJ(buffer);
737           buffer = ALLOC_N_OBJ(BYTE, nSize);
738           nBufLen = nSize;
739         }
740         WideCharToMultiByte(nCodePage, 0, item->member.run.strText->szData + nStart,
741                             nLen, buffer, nSize, NULL, NULL);
742         success = ME_StreamOutMove(editor, buffer, nSize - 1);
743       }
744     }
745     
746     nChars -= nLen;
747     nStart = 0;
748     item = ME_FindItemFwd(item, diRun);
749   }
750   
751   if (buffer)
752     FREE_OBJ(buffer);
753   return success;
754 }
755
756
757 LRESULT
758 ME_StreamOut(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream)
759 {
760   int nStart, nTo;
761   
762   ME_StreamOutInit(editor, stream);
763
764   if (dwFormat & SFF_SELECTION)
765     ME_GetSelection(editor, &nStart, &nTo);
766   else {
767     nStart = 0;
768     nTo = -1;
769   }
770   if (nTo == -1)
771     nTo = ME_GetTextLength(editor);
772   TRACE("from %d to %d\n", nStart, nTo);
773   
774   if (dwFormat & SF_RTF || dwFormat & SF_RTFNOOBJS)
775     ME_StreamOutRTF(editor, nStart, nTo - nStart, dwFormat);
776   else if (dwFormat & SF_TEXT || dwFormat & SF_TEXTIZED)
777     ME_StreamOutText(editor, nStart, nTo - nStart, dwFormat);
778   
779   ME_StreamOutFlush(editor);
780   return ME_StreamOutFree(editor);
781 }