string.c \
style.c \
undo.c \
- wrap.c
+ wrap.c \
+ writer.c
@MAKE_DLL_RULES@
{
row = ME_FindItemBack(tmp, diStartRow);
pSizeRun = run = tmp;
- sz = ME_GetRunSize(&c, &run->member.run, ME_StrLen(run->member.run.strText));
+ sz = ME_GetRunSize(&c, ¶->member.para, &run->member.run, ME_StrLen(run->member.run.strText));
}
}
if (pCursor->nOffset && !(run->member.run.nFlags & MERF_SKIPPED)) {
- sz = ME_GetRunSize(&c, &run->member.run, pCursor->nOffset);
+ sz = ME_GetRunSize(&c, ¶->member.para, &run->member.run, pCursor->nOffset);
}
CreateCaret(editor->hWnd, NULL, 0, pSizeRun->member.run.nAscent+pSizeRun->member.run.nDescent);
SetCaretPos(run->member.run.pt.x+sz.cx,
- para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-GetScrollPos(editor->hWnd, SB_VERT));
+ para->member.para.nYPos+row->member.row.nBaseline+pSizeRun->member.run.pt.y-pSizeRun->member.run.nAscent-ME_GetYScrollPos(editor));
} else {
assert(0 == "Wrapped paragraph run without a row?");
CreateCaret(editor->hWnd, NULL, 0, 10);
assert(style);
editor->bCaretAtEnd = FALSE;
- /*
- if (!style)
- style = ME_GetInsertStyle(editor, nCursor);
- else
- ME_AddRefStyle(style);
- */
ME_AddRefStyle(style);
/* FIXME really HERE ? */
len = lstrlenW(str);
pos = str;
/* FIXME this sucks - no respect for unicode (what else can be a line separator in unicode?) */
- while(pos-str < len && *pos != '\r' && *pos != '\n')
+ while(pos-str < len && *pos != '\r' && *pos != '\n' && *pos != '\t')
pos++;
- /* handle EOLs */
- if (pos-str < len) {
+ if (pos-str < len && *pos == '\t') { /* handle tabs */
+ ME_DisplayItem *pNewRun = NULL;
+ WCHAR tab = '\t';
+
+ if (pos!=str)
+ ME_InsertTextFromCursor(editor, nCursor, str, pos-str, style);
+
+ p = &editor->pCursors[nCursor];
+ assert(style);
+ assert(p->pRun->type == diRun);
+ pNewRun = ME_MakeRun(style, ME_MakeStringN(&tab, 1), MERF_TAB); /* addrefs style */
+ ME_InsertRun(editor, ME_CharOfsFromRunOfs(editor, p->pRun, p->nOffset), pNewRun);
+ ME_DestroyDisplayItem(pNewRun);
+ ME_ReleaseStyle(style);
+
+ pos++;
+ if(pos-str < len) {
+ ME_InsertTextFromCursor(editor, nCursor, pos, len-(pos-str), style);
+ }
+ return;
+ }
+ if (pos-str < len) { /* handle EOLs */
ME_DisplayItem *tp, *end_run;
ME_Paragraph *para;
ME_Style *tmp_style;
}
assert(0);
}
+ return FALSE;
}
BOOL ME_ArrowRight(ME_TextEditor *editor, ME_Cursor *p)
editor->nUDArrowX = -1;
- y += GetScrollPos(editor->hWnd, SB_VERT);
+ y += ME_GetYScrollPos(editor);
tmp_cursor = editor->pCursors[0];
is_selection = ME_IsSelection(editor);
{
ME_Cursor tmp_cursor;
- y += GetScrollPos(editor->hWnd, SB_VERT);
+ y += ME_GetYScrollPos(editor);
tmp_cursor = editor->pCursors[0];
if (!ME_FindPixelPos(editor, x, y, &editor->pCursors[0], &editor->bCaretAtEnd))
assert(pCursor->pRun->type == diRun);
}
+void ME_ArrowPageUp(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRun = pCursor->pRun;
+ ME_DisplayItem *pLast, *p;
+ int x, y, ys, yd, yp, yprev;
+ ME_Cursor tmp_curs = *pCursor;
+
+ x = ME_GetXForArrow(editor, pCursor);
+ if (!pCursor->nOffset && editor->bCaretAtEnd)
+ pRun = ME_FindItemBack(pRun, diRun);
+
+ p = ME_FindItemBack(pRun, diStartRowOrParagraph);
+ assert(p->type == diStartRow);
+ yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos;
+ yprev = ys = y = yp + p->member.row.nYPos;
+ yd = y - editor->sizeWindow.cy;
+ pLast = p;
+
+ do {
+ p = ME_FindItemBack(p, diStartRowOrParagraph);
+ if (!p)
+ break;
+ if (p->type == diParagraph) { /* crossing paragraphs */
+ if (p->member.para.prev_para == NULL)
+ break;
+ yp = p->member.para.prev_para->member.para.nYPos;
+ continue;
+ }
+ y = yp + p->member.row.nYPos;
+ if (y < yd)
+ break;
+ pLast = p;
+ yprev = y;
+ } while(1);
+
+ pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+ ME_UpdateSelection(editor, &tmp_curs);
+ if (yprev < editor->sizeWindow.cy)
+ {
+ ME_EnsureVisible(editor, ME_FindItemFwd(editor->pBuffer->pFirst, diRun));
+ ME_Repaint(editor);
+ }
+ else {
+ ME_Scroll(editor, 0, ys-yprev);
+ ME_Repaint(editor);
+ }
+ assert(pCursor->pRun);
+ assert(pCursor->pRun->type == diRun);
+}
+
+/* FIXME: in the original RICHEDIT, PageDown always scrolls by the same amount
+ of pixels, even if it makes the scroll bar position exceed its normal maximum.
+ In such a situation, clicking the scrollbar restores its position back to the
+ normal range (ie. sets it to (doclength-screenheight)). */
+
+void ME_ArrowPageDown(ME_TextEditor *editor, ME_Cursor *pCursor)
+{
+ ME_DisplayItem *pRun = pCursor->pRun;
+ ME_DisplayItem *pLast, *p;
+ int x, y, ys, yd, yp, yprev;
+ ME_Cursor tmp_curs = *pCursor;
+
+ x = ME_GetXForArrow(editor, pCursor);
+ if (!pCursor->nOffset && editor->bCaretAtEnd)
+ pRun = ME_FindItemBack(pRun, diRun);
+
+ p = ME_FindItemBack(pRun, diStartRowOrParagraph);
+ assert(p->type == diStartRow);
+ yp = ME_FindItemBack(p, diParagraph)->member.para.nYPos;
+ yprev = ys = y = yp + p->member.row.nYPos;
+ yd = y + editor->sizeWindow.cy;
+ pLast = p;
+
+ do {
+ p = ME_FindItemFwd(p, diStartRowOrParagraph);
+ if (!p)
+ break;
+ if (p->type == diParagraph) {
+ yp = p->member.para.nYPos;
+ continue;
+ }
+ y = yp + p->member.row.nYPos;
+ if (y >= yd)
+ break;
+ pLast = p;
+ yprev = y;
+ } while(1);
+
+ pCursor->pRun = ME_FindRunInRow(editor, pLast, x, &pCursor->nOffset, &editor->bCaretAtEnd);
+ ME_UpdateSelection(editor, &tmp_curs);
+ if (yprev >= editor->nTotalLength-editor->sizeWindow.cy)
+ {
+ ME_EnsureVisible(editor, ME_FindItemBack(editor->pBuffer->pLast, diRun));
+ ME_Repaint(editor);
+ }
+ else {
+ ME_Scroll(editor, 0, ys-yprev);
+ ME_Repaint(editor);
+ }
+ assert(pCursor->pRun);
+ assert(pCursor->pRun->type == diRun);
+}
+
void ME_ArrowHome(ME_TextEditor *editor, ME_Cursor *pCursor)
{
ME_DisplayItem *pRow = ME_FindItemBack(pCursor->pRun, diStartRow);
editor->pCursors[1] = editor->pCursors[0];
else
editor->pCursors[0] = editor->pCursors[1];
- /* FIXME optimize */
- ME_MarkAllForWrapping(editor);
ME_Repaint(editor);
return TRUE;
}
-void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
+BOOL ME_UpdateSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
{
ME_Cursor old_anchor = editor->pCursors[1];
- BOOL bRedraw = FALSE;
- bRedraw = memcmp(&editor->pCursors[0], &editor->pCursors[1], sizeof(ME_Cursor));
-
- if (bRedraw)
- {
- /* FIXME optimize */
- ME_MarkAllForWrapping(editor);
- }
if (GetKeyState(VK_SHIFT)>=0) /* cancelling selection */
{
/* any selection was present ? if so, it's no more, repaint ! */
editor->pCursors[1] = editor->pCursors[0];
if (memcmp(pTempCursor, &old_anchor, sizeof(ME_Cursor))) {
- ME_Repaint(editor);
- return;
+ return TRUE;
}
- return;
+ return FALSE;
}
else
{
if (!memcmp(pTempCursor, &editor->pCursors[1], sizeof(ME_Cursor))) /* starting selection */
{
editor->pCursors[1] = *pTempCursor;
+ return TRUE;
}
}
ME_Repaint(editor);
+ return TRUE;
+}
+
+void ME_RepaintSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor)
+{
+ if (ME_UpdateSelection(editor, pTempCursor)) {
+ ME_EnsureVisible(editor, editor->pCursors[0].pRun);
+ ME_Repaint(editor);
+ }
}
void ME_DeleteSelection(ME_TextEditor *editor)
ME_RepaintSelection(editor, &tmp_curs);
ME_SendSelChange(editor);
return TRUE;
+ case VK_PRIOR:
+ ME_ArrowPageUp(editor, p);
+ ME_ClearTempStyle(editor);
+ ME_SendSelChange(editor);
+ return TRUE;
+ case VK_NEXT:
+ ME_ArrowPageDown(editor, p);
+ ME_ClearTempStyle(editor);
+ ME_SendSelChange(editor);
+ return TRUE;
}
editor->nUDArrowX = -1;
}
if (ME_ArrowLeft(editor, p)) {
editor->bCaretAtEnd = FALSE; /* FIXME or maybe not */
+ ME_ClearTempStyle(editor);
ME_MoveCaret(editor);
ME_DeleteTextAtCursor(editor, nCursor, 1);
ME_UpdateRepaint(editor);
if (ME_IsSelection(editor))
{
ME_DeleteSelection(editor);
+ ME_ClearTempStyle(editor);
ME_UpdateRepaint(editor);
return TRUE;
}
ME_DeleteTextAtCursor(editor, nCursor, 1);
+ ME_ClearTempStyle(editor);
ME_UpdateRepaint(editor);
return TRUE;
}
Messages (ANSI versions not done yet)
- EM_AUTOURLDETECT 2.0
- - EM_CANPASTE
+ + EM_CANPASTE
+ EM_CANREDO 2.0
+ EM_CANUNDO
- EM_CHARFROMPOS
- EM_LINESCROLL
- EM_PASTESPECIAL
- EM_POSFROMCHARS
- - EM_REDO 2.0
+ + EM_REDO 2.0
- EM_REQUESTRESIZE
+ EM_REPLACESEL (proper style?) ANSI&Unicode
- EM_SCROLL
- EM_SETWORDBREAKPROCEX
- EM_SETWORDWRAPMODE 1.0asian
- EM_STOPGROUPTYPING 2.0
- - EM_STREAMIN
- - EM_STREAMOUT
- - EM_UNDO
+ + EM_STREAMIN (can't fall back to text when the RTF isn't really RTF)
+ + EM_STREAMOUT
+ + EM_UNDO
+ WM_CHAR
+ WM_CLEAR
- - WM_COPY (lame implementation, no RTF support)
- - WM_CUT (lame implementation, no RTF support)
+ + WM_COPY
+ + WM_CUT
+ WM_GETDLGCODE (the current implementation is incomplete)
+ WM_GETTEXT (ANSI&Unicode)
+ WM_GETTEXTLENGTH (ANSI version sucks)
- - WM_PASTE
+ + WM_PASTE
- WM_SETFONT
+ WM_SETTEXT (resets undo stack !) (proper style?) ANSI&Unicode
- WM_STYLECHANGING
/*
* RICHED20 TODO (incomplete):
*
- * - font caching
+ * - messages/styles/notifications listed above
+ * - Undo coalescing
* - add remaining CHARFORMAT/PARAFORMAT fields
* - right/center align should strip spaces from the beginning
- * - more advanced navigation (Ctrl-arrows, PageUp/PageDn)
+ * - more advanced navigation (Ctrl-arrows)
* - tabs
- * - pictures (not just smiling faces that lack API support ;-) )
- * - OLE objects
+ * - pictures/OLE objects (not just smiling faces that lack API support ;-) )
+ * - COM interface (looks like a major pain in the TODO list)
* - calculate heights of pictures (half-done)
- * - EM_STREAMIN/EM_STREAMOUT
* - horizontal scrolling (not even started)
- * - fix scrollbars and refresh (it sucks bigtime)
* - hysteresis during wrapping (related to scrollbars appearing/disappearing)
- * - should remember maximum row width for wrap hysteresis
* - find/replace
* - how to implement EM_FORMATRANGE and EM_DISPLAYBAND ? (Mission Impossible)
- * - italic cursor with italic fonts
+ * - italic caret with italic fonts
* - IME
* - most notifications aren't sent at all (the most important ones are)
* - when should EN_SELCHANGE be sent after text change ? (before/after EN_UPDATE?)
* - WM_SETTEXT may use wrong style (but I'm 80% sure it's OK)
* - EM_GETCHARFORMAT with SCF_SELECTION may not behave 100% like in original (but very close)
- * - bugs in end-of-text handling (the gray bar) could get me in jail ;-)
- * - determination of row size
- * - end-of-paragraph marks should be of reasonable size
*
* Bugs that are probably fixed, but not so easy to verify:
* - EN_UPDATE/EN_CHANGE are handled very incorrectly (should be OK now)
*/
#include "editor.h"
-#include <ole2.h>
-#include <richole.h>
-#include <winreg.h>
+#include "ole2.h"
+#include "richole.h"
+#include "winreg.h"
#define NO_SHLWAPI_STREAM
-#include <shlwapi.h>
+#include "shlwapi.h"
#include "rtf.h"
return buf;
}
-#define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
-static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, EDITSTREAM *stream, ME_Style *style)
+static LRESULT ME_StreamInText(ME_TextEditor *editor, DWORD dwFormat, ME_InStream *stream, ME_Style *style)
{
- BYTE buffer[STREAMIN_BUFFER_SIZE+1];
WCHAR wszText[STREAMIN_BUFFER_SIZE+1];
+ WCHAR *pText;
TRACE("%08lx %p\n", dwFormat, stream);
- stream->dwError = 0;
do {
- long nDataSize = 0, nWideChars = 0;
- stream->dwError = stream->pfnCallback(stream->dwCookie,
- (dwFormat & SF_UNICODE ? (BYTE *)wszText : buffer),
- STREAMIN_BUFFER_SIZE, &nDataSize);
-
- if (stream->dwError)
- break;
- if (!nDataSize)
- break;
+ long nWideChars = 0;
+
+ if (!stream->dwSize)
+ {
+ ME_StreamInFill(stream);
+ if (stream->editstream->dwError)
+ break;
+ if (!stream->dwSize)
+ break;
+ }
if (!(dwFormat & SF_UNICODE))
{
/* FIXME? this is doomed to fail on true MBCS like UTF-8, luckily they're unlikely to be used as CP_ACP */
- nWideChars = MultiByteToWideChar(CP_ACP, 0, buffer, nDataSize, wszText, STREAMIN_BUFFER_SIZE);
+ nWideChars = MultiByteToWideChar(CP_ACP, 0, stream->buffer, stream->dwSize, wszText, STREAMIN_BUFFER_SIZE);
+ pText = wszText;
}
else
- nWideChars = nDataSize>>1;
- ME_InsertTextFromCursor(editor, 0, wszText, nWideChars, style);
- if (nDataSize<STREAMIN_BUFFER_SIZE)
+ {
+ nWideChars = stream->dwSize >> 1;
+ pText = (WCHAR *)stream->buffer;
+ }
+
+ ME_InsertTextFromCursor(editor, 0, pText, nWideChars, style);
+ if (stream->dwSize < STREAMIN_BUFFER_SIZE)
break;
} while(1);
- ME_CommitUndo(editor);
+ ME_CommitUndo(editor);
+ ME_Repaint(editor);
return 0;
}
+void ME_RTFCharAttrHook(RTF_Info *info)
+{
+ CHARFORMAT2W fmt;
+ fmt.cbSize = sizeof(fmt);
+ fmt.dwMask = 0;
+
+ switch(info->rtfMinor)
+ {
+ case rtfPlain:
+ /* FIXME add more flags once they're implemented */
+ fmt.dwMask = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE | CFM_STRIKEOUT | CFM_COLOR | CFM_BACKCOLOR | CFM_SIZE | CFM_WEIGHT;
+ fmt.dwEffects = CFE_AUTOCOLOR | CFE_AUTOBACKCOLOR;
+ fmt.yHeight = 12*20; /* 12pt */
+ fmt.wWeight = 400;
+ break;
+ case rtfBold:
+ fmt.dwMask = CFM_BOLD;
+ fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+ break;
+ case rtfItalic:
+ fmt.dwMask = CFM_ITALIC;
+ fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+ break;
+ case rtfUnderline:
+ fmt.dwMask = CFM_UNDERLINE;
+ fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+ break;
+ case rtfStrikeThru:
+ fmt.dwMask = CFM_STRIKEOUT;
+ fmt.dwEffects = info->rtfParam ? fmt.dwMask : 0;
+ break;
+ case rtfBackColor:
+ fmt.dwMask = CFM_BACKCOLOR;
+ fmt.dwEffects = 0;
+ if (info->rtfParam == 0)
+ fmt.dwEffects = CFE_AUTOBACKCOLOR;
+ else if (info->rtfParam != rtfNoParam)
+ {
+ RTFColor *c = RTFGetColor(info, info->rtfParam);
+ fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
+ }
+ break;
+ case rtfForeColor:
+ fmt.dwMask = CFM_COLOR;
+ fmt.dwEffects = 0;
+ if (info->rtfParam == 0)
+ fmt.dwEffects = CFE_AUTOCOLOR;
+ else if (info->rtfParam != rtfNoParam)
+ {
+ RTFColor *c = RTFGetColor(info, info->rtfParam);
+ fmt.crTextColor = (c->rtfCBlue<<16)|(c->rtfCGreen<<8)|(c->rtfCRed);
+ }
+ break;
+ case rtfFontNum:
+ if (info->rtfParam != rtfNoParam)
+ {
+ RTFFont *f = RTFGetFont(info, info->rtfParam);
+ if (f)
+ {
+ MultiByteToWideChar(CP_ACP, 0, f->rtfFName, -1, fmt.szFaceName, sizeof(fmt.szFaceName)/sizeof(WCHAR));
+ fmt.szFaceName[sizeof(fmt.szFaceName)/sizeof(WCHAR)-1] = '\0';
+ fmt.bCharSet = f->rtfFCharSet;
+ fmt.dwMask = CFM_FACE | CFM_CHARSET;
+ }
+ }
+ break;
+ case rtfFontSize:
+ fmt.dwMask = CFM_SIZE;
+ if (info->rtfParam != rtfNoParam)
+ fmt.yHeight = info->rtfParam*10;
+ break;
+ }
+ if (fmt.dwMask) {
+ ME_Style *style2;
+ RTFFlushOutputBuffer(info);
+ /* FIXME too slow ? how come ? */
+ style2 = ME_ApplyStyle(info->style, &fmt);
+ ME_ReleaseStyle(info->style);
+ info->style = style2;
+ }
+}
+
+/* FIXME this function doesn't get any information about context of the RTF tag, which is very bad,
+ the same tags mean different things in different contexts */
+void ME_RTFParAttrHook(RTF_Info *info)
+{
+ PARAFORMAT2 fmt;
+ fmt.cbSize = sizeof(fmt);
+ fmt.dwMask = 0;
+
+ switch(info->rtfMinor)
+ {
+ case rtfParDef: /* I'm not 100% sure what does it do, but I guess it restores default paragraph attributes */
+ fmt.dwMask = PFM_ALIGNMENT | PFM_TABSTOPS | PFM_OFFSET | PFM_STARTINDENT;
+ fmt.wAlignment = PFA_LEFT;
+ fmt.cTabCount = 0;
+ fmt.dxOffset = fmt.dxStartIndent = 0;
+ break;
+ case rtfFirstIndent:
+ ME_GetSelectionParaFormat(info->editor, &fmt);
+ fmt.dwMask = PFM_STARTINDENT;
+ fmt.dxStartIndent = info->rtfParam + fmt.dxOffset;
+ break;
+ case rtfLeftIndent:
+ {
+ int first, left;
+ ME_GetSelectionParaFormat(info->editor, &fmt);
+ first = fmt.dxStartIndent;
+ left = info->rtfParam;
+ fmt.dwMask = PFM_STARTINDENT|PFM_OFFSET;
+ fmt.dxStartIndent = first + left;
+ fmt.dxOffset = -first;
+ break;
+ }
+ case rtfRightIndent:
+ fmt.dwMask = PFM_RIGHTINDENT;
+ fmt.dxRightIndent = info->rtfParam;
+ break;
+ case rtfQuadLeft:
+ case rtfQuadJust:
+ fmt.dwMask = PFM_ALIGNMENT;
+ fmt.wAlignment = PFA_LEFT;
+ break;
+ case rtfQuadRight:
+ fmt.dwMask = PFM_ALIGNMENT;
+ fmt.wAlignment = PFA_RIGHT;
+ break;
+ case rtfQuadCenter:
+ fmt.dwMask = PFM_ALIGNMENT;
+ fmt.wAlignment = PFA_CENTER;
+ break;
+ case rtfTabPos:
+ ME_GetSelectionParaFormat(info->editor, &fmt);
+ if (!(fmt.dwMask & PFM_TABSTOPS))
+ {
+ fmt.dwMask |= PFM_TABSTOPS;
+ fmt.cTabCount = 0;
+ }
+ if (fmt.cTabCount < MAX_TAB_STOPS)
+ fmt.rgxTabs[fmt.cTabCount++] = info->rtfParam;
+ break;
+ }
+ if (fmt.dwMask) {
+ RTFFlushOutputBuffer(info);
+ /* FIXME too slow ? how come ?*/
+ ME_SetSelectionParaFormat(info->editor, &fmt);
+ }
+}
+
+void ME_RTFReadHook(RTF_Info *info) {
+ switch(info->rtfClass)
+ {
+ case rtfGroup:
+ switch(info->rtfMajor)
+ {
+ case rtfBeginGroup:
+ if (info->stackTop < maxStack) {
+ memcpy(&info->stack[info->stackTop].fmt, &info->style->fmt, sizeof(CHARFORMAT2W));
+ info->stack[info->stackTop].codePage = info->codePage;
+ info->stack[info->stackTop].unicodeLength = info->unicodeLength;
+ }
+ info->stackTop++;
+ break;
+ case rtfEndGroup:
+ {
+ ME_Style *s;
+ RTFFlushOutputBuffer(info);
+ info->stackTop--;
+ /* FIXME too slow ? how come ? */
+ s = ME_ApplyStyle(info->style, &info->stack[info->stackTop].fmt);
+ ME_ReleaseStyle(info->style);
+ info->style = s;
+ info->codePage = info->stack[info->stackTop].codePage;
+ info->unicodeLength = info->stack[info->stackTop].unicodeLength;
+ break;
+ }
+ }
+ break;
+ case rtfControl:
+ switch(info->rtfMajor)
+ {
+ case rtfCharAttr:
+ ME_RTFCharAttrHook(info);
+ break;
+ case rtfParAttr:
+ ME_RTFParAttrHook(info);
+ break;
+ }
+ break;
+ }
+}
+
+void
+ME_StreamInFill(ME_InStream *stream)
+{
+ stream->editstream->dwError = stream->editstream->pfnCallback(stream->editstream->dwCookie,
+ stream->buffer,
+ sizeof(stream->buffer),
+ &stream->dwSize);
+ stream->dwUsed = 0;
+}
+
static LRESULT ME_StreamIn(ME_TextEditor *editor, DWORD format, EDITSTREAM *stream)
{
RTF_Info parser;
ME_Style *style;
+ int from, to, to2, nUndoMode;
+ ME_UndoItem *pUI;
+ int nEventMask = editor->nEventMask;
+ ME_InStream inStream;
TRACE("%p %p\n", stream, editor->hWnd);
+ editor->nEventMask = 0;
+ ME_GetSelection(editor, &from, &to);
if (format & SFF_SELECTION) {
style = ME_GetSelectionInsertStyle(editor);
- SendMessageW(editor->hWnd, WM_CLEAR, 0, 0);
+
+ ME_InternalDeleteText(editor, from, to-from);
}
else {
style = editor->pBuffer->pDefaultStyle;
ME_AddRefStyle(style);
SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
- SetWindowTextA(editor->hWnd, "");
+ ME_InternalDeleteText(editor, 0, ME_GetTextLength(editor));
+ from = to = 0;
ME_ClearTempStyle(editor);
+ /* FIXME restore default paragraph formatting ! */
+ }
+
+ nUndoMode = editor->nUndoMode;
+ editor->nUndoMode = umIgnore;
+
+ inStream.editstream = stream;
+ inStream.editstream->dwError = 0;
+ inStream.dwSize = 0;
+ inStream.dwUsed = 0;
+
+ if (format & SF_RTF)
+ {
+ /* Check if it's really RTF, and if it is not, use plain text */
+ ME_StreamInFill(&inStream);
+ if (!inStream.editstream->dwError)
+ {
+ if (strncmp(inStream.buffer, "{\\rtf1", 6) && strncmp(inStream.buffer, "{\\urtf", 6))
+ {
+ format &= ~SF_RTF;
+ format |= SF_TEXT;
+ }
+ }
}
- if (format & SF_RTF) {
- /* setup the RTF parser */
- memset(&parser, 0, sizeof parser);
- RTFSetEditStream(&parser, stream);
- parser.rtfFormat = format&(SF_TEXT|SF_RTF);
- parser.hwndEdit = editor->hWnd;
- WriterInit(&parser);
- RTFInit(&parser);
- BeginFile(&parser);
+ if (!inStream.editstream->dwError)
+ {
+ if (format & SF_RTF) {
+ /* setup the RTF parser */
+ memset(&parser, 0, sizeof parser);
+ RTFSetEditStream(&parser, &inStream);
+ parser.rtfFormat = format&(SF_TEXT|SF_RTF);
+ parser.hwndEdit = editor->hWnd;
+ parser.editor = editor;
+ parser.style = style;
+ WriterInit(&parser);
+ RTFInit(&parser);
+ RTFSetReadHook(&parser, ME_RTFReadHook);
+ BeginFile(&parser);
- /* do the parsing */
- RTFRead(&parser);
- RTFFlushOutputBuffer(&parser);
+ /* do the parsing */
+ RTFRead(&parser);
+ RTFFlushOutputBuffer(&parser);
+ RTFDestroy(&parser);
+
+ style = parser.style;
+ }
+ else if (format & SF_TEXT)
+ ME_StreamInText(editor, format, &inStream, style);
+ else
+ ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
+ ME_GetSelection(editor, &to, &to2);
+ /* put the cursor at the top */
+ if (!(format & SFF_SELECTION))
+ SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
+ else
+ {
+ /* FIXME where to put cursor now ? */
+ }
}
- else if (format & SF_TEXT)
- ME_StreamInText(editor, format, stream, style);
- else
- ERR("EM_STREAMIN without SF_TEXT or SF_RTF\n");
- /* put the cursor at the top */
- if (!(format & SFF_SELECTION))
- SendMessageA(editor->hWnd, EM_SETSEL, 0, 0);
- else
+
+ editor->nUndoMode = nUndoMode;
+ pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
+ TRACE("from %d to %d\n", from, to);
+ if (pUI && from < to)
{
- /* FIXME where to put cursor now ? */
+ pUI->nStart = from;
+ pUI->nLen = to-from;
}
- ME_ReleaseStyle(style);
+ ME_CommitUndo(editor);
+ ME_ReleaseStyle(style);
+ editor->nEventMask = nEventMask;
+ InvalidateRect(editor->hWnd, NULL, TRUE);
+ ME_UpdateRepaint(editor);
+ if (!(format & SFF_SELECTION)) {
+ ME_ClearTempStyle(editor);
+ }
+ ME_MoveCaret(editor);
+ ME_SendSelChange(editor);
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;
ed->pCursors[1].pRun = ME_FindItemFwd(ed->pBuffer->pFirst, diRun);
ed->pCursors[1].nOffset = 0;
ed->nLastTotalLength = ed->nTotalLength = 0;
- ed->nScrollPos = 0;
ed->nUDArrowX = -1;
ed->nSequence = 0;
ed->rgbBackColor = -1;
ed->nUndoMode = umAddToUndo;
ed->nParagraphs = 1;
ed->nLastSelStart = ed->nLastSelEnd = 0;
+ ed->nScrollPosY = 0;
for (i=0; i<HFONT_CACHE_SIZE; i++)
{
ed->pFontCache[i].nRefs = 0;
return ed;
}
+typedef struct tagME_GlobalDestStruct
+{
+ HGLOBAL hData;
+ int nLength;
+} ME_GlobalDestStruct;
+
+static DWORD CALLBACK ME_AppendToHGLOBAL(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+ ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+ int nMaxSize;
+ BYTE *pDest;
+
+ nMaxSize = GlobalSize(pData->hData);
+ if (pData->nLength+cb+1 >= cb)
+ {
+ /* round up to 2^17 */
+ int nNewSize = (((nMaxSize+cb+1)|0x1FFFF)+1) & 0xFFFE0000;
+ pData->hData = GlobalReAlloc(pData->hData, nNewSize, 0);
+ }
+ pDest = (BYTE *)GlobalLock(pData->hData);
+ memcpy(pDest + pData->nLength, lpBuff, cb);
+ pData->nLength += cb;
+ pDest[pData->nLength] = '\0';
+ GlobalUnlock(pData->hData);
+ *pcb = cb;
+
+ return 0;
+}
+
+static DWORD CALLBACK ME_ReadFromHGLOBALUnicode(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+ ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+ int i;
+ WORD *pSrc, *pDest;
+
+ cb = cb >> 1;
+ pDest = (WORD *)lpBuff;
+ pSrc = (WORD *)GlobalLock(pData->hData);
+ for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
+ pDest[i] = pSrc[pData->nLength+i];
+ }
+ pData->nLength += i;
+ *pcb = 2*i;
+ GlobalUnlock(pData->hData);
+ return 0;
+}
+
+static DWORD CALLBACK ME_ReadFromHGLOBALRTF(DWORD_PTR dwCookie, LPBYTE lpBuff, LONG cb, LONG *pcb)
+{
+ ME_GlobalDestStruct *pData = (ME_GlobalDestStruct *)dwCookie;
+ int i;
+ BYTE *pSrc, *pDest;
+
+ pDest = lpBuff;
+ pSrc = (BYTE *)GlobalLock(pData->hData);
+ for (i = 0; i<cb && pSrc[pData->nLength+i]; i++) {
+ pDest[i] = pSrc[pData->nLength+i];
+ }
+ pData->nLength += i;
+ *pcb = i;
+ GlobalUnlock(pData->hData);
+ return 0;
+}
+
void ME_DestroyEditor(ME_TextEditor *editor)
{
ME_DisplayItem *pFirst = editor->pBuffer->pFirst;
switch(msg) {
UNSUPPORTED_MSG(EM_AUTOURLDETECT)
- UNSUPPORTED_MSG(EM_CANPASTE)
UNSUPPORTED_MSG(EM_CHARFROMPOS)
UNSUPPORTED_MSG(EM_DISPLAYBAND)
UNSUPPORTED_MSG(EM_EXLIMITTEXT)
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)
UNSUPPORTED_MSG(WM_STYLECHANGED)
/* UNSUPPORTED_MSG(WM_UNICHAR) FIXME missing in Wine headers */
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;
case EM_SETCHARFORMAT:
{
CHARFORMAT2W buf, *p;
+ BOOL bRepaint = TRUE;
p = ME_ToCF2W(&buf, (CHARFORMAT2W *)lParam);
if (!wParam)
ME_SetDefaultCharFormat(editor, p);
FIXME("word selection not supported\n");
else if (wParam == SCF_ALL)
ME_SetCharFormat(editor, 0, ME_GetTextLength(editor), p);
- else
+ else {
+ int from, to;
+ ME_GetSelection(editor, &from, &to);
+ bRepaint = (from != to);
ME_SetSelectionCharFormat(editor, p);
+ }
ME_CommitUndo(editor);
- ME_UpdateRepaint(editor);
+ if (bRepaint)
+ ME_UpdateRepaint(editor);
return 0;
}
case EM_GETCHARFORMAT:
}
case EM_SETPARAFORMAT:
ME_SetSelectionParaFormat(editor, (PARAFORMAT2 *)lParam);
+ ME_UpdateRepaint(editor);
ME_CommitUndo(editor);
return 0;
case EM_GETPARAFORMAT:
ME_UpdateRepaint(editor);
return 0;
}
+ case EM_CANPASTE:
+ {
+ UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
+ if (IsClipboardFormatAvailable(nRTFFormat))
+ return TRUE;
+ if (IsClipboardFormatAvailable(CF_UNICODETEXT))
+ return TRUE;
+ return FALSE;
+ }
+ case WM_PASTE:
+ {
+ DWORD dwFormat = 0;
+ EDITSTREAM es;
+ ME_GlobalDestStruct gds;
+ UINT nRTFFormat = RegisterClipboardFormatA("Rich Text Format");
+ UINT cf = 0;
+
+ if (IsClipboardFormatAvailable(nRTFFormat))
+ cf = nRTFFormat, dwFormat = SF_RTF;
+ else if (IsClipboardFormatAvailable(CF_UNICODETEXT))
+ cf = CF_UNICODETEXT, dwFormat = SF_TEXT|SF_UNICODE;
+ else
+ return 0;
+
+ if (!OpenClipboard(hWnd))
+ return 0;
+ gds.hData = GetClipboardData(cf);
+ gds.nLength = 0;
+ es.dwCookie = (DWORD)&gds;
+ es.pfnCallback = dwFormat == SF_RTF ? ME_ReadFromHGLOBALRTF : ME_ReadFromHGLOBALUnicode;
+ SendMessageW(hWnd, EM_STREAMIN, dwFormat|SFF_SELECTION, (LPARAM)&es);
+
+ CloseClipboard();
+ return 0;
+ }
case WM_CUT:
case WM_COPY:
{
int from, to, pars;
WCHAR *data;
HANDLE hData;
+ EDITSTREAM es;
+ ME_GlobalDestStruct gds;
if (!OpenClipboard(hWnd))
return 0;
data = (WCHAR *)GlobalLock(hData);
ME_GetTextW(editor, data, from, to-from, TRUE);
GlobalUnlock(hData);
- SetClipboardData(CF_UNICODETEXT, hData);
+
+ gds.hData = GlobalAlloc(GMEM_MOVEABLE, 0);
+ gds.nLength = 0;
+ es.dwCookie = (DWORD)&gds;
+ es.pfnCallback = ME_AppendToHGLOBAL;
+ SendMessageW(hWnd, EM_STREAMOUT, SFF_SELECTION|SF_RTF, (LPARAM)&es);
+ GlobalReAlloc(gds.hData, gds.nLength+1, 0);
+
+ SetClipboardData(CF_UNICODETEXT, hData);
+ SetClipboardData(RegisterClipboardFormatA("Rich Text Format"), gds.hData);
+
CloseClipboard();
if (msg == WM_CUT)
{
return 0; /* FIXME really 0 ? */
}
wstr = LOWORD(wParam);
- if (((unsigned)wstr)>=' ' || wstr=='\r') {
+ if (((unsigned)wstr)>=' ' || wstr=='\r' || wstr=='\t') {
/* FIXME maybe it would make sense to call EM_REPLACESEL instead ? */
ME_Style *style = ME_GetInsertStyle(editor, 0);
ME_SaveTempStyle(editor);
}
case WM_VSCROLL:
{
+ int nPos = editor->nScrollPosY;
si.cbSize = sizeof(SCROLLINFO);
si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
GetScrollInfo(hWnd, SB_VERT, &si);
switch(LOWORD(wParam)) {
+ case SB_LINEUP:
+ nPos -= 24; /* FIXME follow the original */
+ if (nPos<0) nPos = 0;
+ break;
+ case SB_LINEDOWN:
+ {
+ int nEnd = editor->nTotalLength - editor->sizeWindow.cy;
+ nPos += 24; /* FIXME follow the original */
+ if (nPos>=nEnd) nPos = nEnd;
+ break;
+ }
+ case SB_PAGEUP:
+ nPos -= editor->sizeWindow.cy;
+ if (nPos<0) nPos = 0;
+ break;
+ case SB_PAGEDOWN:
+ nPos += editor->sizeWindow.cy;
+ if (nPos>=editor->nTotalLength) nPos = editor->nTotalLength-1;
+ break;
case SB_THUMBTRACK:
- SetScrollPos(hWnd, SB_VERT, si.nTrackPos, FALSE);
- ScrollWindow(hWnd, 0, si.nPos-si.nTrackPos, NULL, NULL);
- /* InvalidateRect(hWnd, NULL, TRUE); */
- UpdateWindow(hWnd);
+ case SB_THUMBPOSITION:
+ nPos = si.nTrackPos;
break;
}
+ if (nPos != editor->nScrollPosY) {
+ ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
+ editor->nScrollPosY = nPos;
+ SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
+ UpdateWindow(hWnd);
+ }
+ break;
+ }
+ case WM_MOUSEWHEEL:
+ {
+ int gcWheelDelta = 0, nPos = editor->nScrollPosY;
+ UINT pulScrollLines;
+
+ SystemParametersInfoW(SPI_GETWHEELSCROLLLINES,0, &pulScrollLines, 0);
+ gcWheelDelta -= GET_WHEEL_DELTA_WPARAM(wParam);
+ if (abs(gcWheelDelta) >= WHEEL_DELTA && pulScrollLines)
+ nPos += pulScrollLines * (gcWheelDelta / WHEEL_DELTA) * 8;
+ if(nPos>=editor->nTotalLength)
+ nPos = editor->nTotalLength - 1;
+ if (nPos<0)
+ nPos = 0;
+ if (nPos != editor->nScrollPosY) {
+ ScrollWindow(hWnd, 0, editor->nScrollPosY-nPos, NULL, NULL);
+ editor->nScrollPosY = nPos;
+ SetScrollPos(hWnd, SB_VERT, nPos, TRUE);
+ UpdateWindow(hWnd);
+ }
break;
}
case WM_SIZE:
{
ME_MarkAllForWrapping(editor);
+ ME_WrapMarkedParagraphs(editor);
+ ME_UpdateScrollBar(editor);
ME_Repaint(editor);
return DefWindowProcW(hWnd, msg, wParam, lParam);
}
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;
#include "editstr.h"
-#define ALLOC_OBJ(type) (type *)HeapAlloc(me_heap, 0, sizeof(type))
-#define ALLOC_N_OBJ(type, count) (type *)HeapAlloc(me_heap, 0, count*sizeof(type))
+#define ALLOC_OBJ(type) HeapAlloc(me_heap, 0, sizeof(type))
+#define ALLOC_N_OBJ(type, count) HeapAlloc(me_heap, 0, (count)*sizeof(type))
#define FREE_OBJ(ptr) HeapFree(me_heap, 0, ptr)
/* style.c */
LPSTR ME_ToAnsi(HWND hWnd, LPVOID psz);
void ME_EndToAnsi(HWND hWnd, LPVOID psz);
+static inline int ME_IsWSpace(WCHAR ch)
+{
+ return ch > '\0' && ch <= ' ';
+}
/* note: those two really return the first matching offset (starting from EOS)+1
* in other words, an offset of the first trailing white/black */
void ME_CheckCharOffsets(ME_TextEditor *editor);
void ME_PropagateCharOffset(ME_DisplayItem *p, int shift);
void ME_GetGraphicsSize(ME_TextEditor *editor, ME_Run *run, SIZE *pSize);
-int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run);
+int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Paragraph *para, ME_Run *run);
/* this one accounts for 1/2 char tolerance */
int ME_CharFromPointCursor(ME_TextEditor *editor, int cx, ME_Run *run);
int ME_PointFromChar(ME_TextEditor *editor, ME_Run *pRun, int nOffset);
int ME_FindSplitPoint(ME_Context *c, POINT *pt, ME_Run *run, int desperate);
void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run);
ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *run);
-void ME_CalcRunExtent(ME_Context *c, ME_Run *run);
-SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen);
+void ME_CalcRunExtent(ME_Context *c, ME_Paragraph *para, ME_Run *run);
+SIZE ME_GetRunSize(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLen);
void ME_CursorFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_Cursor *pCursor);
void ME_RunOfsFromCharOfs(ME_TextEditor *editor, int nCharOfs, ME_DisplayItem **ppRun, int *pOfs);
int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs);
void ME_InternalDeleteText(ME_TextEditor *editor, int nOfs, int nChars);
int ME_GetTextLength(ME_TextEditor *editor);
ME_Style *ME_GetSelectionInsertStyle(ME_TextEditor *editor);
+BOOL ME_UpdateSelection(ME_TextEditor *editor, ME_Cursor *pTempCursor);
/* wrap.c */
void ME_PrepareParagraphForWrapping(ME_Context *c, ME_DisplayItem *tp);
ME_DisplayItem *ME_MakeRow(int height, int baseline, int width);
void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd);
void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp);
-void ME_WrapMarkedParagraphs(ME_TextEditor *editor);
+BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor);
/* para.c */
ME_DisplayItem *ME_GetParagraph(ME_DisplayItem *run);
void ME_Repaint(ME_TextEditor *editor);
void ME_UpdateRepaint(ME_TextEditor *editor);
void ME_DrawParagraph(ME_Context *c, ME_DisplayItem *paragraph);
-void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos);
-int ME_GetScrollPos(ME_TextEditor *editor);
+void ME_UpdateScrollBar(ME_TextEditor *editor);
+int ME_GetYScrollPos(ME_TextEditor *editor);
void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun);
COLORREF ME_GetBackColor(ME_TextEditor *editor);
+void ME_Scroll(ME_TextEditor *editor, int cx, int cy);
/* richole.c */
extern LRESULT CreateIRichEditOle(LPVOID *);
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);
+void ME_StreamInFill(ME_InStream *stream);
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);
#define MERF_STYLEFLAGS 0x0FFF
/* run contains non-text content, which has its own rules for wrapping, sizing etc */
#define MERF_GRAPHICS 1
+/* run is a tab (or, in future, any kind of content whose size is dependent on run position) */
+#define MERF_TAB 2
/* run is splittable (contains white spaces in the middle or end) */
#define MERF_SPLITTABLE 0x001000
/* the "end of paragraph" run, contains 1 character */
#define MERF_ENDPARA 0x100000
+/* runs with any of these flags set cannot be joined */
+#define MERF_NOJOIN (MERF_GRAPHICS|MERF_TAB|MERF_ENDPARA)
+/* runs that don't contain real text */
+#define MERF_NOTEXT (MERF_GRAPHICS|MERF_TAB|MERF_ENDPARA)
+
/* those flags are kept when the row is split */
#define MERF_SPLITMASK (~(0))
umAddBackToUndo
} ME_UndoMode;
+typedef struct tagME_FontTableItem {
+ BYTE bCharSet;
+ WCHAR *szFaceName;
+} ME_FontTableItem;
+
+
+#define STREAMIN_BUFFER_SIZE 4096 /* M$ compatibility */
+
+struct tagME_InStream {
+ EDITSTREAM *editstream;
+ DWORD dwSize;
+ DWORD dwUsed;
+ BYTE buffer[STREAMIN_BUFFER_SIZE];
+};
+typedef struct tagME_InStream ME_InStream;
+
+
+#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;
+ UINT nDefaultCodePage;
+} ME_OutStream;
+
typedef struct tagME_FontCacheItem
{
LOGFONTW lfSpecs;
ME_Cursor *pCursors;
int nCursors;
SIZE sizeWindow;
- int nScrollPos;
int nTotalLength, nLastTotalLength;
int nUDArrowX;
int nSequence;
int nParagraphs;
int nLastSelStart, nLastSelEnd;
ME_FontCacheItem pFontCache[HFONT_CACHE_SIZE];
+ ME_OutStream *pStream;
+ BOOL bScrollX, bScrollY;
+ int nScrollPosY;
} ME_TextEditor;
typedef struct tagME_Context
int yoffset;
editor->nSequence++;
- yoffset = GetScrollPos(editor->hWnd, SB_VERT);
+ yoffset = ME_GetYScrollPos(editor);
ME_InitContext(&c, editor, hDC);
SetBkMode(hDC, TRANSPARENT);
ME_MoveCaret(editor);
item = editor->pBuffer->pFirst->next;
c.pt.y -= yoffset;
while(item != editor->pBuffer->pLast) {
+ int ye;
assert(item->type == diParagraph);
+ ye = c.pt.y + item->member.para.nHeight;
if (!bOnlyNew || (item->member.para.nFlags & MEPF_REPAINT))
{
BOOL bPaint = (rcUpdate == NULL);
if (bPaint)
{
ME_DrawParagraph(&c, item);
- item->member.para.nFlags &= ~MEPF_REPAINT;
+ if (!rcUpdate || (rcUpdate->top<=c.pt.y && rcUpdate->bottom>=ye))
+ item->member.para.nFlags &= ~MEPF_REPAINT;
}
}
- c.pt.y += item->member.para.nHeight;
+ c.pt.y = ye;
item = item->member.para.next_para;
}
if (c.pt.y<c.rcView.bottom) {
ME_DisplayItem *pRun = NULL;
int nOffset = -1;
HDC hDC;
-
int nCharOfs = ME_CharOfsFromRunOfs(editor, pCursor->pRun, pCursor->nOffset);
+
ME_RunOfsFromCharOfs(editor, nCharOfs, &pRun, &nOffset);
assert(pRun == pCursor->pRun);
assert(nOffset == pCursor->nOffset);
ME_MarkSelectionForRepaint(editor);
- ME_WrapMarkedParagraphs(editor);
+ if (ME_WrapMarkedParagraphs(editor)) {
+ ME_UpdateScrollBar(editor);
+ }
hDC = GetDC(editor->hWnd);
ME_HideCaret(editor);
ME_PaintContent(editor, hDC, TRUE, NULL);
ReleaseDC(editor->hWnd, hDC);
ME_ShowCaret(editor);
+ ME_EnsureVisible(editor, pCursor->pRun);
}
void ME_UpdateRepaint(ME_TextEditor *editor)
ME_Run *run = &rundi->member.run;
int runofs = run->nCharOfs+para->nCharOfs;
+ /* you can always comment it out if you need visible paragraph marks */
+ if (run->nFlags & (MERF_ENDPARA|MERF_TAB))
+ return;
if (run->nFlags & MERF_GRAPHICS) {
int blfrom, blto;
ME_GetSelection(c->editor, &blfrom, &blto);
visible = RectVisible(c->hDC, &rcPara);
if (visible) {
HBRUSH hbr;
+ hbr = CreateSolidBrush(ME_GetBackColor(c->editor));
/* left margin */
rc.left = c->rcView.left;
rc.right = c->rcView.left+nMargWidth;
rc.top = y;
rc.bottom = y+p->member.row.nHeight;
- FillRect(c->hDC, &rc, c->hbrMargin);
+ FillRect(c->hDC, &rc, hbr/* c->hbrMargin */);
/* right margin */
rc.left = xe;
rc.right = c->rcView.right;
- FillRect(c->hDC, &rc, c->hbrMargin);
- rc.left = c->rcView.left+para->nLeftMargin;
+ FillRect(c->hDC, &rc, hbr/* c->hbrMargin */);
+ rc.left = c->rcView.left+nMargWidth;
rc.right = xe;
- hbr = CreateSolidBrush(ME_GetBackColor(c->editor));
FillRect(c->hDC, &rc, hbr);
DeleteObject(hbr);
}
SetTextAlign(c->hDC, align);
}
-void ME_UpdateScrollBar(ME_TextEditor *editor, int ypos)
+void ME_Scroll(ME_TextEditor *editor, int cx, int cy)
{
- float perc = 0.0;
SCROLLINFO si;
HWND hWnd = editor->hWnd;
- int overflow = editor->nTotalLength - editor->sizeWindow.cy;
+
si.cbSize = sizeof(SCROLLINFO);
- si.fMask = SIF_PAGE|SIF_POS|SIF_RANGE|SIF_TRACKPOS;
+ si.fMask = SIF_POS;
GetScrollInfo(hWnd, SB_VERT, &si);
-
- if (ypos < 0) {
- if (si.nMax<1) si.nMax = 1;
- perc = 1.0*si.nPos/si.nMax;
- ypos = perc*overflow;
- }
- if (ypos >= overflow && overflow > 0)
- ypos = overflow - 1;
+ si.nPos = editor->nScrollPosY -= cy;
+ SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
+ if (abs(cy) > editor->sizeWindow.cy)
+ InvalidateRect(editor->hWnd, NULL, TRUE);
+ else
+ ScrollWindowEx(hWnd, cx, cy, NULL, NULL, NULL, NULL, SW_ERASE|SW_INVALIDATE);
+}
- if (overflow > 0) {
- EnableScrollBar(hWnd, SB_VERT, ESB_ENABLE_BOTH);
- SetScrollRange(hWnd, SB_VERT, 0, overflow, FALSE);
- SetScrollPos(hWnd, SB_VERT, ypos, TRUE);
- } else {
- EnableScrollBar(hWnd, SB_VERT, ESB_DISABLE_BOTH);
- SetScrollRange(hWnd, SB_VERT, 0, 0, FALSE);
- SetScrollPos(hWnd, SB_VERT, 0, TRUE);
- }
- if (ypos != si.nPos)
+void ME_UpdateScrollBar(ME_TextEditor *editor)
+{
+ HWND hWnd = editor->hWnd;
+ SCROLLINFO si;
+ int nOldLen = editor->nTotalLength;
+ BOOL bScrollY = (editor->nTotalLength > editor->sizeWindow.cy);
+ BOOL bUpdateScrollBars;
+ si.cbSize = sizeof(si);
+ si.fMask = SIF_POS | SIF_RANGE;
+ GetScrollInfo(hWnd, SB_VERT, &si);
+ bUpdateScrollBars = (bScrollY || editor->bScrollY)&& ((si.nMax != nOldLen) || (si.nPage != editor->sizeWindow.cy));
+
+ if (bScrollY != editor->bScrollY)
{
- TRACE("ScrollWindow(%d, %d, %d, %0.4f)\n", si.nPos, si.nMax, ypos, perc);
- ScrollWindow(hWnd, 0, si.nPos - ypos, NULL, NULL);
- UpdateWindow(hWnd);
+ si.fMask = SIF_RANGE | SIF_PAGE;
+ si.nMin = 0;
+ si.nPage = editor->sizeWindow.cy;
+ if (bScrollY) {
+ si.nMax = editor->nTotalLength;
+ } else {
+ si.nMax = 0;
+ }
+ SetScrollInfo(hWnd, SB_VERT, &si, FALSE);
+ ME_MarkAllForWrapping(editor);
+ editor->bScrollY = bScrollY;
+ ME_WrapMarkedParagraphs(editor);
+ bUpdateScrollBars = TRUE;
+ }
+ if (bUpdateScrollBars) {
+ int nScroll = 0;
+ si.fMask = SIF_PAGE | SIF_RANGE | SIF_POS;
+ if (editor->nTotalLength > editor->sizeWindow.cy) {
+ si.nMax = editor->nTotalLength;
+ si.nPage = editor->sizeWindow.cy;
+ if (si.nPos > si.nMax-si.nPage) {
+ nScroll = (si.nMax-si.nPage)-si.nPos;
+ si.nPos = si.nMax-si.nPage;
+ }
+ }
+ else {
+ si.nMax = 0;
+ si.nPage = 0;
+ si.nPos = 0;
+ }
+ TRACE("min=%d max=%d page=%d pos=%d shift=%d\n", si.nMin, si.nMax, si.nPage, si.nPos, nScroll);
+ editor->nScrollPosY = si.nPos;
+ SetScrollInfo(hWnd, SB_VERT, &si, TRUE);
+ if (nScroll)
+ ScrollWindow(hWnd, 0, -nScroll, NULL, NULL);
}
}
-int ME_GetScrollPos(ME_TextEditor *editor)
+int ME_GetYScrollPos(ME_TextEditor *editor)
{
- return GetScrollPos(editor->hWnd, SB_VERT);
+ return editor->nScrollPosY;
}
void ME_EnsureVisible(ME_TextEditor *editor, ME_DisplayItem *pRun)
{
ME_DisplayItem *pRow = ME_FindItemBack(pRun, diStartRow);
ME_DisplayItem *pPara = ME_FindItemBack(pRun, diParagraph);
- int y, yrel, yheight;
+ int y, yrel, yheight, yold;
+ HWND hWnd = editor->hWnd;
assert(pRow);
assert(pPara);
y = pPara->member.para.nYPos+pRow->member.row.nYPos;
yheight = pRow->member.row.nHeight;
- yrel = y - ME_GetScrollPos(editor);
- if (yrel < 0)
- ME_UpdateScrollBar(editor, y);
- else if (yrel + yheight > editor->sizeWindow.cy)
- {
- ME_UpdateScrollBar(editor, y + yheight - editor->sizeWindow.cy);
+ yold = ME_GetYScrollPos(editor);
+ yrel = y - yold;
+ if (yrel < 0) {
+ editor->nScrollPosY = y;
+ SetScrollPos(hWnd, SB_VERT, y, TRUE);
+ ScrollWindow(hWnd, 0, -yrel, NULL, NULL);
+ UpdateWindow(hWnd);
+ } else if (yrel + yheight > editor->sizeWindow.cy) {
+ int newy = y+yheight-editor->sizeWindow.cy;
+ editor->nScrollPosY = newy;
+ SetScrollPos(hWnd, SB_VERT, newy, TRUE);
+ ScrollWindow(hWnd, 0, -(newy-yold), NULL, NULL);
+ UpdateWindow(hWnd);
}
}
ZeroMemory(&fmt, sizeof(fmt));
fmt.cbSize = sizeof(fmt);
- fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT;
+ fmt.dwMask = PFM_ALIGNMENT | PFM_OFFSET | PFM_STARTINDENT | PFM_RIGHTINDENT | PFM_TABSTOPS;
CopyMemory(para->member.para.pFmt, &fmt, sizeof(PARAFORMAT2));
if (pFmt->dwMask & PFM_ALIGNMENT)
para->member.para.pFmt->wAlignment = pFmt->wAlignment;
+ if (pFmt->dwMask & PFM_STARTINDENT)
+ para->member.para.pFmt->dxStartIndent = pFmt->dxStartIndent;
+ if (pFmt->dwMask & PFM_OFFSET)
+ para->member.para.pFmt->dxOffset = pFmt->dxOffset;
+ if (pFmt->dwMask & PFM_OFFSETINDENT)
+ para->member.para.pFmt->dxStartIndent += pFmt->dxStartIndent;
+
+ if (pFmt->dwMask & PFM_TABSTOPS)
+ {
+ para->member.para.pFmt->cTabCount = pFmt->cTabCount;
+ memcpy(para->member.para.pFmt->rgxTabs, pFmt->rgxTabs, pFmt->cTabCount*sizeof(int));
+ }
/* FIXME to be continued (indents, bulleting and such) */
break;
para = para->member.para.next_para;
} while(1);
- ME_Repaint(editor);
}
void ME_GetParaFormat(ME_TextEditor *editor, ME_DisplayItem *para, PARAFORMAT2 *pFmt)
ZeroMemory(&tmp, sizeof(tmp));
tmp.cbSize = sizeof(tmp);
ME_GetParaFormat(editor, para, &tmp);
- assert(tmp.dwMask & PFM_ALIGNMENT);
+ assert(tmp.dwMask & PFM_ALIGNMENT);
if (pFmt->wAlignment != tmp.wAlignment)
pFmt->dwMask &= ~PFM_ALIGNMENT;
+ assert(tmp.dwMask & PFM_STARTINDENT);
+ if (pFmt->dxStartIndent != tmp.dxStartIndent)
+ pFmt->dwMask &= ~PFM_STARTINDENT;
+
+ assert(tmp.dwMask & PFM_OFFSET);
+ if (pFmt->dxOffset != tmp.dxOffset)
+ pFmt->dwMask &= ~PFM_OFFSET;
+
+ assert(tmp.dwMask & PFM_TABSTOPS);
+ if (pFmt->dwMask & PFM_TABSTOPS) {
+ if (pFmt->cTabCount != tmp.cTabCount)
+ pFmt->dwMask &= ~PFM_TABSTOPS;
+ else
+ if (memcmp(pFmt->rgxTabs, tmp.rgxTabs, tmp.cTabCount*sizeof(int)))
+ pFmt->dwMask &= ~PFM_TABSTOPS;
+ }
+
if (para == para_end)
return;
para = para->member.para.next_para;
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
-
-#include "rtf.h"
+#include <assert.h>
#include "windef.h"
#include "winbase.h"
#include "wine/debug.h"
+#include "editor.h"
+#include "rtf.h"
+
WINE_DEFAULT_DEBUG_CHANNEL(richedit);
extern HANDLE me_heap;
-static int _RTFGetChar(RTF_Info *);
+static int _RTFGetChar(RTF_Info *);
static void _RTFGetToken (RTF_Info *);
static void _RTFGetToken2 (RTF_Info *);
static int GetChar (RTF_Info *);
static void Lookup (RTF_Info *, char *);
static int Hash (char*);
-static void CharSetInit (RTF_Info *);
-static void ReadCharSetMaps (RTF_Info *);
+static void CharAttr(RTF_Info *info);
+static void CharSet(RTF_Info *info);
+static void DocAttr(RTF_Info *info);
+static void RTFFlushCPOutputBuffer(RTF_Info *info);
+static void RTFPutCodePageChar(RTF_Info *info, int c);
-/*
- RTF ANSI character set (\ansi) general map
- These are taken from the ISO-Latin-1 (ISO-8859-1) encodings, with
- a few additions
-
- Field 1 is the standard character name which the character value in
- field 2 maps onto. (It doesn't mean "to produce the character in field 1,
- use the value in field 2.)
-
- The character value may be given either as a single character (which will be
- converted to the ASCII value of the character), or in numeric format, either
- in decimal or 0xyy as hex yy. Single or double quotes may be used to quote
- characters.*/
-
-int ansi_gen[] =
-{
- rtfSC_formula ,0x06,
- rtfSC_nobrkhyphen ,0x1e,
- rtfSC_opthyphen ,0x1f,
- rtfSC_space ,' ',
- rtfSC_exclam ,'!',
- rtfSC_quotedbl ,'"',
- rtfSC_numbersign ,'#',
- rtfSC_dollar ,'$',
- rtfSC_percent ,'%',
- rtfSC_ampersand ,'&',
- rtfSC_quoteright ,'\'',
- rtfSC_parenleft ,'(',
- rtfSC_parenright ,')',
- rtfSC_asterisk ,'*',
- rtfSC_plus ,'+',
- rtfSC_comma ,',',
- rtfSC_hyphen ,'-',
- rtfSC_period ,'.',
- rtfSC_slash ,'/',
- rtfSC_zero ,'0',
- rtfSC_one ,'1',
- rtfSC_two ,'2',
- rtfSC_three ,'3',
- rtfSC_four ,'4',
- rtfSC_five ,'5',
- rtfSC_six ,'6',
- rtfSC_seven ,'7',
- rtfSC_eight ,'8',
- rtfSC_nine ,'9',
- rtfSC_colon ,':',
- rtfSC_semicolon ,';',
- rtfSC_less ,'<',
- rtfSC_equal ,'=',
- rtfSC_greater ,'>',
- rtfSC_question ,'?',
- rtfSC_at ,'@',
- rtfSC_A ,'A',
- rtfSC_B ,'B',
- rtfSC_C ,'C',
- rtfSC_D ,'D',
- rtfSC_E ,'E',
- rtfSC_F ,'F',
- rtfSC_G ,'G',
- rtfSC_H ,'H',
- rtfSC_I ,'I',
- rtfSC_J ,'J',
- rtfSC_K ,'K',
- rtfSC_L ,'L',
- rtfSC_M ,'M',
- rtfSC_N ,'N',
- rtfSC_O ,'O',
- rtfSC_P ,'P',
- rtfSC_Q ,'Q',
- rtfSC_R ,'R',
- rtfSC_S ,'S',
- rtfSC_T ,'T',
- rtfSC_U ,'U',
- rtfSC_V ,'V',
- rtfSC_W ,'W',
- rtfSC_X ,'X',
- rtfSC_Y ,'Y',
- rtfSC_Z ,'Z',
- rtfSC_bracketleft ,'[',
- rtfSC_backslash ,'\\',
- rtfSC_bracketright ,']',
- rtfSC_asciicircum ,'^',
- rtfSC_underscore ,'_',
- rtfSC_quoteleft ,'`',
- rtfSC_a ,'a',
- rtfSC_b ,'b',
- rtfSC_c ,'c',
- rtfSC_d ,'d',
- rtfSC_e ,'e',
- rtfSC_f ,'f',
- rtfSC_g ,'g',
- rtfSC_h ,'h',
- rtfSC_i ,'i',
- rtfSC_j ,'j',
- rtfSC_k ,'k',
- rtfSC_l ,'l',
- rtfSC_m ,'m',
- rtfSC_n ,'n',
- rtfSC_o ,'o',
- rtfSC_p ,'p',
- rtfSC_q ,'q',
- rtfSC_r ,'r',
- rtfSC_s ,'s',
- rtfSC_t ,'t',
- rtfSC_u ,'u',
- rtfSC_v ,'v',
- rtfSC_w ,'w',
- rtfSC_x ,'x',
- rtfSC_y ,'y',
- rtfSC_z ,'z',
- rtfSC_braceleft ,'{',
- rtfSC_bar ,'|',
- rtfSC_braceright ,'}',
- rtfSC_asciitilde ,'~',
- rtfSC_nobrkspace ,0xa0,
- rtfSC_exclamdown ,0xa1,
- rtfSC_cent ,0xa2,
- rtfSC_sterling ,0xa3,
- rtfSC_currency ,0xa4,
- rtfSC_yen ,0xa5,
- rtfSC_brokenbar ,0xa6,
- rtfSC_section ,0xa7,
- rtfSC_dieresis ,0xa8,
- rtfSC_copyright ,0xa9,
- rtfSC_ordfeminine ,0xaa,
- rtfSC_guillemotleft ,0xab,
- rtfSC_logicalnot ,0xac,
- rtfSC_opthyphen ,0xad,
- rtfSC_registered ,0xae,
- rtfSC_macron ,0xaf,
- rtfSC_degree ,0xb0,
- rtfSC_plusminus ,0xb1,
- rtfSC_twosuperior ,0xb2,
- rtfSC_threesuperior ,0xb3,
- rtfSC_acute ,0xb4,
- rtfSC_mu ,0xb5,
- rtfSC_paragraph ,0xb6,
- rtfSC_periodcentered ,0xb7,
- rtfSC_cedilla ,0xb8,
- rtfSC_onesuperior ,0xb9,
- rtfSC_ordmasculine ,0xba,
- rtfSC_guillemotright ,0xbb,
- rtfSC_onequarter ,0xbc,
- rtfSC_onehalf ,0xbd,
- rtfSC_threequarters ,0xbe,
- rtfSC_questiondown ,0xbf,
- rtfSC_Agrave ,0xc0,
- rtfSC_Aacute ,0xc1,
- rtfSC_Acircumflex ,0xc2,
- rtfSC_Atilde ,0xc3,
- rtfSC_Adieresis ,0xc4,
- rtfSC_Aring ,0xc5,
- rtfSC_AE ,0xc6,
- rtfSC_Ccedilla ,0xc7,
- rtfSC_Egrave ,0xc8,
- rtfSC_Eacute ,0xc9,
- rtfSC_Ecircumflex ,0xca,
- rtfSC_Edieresis ,0xcb,
- rtfSC_Igrave ,0xcc,
- rtfSC_Iacute ,0xcd,
- rtfSC_Icircumflex ,0xce,
- rtfSC_Idieresis ,0xcf,
- rtfSC_Eth ,0xd0,
- rtfSC_Ntilde ,0xd1,
- rtfSC_Ograve ,0xd2,
- rtfSC_Oacute ,0xd3,
- rtfSC_Ocircumflex ,0xd4,
- rtfSC_Otilde ,0xd5,
- rtfSC_Odieresis ,0xd6,
- rtfSC_multiply ,0xd7,
- rtfSC_Oslash ,0xd8,
- rtfSC_Ugrave ,0xd9,
- rtfSC_Uacute ,0xda,
- rtfSC_Ucircumflex ,0xdb,
- rtfSC_Udieresis ,0xdc,
- rtfSC_Yacute ,0xdd,
- rtfSC_Thorn ,0xde,
- rtfSC_germandbls ,0xdf,
- rtfSC_agrave ,0xe0,
- rtfSC_aacute ,0xe1,
- rtfSC_acircumflex ,0xe2,
- rtfSC_atilde ,0xe3,
- rtfSC_adieresis ,0xe4,
- rtfSC_aring ,0xe5,
- rtfSC_ae ,0xe6,
- rtfSC_ccedilla ,0xe7,
- rtfSC_egrave ,0xe8,
- rtfSC_eacute ,0xe9,
- rtfSC_ecircumflex ,0xea,
- rtfSC_edieresis ,0xeb,
- rtfSC_igrave ,0xec,
- rtfSC_iacute ,0xed,
- rtfSC_icircumflex ,0xee,
- rtfSC_idieresis ,0xef,
- rtfSC_eth ,0xf0,
- rtfSC_ntilde ,0xf1,
- rtfSC_ograve ,0xf2,
- rtfSC_oacute ,0xf3,
- rtfSC_ocircumflex ,0xf4,
- rtfSC_otilde ,0xf5,
- rtfSC_odieresis ,0xf6,
- rtfSC_divide ,0xf7,
- rtfSC_oslash ,0xf8,
- rtfSC_ugrave ,0xf9,
- rtfSC_uacute ,0xfa,
- rtfSC_ucircumflex ,0xfb,
- rtfSC_udieresis ,0xfc,
- rtfSC_yacute ,0xfd,
- rtfSC_thorn ,0xfe,
- rtfSC_ydieresis ,0xff
-};
+/* ---------------------------------------------------------------------- */
/*
- * RTF ANSI character set (\ansi) Symbol font map
- *
- * Field 1 is the standard character name which the character value in
- * field 2 maps onto. (It doesn't mean "to produce the character in field 1,
- * use the value in field 2.)
- *
- * The character value may be given either as a single character (which will be
- * converted to the ASCII value of the character), or in numeric format, either
- * in decimal or 0xyy as hex yy. Single or double quotes may be used to quote
- * characters.
- *
+ * Memory allocation routines
*/
-int ansi_sym[] =
-{
- rtfSC_formula ,0x06,
- rtfSC_nobrkhyphen ,0x1e,
- rtfSC_opthyphen ,0x1f,
- rtfSC_space ,' ',
- rtfSC_exclam ,'!',
- rtfSC_universal ,'"',
- rtfSC_mathnumbersign ,'#',
- rtfSC_existential ,'$',
- rtfSC_percent ,'%',
- rtfSC_ampersand ,'&',
- rtfSC_suchthat ,'\\',
- rtfSC_parenleft ,'(',
- rtfSC_parenright ,')',
- rtfSC_mathasterisk ,'*',
- rtfSC_mathplus ,'+',
- rtfSC_comma ,',',
- rtfSC_mathminus ,'-',
- rtfSC_period ,'.',
- rtfSC_slash ,'/',
- rtfSC_zero ,'0',
- rtfSC_one ,'1',
- rtfSC_two ,'2',
- rtfSC_three ,'3',
- rtfSC_four ,'4',
- rtfSC_five ,'5',
- rtfSC_six ,'6',
- rtfSC_seven ,'7',
- rtfSC_eight ,'8',
- rtfSC_nine ,'9',
- rtfSC_colon ,':',
- rtfSC_semicolon ,';',
- rtfSC_less ,'<',
- rtfSC_mathequal ,'=',
- rtfSC_greater ,'>',
- rtfSC_question ,'?',
- rtfSC_congruent ,'@',
- rtfSC_Alpha ,'A',
- rtfSC_Beta ,'B',
- rtfSC_Chi ,'C',
- rtfSC_Delta ,'D',
- rtfSC_Epsilon ,'E',
- rtfSC_Phi ,'F',
- rtfSC_Gamma ,'G',
- rtfSC_Eta ,'H',
- rtfSC_Iota ,'I',
- rtfSC_Kappa ,'K',
- rtfSC_Lambda ,'L',
- rtfSC_Mu ,'M',
- rtfSC_Nu ,'N',
- rtfSC_Omicron ,'O',
- rtfSC_Pi ,'P',
- rtfSC_Theta ,'Q',
- rtfSC_Rho ,'R',
- rtfSC_Sigma ,'S',
- rtfSC_Tau ,'T',
- rtfSC_Upsilon ,'U',
- rtfSC_varsigma ,'V',
- rtfSC_Omega ,'W',
- rtfSC_Xi ,'X',
- rtfSC_Psi ,'Y',
- rtfSC_Zeta ,'Z',
- rtfSC_bracketleft ,'[',
- rtfSC_backslash ,'\\',
- rtfSC_bracketright ,']',
- rtfSC_asciicircum ,'^',
- rtfSC_underscore ,'_',
- rtfSC_quoteleft ,'`',
- rtfSC_alpha ,'a',
- rtfSC_beta ,'b',
- rtfSC_chi ,'c',
- rtfSC_delta ,'d',
- rtfSC_epsilon ,'e',
- rtfSC_phi ,'f',
- rtfSC_gamma ,'g',
- rtfSC_eta ,'h',
- rtfSC_iota ,'i',
- rtfSC_kappa ,'k',
- rtfSC_lambda ,'l',
- rtfSC_mu ,'m',
- rtfSC_nu ,'n',
- rtfSC_omicron ,'o',
- rtfSC_pi ,'p',
- rtfSC_theta ,'q',
- rtfSC_rho ,'r',
- rtfSC_sigma ,'s',
- rtfSC_tau ,'t',
- rtfSC_upsilon ,'u',
- rtfSC_omega ,'w',
- rtfSC_xi ,'x',
- rtfSC_psi ,'y',
- rtfSC_zeta ,'z',
- rtfSC_braceleft ,'{',
- rtfSC_bar ,'|',
- rtfSC_braceright ,'}',
- rtfSC_mathtilde ,'~'
-};
/*
- * Output sequence map for rtf2text
- *
- * Field 1 is the standard character name. Field 2 is the output sequence
- * to produce for that character.
- *
- * The output sequence is simply a string of characters. If it contains
- * whitespace, it may be quoted. If it contains quotes, it may be quoted
- * with a different quote character.
- *
- * characters in ASCII range (32-127
+ * Return pointer to block of size bytes, or NULL if there's
+ * not enough memory available.
*/
+static inline void *RTFAlloc(int size)
+{
+ return HeapAlloc(me_heap, 0, size);
+}
+
+
+static inline void * RTFReAlloc(void *ptr, int size)
+{
+ return HeapReAlloc(me_heap, 0, ptr, size);
+}
-const char *text_map[] = {
- "space" ," ",
- "exclam" ,"!",
- "quotedbl" ,"\"",
- "numbersign" ,"#",
- "dollar" ,"$",
- "percent" ,"%",
- "ampersand" ,"&",
- "quoteright" ,"'",
- "parenleft" ,"(",
- "parenright" ,")",
- "asterisk" ,"*",
- "plus" ,"+",
- "comma" ,",",
- "hyphen" ,"-",
- "period" ,".",
- "slash" ,"/",
- "zero" ,"0",
- "one" ,"1",
- "two" ,"2",
- "three" ,"3",
- "four" ,"4",
- "five" ,"5",
- "six" ,"6",
- "seven" ,"7",
- "eight" ,"8",
- "nine" ,"9",
- "colon" ,":",
- "semicolon" ,";",
- "less" ,"<",
- "equal" ,"=",
- "greater" ,">",
- "question" ,"?",
- "at" ,"@",
- "A" ,"A",
- "B" ,"B",
- "C" ,"C",
- "D" ,"D",
- "E" ,"E",
- "F" ,"F",
- "G" ,"G",
- "H" ,"H",
- "I" ,"I",
- "J" ,"J",
- "K" ,"K",
- "L" ,"L",
- "M" ,"M",
- "N" ,"N",
- "O" ,"O",
- "P" ,"P",
- "Q" ,"Q",
- "R" ,"R",
- "S" ,"S",
- "T" ,"T",
- "U" ,"U",
- "V" ,"V",
- "W" ,"W",
- "X" ,"X",
- "Y" ,"Y",
- "Z" ,"Z",
- "bracketleft" ,"[",
- "backslash" ,"\\",
- "bracketright" ,"]",
- "asciicircum" ,"^",
- "underscore" ,"_",
- "quoteleft" ,"`",
- "a" ,"a",
- "b" ,"b",
- "c" ,"c",
- "d" ,"d",
- "e" ,"e",
- "f" ,"f",
- "g" ,"g",
- "h" ,"h",
- "i" ,"i",
- "j" ,"j",
- "k" ,"k",
- "l" ,"l",
- "m" ,"m",
- "n" ,"n",
- "o" ,"o",
- "p" ,"p",
- "q" ,"q",
- "r" ,"r",
- "s" ,"s",
- "t" ,"t",
- "u" ,"u",
- "v" ,"v",
- "w" ,"w",
- "x" ,"x",
- "y" ,"y",
- "z" ,"z",
- "braceleft" ,"{",
- "bar" ,"|",
- "braceright" ,"}",
- "asciitilde" ,"~",
- "AE" ,"AE",
- "OE" ,"OE",
- "acute" ,"'",
- "ae" ,"ae",
- "angleleft" ,"<",
- "angleright" ,">",
- "arrowboth" ,"<->",
- "arrowdblboth" ,"<=>",
- "arrowdblleft" ,"<=",
- "arrowdblright" ,"=>",
- "arrowleft" ,"<-",
- "arrowright" ,"->",
- "bullet" ,"o",
- "cent" ,"cent",
- "circumflex" ,"^",
- "copyright" ,"(c)",
- "copyrightsans" ,"(c)",
- "degree" ,"deg.",
- "divide" ,"/",
- "dotlessi" ,"i",
- "ellipsis" ,"...",
- "emdash" ,"--",
- "endash" ,"-",
- "fi" ,"fi",
- "fl" ,"fl",
- "fraction" ,"/",
- "germandbls" ,"ss",
- "grave" ,"`",
- "greaterequal" ,">=",
- "guillemotleft" ,"<<",
- "guillemotright" ,">>",
- "guilsinglleft" ,"<",
- "guilsinglright" ,">",
- "lessequal" ,"<=",
- "logicalnot" ,"~",
- "mathasterisk" ,"*",
- "mathequal" ,"=",
- "mathminus" ,"-",
- "mathnumbersign" ,"#",
- "mathplus" ,"+",
- "mathtilde" ,"~",
- "minus" ,"-",
- "mu" ,"u",
- "multiply" ,"x",
- "nobrkhyphen" ,"-",
- "nobrkspace" ," ",
- "notequal" ,"!=",
- "oe" ,"oe",
- "onehalf" ,"1/2",
- "onequarter" ,"1/4",
- "periodcentered" ,".",
- "plusminus" ,"+/-",
- "quotedblbase" ,",,",
- "quotedblleft" ,"\"",
- "quotedblright" ,"\"",
- "quotesinglbase" ,",",
- "registered" ,"reg.",
- "registersans" ,"reg.",
- "threequarters" ,"3/4",
- "tilde" ,"~",
- "trademark" ,"(TM)",
- "trademarksans" ,"(TM)"
-};
/*
- * This array is used to map standard character names onto their numeric codes.
- * The position of the name within the array is the code.
- * stdcharnames.h is generated in the ../h directory.
+ * Saves a string on the heap and returns a pointer to it.
*/
+static inline char *RTFStrSave(char *s)
+{
+ char *p;
+
+ p = RTFAlloc (lstrlenA(s) + 1);
+ if (p == NULL)
+ return NULL;
+ return lstrcpyA (p, s);
+}
+
+
+static inline void RTFFree(void *p)
+{
+ HeapFree(me_heap, 0, p);
+}
+
+
+/* ---------------------------------------------------------------------- */
-const char *stdCharName[] =
-{
- "nothing",
- "space",
- "exclam",
- "quotedbl",
- "numbersign",
- "dollar",
- "percent",
- "ampersand",
- "quoteright",
- "parenleft",
- "parenright",
- "asterisk",
- "plus",
- "comma",
- "hyphen",
- "period",
- "slash",
- "zero",
- "one",
- "two",
- "three",
- "four",
- "five",
- "six",
- "seven",
- "eight",
- "nine",
- "colon",
- "semicolon",
- "less",
- "equal",
- "greater",
- "question",
- "at",
- "A",
- "B",
- "C",
- "D",
- "E",
- "F",
- "G",
- "H",
- "I",
- "J",
- "K",
- "L",
- "M",
- "N",
- "O",
- "P",
- "Q",
- "R",
- "S",
- "T",
- "U",
- "V",
- "W",
- "X",
- "Y",
- "Z",
- "bracketleft",
- "backslash",
- "bracketright",
- "asciicircum",
- "underscore",
- "quoteleft",
- "a",
- "b",
- "c",
- "d",
- "e",
- "f",
- "g",
- "h",
- "i",
- "j",
- "k",
- "l",
- "m",
- "n",
- "o",
- "p",
- "q",
- "r",
- "s",
- "t",
- "u",
- "v",
- "w",
- "x",
- "y",
- "z",
- "braceleft",
- "bar",
- "braceright",
- "asciitilde",
- "exclamdown",
- "cent",
- "sterling",
- "fraction",
- "yen",
- "florin",
- "section",
- "currency",
- "quotedblleft",
- "guillemotleft",
- "guilsinglleft",
- "guilsinglright",
- "fi",
- "fl",
- "endash",
- "dagger",
- "daggerdbl",
- "periodcentered",
- "paragraph",
- "bullet",
- "quotesinglbase",
- "quotedblbase",
- "quotedblright",
- "guillemotright",
- "ellipsis",
- "perthousand",
- "questiondown",
- "grave",
- "acute",
- "circumflex",
- "tilde",
- "macron",
- "breve",
- "dotaccent",
- "dieresis",
- "ring",
- "cedilla",
- "hungarumlaut",
- "ogonek",
- "caron",
- "emdash",
- "AE",
- "ordfeminine",
- "Lslash",
- "Oslash",
- "OE",
- "ordmasculine",
- "ae",
- "dotlessi",
- "lslash",
- "oslash",
- "oe",
- "germandbls",
- "Aacute",
- "Acircumflex",
- "Adieresis",
- "Agrave",
- "Aring",
- "Atilde",
- "Ccedilla",
- "Eacute",
- "Ecircumflex",
- "Edieresis",
- "Egrave",
- "Eth",
- "Iacute",
- "Icircumflex",
- "Idieresis",
- "Igrave",
- "Ntilde",
- "Oacute",
- "Ocircumflex",
- "Odieresis",
- "Ograve",
- "Otilde",
- "Scaron",
- "Thorn",
- "Uacute",
- "Ucircumflex",
- "Udieresis",
- "Ugrave",
- "Yacute",
- "Ydieresis",
- "aacute",
- "acircumflex",
- "adieresis",
- "agrave",
- "aring",
- "atilde",
- "brokenbar",
- "ccedilla",
- "copyright",
- "degree",
- "divide",
- "eacute",
- "ecircumflex",
- "edieresis",
- "egrave",
- "eth",
- "iacute",
- "icircumflex",
- "idieresis",
- "igrave",
- "logicalnot",
- "minus",
- "multiply",
- "ntilde",
- "oacute",
- "ocircumflex",
- "odieresis",
- "ograve",
- "onehalf",
- "onequarter",
- "onesuperior",
- "otilde",
- "plusminus",
- "registered",
- "thorn",
- "threequarters",
- "threesuperior",
- "trademark",
- "twosuperior",
- "uacute",
- "ucircumflex",
- "udieresis",
- "ugrave",
- "yacute",
- "ydieresis",
- "Alpha",
- "Beta",
- "Chi",
- "Delta",
- "Epsilon",
- "Phi",
- "Gamma",
- "Eta",
- "Iota",
- "Kappa",
- "Lambda",
- "Mu",
- "Nu",
- "Omicron",
- "Pi",
- "Theta",
- "Rho",
- "Sigma",
- "Tau",
- "Upsilon",
- "varUpsilon",
- "Omega",
- "Xi",
- "Psi",
- "Zeta",
- "alpha",
- "beta",
- "chi",
- "delta",
- "epsilon",
- "phi",
- "varphi",
- "gamma",
- "eta",
- "iota",
- "kappa",
- "lambda",
- "mu",
- "nu",
- "omicron",
- "pi",
- "varpi",
- "theta",
- "vartheta",
- "rho",
- "sigma",
- "varsigma",
- "tau",
- "upsilon",
- "omega",
- "xi",
- "psi",
- "zeta",
- "nobrkspace",
- "nobrkhyphen",
- "lessequal",
- "greaterequal",
- "infinity",
- "integral",
- "notequal",
- "radical",
- "radicalex",
- "approxequal",
- "apple",
- "partialdiff",
- "opthyphen",
- "formula",
- "lozenge",
- "universal",
- "existential",
- "suchthat",
- "congruent",
- "therefore",
- "perpendicular",
- "minute",
- "club",
- "diamond",
- "heart",
- "spade",
- "arrowboth",
- "arrowleft",
- "arrowup",
- "arrowright",
- "arrowdown",
- "second",
- "proportional",
- "equivalence",
- "arrowvertex",
- "arrowhorizex",
- "carriagereturn",
- "aleph",
- "Ifraktur",
- "Rfraktur",
- "weierstrass",
- "circlemultiply",
- "circleplus",
- "emptyset",
- "intersection",
- "union",
- "propersuperset",
- "reflexsuperset",
- "notsubset",
- "propersubset",
- "reflexsubset",
- "element",
- "notelement",
- "angle",
- "gradient",
- "product",
- "logicaland",
- "logicalor",
- "arrowdblboth",
- "arrowdblleft",
- "arrowdblup",
- "arrowdblright",
- "arrowdbldown",
- "angleleft",
- "registersans",
- "copyrightsans",
- "trademarksans",
- "angleright",
- "mathplus",
- "mathminus",
- "mathasterisk",
- "mathnumbersign",
- "dotmath",
- "mathequal",
- "mathtilde",
- (char *) NULL
-};
int _RTFGetChar(RTF_Info *info)
{
int ch;
+ ME_InStream *stream = info->stream;
TRACE("\n");
/* if the last buffer wasn't full, it's EOF */
- if (info->dwInputSize > 0 &&
- info->dwInputSize == info->dwInputUsed && info->dwInputSize < sizeof(info->InputBuffer))
- return EOF;
- if (info->dwInputSize <= info->dwInputUsed)
+ if (stream->dwSize > 0 && stream->dwSize == stream->dwUsed
+ && stream->dwSize < sizeof(stream->buffer))
+ return EOF;
+ if (stream->dwSize <= stream->dwUsed)
{
- long count = 0;
- info->editstream.dwError = info->editstream.pfnCallback(info->editstream.dwCookie,
- info->InputBuffer, sizeof(info->InputBuffer), &count);
- /* if error, it's EOF */
- if (info->editstream.dwError)
- return EOF;
- /* if no bytes read, it's EOF */
- if(count == 0)
+ ME_StreamInFill(stream);
+ /* if error, it's EOF */
+ if (stream->editstream->dwError)
+ return EOF;
+ /* if no bytes read, it's EOF */
+ if (stream->dwSize == 0)
return EOF;
- info->dwInputSize = count;
- info->dwInputUsed = 0;
}
- ch = info->InputBuffer[info->dwInputUsed++];
+ ch = stream->buffer[stream->dwUsed++];
if (!ch)
return EOF;
return ch;
}
-void RTFSetEditStream(RTF_Info *info, EDITSTREAM *es)
+void RTFSetEditStream(RTF_Info *info, ME_InStream *stream)
{
TRACE("\n");
- info->editstream.dwCookie = es->dwCookie;
- info->editstream.dwError = es->dwError;
- info->editstream.pfnCallback = es->pfnCallback;
+ info->stream = stream;
}
+static void
+RTFDestroyAttrs(RTF_Info *info)
+{
+ RTFColor *cp;
+ RTFFont *fp;
+ RTFStyle *sp;
+ RTFStyleElt *eltList, *ep;
+
+ while (info->fontList)
+ {
+ fp = info->fontList->rtfNextFont;
+ RTFFree (info->fontList->rtfFName);
+ RTFFree (info->fontList);
+ info->fontList = fp;
+ }
+ while (info->colorList)
+ {
+ cp = info->colorList->rtfNextColor;
+ RTFFree (info->colorList);
+ info->colorList = cp;
+ }
+ while (info->styleList)
+ {
+ sp = info->styleList->rtfNextStyle;
+ eltList = info->styleList->rtfSSEList;
+ while (eltList)
+ {
+ ep = eltList->rtfNextSE;
+ RTFFree (eltList->rtfSEText);
+ RTFFree (eltList);
+ eltList = ep;
+ }
+ RTFFree (info->styleList->rtfSName);
+ RTFFree (info->styleList);
+ info->styleList = sp;
+ }
+}
+
+
+void
+RTFDestroy(RTF_Info *info)
+{
+ if (info->rtfTextBuf)
+ {
+ RTFFree(info->rtfTextBuf);
+ RTFFree(info->pushedTextBuf);
+ }
+ RTFDestroyAttrs(info);
+ RTFFree(info->cpOutputBuffer);
+}
+
+
/*
* Initialize the reader. This may be called multiple times,
* to read multiple files. The only thing not reset is the input
void RTFInit(RTF_Info *info)
{
int i;
- RTFColor *cp;
- RTFFont *fp;
- RTFStyle *sp;
- RTFStyleElt *eltList, *ep;
TRACE("\n");
- if (info->rtfTextBuf == (char *) NULL) /* initialize the text buffers */
+ if (info->rtfTextBuf == NULL) /* initialize the text buffers */
{
info->rtfTextBuf = RTFAlloc (rtfBufSiz);
info->pushedTextBuf = RTFAlloc (rtfBufSiz);
- if (info->rtfTextBuf == (char *) NULL
- || info->pushedTextBuf == (char *) NULL)
+ if (info->rtfTextBuf == NULL || info->pushedTextBuf == NULL)
RTFPanic (info,"Cannot allocate text buffers.");
info->rtfTextBuf[0] = info->pushedTextBuf[0] = '\0';
}
RTFFree (info->inputName);
RTFFree (info->outputName);
- info->inputName = info->outputName = (char *) NULL;
+ info->inputName = info->outputName = NULL;
/* initialize lookup table */
LookupInit ();
for (i = 0; i < rtfMaxClass; i++)
- RTFSetClassCallback (info, i, (RTFFuncPtr) NULL);
+ RTFSetClassCallback (info, i, NULL);
for (i = 0; i < rtfMaxDestination; i++)
- RTFSetDestinationCallback (info, i, (RTFFuncPtr) NULL);
+ RTFSetDestinationCallback (info, i, NULL);
/* install built-in destination readers */
RTFSetDestinationCallback (info, rtfFontTbl, ReadFontTbl);
RTFSetDestinationCallback (info, rtfObject, ReadObjGroup);
- RTFSetReadHook (info, (RTFFuncPtr) NULL);
+ RTFSetReadHook (info, NULL);
/* dump old lists if necessary */
- while (info->fontList != (RTFFont *) NULL)
- {
- fp = info->fontList->rtfNextFont;
- RTFFree (info->fontList->rtfFName);
- RTFFree ((char *) info->fontList);
- info->fontList = fp;
- }
- while (info->colorList != (RTFColor *) NULL)
- {
- cp = info->colorList->rtfNextColor;
- RTFFree ((char *) info->colorList);
- info->colorList = cp;
- }
- while (info->styleList != (RTFStyle *) NULL)
- {
- sp = info->styleList->rtfNextStyle;
- eltList = info->styleList->rtfSSEList;
- while (eltList != (RTFStyleElt *) NULL)
- {
- ep = eltList->rtfNextSE;
- RTFFree (eltList->rtfSEText);
- RTFFree ((char *) eltList);
- eltList = ep;
- }
- RTFFree (info->styleList->rtfSName);
- RTFFree ((char *) info->styleList);
- info->styleList = sp;
- }
+ RTFDestroyAttrs(info);
+
+ info->ansiCodePage = 1252; /* Latin-1; actually unused */
+ info->unicodeLength = 1; /* \uc1 is the default */
+ info->codePage = info->ansiCodePage;
info->rtfClass = -1;
info->pushedClass = -1;
info->prevChar = EOF;
info->bumpLine = 0;
- CharSetInit (info);
- info->csTop = 0;
+ info->dwCPOutputCount = 0;
+ if (!info->cpOutputBuffer)
+ {
+ info->dwMaxCPOutputCount = 0x1000;
+ info->cpOutputBuffer = RTFAlloc(info->dwMaxCPOutputCount);
+ }
}
/*
{
TRACE("\n");
- if ((info->inputName = RTFStrSave (name)) == (char *) NULL)
+ info->inputName = RTFStrSave (name);
+ if (info->inputName == NULL)
RTFPanic (info,"RTFSetInputName: out of memory");
}
{
TRACE("\n");
- if ((info->outputName = RTFStrSave (name)) == (char *) NULL)
+ info->outputName = RTFStrSave (name);
+ if (info->outputName == NULL)
RTFPanic (info, "RTFSetOutputName: out of memory");
}
RTFFuncPtr RTFGetClassCallback(RTF_Info *info, int class)
{
if (class >= 0 && class < rtfMaxClass)
- return (info->ccb[class]);
- return ((RTFFuncPtr) NULL);
+ return info->ccb[class];
+ return NULL;
}
RTFFuncPtr RTFGetDestinationCallback(RTF_Info *info, int dest)
{
if (dest >= 0 && dest < rtfMaxDestination)
- return (info->dcb[dest]);
- return ((RTFFuncPtr) NULL);
+ return info->dcb[dest];
+ return NULL;
}
if (RTFCheckCM (info, rtfControl, rtfDestination))
{
/* invoke destination-specific callback if there is one */
- if ((p = RTFGetDestinationCallback (info, info->rtfMinor))
- != (RTFFuncPtr) NULL)
+ p = RTFGetDestinationCallback (info, info->rtfMinor);
+ if (p != NULL)
{
(*p) (info);
return;
}
}
/* invoke class callback if there is one */
- if ((p = RTFGetClassCallback (info, info->rtfClass)) != (RTFFuncPtr) NULL)
+ p = RTFGetClassCallback (info, info->rtfClass);
+ if (p != NULL)
(*p) (info);
}
for (;;)
{
_RTFGetToken (info);
- if ((p = RTFGetReadHook (info)) != (RTFFuncPtr) NULL)
+ p = RTFGetReadHook (info);
+ if (p != NULL)
(*p) (info); /* give read hook a look at token */
/* Silently discard newlines, carriage returns, nulls. */
info->pushedMajor = info->rtfMajor;
info->pushedMinor = info->rtfMinor;
info->pushedParam = info->rtfParam;
- (void) strcpy (info->pushedTextBuf, info->rtfTextBuf);
+ lstrcpyA (info->pushedTextBuf, info->rtfTextBuf);
}
static void _RTFGetToken(RTF_Info *info)
{
- RTFFont *fp;
-
TRACE("\n");
- if (info->rtfFormat == SF_TEXT) {
- info->rtfMajor = GetChar (info);
- info->rtfMinor = rtfSC_nothing;
- info->rtfParam = rtfNoParam;
- info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
- if (info->rtfMajor == EOF)
- info->rtfClass = rtfEOF;
- else
- info->rtfClass = rtfText;
- return;
+ if (info->rtfFormat == SF_TEXT)
+ {
+ info->rtfMajor = GetChar (info);
+ info->rtfMinor = 0;
+ info->rtfParam = rtfNoParam;
+ info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
+ if (info->rtfMajor == EOF)
+ info->rtfClass = rtfEOF;
+ else
+ info->rtfClass = rtfText;
+ return;
}
/* first check for pushed token from RTFUngetToken() */
info->rtfMajor = info->pushedMajor;
info->rtfMinor = info->pushedMinor;
info->rtfParam = info->pushedParam;
- (void) strcpy (info->rtfTextBuf, info->pushedTextBuf);
- info->rtfTextLen = strlen (info->rtfTextBuf);
+ lstrcpyA (info->rtfTextBuf, info->pushedTextBuf);
+ info->rtfTextLen = lstrlenA(info->rtfTextBuf);
info->pushedClass = -1;
return;
}
*/
_RTFGetToken2 (info);
- if (info->rtfClass == rtfText) /* map RTF char to standard code */
- info->rtfMinor = RTFMapChar (info, info->rtfMajor);
-
- /*
- * If auto-charset stuff is activated, see if anything needs doing,
- * like reading the charset maps or switching between them.
- */
+}
- if (info->autoCharSetFlags == 0)
- return;
- if ((info->autoCharSetFlags & rtfReadCharSet)
- && RTFCheckCM (info, rtfControl, rtfCharSet))
- {
- ReadCharSetMaps (info);
- }
- else if ((info->autoCharSetFlags & rtfSwitchCharSet)
- && RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
- {
- if ((fp = RTFGetFont (info, info->rtfParam)) != (RTFFont *) NULL)
- {
- if (strncmp (fp->rtfFName, "Symbol", 6) == 0)
- info->curCharSet = rtfCSSymbol;
- else
- info->curCharSet = rtfCSGeneral;
- RTFSetCharSet (info, info->curCharSet);
- }
- }
- else if ((info->autoCharSetFlags & rtfSwitchCharSet) && info->rtfClass == rtfGroup)
- {
- switch (info->rtfMajor)
+int
+RTFCharSetToCodePage(RTF_Info *info, int charset)
+{
+ switch (charset)
+ {
+ case ANSI_CHARSET:
+ return 1252;
+ case DEFAULT_CHARSET:
+ return CP_ACP;
+ case SYMBOL_CHARSET:
+ return CP_SYMBOL;
+ case MAC_CHARSET:
+ return CP_MACCP;
+ case SHIFTJIS_CHARSET:
+ return 932;
+ case HANGEUL_CHARSET:
+ return 949;
+ case JOHAB_CHARSET:
+ return 1361;
+ case GB2312_CHARSET:
+ return 936;
+ case CHINESEBIG5_CHARSET:
+ return 950;
+ case GREEK_CHARSET:
+ return 1253;
+ case TURKISH_CHARSET:
+ return 1254;
+ case VIETNAMESE_CHARSET:
+ return 1258;
+ case HEBREW_CHARSET:
+ return 1255;
+ case ARABIC_CHARSET:
+ return 1256;
+ case BALTIC_CHARSET:
+ return 1257;
+ case RUSSIAN_CHARSET:
+ return 1251;
+ case THAI_CHARSET:
+ return 874;
+ case EASTEUROPE_CHARSET:
+ return 1250;
+ case OEM_CHARSET:
+ return CP_OEMCP;
+ default:
{
- case rtfBeginGroup:
- if (info->csTop >= maxCSStack)
- RTFPanic (info, "_RTFGetToken: stack overflow");
- info->csStack[info->csTop++] = info->curCharSet;
- break;
- case rtfEndGroup:
- /*
- * If stack top is 1 at this point, we are ending the
- * group started by the initial {, which ends the
- * RTF stream
- */
- if (info->csTop <= 0)
- RTFPanic (info,"_RTFGetToken: stack underflow");
- else if (info->csTop == 1)
- info->rtfClass = rtfEOF;
+ CHARSETINFO csi;
+ DWORD n = charset;
+
+ /* FIXME: TranslateCharsetInfo does not work as good as it
+ * should, so let's use it only when all else fails */
+ if (!TranslateCharsetInfo(&n, &csi, TCI_SRCCHARSET))
+ RTFMsg(info, "%s: unknown charset %u\n", __FUNCTION__, charset);
else
- {
- info->curCharSet = info->csStack[--info->csTop];
- RTFSetCharSet (info, info->curCharSet);
- }
- break;
+ return csi.ciACP;
}
}
+ return 0;
}
{
/* should do isxdigit check! */
info->rtfClass = rtfText;
- info->rtfMajor = RTFCharToHex (c) * 16
- + RTFCharToHex (c2);
+ info->rtfMajor = RTFCharToHex (c) * 16 + RTFCharToHex (c2);
return;
}
/* early eof, whoops (class is rtfUnknown) */
info->rtfMinor = minor;
info->rtfParam = param;
if (param == rtfNoParam)
- (void) strcpy (info->rtfTextBuf, text);
+ lstrcpyA(info->rtfTextBuf, text);
else
sprintf (info->rtfTextBuf, "%s%d", text, param);
- info->rtfTextLen = strlen (info->rtfTextBuf);
-}
-
-
-/* ---------------------------------------------------------------------- */
-
-/*
- * Routines to handle mapping of RTF character sets
- * onto standard characters.
- *
- * RTFStdCharCode(name) given char name, produce numeric code
- * RTFStdCharName(code) given char code, return name
- * RTFMapChar(c) map input (RTF) char code to std code
- * RTFSetCharSet(id) select given charset map
- * RTFGetCharSet() get current charset map
- *
- * See ../h/README for more information about charset names and codes.
- */
-
-
-/*
- * Initialize charset stuff.
- */
-
-static void CharSetInit(RTF_Info *info)
-{
- TRACE("\n");
-
- info->autoCharSetFlags = (rtfReadCharSet | rtfSwitchCharSet);
- RTFFree (info->genCharSetFile);
- info->genCharSetFile = (char *) NULL;
- info->haveGenCharSet = 0;
- RTFFree (info->symCharSetFile);
- info->symCharSetFile = (char *) NULL;
- info->haveSymCharSet = 0;
- info->curCharSet = rtfCSGeneral;
- info->curCharCode = info->genCharCode;
-}
-
-
-/*
- * Specify the name of a file to be read when auto-charset-file reading is
- * done.
- */
-
-void RTFSetCharSetMap (RTF_Info *info, char *name, int csId)
-{
- TRACE("\n");
-
- if ((name = RTFStrSave (name)) == (char *) NULL) /* make copy */
- RTFPanic (info,"RTFSetCharSetMap: out of memory");
- switch (csId)
- {
- case rtfCSGeneral:
- RTFFree (info->genCharSetFile); /* free any previous value */
- info->genCharSetFile = name;
- break;
- case rtfCSSymbol:
- RTFFree (info->symCharSetFile); /* free any previous value */
- info->symCharSetFile = name;
- break;
- }
-}
-
-
-/*
- * Do auto-charset-file reading.
- * will always use the ansi charset no mater what the value
- * of the rtfTextBuf is.
- *
- * TODO: add support for other charset in the future.
- *
- */
-
-static void ReadCharSetMaps(RTF_Info *info)
-{
- char buf[rtfBufSiz];
-
- TRACE("\n");
-
- if (info->genCharSetFile != (char *) NULL)
- (void) strcpy (buf, info->genCharSetFile);
- else
- sprintf (buf, "%s-gen", &info->rtfTextBuf[1]);
- if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
- RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
- if (info->symCharSetFile != (char *) NULL)
- (void) strcpy (buf, info->symCharSetFile);
- else
- sprintf (buf, "%s-sym", &info->rtfTextBuf[1]);
- if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
- RTFPanic (info,"ReadCharSetMaps: Cannot read charset map %s", buf);
-}
-
-
-
-/*
- * Convert a CharSetMap (character_name, character) into
- * this form : array[character_ident] = character;
- */
-
-int RTFReadCharSetMap(RTF_Info *info, int csId)
-{
- int *stdCodeArray;
- unsigned int i;
-
- TRACE("\n");
-
- switch (csId)
- {
- default:
- return (0); /* illegal charset id */
- case rtfCSGeneral:
-
- info->haveGenCharSet = 1;
- stdCodeArray = info->genCharCode;
- for (i = 0; i < charSetSize; i++)
- {
- stdCodeArray[i] = rtfSC_nothing;
- }
-
- for ( i = 0 ; i< sizeof(ansi_gen)/(sizeof(int));i+=2)
- {
- stdCodeArray[ ansi_gen[i+1] ] = ansi_gen[i];
- }
- break;
-
- case rtfCSSymbol:
-
- info->haveSymCharSet = 1;
- stdCodeArray = info->symCharCode;
- for (i = 0; i < charSetSize; i++)
- {
- stdCodeArray[i] = rtfSC_nothing;
- }
-
- for ( i = 0 ; i< sizeof(ansi_sym)/(sizeof(int));i+=2)
- {
- stdCodeArray[ ansi_sym[i+1] ] = ansi_sym[i];
- }
- break;
- }
-
- return (1);
-}
-
-
-/*
- * Given a standard character name (a string), find its code (a number).
- * Return -1 if name is unknown.
- */
-
-int RTFStdCharCode(RTF_Info *info, const char *name)
-{
- int i;
-
- TRACE("\n");
-
- for (i = 0; i < rtfSC_MaxChar; i++)
- {
- if (strcmp (name, stdCharName[i]) == 0)
- return (i);
- }
- return (-1);
-}
-
-
-/*
- * Given a standard character code (a number), find its name (a string).
- * Return NULL if code is unknown.
- */
-
-const char *RTFStdCharName(RTF_Info *info, int code)
-{
- if (code < 0 || code >= rtfSC_MaxChar)
- return ((char *) NULL);
- return (stdCharName[code]);
-}
-
-
-/*
- * Given an RTF input character code, find standard character code.
- * The translator should read the appropriate charset maps when it finds a
- * charset control. However, the file might not contain one. In this
- * case, no map will be available. When the first attempt is made to
- * map a character under these circumstances, RTFMapChar() assumes ANSI
- * and reads the map as necessary.
- */
-
-int RTFMapChar(RTF_Info *info, int c)
-{
- TRACE("\n");
-
- switch (info->curCharSet)
- {
- case rtfCSGeneral:
- if (!info->haveGenCharSet)
- {
- if (RTFReadCharSetMap (info, rtfCSGeneral) == 0)
- RTFPanic (info,"RTFMapChar: cannot read ansi-gen");
- }
- break;
- case rtfCSSymbol:
- if (!info->haveSymCharSet)
- {
- if (RTFReadCharSetMap (info, rtfCSSymbol) == 0)
- RTFPanic (info,"RTFMapChar: cannot read ansi-sym");
- }
- break;
- }
- if (c < 0 || c >= charSetSize)
- return (rtfSC_nothing);
- return (info->curCharCode[c]);
-}
-
-
-/*
- * Set the current character set. If csId is illegal, uses general charset.
- */
-
-void RTFSetCharSet(RTF_Info *info, int csId)
-{
- TRACE("\n");
-
- switch (csId)
- {
- default: /* use general if csId unknown */
- case rtfCSGeneral:
- info->curCharCode = info->genCharCode;
- info->curCharSet = csId;
- break;
- case rtfCSSymbol:
- info->curCharCode = info->symCharCode;
- info->curCharSet = csId;
- break;
- }
-}
-
-
-int RTFGetCharSet(RTF_Info *info)
-{
- return (info->curCharSet);
+ info->rtfTextLen = lstrlenA (info->rtfTextBuf);
}
for (;;)
{
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
break;
if (old < 0) /* first entry - determine tbl type */
{
if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
RTFPanic (info, "%s: missing \"{\"", fn);
- (void) RTFGetToken (info); /* yes, skip to next token */
+ RTFGetToken (info); /* yes, skip to next token */
}
- if ((fp = New (RTFFont)) == (RTFFont *) NULL)
+ fp = New (RTFFont);
+ if (fp == NULL)
RTFPanic (info, "%s: cannot allocate font entry", fn);
fp->rtfNextFont = info->fontList;
info->fontList = fp;
- fp->rtfFName = (char *) NULL;
- fp->rtfFAltName = (char *) NULL;
+ fp->rtfFName = NULL;
+ fp->rtfFAltName = NULL;
fp->rtfFNum = -1;
fp->rtfFFamily = 0;
- fp->rtfFCharSet = 0;
+ fp->rtfFCharSet = DEFAULT_CHARSET; /* 1 */
fp->rtfFPitch = 0;
fp->rtfFType = 0;
- fp->rtfFCodePage = 0;
+ fp->rtfFCodePage = CP_ACP;
while (info->rtfClass != rtfEOF
&& !RTFCheckCM (info, rtfText, ';')
break; /* ignore unknown? */
case rtfFontCharSet:
fp->rtfFCharSet = info->rtfParam;
+ if (!fp->rtfFCodePage)
+ fp->rtfFCodePage = RTFCharSetToCodePage(info, info->rtfParam);
break;
case rtfFontPitch:
fp->rtfFPitch = info->rtfParam;
&& !RTFCheckCM (info, rtfText, ';'))
{
*bp++ = info->rtfMajor;
- (void) RTFGetToken (info);
+ RTFGetToken (info);
}
/* FIX: in some cases the <fontinfo> isn't finished with a semi-column */
if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
{
- RTFUngetToken (info);
+ RTFUngetToken (info);
}
*bp = '\0';
fp->rtfFName = RTFStrSave (buf);
- if (fp->rtfFName == (char *) NULL)
+ if (fp->rtfFName == NULL)
RTFPanic (info, "%s: cannot allocate font name", fn);
/* already have next token; don't read one */
/* at bottom of loop */
RTFMsg (info, "%s: unknown token \"%s\"\n",
fn,info->rtfTextBuf);
}
- (void) RTFGetToken (info);
+ RTFGetToken (info);
}
if (old == 0) /* need to see "}" here */
{
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
RTFPanic (info, "%s: missing \"}\"", fn);
}
for (;;)
{
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
break;
- if ((cp = New (RTFColor)) == (RTFColor *) NULL)
+ cp = New (RTFColor);
+ if (cp == NULL)
RTFPanic (info,"%s: cannot allocate color entry", fn);
cp->rtfCNum = cnum++;
cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
}
RTFGetToken (info);
}
- if (!RTFCheckCM (info, rtfText, (int) ';'))
+ if (!RTFCheckCM (info, rtfText, ';'))
RTFPanic (info,"%s: malformed entry", fn);
}
RTFRouteToken (info); /* feed "}" back to router */
for (;;)
{
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
break;
- if ((sp = New (RTFStyle)) == (RTFStyle *) NULL)
+ sp = New (RTFStyle);
+ if (sp == NULL)
RTFPanic (info,"%s: cannot allocate stylesheet entry", fn);
- sp->rtfSName = (char *) NULL;
+ sp->rtfSName = NULL;
sp->rtfSNum = -1;
sp->rtfSType = rtfParStyle;
sp->rtfSAdditive = 0;
sp->rtfSBasedOn = rtfNoStyleNum;
sp->rtfSNextPar = -1;
- sp->rtfSSEList = sepLast = (RTFStyleElt *) NULL;
+ sp->rtfSSEList = sepLast = NULL;
sp->rtfNextStyle = info->styleList;
sp->rtfExpanding = 0;
info->styleList = sp;
RTFPanic (info,"%s: missing \"{\"", fn);
for (;;)
{
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (info->rtfClass == rtfEOF
|| RTFCheckCM (info, rtfText, ';'))
break;
sp->rtfSNextPar = info->rtfParam;
continue;
}
- if ((sep = New (RTFStyleElt)) == (RTFStyleElt *) NULL)
+ sep = New (RTFStyleElt);
+ if (sep == NULL)
RTFPanic (info,"%s: cannot allocate style element", fn);
sep->rtfSEClass = info->rtfClass;
sep->rtfSEMajor = info->rtfMajor;
sep->rtfSEMinor = info->rtfMinor;
sep->rtfSEParam = info->rtfParam;
- if ((sep->rtfSEText = RTFStrSave (info->rtfTextBuf))
- == (char *) NULL)
+ sep->rtfSEText = RTFStrSave (info->rtfTextBuf);
+ if (sep->rtfSEText == NULL)
RTFPanic (info,"%s: cannot allocate style element text", fn);
- if (sepLast == (RTFStyleElt *) NULL)
+ if (sepLast == NULL)
sp->rtfSSEList = sep; /* first element */
else /* add to end */
sepLast->rtfNextSE = sep;
- sep->rtfNextSE = (RTFStyleElt *) NULL;
+ sep->rtfNextSE = NULL;
sepLast = sep;
}
else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
if (info->rtfMajor == ';')
{
/* put back for "for" loop */
- (void) RTFUngetToken (info);
+ RTFUngetToken (info);
break;
}
*bp++ = info->rtfMajor;
- (void) RTFGetToken (info);
+ RTFGetToken (info);
}
*bp = '\0';
- if ((sp->rtfSName = RTFStrSave (buf)) == (char *) NULL)
+ sp->rtfSName = RTFStrSave (buf);
+ if (sp->rtfSName == NULL)
RTFPanic (info, "%s: cannot allocate style name", fn);
}
else /* unrecognized */
fn, info->rtfTextBuf);
}
}
- (void) RTFGetToken (info);
+ RTFGetToken (info);
if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
RTFPanic (info, "%s: missing \"}\"", fn);
*
* Some German RTF writers use "Standard" instead of "Normal".
*/
- if (sp->rtfSName == (char *) NULL)
+ if (sp->rtfSName == NULL)
RTFPanic (info,"%s: missing style name", fn);
if (sp->rtfSNum < 0)
{
if (num == -1)
return (info->styleList);
- for (s = info->styleList; s != (RTFStyle *) NULL; s = s->rtfNextStyle)
+ for (s = info->styleList; s != NULL; s = s->rtfNextStyle)
{
if (s->rtfSNum == num)
break;
if (num == -1)
return (info->fontList);
- for (f = info->fontList; f != (RTFFont *) NULL; f = f->rtfNextFont)
+ for (f = info->fontList; f != NULL; f = f->rtfNextFont)
{
if (f->rtfFNum == num)
break;
if (num == -1)
return (info->colorList);
- for (c = info->colorList; c != (RTFColor *) NULL; c = c->rtfNextColor)
+ for (c = info->colorList; c != NULL; c = c->rtfNextColor)
{
if (c->rtfCNum == num)
break;
TRACE("\n");
- if (n == -1 || (s = RTFGetStyle (info, n)) == (RTFStyle *) NULL)
+ if (n == -1)
+ return;
+ s = RTFGetStyle (info, n);
+ if (s == NULL)
return;
if (s->rtfExpanding != 0)
RTFPanic (info,"Style expansion loop, style %d", n);
* isn't used because it would add the param value to the end
* of the token text, which already has it in.
*/
- for (se = s->rtfSSEList; se != (RTFStyleElt *) NULL; se = se->rtfNextSE)
+ for (se = s->rtfSSEList; se != NULL; se = se->rtfNextSE)
{
info->rtfClass = se->rtfSEClass;
info->rtfMajor = se->rtfSEMajor;
info->rtfMinor = se->rtfSEMinor;
info->rtfParam = se->rtfSEParam;
- (void) strcpy (info->rtfTextBuf, se->rtfSEText);
- info->rtfTextLen = strlen (info->rtfTextBuf);
+ lstrcpyA (info->rtfTextBuf, se->rtfSEText);
+ info->rtfTextLen = lstrlenA (info->rtfTextBuf);
RTFRouteToken (info);
}
s->rtfExpanding = 0; /* done - clear expansion flag */
{ rtfSpecialChar, rtfNoWidthNonJoiner, "zwnj", 0 },
/* is this valid? */
{ rtfSpecialChar, rtfCurHeadPict, "chpict", 0 },
+ { rtfSpecialChar, rtfUnicode, "u", 0 },
/*
* Character formatting attributes
{ rtfCharAttr, rtfLanguage, "lang", 0 },
/* this has disappeared from spec 1.2 */
{ rtfCharAttr, rtfGray, "gray", 0 },
+ { rtfCharAttr, rtfUnicodeLength, "uc", 0 },
/*
* Paragraph formatting attributes
{ rtfDocAttr, rtfRTLDoc, "rtldoc", 0 },
{ rtfDocAttr, rtfLTRDoc, "ltrdoc", 0 },
+
+ { rtfDocAttr, rtfAnsiCodePage, "ansicpg", 0 },
+ { rtfDocAttr, rtfUTF8RTF, "urtf", 0 },
/*
* Style attributes
{ rtfDestination, rtfStyleSheet, "stylesheet", 0 },
{ rtfDestination, rtfKeyCode, "keycode", 0 },
{ rtfDestination, rtfRevisionTbl, "revtbl", 0 },
+ { rtfDestination, rtfGenerator, "generator", 0 },
{ rtfDestination, rtfInfo, "info", 0 },
{ rtfDestination, rtfITitle, "title", 0 },
{ rtfDestination, rtfISubject, "subject", 0 },
{ 0, -1, (char *) NULL, 0 }
};
+#define RTF_KEY_COUNT (sizeof(rtfKey) / sizeof(RTFKey))
+
+typedef struct tagRTFHashTableEntry {
+ int count;
+ RTFKey **value;
+} RTFHashTableEntry;
+
+static RTFHashTableEntry rtfHashTable[RTF_KEY_COUNT * 2];
/*
if (inited == 0)
{
- for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
+ memset(rtfHashTable, 0, RTF_KEY_COUNT * 2 * sizeof(*rtfHashTable));
+ for (rp = rtfKey; rp->rtfKStr != NULL; rp++) {
+ int index;
+
rp->rtfKHash = Hash ((char*)rp->rtfKStr);
+ index = rp->rtfKHash % (RTF_KEY_COUNT * 2);
+ if (!rtfHashTable[index].count)
+ rtfHashTable[index].value = RTFAlloc(sizeof(RTFKey *));
+ else
+ rtfHashTable[index].value = RTFReAlloc(rtfHashTable[index].value, sizeof(RTFKey *) * (rtfHashTable[index].count + 1));
+ rtfHashTable[index].value[rtfHashTable[index].count++] = rp;
+ }
++inited;
}
}
{
RTFKey *rp;
int hash;
+ RTFHashTableEntry *entry;
+ int i;
TRACE("\n");
++s; /* skip over the leading \ character */
hash = Hash (s);
- for (rp = rtfKey; rp->rtfKStr != (char *) NULL; rp++)
+ entry = &rtfHashTable[hash % (RTF_KEY_COUNT * 2)];
+ for (i = 0; i < entry->count; i++)
{
+ rp = entry->value[i];
if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
{
info->rtfClass = rtfControl;
int val = 0;
while ((c = *s++) != '\0')
- val += (int) c;
+ val += c;
return (val);
}
-/* ---------------------------------------------------------------------- */
-
-/*
- * Memory allocation routines
- */
-
-
-/*
- * Return pointer to block of size bytes, or NULL if there's
- * not enough memory available.
- *
- * This is called through RTFAlloc(), a define which coerces the
- * argument to int. This avoids the persistent problem of allocation
- * failing under THINK C when a long is passed.
- */
-
-char *_RTFAlloc(int size)
-{
- return HeapAlloc(me_heap, 0, size);
-}
-
-
-/*
- * Saves a string on the heap and returns a pointer to it.
- */
-
-
-char *RTFStrSave(char *s)
-{
- char *p;
-
- if ((p = RTFAlloc ((int) (strlen (s) + 1))) == (char *) NULL)
- return ((char *) NULL);
- return (strcpy (p, s));
-}
-
-
-void RTFFree(char *p)
-{
- HeapFree(me_heap, 0, p);
-}
-
/* ---------------------------------------------------------------------- */
/* ---------------------------------------------------------------------- */
/*
- * RTFReadOutputMap() -- Read output translation map
- */
-
-/*
- * Read in an array describing the relation between the standard character set
- * and an RTF translator's corresponding output sequences. Each line consists
- * of a standard character name and the output sequence for that character.
- *
- * outMap is an array of strings into which the sequences should be placed.
- * It should be declared like this in the calling program:
- *
- * char *outMap[rtfSC_MaxChar];
- *
- * reinit should be non-zero if outMap should be initialized
- * zero otherwise.
+ * Print message.
*
- */
-
-int RTFReadOutputMap(RTF_Info *info, char *outMap[], int reinit)
-{
- unsigned int i;
- int stdCode;
-
- if (reinit)
- {
- for (i = 0; i < rtfSC_MaxChar; i++)
- {
- outMap[i] = (char *) NULL;
- }
- }
-
- for (i=0 ;i< sizeof(text_map)/sizeof(char*); i+=2)
- {
- const char *name = text_map[i];
- const char *seq = text_map[i+1];
- stdCode = RTFStdCharCode( info, name );
- outMap[stdCode] = (char*)seq;
- }
-
- return (1);
-}
-
-/* ---------------------------------------------------------------------- */
-
-/*
- * Open a library file.
- */
-
-
-void RTFSetOpenLibFileProc(RTF_Info *info, FILE *(*proc)())
-{
- info->libFileOpen = proc;
-}
-
-
-FILE *RTFOpenLibFile (RTF_Info *info, char *file, char *mode)
-{
- if (info->libFileOpen == NULL)
- return ((FILE *) NULL);
- return ((*info->libFileOpen) (file, mode));
-}
-
-
-/* ---------------------------------------------------------------------- */
-
-/*
- * Print message. Default is to send message to stderr
- * but this may be overridden with RTFSetMsgProc().
- *
- * Message should include linefeeds as necessary. If the default
- * function is overridden, the overriding function may want to
- * map linefeeds to another line ending character or sequence if
- * the host system doesn't use linefeeds.
+ * Message should include linefeeds as necessary.
*/
va_start (args,fmt);
vsprintf (buf, fmt, args);
va_end (args);
- (void) strcat (buf, "\n");
- if (info->prevChar != EOF && info->rtfTextBuf != (char *) NULL)
+ lstrcatA (buf, "\n");
+ if (info->prevChar != EOF && info->rtfTextBuf != NULL)
{
- sprintf (buf + strlen (buf),
+ sprintf (buf + lstrlenA (buf),
"Last token read was \"%s\" near line %ld, position %d.\n",
info->rtfTextBuf, info->rtfLineNum, info->rtfLinePos);
}
static void ControlClass (RTF_Info *info);
static void Destination (RTF_Info *info);
static void SpecialChar (RTF_Info *info);
-static void PutStdChar (RTF_Info *info, int stdCode);
-static void PutLitChar (RTF_Info *info, int c);
-static void PutLitStr (RTF_Info *info, char *s);
+static void RTFPutUnicodeChar (RTF_Info *info, int c);
/*
* Initialize the writer.
void
WriterInit (RTF_Info *info )
{
- RTFReadOutputMap (info, info->outMap,1);
}
return (1);
}
-
/*
- * Write out a character. rtfMajor contains the input character, rtfMinor
- * contains the corresponding standard character code.
- *
- * If the input character isn't in the charset map, try to print some
- * representation of it.
+ * Write out a character.
*/
static void
TextClass (RTF_Info *info)
{
- char buf[rtfBufSiz];
-
- TRACE("\n");
-
- if (info->rtfFormat == SF_TEXT)
- PutLitChar (info, info->rtfMajor);
- else if (info->rtfMinor != rtfSC_nothing)
- PutStdChar (info, info->rtfMinor);
- else
- {
- if (info->rtfMajor < 128) /* in ASCII range */
- sprintf (buf, "[[%c]]", info->rtfMajor);
- else
- sprintf (buf, "[[\\'%02x]]", info->rtfMajor);
- PutLitStr (info, buf);
- }
+ RTFPutCodePageChar(info, info->rtfMajor);
}
switch (info->rtfMajor)
{
+ case rtfCharAttr:
+ CharAttr(info);
+ break;
+ case rtfCharSet:
+ CharSet(info);
+ break;
case rtfDestination:
Destination (info);
break;
+ case rtfDocAttr:
+ DocAttr(info);
+ break;
case rtfSpecialChar:
- SpecialChar (info);
+ SpecialChar (info);
break;
}
}
+static void
+CharAttr(RTF_Info *info)
+{
+ RTFFont *font;
+
+ switch (info->rtfMinor)
+ {
+ case rtfFontNum:
+ font = RTFGetFont(info, info->rtfParam);
+ if (font)
+ {
+ if (info->ansiCodePage != CP_UTF8)
+ info->codePage = font->rtfFCodePage;
+ }
+ else
+ RTFMsg(info, "unknown font %d\n", info->rtfParam);
+ break;
+ case rtfUnicodeLength:
+ info->unicodeLength = info->rtfParam;
+ break;
+ }
+}
+
+
+static void
+CharSet(RTF_Info *info)
+{
+ switch (info->rtfMinor)
+ {
+ case rtfAnsiCharSet:
+ info->ansiCodePage = 1252; /* Latin-1 */
+ break;
+ case rtfMacCharSet:
+ info->ansiCodePage = 10000; /* MacRoman */
+ break;
+ case rtfPcCharSet:
+ info->ansiCodePage = 437;
+ break;
+ case rtfPcaCharSet:
+ info->ansiCodePage = 850;
+ break;
+ }
+}
+
/*
- * This function notices destinations that should be ignored
+ * This function notices destinations that aren't explicitly handled
* and skips to their ends. This keeps, for instance, picture
* data from being considered as plain text.
*/
Destination (RTF_Info *info)
{
TRACE("\n");
-
- switch (info->rtfMinor)
- {
- case rtfPict:
- case rtfFNContSep:
- case rtfFNContNotice:
- case rtfInfo:
- case rtfIndexRange:
- case rtfITitle:
- case rtfISubject:
- case rtfIAuthor:
- case rtfIOperator:
- case rtfIKeywords:
- case rtfIComment:
- case rtfIVersion:
- case rtfIDoccomm:
- RTFSkipGroup (info);
- break;
- }
+ if (!RTFGetDestinationCallback(info, info->rtfMinor))
+ RTFSkipGroup (info);
}
-/*
- * The reason these use the rtfSC_xxx thingies instead of just writing
- * out ' ', '-', '"', etc., is so that the mapping for these characters
- * can be controlled by the text-map file.
- */
+static void
+DocAttr(RTF_Info *info)
+{
+ switch (info->rtfMinor)
+ {
+ case rtfAnsiCodePage:
+ info->ansiCodePage = info->rtfParam;
+ break;
+ case rtfUTF8RTF:
+ info->ansiCodePage = CP_UTF8;
+ break;
+ }
+}
+
-void SpecialChar (RTF_Info *info)
+static void SpecialChar (RTF_Info *info)
{
TRACE("\n");
switch (info->rtfMinor)
{
+ case rtfOptDest:
+ /* the next token determines destination, if it's unknown, skip the group */
+ /* this way we filter out the garbage coming from unknown destinations */
+ RTFGetToken(info);
+ if (info->rtfClass != rtfDestination)
+ RTFSkipGroup(info);
+ else
+ RTFRouteToken(info); /* "\*" is ignored with known destinations */
+ break;
+ case rtfUnicode:
+ {
+ int i;
+
+ RTFPutUnicodeChar(info, info->rtfParam);
+
+ /* After \u we must skip number of character tokens set by \ucN */
+ for (i = 0; i < info->unicodeLength; i++)
+ {
+ RTFGetToken(info);
+ if (info->rtfClass != rtfText)
+ {
+ ERR("The token behind \\u is not text, but (%d,%d,%d)\n",
+ info->rtfClass, info->rtfMajor, info->rtfMinor);
+ RTFUngetToken(info);
+ break;
+ }
+ }
+ break;
+ }
case rtfPage:
case rtfSect:
case rtfRow:
case rtfLine:
case rtfPar:
- PutLitChar (info, '\n');
+ RTFPutUnicodeChar (info, '\n');
break;
case rtfCell:
- PutStdChar (info, rtfSC_space); /* make sure cells are separated */
+ RTFPutUnicodeChar (info, ' '); /* make sure cells are separated */
break;
case rtfNoBrkSpace:
- PutStdChar (info, rtfSC_nobrkspace);
+ RTFPutUnicodeChar (info, 0x00A0);
break;
case rtfTab:
- PutLitChar (info, '\t');
+ RTFPutUnicodeChar (info, '\t');
break;
case rtfNoBrkHyphen:
- PutStdChar (info, rtfSC_nobrkhyphen);
+ RTFPutUnicodeChar (info, 0x2011);
break;
case rtfBullet:
- PutStdChar (info, rtfSC_bullet);
+ RTFPutUnicodeChar (info, 0x2022);
break;
case rtfEmDash:
- PutStdChar (info, rtfSC_emdash);
+ RTFPutUnicodeChar (info, 0x2014);
break;
case rtfEnDash:
- PutStdChar (info, rtfSC_endash);
+ RTFPutUnicodeChar (info, 0x2013);
break;
case rtfLQuote:
- PutStdChar (info, rtfSC_quoteleft);
+ RTFPutUnicodeChar (info, 0x2018);
break;
case rtfRQuote:
- PutStdChar (info, rtfSC_quoteright);
+ RTFPutUnicodeChar (info, 0x2019);
break;
case rtfLDblQuote:
- PutStdChar (info, rtfSC_quotedblleft);
+ RTFPutUnicodeChar (info, 0x201C);
break;
case rtfRDblQuote:
- PutStdChar (info, rtfSC_quotedblright);
+ RTFPutUnicodeChar (info, 0x201D);
break;
}
}
-/*
- * Eventually this should keep track of the destination of the
- * current state and only write text when in the initial state.
- *
- * If the output sequence is unspecified in the output map, write
- * the character's standard name instead. This makes map deficiencies
- * obvious and provides incentive to fix it. :-)
- */
-
-void PutStdChar (RTF_Info *info, int stdCode)
+static void
+RTFFlushUnicodeOutputBuffer(RTF_Info *info)
{
+ if (info->dwOutputCount)
+ {
+ ME_InsertTextFromCursor(info->editor, 0, info->OutputBuffer,
+ info->dwOutputCount, info->style);
+ info->dwOutputCount = 0;
+ }
+}
- char *oStr = (char *) NULL;
- char buf[rtfBufSiz];
-
-/* if (stdCode == rtfSC_nothing)
- RTFPanic ("Unknown character code, logic error\n");
-*/
- TRACE("\n");
+static void
+RTFPutUnicodeString(RTF_Info *info, WCHAR *string, int length)
+{
+ if (info->dwCPOutputCount)
+ RTFFlushCPOutputBuffer(info);
+ while (length)
+ {
+ int fit = min(length, sizeof(info->OutputBuffer) / sizeof(WCHAR) - info->dwOutputCount);
- oStr = info->outMap[stdCode];
- if (oStr == (char *) NULL) /* no output sequence in map */
- {
- sprintf (buf, "[[%s]]", RTFStdCharName (info, stdCode));
- oStr = buf;
- }
- PutLitStr (info, oStr);
+ memmove(info->OutputBuffer + info->dwOutputCount, string, fit * sizeof(WCHAR));
+ if (fit == sizeof(info->OutputBuffer) / sizeof(WCHAR) - info->dwOutputCount)
+ RTFFlushUnicodeOutputBuffer(info);
+ else
+ info->dwOutputCount += fit;
+ length -= fit;
+ string += fit;
+ }
}
-void PutLitChar (RTF_Info *info, int c)
+static void
+RTFFlushCPOutputBuffer(RTF_Info *info)
{
- if( info->dwOutputCount >= ( sizeof info->OutputBuffer - 1 ) )
- RTFFlushOutputBuffer( info );
- info->OutputBuffer[info->dwOutputCount++] = c;
+ int bufferMax = info->dwCPOutputCount * 2 * sizeof(WCHAR);
+ WCHAR *buffer = (WCHAR *)RTFAlloc(bufferMax);
+ int length;
+
+ length = MultiByteToWideChar(info->codePage, 0, info->cpOutputBuffer,
+ info->dwCPOutputCount, buffer, bufferMax);
+ info->dwCPOutputCount = 0;
+
+ RTFPutUnicodeString(info, buffer, length);
+ RTFFree((char *)buffer);
}
-void RTFFlushOutputBuffer( RTF_Info *info )
+void
+RTFFlushOutputBuffer(RTF_Info *info)
{
- info->OutputBuffer[info->dwOutputCount] = 0;
- SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) info->OutputBuffer );
- info->dwOutputCount = 0;
+ if (info->dwCPOutputCount)
+ RTFFlushCPOutputBuffer(info);
+ RTFFlushUnicodeOutputBuffer(info);
}
-static void PutLitStr (RTF_Info *info, char *str )
+static void
+RTFPutUnicodeChar(RTF_Info *info, int c)
{
- int len = strlen( str );
+ if (info->dwCPOutputCount)
+ RTFFlushCPOutputBuffer(info);
+ if (info->dwOutputCount * sizeof(WCHAR) >= ( sizeof info->OutputBuffer - 1 ) )
+ RTFFlushUnicodeOutputBuffer( info );
+ info->OutputBuffer[info->dwOutputCount++] = c;
+}
- if( ( len + info->dwOutputCount + 1 ) > sizeof info->OutputBuffer )
- RTFFlushOutputBuffer( info );
- if( ( len + 1 ) >= sizeof info->OutputBuffer )
- {
- SendMessageA( info->hwndEdit, EM_REPLACESEL, FALSE, (LPARAM) str );
- return;
- }
- strcpy( &info->OutputBuffer[info->dwOutputCount], str );
- info->dwOutputCount += len;
+static void
+RTFPutCodePageChar(RTF_Info *info, int c)
+{
+ /* Use dynamic buffer here because it's the best way to handle
+ * MBCS codepages without having to worry about partial chars */
+ if (info->dwCPOutputCount >= info->dwMaxCPOutputCount)
+ {
+ info->dwMaxCPOutputCount *= 2;
+ info->cpOutputBuffer = RTFReAlloc(info->cpOutputBuffer, info->dwMaxCPOutputCount);
+ }
+ info->cpOutputBuffer[info->dwCPOutputCount++] = c;
}
*ppvObj = (LPVOID) This;
return S_OK;
}
+ FIXME("%p: unhandled interface %s\n", This, debugstr_guid(riid) );
return E_NOINTERFACE;
}
LPCSTR lpstrContainerApp, LPCSTR lpstrContainerObj)
{
IRichEditOleImpl *This = (IRichEditOleImpl *)me;
- FIXME("stub %p\n",This);
+ FIXME("stub %p %s %s\n",This, lpstrContainerApp, lpstrContainerObj);
return E_NOTIMPL;
}
#include "richedit.h"
-/* The following defines are automatically generated. Do not edit. */
-
-
-/* These must be sequential beginning from zero */
-
-#define rtfSC_nothing 0
-#define rtfSC_space 1
-#define rtfSC_exclam 2
-#define rtfSC_quotedbl 3
-#define rtfSC_numbersign 4
-#define rtfSC_dollar 5
-#define rtfSC_percent 6
-#define rtfSC_ampersand 7
-#define rtfSC_quoteright 8
-#define rtfSC_parenleft 9
-#define rtfSC_parenright 10
-#define rtfSC_asterisk 11
-#define rtfSC_plus 12
-#define rtfSC_comma 13
-#define rtfSC_hyphen 14
-#define rtfSC_period 15
-#define rtfSC_slash 16
-#define rtfSC_zero 17
-#define rtfSC_one 18
-#define rtfSC_two 19
-#define rtfSC_three 20
-#define rtfSC_four 21
-#define rtfSC_five 22
-#define rtfSC_six 23
-#define rtfSC_seven 24
-#define rtfSC_eight 25
-#define rtfSC_nine 26
-#define rtfSC_colon 27
-#define rtfSC_semicolon 28
-#define rtfSC_less 29
-#define rtfSC_equal 30
-#define rtfSC_greater 31
-#define rtfSC_question 32
-#define rtfSC_at 33
-#define rtfSC_A 34
-#define rtfSC_B 35
-#define rtfSC_C 36
-#define rtfSC_D 37
-#define rtfSC_E 38
-#define rtfSC_F 39
-#define rtfSC_G 40
-#define rtfSC_H 41
-#define rtfSC_I 42
-#define rtfSC_J 43
-#define rtfSC_K 44
-#define rtfSC_L 45
-#define rtfSC_M 46
-#define rtfSC_N 47
-#define rtfSC_O 48
-#define rtfSC_P 49
-#define rtfSC_Q 50
-#define rtfSC_R 51
-#define rtfSC_S 52
-#define rtfSC_T 53
-#define rtfSC_U 54
-#define rtfSC_V 55
-#define rtfSC_W 56
-#define rtfSC_X 57
-#define rtfSC_Y 58
-#define rtfSC_Z 59
-#define rtfSC_bracketleft 60
-#define rtfSC_backslash 61
-#define rtfSC_bracketright 62
-#define rtfSC_asciicircum 63
-#define rtfSC_underscore 64
-#define rtfSC_quoteleft 65
-#define rtfSC_a 66
-#define rtfSC_b 67
-#define rtfSC_c 68
-#define rtfSC_d 69
-#define rtfSC_e 70
-#define rtfSC_f 71
-#define rtfSC_g 72
-#define rtfSC_h 73
-#define rtfSC_i 74
-#define rtfSC_j 75
-#define rtfSC_k 76
-#define rtfSC_l 77
-#define rtfSC_m 78
-#define rtfSC_n 79
-#define rtfSC_o 80
-#define rtfSC_p 81
-#define rtfSC_q 82
-#define rtfSC_r 83
-#define rtfSC_s 84
-#define rtfSC_t 85
-#define rtfSC_u 86
-#define rtfSC_v 87
-#define rtfSC_w 88
-#define rtfSC_x 89
-#define rtfSC_y 90
-#define rtfSC_z 91
-#define rtfSC_braceleft 92
-#define rtfSC_bar 93
-#define rtfSC_braceright 94
-#define rtfSC_asciitilde 95
-#define rtfSC_exclamdown 96
-#define rtfSC_cent 97
-#define rtfSC_sterling 98
-#define rtfSC_fraction 99
-#define rtfSC_yen 100
-#define rtfSC_florin 101
-#define rtfSC_section 102
-#define rtfSC_currency 103
-#define rtfSC_quotedblleft 104
-#define rtfSC_guillemotleft 105
-#define rtfSC_guilsinglleft 106
-#define rtfSC_guilsinglright 107
-#define rtfSC_fi 108
-#define rtfSC_fl 109
-#define rtfSC_endash 110
-#define rtfSC_dagger 111
-#define rtfSC_daggerdbl 112
-#define rtfSC_periodcentered 113
-#define rtfSC_paragraph 114
-#define rtfSC_bullet 115
-#define rtfSC_quotesinglbase 116
-#define rtfSC_quotedblbase 117
-#define rtfSC_quotedblright 118
-#define rtfSC_guillemotright 119
-#define rtfSC_ellipsis 120
-#define rtfSC_perthousand 121
-#define rtfSC_questiondown 122
-#define rtfSC_grave 123
-#define rtfSC_acute 124
-#define rtfSC_circumflex 125
-#define rtfSC_tilde 126
-#define rtfSC_macron 127
-#define rtfSC_breve 128
-#define rtfSC_dotaccent 129
-#define rtfSC_dieresis 130
-#define rtfSC_ring 131
-#define rtfSC_cedilla 132
-#define rtfSC_hungarumlaut 133
-#define rtfSC_ogonek 134
-#define rtfSC_caron 135
-#define rtfSC_emdash 136
-#define rtfSC_AE 137
-#define rtfSC_ordfeminine 138
-#define rtfSC_Lslash 139
-#define rtfSC_Oslash 140
-#define rtfSC_OE 141
-#define rtfSC_ordmasculine 142
-#define rtfSC_ae 143
-#define rtfSC_dotlessi 144
-#define rtfSC_lslash 145
-#define rtfSC_oslash 146
-#define rtfSC_oe 147
-#define rtfSC_germandbls 148
-#define rtfSC_Aacute 149
-#define rtfSC_Acircumflex 150
-#define rtfSC_Adieresis 151
-#define rtfSC_Agrave 152
-#define rtfSC_Aring 153
-#define rtfSC_Atilde 154
-#define rtfSC_Ccedilla 155
-#define rtfSC_Eacute 156
-#define rtfSC_Ecircumflex 157
-#define rtfSC_Edieresis 158
-#define rtfSC_Egrave 159
-#define rtfSC_Eth 160
-#define rtfSC_Iacute 161
-#define rtfSC_Icircumflex 162
-#define rtfSC_Idieresis 163
-#define rtfSC_Igrave 164
-#define rtfSC_Ntilde 165
-#define rtfSC_Oacute 166
-#define rtfSC_Ocircumflex 167
-#define rtfSC_Odieresis 168
-#define rtfSC_Ograve 169
-#define rtfSC_Otilde 170
-#define rtfSC_Scaron 171
-#define rtfSC_Thorn 172
-#define rtfSC_Uacute 173
-#define rtfSC_Ucircumflex 174
-#define rtfSC_Udieresis 175
-#define rtfSC_Ugrave 176
-#define rtfSC_Yacute 177
-#define rtfSC_Ydieresis 178
-#define rtfSC_aacute 179
-#define rtfSC_acircumflex 180
-#define rtfSC_adieresis 181
-#define rtfSC_agrave 182
-#define rtfSC_aring 183
-#define rtfSC_atilde 184
-#define rtfSC_brokenbar 185
-#define rtfSC_ccedilla 186
-#define rtfSC_copyright 187
-#define rtfSC_degree 188
-#define rtfSC_divide 189
-#define rtfSC_eacute 190
-#define rtfSC_ecircumflex 191
-#define rtfSC_edieresis 192
-#define rtfSC_egrave 193
-#define rtfSC_eth 194
-#define rtfSC_iacute 195
-#define rtfSC_icircumflex 196
-#define rtfSC_idieresis 197
-#define rtfSC_igrave 198
-#define rtfSC_logicalnot 199
-#define rtfSC_minus 200
-#define rtfSC_multiply 201
-#define rtfSC_ntilde 202
-#define rtfSC_oacute 203
-#define rtfSC_ocircumflex 204
-#define rtfSC_odieresis 205
-#define rtfSC_ograve 206
-#define rtfSC_onehalf 207
-#define rtfSC_onequarter 208
-#define rtfSC_onesuperior 209
-#define rtfSC_otilde 210
-#define rtfSC_plusminus 211
-#define rtfSC_registered 212
-#define rtfSC_thorn 213
-#define rtfSC_threequarters 214
-#define rtfSC_threesuperior 215
-#define rtfSC_trademark 216
-#define rtfSC_twosuperior 217
-#define rtfSC_uacute 218
-#define rtfSC_ucircumflex 219
-#define rtfSC_udieresis 220
-#define rtfSC_ugrave 221
-#define rtfSC_yacute 222
-#define rtfSC_ydieresis 223
-#define rtfSC_Alpha 224
-#define rtfSC_Beta 225
-#define rtfSC_Chi 226
-#define rtfSC_Delta 227
-#define rtfSC_Epsilon 228
-#define rtfSC_Phi 229
-#define rtfSC_Gamma 230
-#define rtfSC_Eta 231
-#define rtfSC_Iota 232
-#define rtfSC_Kappa 233
-#define rtfSC_Lambda 234
-#define rtfSC_Mu 235
-#define rtfSC_Nu 236
-#define rtfSC_Omicron 237
-#define rtfSC_Pi 238
-#define rtfSC_Theta 239
-#define rtfSC_Rho 240
-#define rtfSC_Sigma 241
-#define rtfSC_Tau 242
-#define rtfSC_Upsilon 243
-#define rtfSC_varUpsilon 244
-#define rtfSC_Omega 245
-#define rtfSC_Xi 246
-#define rtfSC_Psi 247
-#define rtfSC_Zeta 248
-#define rtfSC_alpha 249
-#define rtfSC_beta 250
-#define rtfSC_chi 251
-#define rtfSC_delta 252
-#define rtfSC_epsilon 253
-#define rtfSC_phi 254
-#define rtfSC_varphi 255
-#define rtfSC_gamma 256
-#define rtfSC_eta 257
-#define rtfSC_iota 258
-#define rtfSC_kappa 259
-#define rtfSC_lambda 260
-#define rtfSC_mu 261
-#define rtfSC_nu 262
-#define rtfSC_omicron 263
-#define rtfSC_pi 264
-#define rtfSC_varpi 265
-#define rtfSC_theta 266
-#define rtfSC_vartheta 267
-#define rtfSC_rho 268
-#define rtfSC_sigma 269
-#define rtfSC_varsigma 270
-#define rtfSC_tau 271
-#define rtfSC_upsilon 272
-#define rtfSC_omega 273
-#define rtfSC_xi 274
-#define rtfSC_psi 275
-#define rtfSC_zeta 276
-#define rtfSC_nobrkspace 277
-#define rtfSC_nobrkhyphen 278
-#define rtfSC_lessequal 279
-#define rtfSC_greaterequal 280
-#define rtfSC_infinity 281
-#define rtfSC_integral 282
-#define rtfSC_notequal 283
-#define rtfSC_radical 284
-#define rtfSC_radicalex 285
-#define rtfSC_approxequal 286
-#define rtfSC_apple 287
-#define rtfSC_partialdiff 288
-#define rtfSC_opthyphen 289
-#define rtfSC_formula 290
-#define rtfSC_lozenge 291
-#define rtfSC_universal 292
-#define rtfSC_existential 293
-#define rtfSC_suchthat 294
-#define rtfSC_congruent 295
-#define rtfSC_therefore 296
-#define rtfSC_perpendicular 297
-#define rtfSC_minute 298
-#define rtfSC_club 299
-#define rtfSC_diamond 300
-#define rtfSC_heart 301
-#define rtfSC_spade 302
-#define rtfSC_arrowboth 303
-#define rtfSC_arrowleft 304
-#define rtfSC_arrowup 305
-#define rtfSC_arrowright 306
-#define rtfSC_arrowdown 307
-#define rtfSC_second 308
-#define rtfSC_proportional 309
-#define rtfSC_equivalence 310
-#define rtfSC_arrowvertex 311
-#define rtfSC_arrowhorizex 312
-#define rtfSC_carriagereturn 313
-#define rtfSC_aleph 314
-#define rtfSC_Ifraktur 315
-#define rtfSC_Rfraktur 316
-#define rtfSC_weierstrass 317
-#define rtfSC_circlemultiply 318
-#define rtfSC_circleplus 319
-#define rtfSC_emptyset 320
-#define rtfSC_intersection 321
-#define rtfSC_union 322
-#define rtfSC_propersuperset 323
-#define rtfSC_reflexsuperset 324
-#define rtfSC_notsubset 325
-#define rtfSC_propersubset 326
-#define rtfSC_reflexsubset 327
-#define rtfSC_element 328
-#define rtfSC_notelement 329
-#define rtfSC_angle 330
-#define rtfSC_gradient 331
-#define rtfSC_product 332
-#define rtfSC_logicaland 333
-#define rtfSC_logicalor 334
-#define rtfSC_arrowdblboth 335
-#define rtfSC_arrowdblleft 336
-#define rtfSC_arrowdblup 337
-#define rtfSC_arrowdblright 338
-#define rtfSC_arrowdbldown 339
-#define rtfSC_angleleft 340
-#define rtfSC_registersans 341
-#define rtfSC_copyrightsans 342
-#define rtfSC_trademarksans 343
-#define rtfSC_angleright 344
-#define rtfSC_mathplus 345
-#define rtfSC_mathminus 346
-#define rtfSC_mathasterisk 347
-#define rtfSC_mathnumbersign 348
-#define rtfSC_dotmath 349
-#define rtfSC_mathequal 350
-#define rtfSC_mathtilde 351
-
-#define rtfSC_MaxChar 352
/*
* rtf.h - RTF document processing stuff. Release 1.10.
*/
*/
-# ifdef THINK_C
-# define rtfNoParam (-32768) /* 16-bit max. neg. value */
-# endif
-# ifndef rtfNoParam
# define rtfNoParam (-1000000)
-# endif
-
# define rtfIndexRange 71
# define rtfTOC 72
# define rtfNeXTGraphic 73
-# define rtfMaxDestination 74 /* highest dest + 1 */
+# define rtfGenerator 74
+# define rtfMaxDestination 75 /* highest dest + 1 */
# define rtfFontFamily 4
# define rtfFFNil 0
# define rtfNoWidthNonJoiner 56 /* new in 1.10 */
# define rtfCurHeadPict 57 /* valid? */
/*# define rtfCurAnnot 58*/ /* apparently not used */
+# define rtfUnicode 58 /* no better category*/
# define rtfStyleAttr 7
# define rtfAdditive 0 /* new in 1.10 */
# define rtfAnnotProtected 75 /* new in 1.10 */
# define rtfRTLDoc 76 /* new in 1.10 */
# define rtfLTRDoc 77 /* new in 1.10 */
+# define rtfAnsiCodePage 78
+# define rtfUTF8RTF 79
# define rtfSectAttr 9
# define rtfSectDef 0
# define rtfCharCharSet 33 /* new in 1.10 */
# define rtfLanguage 34
# define rtfGray 35
+# define rtfUnicodeLength 36
# define rtfPictAttr 13
# define rtfMacQD 0
# define rtfLangTurkish 0x041f
# define rtfLangUrdu 0x0420
-/*
- * CharSet indices
- */
-
-# define rtfCSGeneral 0 /* general (default) charset */
-# define rtfCSSymbol 1 /* symbol charset */
-
-/*
- * Flags for auto-charset-processing. Both are on by default.
- */
-
-# define rtfReadCharSet 0x01 /* auto-read charset files */
-# define rtfSwitchCharSet 0x02 /* auto-switch charset maps */
-
/*
* Style types
*/
# define New(t) ((t *) RTFAlloc ((int) sizeof (t)))
-/* maximum number of character values representable in a byte */
-
-# define charSetSize 256
-
-/* charset stack size */
-
-# define maxCSStack 10
+/* Parser stack size */
+# define maxStack 32
struct _RTF_Info;
typedef struct _RTF_Info RTF_Info;
typedef void (*RTFFuncPtr) (RTF_Info *); /* generic function pointer */
+
+/* RTF parser stack element */
+struct tagRTFState {
+ CHARFORMAT2W fmt;
+ int codePage;
+ int unicodeLength;
+};
+typedef struct tagRTFState RTFState;
+
+
struct _RTF_Info {
/*
* Public variables (listed in rtf.h)
int prevChar;
int bumpLine;
+ /* Document-wide attributes */
RTFFont *fontList; /* these lists MUST be */
RTFColor *colorList; /* initialized to NULL */
RTFStyle *styleList;
+ int ansiCodePage; /* ANSI codepage used in conversion to Unicode */
+
+ /* Character attributes */
+ int unicodeLength; /* The length of ANSI representation of Unicode characters */
+ int codePage; /* Current codepage for text conversion */
char *inputName;
char *outputName;
- EDITSTREAM editstream;
- char InputBuffer[0x1000];
- DWORD dwInputSize;
- DWORD dwInputUsed;
+ ME_InStream *stream;
/* edit window to output to */
HWND hwndEdit;
-
- /*
- * These arrays are used to map RTF input character values onto the standard
- * character names represented by the values. Input character values are
- * used as indices into the arrays to produce standard character codes.
- */
-
-
- char *genCharSetFile ;
- int genCharCode[charSetSize]; /* general */
- int haveGenCharSet;
-
- char *symCharSetFile;
- int symCharCode[charSetSize]; /* symbol */
- int haveSymCharSet;
-
- int curCharSet;
- int *curCharCode;
-
- /*
- * By default, the reader is configured to handle charset mapping invisibly,
- * including reading the charset files and switching charset maps as necessary
- * for Symbol font.
- */
-
- int autoCharSetFlags;
-
- /*
- * Stack for keeping track of charset map on group begin/end. This is
- * necessary because group termination reverts the font to the previous
- * value, which may implicitly change it.
- */
-
- int csStack[maxCSStack];
- int csTop;
+
+ ME_TextEditor *editor;
+ ME_Style *style;
RTFFuncPtr ccb[rtfMaxClass]; /* class callbacks */
RTFFuncPtr panicProc;
- FILE *(*libFileOpen) ();
+ DWORD dwOutputCount;
+ WCHAR OutputBuffer[0x1000];
- char *outMap[rtfSC_MaxChar];
+ DWORD dwCPOutputCount;
+ DWORD dwMaxCPOutputCount;
+ char *cpOutputBuffer;
- DWORD dwOutputCount;
- char OutputBuffer[0x1000];
+ RTFState stack[maxStack];
+ int stackTop;
};
*/
void RTFInit (RTF_Info *);
+void RTFDestroy(RTF_Info *info);
void RTFSetInputName (RTF_Info *, char *);
char *RTFGetInputName (RTF_Info *);
void RTFSetOutputName (RTF_Info *, char *);
RTFFont *RTFGetFont (RTF_Info *, int);
RTFColor *RTFGetColor (RTF_Info *, int);
RTFStyle *RTFGetStyle (RTF_Info *, int);
-# define RTFAlloc(size) _RTFAlloc ((int) size)
-char *_RTFAlloc (int);
-char *RTFStrSave (char *);
-void RTFFree (char *);
int RTFCharToHex ( char);
int RTFHexToChar ( int );
void RTFSetMsgProc ( RTFFuncPtr );
void RTFMsg (RTF_Info *, const char *fmt, ...);
void RTFPanic (RTF_Info *, const char *fmt, ...);
-int RTFReadOutputMap ( RTF_Info *, char *[], int);
-int RTFReadCharSetMap ( RTF_Info *, int);
-void RTFSetCharSetMap ( RTF_Info *, char *, int);
-int RTFStdCharCode ( RTF_Info *, const char *);
-const char *RTFStdCharName ( RTF_Info *, int);
-int RTFMapChar ( RTF_Info *, int);
-int RTFGetCharSet( RTF_Info * );
-void RTFSetCharSet( RTF_Info *, int);
-
-void RTFSetOpenLibFileProc ( RTF_Info *, FILE *(*)());
-FILE *RTFOpenLibFile ( RTF_Info *, char *, char *);
-
void RTFFlushOutputBuffer( RTF_Info *info );
-void RTFSetEditStream(RTF_Info *, EDITSTREAM *es);
+void RTFSetEditStream(RTF_Info *info, ME_InStream *stream);
void WriterInit (RTF_Info *);
int BeginFile (RTF_Info *);
+int RTFCharSetToCodePage(RTF_Info *info, int charset);
+
#endif
int ME_CanJoinRuns(ME_Run *run1, ME_Run *run2)
{
- if ((run1->nFlags | run2->nFlags) & (MERF_ENDPARA | MERF_GRAPHICS))
+ if ((run1->nFlags | run2->nFlags) & MERF_NOJOIN)
return 0;
if (run1->style != run2->style)
return 0;
ofs = 0;
break;
case diRun:
- TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n",
- p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
+ TRACE("run, real ofs = %d (+ofsp = %d), counted = %d, len = %d, txt = \"%s\", flags=%08x, fx&mask = %08lx\n",
+ p->member.run.nCharOfs, p->member.run.nCharOfs+ofsp, ofsp+ofs,
p->member.run.strText->nLen, debugstr_w(p->member.run.strText->szData),
p->member.run.nFlags,
p->member.run.style->fmt.dwMask & p->member.run.style->fmt.dwEffects);
int ME_CharOfsFromRunOfs(ME_TextEditor *editor, ME_DisplayItem *pRun, int nOfs)
{
ME_DisplayItem *pPara;
-
+
assert(pRun->type == diRun);
assert(pRun->member.run.nCharOfs != -1);
pPara = ME_FindItemBack(pRun, diParagraph);
assert(pPara);
assert(pPara->type==diParagraph);
- return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs
+ return pPara->member.para.nCharOfs + pRun->member.run.nCharOfs
+ ME_VPosToPos(pRun->member.run.strText, nOfs);
}
{
ME_DisplayItem *pPara;
int nParaOfs;
-
+
pPara = editor->pBuffer->pFirst->member.para.next_para;
assert(pPara);
assert(ppRun);
{
nParaOfs = pPara->member.para.nCharOfs;
assert(nCharOfs >= nParaOfs);
-
+
if (nCharOfs < pPara->member.para.next_para->member.para.nCharOfs)
{
*ppRun = ME_FindItemFwd(pPara, diRun);
- assert(*ppRun);
+ assert(*ppRun);
while (!((*ppRun)->member.run.nFlags & MERF_ENDPARA))
{
ME_DisplayItem *pNext = ME_FindItemFwd(*ppRun, diRun);
assert(pNext);
assert(pNext->type == diRun);
if (nCharOfs < nParaOfs + pNext->member.run.nCharOfs) {
- *pOfs = ME_PosToVPos((*ppRun)->member.run.strText,
+ *pOfs = ME_PosToVPos((*ppRun)->member.run.strText,
nCharOfs - nParaOfs - (*ppRun)->member.run.nCharOfs);
return;
}
if (nCharOfs == nParaOfs + (*ppRun)->member.run.nCharOfs) {
*pOfs = 0;
return;
- }
+ }
}
pPara = pPara->member.para.next_para;
}
*ppRun = ME_FindItemBack(editor->pBuffer->pLast, diRun);
- *pOfs = 0;
+ *pOfs = 0;
assert((*ppRun)->member.run.nFlags & MERF_ENDPARA);
}
assert(p->member.run.nCharOfs != -1);
ME_GetParagraph(p)->member.para.nFlags |= MEPF_REWRAP;
+ if (editor->bCaretAtEnd && editor->pCursors[0].pRun == pNext)
+ editor->bCaretAtEnd = FALSE;
for (i=0; i<editor->nCursors; i++) {
if (editor->pCursors[i].pRun == pNext) {
editor->pCursors[i].pRun = p;
editor->pCursors[i].nOffset += ME_StrVLen(p->member.run.strText);
}
}
-
+
ME_AppendString(p->member.run.strText, pNext->member.run.strText);
ME_Remove(pNext);
ME_DestroyDisplayItem(pNext);
ME_TextEditor *editor = c->editor;
ME_DisplayItem *item2 = NULL;
ME_Run *run, *run2;
-
+ ME_Paragraph *para = &ME_GetParagraph(item)->member.para;
+
assert(item->member.run.nCharOfs != -1);
if(TRACE_ON(richedit))
{
run->pt.x, run->pt.y);
item2 = ME_SplitRunSimple(editor, item, nVChar);
-
+
run2 = &item2->member.run;
-
- ME_CalcRunExtent(c, run);
- ME_CalcRunExtent(c, run2);
-
+
+ ME_CalcRunExtent(c, para, run);
+ ME_CalcRunExtent(c, para, run2);
+
run2->pt.x = run->pt.x+run->nWidth;
run2->pt.y = run->pt.y;
-
+
if(TRACE_ON(richedit))
{
TRACE("Before check after split\n");
ME_CheckCharOffsets(editor);
TRACE("After check after split\n");
- TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n",
+ TRACE("After split: %s(%ld, %ld), %s(%ld, %ld)\n",
debugstr_w(run->strText->szData), run->pt.x, run->pt.y,
debugstr_w(run2->strText->szData), run2->pt.x, run2->pt.y);
}
return item2;
}
-
+
/* split a run starting from voffset */
ME_DisplayItem *ME_SplitRunSimple(ME_TextEditor *editor, ME_DisplayItem *item, int nVChar)
{
int i;
assert(nVChar > 0 && nVChar < ME_StrVLen(run->strText));
assert(item->type == diRun);
- assert(!(item->member.run.nFlags & MERF_GRAPHICS));
+ assert(!(item->member.run.nFlags & (MERF_GRAPHICS | MERF_TAB)));
assert(item->member.run.nCharOfs != -1);
- item2 = ME_MakeRun(run->style,
+ item2 = ME_MakeRun(run->style,
ME_VSplitString(run->strText, nVChar), run->nFlags&MERF_SPLITMASK);
-
+
item2->member.run.nCharOfs = item->member.run.nCharOfs+
ME_VPosToPos(item->member.run.strText, nVChar);
run2 = &item2->member.run;
ME_InsertBefore(item->next, item2);
-
+
ME_UpdateRunFlags(editor, run);
ME_UpdateRunFlags(editor, run2);
for (i=0; i<editor->nCursors; i++) {
- if (editor->pCursors[i].pRun == item &&
+ if (editor->pCursors[i].pRun == item &&
editor->pCursors[i].nOffset >= nVChar) {
assert(item2->type == diRun);
editor->pCursors[i].pRun = item2;
return item2;
}
-/* split the start and final whitespace into separate runs */
-/* returns the last run added */
-/*
-ME_DisplayItem *ME_SplitFurther(ME_TextEditor *editor, ME_DisplayItem *item)
-{
- int i, nVLen, nChanged;
- assert(item->type == diRun);
- assert(!(item->member.run.nFlags & MERF_GRAPHICS));
- return item;
-}
-*/
-
ME_DisplayItem *ME_MakeRun(ME_Style *s, ME_String *strData, int nFlags)
{
ME_DisplayItem *item = ME_MakeDI(diRun);
ME_Cursor tmp;
ME_DisplayItem *pDI;
ME_UndoItem *pUI;
-
+
assert(pItem->type == diRun || pItem->type == diUndoInsertRun);
-
+
pUI = ME_AddUndoItem(editor, diUndoDeleteRun, NULL);
- pUI->nStart = nCharOfs;
- pUI->nLen = pItem->member.run.strText->nLen;
+ if (pUI) {
+ pUI->nStart = nCharOfs;
+ pUI->nLen = pItem->member.run.strText->nLen;
+ }
ME_CursorFromCharOfs(editor, nCharOfs, &tmp);
if (tmp.nOffset) {
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
TRACE("Shift length:%d\n", pDI->member.run.strText->nLen);
ME_PropagateCharOffset(tmp.pRun, pDI->member.run.strText->nLen);
ME_GetParagraph(tmp.pRun)->member.para.nFlags |= MEPF_REWRAP;
-
- return pDI;
-}
-static inline int ME_IsWSpace(WCHAR ch)
-{
- return ch <= ' ';
+ return pDI;
}
void ME_UpdateRunFlags(ME_TextEditor *editor, ME_Run *run)
run->nFlags |= MERF_SPLITTABLE;
else
run->nFlags &= ~MERF_SPLITTABLE;
-
- if (!(run->nFlags & MERF_GRAPHICS)) {
+
+ if (!(run->nFlags & MERF_NOTEXT)) {
if (ME_IsWhitespaces(run->strText))
run->nFlags |= MERF_WHITESPACE | MERF_STARTWHITE | MERF_ENDWHITE;
else
{
run->nFlags &= ~MERF_WHITESPACE;
-
+
if (ME_IsWSpace(ME_GetCharFwd(run->strText,0)))
run->nFlags |= MERF_STARTWHITE;
else
run->nFlags &= ~MERF_STARTWHITE;
-
+
if (ME_IsWSpace(ME_GetCharBack(run->strText,0)))
run->nFlags |= MERF_ENDWHITE;
else
pSize->cy = 64;
}
-int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Run *run)
+int ME_CharFromPoint(ME_TextEditor *editor, int cx, ME_Paragraph *para, ME_Run *run)
{
int fit = 0;
HGDIOBJ hOldFont;
if (!run->strText->nLen)
return 0;
+ if (run->nFlags & MERF_TAB)
+ {
+ if (cx < run->nWidth/2)
+ return 0;
+ return 1;
+ }
if (run->nFlags & MERF_GRAPHICS)
{
SIZE sz;
}
hDC = GetDC(editor->hWnd);
hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
- GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
+ GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
cx, &fit, NULL, &sz);
- ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
+ ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
ReleaseDC(editor->hWnd, hDC);
return fit;
}
if (!run->strText->nLen)
return 0;
+ if (run->nFlags & MERF_TAB)
+ {
+ if (cx < run->nWidth/2)
+ return 0;
+ return 1;
+ }
if (run->nFlags & MERF_GRAPHICS)
{
SIZE sz;
hDC = GetDC(editor->hWnd);
hOldFont = ME_SelectStyleFont(editor, hDC, run->style);
- GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
+ GetTextExtentExPointW(hDC, run->strText->szData, run->strText->nLen,
cx, &fit, NULL, &sz);
if (fit != run->strText->nLen)
{
int chars = 1;
-
+
GetTextExtentPoint32W(hDC, run->strText->szData, fit, &sz2);
fit1 = ME_StrRelPos(run->strText, fit, &chars);
GetTextExtentPoint32W(hDC, run->strText->szData, fit1, &sz3);
if (cx >= (sz2.cx+sz3.cx)/2)
fit = fit1;
}
- ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
+ ME_UnselectStyleFont(editor, hDC, run->style, hOldFont);
ReleaseDC(editor->hWnd, hDC);
return fit;
}
SIZE size;
HDC hDC = GetDC(editor->hWnd);
HGDIOBJ hOldFont;
-
+
if (pRun->nFlags & MERF_GRAPHICS)
{
if (!nOffset) return 0;
}
hOldFont = ME_SelectStyleFont(editor, hDC, pRun->style);
GetTextExtentPoint32W(hDC, pRun->strText->szData, nOffset, &size);
- ME_UnselectStyleFont(editor, hDC, pRun->style, hOldFont);
+ ME_UnselectStyleFont(editor, hDC, pRun->style, hOldFont);
ReleaseDC(editor->hWnd, hDC);
return size.cx;
}
-void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s,
+void ME_GetTextExtent(ME_Context *c, LPCWSTR szText, int nChars, ME_Style *s,
SIZE *size)
{
HDC hDC = c->hDC;
HGDIOBJ hOldFont;
hOldFont = ME_SelectStyleFont(c->editor, hDC, s);
GetTextExtentPoint32W(hDC, szText, nChars, size);
- ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
+ ME_UnselectStyleFont(c->editor, hDC, s, hOldFont);
}
-SIZE ME_GetRunSize(ME_Context *c, ME_Run *run, int nLen)
+SIZE ME_GetRunSizeCommon(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLen, int *pAscent, int *pDescent)
{
SIZE size;
int nMaxLen = ME_StrVLen(run->strText);
if (nLen>nMaxLen)
nLen = nMaxLen;
-
+
+ /* FIXME the following call also ensures that TEXTMETRIC structure is filled
+ * this is wasteful for graphics and TAB runs, but that shouldn't matter
+ * in practice
+ */
+ ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
+ assert(run->style->tm.tmAscent>0);
+ assert(run->style->tm.tmDescent>0);
+ *pAscent = run->style->tm.tmAscent;
+ *pDescent = run->style->tm.tmDescent;
+ size.cy = *pAscent + *pDescent;
+
+ if (run->nFlags & MERF_TAB)
+ {
+ int pos = 0, i = 0, ppos;
+ int lpsx = GetDeviceCaps(c->hDC, LOGPIXELSX);
+ PARAFORMAT2 *pFmt = para->pFmt;
+ do {
+ if (i < pFmt->cTabCount)
+ {
+ pos = pFmt->rgxTabs[i]&0x00FFFFFF;
+ i++;
+ }
+ else
+ {
+ pos += 720-(pos%720);
+ }
+ ppos = pos*lpsx/1440;
+ if (ppos>run->pt.x) {
+ size.cx = ppos - run->pt.x;
+ break;
+ }
+ } while(1);
+ size.cy = *pAscent + *pDescent;
+ return size;
+ }
if (run->nFlags & MERF_GRAPHICS)
{
ME_GetGraphicsSize(c->editor, run, &size);
+ if (size.cy > *pAscent)
+ *pAscent = size.cy;
+ /* descent is unchanged */
return size;
}
- ME_GetTextExtent(c, run->strText->szData, nLen, run->style, &size);
-
return size;
}
-void ME_CalcRunExtent(ME_Context *c, ME_Run *run)
+SIZE ME_GetRunSize(ME_Context *c, ME_Paragraph *para, ME_Run *run, int nLen)
+{
+ int asc, desc;
+ return ME_GetRunSizeCommon(c, para, run, nLen, &asc, &desc);
+}
+
+void ME_CalcRunExtent(ME_Context *c, ME_Paragraph *para, ME_Run *run)
{
- SIZE size;
int nEnd = ME_StrVLen(run->strText);
-
- if (run->nFlags & MERF_GRAPHICS) {
- ME_GetGraphicsSize(c->editor, run, &size);
- run->nWidth = size.cx;
- run->nAscent = size.cy;
- run->nDescent = 0;
- return;
- }
- ME_GetTextExtent(c, run->strText->szData, nEnd, run->style, &size);
+ SIZE size = ME_GetRunSizeCommon(c, para, run, nEnd, &run->nAscent, &run->nDescent);
run->nWidth = size.cx;
- run->nAscent = run->style->tm.tmAscent;
- run->nDescent = run->style->tm.tmDescent;
+ assert(size.cx);
}
void ME_MustBeWrapped(ME_Context *c, ME_DisplayItem *para)
{
ME_Cursor tmp, tmp2;
ME_DisplayItem *para;
-
+
ME_CursorFromCharOfs(editor, nOfs, &tmp);
if (tmp.nOffset)
tmp.pRun = ME_SplitRunSimple(editor, tmp.pRun, tmp.nOffset);
para = ME_GetParagraph(tmp.pRun);
para->member.para.nFlags |= MEPF_REWRAP;
-
+
while(tmp.pRun != tmp2.pRun)
{
ME_UndoItem *undo = NULL;
void ME_SetDefaultCharFormat(ME_TextEditor *editor, CHARFORMAT2W *mod)
{
- ME_Style *style;
+ ME_Style *style;
ME_UndoItem *undo;
-
+
assert(mod->cbSize == sizeof(CHARFORMAT2W));
undo = ME_AddUndoItem(editor, diUndoSetDefaultCharFormat, NULL);
if (undo) {
ME_DisplayItem *run, *run_end;
int nOffset, nOffset2;
CHARFORMAT2W tmp;
-
+
if (nTo>nFrom) /* selection consists of chars from nFrom up to nTo-1 */
nTo--;
-
+
ME_RunOfsFromCharOfs(editor, nFrom, &run, &nOffset);
if (nFrom == nTo) /* special case - if selection is empty, take previous char's formatting */
{
return;
}
ME_RunOfsFromCharOfs(editor, nTo, &run_end, &nOffset2);
-
+
ME_GetRunCharFormat(editor, run, pFmt);
-
+
if (run == run_end) return;
-
+
do {
/* FIXME add more style feature comparisons */
int nAttribs = CFM_SIZE | CFM_FACE | CFM_COLOR;
int nEffects = CFM_BOLD | CFM_ITALIC | CFM_UNDERLINE;
run = ME_FindItemFwd(run, diRun);
-
+
ZeroMemory(&tmp, sizeof(tmp));
tmp.cbSize = sizeof(tmp);
ME_GetRunCharFormat(editor, run, &tmp);
-
+
assert((tmp.dwMask & nAttribs) == nAttribs);
assert((tmp.dwMask & nEffects) == nEffects);
/* reset flags that differ */
pFmt->dwMask &= ~CFM_COLOR;
}
}
-
+
pFmt->dwMask &= ~((pFmt->dwEffects ^ tmp.dwEffects) & nEffects);
-
+
} while(run != run_end);
}
{
/* FIXME multibyte */
WCHAR *pos = s->szData;
- while(*pos++ == ' ')
+ while(ME_IsWSpace(*pos++))
;
pos--;
if (*pos)
int ME_IsSplitable(ME_String *s)
{
- /* FIXME multibyte */
WCHAR *pos = s->szData;
WCHAR ch;
- while(*pos++ == L' ')
+ while(ME_IsWSpace(*pos++))
;
pos--;
while((ch = *pos++) != 0)
{
- if (ch == L' ')
+ if (ME_IsWSpace(ch))
return 1;
}
return 0;
int ME_FindNonWhitespaceV(ME_String *s, int nVChar) {
int i;
- for (i = nVChar; isspace(s->szData[i]) && i<s->nLen; i++)
+ for (i = nVChar; i<s->nLen && ME_IsWSpace(s->szData[i]); i++)
;
return i;
/* note: returns offset of the first trailing whitespace */
int ME_ReverseFindNonWhitespaceV(ME_String *s, int nVChar) {
int i;
- for (i = nVChar; i>0 && isspace(s->szData[i-1]); i--)
+ for (i = nVChar; i>0 && ME_IsWSpace(s->szData[i-1]); i--)
;
return i;
/* note: returns offset of the first trailing nonwhitespace */
int ME_ReverseFindWhitespaceV(ME_String *s, int nVChar) {
int i;
- for (i = nVChar; i>0 && !isspace(s->szData[i-1]); i--)
+ for (i = nVChar; i>0 && !ME_IsWSpace(s->szData[i-1]); i--)
;
return i;
if (f->dwMask & CFM_FACE)
MultiByteToWideChar(0, 0, f->szFaceName, -1, to->szFaceName, sizeof(to->szFaceName));
/* copy the rest of the 2A structure to 2W */
- CopyMemory(1+((CHARFORMATW *)from), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
+ CopyMemory(1+((CHARFORMATW *)to), f+1, sizeof(CHARFORMAT2A)-sizeof(CHARFORMATA));
to->cbSize = sizeof(CHARFORMAT2W);
return to;
}
s->nSequence = -2;
s->nRefs = 1;
s->hFont = NULL;
+ s->tm.tmAscent = -1;
all_refs++;
return s;
}
{
ME_DisplayItem *p, *pNext;
+ if (editor->nUndoMode == umIgnore)
+ return;
+
TRACE("Emptying undo stack\n");
p = editor->pUndoStack;
}
void ME_CommitUndo(ME_TextEditor *editor) {
+
+ if (editor->nUndoMode == umIgnore)
+ return;
+
assert(editor->nUndoMode == umAddToUndo);
/* no transactions, no need to commit */
{
ME_UndoItem *pUItem = (ME_UndoItem *)pItem;
+ if (editor->nUndoMode == umIgnore)
+ return;
TRACE("Playing undo/redo item, id=%s\n", ME_GetDITypeName(pItem->type));
switch(pItem->type)
ME_DisplayItem *p;
ME_UndoMode nMode = editor->nUndoMode;
+ if (editor->nUndoMode == umIgnore)
+ return;
assert(nMode == umAddToUndo || nMode == umIgnore);
/* no undo items ? */
assert(nMode == umAddToUndo || nMode == umIgnore);
+ if (editor->nUndoMode == umIgnore)
+ return;
/* no redo items ? */
if (!editor->pRedoStack)
return;
/*
* Unsolved problems:
- *
+ *
* - center and right align in WordPad omits all spaces at the start, we don't
* - objects/images are not handled yet
- * - no tabs
- */
-
+ * - no tabs
+ */
+
ME_DisplayItem *ME_MakeRow(int height, int baseline, int width)
{
ME_DisplayItem *item = ME_MakeDI(diStartRow);
-
+
item->member.row.nHeight = height;
item->member.row.nBaseline = baseline;
item->member.row.nWidth = width;
wc->pLastSplittableRun = NULL;
wc->nAvailWidth = wc->nTotalWidth - (wc->nRow ? wc->nLeftMargin : wc->nFirstMargin) - wc->nRightMargin;
wc->pt.x = 0;
-}
+}
void ME_InsertRowStart(ME_WrapContext *wc, ME_DisplayItem *pEnd)
{
{
if (wc->pRowStart)
ME_InsertRowStart(wc, p->next);
-
+
/*
p = p->member.para.prev_para->next;
while(p) {
p = p->next;
}
*/
-}
+}
void ME_WrapSizeRun(ME_WrapContext *wc, ME_DisplayItem *p)
{
/* FIXME compose style (out of character and paragraph styles) here */
-
+
ME_UpdateRunFlags(wc->context->editor, &p->member.run);
-
- ME_CalcRunExtent(wc->context, &p->member.run);
+
+ ME_CalcRunExtent(wc->context, &ME_GetParagraph(p)->member.para, &p->member.run);
}
ME_DisplayItem *ME_MaximizeSplit(ME_WrapContext *wc, ME_DisplayItem *p, int i)
while(piter != wc->pRowStart)
{
piter = ME_FindItemBack(piter, diRun);
- if (piter->member.run.nFlags & MERF_WHITESPACE)
+ if (piter->member.run.nFlags & MERF_WHITESPACE)
{
pp = piter;
continue;
ME_DisplayItem *piter = p, *pp;
int i, idesp, len;
ME_Run *run = &p->member.run;
-
- idesp = i = ME_CharFromPoint(wc->context->editor, loc, run);
+
+ idesp = i = ME_CharFromPoint(wc->context->editor, loc, &ME_GetParagraph(p)->member.para, run);
len = ME_StrVLen(run->strText);
assert(len>0);
assert(i<len);
TRACE("Must backtrack to split at: %s\n", debugstr_w(p->member.run.strText->szData));
if (wc->pLastSplittableRun)
{
- if (wc->pLastSplittableRun->member.run.nFlags & MERF_GRAPHICS)
+ if (wc->pLastSplittableRun->member.run.nFlags & (MERF_GRAPHICS|MERF_TAB))
{
wc->pt = wc->ptLastSplittableRun;
return wc->pLastSplittableRun;
they serve no other purpose */
ME_UpdateRunFlags(wc->context->editor, run);
assert((wc->pLastSplittableRun->member.run.nFlags & MERF_SPLITTABLE));
-
+
piter = wc->pLastSplittableRun;
run = &piter->member.run;
len = ME_StrVLen(run->strText);
i = ME_ReverseFindWhitespaceV(run->strText, len);
if (i == len)
i = ME_ReverseFindNonWhitespaceV(run->strText, len);
- if (i) {
+ if (i) {
ME_DisplayItem *piter2 = ME_SplitRun(wc->context, piter, i);
wc->pt = piter2->member.run.pt;
return piter2;
}
/* the run is one char, can't split it */
return piter;
- }
+ }
}
ME_DisplayItem *ME_WrapHandleRun(ME_WrapContext *wc, ME_DisplayItem *p)
ME_Run *run;
int len;
- assert(p->type == diRun);
+ assert(p->type == diRun);
if (!wc->pRowStart)
wc->pRowStart = p;
- ME_WrapSizeRun(wc, p);
run = &p->member.run;
run->pt.x = wc->pt.x;
run->pt.y = wc->pt.y;
- len = ME_StrVLen(run->strText);
-
+ ME_WrapSizeRun(wc, p);
+ len = ME_StrVLen(run->strText);
+
if (wc->bOverflown) /* just skipping final whitespaces */
- {
- if (run->nFlags & MERF_WHITESPACE) {
+ {
+ if (run->nFlags & (MERF_WHITESPACE|MERF_TAB)) {
p->member.run.nFlags |= MERF_SKIPPED;
/* wc->pt.x += run->nWidth; */
/* skip runs consisting of only whitespaces */
return p->next;
}
-
+
if (run->nFlags & MERF_STARTWHITE) {
/* try to split the run at the first non-white char */
int black;
wc->bOverflown = FALSE;
pp = ME_SplitRun(wc->context, p, black);
p->member.run.nFlags |= MERF_SKIPPED;
-/*
- run->pt = wc->pt;
- wc->pt.x += run->nWidth;
- */
ME_InsertRowStart(wc, pp);
return pp;
}
/* will current run fit? */
if (wc->pt.x + run->nWidth > wc->nAvailWidth)
{
- int loc = wc->nAvailWidth - wc->pt.x;
+ int loc = wc->nAvailWidth - wc->pt.x;
/* total white run ? */
if (run->nFlags & MERF_WHITESPACE) {
/* let the overflow logic handle it */
wc->bOverflown = TRUE;
return p;
}
- /* graphics - we can split before */
- if (run->nFlags & MERF_GRAPHICS) {
+ /* graphics or TAB - we can split before */
+ if (run->nFlags & (MERF_GRAPHICS|MERF_TAB)) {
wc->bOverflown = TRUE;
return p;
}
ERR("failure!\n");
/* not found anything - writing over margins is the only option left */
}
- if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
- || ((run->nFlags & MERF_GRAPHICS) && (p != wc->pRowStart)))
+ if ((run->nFlags & (MERF_SPLITTABLE | MERF_STARTWHITE))
+ || ((run->nFlags & (MERF_GRAPHICS|MERF_TAB)) && (p != wc->pRowStart)))
{
wc->pLastSplittableRun = p;
wc->ptLastSplittableRun = wc->pt;
wc->pt.x += run->nWidth;
return p->next;
}
-
+
void ME_WrapTextParagraph(ME_Context *c, ME_DisplayItem *tp) {
ME_DisplayItem *p;
ME_WrapContext wc;
+ int dpi = GetDeviceCaps(c->hDC, LOGPIXELSX);
- assert(tp->type == diParagraph);
+ assert(tp->type == diParagraph);
if (!(tp->member.para.nFlags & MEPF_REWRAP)) {
return;
}
ME_PrepareParagraphForWrapping(c, tp);
-
+
wc.context = c;
/* wc.para_style = tp->member.para.style; */
wc.style = NULL;
+ tp->member.para.nRightMargin = tp->member.para.pFmt->dxRightIndent*dpi/1440;
+ tp->member.para.nFirstMargin = tp->member.para.pFmt->dxStartIndent*dpi/1440;
+ tp->member.para.nLeftMargin = (tp->member.para.pFmt->dxStartIndent+tp->member.para.pFmt->dxOffset)*dpi/1440;
wc.nFirstMargin = tp->member.para.nFirstMargin;
wc.nLeftMargin = tp->member.para.nLeftMargin;
wc.nRightMargin = tp->member.para.nRightMargin;
wc.nTotalWidth = c->rcView.right - c->rcView.left;
wc.nAvailWidth = wc.nTotalWidth - wc.nFirstMargin - wc.nRightMargin;
wc.pRowStart = NULL;
-
+
ME_BeginRow(&wc);
for (p = tp->next; p!=tp->member.para.next_para; ) {
assert(p->type != diStartRow);
}
}
-void ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
+BOOL ME_WrapMarkedParagraphs(ME_TextEditor *editor) {
HWND hWnd = editor->hWnd;
HDC hDC = GetDC(hWnd);
ME_DisplayItem *item;
ME_Context c;
-
+ BOOL bModified = FALSE;
+
ME_InitContext(&c, editor, hDC);
c.pt.x = 0;
c.pt.y = 0;
item = editor->pBuffer->pFirst->next;
while(item != editor->pBuffer->pLast) {
BOOL bRedraw = FALSE;
-
+
assert(item->type == diParagraph);
if ((item->member.para.nFlags & MEPF_REWRAP)
|| (item->member.para.nYPos != c.pt.y))
bRedraw = TRUE;
item->member.para.nYPos = c.pt.y;
-
+
ME_WrapTextParagraph(&c, item);
if (bRedraw)
item->member.para.nFlags |= MEPF_REPAINT;
+ bModified = bModified | bRedraw;
+
c.pt.y += item->member.para.nHeight;
item = item->member.para.next_para;
}
editor->sizeWindow.cx = c.rcView.right-c.rcView.left;
editor->sizeWindow.cy = c.rcView.bottom-c.rcView.top;
editor->nTotalLength = c.pt.y;
-
+
ME_DestroyContext(&c);
ReleaseDC(hWnd, hDC);
+ return bModified;
}
--- /dev/null
+/*
+ * 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"
+#include "rtf.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(richedit);
+
+
+static BOOL
+ME_StreamOutRTFText(ME_TextEditor *editor, WCHAR *text, LONG nChars);
+
+
+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 = NULL;
+ 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";
+ /* TODO: If the original document contained an \ansicpg value, retain it.
+ * Otherwise, M$ richedit emits a codepage number determined from the
+ * charset of the default font here. Anyway, this value is not used by
+ * the reader... */
+ 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->nDefaultCodePage = 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, ME_DisplayItem *pLastRun)
+{
+ ME_DisplayItem *item = pFirstRun;
+ 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->dwMask & CFM_CHARSET) ? fmt->bCharSet : DEFAULT_CHARSET;
+
+ 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++) {
+ if (table[i].bCharSet != DEFAULT_CHARSET) {
+ if (!ME_StreamOutPrint(editor, "{\\f%u\\fcharset%u ", i, table[i].bCharSet))
+ return FALSE;
+ } else {
+ if (!ME_StreamOutPrint(editor, "{\\f%u ", i))
+ return FALSE;
+ }
+ if (!ME_StreamOutRTFText(editor, table[i].szFaceName, -1))
+ return FALSE;
+ if (!ME_StreamOutPrint(editor, ";}\r\n"))
+ 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)
+{
+ PARAFORMAT2 *fmt = para->member.para.pFmt;
+ char props[STREAMOUT_BUFFER_SIZE] = "";
+ int i;
+
+ /* TODO: Don't emit anything if the last PARAFORMAT2 is inherited */
+ if (!ME_StreamOutPrint(editor, "\\pard"))
+ return FALSE;
+
+ /* TODO: PFM_BORDER. M$ does not emit any keywords for these properties, and
+ * when streaming border keywords in, PFM_BORDER is set, but wBorder field is
+ * set very different from the documentation.
+ * (Tested with RichEdit 5.50.25.0601) */
+
+ if (fmt->dwMask & PFM_ALIGNMENT) {
+ switch (fmt->wAlignment) {
+ case PFA_LEFT:
+ /* Default alignment: not emitted */
+ break;
+ case PFA_RIGHT:
+ strcat(props, "\\qr");
+ break;
+ case PFA_CENTER:
+ strcat(props, "\\qc");
+ break;
+ case PFA_JUSTIFY:
+ strcat(props, "\\qj");
+ break;
+ }
+ }
+
+ if (fmt->dwMask & PFM_LINESPACING) {
+ /* FIXME: MSDN says that the bLineSpacingRule field is controlled by the
+ * PFM_SPACEAFTER flag. Is that true? I don't believe so. */
+ switch (fmt->bLineSpacingRule) {
+ case 0: /* Single spacing */
+ strcat(props, "\\sl-240\\slmult1");
+ break;
+ case 1: /* 1.5 spacing */
+ strcat(props, "\\sl-360\\slmult1");
+ break;
+ case 2: /* Double spacing */
+ strcat(props, "\\sl-480\\slmult1");
+ break;
+ case 3:
+ sprintf(props + strlen(props), "\\sl%ld\\slmult0", fmt->dyLineSpacing);
+ break;
+ case 4:
+ sprintf(props + strlen(props), "\\sl-%ld\\slmult0", fmt->dyLineSpacing);
+ break;
+ case 5:
+ sprintf(props + strlen(props), "\\sl-%ld\\slmult1", fmt->dyLineSpacing * 240 / 20);
+ break;
+ }
+ }
+
+ if (fmt->dwMask & PFM_DONOTHYPHEN && fmt->wEffects & PFE_DONOTHYPHEN)
+ strcat(props, "\\hyph0");
+ if (fmt->dwMask & PFM_KEEP && fmt->wEffects & PFE_KEEP)
+ strcat(props, "\\keep");
+ if (fmt->dwMask & PFM_KEEPNEXT && fmt->wEffects & PFE_KEEPNEXT)
+ strcat(props, "\\keepn");
+ if (fmt->dwMask & PFM_NOLINENUMBER && fmt->wEffects & PFE_NOLINENUMBER)
+ strcat(props, "\\noline");
+ if (fmt->dwMask & PFM_NOWIDOWCONTROL && fmt->wEffects & PFE_NOWIDOWCONTROL)
+ strcat(props, "\\nowidctlpar");
+ if (fmt->dwMask & PFM_PAGEBREAKBEFORE && fmt->wEffects & PFE_PAGEBREAKBEFORE)
+ strcat(props, "\\pagebb");
+ if (fmt->dwMask & PFM_RTLPARA && fmt->wEffects & PFE_RTLPARA)
+ strcat(props, "\\rtlpar");
+ if (fmt->dwMask & PFM_SIDEBYSIDE && fmt->wEffects & PFE_SIDEBYSIDE)
+ strcat(props, "\\sbys");
+ if (fmt->dwMask & PFM_TABLE && fmt->dwMask & PFE_TABLE)
+ strcat(props, "\\intbl");
+
+ if (fmt->dwMask & PFM_OFFSET)
+ sprintf(props + strlen(props), "\\li%ld", fmt->dxOffset);
+ if (fmt->dwMask & PFM_OFFSETINDENT || fmt->dwMask & PFM_STARTINDENT)
+ sprintf(props + strlen(props), "\\fi%ld", fmt->dxStartIndent);
+ if (fmt->dwMask & PFM_RIGHTINDENT)
+ sprintf(props + strlen(props), "\\ri%ld", fmt->dxRightIndent);
+ if (fmt->dwMask & PFM_SPACEAFTER)
+ sprintf(props + strlen(props), "\\sa%ld", fmt->dySpaceAfter);
+ if (fmt->dwMask & PFM_SPACEBEFORE)
+ sprintf(props + strlen(props), "\\sb%ld", fmt->dySpaceBefore);
+ if (fmt->dwMask & PFM_STYLE)
+ sprintf(props + strlen(props), "\\s%d", fmt->sStyle);
+
+ if (fmt->dwMask & PFM_TABSTOPS) {
+ static const char *leader[6] = { "", "\\tldot", "\\tlhyph", "\\tlul", "\\tlth", "\\tleq" };
+
+ for (i = 0; i < fmt->cTabCount; i++) {
+ switch ((fmt->rgxTabs[i] >> 24) & 0xF) {
+ case 1:
+ strcat(props, "\\tqc");
+ break;
+ case 2:
+ strcat(props, "\\tqr");
+ break;
+ case 3:
+ strcat(props, "\\tqdec");
+ break;
+ case 4:
+ /* Word bar tab (vertical bar). Handled below */
+ break;
+ }
+ if (fmt->rgxTabs[i] >> 28 <= 5)
+ strcat(props, leader[fmt->rgxTabs[i] >> 28]);
+ }
+ }
+
+
+ if (fmt->dwMask & PFM_SHADING) {
+ static const char *style[16] = { "", "\\bgdkhoriz", "\\bgdkvert", "\\bgdkfdiag",
+ "\\bgdkbdiag", "\\bgdkcross", "\\bgdkdcross",
+ "\\bghoriz", "\\bgvert", "\\bgfdiag",
+ "\\bgbdiag", "\\bgcross", "\\bgdcross",
+ "", "", "" };
+ if (fmt->wShadingWeight)
+ sprintf(props + strlen(props), "\\shading%d", fmt->wShadingWeight);
+ if (fmt->wShadingStyle & 0xF)
+ strcat(props, style[fmt->wShadingStyle & 0xF]);
+ sprintf(props + strlen(props), "\\cfpat%d\\cbpat%d",
+ (fmt->wShadingStyle >> 4) & 0xF, (fmt->wShadingStyle >> 8) & 0xF);
+ }
+
+ if (*props && !ME_StreamOutPrint(editor, props))
+ return FALSE;
+
+ 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)
+ 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)
+ {
+ if (i != editor->pStream->nDefaultFont)
+ sprintf(props + strlen(props), "\\f%u", i);
+
+ /* In UTF-8 mode, charsets/codepages are not used */
+ if (editor->pStream->nDefaultCodePage != CP_UTF8)
+ {
+ if (editor->pStream->fonttbl[i].bCharSet == DEFAULT_CHARSET)
+ editor->pStream->nCodePage = editor->pStream->nDefaultCodePage;
+ else
+ editor->pStream->nCodePage = RTFCharSetToCodePage(NULL,
+ editor->pStream->fonttbl[i].bCharSet);
+ }
+ }
+ }
+ 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;
+
+ if (nChars == -1)
+ nChars = lstrlenW(text);
+
+ while (nChars) {
+ /* In UTF-8 mode, font charsets are not used. */
+ if (editor->pStream->nDefaultCodePage == 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 = FALSE;
+ BYTE letter[3];
+ int nBytes, i;
+
+ /* FIXME: In the MS docs for WideCharToMultiByte there is a big list of
+ * codepages including CP_SYMBOL for which the last parameter must be set
+ * to NULL for the function to succeed. But in Wine we need to care only
+ * about CP_SYMBOL */
+ nBytes = WideCharToMultiByte(editor->pStream->nCodePage, 0, text, 1,
+ letter, 3, NULL,
+ (editor->pStream->nCodePage == CP_SYMBOL) ? NULL : &unknown);
+ if (unknown)
+ pos += sprintf(buffer + pos, "\\u%d?", (short)*text);
+ else if (*letter < 128) {
+ if (*letter == '{' || *letter == '}' || *letter == '\\')
+ buffer[pos++] = '\\';
+ buffer[pos++] = *letter;
+ } else {
+ for (i = 0; i < nBytes; i++)
+ pos += sprintf(buffer + pos, "\\'%02x", letter[i]);
+ }
+ text++;
+ nChars--;
+ }
+ if (pos >= STREAMOUT_BUFFER_SIZE - 11) {
+ 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)
+{
+ ME_DisplayItem *p, *pEnd;
+ int nOffset, nEndLen;
+ ME_RunOfsFromCharOfs(editor, nStart, &p, &nOffset);
+ ME_RunOfsFromCharOfs(editor, nStart+nChars, &pEnd, &nEndLen);
+
+ if (!ME_StreamOutRTFHeader(editor, dwFormat))
+ return FALSE;
+
+ if (!ME_StreamOutRTFFontAndColorTbl(editor, p, pEnd))
+ 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 */
+
+ if (!ME_StreamOutRTFParaProps(editor, ME_GetParagraph(p)))
+ return FALSE;
+
+ while(1)
+ {
+ switch(p->type)
+ {
+ case diParagraph:
+ if (!ME_StreamOutRTFParaProps(editor, p))
+ return FALSE;
+ break;
+ case diRun:
+ if (p == pEnd && !nEndLen)
+ break;
+ TRACE("flags %xh\n", p->member.run.nFlags);
+ /* TODO: emit embedded objects */
+ if (p->member.run.nFlags & MERF_GRAPHICS)
+ FIXME("embedded objects are not handled\n");
+ if (p->member.run.nFlags & MERF_ENDPARA) {
+ if (!ME_StreamOutPrint(editor, "\r\n\\par"))
+ return FALSE;
+ nChars--;
+ } else {
+ int nEnd;
+
+ 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;
+
+ nEnd = (p == pEnd) ? nEndLen : ME_StrLen(p->member.run.strText);
+ if (!ME_StreamOutRTFText(editor, p->member.run.strText->szData + nOffset, nEnd - nOffset))
+ return FALSE;
+ nOffset = 0;
+ if (!ME_StreamOutPrint(editor, "}"))
+ return FALSE;
+ }
+ break;
+ default: /* we missed the last item */
+ assert(0);
+ }
+ if (p == pEnd)
+ break;
+ p = ME_FindItemFwd(p, diRunOrParagraphOrEnd);
+ }
+ if (!ME_StreamOutPrint(editor, "}"))
+ return FALSE;
+ return TRUE;
+}
+
+
+static BOOL
+ME_StreamOutText(ME_TextEditor *editor, int nStart, int nChars, DWORD dwFormat)
+{
+ /* FIXME: use ME_RunOfsFromCharOfs */
+ 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);
+}
#define CFE_AUTOBACKCOLOR 0x04000000
#define CFE_SUBSCRIPT 0x00010000
#define CFE_SUPERSCRIPT 0x00020000
+#define CFE_ALLCAPS CFM_ALLCAPS
+#define CFE_EMBOSS CFM_EMBOSS
+#define CFE_HIDDEN CFM_HIDDEN
+#define CFE_IMPRINT CFM_IMPRINT
+#define CFE_OUTLINE CFM_OUTLINE
+#define CFE_SHADOW CFM_SHADOW
+#define CFE_SMALLCAPS CFM_SMALLCAPS
+#define CFU_CF1UNDERLINE 0xff
+#define CFU_UNDERLINENONE 0
+#define CFU_UNDERLINE 1
+#define CFU_UNDERLINEWORD 2
+#define CFU_UNDERLINEDOUBLE 3
+#define CFU_UNDERLINEDOTTED 4
#define IMF_FORCENONE 1
#define IMF_FORCEENABLE 2
#define IMF_FORCEDISABLE 4
LONG dySpaceBefore;
LONG dySpaceAfter;
LONG dyLineSpacing;
- SHORT sStype;
+ SHORT sStyle;
BYTE bLineSpacingRule;
BYTE bOutlineLevel;
WORD wShadingWeight;