mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-27 11:53:32 +00:00
[RICHED20] Sync to Wine-10.0 Sync wine/rbtree.h to Wine-10.0 as changes in riched20 require this. Also added a define to allow directx code to compile. Sync riched20 to Wine-10.0 and its tests using the script. Signed-off-by: Francois Gouget <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 9f72c5601173728c52626342a1ec6c9a4c70670f by Francois Gouget <[email protected]> Update WINESYNC.txt Update precomp.h fix build [WINESYNC]: riched20 is now in sync with wine-staging wine-10.0 [WINESYNC] riched20: Only call ME_SendRequestResize when control is in-place active. Prevent GetClientRect from being called when not in-place active. wine commit id e61db557ab8473a490c84301dbb4b4665b6c9c9f by Charlotte Pabst <[email protected]> [WINESYNC] riched20: Exit from editor_ensure_visible when control is not in-place active. According to microsoft documentation, ScrollWindowEx, SetScrollPos and SetScrollRange may only be called while the control is in-place active. This fixes a segmentation fault in an application. wine commit id dafaadcb4addd9af334433666a4bc2e7aa6eb9ff by Charlotte Pabst <[email protected]> [WINESYNC] riched20/tests: Test that ScrollWindowEx and GetClientRect are only called when control is in-place active. wine commit id f1301cedbea51be107017bb8545956bcfd02b610 by Charlotte Pabst <[email protected]> Update editor.c [WINESYNC] riched20: Release IME input context when done using it. wine commit id 3f51fb96c162e8d3314627b3db25564fa7d9d419 by Zhiyi Zhang <[email protected]> [WINESYNC] riched20: In para_set_fmt protect against out of bound cTabStop values. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56730 wine commit id 7b2ff977739df25252d46552d2447af50c23040e by Fabian Maurer <[email protected]> Update editor.c [WINESYNC] riched20: Don't write past end of buffer. Signed-off-by: Eric Pouech <[email protected]> wine commit id 5cbe3a6cb186cffed20e3fea5f88abd0880d3914 by Eric Pouech <[email protected]> [WINESYNC] riched20: Don't crash when no OLE obj is present. This was generating a lot of crashes in riched20 tests. Signed-off-by: Eric Pouech <[email protected]> wine commit id 015aae30cf2217164f631d1c03abfc9ea63bb43d by Eric Pouech <[email protected]> Update editor.c [WINESYNC] riched20: Use CRT allocation functions. wine commit id 42579f1dcd2f14e765e2f53ceb5b46fff5623f10 by Alex Henrie <[email protected]> [WINESYNC] riched20/tests: Make the editor paste test failures more informative and unique. Knowing the content of the edit field is more useful than knowing the strcmp() result. The text field may contain carriage returns and linefeeds so use wine_dbgstr_a() so they are clearly visible in the failure message. Also prefix the ok messages with a unique string to indicate which test_WM_PASTE() test failed. wine commit id b8950d04e439f649f2167477a36ee453ad8b81ef by Francois Gouget <[email protected]> [WINESYNC] riched20: Don't call wrap_marked_paras_dc() before the editor is fully initialized. Today, CreateTextServices() sometimes triggers an assertion failure in select_style(). When ME_MakeEditor() calls ME_MakeFirstParagraph(), the editor (ME_TextEditor) is not in a fully initialized state. For example, the font cache (pFontCache) is not fully initialized, which sometimes makes select_style() believe that the cache slots are fully occupied. Fix this by delaying the call to wrap_marked_paras_dc() until the editor is fully initialized. Also, delay the call to ITextHost::TxReleaseDC() until after wrap_marked_paras_dc(), since we need the device context a bit longer. Fixes: b70eb32c5f3e506181d9e1e9fbef62e9baf3674d wine commit id 548d7179e3b2b9961321a4df50644c9b3874a295 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Factor out device context acquisition from ME_MakeFirstParagraph. This lets ME_MakeEditor() reuse the device context throughout the editor initialization process. wine commit id 03036f42e1afa9c79246dcee6b9fe876286b1aa4 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Don't call ME_UpdateRepaint inside set_selection(). This partially reverts c5daf6a30f21 (riched20: Call ME_UpdateRepaint instead of editor_ensure_visible in set_selection.) Fixes: c5daf6a30f21acd20263692b278acba49e014cc1 wine commit id ac5110131493b8b05795e8825285e41ca40ab6f1 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Add tests for change notifications caused by EM_SETSEL{,EX}. wine commit id c35926e49825f7a3d48ba826085fe1ae3d2a7e27 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Wrap marked paragraphs at Text Services initialization. Today, CreateTextServices() returns an Rich Edit object without row start and end marks, which are expected to exist by many Rich Edit operations as well as EM_* message handlers. This leads to a crash when certain messages (e.g., EM_SCROLLCARET) are sent to the Rich Edit object via ITextServices::TxSendMessage(), unless ME_WrapMarkedParagraphs() has been called beforehand. Fix this by calling wrap_marked_paras_dc() early in the initialization process. This is not a problem for windowed Rich Edit controls, which already calls ME_WrapMarkedParagraphs() before the user or application starts interacting with it. wine commit id b70eb32c5f3e506181d9e1e9fbef62e9baf3674d by Jinoh Kang <[email protected]> [WINESYNC] riched20: Use nameless unions/structs. wine commit id 26826d3af8b1969de389f3d6702d7c8b805126e1 by Alexandre Julliard <[email protected]> Update editor.h [WINESYNC] riched20: Remove DECLSPEC_HIDDEN usage. wine commit id c04ab166498b5841ecde620a9bd0b74969b01596 by Alistair Leslie-Hughes <[email protected]> [WINESYNC] riched20: Call ME_UpdateRepaint instead of editor_ensure_visible in set_selection. To prevent cases where ME_WrapMarkedParagraphs has not been called causing a NULL pointer when editor_ensure_visible tries to get the row from the cursor. wine commit id c5daf6a30f21acd20263692b278acba49e014cc1 by Santino Mazza <[email protected]> [WINESYNC] riched20: Update paragraph position in marked tree. nCharOfs is the key for paragraphs added to the marked tree If it is updated, re-add the entry to update its position wine commit id f1aa3f37ef326b7cbf8fc94e982c9f0b398181c4 by Daniel Lehman <[email protected]> Update editor.c [WINESYNC] riched20: Update the editor IME position on GCS_RESULTSTR. So that a GCS_RESULTSTR followed by GCS_COMPSTR, without interruping the composition, begins inserting the new composition text after the result instead of before it. wine commit id de45bc33a73eac5dbc3502b4a43cd48399507c73 by Rémi Bernon <[email protected]> [WINESYNC] riched20/tests: Fix a typo in a comment. wine commit id 145e3c758b9594596835e291b2fcb3c80e4c0479 by Francois Gouget <[email protected]> [WINESYNC] riched20/tests: Don't specify DT_WORDBREAK in _check_txgetnaturalsize(). Today, test_TxGetNaturalSize() creates a pop-up window with a fixed size (extent) of 100 x 100. The test function then proceeds to compute the natural size of rich edit control that fits the sample text "TestSomeText" and compare it to the RECT calculated by DrawText. Apparently, this test fails if the width of the sample text "TestSomeText" exceeds the width of the test window's client area. In this case, DrawText() with DT_WORDBREAK breaks the text into the two lines due to text wrapping; however, Rich Edit's ITextServices::TxGetNaturalSize implementation does not seem to perform text wrapping on overflow. This results in extent mismatch. (Note that the test may either succeed or fail depending on the current font used for DEFAULT_GUI_FONT, which defaults to Tahoma according to Microsoft's documentation. To reproduce this failure, it may be necessary to switch fonts or change the width of the pop-up window.) Fix this by omitting the DT_WORDBREAK flag for the DrawText() call in _check_txgetnaturalsize(). Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54637 wine commit id f54a1dac64c89aea3fe02ae8c0d6eef43a0aa117 by Jinoh Kang <[email protected]> Update editstr.h [WINESYNC] riched20: Implement ITextDocument::Freeze and ITextDocument::Unfreeze. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54617 wine commit id 3fe15349d39ccf31026a92eae30f83f65c91cb1f by Jinoh Kang <[email protected]> [WINESYNC] riched20: Don't assume that TxDraw preserves the device context's brush selection. Today, RichEditWndProc_common assumes that ITextServices::TxDraw preserves the brush selection of the given device context. However, this invariant may be broken by misbehaving embedded OLE objects in the text document. Fix this by not assuming that the return value of the second SelectObject() call equals the brush passed to the first SelectObject() call in RichEditWndProc_common's WM_PAINT / WM_PRINTCLIENT case. wine commit id 2027be7e0370c92595f126a0b0dd167a635d50a2 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Test for ITextDocument::Freeze and ITextDocument::Unfreeze. wine commit id 3105fad82b9bae390fcd710a2fc10a18988b4622 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Don't set para unless committing cursor move in ME_MoveCursorWords. Introduce a temporary variable, `other_para`, so that we don't end up returning incorrect para (with stale run) inside the output cursor. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54564 wine commit id 7e28fa5c558f1417f8f033cb843424778bbfb8b8 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Test for word movement across paragraph boundary. wine commit id 21d25b17c2cae8eb2d2376ce42b76e3f61eb0bba by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Fix the EM_GETSELTEXT tests in Hindi on Windows. On Windows EM_GETSELTEXT fails with an invalid parameter error in the Hindi locale but works in most other locales, including UTF-8 ones. So check for this issue and adjust the expected results accordingly. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54559 Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54565 wine commit id 37cd13abdf5d91c7b3f25b39fa14976bc3a3e3a3 by Francois Gouget <[email protected]> [WINESYNC] riched20/tests: EM_GETSELTEXT's wparam is not used and should be 0. wine commit id baa41970647a7e548701a72bea2e4aa2a61ca27e by Francois Gouget <[email protected]> [WINESYNC] riched20/tests: Add context to the EM_FORMATRANGE tests. wine commit id 020145248371bbfd2e473e79ba873e8313dde72d by Francois Gouget <[email protected]> [WINESYNC] riched20/tests: Check clipboard content after destroying the editor window. Destroying the editor window should render all the formats so they are still available after. wine commit id b5b5b0b365639b889a0240d4992e16174b428f75 by Francois Gouget <[email protected]> [WINESYNC] riched20/tests: Add retry loop around OpenClipboard() to avoid access denial. Reusing existing helper from user32 & comctl32. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47888 Signed-off-by: Eric Pouech <[email protected]> wine commit id 25743d46f4103c7345c955b87b4134a065fb5f1d by Eric Pouech <[email protected]> [WINESYNC] riched20/tests: Ensure clipboard has been pasted before testing the result. The patch does reduce error failures locally. For the record: - WM_PASTE in undo operations haven't been transformed (as it requires UNDO stack manipulation in a coherent way across platforms) Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=47888 Signed-off-by: Eric Pouech <[email protected]> wine commit id 4fd5252f635817b431673accaba718c7481f48f6 by Eric Pouech <[email protected]> [WINESYNC] riched20: Support SFF_SELECTION when streaming in plain text. Signed-off-by: Eric Pouech <[email protected]> wine commit id 1b7a918bd7ec8bc2c51bcb76525b56b17c5ff0c0 by Eric Pouech <[email protected]> [WINESYNC] riched20/tests: Add tests when pasting in plain text mode. Signed-off-by: Eric Pouech <[email protected]> wine commit id 82fa1048b5cc7d9c6a2a289403f25adbdd8158c7 by Eric Pouech <[email protected]> [WINESYNC] riched20: Obtain the composition start index after deleting selection. Today, the Rich Edit control handles the WM_IME_STARTCOMPOSITION message by the computing the composition start position (imeStartIndex) _before_ calling ME_DeleteSelection(), which shifts the character positions after the range of deletion. If the selection were not empty, imeStartIndex immediately becomes stale, since it does not take into account the number of deleted characters before it. Fix this by computing imeStartIndex after the ME_DeleteSelection() call. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=54318 wine commit id 8c43577f0f9b4422d3641457c595c3998bd54a18 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Call GlobalReAlloc with GMEM_MOVEABLE flag. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53474 wine commit id dfd44bb979ee2052eceff094083147e9879fa807 by Rémi Bernon <[email protected]> [WINESYNC] riched20: Fix failing test in UTF8 locales. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52875 Signed-off-by: Eric Pouech <[email protected]> wine commit id 5902cfd2990261189ce9af27981a95ac12fa2a5d by Eric Pouech <[email protected]> [WINESYNC] riched20: Don't emit EN_CHANGE events during window creation. wine commit id 5c997de8dc1e4885266fa884a2fb74f840e3bedc by Evan Tang <[email protected]> [WINESYNC] riched20: Add test for EN_CHANGE events during window creation. GenoPro isn't very happy if it receives one wine commit id 0b069106b0e0f141d6925d3e10166be89fc59145 by Evan Tang <[email protected]> [WINESYNC] riched20: Move test class creation to a helper function. wine commit id f9083961ed187777d660796f7136addc04fff8b0 by Evan Tang <[email protected]> [WINESYNC] riched20/tests: Use ARRAY_SIZE instead of open coding it. wine commit id 90b6288f54afdc4430dac146779a92b89f4d1219 by Michael Stefaniuc <[email protected]> [WINESYNC] riched20/test: Add scrolling test for richole. Signed-off-by: Santino Mazza <[email protected]> wine commit id 059c70d736baa164a1b246e949acd006cf83ab5a by Santino Mazza <[email protected]> [WINESYNC] riched20: Ensure that cursor is visible when selecting text. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=40444 Signed-off-by: Santino Mazza <[email protected]> wine commit id 25209c162fc209124b369abbad55d915d31909b2 by Santino Mazza <[email protected]> [WINESYNC] riched20/tests: When selection is out of view, the scrollbar should move. Signed-off-by: Santino Mazza <[email protected]> wine commit id ee131e9eba7dad090f0baf4a73fab2d62dc73b52 by Santino Mazza <[email protected]> [WINESYNC] riched20: Remove duplicate code. Signed-off-by: David Kahurani <[email protected]> wine commit id 3588479e1a674e5f43f1acb9e7c0aa8cecf98f25 by David Kahurani <[email protected]> [WINESYNC] riched20: Implement ITextDocument::Undo and ITextDocument::Redo. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53289 Signed-off-by: Jinoh Kang <[email protected]> wine commit id ded8227774570a1ea320dc34f2de0033f85b3ce7 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Add undo control state variable to ME_TextEditor. Signed-off-by: Jinoh Kang <[email protected]> wine commit id 40c54854e61d8adfca21e2fa9260712e4f5453e9 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Factor out undo-ignored status check. Signed-off-by: Jinoh Kang <[email protected]> wine commit id 0cd86e010e41eebdc1a4c142f9cc6a4948554d0e by Jinoh Kang <[email protected]> [WINESYNC] riched20: Ensure MEPF_COMPLEX is unset when in password input mode. Otherwise, Wine may crash while attempting to fetch the script shaping information, which does not exist since shape_para() was not called. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53335 Signed-off-by: Jinoh Kang <[email protected]> wine commit id f77f12ba3b1dad9ebdcb116c66b137e9103a0d1b by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Test for ITextDocument::Undo and ITextDocument::Redo. Signed-off-by: Jinoh Kang <[email protected]> wine commit id 73284ea18891a152dce989ffff59e2c527f94c73 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Commit and wrap paragraphs after changing text properties. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=53170 Signed-off-by: Jinoh Kang <[email protected]> wine commit id 0974f047b57357252ec94bea119e5291250a446c by Jinoh Kang <[email protected]> [WINESYNC] riched20: Draw OLE objects with IViewObject implementation. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52752 Signed-off-by: Jinoh Kang <[email protected]> wine commit id ecc607c6da96a5c2e8988f3021e98b442fe24c12 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Fetch extent from object if REOBJECT::sizel is unset. Signed-off-by: Jinoh Kang <[email protected]> wine commit id 164897e6c94d3eee4798f82a730472a5a847f9b2 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Add tests for drawing embedded OLE objects. Signed-off-by: Jinoh Kang <[email protected]> wine commit id a7d29d1625f7e02df4fbf59ef4e4a999d39edde0 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Fix the spelling of a comment. Signed-off-by: Francois Gouget <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id f9fa5f9e523b45766f715c6981c6a6ab5f183157 by Francois Gouget <[email protected]> [WINESYNC] riched20: Implement callback to IRichEditOleCallback_QueryInsertObject. Signed-off-by: Jinoh Kang <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 10667b39aad6710668f60c1ba8f10e50aeb87242 by Jinoh Kang <[email protected]> [WINESYNC] riched20/tests: Add tests for IRichEditOleCallback_QueryInsertObject. Signed-off-by: Jinoh Kang <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 95b4072cc774ba7a7eabb0d4c114b933b7d71105 by Jinoh Kang <[email protected]> Update richole.c [WINESYNC] riched20: Set REOBJECT's cp correctly in IRichEditOle::GetObject. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52705 Signed-off-by: Jinoh Kang <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 194ba06c403fd72fbfb95048385c9ca4838af169 by Jinoh Kang <[email protected]> [WINESYNC] riched20: Add back-reference to outer ME_Run from struct re_object. Signed-off-by: Jinoh Kang <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 05796c4e2a4acb91e6035c536b4d0e1f7e16896d by Jinoh Kang <[email protected]> fix [WINESYNC] riched20/tests: Enable compilation with long types. Signed-off-by: Eric Pouech <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 9e90cecdc603d79c6332a03884ed8d4851ef2e88 by Eric Pouech <[email protected]> [WINESYNC] riched20/tests: Use correct integral type. Signed-off-by: Eric Pouech <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 8b66db027d4112bfd4287b03d5c7b6f33994adc3 by Eric Pouech <[email protected]> fix build, sync rbtree.h [WINESYNC] riched20: Enable compilation with long types. Signed-off-by: Eric Pouech <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 42d0e56643740a0eac5913a9f176e789457cb475 by Eric Pouech <[email protected]> [WINESYNC] riched20/tests: Add a test to see what richedit class flavours should be available. Signed-off-by: Dmitry Timoshkov <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id d54a0e2bea4548bb5d9c0cca9f918d7c46591e23 by Dmitry Timoshkov <[email protected]> [WINESYNC] riched20: Use correct integral type. Signed-off-by: Eric Pouech <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 63a643f7f42ecd0c2625a5f5704b5b9647349e7b by Eric Pouech <[email protected]> [WINESYNC] riched20/tests: Avoid "misleading indentation" warnings. Signed-off-by: Fabian Maurer <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 2d6fad7bda2f2e21a871f405a6b0c3e70ebf6a6a by Fabian Maurer <[email protected]> [WINESYNC] include: Remove the wine_ prefix on rbtree functions. Add defines for frequently-used functions. For compatibility with vkd3d. Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 3527230c461b6baba9c29e69b575ada85bdbbc43 by Alexandre Julliard <[email protected]> [WINESYNC] riched20: Defer releasing the host if inside a notify callback. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=51521 Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 8138159f20d6453443861570689e0f14018051da by Huw Davies <[email protected]> [WINESYNC] riched20: Return the nearest char pos if the coords are outside the format rect. For the two tests that remain todo_wine, the results differ between riched20 and msftedit, with Wine's implementation matching msftedit. Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=52041 Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 363508454d5f8f1fc5916bda47e6429756b7dc8f by Huw Davies <[email protected]> [WINESYNC] riched20: Check font_cache in select_style() too. Signed-off-by: Serge Gautherie <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 177f808aa86e5442be1a0a92ddccc4904bf2e743 by Serge Gautherie <[email protected]> [WINESYNC] riched20: Use the standard va_list instead of __ms_va_list. Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 5b15bf33255bd373cbf26ec373d16a8234ec6b1c by Alexandre Julliard <[email protected]> [WINESYNC] riched20: Use regular allocation helpers for text services objects. Signed-off-by: Nikolay Sivov <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 2064c206c0b67edb8524f572166b974ba9cbd6fe by Nikolay Sivov <[email protected]> [WINESYNC] riched20: Remove New() allocation macro. Signed-off-by: Nikolay Sivov <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 1d5c9defb19fca6cfb1107384fc2325d391726d6 by Nikolay Sivov <[email protected]> [WINESYNC] riched20: Don't create custom heap. Signed-off-by: Dmitry Timoshkov <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 8bff437e9d5e047e689d8b32f62995785c0cd48e by Dmitry Timoshkov <[email protected]> [WINESYNC] riched20/tests: Don't cast NULL. Signed-off-by: Michael Stefaniuc <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id b701c3df99fbd6d4df7eaa11327bb11db95d8676 by Michael Stefaniuc <[email protected]> [WINESYNC] riched20/tests: Add more tests for testing EM_GETTEXTEX with OLE objects. Signed-off-by: Jactry Zeng <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 40988d6c3886d0f7a914c45ea68264bfc8540859 by Jactry Zeng <[email protected]> Update richole.c [WINESYNC] riched20: Allow to insert OLE object at specified offset. Signed-off-by: Jactry Zeng <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id 8c894382dde5b9da88b0e9f23a753d5e851a43d6 by Jactry Zeng <[email protected]> Update richole.c [WINESYNC] riched20/tests: Lock hGlobal before use. Signed-off-by: Jactry Zeng <[email protected]> Signed-off-by: Huw Davies <[email protected]> Signed-off-by: Alexandre Julliard <[email protected]> wine commit id f9c8c95856ded333be35ff41ab40a246bfe345b6 by Jactry Zeng <[email protected]> Co-authored-by: winesync <[email protected]>
2635 lines
77 KiB
C
2635 lines
77 KiB
C
/*
|
|
* WINE RTF file reader
|
|
*
|
|
* Portions Copyright 2004 Mike McCormack for CodeWeavers
|
|
* Portions Copyright 2006 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
/*
|
|
* Derived from RTF Tools by Paul DuBois ([email protected])
|
|
* Homepage: http://www.snake.net/software/RTF/
|
|
* Original license follows:
|
|
*/
|
|
|
|
/*
|
|
* reader.c - RTF file reader. Release 1.10.
|
|
*
|
|
* ....
|
|
*
|
|
* Author: Paul DuBois [email protected]
|
|
*
|
|
* This software may be redistributed without restriction and used for
|
|
* any purpose whatsoever.
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <stdarg.h>
|
|
#include <stdlib.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);
|
|
|
|
static int _RTFGetChar(RTF_Info *);
|
|
static void _RTFGetToken (RTF_Info *);
|
|
static void _RTFGetToken2 (RTF_Info *);
|
|
static int GetChar (RTF_Info *);
|
|
static void ReadFontTbl (RTF_Info *);
|
|
static void ReadColorTbl (RTF_Info *);
|
|
static void ReadStyleSheet (RTF_Info *);
|
|
static void ReadInfoGroup (RTF_Info *);
|
|
static void ReadPictGroup (RTF_Info *);
|
|
static void ReadObjGroup (RTF_Info *);
|
|
static void Lookup (RTF_Info *, char *);
|
|
static int Hash (const char *);
|
|
|
|
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);
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
int _RTFGetChar(RTF_Info *info)
|
|
{
|
|
int ch;
|
|
ME_InStream *stream = info->stream;
|
|
|
|
if (stream->dwSize <= stream->dwUsed)
|
|
{
|
|
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;
|
|
}
|
|
ch = (unsigned char)stream->buffer[stream->dwUsed++];
|
|
if (!ch)
|
|
return ' ';
|
|
return ch;
|
|
}
|
|
|
|
void RTFSetEditStream(RTF_Info *info, ME_InStream *stream)
|
|
{
|
|
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;
|
|
free (info->fontList->rtfFName);
|
|
free (info->fontList);
|
|
info->fontList = fp;
|
|
}
|
|
while (info->colorList)
|
|
{
|
|
cp = info->colorList->rtfNextColor;
|
|
free (info->colorList);
|
|
info->colorList = cp;
|
|
}
|
|
while (info->styleList)
|
|
{
|
|
sp = info->styleList->rtfNextStyle;
|
|
eltList = info->styleList->rtfSSEList;
|
|
while (eltList)
|
|
{
|
|
ep = eltList->rtfNextSE;
|
|
free (eltList->rtfSEText);
|
|
free (eltList);
|
|
eltList = ep;
|
|
}
|
|
free (info->styleList->rtfSName);
|
|
free (info->styleList);
|
|
info->styleList = sp;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
RTFDestroy(RTF_Info *info)
|
|
{
|
|
if (info->rtfTextBuf)
|
|
{
|
|
free(info->rtfTextBuf);
|
|
free(info->pushedTextBuf);
|
|
}
|
|
RTFDestroyAttrs(info);
|
|
free(info->cpOutputBuffer);
|
|
while (info->tableDef)
|
|
{
|
|
RTFTable *tableDef = info->tableDef;
|
|
info->tableDef = tableDef->parent;
|
|
free(tableDef);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Callback table manipulation routines
|
|
*/
|
|
|
|
|
|
/*
|
|
* Install or return a writer callback for a token class
|
|
*/
|
|
|
|
static void RTFSetClassCallback(RTF_Info *info, int class, RTFFuncPtr callback)
|
|
{
|
|
if (class >= 0 && class < rtfMaxClass)
|
|
info->ccb[class] = callback;
|
|
}
|
|
|
|
|
|
static RTFFuncPtr RTFGetClassCallback(const RTF_Info *info, int class)
|
|
{
|
|
if (class >= 0 && class < rtfMaxClass)
|
|
return info->ccb[class];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/*
|
|
* Initialize the reader. This may be called multiple times,
|
|
* to read multiple files. The only thing not reset is the input
|
|
* stream; that must be done with RTFSetStream().
|
|
*/
|
|
|
|
void RTFInit(RTF_Info *info)
|
|
{
|
|
int i;
|
|
|
|
if (info->rtfTextBuf == NULL) /* initialize the text buffers */
|
|
{
|
|
info->rtfTextBuf = malloc (rtfBufSiz);
|
|
info->pushedTextBuf = malloc (rtfBufSiz);
|
|
if (info->rtfTextBuf == NULL || info->pushedTextBuf == NULL) {
|
|
ERR ("Cannot allocate text buffers.\n");
|
|
return;
|
|
}
|
|
info->rtfTextBuf[0] = info->pushedTextBuf[0] = '\0';
|
|
}
|
|
|
|
for (i = 0; i < rtfMaxClass; i++)
|
|
RTFSetClassCallback (info, i, NULL);
|
|
for (i = 0; i < rtfMaxDestination; i++)
|
|
RTFSetDestinationCallback (info, i, NULL);
|
|
|
|
/* install built-in destination readers */
|
|
RTFSetDestinationCallback (info, rtfFontTbl, ReadFontTbl);
|
|
RTFSetDestinationCallback (info, rtfColorTbl, ReadColorTbl);
|
|
RTFSetDestinationCallback (info, rtfStyleSheet, ReadStyleSheet);
|
|
RTFSetDestinationCallback (info, rtfInfo, ReadInfoGroup);
|
|
RTFSetDestinationCallback (info, rtfPict, ReadPictGroup);
|
|
RTFSetDestinationCallback (info, rtfObject, ReadObjGroup);
|
|
|
|
|
|
RTFSetReadHook (info, NULL);
|
|
|
|
/* dump old lists if necessary */
|
|
|
|
RTFDestroyAttrs(info);
|
|
|
|
info->ansiCodePage = 1252; /* Latin-1; actually unused */
|
|
info->unicodeLength = 1; /* \uc1 is the default */
|
|
info->codePage = info->ansiCodePage;
|
|
info->defFont = 0;
|
|
|
|
info->rtfClass = -1;
|
|
info->pushedClass = -1;
|
|
info->pushedChar = EOF;
|
|
|
|
info->rtfLineNum = 0;
|
|
info->rtfLinePos = 0;
|
|
info->prevChar = EOF;
|
|
info->bumpLine = FALSE;
|
|
|
|
info->dwCPOutputCount = 0;
|
|
if (!info->cpOutputBuffer)
|
|
{
|
|
info->dwMaxCPOutputCount = 0x1000;
|
|
info->cpOutputBuffer = malloc (info->dwMaxCPOutputCount);
|
|
}
|
|
|
|
info->tableDef = NULL;
|
|
info->nestingLevel = 0;
|
|
info->canInheritInTbl = FALSE;
|
|
info->borderType = 0;
|
|
|
|
memset(&info->fmt, 0, sizeof(info->fmt));
|
|
info->fmt.cbSize = sizeof(info->fmt);
|
|
}
|
|
|
|
/*
|
|
* Install or return a writer callback for a destination type
|
|
*/
|
|
|
|
void RTFSetDestinationCallback(RTF_Info *info, int dest, RTFFuncPtr callback)
|
|
{
|
|
if (dest >= 0 && dest < rtfMaxDestination)
|
|
info->dcb[dest] = callback;
|
|
}
|
|
|
|
|
|
static RTFFuncPtr RTFGetDestinationCallback(const RTF_Info *info, int dest)
|
|
{
|
|
if (dest >= 0 && dest < rtfMaxDestination)
|
|
return info->dcb[dest];
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Token reading routines
|
|
*/
|
|
|
|
|
|
/*
|
|
* Read the input stream, invoking the writer's callbacks
|
|
* where appropriate.
|
|
*/
|
|
|
|
void RTFRead(RTF_Info *info)
|
|
{
|
|
while (RTFGetToken (info) != rtfEOF)
|
|
RTFRouteToken (info);
|
|
}
|
|
|
|
|
|
/*
|
|
* Route a token. If it's a destination for which a reader is
|
|
* installed, process the destination internally, otherwise
|
|
* pass the token to the writer's class callback.
|
|
*/
|
|
|
|
void RTFRouteToken(RTF_Info *info)
|
|
{
|
|
RTFFuncPtr p;
|
|
|
|
if (info->rtfClass < 0 || info->rtfClass >= rtfMaxClass) /* watchdog */
|
|
{
|
|
ERR( "Unknown class %d: %s (reader malfunction)\n",
|
|
info->rtfClass, info->rtfTextBuf);
|
|
}
|
|
if (RTFCheckCM (info, rtfControl, rtfDestination))
|
|
{
|
|
/* invoke destination-specific callback if there is one */
|
|
p = RTFGetDestinationCallback (info, info->rtfMinor);
|
|
if (p != NULL)
|
|
{
|
|
(*p) (info);
|
|
return;
|
|
}
|
|
}
|
|
/* invoke class callback if there is one */
|
|
p = RTFGetClassCallback (info, info->rtfClass);
|
|
if (p != NULL)
|
|
(*p) (info);
|
|
}
|
|
|
|
|
|
/*
|
|
* Skip to the end of the current group. When this returns,
|
|
* writers that maintain a state stack may want to call their
|
|
* state unstacker; global vars will still be set to the group's
|
|
* closing brace.
|
|
*/
|
|
|
|
void RTFSkipGroup(RTF_Info *info)
|
|
{
|
|
int level = 1;
|
|
|
|
while (RTFGetToken (info) != rtfEOF)
|
|
{
|
|
if (info->rtfClass == rtfGroup)
|
|
{
|
|
if (info->rtfMajor == rtfBeginGroup)
|
|
++level;
|
|
else if (info->rtfMajor == rtfEndGroup)
|
|
{
|
|
if (--level < 1)
|
|
break; /* end of initial group */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Do no special processing on the group.
|
|
*
|
|
* This acts as a placeholder for a callback in order to indicate that it
|
|
* shouldn't be ignored. Instead it will fallback on the loop in RTFRead.
|
|
*/
|
|
void RTFReadGroup (RTF_Info *info)
|
|
{
|
|
}
|
|
|
|
|
|
/*
|
|
* Install or return a token reader hook.
|
|
*/
|
|
|
|
void RTFSetReadHook(RTF_Info *info, RTFFuncPtr f)
|
|
{
|
|
info->readHook = f;
|
|
}
|
|
|
|
|
|
static RTFFuncPtr RTFGetReadHook(const RTF_Info *info)
|
|
{
|
|
return (info->readHook);
|
|
}
|
|
|
|
|
|
/*
|
|
* Read one token. Call the read hook if there is one. The
|
|
* token class is the return value. Returns rtfEOF when there
|
|
* are no more tokens.
|
|
*/
|
|
|
|
int RTFGetToken(RTF_Info *info)
|
|
{
|
|
RTFFuncPtr p;
|
|
|
|
/* don't try to return anything once EOF is reached */
|
|
if (info->rtfClass == rtfEOF) {
|
|
return rtfEOF;
|
|
}
|
|
|
|
for (;;)
|
|
{
|
|
_RTFGetToken (info);
|
|
p = RTFGetReadHook (info);
|
|
if (p != NULL)
|
|
(*p) (info); /* give read hook a look at token */
|
|
|
|
/* Silently discard newlines, carriage returns, nulls. */
|
|
if (!(info->rtfClass == rtfText && info->rtfFormat != SF_TEXT
|
|
&& (info->rtfMajor == '\r' || info->rtfMajor == '\n' || info->rtfMajor == '\0')))
|
|
break;
|
|
}
|
|
return (info->rtfClass);
|
|
}
|
|
|
|
|
|
static void RTFUngetToken(RTF_Info *info)
|
|
{
|
|
if (info->pushedClass >= 0) /* there's already an ungotten token */
|
|
ERR ("cannot unget two tokens\n");
|
|
if (info->rtfClass < 0)
|
|
ERR ("no token to unget\n");
|
|
info->pushedClass = info->rtfClass;
|
|
info->pushedMajor = info->rtfMajor;
|
|
info->pushedMinor = info->rtfMinor;
|
|
info->pushedParam = info->rtfParam;
|
|
lstrcpyA (info->pushedTextBuf, info->rtfTextBuf);
|
|
/* The read hook decrements stackTop on rtfEndGroup, so
|
|
* increment the value to compensate for it being decremented
|
|
* twice due to the RTFUngetToken. */
|
|
if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
{
|
|
info->stack[info->stackTop].style = info->style;
|
|
ME_AddRefStyle(info->style);
|
|
info->stackTop++;
|
|
}
|
|
}
|
|
|
|
|
|
static void _RTFGetToken(RTF_Info *info)
|
|
{
|
|
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() */
|
|
|
|
if (info->pushedClass >= 0)
|
|
{
|
|
info->rtfClass = info->pushedClass;
|
|
info->rtfMajor = info->pushedMajor;
|
|
info->rtfMinor = info->pushedMinor;
|
|
info->rtfParam = info->pushedParam;
|
|
lstrcpyA (info->rtfTextBuf, info->pushedTextBuf);
|
|
info->rtfTextLen = lstrlenA(info->rtfTextBuf);
|
|
info->pushedClass = -1;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Beyond this point, no token is ever seen twice, which is
|
|
* important, e.g., for making sure no "}" pops the font stack twice.
|
|
*/
|
|
|
|
_RTFGetToken2 (info);
|
|
}
|
|
|
|
|
|
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:
|
|
{
|
|
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))
|
|
ERR("unknown charset %d\n", charset);
|
|
else
|
|
return csi.ciACP;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/* this shouldn't be called anywhere but from _RTFGetToken() */
|
|
|
|
static void _RTFGetToken2(RTF_Info *info)
|
|
{
|
|
int sign;
|
|
int c;
|
|
|
|
/* initialize token vars */
|
|
|
|
info->rtfClass = rtfUnknown;
|
|
info->rtfParam = rtfNoParam;
|
|
info->rtfTextBuf[info->rtfTextLen = 0] = '\0';
|
|
|
|
/* get first character, which may be a pushback from previous token */
|
|
|
|
if (info->pushedChar != EOF)
|
|
{
|
|
c = info->pushedChar;
|
|
info->rtfTextBuf[info->rtfTextLen++] = c;
|
|
info->rtfTextBuf[info->rtfTextLen] = '\0';
|
|
info->pushedChar = EOF;
|
|
}
|
|
else if ((c = GetChar (info)) == EOF)
|
|
{
|
|
info->rtfClass = rtfEOF;
|
|
return;
|
|
}
|
|
|
|
if (c == '{')
|
|
{
|
|
info->rtfClass = rtfGroup;
|
|
info->rtfMajor = rtfBeginGroup;
|
|
return;
|
|
}
|
|
if (c == '}')
|
|
{
|
|
info->rtfClass = rtfGroup;
|
|
info->rtfMajor = rtfEndGroup;
|
|
return;
|
|
}
|
|
if (c != '\\')
|
|
{
|
|
/*
|
|
* Two possibilities here:
|
|
* 1) ASCII 9, effectively like \tab control symbol
|
|
* 2) literal text char
|
|
*/
|
|
if (c == '\t') /* ASCII 9 */
|
|
{
|
|
info->rtfClass = rtfControl;
|
|
info->rtfMajor = rtfSpecialChar;
|
|
info->rtfMinor = rtfTab;
|
|
}
|
|
else
|
|
{
|
|
info->rtfClass = rtfText;
|
|
info->rtfMajor = c;
|
|
}
|
|
return;
|
|
}
|
|
if ((c = GetChar (info)) == EOF)
|
|
{
|
|
/* early eof, whoops (class is rtfUnknown) */
|
|
return;
|
|
}
|
|
if (!isalpha (c))
|
|
{
|
|
/*
|
|
* Three possibilities here:
|
|
* 1) hex encoded text char, e.g., \'d5, \'d3
|
|
* 2) special escaped text char, e.g., \{, \}
|
|
* 3) control symbol, e.g., \_, \-, \|, \<10>
|
|
*/
|
|
if (c == '\'') /* hex char */
|
|
{
|
|
int c2;
|
|
|
|
if ((c = GetChar (info)) != EOF && (c2 = GetChar (info)) != EOF
|
|
&& isxdigit(c) && isxdigit(c2))
|
|
{
|
|
info->rtfClass = rtfText;
|
|
info->rtfMajor = RTFCharToHex (c) * 16 + RTFCharToHex (c2);
|
|
return;
|
|
}
|
|
/* early eof, whoops */
|
|
info->rtfClass = rtfEOF;
|
|
info->stream->editstream->dwError = -14;
|
|
return;
|
|
}
|
|
|
|
/* escaped char */
|
|
/*if (index (":{}\\", c) != NULL)*/ /* escaped char */
|
|
if (c == ':' || c == '{' || c == '}' || c == '\\')
|
|
{
|
|
info->rtfClass = rtfText;
|
|
info->rtfMajor = c;
|
|
return;
|
|
}
|
|
|
|
/* control symbol */
|
|
Lookup (info, info->rtfTextBuf); /* sets class, major, minor */
|
|
return;
|
|
}
|
|
/* control word */
|
|
while (isalpha (c))
|
|
{
|
|
if ((c = GetChar (info)) == EOF)
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* At this point, the control word is all collected, so the
|
|
* major/minor numbers are determined before the parameter
|
|
* (if any) is scanned. There will be one too many characters
|
|
* in the buffer, though, so fix up before and restore after
|
|
* looking up.
|
|
*/
|
|
|
|
if (c != EOF)
|
|
info->rtfTextBuf[info->rtfTextLen-1] = '\0';
|
|
Lookup (info, info->rtfTextBuf); /* sets class, major, minor */
|
|
if (c != EOF)
|
|
info->rtfTextBuf[info->rtfTextLen-1] = c;
|
|
|
|
/*
|
|
* Should be looking at first digit of parameter if there
|
|
* is one, unless it's negative. In that case, next char
|
|
* is '-', so need to gobble next char, and remember sign.
|
|
*/
|
|
|
|
sign = 1;
|
|
if (c == '-')
|
|
{
|
|
sign = -1;
|
|
c = GetChar (info);
|
|
}
|
|
if (c != EOF && isdigit (c))
|
|
{
|
|
info->rtfParam = 0;
|
|
while (isdigit (c)) /* gobble parameter */
|
|
{
|
|
info->rtfParam = info->rtfParam * 10 + c - '0';
|
|
if ((c = GetChar (info)) == EOF)
|
|
break;
|
|
}
|
|
info->rtfParam *= sign;
|
|
}
|
|
/*
|
|
* If control symbol delimiter was a blank, gobble it.
|
|
* Otherwise the character is first char of next token, so
|
|
* push it back for next call. In either case, delete the
|
|
* delimiter from the token buffer.
|
|
*/
|
|
if (c != EOF)
|
|
{
|
|
if (c != ' ')
|
|
info->pushedChar = c;
|
|
info->rtfTextBuf[--info->rtfTextLen] = '\0';
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Read the next character from the input. This handles setting the
|
|
* current line and position-within-line variables. Those variables are
|
|
* set correctly whether lines end with CR, LF, or CRLF (the last being
|
|
* the tricky case).
|
|
*
|
|
* bumpLine indicates whether the line number should be incremented on
|
|
* the *next* input character.
|
|
*/
|
|
|
|
|
|
static int GetChar(RTF_Info *info)
|
|
{
|
|
int c;
|
|
BOOL oldBumpLine;
|
|
|
|
if ((c = _RTFGetChar(info)) != EOF)
|
|
{
|
|
info->rtfTextBuf[info->rtfTextLen++] = c;
|
|
info->rtfTextBuf[info->rtfTextLen] = '\0';
|
|
}
|
|
if (info->prevChar == EOF)
|
|
info->bumpLine = TRUE;
|
|
oldBumpLine = info->bumpLine; /* TRUE if prev char was line ending */
|
|
info->bumpLine = FALSE;
|
|
if (c == '\r')
|
|
info->bumpLine = TRUE;
|
|
else if (c == '\n')
|
|
{
|
|
info->bumpLine = TRUE;
|
|
if (info->prevChar == '\r') /* oops, previous \r wasn't */
|
|
oldBumpLine = FALSE; /* really a line ending */
|
|
}
|
|
++info->rtfLinePos;
|
|
if (oldBumpLine) /* were we supposed to increment the */
|
|
{ /* line count on this char? */
|
|
++info->rtfLineNum;
|
|
info->rtfLinePos = 1;
|
|
}
|
|
info->prevChar = c;
|
|
return (c);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Special destination readers. They gobble the destination so the
|
|
* writer doesn't have to deal with them. That's wrong for any
|
|
* translator that wants to process any of these itself. In that
|
|
* case, these readers should be overridden by installing a different
|
|
* destination callback.
|
|
*
|
|
* NOTE: The last token read by each of these reader will be the
|
|
* destination's terminating '}', which will then be the current token.
|
|
* That '}' token is passed to RTFRouteToken() - the writer has already
|
|
* seen the '{' that began the destination group, and may have pushed a
|
|
* state; it also needs to know at the end of the group that a state
|
|
* should be popped.
|
|
*
|
|
* It's important that rtf.h and the control token lookup table list
|
|
* as many symbols as possible, because these destination readers
|
|
* unfortunately make strict assumptions about the input they expect,
|
|
* and a token of class rtfUnknown will throw them off easily.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Read { \fonttbl ... } destination. Old font tables don't have
|
|
* braces around each table entry; try to adjust for that.
|
|
*/
|
|
|
|
static void ReadFontTbl(RTF_Info *info)
|
|
{
|
|
RTFFont *fp = NULL;
|
|
char buf[rtfBufSiz], *bp;
|
|
int old = -1;
|
|
|
|
for (;;)
|
|
{
|
|
RTFGetToken (info);
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
break;
|
|
if (old < 0) /* first entry - determine tbl type */
|
|
{
|
|
if (RTFCheckCMM (info, rtfControl, rtfCharAttr, rtfFontNum))
|
|
old = 1; /* no brace */
|
|
else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
|
|
old = 0; /* brace */
|
|
else /* can't tell! */
|
|
ERR ("cannot determine format\n");
|
|
}
|
|
if (old == 0) /* need to find "{" here */
|
|
{
|
|
if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
|
|
ERR ("missing \"{\"\n");
|
|
RTFGetToken (info); /* yes, skip to next token */
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
}
|
|
fp = malloc (sizeof(*fp));
|
|
if (fp == NULL) {
|
|
ERR ("cannot allocate font entry\n");
|
|
break;
|
|
}
|
|
|
|
fp->rtfNextFont = info->fontList;
|
|
info->fontList = fp;
|
|
|
|
fp->rtfFName = NULL;
|
|
fp->rtfFAltName = NULL;
|
|
fp->rtfFNum = -1;
|
|
fp->rtfFFamily = FF_DONTCARE;
|
|
fp->rtfFCharSet = DEFAULT_CHARSET; /* 1 */
|
|
fp->rtfFPitch = DEFAULT_PITCH;
|
|
fp->rtfFType = 0;
|
|
fp->rtfFCodePage = CP_ACP;
|
|
|
|
while (info->rtfClass != rtfEOF
|
|
&& !RTFCheckCM (info, rtfText, ';')
|
|
&& !RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
{
|
|
if (info->rtfClass == rtfControl)
|
|
{
|
|
switch (info->rtfMajor)
|
|
{
|
|
default:
|
|
/* ignore token but announce it */
|
|
WARN ("unknown token \"%s\"\n",
|
|
info->rtfTextBuf);
|
|
break;
|
|
case rtfFontFamily:
|
|
fp->rtfFFamily = info->rtfMinor;
|
|
break;
|
|
case rtfCharAttr:
|
|
switch (info->rtfMinor)
|
|
{
|
|
default:
|
|
break; /* ignore unknown? */
|
|
case rtfFontNum:
|
|
fp->rtfFNum = info->rtfParam;
|
|
break;
|
|
}
|
|
break;
|
|
case rtfFontAttr:
|
|
switch (info->rtfMinor)
|
|
{
|
|
default:
|
|
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;
|
|
break;
|
|
case rtfFontCodePage:
|
|
fp->rtfFCodePage = info->rtfParam;
|
|
break;
|
|
case rtfFTypeNil:
|
|
case rtfFTypeTrueType:
|
|
fp->rtfFType = info->rtfParam;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup)) /* dest */
|
|
{
|
|
RTFSkipGroup (info); /* ignore for now */
|
|
}
|
|
else if (info->rtfClass == rtfText) /* font name */
|
|
{
|
|
bp = buf;
|
|
while (info->rtfClass == rtfText
|
|
&& !RTFCheckCM (info, rtfText, ';'))
|
|
{
|
|
*bp++ = info->rtfMajor;
|
|
RTFGetToken (info);
|
|
}
|
|
|
|
/* FIX: in some cases the <fontinfo> isn't finished with a semi-column */
|
|
if(RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
{
|
|
RTFUngetToken (info);
|
|
}
|
|
*bp = '\0';
|
|
fp->rtfFName = strdup (buf);
|
|
if (fp->rtfFName == NULL)
|
|
ERR ("cannot allocate font name\n");
|
|
/* already have next token; don't read one */
|
|
/* at bottom of loop */
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
/* ignore token but announce it */
|
|
WARN ("unknown token \"%s\"\n", info->rtfTextBuf);
|
|
}
|
|
RTFGetToken (info);
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
}
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
if (old == 0) /* need to see "}" here */
|
|
{
|
|
RTFGetToken (info);
|
|
if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
ERR ("missing \"}\"\n");
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
}
|
|
|
|
/* Apply the real properties of the default font */
|
|
if (fp->rtfFNum == info->defFont)
|
|
{
|
|
if (info->ansiCodePage != CP_UTF8)
|
|
info->codePage = fp->rtfFCodePage;
|
|
TRACE("default font codepage %d\n", info->codePage);
|
|
}
|
|
}
|
|
if (!fp || (fp->rtfFNum == -1))
|
|
ERR("missing font number\n");
|
|
/*
|
|
* Could check other pieces of structure here, too, I suppose.
|
|
*/
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
|
|
/* Set default font */
|
|
info->rtfClass = rtfControl;
|
|
info->rtfMajor = rtfCharAttr;
|
|
info->rtfMinor = rtfFontNum;
|
|
info->rtfParam = info->defFont;
|
|
lstrcpyA(info->rtfTextBuf, "f");
|
|
RTFUngetToken(info);
|
|
}
|
|
|
|
|
|
/*
|
|
* The color table entries have color values of -1 if
|
|
* the default color should be used for the entry (only
|
|
* a semi-colon is given in the definition, no color values).
|
|
* There will be a problem if a partial entry (1 or 2 but
|
|
* not 3 color values) is given. The possibility is ignored
|
|
* here.
|
|
*/
|
|
|
|
static void ReadColorTbl(RTF_Info *info)
|
|
{
|
|
RTFColor *cp;
|
|
int cnum = 0;
|
|
int group_level = 1;
|
|
|
|
for (;;)
|
|
{
|
|
RTFGetToken (info);
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
{
|
|
group_level--;
|
|
if (!group_level)
|
|
break;
|
|
continue;
|
|
}
|
|
else if (RTFCheckCM(info, rtfGroup, rtfBeginGroup))
|
|
{
|
|
group_level++;
|
|
continue;
|
|
}
|
|
|
|
cp = malloc (sizeof(*cp));
|
|
if (cp == NULL) {
|
|
ERR ("cannot allocate color entry\n");
|
|
break;
|
|
}
|
|
cp->rtfCNum = cnum++;
|
|
cp->rtfNextColor = info->colorList;
|
|
info->colorList = cp;
|
|
if (!RTFCheckCM (info, rtfControl, rtfColorName))
|
|
cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = -1;
|
|
else {
|
|
cp->rtfCRed = cp->rtfCGreen = cp->rtfCBlue = 0;
|
|
do {
|
|
switch (info->rtfMinor)
|
|
{
|
|
case rtfRed: cp->rtfCRed = info->rtfParam & 0xFF; break;
|
|
case rtfGreen: cp->rtfCGreen = info->rtfParam & 0xFF; break;
|
|
case rtfBlue: cp->rtfCBlue = info->rtfParam & 0xFF; break;
|
|
}
|
|
RTFGetToken (info);
|
|
} while (RTFCheckCM (info, rtfControl, rtfColorName));
|
|
}
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
if (!RTFCheckCM (info, rtfText, ';'))
|
|
ERR ("malformed entry\n");
|
|
}
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
}
|
|
|
|
|
|
/*
|
|
* The "Normal" style definition doesn't contain any style number,
|
|
* all others do. Normal style is given style rtfNormalStyleNum.
|
|
*/
|
|
|
|
static void ReadStyleSheet(RTF_Info *info)
|
|
{
|
|
RTFStyle *sp;
|
|
RTFStyleElt *sep, *sepLast;
|
|
char buf[rtfBufSiz], *bp;
|
|
int real_style;
|
|
|
|
for (;;)
|
|
{
|
|
RTFGetToken (info);
|
|
if (info->rtfClass == rtfEOF)
|
|
break;
|
|
if (RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
break;
|
|
sp = malloc (sizeof(*sp));
|
|
if (sp == NULL) {
|
|
ERR ("cannot allocate stylesheet entry\n");
|
|
break;
|
|
}
|
|
sp->rtfSName = NULL;
|
|
sp->rtfSNum = -1;
|
|
sp->rtfSType = rtfParStyle;
|
|
sp->rtfSAdditive = 0;
|
|
sp->rtfSBasedOn = rtfNoStyleNum;
|
|
sp->rtfSNextPar = -1;
|
|
sp->rtfSSEList = sepLast = NULL;
|
|
sp->rtfNextStyle = info->styleList;
|
|
sp->rtfExpanding = 0;
|
|
info->styleList = sp;
|
|
if (!RTFCheckCM (info, rtfGroup, rtfBeginGroup))
|
|
ERR ("missing \"{\"\n");
|
|
real_style = TRUE;
|
|
for (;;)
|
|
{
|
|
RTFGetToken (info);
|
|
if (info->rtfClass == rtfEOF
|
|
|| RTFCheckCM (info, rtfText, ';'))
|
|
break;
|
|
if (info->rtfClass == rtfControl)
|
|
{
|
|
if (RTFCheckMM (info, rtfSpecialChar, rtfOptDest)) {
|
|
RTFGetToken(info);
|
|
ERR("skipping optional destination\n");
|
|
RTFSkipGroup(info);
|
|
info->rtfClass = rtfGroup;
|
|
info->rtfMajor = rtfEndGroup;
|
|
real_style = FALSE;
|
|
break; /* ignore "\*" */
|
|
}
|
|
if (RTFCheckMM (info, rtfParAttr, rtfStyleNum))
|
|
{
|
|
sp->rtfSNum = info->rtfParam;
|
|
sp->rtfSType = rtfParStyle;
|
|
continue;
|
|
}
|
|
if (RTFCheckMM (info, rtfCharAttr, rtfCharStyleNum))
|
|
{
|
|
sp->rtfSNum = info->rtfParam;
|
|
sp->rtfSType = rtfCharStyle;
|
|
continue;
|
|
}
|
|
if (RTFCheckMM (info, rtfSectAttr, rtfSectStyleNum))
|
|
{
|
|
sp->rtfSNum = info->rtfParam;
|
|
sp->rtfSType = rtfSectStyle;
|
|
continue;
|
|
}
|
|
if (RTFCheckMM (info, rtfStyleAttr, rtfBasedOn))
|
|
{
|
|
sp->rtfSBasedOn = info->rtfParam;
|
|
continue;
|
|
}
|
|
if (RTFCheckMM (info, rtfStyleAttr, rtfAdditive))
|
|
{
|
|
sp->rtfSAdditive = 1;
|
|
continue;
|
|
}
|
|
if (RTFCheckMM (info, rtfStyleAttr, rtfNext))
|
|
{
|
|
sp->rtfSNextPar = info->rtfParam;
|
|
continue;
|
|
}
|
|
sep = malloc (sizeof(*sep));
|
|
if (sep == NULL)
|
|
{
|
|
ERR ("cannot allocate style element\n");
|
|
break;
|
|
}
|
|
sep->rtfSEClass = info->rtfClass;
|
|
sep->rtfSEMajor = info->rtfMajor;
|
|
sep->rtfSEMinor = info->rtfMinor;
|
|
sep->rtfSEParam = info->rtfParam;
|
|
sep->rtfSEText = strdup (info->rtfTextBuf);
|
|
if (sep->rtfSEText == NULL)
|
|
ERR ("cannot allocate style element text\n");
|
|
if (sepLast == NULL)
|
|
sp->rtfSSEList = sep; /* first element */
|
|
else /* add to end */
|
|
sepLast->rtfNextSE = sep;
|
|
sep->rtfNextSE = NULL;
|
|
sepLast = sep;
|
|
}
|
|
else if (RTFCheckCM (info, rtfGroup, rtfBeginGroup))
|
|
{
|
|
/*
|
|
* This passes over "{\*\keycode ... }, among
|
|
* other things. A temporary (perhaps) hack.
|
|
*/
|
|
ERR("skipping begin\n");
|
|
RTFSkipGroup (info);
|
|
continue;
|
|
}
|
|
else if (info->rtfClass == rtfText) /* style name */
|
|
{
|
|
bp = buf;
|
|
while (info->rtfClass == rtfText)
|
|
{
|
|
if (info->rtfMajor == ';')
|
|
{
|
|
/* put back for "for" loop */
|
|
RTFUngetToken (info);
|
|
break;
|
|
}
|
|
*bp++ = info->rtfMajor;
|
|
RTFGetToken (info);
|
|
}
|
|
*bp = '\0';
|
|
sp->rtfSName = strdup (buf);
|
|
if (sp->rtfSName == NULL)
|
|
ERR ("cannot allocate style name\n");
|
|
}
|
|
else /* unrecognized */
|
|
{
|
|
/* ignore token but announce it */
|
|
WARN ("unknown token \"%s\"\n", info->rtfTextBuf);
|
|
}
|
|
}
|
|
if (real_style) {
|
|
RTFGetToken (info);
|
|
if (!RTFCheckCM (info, rtfGroup, rtfEndGroup))
|
|
ERR ("missing \"}\"\n");
|
|
/*
|
|
* Check over the style structure. A name is a must.
|
|
* If no style number was specified, check whether it's the
|
|
* Normal style (in which case it's given style number
|
|
* rtfNormalStyleNum). Note that some "normal" style names
|
|
* just begin with "Normal" and can have other stuff following,
|
|
* e.g., "Normal,Times 10 point". Ugh.
|
|
*
|
|
* Some German RTF writers use "Standard" instead of "Normal".
|
|
*/
|
|
if (sp->rtfSName == NULL)
|
|
ERR ("missing style name\n");
|
|
if (sp->rtfSNum < 0)
|
|
{
|
|
if (strncmp (buf, "Normal", 6) != 0
|
|
&& strncmp (buf, "Standard", 8) != 0)
|
|
ERR ("missing style number\n");
|
|
sp->rtfSNum = rtfNormalStyleNum;
|
|
}
|
|
if (sp->rtfSNextPar == -1) /* if \snext not given, */
|
|
sp->rtfSNextPar = sp->rtfSNum; /* next is itself */
|
|
}
|
|
/* otherwise we're just dealing with fake end group from skipped group */
|
|
}
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
}
|
|
|
|
|
|
static void ReadInfoGroup(RTF_Info *info)
|
|
{
|
|
RTFSkipGroup (info);
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
}
|
|
|
|
|
|
static void ReadPictGroup(RTF_Info *info)
|
|
{
|
|
RTFSkipGroup (info);
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
}
|
|
|
|
|
|
static void ReadObjGroup(RTF_Info *info)
|
|
{
|
|
RTFSkipGroup (info);
|
|
RTFRouteToken (info); /* feed "}" back to router */
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Routines to return pieces of stylesheet, or font or color tables.
|
|
* References to style 0 are mapped onto the Normal style.
|
|
*/
|
|
|
|
RTFFont *RTFGetFont(const RTF_Info *info, int num)
|
|
{
|
|
RTFFont *f;
|
|
|
|
if (num == -1)
|
|
return (info->fontList);
|
|
for (f = info->fontList; f != NULL; f = f->rtfNextFont)
|
|
{
|
|
if (f->rtfFNum == num)
|
|
break;
|
|
}
|
|
return (f); /* NULL if not found */
|
|
}
|
|
|
|
|
|
RTFColor *RTFGetColor(const RTF_Info *info, int num)
|
|
{
|
|
RTFColor *c;
|
|
|
|
if (num == -1)
|
|
return (info->colorList);
|
|
for (c = info->colorList; c != NULL; c = c->rtfNextColor)
|
|
{
|
|
if (c->rtfCNum == num)
|
|
break;
|
|
}
|
|
return (c); /* NULL if not found */
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* Control symbol lookup routines
|
|
*/
|
|
|
|
|
|
typedef struct RTFKey RTFKey;
|
|
|
|
struct RTFKey
|
|
{
|
|
int rtfKMajor; /* major number */
|
|
int rtfKMinor; /* minor number */
|
|
const char *rtfKStr; /* symbol name */
|
|
int rtfKHash; /* symbol name hash value */
|
|
};
|
|
|
|
/*
|
|
* A minor number of -1 means the token has no minor number
|
|
* (all valid minor numbers are >= 0).
|
|
*/
|
|
|
|
static RTFKey rtfKey[] =
|
|
{
|
|
/*
|
|
* Special characters
|
|
*/
|
|
|
|
{ rtfSpecialChar, rtfIIntVersion, "vern", 0 },
|
|
{ rtfSpecialChar, rtfICreateTime, "creatim", 0 },
|
|
{ rtfSpecialChar, rtfIRevisionTime, "revtim", 0 },
|
|
{ rtfSpecialChar, rtfIPrintTime, "printim", 0 },
|
|
{ rtfSpecialChar, rtfIBackupTime, "buptim", 0 },
|
|
{ rtfSpecialChar, rtfIEditTime, "edmins", 0 },
|
|
{ rtfSpecialChar, rtfIYear, "yr", 0 },
|
|
{ rtfSpecialChar, rtfIMonth, "mo", 0 },
|
|
{ rtfSpecialChar, rtfIDay, "dy", 0 },
|
|
{ rtfSpecialChar, rtfIHour, "hr", 0 },
|
|
{ rtfSpecialChar, rtfIMinute, "min", 0 },
|
|
{ rtfSpecialChar, rtfISecond, "sec", 0 },
|
|
{ rtfSpecialChar, rtfINPages, "nofpages", 0 },
|
|
{ rtfSpecialChar, rtfINWords, "nofwords", 0 },
|
|
{ rtfSpecialChar, rtfINChars, "nofchars", 0 },
|
|
{ rtfSpecialChar, rtfIIntID, "id", 0 },
|
|
|
|
{ rtfSpecialChar, rtfCurHeadDate, "chdate", 0 },
|
|
{ rtfSpecialChar, rtfCurHeadDateLong, "chdpl", 0 },
|
|
{ rtfSpecialChar, rtfCurHeadDateAbbrev, "chdpa", 0 },
|
|
{ rtfSpecialChar, rtfCurHeadTime, "chtime", 0 },
|
|
{ rtfSpecialChar, rtfCurHeadPage, "chpgn", 0 },
|
|
{ rtfSpecialChar, rtfSectNum, "sectnum", 0 },
|
|
{ rtfSpecialChar, rtfCurFNote, "chftn", 0 },
|
|
{ rtfSpecialChar, rtfCurAnnotRef, "chatn", 0 },
|
|
{ rtfSpecialChar, rtfFNoteSep, "chftnsep", 0 },
|
|
{ rtfSpecialChar, rtfFNoteCont, "chftnsepc", 0 },
|
|
{ rtfSpecialChar, rtfCell, "cell", 0 },
|
|
{ rtfSpecialChar, rtfRow, "row", 0 },
|
|
{ rtfSpecialChar, rtfPar, "par", 0 },
|
|
/* newline and carriage return are synonyms for */
|
|
/* \par when they are preceded by a \ character */
|
|
{ rtfSpecialChar, rtfPar, "\n", 0 },
|
|
{ rtfSpecialChar, rtfPar, "\r", 0 },
|
|
{ rtfSpecialChar, rtfSect, "sect", 0 },
|
|
{ rtfSpecialChar, rtfPage, "page", 0 },
|
|
{ rtfSpecialChar, rtfColumn, "column", 0 },
|
|
{ rtfSpecialChar, rtfLine, "line", 0 },
|
|
{ rtfSpecialChar, rtfSoftPage, "softpage", 0 },
|
|
{ rtfSpecialChar, rtfSoftColumn, "softcol", 0 },
|
|
{ rtfSpecialChar, rtfSoftLine, "softline", 0 },
|
|
{ rtfSpecialChar, rtfSoftLineHt, "softlheight", 0 },
|
|
{ rtfSpecialChar, rtfTab, "tab", 0 },
|
|
{ rtfSpecialChar, rtfEmDash, "emdash", 0 },
|
|
{ rtfSpecialChar, rtfEnDash, "endash", 0 },
|
|
{ rtfSpecialChar, rtfEmSpace, "emspace", 0 },
|
|
{ rtfSpecialChar, rtfEnSpace, "enspace", 0 },
|
|
{ rtfSpecialChar, rtfBullet, "bullet", 0 },
|
|
{ rtfSpecialChar, rtfLQuote, "lquote", 0 },
|
|
{ rtfSpecialChar, rtfRQuote, "rquote", 0 },
|
|
{ rtfSpecialChar, rtfLDblQuote, "ldblquote", 0 },
|
|
{ rtfSpecialChar, rtfRDblQuote, "rdblquote", 0 },
|
|
{ rtfSpecialChar, rtfFormula, "|", 0 },
|
|
{ rtfSpecialChar, rtfNoBrkSpace, "~", 0 },
|
|
{ rtfSpecialChar, rtfNoReqHyphen, "-", 0 },
|
|
{ rtfSpecialChar, rtfNoBrkHyphen, "_", 0 },
|
|
{ rtfSpecialChar, rtfOptDest, "*", 0 },
|
|
{ rtfSpecialChar, rtfLTRMark, "ltrmark", 0 },
|
|
{ rtfSpecialChar, rtfRTLMark, "rtlmark", 0 },
|
|
{ rtfSpecialChar, rtfNoWidthJoiner, "zwj", 0 },
|
|
{ rtfSpecialChar, rtfNoWidthNonJoiner, "zwnj", 0 },
|
|
/* is this valid? */
|
|
{ rtfSpecialChar, rtfCurHeadPict, "chpict", 0 },
|
|
{ rtfSpecialChar, rtfUnicode, "u", 0 },
|
|
{ rtfSpecialChar, rtfNestCell, "nestcell", 0 },
|
|
{ rtfSpecialChar, rtfNestRow, "nestrow", 0 },
|
|
|
|
/*
|
|
* Character formatting attributes
|
|
*/
|
|
|
|
{ rtfCharAttr, rtfPlain, "plain", 0 },
|
|
{ rtfCharAttr, rtfBold, "b", 0 },
|
|
{ rtfCharAttr, rtfAllCaps, "caps", 0 },
|
|
{ rtfCharAttr, rtfDeleted, "deleted", 0 },
|
|
{ rtfCharAttr, rtfSubScript, "dn", 0 },
|
|
{ rtfCharAttr, rtfSubScrShrink, "sub", 0 },
|
|
{ rtfCharAttr, rtfNoSuperSub, "nosupersub", 0 },
|
|
{ rtfCharAttr, rtfExpand, "expnd", 0 },
|
|
{ rtfCharAttr, rtfExpandTwips, "expndtw", 0 },
|
|
{ rtfCharAttr, rtfKerning, "kerning", 0 },
|
|
{ rtfCharAttr, rtfFontNum, "f", 0 },
|
|
{ rtfCharAttr, rtfFontSize, "fs", 0 },
|
|
{ rtfCharAttr, rtfItalic, "i", 0 },
|
|
{ rtfCharAttr, rtfOutline, "outl", 0 },
|
|
{ rtfCharAttr, rtfRevised, "revised", 0 },
|
|
{ rtfCharAttr, rtfRevAuthor, "revauth", 0 },
|
|
{ rtfCharAttr, rtfRevDTTM, "revdttm", 0 },
|
|
{ rtfCharAttr, rtfSmallCaps, "scaps", 0 },
|
|
{ rtfCharAttr, rtfShadow, "shad", 0 },
|
|
{ rtfCharAttr, rtfStrikeThru, "strike", 0 },
|
|
{ rtfCharAttr, rtfUnderline, "ul", 0 },
|
|
{ rtfCharAttr, rtfDotUnderline, "uld", 0 },
|
|
{ rtfCharAttr, rtfDbUnderline, "uldb", 0 },
|
|
{ rtfCharAttr, rtfNoUnderline, "ulnone", 0 },
|
|
{ rtfCharAttr, rtfWordUnderline, "ulw", 0 },
|
|
{ rtfCharAttr, rtfSuperScript, "up", 0 },
|
|
{ rtfCharAttr, rtfSuperScrShrink, "super", 0 },
|
|
{ rtfCharAttr, rtfInvisible, "v", 0 },
|
|
{ rtfCharAttr, rtfForeColor, "cf", 0 },
|
|
{ rtfCharAttr, rtfBackColor, "highlight", 0 },
|
|
{ rtfCharAttr, rtfRTLChar, "rtlch", 0 },
|
|
{ rtfCharAttr, rtfLTRChar, "ltrch", 0 },
|
|
{ rtfCharAttr, rtfCharStyleNum, "cs", 0 },
|
|
{ rtfCharAttr, rtfCharCharSet, "cchs", 0 },
|
|
{ rtfCharAttr, rtfLanguage, "lang", 0 },
|
|
/* this has disappeared from spec 1.2 */
|
|
{ rtfCharAttr, rtfGray, "gray", 0 },
|
|
{ rtfCharAttr, rtfUnicodeLength, "uc", 0 },
|
|
|
|
/*
|
|
* Paragraph formatting attributes
|
|
*/
|
|
|
|
{ rtfParAttr, rtfParDef, "pard", 0 },
|
|
{ rtfParAttr, rtfStyleNum, "s", 0 },
|
|
{ rtfParAttr, rtfHyphenate, "hyphpar", 0 },
|
|
{ rtfParAttr, rtfInTable, "intbl", 0 },
|
|
{ rtfParAttr, rtfKeep, "keep", 0 },
|
|
{ rtfParAttr, rtfNoWidowControl, "nowidctlpar", 0 },
|
|
{ rtfParAttr, rtfKeepNext, "keepn", 0 },
|
|
{ rtfParAttr, rtfOutlineLevel, "level", 0 },
|
|
{ rtfParAttr, rtfNoLineNum, "noline", 0 },
|
|
{ rtfParAttr, rtfPBBefore, "pagebb", 0 },
|
|
{ rtfParAttr, rtfSideBySide, "sbys", 0 },
|
|
{ rtfParAttr, rtfQuadLeft, "ql", 0 },
|
|
{ rtfParAttr, rtfQuadRight, "qr", 0 },
|
|
{ rtfParAttr, rtfQuadJust, "qj", 0 },
|
|
{ rtfParAttr, rtfQuadCenter, "qc", 0 },
|
|
{ rtfParAttr, rtfFirstIndent, "fi", 0 },
|
|
{ rtfParAttr, rtfLeftIndent, "li", 0 },
|
|
{ rtfParAttr, rtfRightIndent, "ri", 0 },
|
|
{ rtfParAttr, rtfSpaceBefore, "sb", 0 },
|
|
{ rtfParAttr, rtfSpaceAfter, "sa", 0 },
|
|
{ rtfParAttr, rtfSpaceBetween, "sl", 0 },
|
|
{ rtfParAttr, rtfSpaceMultiply, "slmult", 0 },
|
|
|
|
{ rtfParAttr, rtfSubDocument, "subdocument", 0 },
|
|
|
|
{ rtfParAttr, rtfRTLPar, "rtlpar", 0 },
|
|
{ rtfParAttr, rtfLTRPar, "ltrpar", 0 },
|
|
|
|
{ rtfParAttr, rtfTabPos, "tx", 0 },
|
|
/*
|
|
* FrameMaker writes \tql (to mean left-justified tab, apparently)
|
|
* although it's not in the spec. It's also redundant, since lj
|
|
* tabs are the default.
|
|
*/
|
|
{ rtfParAttr, rtfTabLeft, "tql", 0 },
|
|
{ rtfParAttr, rtfTabRight, "tqr", 0 },
|
|
{ rtfParAttr, rtfTabCenter, "tqc", 0 },
|
|
{ rtfParAttr, rtfTabDecimal, "tqdec", 0 },
|
|
{ rtfParAttr, rtfTabBar, "tb", 0 },
|
|
{ rtfParAttr, rtfLeaderDot, "tldot", 0 },
|
|
{ rtfParAttr, rtfLeaderHyphen, "tlhyph", 0 },
|
|
{ rtfParAttr, rtfLeaderUnder, "tlul", 0 },
|
|
{ rtfParAttr, rtfLeaderThick, "tlth", 0 },
|
|
{ rtfParAttr, rtfLeaderEqual, "tleq", 0 },
|
|
|
|
{ rtfParAttr, rtfParLevel, "pnlvl", 0 },
|
|
{ rtfParAttr, rtfParBullet, "pnlvlblt", 0 },
|
|
{ rtfParAttr, rtfParSimple, "pnlvlbody", 0 },
|
|
{ rtfParAttr, rtfParNumCont, "pnlvlcont", 0 },
|
|
{ rtfParAttr, rtfParNumOnce, "pnnumonce", 0 },
|
|
{ rtfParAttr, rtfParNumAcross, "pnacross", 0 },
|
|
{ rtfParAttr, rtfParHangIndent, "pnhang", 0 },
|
|
{ rtfParAttr, rtfParNumRestart, "pnrestart", 0 },
|
|
{ rtfParAttr, rtfParNumCardinal, "pncard", 0 },
|
|
{ rtfParAttr, rtfParNumDecimal, "pndec", 0 },
|
|
{ rtfParAttr, rtfParNumULetter, "pnucltr", 0 },
|
|
{ rtfParAttr, rtfParNumURoman, "pnucrm", 0 },
|
|
{ rtfParAttr, rtfParNumLLetter, "pnlcltr", 0 },
|
|
{ rtfParAttr, rtfParNumLRoman, "pnlcrm", 0 },
|
|
{ rtfParAttr, rtfParNumOrdinal, "pnord", 0 },
|
|
{ rtfParAttr, rtfParNumOrdinalText, "pnordt", 0 },
|
|
{ rtfParAttr, rtfParNumBold, "pnb", 0 },
|
|
{ rtfParAttr, rtfParNumItalic, "pni", 0 },
|
|
{ rtfParAttr, rtfParNumAllCaps, "pncaps", 0 },
|
|
{ rtfParAttr, rtfParNumSmallCaps, "pnscaps", 0 },
|
|
{ rtfParAttr, rtfParNumUnder, "pnul", 0 },
|
|
{ rtfParAttr, rtfParNumDotUnder, "pnuld", 0 },
|
|
{ rtfParAttr, rtfParNumDbUnder, "pnuldb", 0 },
|
|
{ rtfParAttr, rtfParNumNoUnder, "pnulnone", 0 },
|
|
{ rtfParAttr, rtfParNumWordUnder, "pnulw", 0 },
|
|
{ rtfParAttr, rtfParNumStrikethru, "pnstrike", 0 },
|
|
{ rtfParAttr, rtfParNumForeColor, "pncf", 0 },
|
|
{ rtfParAttr, rtfParNumFont, "pnf", 0 },
|
|
{ rtfParAttr, rtfParNumFontSize, "pnfs", 0 },
|
|
{ rtfParAttr, rtfParNumIndent, "pnindent", 0 },
|
|
{ rtfParAttr, rtfParNumSpacing, "pnsp", 0 },
|
|
{ rtfParAttr, rtfParNumInclPrev, "pnprev", 0 },
|
|
{ rtfParAttr, rtfParNumCenter, "pnqc", 0 },
|
|
{ rtfParAttr, rtfParNumLeft, "pnql", 0 },
|
|
{ rtfParAttr, rtfParNumRight, "pnqr", 0 },
|
|
{ rtfParAttr, rtfParNumStartAt, "pnstart", 0 },
|
|
|
|
{ rtfParAttr, rtfBorderTop, "brdrt", 0 },
|
|
{ rtfParAttr, rtfBorderBottom, "brdrb", 0 },
|
|
{ rtfParAttr, rtfBorderLeft, "brdrl", 0 },
|
|
{ rtfParAttr, rtfBorderRight, "brdrr", 0 },
|
|
{ rtfParAttr, rtfBorderBetween, "brdrbtw", 0 },
|
|
{ rtfParAttr, rtfBorderBar, "brdrbar", 0 },
|
|
{ rtfParAttr, rtfBorderBox, "box", 0 },
|
|
{ rtfParAttr, rtfBorderSingle, "brdrs", 0 },
|
|
{ rtfParAttr, rtfBorderThick, "brdrth", 0 },
|
|
{ rtfParAttr, rtfBorderShadow, "brdrsh", 0 },
|
|
{ rtfParAttr, rtfBorderDouble, "brdrdb", 0 },
|
|
{ rtfParAttr, rtfBorderDot, "brdrdot", 0 },
|
|
{ rtfParAttr, rtfBorderDot, "brdrdash", 0 },
|
|
{ rtfParAttr, rtfBorderHair, "brdrhair", 0 },
|
|
{ rtfParAttr, rtfBorderWidth, "brdrw", 0 },
|
|
{ rtfParAttr, rtfBorderColor, "brdrcf", 0 },
|
|
{ rtfParAttr, rtfBorderSpace, "brsp", 0 },
|
|
|
|
{ rtfParAttr, rtfShading, "shading", 0 },
|
|
{ rtfParAttr, rtfBgPatH, "bghoriz", 0 },
|
|
{ rtfParAttr, rtfBgPatV, "bgvert", 0 },
|
|
{ rtfParAttr, rtfFwdDiagBgPat, "bgfdiag", 0 },
|
|
{ rtfParAttr, rtfBwdDiagBgPat, "bgbdiag", 0 },
|
|
{ rtfParAttr, rtfHatchBgPat, "bgcross", 0 },
|
|
{ rtfParAttr, rtfDiagHatchBgPat, "bgdcross", 0 },
|
|
{ rtfParAttr, rtfDarkBgPatH, "bgdkhoriz", 0 },
|
|
{ rtfParAttr, rtfDarkBgPatV, "bgdkvert", 0 },
|
|
{ rtfParAttr, rtfFwdDarkBgPat, "bgdkfdiag", 0 },
|
|
{ rtfParAttr, rtfBwdDarkBgPat, "bgdkbdiag", 0 },
|
|
{ rtfParAttr, rtfDarkHatchBgPat, "bgdkcross", 0 },
|
|
{ rtfParAttr, rtfDarkDiagHatchBgPat, "bgdkdcross", 0 },
|
|
{ rtfParAttr, rtfBgPatLineColor, "cfpat", 0 },
|
|
{ rtfParAttr, rtfBgPatColor, "cbpat", 0 },
|
|
{ rtfParAttr, rtfNestLevel, "itap", 0 },
|
|
|
|
/*
|
|
* Section formatting attributes
|
|
*/
|
|
|
|
{ rtfSectAttr, rtfSectDef, "sectd", 0 },
|
|
{ rtfSectAttr, rtfENoteHere, "endnhere", 0 },
|
|
{ rtfSectAttr, rtfPrtBinFirst, "binfsxn", 0 },
|
|
{ rtfSectAttr, rtfPrtBin, "binsxn", 0 },
|
|
{ rtfSectAttr, rtfSectStyleNum, "ds", 0 },
|
|
|
|
{ rtfSectAttr, rtfNoBreak, "sbknone", 0 },
|
|
{ rtfSectAttr, rtfColBreak, "sbkcol", 0 },
|
|
{ rtfSectAttr, rtfPageBreak, "sbkpage", 0 },
|
|
{ rtfSectAttr, rtfEvenBreak, "sbkeven", 0 },
|
|
{ rtfSectAttr, rtfOddBreak, "sbkodd", 0 },
|
|
|
|
{ rtfSectAttr, rtfColumns, "cols", 0 },
|
|
{ rtfSectAttr, rtfColumnSpace, "colsx", 0 },
|
|
{ rtfSectAttr, rtfColumnNumber, "colno", 0 },
|
|
{ rtfSectAttr, rtfColumnSpRight, "colsr", 0 },
|
|
{ rtfSectAttr, rtfColumnWidth, "colw", 0 },
|
|
{ rtfSectAttr, rtfColumnLine, "linebetcol", 0 },
|
|
|
|
{ rtfSectAttr, rtfLineModulus, "linemod", 0 },
|
|
{ rtfSectAttr, rtfLineDist, "linex", 0 },
|
|
{ rtfSectAttr, rtfLineStarts, "linestarts", 0 },
|
|
{ rtfSectAttr, rtfLineRestart, "linerestart", 0 },
|
|
{ rtfSectAttr, rtfLineRestartPg, "lineppage", 0 },
|
|
{ rtfSectAttr, rtfLineCont, "linecont", 0 },
|
|
|
|
{ rtfSectAttr, rtfSectPageWid, "pgwsxn", 0 },
|
|
{ rtfSectAttr, rtfSectPageHt, "pghsxn", 0 },
|
|
{ rtfSectAttr, rtfSectMarginLeft, "marglsxn", 0 },
|
|
{ rtfSectAttr, rtfSectMarginRight, "margrsxn", 0 },
|
|
{ rtfSectAttr, rtfSectMarginTop, "margtsxn", 0 },
|
|
{ rtfSectAttr, rtfSectMarginBottom, "margbsxn", 0 },
|
|
{ rtfSectAttr, rtfSectMarginGutter, "guttersxn", 0 },
|
|
{ rtfSectAttr, rtfSectLandscape, "lndscpsxn", 0 },
|
|
{ rtfSectAttr, rtfTitleSpecial, "titlepg", 0 },
|
|
{ rtfSectAttr, rtfHeaderY, "headery", 0 },
|
|
{ rtfSectAttr, rtfFooterY, "footery", 0 },
|
|
|
|
{ rtfSectAttr, rtfPageStarts, "pgnstarts", 0 },
|
|
{ rtfSectAttr, rtfPageCont, "pgncont", 0 },
|
|
{ rtfSectAttr, rtfPageRestart, "pgnrestart", 0 },
|
|
{ rtfSectAttr, rtfPageNumRight, "pgnx", 0 },
|
|
{ rtfSectAttr, rtfPageNumTop, "pgny", 0 },
|
|
{ rtfSectAttr, rtfPageDecimal, "pgndec", 0 },
|
|
{ rtfSectAttr, rtfPageURoman, "pgnucrm", 0 },
|
|
{ rtfSectAttr, rtfPageLRoman, "pgnlcrm", 0 },
|
|
{ rtfSectAttr, rtfPageULetter, "pgnucltr", 0 },
|
|
{ rtfSectAttr, rtfPageLLetter, "pgnlcltr", 0 },
|
|
{ rtfSectAttr, rtfPageNumHyphSep, "pgnhnsh", 0 },
|
|
{ rtfSectAttr, rtfPageNumSpaceSep, "pgnhnsp", 0 },
|
|
{ rtfSectAttr, rtfPageNumColonSep, "pgnhnsc", 0 },
|
|
{ rtfSectAttr, rtfPageNumEmdashSep, "pgnhnsm", 0 },
|
|
{ rtfSectAttr, rtfPageNumEndashSep, "pgnhnsn", 0 },
|
|
|
|
{ rtfSectAttr, rtfTopVAlign, "vertalt", 0 },
|
|
/* misspelled as "vertal" in specification 1.0 */
|
|
{ rtfSectAttr, rtfBottomVAlign, "vertalb", 0 },
|
|
{ rtfSectAttr, rtfCenterVAlign, "vertalc", 0 },
|
|
{ rtfSectAttr, rtfJustVAlign, "vertalj", 0 },
|
|
|
|
{ rtfSectAttr, rtfRTLSect, "rtlsect", 0 },
|
|
{ rtfSectAttr, rtfLTRSect, "ltrsect", 0 },
|
|
|
|
/* I've seen these in an old spec, but not in real files... */
|
|
/*rtfSectAttr, rtfNoBreak, "nobreak", 0,*/
|
|
/*rtfSectAttr, rtfColBreak, "colbreak", 0,*/
|
|
/*rtfSectAttr, rtfPageBreak, "pagebreak", 0,*/
|
|
/*rtfSectAttr, rtfEvenBreak, "evenbreak", 0,*/
|
|
/*rtfSectAttr, rtfOddBreak, "oddbreak", 0,*/
|
|
|
|
/*
|
|
* Document formatting attributes
|
|
*/
|
|
|
|
{ rtfDocAttr, rtfDefTab, "deftab", 0 },
|
|
{ rtfDocAttr, rtfHyphHotZone, "hyphhotz", 0 },
|
|
{ rtfDocAttr, rtfHyphConsecLines, "hyphconsec", 0 },
|
|
{ rtfDocAttr, rtfHyphCaps, "hyphcaps", 0 },
|
|
{ rtfDocAttr, rtfHyphAuto, "hyphauto", 0 },
|
|
{ rtfDocAttr, rtfLineStart, "linestart", 0 },
|
|
{ rtfDocAttr, rtfFracWidth, "fracwidth", 0 },
|
|
/* \makeback was given in old version of spec, it's now */
|
|
/* listed as \makebackup */
|
|
{ rtfDocAttr, rtfMakeBackup, "makeback", 0 },
|
|
{ rtfDocAttr, rtfMakeBackup, "makebackup", 0 },
|
|
{ rtfDocAttr, rtfRTFDefault, "defformat", 0 },
|
|
{ rtfDocAttr, rtfPSOverlay, "psover", 0 },
|
|
{ rtfDocAttr, rtfDocTemplate, "doctemp", 0 },
|
|
{ rtfDocAttr, rtfDefLanguage, "deflang", 0 },
|
|
|
|
{ rtfDocAttr, rtfFENoteType, "fet", 0 },
|
|
{ rtfDocAttr, rtfFNoteEndSect, "endnotes", 0 },
|
|
{ rtfDocAttr, rtfFNoteEndDoc, "enddoc", 0 },
|
|
{ rtfDocAttr, rtfFNoteText, "ftntj", 0 },
|
|
{ rtfDocAttr, rtfFNoteBottom, "ftnbj", 0 },
|
|
{ rtfDocAttr, rtfENoteEndSect, "aendnotes", 0 },
|
|
{ rtfDocAttr, rtfENoteEndDoc, "aenddoc", 0 },
|
|
{ rtfDocAttr, rtfENoteText, "aftntj", 0 },
|
|
{ rtfDocAttr, rtfENoteBottom, "aftnbj", 0 },
|
|
{ rtfDocAttr, rtfFNoteStart, "ftnstart", 0 },
|
|
{ rtfDocAttr, rtfENoteStart, "aftnstart", 0 },
|
|
{ rtfDocAttr, rtfFNoteRestartPage, "ftnrstpg", 0 },
|
|
{ rtfDocAttr, rtfFNoteRestart, "ftnrestart", 0 },
|
|
{ rtfDocAttr, rtfFNoteRestartCont, "ftnrstcont", 0 },
|
|
{ rtfDocAttr, rtfENoteRestart, "aftnrestart", 0 },
|
|
{ rtfDocAttr, rtfENoteRestartCont, "aftnrstcont", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumArabic, "ftnnar", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumLLetter, "ftnnalc", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumULetter, "ftnnauc", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumLRoman, "ftnnrlc", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumURoman, "ftnnruc", 0 },
|
|
{ rtfDocAttr, rtfFNoteNumChicago, "ftnnchi", 0 },
|
|
{ rtfDocAttr, rtfENoteNumArabic, "aftnnar", 0 },
|
|
{ rtfDocAttr, rtfENoteNumLLetter, "aftnnalc", 0 },
|
|
{ rtfDocAttr, rtfENoteNumULetter, "aftnnauc", 0 },
|
|
{ rtfDocAttr, rtfENoteNumLRoman, "aftnnrlc", 0 },
|
|
{ rtfDocAttr, rtfENoteNumURoman, "aftnnruc", 0 },
|
|
{ rtfDocAttr, rtfENoteNumChicago, "aftnnchi", 0 },
|
|
|
|
{ rtfDocAttr, rtfPaperWidth, "paperw", 0 },
|
|
{ rtfDocAttr, rtfPaperHeight, "paperh", 0 },
|
|
{ rtfDocAttr, rtfPaperSize, "psz", 0 },
|
|
{ rtfDocAttr, rtfLeftMargin, "margl", 0 },
|
|
{ rtfDocAttr, rtfRightMargin, "margr", 0 },
|
|
{ rtfDocAttr, rtfTopMargin, "margt", 0 },
|
|
{ rtfDocAttr, rtfBottomMargin, "margb", 0 },
|
|
{ rtfDocAttr, rtfFacingPage, "facingp", 0 },
|
|
{ rtfDocAttr, rtfGutterWid, "gutter", 0 },
|
|
{ rtfDocAttr, rtfMirrorMargin, "margmirror", 0 },
|
|
{ rtfDocAttr, rtfLandscape, "landscape", 0 },
|
|
{ rtfDocAttr, rtfPageStart, "pgnstart", 0 },
|
|
{ rtfDocAttr, rtfWidowCtrl, "widowctrl", 0 },
|
|
|
|
{ rtfDocAttr, rtfLinkStyles, "linkstyles", 0 },
|
|
|
|
{ rtfDocAttr, rtfNoAutoTabIndent, "notabind", 0 },
|
|
{ rtfDocAttr, rtfWrapSpaces, "wraptrsp", 0 },
|
|
{ rtfDocAttr, rtfPrintColorsBlack, "prcolbl", 0 },
|
|
{ rtfDocAttr, rtfNoExtraSpaceRL, "noextrasprl", 0 },
|
|
{ rtfDocAttr, rtfNoColumnBalance, "nocolbal", 0 },
|
|
{ rtfDocAttr, rtfCvtMailMergeQuote, "cvmme", 0 },
|
|
{ rtfDocAttr, rtfSuppressTopSpace, "sprstsp", 0 },
|
|
{ rtfDocAttr, rtfSuppressPreParSpace, "sprsspbf", 0 },
|
|
{ rtfDocAttr, rtfCombineTblBorders, "otblrul", 0 },
|
|
{ rtfDocAttr, rtfTranspMetafiles, "transmf", 0 },
|
|
{ rtfDocAttr, rtfSwapBorders, "swpbdr", 0 },
|
|
{ rtfDocAttr, rtfShowHardBreaks, "brkfrm", 0 },
|
|
|
|
{ rtfDocAttr, rtfFormProtected, "formprot", 0 },
|
|
{ rtfDocAttr, rtfAllProtected, "allprot", 0 },
|
|
{ rtfDocAttr, rtfFormShading, "formshade", 0 },
|
|
{ rtfDocAttr, rtfFormDisplay, "formdisp", 0 },
|
|
{ rtfDocAttr, rtfPrintData, "printdata", 0 },
|
|
|
|
{ rtfDocAttr, rtfRevProtected, "revprot", 0 },
|
|
{ rtfDocAttr, rtfRevisions, "revisions", 0 },
|
|
{ rtfDocAttr, rtfRevDisplay, "revprop", 0 },
|
|
{ rtfDocAttr, rtfRevBar, "revbar", 0 },
|
|
|
|
{ rtfDocAttr, rtfAnnotProtected, "annotprot", 0 },
|
|
|
|
{ rtfDocAttr, rtfRTLDoc, "rtldoc", 0 },
|
|
{ rtfDocAttr, rtfLTRDoc, "ltrdoc", 0 },
|
|
|
|
{ rtfDocAttr, rtfAnsiCodePage, "ansicpg", 0 },
|
|
{ rtfDocAttr, rtfUTF8RTF, "urtf", 0 },
|
|
|
|
/*
|
|
* Style attributes
|
|
*/
|
|
|
|
{ rtfStyleAttr, rtfAdditive, "additive", 0 },
|
|
{ rtfStyleAttr, rtfBasedOn, "sbasedon", 0 },
|
|
{ rtfStyleAttr, rtfNext, "snext", 0 },
|
|
|
|
/*
|
|
* Picture attributes
|
|
*/
|
|
|
|
{ rtfPictAttr, rtfMacQD, "macpict", 0 },
|
|
{ rtfPictAttr, rtfPMMetafile, "pmmetafile", 0 },
|
|
{ rtfPictAttr, rtfWinMetafile, "wmetafile", 0 },
|
|
{ rtfPictAttr, rtfDevIndBitmap, "dibitmap", 0 },
|
|
{ rtfPictAttr, rtfWinBitmap, "wbitmap", 0 },
|
|
{ rtfPictAttr, rtfEmfBlip, "emfblip", 0 },
|
|
{ rtfPictAttr, rtfPixelBits, "wbmbitspixel", 0 },
|
|
{ rtfPictAttr, rtfBitmapPlanes, "wbmplanes", 0 },
|
|
{ rtfPictAttr, rtfBitmapWid, "wbmwidthbytes", 0 },
|
|
|
|
{ rtfPictAttr, rtfPicWid, "picw", 0 },
|
|
{ rtfPictAttr, rtfPicHt, "pich", 0 },
|
|
{ rtfPictAttr, rtfPicGoalWid, "picwgoal", 0 },
|
|
{ rtfPictAttr, rtfPicGoalHt, "pichgoal", 0 },
|
|
/* these two aren't in the spec, but some writers emit them */
|
|
{ rtfPictAttr, rtfPicGoalWid, "picwGoal", 0 },
|
|
{ rtfPictAttr, rtfPicGoalHt, "pichGoal", 0 },
|
|
{ rtfPictAttr, rtfPicScaleX, "picscalex", 0 },
|
|
{ rtfPictAttr, rtfPicScaleY, "picscaley", 0 },
|
|
{ rtfPictAttr, rtfPicScaled, "picscaled", 0 },
|
|
{ rtfPictAttr, rtfPicCropTop, "piccropt", 0 },
|
|
{ rtfPictAttr, rtfPicCropBottom, "piccropb", 0 },
|
|
{ rtfPictAttr, rtfPicCropLeft, "piccropl", 0 },
|
|
{ rtfPictAttr, rtfPicCropRight, "piccropr", 0 },
|
|
|
|
{ rtfPictAttr, rtfPicMFHasBitmap, "picbmp", 0 },
|
|
{ rtfPictAttr, rtfPicMFBitsPerPixel, "picbpp", 0 },
|
|
|
|
{ rtfPictAttr, rtfPicBinary, "bin", 0 },
|
|
|
|
/*
|
|
* NeXT graphic attributes
|
|
*/
|
|
|
|
{ rtfNeXTGrAttr, rtfNeXTGWidth, "width", 0 },
|
|
{ rtfNeXTGrAttr, rtfNeXTGHeight, "height", 0 },
|
|
|
|
/*
|
|
* Destinations
|
|
*/
|
|
|
|
{ rtfDestination, rtfFontTbl, "fonttbl", 0 },
|
|
{ rtfDestination, rtfFontAltName, "falt", 0 },
|
|
{ rtfDestination, rtfEmbeddedFont, "fonteb", 0 },
|
|
{ rtfDestination, rtfFontFile, "fontfile", 0 },
|
|
{ rtfDestination, rtfFileTbl, "filetbl", 0 },
|
|
{ rtfDestination, rtfFileInfo, "file", 0 },
|
|
{ rtfDestination, rtfColorTbl, "colortbl", 0 },
|
|
{ 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 },
|
|
{ rtfDestination, rtfIAuthor, "author", 0 },
|
|
{ rtfDestination, rtfIOperator, "operator", 0 },
|
|
{ rtfDestination, rtfIKeywords, "keywords", 0 },
|
|
{ rtfDestination, rtfIComment, "comment", 0 },
|
|
{ rtfDestination, rtfIVersion, "version", 0 },
|
|
{ rtfDestination, rtfIDoccomm, "doccomm", 0 },
|
|
/* \verscomm may not exist -- was seen in earlier spec version */
|
|
{ rtfDestination, rtfIVerscomm, "verscomm", 0 },
|
|
{ rtfDestination, rtfNextFile, "nextfile", 0 },
|
|
{ rtfDestination, rtfTemplate, "template", 0 },
|
|
{ rtfDestination, rtfFNSep, "ftnsep", 0 },
|
|
{ rtfDestination, rtfFNContSep, "ftnsepc", 0 },
|
|
{ rtfDestination, rtfFNContNotice, "ftncn", 0 },
|
|
{ rtfDestination, rtfENSep, "aftnsep", 0 },
|
|
{ rtfDestination, rtfENContSep, "aftnsepc", 0 },
|
|
{ rtfDestination, rtfENContNotice, "aftncn", 0 },
|
|
{ rtfDestination, rtfPageNumLevel, "pgnhn", 0 },
|
|
{ rtfDestination, rtfParNumLevelStyle, "pnseclvl", 0 },
|
|
{ rtfDestination, rtfHeader, "header", 0 },
|
|
{ rtfDestination, rtfFooter, "footer", 0 },
|
|
{ rtfDestination, rtfHeaderLeft, "headerl", 0 },
|
|
{ rtfDestination, rtfHeaderRight, "headerr", 0 },
|
|
{ rtfDestination, rtfHeaderFirst, "headerf", 0 },
|
|
{ rtfDestination, rtfFooterLeft, "footerl", 0 },
|
|
{ rtfDestination, rtfFooterRight, "footerr", 0 },
|
|
{ rtfDestination, rtfFooterFirst, "footerf", 0 },
|
|
{ rtfDestination, rtfParNumText, "pntext", 0 },
|
|
{ rtfDestination, rtfParNumbering, "pn", 0 },
|
|
{ rtfDestination, rtfParNumTextAfter, "pntxta", 0 },
|
|
{ rtfDestination, rtfParNumTextBefore, "pntxtb", 0 },
|
|
{ rtfDestination, rtfBookmarkStart, "bkmkstart", 0 },
|
|
{ rtfDestination, rtfBookmarkEnd, "bkmkend", 0 },
|
|
{ rtfDestination, rtfPict, "pict", 0 },
|
|
{ rtfDestination, rtfObject, "object", 0 },
|
|
{ rtfDestination, rtfObjClass, "objclass", 0 },
|
|
{ rtfDestination, rtfObjName, "objname", 0 },
|
|
{ rtfObjAttr, rtfObjTime, "objtime", 0 },
|
|
{ rtfDestination, rtfObjData, "objdata", 0 },
|
|
{ rtfDestination, rtfObjAlias, "objalias", 0 },
|
|
{ rtfDestination, rtfObjSection, "objsect", 0 },
|
|
/* objitem and objtopic aren't documented in the spec! */
|
|
{ rtfDestination, rtfObjItem, "objitem", 0 },
|
|
{ rtfDestination, rtfObjTopic, "objtopic", 0 },
|
|
{ rtfDestination, rtfObjResult, "result", 0 },
|
|
{ rtfDestination, rtfDrawObject, "do", 0 },
|
|
{ rtfDestination, rtfFootnote, "footnote", 0 },
|
|
{ rtfDestination, rtfAnnotRefStart, "atrfstart", 0 },
|
|
{ rtfDestination, rtfAnnotRefEnd, "atrfend", 0 },
|
|
{ rtfDestination, rtfAnnotID, "atnid", 0 },
|
|
{ rtfDestination, rtfAnnotAuthor, "atnauthor", 0 },
|
|
{ rtfDestination, rtfAnnotation, "annotation", 0 },
|
|
{ rtfDestination, rtfAnnotRef, "atnref", 0 },
|
|
{ rtfDestination, rtfAnnotTime, "atntime", 0 },
|
|
{ rtfDestination, rtfAnnotIcon, "atnicn", 0 },
|
|
{ rtfDestination, rtfField, "field", 0 },
|
|
{ rtfDestination, rtfFieldInst, "fldinst", 0 },
|
|
{ rtfDestination, rtfFieldResult, "fldrslt", 0 },
|
|
{ rtfDestination, rtfDataField, "datafield", 0 },
|
|
{ rtfDestination, rtfIndex, "xe", 0 },
|
|
{ rtfDestination, rtfIndexText, "txe", 0 },
|
|
{ rtfDestination, rtfIndexRange, "rxe", 0 },
|
|
{ rtfDestination, rtfTOC, "tc", 0 },
|
|
{ rtfDestination, rtfNeXTGraphic, "NeXTGraphic", 0 },
|
|
{ rtfDestination, rtfNestTableProps, "nesttableprops", 0 },
|
|
{ rtfDestination, rtfNoNestTables, "nonesttables", 0 },
|
|
{ rtfDestination, rtfShpPict, "shppict", 0 },
|
|
{ rtfDestination, rtfNonShpPict, "nonshppict", 0 },
|
|
|
|
/*
|
|
* Font families
|
|
*/
|
|
|
|
{ rtfFontFamily, rtfFFNil, "fnil", 0 },
|
|
{ rtfFontFamily, rtfFFRoman, "froman", 0 },
|
|
{ rtfFontFamily, rtfFFSwiss, "fswiss", 0 },
|
|
{ rtfFontFamily, rtfFFModern, "fmodern", 0 },
|
|
{ rtfFontFamily, rtfFFScript, "fscript", 0 },
|
|
{ rtfFontFamily, rtfFFDecor, "fdecor", 0 },
|
|
{ rtfFontFamily, rtfFFTech, "ftech", 0 },
|
|
{ rtfFontFamily, rtfFFBidirectional, "fbidi", 0 },
|
|
|
|
/*
|
|
* Font attributes
|
|
*/
|
|
|
|
{ rtfFontAttr, rtfFontCharSet, "fcharset", 0 },
|
|
{ rtfFontAttr, rtfFontPitch, "fprq", 0 },
|
|
{ rtfFontAttr, rtfFontCodePage, "cpg", 0 },
|
|
{ rtfFontAttr, rtfFTypeNil, "ftnil", 0 },
|
|
{ rtfFontAttr, rtfFTypeTrueType, "fttruetype", 0 },
|
|
|
|
/*
|
|
* File table attributes
|
|
*/
|
|
|
|
{ rtfFileAttr, rtfFileNum, "fid", 0 },
|
|
{ rtfFileAttr, rtfFileRelPath, "frelative", 0 },
|
|
{ rtfFileAttr, rtfFileOSNum, "fosnum", 0 },
|
|
|
|
/*
|
|
* File sources
|
|
*/
|
|
|
|
{ rtfFileSource, rtfSrcMacintosh, "fvalidmac", 0 },
|
|
{ rtfFileSource, rtfSrcDOS, "fvaliddos", 0 },
|
|
{ rtfFileSource, rtfSrcNTFS, "fvalidntfs", 0 },
|
|
{ rtfFileSource, rtfSrcHPFS, "fvalidhpfs", 0 },
|
|
{ rtfFileSource, rtfSrcNetwork, "fnetwork", 0 },
|
|
|
|
/*
|
|
* Color names
|
|
*/
|
|
|
|
{ rtfColorName, rtfRed, "red", 0 },
|
|
{ rtfColorName, rtfGreen, "green", 0 },
|
|
{ rtfColorName, rtfBlue, "blue", 0 },
|
|
|
|
/*
|
|
* Charset names
|
|
*/
|
|
|
|
{ rtfCharSet, rtfMacCharSet, "mac", 0 },
|
|
{ rtfCharSet, rtfAnsiCharSet, "ansi", 0 },
|
|
{ rtfCharSet, rtfPcCharSet, "pc", 0 },
|
|
{ rtfCharSet, rtfPcaCharSet, "pca", 0 },
|
|
|
|
/*
|
|
* Table attributes
|
|
*/
|
|
|
|
{ rtfTblAttr, rtfRowDef, "trowd", 0 },
|
|
{ rtfTblAttr, rtfRowGapH, "trgaph", 0 },
|
|
{ rtfTblAttr, rtfCellPos, "cellx", 0 },
|
|
{ rtfTblAttr, rtfMergeRngFirst, "clmgf", 0 },
|
|
{ rtfTblAttr, rtfMergePrevious, "clmrg", 0 },
|
|
|
|
{ rtfTblAttr, rtfRowLeft, "trql", 0 },
|
|
{ rtfTblAttr, rtfRowRight, "trqr", 0 },
|
|
{ rtfTblAttr, rtfRowCenter, "trqc", 0 },
|
|
{ rtfTblAttr, rtfRowLeftEdge, "trleft", 0 },
|
|
{ rtfTblAttr, rtfRowHt, "trrh", 0 },
|
|
{ rtfTblAttr, rtfRowHeader, "trhdr", 0 },
|
|
{ rtfTblAttr, rtfRowKeep, "trkeep", 0 },
|
|
|
|
{ rtfTblAttr, rtfRTLRow, "rtlrow", 0 },
|
|
{ rtfTblAttr, rtfLTRRow, "ltrrow", 0 },
|
|
|
|
{ rtfTblAttr, rtfRowBordTop, "trbrdrt", 0 },
|
|
{ rtfTblAttr, rtfRowBordLeft, "trbrdrl", 0 },
|
|
{ rtfTblAttr, rtfRowBordBottom, "trbrdrb", 0 },
|
|
{ rtfTblAttr, rtfRowBordRight, "trbrdrr", 0 },
|
|
{ rtfTblAttr, rtfRowBordHoriz, "trbrdrh", 0 },
|
|
{ rtfTblAttr, rtfRowBordVert, "trbrdrv", 0 },
|
|
|
|
{ rtfTblAttr, rtfCellBordBottom, "clbrdrb", 0 },
|
|
{ rtfTblAttr, rtfCellBordTop, "clbrdrt", 0 },
|
|
{ rtfTblAttr, rtfCellBordLeft, "clbrdrl", 0 },
|
|
{ rtfTblAttr, rtfCellBordRight, "clbrdrr", 0 },
|
|
|
|
{ rtfTblAttr, rtfCellShading, "clshdng", 0 },
|
|
{ rtfTblAttr, rtfCellBgPatH, "clbghoriz", 0 },
|
|
{ rtfTblAttr, rtfCellBgPatV, "clbgvert", 0 },
|
|
{ rtfTblAttr, rtfCellFwdDiagBgPat, "clbgfdiag", 0 },
|
|
{ rtfTblAttr, rtfCellBwdDiagBgPat, "clbgbdiag", 0 },
|
|
{ rtfTblAttr, rtfCellHatchBgPat, "clbgcross", 0 },
|
|
{ rtfTblAttr, rtfCellDiagHatchBgPat, "clbgdcross", 0 },
|
|
/*
|
|
* The spec lists "clbgdkhor", but the corresponding non-cell
|
|
* control is "bgdkhoriz". At any rate Macintosh Word seems
|
|
* to accept both "clbgdkhor" and "clbgdkhoriz".
|
|
*/
|
|
{ rtfTblAttr, rtfCellDarkBgPatH, "clbgdkhoriz", 0 },
|
|
{ rtfTblAttr, rtfCellDarkBgPatH, "clbgdkhor", 0 },
|
|
{ rtfTblAttr, rtfCellDarkBgPatV, "clbgdkvert", 0 },
|
|
{ rtfTblAttr, rtfCellFwdDarkBgPat, "clbgdkfdiag", 0 },
|
|
{ rtfTblAttr, rtfCellBwdDarkBgPat, "clbgdkbdiag", 0 },
|
|
{ rtfTblAttr, rtfCellDarkHatchBgPat, "clbgdkcross", 0 },
|
|
{ rtfTblAttr, rtfCellDarkDiagHatchBgPat, "clbgdkdcross", 0 },
|
|
{ rtfTblAttr, rtfCellBgPatLineColor, "clcfpat", 0 },
|
|
{ rtfTblAttr, rtfCellBgPatColor, "clcbpat", 0 },
|
|
|
|
/*
|
|
* Field attributes
|
|
*/
|
|
|
|
{ rtfFieldAttr, rtfFieldDirty, "flddirty", 0 },
|
|
{ rtfFieldAttr, rtfFieldEdited, "fldedit", 0 },
|
|
{ rtfFieldAttr, rtfFieldLocked, "fldlock", 0 },
|
|
{ rtfFieldAttr, rtfFieldPrivate, "fldpriv", 0 },
|
|
{ rtfFieldAttr, rtfFieldAlt, "fldalt", 0 },
|
|
|
|
/*
|
|
* Positioning attributes
|
|
*/
|
|
|
|
{ rtfPosAttr, rtfAbsWid, "absw", 0 },
|
|
{ rtfPosAttr, rtfAbsHt, "absh", 0 },
|
|
|
|
{ rtfPosAttr, rtfRPosMargH, "phmrg", 0 },
|
|
{ rtfPosAttr, rtfRPosPageH, "phpg", 0 },
|
|
{ rtfPosAttr, rtfRPosColH, "phcol", 0 },
|
|
{ rtfPosAttr, rtfPosX, "posx", 0 },
|
|
{ rtfPosAttr, rtfPosNegX, "posnegx", 0 },
|
|
{ rtfPosAttr, rtfPosXCenter, "posxc", 0 },
|
|
{ rtfPosAttr, rtfPosXInside, "posxi", 0 },
|
|
{ rtfPosAttr, rtfPosXOutSide, "posxo", 0 },
|
|
{ rtfPosAttr, rtfPosXRight, "posxr", 0 },
|
|
{ rtfPosAttr, rtfPosXLeft, "posxl", 0 },
|
|
|
|
{ rtfPosAttr, rtfRPosMargV, "pvmrg", 0 },
|
|
{ rtfPosAttr, rtfRPosPageV, "pvpg", 0 },
|
|
{ rtfPosAttr, rtfRPosParaV, "pvpara", 0 },
|
|
{ rtfPosAttr, rtfPosY, "posy", 0 },
|
|
{ rtfPosAttr, rtfPosNegY, "posnegy", 0 },
|
|
{ rtfPosAttr, rtfPosYInline, "posyil", 0 },
|
|
{ rtfPosAttr, rtfPosYTop, "posyt", 0 },
|
|
{ rtfPosAttr, rtfPosYCenter, "posyc", 0 },
|
|
{ rtfPosAttr, rtfPosYBottom, "posyb", 0 },
|
|
|
|
{ rtfPosAttr, rtfNoWrap, "nowrap", 0 },
|
|
{ rtfPosAttr, rtfDistFromTextAll, "dxfrtext", 0 },
|
|
{ rtfPosAttr, rtfDistFromTextX, "dfrmtxtx", 0 },
|
|
{ rtfPosAttr, rtfDistFromTextY, "dfrmtxty", 0 },
|
|
/* \dyfrtext no longer exists in spec 1.2, apparently */
|
|
/* replaced by \dfrmtextx and \dfrmtexty. */
|
|
{ rtfPosAttr, rtfTextDistY, "dyfrtext", 0 },
|
|
|
|
{ rtfPosAttr, rtfDropCapLines, "dropcapli", 0 },
|
|
{ rtfPosAttr, rtfDropCapType, "dropcapt", 0 },
|
|
|
|
/*
|
|
* Object controls
|
|
*/
|
|
|
|
{ rtfObjAttr, rtfObjEmb, "objemb", 0 },
|
|
{ rtfObjAttr, rtfObjLink, "objlink", 0 },
|
|
{ rtfObjAttr, rtfObjAutoLink, "objautlink", 0 },
|
|
{ rtfObjAttr, rtfObjSubscriber, "objsub", 0 },
|
|
{ rtfObjAttr, rtfObjPublisher, "objpub", 0 },
|
|
{ rtfObjAttr, rtfObjICEmb, "objicemb", 0 },
|
|
|
|
{ rtfObjAttr, rtfObjLinkSelf, "linkself", 0 },
|
|
{ rtfObjAttr, rtfObjLock, "objupdate", 0 },
|
|
{ rtfObjAttr, rtfObjUpdate, "objlock", 0 },
|
|
|
|
{ rtfObjAttr, rtfObjHt, "objh", 0 },
|
|
{ rtfObjAttr, rtfObjWid, "objw", 0 },
|
|
{ rtfObjAttr, rtfObjSetSize, "objsetsize", 0 },
|
|
{ rtfObjAttr, rtfObjAlign, "objalign", 0 },
|
|
{ rtfObjAttr, rtfObjTransposeY, "objtransy", 0 },
|
|
{ rtfObjAttr, rtfObjCropTop, "objcropt", 0 },
|
|
{ rtfObjAttr, rtfObjCropBottom, "objcropb", 0 },
|
|
{ rtfObjAttr, rtfObjCropLeft, "objcropl", 0 },
|
|
{ rtfObjAttr, rtfObjCropRight, "objcropr", 0 },
|
|
{ rtfObjAttr, rtfObjScaleX, "objscalex", 0 },
|
|
{ rtfObjAttr, rtfObjScaleY, "objscaley", 0 },
|
|
|
|
{ rtfObjAttr, rtfObjResRTF, "rsltrtf", 0 },
|
|
{ rtfObjAttr, rtfObjResPict, "rsltpict", 0 },
|
|
{ rtfObjAttr, rtfObjResBitmap, "rsltbmp", 0 },
|
|
{ rtfObjAttr, rtfObjResText, "rslttxt", 0 },
|
|
{ rtfObjAttr, rtfObjResMerge, "rsltmerge", 0 },
|
|
|
|
{ rtfObjAttr, rtfObjBookmarkPubObj, "bkmkpub", 0 },
|
|
{ rtfObjAttr, rtfObjPubAutoUpdate, "pubauto", 0 },
|
|
|
|
/*
|
|
* Associated character formatting attributes
|
|
*/
|
|
|
|
{ rtfACharAttr, rtfACBold, "ab", 0 },
|
|
{ rtfACharAttr, rtfACAllCaps, "caps", 0 },
|
|
{ rtfACharAttr, rtfACForeColor, "acf", 0 },
|
|
{ rtfACharAttr, rtfACSubScript, "adn", 0 },
|
|
{ rtfACharAttr, rtfACExpand, "aexpnd", 0 },
|
|
{ rtfACharAttr, rtfACFontNum, "af", 0 },
|
|
{ rtfACharAttr, rtfACFontSize, "afs", 0 },
|
|
{ rtfACharAttr, rtfACItalic, "ai", 0 },
|
|
{ rtfACharAttr, rtfACLanguage, "alang", 0 },
|
|
{ rtfACharAttr, rtfACOutline, "aoutl", 0 },
|
|
{ rtfACharAttr, rtfACSmallCaps, "ascaps", 0 },
|
|
{ rtfACharAttr, rtfACShadow, "ashad", 0 },
|
|
{ rtfACharAttr, rtfACStrikeThru, "astrike", 0 },
|
|
{ rtfACharAttr, rtfACUnderline, "aul", 0 },
|
|
{ rtfACharAttr, rtfACDotUnderline, "auld", 0 },
|
|
{ rtfACharAttr, rtfACDbUnderline, "auldb", 0 },
|
|
{ rtfACharAttr, rtfACNoUnderline, "aulnone", 0 },
|
|
{ rtfACharAttr, rtfACWordUnderline, "aulw", 0 },
|
|
{ rtfACharAttr, rtfACSuperScript, "aup", 0 },
|
|
|
|
/*
|
|
* Footnote attributes
|
|
*/
|
|
|
|
{ rtfFNoteAttr, rtfFNAlt, "ftnalt", 0 },
|
|
|
|
/*
|
|
* Key code attributes
|
|
*/
|
|
|
|
{ rtfKeyCodeAttr, rtfAltKey, "alt", 0 },
|
|
{ rtfKeyCodeAttr, rtfShiftKey, "shift", 0 },
|
|
{ rtfKeyCodeAttr, rtfControlKey, "ctrl", 0 },
|
|
{ rtfKeyCodeAttr, rtfFunctionKey, "fn", 0 },
|
|
|
|
/*
|
|
* Bookmark attributes
|
|
*/
|
|
|
|
{ rtfBookmarkAttr, rtfBookmarkFirstCol, "bkmkcolf", 0 },
|
|
{ rtfBookmarkAttr, rtfBookmarkLastCol, "bkmkcoll", 0 },
|
|
|
|
/*
|
|
* Index entry attributes
|
|
*/
|
|
|
|
{ rtfIndexAttr, rtfIndexNumber, "xef", 0 },
|
|
{ rtfIndexAttr, rtfIndexBold, "bxe", 0 },
|
|
{ rtfIndexAttr, rtfIndexItalic, "ixe", 0 },
|
|
|
|
/*
|
|
* Table of contents attributes
|
|
*/
|
|
|
|
{ rtfTOCAttr, rtfTOCType, "tcf", 0 },
|
|
{ rtfTOCAttr, rtfTOCLevel, "tcl", 0 },
|
|
|
|
/*
|
|
* Drawing object attributes
|
|
*/
|
|
|
|
{ rtfDrawAttr, rtfDrawLock, "dolock", 0 },
|
|
{ rtfDrawAttr, rtfDrawPageRelX, "doxpage", 0 },
|
|
{ rtfDrawAttr, rtfDrawColumnRelX, "dobxcolumn", 0 },
|
|
{ rtfDrawAttr, rtfDrawMarginRelX, "dobxmargin", 0 },
|
|
{ rtfDrawAttr, rtfDrawPageRelY, "dobypage", 0 },
|
|
{ rtfDrawAttr, rtfDrawColumnRelY, "dobycolumn", 0 },
|
|
{ rtfDrawAttr, rtfDrawMarginRelY, "dobymargin", 0 },
|
|
{ rtfDrawAttr, rtfDrawHeight, "dobhgt", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawBeginGroup, "dpgroup", 0 },
|
|
{ rtfDrawAttr, rtfDrawGroupCount, "dpcount", 0 },
|
|
{ rtfDrawAttr, rtfDrawEndGroup, "dpendgroup", 0 },
|
|
{ rtfDrawAttr, rtfDrawArc, "dparc", 0 },
|
|
{ rtfDrawAttr, rtfDrawCallout, "dpcallout", 0 },
|
|
{ rtfDrawAttr, rtfDrawEllipse, "dpellipse", 0 },
|
|
{ rtfDrawAttr, rtfDrawLine, "dpline", 0 },
|
|
{ rtfDrawAttr, rtfDrawPolygon, "dppolygon", 0 },
|
|
{ rtfDrawAttr, rtfDrawPolyLine, "dppolyline", 0 },
|
|
{ rtfDrawAttr, rtfDrawRect, "dprect", 0 },
|
|
{ rtfDrawAttr, rtfDrawTextBox, "dptxbx", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawOffsetX, "dpx", 0 },
|
|
{ rtfDrawAttr, rtfDrawSizeX, "dpxsize", 0 },
|
|
{ rtfDrawAttr, rtfDrawOffsetY, "dpy", 0 },
|
|
{ rtfDrawAttr, rtfDrawSizeY, "dpysize", 0 },
|
|
|
|
{ rtfDrawAttr, rtfCOAngle, "dpcoa", 0 },
|
|
{ rtfDrawAttr, rtfCOAccentBar, "dpcoaccent", 0 },
|
|
{ rtfDrawAttr, rtfCOBestFit, "dpcobestfit", 0 },
|
|
{ rtfDrawAttr, rtfCOBorder, "dpcoborder", 0 },
|
|
{ rtfDrawAttr, rtfCOAttachAbsDist, "dpcodabs", 0 },
|
|
{ rtfDrawAttr, rtfCOAttachBottom, "dpcodbottom", 0 },
|
|
{ rtfDrawAttr, rtfCOAttachCenter, "dpcodcenter", 0 },
|
|
{ rtfDrawAttr, rtfCOAttachTop, "dpcodtop", 0 },
|
|
{ rtfDrawAttr, rtfCOLength, "dpcolength", 0 },
|
|
{ rtfDrawAttr, rtfCONegXQuadrant, "dpcominusx", 0 },
|
|
{ rtfDrawAttr, rtfCONegYQuadrant, "dpcominusy", 0 },
|
|
{ rtfDrawAttr, rtfCOOffset, "dpcooffset", 0 },
|
|
{ rtfDrawAttr, rtfCOAttachSmart, "dpcosmarta", 0 },
|
|
{ rtfDrawAttr, rtfCODoubleLine, "dpcotdouble", 0 },
|
|
{ rtfDrawAttr, rtfCORightAngle, "dpcotright", 0 },
|
|
{ rtfDrawAttr, rtfCOSingleLine, "dpcotsingle", 0 },
|
|
{ rtfDrawAttr, rtfCOTripleLine, "dpcottriple", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawTextBoxMargin, "dptxbxmar", 0 },
|
|
{ rtfDrawAttr, rtfDrawTextBoxText, "dptxbxtext", 0 },
|
|
{ rtfDrawAttr, rtfDrawRoundRect, "dproundr", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawPointX, "dpptx", 0 },
|
|
{ rtfDrawAttr, rtfDrawPointY, "dppty", 0 },
|
|
{ rtfDrawAttr, rtfDrawPolyCount, "dppolycount", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawArcFlipX, "dparcflipx", 0 },
|
|
{ rtfDrawAttr, rtfDrawArcFlipY, "dparcflipy", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawLineBlue, "dplinecob", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineGreen, "dplinecog", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineRed, "dplinecor", 0 },
|
|
{ rtfDrawAttr, rtfDrawLinePalette, "dplinepal", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineDashDot, "dplinedado", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineDashDotDot, "dplinedadodo", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineDash, "dplinedash", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineDot, "dplinedot", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineGray, "dplinegray", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineHollow, "dplinehollow", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineSolid, "dplinesolid", 0 },
|
|
{ rtfDrawAttr, rtfDrawLineWidth, "dplinew", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawHollowEndArrow, "dpaendhol", 0 },
|
|
{ rtfDrawAttr, rtfDrawEndArrowLength, "dpaendl", 0 },
|
|
{ rtfDrawAttr, rtfDrawSolidEndArrow, "dpaendsol", 0 },
|
|
{ rtfDrawAttr, rtfDrawEndArrowWidth, "dpaendw", 0 },
|
|
{ rtfDrawAttr, rtfDrawHollowStartArrow,"dpastarthol", 0 },
|
|
{ rtfDrawAttr, rtfDrawStartArrowLength,"dpastartl", 0 },
|
|
{ rtfDrawAttr, rtfDrawSolidStartArrow, "dpastartsol", 0 },
|
|
{ rtfDrawAttr, rtfDrawStartArrowWidth, "dpastartw", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawBgFillBlue, "dpfillbgcb", 0 },
|
|
{ rtfDrawAttr, rtfDrawBgFillGreen, "dpfillbgcg", 0 },
|
|
{ rtfDrawAttr, rtfDrawBgFillRed, "dpfillbgcr", 0 },
|
|
{ rtfDrawAttr, rtfDrawBgFillPalette, "dpfillbgpal", 0 },
|
|
{ rtfDrawAttr, rtfDrawBgFillGray, "dpfillbggray", 0 },
|
|
{ rtfDrawAttr, rtfDrawFgFillBlue, "dpfillfgcb", 0 },
|
|
{ rtfDrawAttr, rtfDrawFgFillGreen, "dpfillfgcg", 0 },
|
|
{ rtfDrawAttr, rtfDrawFgFillRed, "dpfillfgcr", 0 },
|
|
{ rtfDrawAttr, rtfDrawFgFillPalette, "dpfillfgpal", 0 },
|
|
{ rtfDrawAttr, rtfDrawFgFillGray, "dpfillfggray", 0 },
|
|
{ rtfDrawAttr, rtfDrawFillPatIndex, "dpfillpat", 0 },
|
|
|
|
{ rtfDrawAttr, rtfDrawShadow, "dpshadow", 0 },
|
|
{ rtfDrawAttr, rtfDrawShadowXOffset, "dpshadx", 0 },
|
|
{ rtfDrawAttr, rtfDrawShadowYOffset, "dpshady", 0 },
|
|
|
|
{ rtfVersion, -1, "rtf", 0 },
|
|
{ rtfDefFont, -1, "deff", 0 },
|
|
|
|
{ 0, -1, NULL, 0 }
|
|
};
|
|
|
|
typedef struct tagRTFHashTableEntry {
|
|
int count;
|
|
RTFKey **value;
|
|
} RTFHashTableEntry;
|
|
|
|
static RTFHashTableEntry rtfHashTable[ARRAY_SIZE(rtfKey) * 2];
|
|
|
|
|
|
/*
|
|
* Initialize lookup table hash values. Only need to do this once.
|
|
*/
|
|
|
|
void LookupInit(void)
|
|
{
|
|
RTFKey *rp;
|
|
|
|
memset(rtfHashTable, 0, sizeof rtfHashTable);
|
|
for (rp = rtfKey; rp->rtfKStr != NULL; rp++)
|
|
{
|
|
int index;
|
|
|
|
rp->rtfKHash = Hash (rp->rtfKStr);
|
|
index = rp->rtfKHash % (ARRAY_SIZE(rtfKey) * 2);
|
|
rtfHashTable[index].value = realloc(rtfHashTable[index].value, sizeof(RTFKey *) * (rtfHashTable[index].count + 1));
|
|
rtfHashTable[index].value[rtfHashTable[index].count++] = rp;
|
|
}
|
|
}
|
|
|
|
void LookupCleanup(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(rtfKey) * 2; i++)
|
|
{
|
|
free(rtfHashTable[i].value);
|
|
rtfHashTable[i].value = NULL;
|
|
rtfHashTable[i].count = 0;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* Determine major and minor number of control token. If it's
|
|
* not found, the class turns into rtfUnknown.
|
|
*/
|
|
|
|
static void Lookup(RTF_Info *info, char *s)
|
|
{
|
|
RTFKey *rp;
|
|
int hash;
|
|
RTFHashTableEntry *entry;
|
|
int i;
|
|
|
|
++s; /* skip over the leading \ character */
|
|
hash = Hash (s);
|
|
entry = &rtfHashTable[hash % (ARRAY_SIZE(rtfKey) * 2)];
|
|
for (i = 0; i < entry->count; i++)
|
|
{
|
|
rp = entry->value[i];
|
|
if (hash == rp->rtfKHash && strcmp (s, rp->rtfKStr) == 0)
|
|
{
|
|
info->rtfClass = rtfControl;
|
|
info->rtfMajor = rp->rtfKMajor;
|
|
info->rtfMinor = rp->rtfKMinor;
|
|
return;
|
|
}
|
|
}
|
|
info->rtfClass = rtfUnknown;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute hash value of symbol
|
|
*/
|
|
|
|
static int Hash(const char *s)
|
|
{
|
|
char c;
|
|
int val = 0;
|
|
|
|
while ((c = *s++) != '\0')
|
|
val += c;
|
|
return (val);
|
|
}
|
|
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
/*
|
|
* Token comparison routines
|
|
*/
|
|
|
|
int RTFCheckCM(const RTF_Info *info, int class, int major)
|
|
{
|
|
return (info->rtfClass == class && info->rtfMajor == major);
|
|
}
|
|
|
|
|
|
int RTFCheckCMM(const RTF_Info *info, int class, int major, int minor)
|
|
{
|
|
return (info->rtfClass == class && info->rtfMajor == major && info->rtfMinor == minor);
|
|
}
|
|
|
|
|
|
int RTFCheckMM(const RTF_Info *info, int major, int minor)
|
|
{
|
|
return (info->rtfMajor == major && info->rtfMinor == minor);
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
|
|
int RTFCharToHex(char c)
|
|
{
|
|
if (isupper (c))
|
|
c = tolower (c);
|
|
if (isdigit (c))
|
|
return (c - '0'); /* '0'..'9' */
|
|
return (c - 'a' + 10); /* 'a'..'f' */
|
|
}
|
|
|
|
|
|
/* ---------------------------------------------------------------------- */
|
|
|
|
/*
|
|
* originally from RTF tools' text-writer.c
|
|
*
|
|
* text-writer -- RTF-to-text translation writer code.
|
|
*
|
|
* Read RTF input, write text of document (text extraction).
|
|
*/
|
|
|
|
static void TextClass (RTF_Info *info);
|
|
static void ControlClass (RTF_Info *info);
|
|
static void DefFont(RTF_Info *info);
|
|
static void Destination (RTF_Info *info);
|
|
static void SpecialChar (RTF_Info *info);
|
|
static void RTFPutUnicodeChar (RTF_Info *info, int c);
|
|
|
|
/*
|
|
* Initialize the writer.
|
|
*/
|
|
|
|
void
|
|
WriterInit (RTF_Info *info )
|
|
{
|
|
}
|
|
|
|
|
|
int
|
|
BeginFile (RTF_Info *info )
|
|
{
|
|
/* install class callbacks */
|
|
|
|
RTFSetClassCallback (info, rtfText, TextClass);
|
|
RTFSetClassCallback (info, rtfControl, ControlClass);
|
|
|
|
return (1);
|
|
}
|
|
|
|
/*
|
|
* Write out a character.
|
|
*/
|
|
|
|
static void
|
|
TextClass (RTF_Info *info)
|
|
{
|
|
RTFPutCodePageChar(info, info->rtfMajor);
|
|
}
|
|
|
|
|
|
static void
|
|
ControlClass (RTF_Info *info)
|
|
{
|
|
switch (info->rtfMajor)
|
|
{
|
|
case rtfCharAttr:
|
|
CharAttr(info);
|
|
ME_RTFCharAttrHook(info);
|
|
break;
|
|
case rtfParAttr:
|
|
ME_RTFParAttrHook(info);
|
|
break;
|
|
case rtfTblAttr:
|
|
ME_RTFTblAttrHook(info);
|
|
break;
|
|
case rtfCharSet:
|
|
CharSet(info);
|
|
break;
|
|
case rtfDefFont:
|
|
DefFont(info);
|
|
break;
|
|
case rtfDestination:
|
|
Destination (info);
|
|
break;
|
|
case rtfDocAttr:
|
|
DocAttr(info);
|
|
break;
|
|
case rtfSpecialChar:
|
|
SpecialChar (info);
|
|
ME_RTFSpecialCharHook(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)
|
|
{
|
|
RTFFlushOutputBuffer(info);
|
|
info->codePage = font->rtfFCodePage;
|
|
}
|
|
TRACE("font %d codepage %d\n", info->rtfParam, info->codePage);
|
|
}
|
|
else
|
|
ERR( "unknown font %d\n", info->rtfParam);
|
|
break;
|
|
case rtfUnicodeLength:
|
|
info->unicodeLength = info->rtfParam;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
CharSet(RTF_Info *info)
|
|
{
|
|
if (info->ansiCodePage == CP_UTF8)
|
|
return;
|
|
|
|
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 aren't explicitly handled
|
|
* and skips to their ends. This keeps, for instance, picture
|
|
* data from being considered as plain text.
|
|
*/
|
|
|
|
static void
|
|
Destination (RTF_Info *info)
|
|
{
|
|
if (!RTFGetDestinationCallback(info, info->rtfMinor))
|
|
RTFSkipGroup (info);
|
|
}
|
|
|
|
|
|
static void
|
|
DefFont(RTF_Info *info)
|
|
{
|
|
TRACE("%d\n", info->rtfParam);
|
|
info->defFont = info->rtfParam;
|
|
}
|
|
|
|
|
|
static void
|
|
DocAttr(RTF_Info *info)
|
|
{
|
|
TRACE("minor %d, param %d\n", info->rtfMinor, info->rtfParam);
|
|
|
|
switch (info->rtfMinor)
|
|
{
|
|
case rtfAnsiCodePage:
|
|
info->codePage = info->ansiCodePage = info->rtfParam;
|
|
break;
|
|
case rtfUTF8RTF:
|
|
info->codePage = info->ansiCodePage = CP_UTF8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void SpecialChar (RTF_Info *info)
|
|
{
|
|
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 rtfLine:
|
|
RTFFlushOutputBuffer(info);
|
|
ME_InsertEndRowFromCursor(info->editor, 0);
|
|
break;
|
|
case rtfPage:
|
|
case rtfSect:
|
|
case rtfPar:
|
|
RTFFlushOutputBuffer(info);
|
|
editor_set_selection_para_fmt( info->editor, &info->fmt );
|
|
memset(&info->fmt, 0, sizeof(info->fmt));
|
|
info->fmt.cbSize = sizeof(info->fmt);
|
|
RTFPutUnicodeChar (info, '\r');
|
|
if (info->editor->bEmulateVersion10) RTFPutUnicodeChar (info, '\n');
|
|
break;
|
|
case rtfNoBrkSpace:
|
|
RTFPutUnicodeChar (info, 0x00A0);
|
|
break;
|
|
case rtfTab:
|
|
RTFPutUnicodeChar (info, '\t');
|
|
break;
|
|
case rtfNoBrkHyphen:
|
|
RTFPutUnicodeChar (info, 0x2011);
|
|
break;
|
|
case rtfBullet:
|
|
RTFPutUnicodeChar (info, 0x2022);
|
|
break;
|
|
case rtfEmDash:
|
|
RTFPutUnicodeChar (info, 0x2014);
|
|
break;
|
|
case rtfEnDash:
|
|
RTFPutUnicodeChar (info, 0x2013);
|
|
break;
|
|
case rtfEmSpace:
|
|
RTFPutUnicodeChar (info, ' ');
|
|
break;
|
|
case rtfEnSpace:
|
|
RTFPutUnicodeChar (info, ' ');
|
|
break;
|
|
case rtfLQuote:
|
|
RTFPutUnicodeChar (info, 0x2018);
|
|
break;
|
|
case rtfRQuote:
|
|
RTFPutUnicodeChar (info, 0x2019);
|
|
break;
|
|
case rtfLDblQuote:
|
|
RTFPutUnicodeChar (info, 0x201C);
|
|
break;
|
|
case rtfRDblQuote:
|
|
RTFPutUnicodeChar (info, 0x201D);
|
|
break;
|
|
case rtfLTRMark:
|
|
RTFPutUnicodeChar (info, 0x200E);
|
|
break;
|
|
case rtfRTLMark:
|
|
RTFPutUnicodeChar (info, 0x200F);
|
|
break;
|
|
case rtfNoWidthJoiner:
|
|
RTFPutUnicodeChar (info, 0x200D);
|
|
break;
|
|
case rtfNoWidthNonJoiner:
|
|
RTFPutUnicodeChar (info, 0x200C);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
RTFFlushUnicodeOutputBuffer(RTF_Info *info)
|
|
{
|
|
if (info->dwOutputCount)
|
|
{
|
|
ME_InsertTextFromCursor(info->editor, 0, info->OutputBuffer,
|
|
info->dwOutputCount, info->style);
|
|
info->dwOutputCount = 0;
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
RTFPutUnicodeString(RTF_Info *info, const WCHAR *string, int length)
|
|
{
|
|
if (info->dwCPOutputCount)
|
|
RTFFlushCPOutputBuffer(info);
|
|
while (length)
|
|
{
|
|
int fit = min(length, ARRAY_SIZE(info->OutputBuffer) - info->dwOutputCount);
|
|
|
|
memmove(info->OutputBuffer + info->dwOutputCount, string, fit * sizeof(WCHAR));
|
|
info->dwOutputCount += fit;
|
|
length -= fit;
|
|
string += fit;
|
|
if (ARRAY_SIZE(info->OutputBuffer) == info->dwOutputCount)
|
|
RTFFlushUnicodeOutputBuffer(info);
|
|
}
|
|
}
|
|
|
|
static void
|
|
RTFFlushCPOutputBuffer(RTF_Info *info)
|
|
{
|
|
int bufferMax = info->dwCPOutputCount * 2 * sizeof(WCHAR);
|
|
WCHAR *buffer = malloc(bufferMax);
|
|
int length;
|
|
|
|
length = MultiByteToWideChar(info->codePage, 0, info->cpOutputBuffer,
|
|
info->dwCPOutputCount, buffer, bufferMax/sizeof(WCHAR));
|
|
info->dwCPOutputCount = 0;
|
|
|
|
RTFPutUnicodeString(info, buffer, length);
|
|
free(buffer);
|
|
}
|
|
|
|
void
|
|
RTFFlushOutputBuffer(RTF_Info *info)
|
|
{
|
|
if (info->dwCPOutputCount)
|
|
RTFFlushCPOutputBuffer(info);
|
|
RTFFlushUnicodeOutputBuffer(info);
|
|
}
|
|
|
|
static void
|
|
RTFPutUnicodeChar(RTF_Info *info, int c)
|
|
{
|
|
if (info->dwCPOutputCount)
|
|
RTFFlushCPOutputBuffer(info);
|
|
if (info->dwOutputCount * sizeof(WCHAR) >= ( sizeof info->OutputBuffer - 1 ) )
|
|
RTFFlushUnicodeOutputBuffer( info );
|
|
info->OutputBuffer[info->dwOutputCount++] = c;
|
|
}
|
|
|
|
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 = realloc(info->cpOutputBuffer, info->dwMaxCPOutputCount);
|
|
}
|
|
info->cpOutputBuffer[info->dwCPOutputCount++] = c;
|
|
}
|