diff --git a/dlls/riched20/Makefile.in b/dlls/riched20/Makefile.in index 03602d91916..9cc9fe9844b 100644 --- a/dlls/riched20/Makefile.in +++ b/dlls/riched20/Makefile.in @@ -20,7 +20,8 @@ C_SRCS = \ string.c \ style.c \ undo.c \ - wrap.c + wrap.c \ + writer.c @MAKE_DLL_RULES@ diff --git a/dlls/riched20/editor.c b/dlls/riched20/editor.c index 430b698ff8c..f40819a4122 100644 --- a/dlls/riched20/editor.c +++ b/dlls/riched20/editor.c @@ -461,6 +461,37 @@ static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stre return 0; } + +ME_DisplayItem * +ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset) +{ + ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); + + while (item && item->member.para.next_para->member.para.nCharOfs <= nOffset) + item = ME_FindItemFwd(item, diParagraph); + + if (!item) + return item; + + nOffset -= item->member.para.nCharOfs; + if (nItemType == diParagraph) { + if (nItemOffset) + *nItemOffset = nOffset; + return item; + } + + do { + item = ME_FindItemFwd(item, diRun); + } while (item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nOffset)); + if (item) { + nOffset -= item->member.run.nCharOfs; + if (nItemOffset) + *nItemOffset = nOffset; + } + return item; +} + + ME_TextEditor *ME_MakeEditor(HWND hWnd) { ME_TextEditor *ed = ALLOC_OBJ(ME_TextEditor); HDC hDC; @@ -586,7 +617,6 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP UNSUPPORTED_MSG(EM_SETUNDOLIMIT) UNSUPPORTED_MSG(EM_SETWORDBREAKPROC) UNSUPPORTED_MSG(EM_SETWORDBREAKPROCEX) - UNSUPPORTED_MSG(EM_STREAMOUT) UNSUPPORTED_MSG(WM_SETFONT) UNSUPPORTED_MSG(WM_PASTE) UNSUPPORTED_MSG(WM_STYLECHANGING) @@ -597,6 +627,8 @@ LRESULT WINAPI RichEditANSIWndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lP case EM_STREAMIN: return ME_StreamIn(editor, wParam, (EDITSTREAM*)lParam); + case EM_STREAMOUT: + return ME_StreamOut(editor, wParam, (EDITSTREAM *)lParam); case WM_GETDLGCODE: { UINT code = DLGC_WANTCHARS|DLGC_WANTARROWS; @@ -999,26 +1031,18 @@ int ME_CountParagraphsBetween(ME_TextEditor *editor, int from, int to) return i; } + int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, int bCRLF) { - ME_DisplayItem *item = ME_FindItemFwd(editor->pBuffer->pFirst, diParagraph); + ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart); int nWritten = 0; - while(item && item->member.para.next_para->member.para.nCharOfs <= nStart) - item = ME_FindItemFwd(item, diParagraph); if (!item) { *buffer = L'\0'; return 0; } - nStart -= item->member.para.nCharOfs; - - do { - item = ME_FindItemFwd(item, diRun); - } while(item && (item->member.run.nCharOfs + ME_StrLen(item->member.run.strText) <= nStart)); assert(item); - nStart -= item->member.run.nCharOfs; - if (nStart) { int nLen = ME_StrLen(item->member.run.strText) - nStart; diff --git a/dlls/riched20/editor.h b/dlls/riched20/editor.h index 8749fd2a04f..1c432eda7fa 100644 --- a/dlls/riched20/editor.h +++ b/dlls/riched20/editor.h @@ -206,7 +206,11 @@ void ME_Undo(ME_TextEditor *editor); void ME_Redo(ME_TextEditor *editor); void ME_EmptyUndoStack(ME_TextEditor *editor); int ME_GetTextW(ME_TextEditor *editor, WCHAR *buffer, int nStart, int nChars, BOOL bCRLF); +ME_DisplayItem *ME_FindItemAtOffset(ME_TextEditor *editor, ME_DIType nItemType, int nOffset, int *nItemOffset); extern int me_debug; extern HANDLE me_heap; extern void DoWrap(ME_TextEditor *editor); + +/* writer.c */ +LRESULT ME_StreamOut(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream); diff --git a/dlls/riched20/editstr.h b/dlls/riched20/editstr.h index d1f48198303..92acb128b33 100644 --- a/dlls/riched20/editstr.h +++ b/dlls/riched20/editstr.h @@ -192,6 +192,27 @@ typedef enum { umAddBackToUndo } ME_UndoMode; +typedef struct tagME_FontTableItem { + BYTE bCharSet; + WCHAR *szFaceName; +} ME_FontTableItem; + +#define STREAMOUT_BUFFER_SIZE 4096 +#define STREAMOUT_FONTTBL_SIZE 8192 +#define STREAMOUT_COLORTBL_SIZE 1024 + +typedef struct tagME_OutStream { + EDITSTREAM *stream; + char buffer[STREAMOUT_BUFFER_SIZE]; + UINT pos, written; + UINT nCodePage; + UINT nFontTblLen; + ME_FontTableItem fonttbl[STREAMOUT_FONTTBL_SIZE]; + UINT nColorTblLen; + COLORREF colortbl[STREAMOUT_COLORTBL_SIZE]; + UINT nDefaultFont; +} ME_OutStream; + typedef struct tagME_FontCacheItem { LOGFONTW lfSpecs; @@ -224,6 +245,7 @@ typedef struct tagME_TextEditor int nParagraphs; int nLastSelStart, nLastSelEnd; ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE]; + ME_OutStream *pStream; } ME_TextEditor; typedef struct tagME_Context diff --git a/dlls/riched20/writer.c b/dlls/riched20/writer.c new file mode 100644 index 00000000000..0881992aa9e --- /dev/null +++ b/dlls/riched20/writer.c @@ -0,0 +1,648 @@ +/* + * RichEdit - RTF writer module + * + * Copyright 2005 by Phil Krylov + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "editor.h" + +WINE_DEFAULT_DEBUG_CHANNEL(richedit); + + +static void +ME_StreamOutInit(ME_TextEditor *editor, EDITSTREAM *stream) +{ + editor->pStream = ALLOC_OBJ(ME_OutStream); + editor->pStream->stream = stream; + editor->pStream->pos = 0; + editor->pStream->written = 0; + editor->pStream->nFontTblLen = 0; + editor->pStream->nColorTblLen = 1; +} + + +static BOOL +ME_StreamOutFlush(ME_TextEditor *editor) +{ + LONG nStart = 0; + LONG nWritten = 0; + EDITSTREAM *stream = editor->pStream->stream; + + do { + stream->dwError = stream->pfnCallback(stream->dwCookie, editor->pStream->buffer + nStart, + editor->pStream->pos - nStart, &nWritten); + if (nWritten == 0 || stream->dwError) + return FALSE; + editor->pStream->written += nWritten; + nStart += nWritten; + } while (nStart < editor->pStream->pos); + editor->pStream->pos = 0; + return TRUE; +} + + +static LONG +ME_StreamOutFree(ME_TextEditor *editor) +{ + LONG written = editor->pStream->written; + + FREE_OBJ(editor->pStream); + editor->pStream = NULL; + return written; +} + + +static BOOL +ME_StreamOutMove(ME_TextEditor *editor, BYTE *buffer, int len) +{ + ME_OutStream *pStream = editor->pStream; + + while (len) { + int space = STREAMOUT_BUFFER_SIZE - pStream->pos; + int fit = min(space, len); + + TRACE("%u:%u:%.*s\n", pStream->pos, fit, fit, buffer); + memmove(pStream->buffer + pStream->pos, buffer, fit); + len -= fit; + buffer += fit; + pStream->pos += fit; + if (pStream->pos == STREAMOUT_BUFFER_SIZE) { + if (!ME_StreamOutFlush(editor)) + return FALSE; + } + } + return TRUE; +} + + +static BOOL +ME_StreamOutPrint(ME_TextEditor *editor, char *format, ...) +{ + char string[STREAMOUT_BUFFER_SIZE]; /* This is going to be enough */ + int len; + va_list valist; + + va_start(valist, format); + len = vsnprintf(string, sizeof(string), format, valist); + va_end(valist); + + return ME_StreamOutMove(editor, string, len); +} + + +static BOOL +ME_StreamOutRTFHeader(ME_TextEditor *editor, int dwFormat) +{ + char *cCharSet; + UINT nCodePage; + LANGID language; + BOOL success; + + if (dwFormat & SF_USECODEPAGE) { + CPINFOEXW info; + + switch (HIWORD(dwFormat)) { + case CP_ACP: + cCharSet = "ansi"; + nCodePage = GetACP(); + break; + case CP_OEMCP: + nCodePage = GetOEMCP(); + if (nCodePage == 437) + cCharSet = "pc"; + else if (nCodePage == 850) + cCharSet = "pca"; + else + cCharSet = "ansi"; + break; + case CP_UTF8: + nCodePage = CP_UTF8; + break; + default: + if (HIWORD(dwFormat) == CP_MACCP) { + cCharSet = "mac"; + nCodePage = 10000; /* MacRoman */ + } else { + cCharSet = "ansi"; + nCodePage = 1252; /* Latin-1 */ + } + if (GetCPInfoExW(HIWORD(dwFormat), 0, &info)) + nCodePage = info.CodePage; + } + } else { + cCharSet = "ansi"; + nCodePage = GetACP(); + } + if (nCodePage == CP_UTF8) + success = ME_StreamOutPrint(editor, "{\\urtf"); + else + success = ME_StreamOutPrint(editor, "{\\rtf1\\%s\\ansicpg%u\\uc1", cCharSet, nCodePage); + + if (!success) + return FALSE; + + editor->pStream->nCodePage = nCodePage; + + /* FIXME: This should be a document property */ + /* TODO: handle SFF_PLAINRTF */ + language = GetUserDefaultLangID(); + if (!ME_StreamOutPrint(editor, "\\deff0\\deflang%u\\deflangfe%u", language, language)) + return FALSE; + + /* FIXME: This should be a document property */ + editor->pStream->nDefaultFont = 0; + + return TRUE; +} + + +static BOOL +ME_StreamOutRTFFontAndColorTbl(ME_TextEditor *editor, ME_DisplayItem *pFirstRun, LONG to) +{ + ME_DisplayItem *item = pFirstRun; + ME_DisplayItem *pLastRun = ME_FindItemAtOffset(editor, diRun, to, NULL); + ME_FontTableItem *table = editor->pStream->fonttbl; + int i; + + do { + CHARFORMAT2W *fmt = &item->member.run.style->fmt; + COLORREF crColor; + + if (fmt->dwMask & CFM_FACE) { + WCHAR *face = fmt->szFaceName; + BYTE bCharSet = fmt->bCharSet; + + for (i = 0; i < editor->pStream->nFontTblLen; i++) + if (table[i].bCharSet == bCharSet + && (table[i].szFaceName == face || !lstrcmpW(table[i].szFaceName, face))) + break; + if (i == editor->pStream->nFontTblLen) { + table[i].bCharSet = bCharSet; + table[i].szFaceName = face; + editor->pStream->nFontTblLen++; + } + } + + if (fmt->dwMask & CFM_COLOR && !(fmt->dwEffects & CFE_AUTOCOLOR)) { + crColor = fmt->crTextColor; + for (i = 1; i < editor->pStream->nColorTblLen; i++) + if (editor->pStream->colortbl[i] == crColor) + break; + if (i == editor->pStream->nColorTblLen) { + editor->pStream->colortbl[i] = crColor; + editor->pStream->nColorTblLen++; + } + } + if (fmt->dwMask & CFM_BACKCOLOR && !(fmt->dwEffects & CFE_AUTOBACKCOLOR)) { + crColor = fmt->crBackColor; + for (i = 1; i < editor->pStream->nColorTblLen; i++) + if (editor->pStream->colortbl[i] == crColor) + break; + if (i == editor->pStream->nColorTblLen) { + editor->pStream->colortbl[i] = crColor; + editor->pStream->nColorTblLen++; + } + } + + if (item == pLastRun) + break; + item = ME_FindItemFwd(item, diRun); + } while (item); + + if (!ME_StreamOutPrint(editor, "{\\fonttbl")) + return FALSE; + + for (i = 0; i < editor->pStream->nFontTblLen; i++) { + char szFaceName[LF_FACESIZE]; + + /* FIXME: Use ME_StreamOutText to emit the font name */ + WideCharToMultiByte(editor->pStream->nCodePage, 0, table[i].szFaceName, -1, + szFaceName, LF_FACESIZE, NULL, NULL); + if (table[i].bCharSet) { + if (!ME_StreamOutPrint(editor, "{\\f%u\\fcharset%u %s;}\r\n", + i, table[i].bCharSet, szFaceName)) + return FALSE; + } else { + if (!ME_StreamOutPrint(editor, "{\\f%u %s;}\r\n", i, szFaceName)) + return FALSE; + } + } + if (!ME_StreamOutPrint(editor, "}")) + return FALSE; + + /* Output colors table if not empty */ + if (editor->pStream->nColorTblLen > 1) { + if (!ME_StreamOutPrint(editor, "{\\colortbl;")) + return FALSE; + for (i = 1; i < editor->pStream->nColorTblLen; i++) { + if (!ME_StreamOutPrint(editor, "\\red%u\\green%u\\blue%u;", + editor->pStream->colortbl[i] & 0xFF, + (editor->pStream->colortbl[i] >> 8) & 0xFF, + (editor->pStream->colortbl[i] >> 16) & 0xFF)) + return FALSE; + } + if (!ME_StreamOutPrint(editor, "}")) + return FALSE; + } + + return TRUE; +} + + +static BOOL +ME_StreamOutRTFParaProps(ME_TextEditor *editor, ME_DisplayItem *para) +{ + char *keyword = NULL; + + /* TODO: Don't emit anything if the last PARAFORMAT2 is inherited */ + if (!ME_StreamOutPrint(editor, "\\pard")) + return FALSE; + + switch (para->member.para.pFmt->wAlignment) { + case PFA_LEFT: + /* Default alignment: not emitted */ + break; + case PFA_RIGHT: + keyword = "\\qr"; + break; + case PFA_CENTER: + keyword = "\\qc"; + break; + case PFA_JUSTIFY: + keyword = "\\qj"; + break; + } + if (keyword && !ME_StreamOutPrint(editor, keyword)) + return FALSE; + + /* TODO: Other properties */ + return TRUE; +} + + +static BOOL +ME_StreamOutRTFCharProps(ME_TextEditor *editor, CHARFORMAT2W *fmt) +{ + char props[STREAMOUT_BUFFER_SIZE] = ""; + int i; + + if (fmt->dwMask & CFM_ALLCAPS && fmt->dwEffects & CFE_ALLCAPS) + strcat(props, "\\caps"); + if (fmt->dwMask & CFM_ANIMATION) + sprintf(props + strlen(props), "\\animtext%u", fmt->bAnimation); + if (fmt->dwMask & CFM_BACKCOLOR) { + if (!(fmt->dwEffects & CFE_AUTOBACKCOLOR)) { + for (i = 1; i < editor->pStream->nColorTblLen; i++) + if (editor->pStream->colortbl[i] == fmt->crBackColor) { + sprintf(props + strlen(props), "\\cb%u", i); + break; + } + } + } + if (fmt->dwMask & CFM_BOLD && fmt->dwEffects & CFE_BOLD) + strcat(props, "\\b"); + if (fmt->dwMask & CFM_COLOR) { + if (!(fmt->dwEffects & CFE_AUTOCOLOR)) { + for (i = 1; i < editor->pStream->nColorTblLen; i++) + if (editor->pStream->colortbl[i] == fmt->crTextColor) { + sprintf(props + strlen(props), "\\cf%u", i); + break; + } + } + } + /* TODO: CFM_DISABLED */ + if (fmt->dwMask & CFM_EMBOSS && fmt->dwEffects & CFE_EMBOSS) + strcat(props, "\\embo"); + if (fmt->dwMask & CFM_HIDDEN && fmt->dwEffects & CFE_HIDDEN) + strcat(props, "\\v"); + if (fmt->dwMask & CFM_IMPRINT && fmt->dwEffects & CFE_IMPRINT) + strcat(props, "\\impr"); + if (fmt->dwMask & CFM_ITALIC && fmt->dwEffects & CFE_ITALIC) + strcat(props, "\\i"); + if (fmt->dwMask & CFM_KERNING) + sprintf(props + strlen(props), "\\kerning%u", fmt->wKerning); + if (fmt->dwMask & CFM_LCID) { + /* TODO: handle SFF_PLAINRTF */ + if (LOWORD(fmt->lcid) == 1024) + strcat(props, "\\noproof\\lang1024\\langnp1024\\langfe1024\\langfenp1024"); + else + sprintf(props + strlen(props), "\\lang%u", LOWORD(fmt->lcid)); + } + /* CFM_LINK is not streamed out by M$ */ + if (fmt->dwMask & CFM_OFFSET) { + if (fmt->yOffset >= 0) + sprintf(props + strlen(props), "\\up%ld", fmt->yOffset); + else + sprintf(props + strlen(props), "\\dn%ld", -fmt->yOffset); + } + if (fmt->dwMask & CFM_OUTLINE && fmt->dwEffects & CFE_OUTLINE) + strcat(props, "\\outl"); + if (fmt->dwMask & CFM_PROTECTED && fmt->dwEffects & CFE_PROTECTED) + strcat(props, "\\protect"); + /* TODO: CFM_REVISED CFM_REVAUTHOR - probably using rsidtbl? */ + if (fmt->dwMask & CFM_SHADOW && fmt->dwEffects & CFE_SHADOW) + strcat(props, "\\shad"); + if (fmt->dwMask & CFM_SIZE && fmt->yHeight / 10 != 24) + sprintf(props + strlen(props), "\\fs%ld", fmt->yHeight / 10); + if (fmt->dwMask & CFM_SMALLCAPS && fmt->dwEffects & CFE_SMALLCAPS) + strcat(props, "\\scaps"); + if (fmt->dwMask & CFM_SPACING) + sprintf(props + strlen(props), "\\expnd%u\\expndtw%u", fmt->sSpacing / 5, fmt->sSpacing); + if (fmt->dwMask & CFM_STRIKEOUT && fmt->dwEffects & CFE_STRIKEOUT) + strcat(props, "\\strike"); + if (fmt->dwMask & CFM_STYLE) { + sprintf(props + strlen(props), "\\cs%u", fmt->sStyle); + /* TODO: emit style contents here */ + } + if (fmt->dwMask & (CFM_SUBSCRIPT | CFM_SUPERSCRIPT)) { + if (fmt->dwEffects & CFE_SUBSCRIPT) + strcat(props, "\\sub"); + else if (fmt->dwEffects & CFE_SUPERSCRIPT) + strcat(props, "\\super"); + } + if (fmt->dwMask & CFM_UNDERLINE || fmt->dwMask & CFM_UNDERLINETYPE) { + if (fmt->dwMask & CFM_UNDERLINETYPE) + switch (fmt->bUnderlineType) { + case CFU_CF1UNDERLINE: + case CFU_UNDERLINE: + strcat(props, "\\ul"); + break; + case CFU_UNDERLINEDOTTED: + strcat(props, "\\uld"); + break; + case CFU_UNDERLINEDOUBLE: + strcat(props, "\\uldb"); + break; + case CFU_UNDERLINEWORD: + strcat(props, "\\ulw"); + break; + case CFU_UNDERLINENONE: + default: + strcat(props, "\\ul0"); + break; + } + else if (fmt->dwEffects & CFE_UNDERLINE) + strcat(props, "\\ul"); + } + /* FIXME: How to emit CFM_WEIGHT? */ + + if (fmt->dwMask & CFM_FACE || fmt->dwMask & CFM_CHARSET) { + WCHAR *szFaceName; + + if (fmt->dwMask & CFM_FACE) + szFaceName = fmt->szFaceName; + else + szFaceName = editor->pStream->fonttbl[0].szFaceName; + for (i = 0; i < editor->pStream->nFontTblLen; i++) { + if (szFaceName == editor->pStream->fonttbl[i].szFaceName + || !lstrcmpW(szFaceName, editor->pStream->fonttbl[i].szFaceName)) + if (!(fmt->dwMask & CFM_CHARSET) + || fmt->bCharSet == editor->pStream->fonttbl[i].bCharSet) + break; + } + if (i < editor->pStream->nFontTblLen && i != editor->pStream->nDefaultFont) + sprintf(props + strlen(props), "\\f%u", i); + } + if (*props) + strcat(props, " "); + if (!ME_StreamOutPrint(editor, props)) + return FALSE; + return TRUE; +} + + +static BOOL +ME_StreamOutRTFText(ME_TextEditor *editor, WCHAR *text, LONG nChars) +{ + char buffer[STREAMOUT_BUFFER_SIZE]; + int pos = 0; + int fit, i; + + while (nChars) { + if (editor->pStream->nCodePage == CP_UTF8) { + /* 6 is the maximum character length in UTF-8 */ + fit = min(nChars, STREAMOUT_BUFFER_SIZE / 6); + WideCharToMultiByte(CP_UTF8, 0, text, fit, buffer, STREAMOUT_BUFFER_SIZE, + NULL, NULL); + nChars -= fit; + text += fit; + for (i = 0; buffer[i]; i++) + if (buffer[i] == '{' || buffer[i] == '}' || buffer[i] == '\\') { + if (!ME_StreamOutPrint(editor, "%.*s\\", i - pos, buffer + pos)) + return FALSE; + pos = i; + } + if (!pos) + if (!ME_StreamOutPrint(editor, "%s", buffer + pos)) + return FALSE; + pos = 0; + } else if (*text < 128) { + if (*text == '{' || *text == '}' || *text == '\\') + buffer[pos++] = '\\'; + buffer[pos++] = (char)(*text++); + nChars--; + } else { + BOOL unknown; + BYTE letter[2]; + + WideCharToMultiByte(editor->pStream->nCodePage, 0, text, 1, letter, 2, + NULL, &unknown); + if (unknown) + pos += sprintf(buffer + pos, "\\u%d?", (int)*text); + else if (*letter < 128) { + if (*letter == '{' || *letter == '}' || *letter == '\\') + buffer[pos++] = '\\'; + buffer[pos++] = *letter; + } else + pos += sprintf(buffer + pos, "\\'%02x", *letter); + text++; + nChars--; + } + if (pos >= STREAMOUT_BUFFER_SIZE - 10) { + if (!ME_StreamOutMove(editor, buffer, pos)) + return FALSE; + pos = 0; + } + } + return ME_StreamOutMove(editor, buffer, pos); +} + + +static BOOL +ME_StreamOutRTF(ME_TextEditor *editor, int nStart, int nChars, int dwFormat) +{ + int nTo; + ME_DisplayItem *para = ME_FindItemAtOffset(editor, diParagraph, nStart, NULL); + ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart); + ME_DisplayItem *last_item = ME_FindItemAtOffset(editor, diRun, nStart + nChars, &nTo); + + if (!ME_StreamOutRTFHeader(editor, dwFormat)) + return FALSE; + + if (!ME_StreamOutRTFFontAndColorTbl(editor, item, nStart + nChars)) + return FALSE; + + /* TODO: stylesheet table */ + + /* FIXME: maybe emit something smarter for the generator? */ + if (!ME_StreamOutPrint(editor, "{\\*\\generator Wine Riched20 2.0.????;}")) + return FALSE; + + /* TODO: information group */ + + /* TODO: document formatting properties */ + + /* FIXME: We have only one document section */ + + /* TODO: section formatting properties */ + + while (para) { + ME_DisplayItem *p; + int nLen; + + if (!ME_StreamOutRTFParaProps(editor, para)) + return FALSE; + + if (!item) { + item = ME_FindItemFwd(para, diRun); + nStart = 0; + } + for (p = item; p && p != para->member.para.next_para; p = p->next) { + TRACE("type %d\n", p->type); + if (p->type == diRun) { + TRACE("flags %xh\n", p->member.run.nFlags); + if (p->member.run.nFlags & MERF_ENDPARA) { + if (!ME_StreamOutPrint(editor, "\r\n\\par")) + return FALSE; + nChars--; + } else { + if (!ME_StreamOutPrint(editor, "{")) + return FALSE; + TRACE("style %p\n", p->member.run.style); + if (!ME_StreamOutRTFCharProps(editor, &p->member.run.style->fmt)) + return FALSE; + + /* TODO: emit embedded objects as well as text */ + nLen = ME_StrLen(p->member.run.strText) - nStart; + if (!ME_StreamOutRTFText(editor, p->member.run.strText->szData + nStart, + (p == last_item ? nTo - nStart : nLen))) + return FALSE; + if (!ME_StreamOutPrint(editor, "}")) + return FALSE; + } + } + if (p == last_item) + break; + } + para = para->member.para.next_para; + item = NULL; + if (p == last_item) + break; + } + if (!ME_StreamOutPrint(editor, "}")) + return FALSE; + return TRUE; +} + + +static BOOL +ME_StreamOutText(ME_TextEditor *editor, int nStart, int nChars, DWORD dwFormat) +{ + ME_DisplayItem *item = ME_FindItemAtOffset(editor, diRun, nStart, &nStart); + int nLen; + UINT nCodePage = CP_ACP; + BYTE *buffer = NULL; + int nBufLen = 0; + BOOL success = TRUE; + + if (!item) + return FALSE; + + if (dwFormat & SF_USECODEPAGE) + nCodePage = HIWORD(dwFormat); + + /* TODO: Handle SF_TEXTIZED */ + + while (success && nChars && item) { + nLen = ME_StrLen(item->member.run.strText) - nStart; + if (nLen > nChars) + nLen = nChars; + + if (item->member.run.nFlags & MERF_ENDPARA) { + WCHAR szEOL[] = { '\r', '\n' }; + + if (dwFormat & SF_UNICODE) + success = ME_StreamOutMove(editor, (BYTE *)szEOL, 4); + else + success = ME_StreamOutMove(editor, "\r\n", 2); + } else { + if (dwFormat & SF_UNICODE) + success = ME_StreamOutMove(editor, (BYTE *)(item->member.run.strText->szData + nStart), + sizeof(WCHAR) * nLen); + else { + int nSize; + + nSize = WideCharToMultiByte(nCodePage, 0, item->member.run.strText->szData + nStart, + nLen, NULL, 0, NULL, NULL); + if (nSize > nBufLen) { + if (buffer) + FREE_OBJ(buffer); + buffer = ALLOC_N_OBJ(BYTE, nSize); + nBufLen = nSize; + } + WideCharToMultiByte(nCodePage, 0, item->member.run.strText->szData + nStart, + nLen, buffer, nSize, NULL, NULL); + success = ME_StreamOutMove(editor, buffer, nSize - 1); + } + } + + nChars -= nLen; + nStart = 0; + item = ME_FindItemFwd(item, diRun); + } + + if (buffer) + FREE_OBJ(buffer); + return success; +} + + +LRESULT +ME_StreamOut(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream) +{ + int nStart, nTo; + + ME_StreamOutInit(editor, stream); + + if (dwFormat & SFF_SELECTION) + ME_GetSelection(editor, &nStart, &nTo); + else { + nStart = 0; + nTo = -1; + } + if (nTo == -1) + nTo = ME_GetTextLength(editor); + TRACE("from %d to %d\n", nStart, nTo); + + if (dwFormat & SF_RTF || dwFormat & SF_RTFNOOBJS) + ME_StreamOutRTF(editor, nStart, nTo - nStart, dwFormat); + else if (dwFormat & SF_TEXT || dwFormat & SF_TEXTIZED) + ME_StreamOutText(editor, nStart, nTo - nStart, dwFormat); + + ME_StreamOutFlush(editor); + return ME_StreamOutFree(editor); +}