diff options
Diffstat (limited to 'writerfilter/source/rtftok')
30 files changed, 16436 insertions, 0 deletions
diff --git a/writerfilter/source/rtftok/README b/writerfilter/source/rtftok/README new file mode 100644 index 000000000..4adbb7563 --- /dev/null +++ b/writerfilter/source/rtftok/README @@ -0,0 +1,12 @@ += Writerfilter-based RTF tokenizer + +== Mathematics + +At the time of writing, all control words understood by SmOoxmlImport are +imported. To view the current status: + +---- +grep M_TOKEN starmath/source/ooxmlimport.cxx |sed 's/.*\(M_TOKEN(\) /\1/;s/ ).*/)/'|sort -u > ~/math-import-list +grep '[^_]M_TOKEN' writerfilter/source/rtftok/rtfdocumentimpl.cxx |sed 's/.*\(M_TOKEN(\)/\1/;s/).*/)/'|sort -u > ~/wf-export-list +diff -u ~/math-import-list ~/wf-export-list |grep ^-M_TOKEN +---- diff --git a/writerfilter/source/rtftok/rtfcharsets.cxx b/writerfilter/source/rtftok/rtfcharsets.cxx new file mode 100644 index 000000000..14d27b5f2 --- /dev/null +++ b/writerfilter/source/rtftok/rtfcharsets.cxx @@ -0,0 +1,64 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfcharsets.hxx" +#include <array> +#include <rtl/textenc.h> + +namespace writerfilter::rtftok +{ +// See RTF spec v1.9.1, page 19 +RTFEncoding const aRTFEncodings[] = { + // charset codepage Windows / Mac name + { 0, 1252 }, // ANSI + { 1, 0 }, // Default + { 2, 42 }, // Symbol + { 77, 10000 }, // Mac Roman + { 78, 10001 }, // Mac Shift Jis + { 79, 10003 }, // Mac Hangul + { 80, 10008 }, // Mac GB2312 + { 81, 10002 }, // Mac Big5 + { 83, 10005 }, // Mac Herbrew + { 84, 10004 }, // Mac Arabic + { 85, 10006 }, // Mac Greek + { 86, 10081 }, // Mac Turkish + { 87, 10021 }, // Mac Thai + { 88, 10029 }, // Mac East Europe + { 89, 10007 }, // Mac Russian + { 128, 932 }, // Shift JIS + { 129, 949 }, // Hangul + { 130, 1361 }, // Johab + { 134, 936 }, // GB2312 + { 136, 950 }, // Big5 + { 161, 1253 }, // Greek + { 162, 1254 }, // Turkish + { 163, 1258 }, // Vietnamese + { 177, 1255 }, // Herbrew + { 178, 1256 }, // Arabic + { 186, 1257 }, // Baltic + { 204, 1251 }, // Russian + { 222, 874 }, // Thai + { 238, 1250 }, // Eastern European + { 254, 437 }, // PC 437 + { 255, 850 }, // OEM +}; + +int nRTFEncodings = std::size(aRTFEncodings); + +RTFFontNameSuffix const aRTFFontNameSuffixes[] = { + { "Baltic", RTL_TEXTENCODING_MS_1257 }, { "CE", RTL_TEXTENCODING_MS_1250 }, + { "Cyr", RTL_TEXTENCODING_MS_1251 }, { "Greek", RTL_TEXTENCODING_MS_1253 }, + { "Tur", RTL_TEXTENCODING_MS_1254 }, { "(Hebrew)", RTL_TEXTENCODING_MS_1255 }, + { "(Arabic)", RTL_TEXTENCODING_MS_1256 }, { "(Vietnamese)", RTL_TEXTENCODING_MS_1258 }, + { "", RTL_TEXTENCODING_DONTKNOW } // End of array +}; + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfcharsets.hxx b/writerfilter/source/rtftok/rtfcharsets.hxx new file mode 100644 index 000000000..826dea271 --- /dev/null +++ b/writerfilter/source/rtftok/rtfcharsets.hxx @@ -0,0 +1,37 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace writerfilter::rtftok +{ +/// RTF legacy charsets +struct RTFEncoding +{ + int charset; + int codepage; +}; +extern RTFEncoding const aRTFEncodings[]; +extern int nRTFEncodings; + +/// Font name can contain special suffixes used +/// to determine encoding for given font table entry +/// For example "Arial CE" is "Arial" with CP1250 encoding +/// List of these suffixes is not official and detected in a empirical +/// way thus may be inexact and incomplete. +struct RTFFontNameSuffix +{ + const char* suffix; + int codepage; +}; +extern RTFFontNameSuffix const aRTFFontNameSuffixes[]; + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfcontrolwords.cxx b/writerfilter/source/rtftok/rtfcontrolwords.cxx new file mode 100644 index 000000000..3f5a0827b --- /dev/null +++ b/writerfilter/source/rtftok/rtfcontrolwords.cxx @@ -0,0 +1,1900 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfcontrolwords.hxx" +#include <oox/token/namespaces.hxx> + +namespace writerfilter::rtftok +{ +RTFSymbol const aRTFControlWords[] = { + // sKeyword nControlType nIndex + { "'", RTFControlType::SYMBOL, RTFKeyword::HEXCHAR, 0 }, + { "-", RTFControlType::SYMBOL, RTFKeyword::OPTHYPH, 0 }, + { "*", RTFControlType::SYMBOL, RTFKeyword::IGNORE, 0 }, + { ":", RTFControlType::SYMBOL, RTFKeyword::SUBENTRY, 0 }, + { "\\", RTFControlType::SYMBOL, RTFKeyword::BACKSLASH, 0 }, + { "\n", RTFControlType::SYMBOL, RTFKeyword::PAR, 0 }, + { "\r", RTFControlType::SYMBOL, RTFKeyword::PAR, 0 }, + { "\r\n", RTFControlType::SYMBOL, RTFKeyword::PAR, 0 }, + { "_", RTFControlType::SYMBOL, RTFKeyword::NOBRKHYPH, 0 }, + { "{", RTFControlType::SYMBOL, RTFKeyword::LBRACE, 0 }, + { "|", RTFControlType::SYMBOL, RTFKeyword::FORMULA, 0 }, + { "}", RTFControlType::SYMBOL, RTFKeyword::RBRACE, 0 }, + { "~", RTFControlType::SYMBOL, RTFKeyword::NOBREAK, 0 }, + { "ab", RTFControlType::TOGGLE, RTFKeyword::AB, 1 }, + { "absh", RTFControlType::VALUE, RTFKeyword::ABSH, 0 }, + { "abslock", RTFControlType::FLAG, RTFKeyword::ABSLOCK, 0 }, + { "absnoovrlp", RTFControlType::TOGGLE, RTFKeyword::ABSNOOVRLP, 1 }, + { "absw", RTFControlType::VALUE, RTFKeyword::ABSW, 0 }, + { "acaps", RTFControlType::TOGGLE, RTFKeyword::ACAPS, 1 }, + { "acccircle", RTFControlType::TOGGLE, RTFKeyword::ACCCIRCLE, 1 }, + { "acccomma", RTFControlType::TOGGLE, RTFKeyword::ACCCOMMA, 1 }, + { "accdot", RTFControlType::TOGGLE, RTFKeyword::ACCDOT, 1 }, + { "accnone", RTFControlType::TOGGLE, RTFKeyword::ACCNONE, 1 }, + { "accunderdot", RTFControlType::TOGGLE, RTFKeyword::ACCUNDERDOT, 1 }, + { "acf", RTFControlType::VALUE, RTFKeyword::ACF, 0 }, + { "adeff", RTFControlType::VALUE, RTFKeyword::ADEFF, 0 }, + { "additive", RTFControlType::FLAG, RTFKeyword::ADDITIVE, 0 }, + { "adeflang", RTFControlType::VALUE, RTFKeyword::ADEFLANG, 0 }, + { "adjustright", RTFControlType::FLAG, RTFKeyword::ADJUSTRIGHT, 0 }, + { "adn", RTFControlType::VALUE, RTFKeyword::ADN, 6 }, + { "aenddoc", RTFControlType::FLAG, RTFKeyword::AENDDOC, 0 }, + { "aendnotes", RTFControlType::FLAG, RTFKeyword::AENDNOTES, 0 }, + { "aexpnd", RTFControlType::VALUE, RTFKeyword::AEXPND, 0 }, + { "af", RTFControlType::VALUE, RTFKeyword::AF, 0 }, + { "afelev", RTFControlType::FLAG, RTFKeyword::AFELEV, 0 }, + { "afs", RTFControlType::VALUE, RTFKeyword::AFS, 24 }, + { "aftnbj", RTFControlType::FLAG, RTFKeyword::AFTNBJ, 0 }, + { "aftncn", RTFControlType::DESTINATION, RTFKeyword::AFTNCN, 0 }, + { "aftnnalc", RTFControlType::FLAG, RTFKeyword::AFTNNALC, 0 }, + { "aftnnar", RTFControlType::FLAG, RTFKeyword::AFTNNAR, 0 }, + { "aftnnauc", RTFControlType::FLAG, RTFKeyword::AFTNNAUC, 0 }, + { "aftnnchi", RTFControlType::FLAG, RTFKeyword::AFTNNCHI, 0 }, + { "aftnnchosung", RTFControlType::FLAG, RTFKeyword::AFTNNCHOSUNG, 0 }, + { "aftnncnum", RTFControlType::FLAG, RTFKeyword::AFTNNCNUM, 0 }, + { "aftnndbar", RTFControlType::FLAG, RTFKeyword::AFTNNDBAR, 0 }, + { "aftnndbnum", RTFControlType::FLAG, RTFKeyword::AFTNNDBNUM, 0 }, + { "aftnndbnumd", RTFControlType::FLAG, RTFKeyword::AFTNNDBNUMD, 0 }, + { "aftnndbnumk", RTFControlType::FLAG, RTFKeyword::AFTNNDBNUMK, 0 }, + { "aftnndbnumt", RTFControlType::FLAG, RTFKeyword::AFTNNDBNUMT, 0 }, + { "aftnnganada", RTFControlType::FLAG, RTFKeyword::AFTNNGANADA, 0 }, + { "aftnngbnum", RTFControlType::FLAG, RTFKeyword::AFTNNGBNUM, 0 }, + { "aftnngbnumd", RTFControlType::FLAG, RTFKeyword::AFTNNGBNUMD, 0 }, + { "aftnngbnumk", RTFControlType::FLAG, RTFKeyword::AFTNNGBNUMK, 0 }, + { "aftnngbnuml", RTFControlType::FLAG, RTFKeyword::AFTNNGBNUML, 0 }, + { "aftnnrlc", RTFControlType::FLAG, RTFKeyword::AFTNNRLC, 0 }, + { "aftnnruc", RTFControlType::FLAG, RTFKeyword::AFTNNRUC, 0 }, + { "aftnnzodiac", RTFControlType::FLAG, RTFKeyword::AFTNNZODIAC, 0 }, + { "aftnnzodiacd", RTFControlType::FLAG, RTFKeyword::AFTNNZODIACD, 0 }, + { "aftnnzodiacl", RTFControlType::FLAG, RTFKeyword::AFTNNZODIACL, 0 }, + { "aftnrestart", RTFControlType::FLAG, RTFKeyword::AFTNRESTART, 0 }, + { "aftnrstcont", RTFControlType::FLAG, RTFKeyword::AFTNRSTCONT, 0 }, + { "aftnsep", RTFControlType::DESTINATION, RTFKeyword::AFTNSEP, 0 }, + { "aftnsepc", RTFControlType::DESTINATION, RTFKeyword::AFTNSEPC, 0 }, + { "aftnstart", RTFControlType::VALUE, RTFKeyword::AFTNSTART, 1 }, + { "aftntj", RTFControlType::FLAG, RTFKeyword::AFTNTJ, 0 }, + { "ai", RTFControlType::TOGGLE, RTFKeyword::AI, 1 }, + { "alang", RTFControlType::VALUE, RTFKeyword::ALANG, 0 }, + { "allowfieldendsel", RTFControlType::FLAG, RTFKeyword::ALLOWFIELDENDSEL, 0 }, + { "allprot", RTFControlType::FLAG, RTFKeyword::ALLPROT, 0 }, + { "alntblind", RTFControlType::FLAG, RTFKeyword::ALNTBLIND, 0 }, + { "alt", RTFControlType::FLAG, RTFKeyword::ALT, 0 }, + { "animtext", RTFControlType::VALUE, RTFKeyword::ANIMTEXT, 0 }, + { "annotation", RTFControlType::DESTINATION, RTFKeyword::ANNOTATION, 0 }, + { "annotprot", RTFControlType::FLAG, RTFKeyword::ANNOTPROT, 0 }, + { "ansi", RTFControlType::FLAG, RTFKeyword::ANSI, 0 }, + { "ansicpg", RTFControlType::VALUE, RTFKeyword::ANSICPG, 0 }, + { "aoutl", RTFControlType::TOGGLE, RTFKeyword::AOUTL, 1 }, + { "ApplyBrkRules", RTFControlType::FLAG, RTFKeyword::APPLYBRKRULES, 0 }, + { "ascaps", RTFControlType::TOGGLE, RTFKeyword::ASCAPS, 1 }, + { "ashad", RTFControlType::TOGGLE, RTFKeyword::ASHAD, 1 }, + { "asianbrkrule", RTFControlType::FLAG, RTFKeyword::ASIANBRKRULE, 0 }, + { "aspalpha", RTFControlType::TOGGLE, RTFKeyword::ASPALPHA, 1 }, + { "aspnum", RTFControlType::TOGGLE, RTFKeyword::ASPNUM, 1 }, + { "astrike", RTFControlType::TOGGLE, RTFKeyword::ASTRIKE, 1 }, + { "atnauthor", RTFControlType::DESTINATION, RTFKeyword::ATNAUTHOR, 0 }, + { "atndate", RTFControlType::DESTINATION, RTFKeyword::ATNDATE, 0 }, + { "atnicn", RTFControlType::DESTINATION, RTFKeyword::ATNICN, 0 }, + { "atnid", RTFControlType::DESTINATION, RTFKeyword::ATNID, 0 }, + { "atnparent", RTFControlType::DESTINATION, RTFKeyword::ATNPARENT, 0 }, + { "atnref", RTFControlType::DESTINATION, RTFKeyword::ATNREF, 0 }, + { "atntime", RTFControlType::DESTINATION, RTFKeyword::ATNTIME, 0 }, + { "atrfend", RTFControlType::DESTINATION, RTFKeyword::ATRFEND, 0 }, + { "atrfstart", RTFControlType::DESTINATION, RTFKeyword::ATRFSTART, 0 }, + { "aul", RTFControlType::TOGGLE, RTFKeyword::AUL, 1 }, + { "auld", RTFControlType::TOGGLE, RTFKeyword::AULD, 1 }, + { "auldb", RTFControlType::TOGGLE, RTFKeyword::AULDB, 1 }, + { "aulnone", RTFControlType::TOGGLE, RTFKeyword::AULNONE, 1 }, + { "aulw", RTFControlType::TOGGLE, RTFKeyword::AULW, 1 }, + { "aup", RTFControlType::VALUE, RTFKeyword::AUP, 6 }, + { "author", RTFControlType::DESTINATION, RTFKeyword::AUTHOR, 0 }, + { "autofmtoverride", RTFControlType::FLAG, RTFKeyword::AUTOFMTOVERRIDE, 0 }, + { "b", RTFControlType::TOGGLE, RTFKeyword::B, 1 }, + { "background", RTFControlType::DESTINATION, RTFKeyword::BACKGROUND, 0 }, + { "bdbfhdr", RTFControlType::FLAG, RTFKeyword::BDBFHDR, 0 }, + { "bdrrlswsix", RTFControlType::FLAG, RTFKeyword::BDRRLSWSIX, 0 }, + { "bgbdiag", RTFControlType::FLAG, RTFKeyword::BGBDIAG, 0 }, + { "bgcross", RTFControlType::FLAG, RTFKeyword::BGCROSS, 0 }, + { "bgdcross", RTFControlType::FLAG, RTFKeyword::BGDCROSS, 0 }, + { "bgdkbdiag", RTFControlType::FLAG, RTFKeyword::BGDKBDIAG, 0 }, + { "bgdkcross", RTFControlType::FLAG, RTFKeyword::BGDKCROSS, 0 }, + { "bgdkdcross", RTFControlType::FLAG, RTFKeyword::BGDKDCROSS, 0 }, + { "bgdkfdiag", RTFControlType::FLAG, RTFKeyword::BGDKFDIAG, 0 }, + { "bgdkhoriz", RTFControlType::FLAG, RTFKeyword::BGDKHORIZ, 0 }, + { "bgdkvert", RTFControlType::FLAG, RTFKeyword::BGDKVERT, 0 }, + { "bgfdiag", RTFControlType::FLAG, RTFKeyword::BGFDIAG, 0 }, + { "bghoriz", RTFControlType::FLAG, RTFKeyword::BGHORIZ, 0 }, + { "bgvert", RTFControlType::FLAG, RTFKeyword::BGVERT, 0 }, + { "bin", RTFControlType::VALUE, RTFKeyword::BIN, 0 }, + { "binfsxn", RTFControlType::VALUE, RTFKeyword::BINFSXN, 0 }, + { "binsxn", RTFControlType::VALUE, RTFKeyword::BINSXN, 0 }, + { "bkmkcolf", RTFControlType::VALUE, RTFKeyword::BKMKCOLF, 0 }, + { "bkmkcoll", RTFControlType::VALUE, RTFKeyword::BKMKCOLL, 0 }, + { "bkmkend", RTFControlType::DESTINATION, RTFKeyword::BKMKEND, 0 }, + { "bkmkpub", RTFControlType::FLAG, RTFKeyword::BKMKPUB, 0 }, + { "bkmkstart", RTFControlType::DESTINATION, RTFKeyword::BKMKSTART, 0 }, + { "bliptag", RTFControlType::VALUE, RTFKeyword::BLIPTAG, 0 }, + { "blipuid", RTFControlType::DESTINATION, RTFKeyword::BLIPUID, 0 }, + { "blipupi", RTFControlType::VALUE, RTFKeyword::BLIPUPI, 0 }, + { "blue", RTFControlType::VALUE, RTFKeyword::BLUE, 0 }, + { "bookfold", RTFControlType::FLAG, RTFKeyword::BOOKFOLD, 0 }, + { "bookfoldrev", RTFControlType::FLAG, RTFKeyword::BOOKFOLDREV, 0 }, + { "bookfoldsheets", RTFControlType::VALUE, RTFKeyword::BOOKFOLDSHEETS, 0 }, + { "box", RTFControlType::FLAG, RTFKeyword::BOX, 0 }, + { "brdrart", RTFControlType::VALUE, RTFKeyword::BRDRART, 0 }, + { "brdrb", RTFControlType::FLAG, RTFKeyword::BRDRB, 0 }, + { "brdrbar", RTFControlType::FLAG, RTFKeyword::BRDRBAR, 0 }, + { "brdrbtw", RTFControlType::FLAG, RTFKeyword::BRDRBTW, 0 }, + { "brdrcf", RTFControlType::VALUE, RTFKeyword::BRDRCF, 0 }, + { "brdrdash", RTFControlType::FLAG, RTFKeyword::BRDRDASH, 0 }, + { "brdrdashd", RTFControlType::FLAG, RTFKeyword::BRDRDASHD, 0 }, + { "brdrdashdd", RTFControlType::FLAG, RTFKeyword::BRDRDASHDD, 0 }, + { "brdrdashdotstr", RTFControlType::FLAG, RTFKeyword::BRDRDASHDOTSTR, 0 }, + { "brdrdashsm", RTFControlType::FLAG, RTFKeyword::BRDRDASHSM, 0 }, + { "brdrdb", RTFControlType::FLAG, RTFKeyword::BRDRDB, 0 }, + { "brdrdot", RTFControlType::FLAG, RTFKeyword::BRDRDOT, 0 }, + { "brdremboss", RTFControlType::FLAG, RTFKeyword::BRDREMBOSS, 0 }, + { "brdrengrave", RTFControlType::FLAG, RTFKeyword::BRDRENGRAVE, 0 }, + { "brdrframe", RTFControlType::FLAG, RTFKeyword::BRDRFRAME, 0 }, + { "brdrhair", RTFControlType::FLAG, RTFKeyword::BRDRHAIR, 0 }, + { "brdrinset", RTFControlType::FLAG, RTFKeyword::BRDRINSET, 0 }, + { "brdrl", RTFControlType::FLAG, RTFKeyword::BRDRL, 0 }, + { "brdrnil", RTFControlType::FLAG, RTFKeyword::BRDRNIL, 0 }, + { "brdrnone", RTFControlType::FLAG, RTFKeyword::BRDRNONE, 0 }, + { "brdroutset", RTFControlType::FLAG, RTFKeyword::BRDROUTSET, 0 }, + { "brdrr", RTFControlType::FLAG, RTFKeyword::BRDRR, 0 }, + { "brdrs", RTFControlType::FLAG, RTFKeyword::BRDRS, 0 }, + { "brdrsh", RTFControlType::FLAG, RTFKeyword::BRDRSH, 0 }, + { "brdrt", RTFControlType::FLAG, RTFKeyword::BRDRT, 0 }, + { "brdrtbl", RTFControlType::FLAG, RTFKeyword::BRDRTBL, 0 }, + { "brdrth", RTFControlType::FLAG, RTFKeyword::BRDRTH, 0 }, + { "brdrthtnlg", RTFControlType::FLAG, RTFKeyword::BRDRTHTNLG, 0 }, + { "brdrthtnmg", RTFControlType::FLAG, RTFKeyword::BRDRTHTNMG, 0 }, + { "brdrthtnsg", RTFControlType::FLAG, RTFKeyword::BRDRTHTNSG, 0 }, + { "brdrtnthlg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHLG, 0 }, + { "brdrtnthmg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHMG, 0 }, + { "brdrtnthsg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHSG, 0 }, + { "brdrtnthtnlg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHTNLG, 0 }, + { "brdrtnthtnmg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHTNMG, 0 }, + { "brdrtnthtnsg", RTFControlType::FLAG, RTFKeyword::BRDRTNTHTNSG, 0 }, + { "brdrtriple", RTFControlType::FLAG, RTFKeyword::BRDRTRIPLE, 0 }, + { "brdrw", RTFControlType::VALUE, RTFKeyword::BRDRW, 0 }, + { "brdrwavy", RTFControlType::FLAG, RTFKeyword::BRDRWAVY, 0 }, + { "brdrwavydb", RTFControlType::FLAG, RTFKeyword::BRDRWAVYDB, 0 }, + { "brkfrm", RTFControlType::FLAG, RTFKeyword::BRKFRM, 0 }, + { "brsp", RTFControlType::VALUE, RTFKeyword::BRSP, 0 }, + { "bullet", RTFControlType::SYMBOL, RTFKeyword::BULLET, 0 }, + { "buptim", RTFControlType::DESTINATION, RTFKeyword::BUPTIM, 0 }, + { "bxe", RTFControlType::FLAG, RTFKeyword::BXE, 0 }, + { "caccentfive", RTFControlType::FLAG, RTFKeyword::CACCENTFIVE, 0 }, + { "caccentfour", RTFControlType::FLAG, RTFKeyword::CACCENTFOUR, 0 }, + { "caccentone", RTFControlType::FLAG, RTFKeyword::CACCENTONE, 0 }, + { "caccentsix", RTFControlType::FLAG, RTFKeyword::CACCENTSIX, 0 }, + { "caccentthree", RTFControlType::FLAG, RTFKeyword::CACCENTTHREE, 0 }, + { "caccenttwo", RTFControlType::FLAG, RTFKeyword::CACCENTTWO, 0 }, + { "cachedcolbal", RTFControlType::FLAG, RTFKeyword::CACHEDCOLBAL, 0 }, + { "caps", RTFControlType::TOGGLE, RTFKeyword::CAPS, 1 }, + { "category", RTFControlType::DESTINATION, RTFKeyword::CATEGORY, 0 }, + { "cb", RTFControlType::VALUE, RTFKeyword::CB, 0 }, + { "cbackgroundone", RTFControlType::FLAG, RTFKeyword::CBACKGROUNDONE, 0 }, + { "cbackgroundtwo", RTFControlType::FLAG, RTFKeyword::CBACKGROUNDTWO, 0 }, + { "cbpat", RTFControlType::VALUE, RTFKeyword::CBPAT, 0 }, + { "cchs", RTFControlType::VALUE, RTFKeyword::CCHS, 0 }, + { "cell", RTFControlType::SYMBOL, RTFKeyword::CELL, 0 }, + { "cellx", RTFControlType::VALUE, RTFKeyword::CELLX, 0 }, + { "cf", RTFControlType::VALUE, RTFKeyword::CF, 0 }, + { "cfollowedhyperlink", RTFControlType::FLAG, RTFKeyword::CFOLLOWEDHYPERLINK, 0 }, + { "cfpat", RTFControlType::VALUE, RTFKeyword::CFPAT, 0 }, + { "cgrid", RTFControlType::VALUE, RTFKeyword::CGRID, 0 }, + { "charrsid", RTFControlType::VALUE, RTFKeyword::CHARRSID, 0 }, + { "charscalex", RTFControlType::VALUE, RTFKeyword::CHARSCALEX, 100 }, + { "chatn", RTFControlType::SYMBOL, RTFKeyword::CHATN, 0 }, + { "chbgbdiag", RTFControlType::FLAG, RTFKeyword::CHBGBDIAG, 0 }, + { "chbgcross", RTFControlType::FLAG, RTFKeyword::CHBGCROSS, 0 }, + { "chbgdcross", RTFControlType::FLAG, RTFKeyword::CHBGDCROSS, 0 }, + { "chbgdkbdiag", RTFControlType::FLAG, RTFKeyword::CHBGDKBDIAG, 0 }, + { "chbgdkcross", RTFControlType::FLAG, RTFKeyword::CHBGDKCROSS, 0 }, + { "chbgdkdcross", RTFControlType::FLAG, RTFKeyword::CHBGDKDCROSS, 0 }, + { "chbgdkfdiag", RTFControlType::FLAG, RTFKeyword::CHBGDKFDIAG, 0 }, + { "chbgdkhoriz", RTFControlType::FLAG, RTFKeyword::CHBGDKHORIZ, 0 }, + { "chbgdkvert", RTFControlType::FLAG, RTFKeyword::CHBGDKVERT, 0 }, + { "chbgfdiag", RTFControlType::FLAG, RTFKeyword::CHBGFDIAG, 0 }, + { "chbghoriz", RTFControlType::FLAG, RTFKeyword::CHBGHORIZ, 0 }, + { "chbgvert", RTFControlType::FLAG, RTFKeyword::CHBGVERT, 0 }, + { "chbrdr", RTFControlType::FLAG, RTFKeyword::CHBRDR, 0 }, + { "chcbpat", RTFControlType::VALUE, RTFKeyword::CHCBPAT, 0 }, + { "chcfpat", RTFControlType::VALUE, RTFKeyword::CHCFPAT, 0 }, + { "chdate", RTFControlType::SYMBOL, RTFKeyword::CHDATE, 0 }, + { "chdpa", RTFControlType::SYMBOL, RTFKeyword::CHDPA, 0 }, + { "chdpl", RTFControlType::SYMBOL, RTFKeyword::CHDPL, 0 }, + { "chftn", RTFControlType::SYMBOL, RTFKeyword::CHFTN, 0 }, + { "chftnsep", RTFControlType::SYMBOL, RTFKeyword::CHFTNSEP, 0 }, + { "chftnsepc", RTFControlType::SYMBOL, RTFKeyword::CHFTNSEPC, 0 }, + { "chpgn", RTFControlType::SYMBOL, RTFKeyword::CHPGN, 0 }, + { "chhres", RTFControlType::VALUE, RTFKeyword::CHHRES, 0 }, + { "chshdng", RTFControlType::VALUE, RTFKeyword::CHSHDNG, 0 }, + { "chtime", RTFControlType::SYMBOL, RTFKeyword::CHTIME, 0 }, + { "chyperlink", RTFControlType::FLAG, RTFKeyword::CHYPERLINK, 0 }, + { "clbgbdiag", RTFControlType::FLAG, RTFKeyword::CLBGBDIAG, 0 }, + { "clbgcross", RTFControlType::FLAG, RTFKeyword::CLBGCROSS, 0 }, + { "clbgdcross", RTFControlType::FLAG, RTFKeyword::CLBGDCROSS, 0 }, + { "clbgdkbdiag", RTFControlType::FLAG, RTFKeyword::CLBGDKBDIAG, 0 }, + { "clbgdkcross", RTFControlType::FLAG, RTFKeyword::CLBGDKCROSS, 0 }, + { "clbgdkdcross", RTFControlType::FLAG, RTFKeyword::CLBGDKDCROSS, 0 }, + { "clbgdkfdiag", RTFControlType::FLAG, RTFKeyword::CLBGDKFDIAG, 0 }, + { "clbgdkhor", RTFControlType::FLAG, RTFKeyword::CLBGDKHOR, 0 }, + { "clbgdkvert", RTFControlType::FLAG, RTFKeyword::CLBGDKVERT, 0 }, + { "clbgfdiag", RTFControlType::FLAG, RTFKeyword::CLBGFDIAG, 0 }, + { "clbghoriz", RTFControlType::FLAG, RTFKeyword::CLBGHORIZ, 0 }, + { "clbgvert", RTFControlType::FLAG, RTFKeyword::CLBGVERT, 0 }, + { "clbrdrb", RTFControlType::FLAG, RTFKeyword::CLBRDRB, 0 }, + { "clbrdrl", RTFControlType::FLAG, RTFKeyword::CLBRDRL, 0 }, + { "clbrdrr", RTFControlType::FLAG, RTFKeyword::CLBRDRR, 0 }, + { "clbrdrt", RTFControlType::FLAG, RTFKeyword::CLBRDRT, 0 }, + { "clcbpat", RTFControlType::VALUE, RTFKeyword::CLCBPAT, 0 }, + { "clcbpatraw", RTFControlType::VALUE, RTFKeyword::CLCBPATRAW, 0 }, + { "clcfpat", RTFControlType::VALUE, RTFKeyword::CLCFPAT, 0 }, + { "clcfpatraw", RTFControlType::VALUE, RTFKeyword::CLCFPATRAW, 0 }, + { "cldel", RTFControlType::FLAG, RTFKeyword::CLDEL, 0 }, + { "cldelauth", RTFControlType::VALUE, RTFKeyword::CLDELAUTH, 0 }, + { "cldeldttm", RTFControlType::VALUE, RTFKeyword::CLDELDTTM, 0 }, + { "cldgll", RTFControlType::FLAG, RTFKeyword::CLDGLL, 0 }, + { "cldglu", RTFControlType::FLAG, RTFKeyword::CLDGLU, 0 }, + { "clFitText", RTFControlType::FLAG, RTFKeyword::CLFITTEXT, 0 }, + { "clftsWidth", RTFControlType::VALUE, RTFKeyword::CLFTSWIDTH, 0 }, + { "clhidemark", RTFControlType::FLAG, RTFKeyword::CLHIDEMARK, 0 }, + { "clins", RTFControlType::FLAG, RTFKeyword::CLINS, 0 }, + { "clinsauth", RTFControlType::VALUE, RTFKeyword::CLINSAUTH, 0 }, + { "clinsdttm", RTFControlType::VALUE, RTFKeyword::CLINSDTTM, 0 }, + { "clmgf", RTFControlType::FLAG, RTFKeyword::CLMGF, 0 }, + { "clmrg", RTFControlType::FLAG, RTFKeyword::CLMRG, 0 }, + { "clmrgd", RTFControlType::FLAG, RTFKeyword::CLMRGD, 0 }, + { "clmrgdauth", RTFControlType::VALUE, RTFKeyword::CLMRGDAUTH, 0 }, + { "clmrgddttm", RTFControlType::VALUE, RTFKeyword::CLMRGDDTTM, 0 }, + { "clmrgdr", RTFControlType::FLAG, RTFKeyword::CLMRGDR, 0 }, + { "clNoWrap", RTFControlType::FLAG, RTFKeyword::CLNOWRAP, 0 }, + { "clpadb", RTFControlType::VALUE, RTFKeyword::CLPADB, 0 }, + { "clpadfb", RTFControlType::VALUE, RTFKeyword::CLPADFB, 0 }, + { "clpadfl", RTFControlType::VALUE, RTFKeyword::CLPADFL, 0 }, + { "clpadfr", RTFControlType::VALUE, RTFKeyword::CLPADFR, 0 }, + { "clpadft", RTFControlType::VALUE, RTFKeyword::CLPADFT, 0 }, + { "clpadl", RTFControlType::VALUE, RTFKeyword::CLPADL, 0 }, + { "clpadr", RTFControlType::VALUE, RTFKeyword::CLPADR, 0 }, + { "clpadt", RTFControlType::VALUE, RTFKeyword::CLPADT, 0 }, + { "clspb", RTFControlType::VALUE, RTFKeyword::CLSPB, 0 }, + { "clspfb", RTFControlType::VALUE, RTFKeyword::CLSPFB, 0 }, + { "clspfl", RTFControlType::VALUE, RTFKeyword::CLSPFL, 0 }, + { "clspfr", RTFControlType::VALUE, RTFKeyword::CLSPFR, 0 }, + { "clspft", RTFControlType::VALUE, RTFKeyword::CLSPFT, 0 }, + { "clspl", RTFControlType::VALUE, RTFKeyword::CLSPL, 0 }, + { "clspr", RTFControlType::VALUE, RTFKeyword::CLSPR, 0 }, + { "clspt", RTFControlType::VALUE, RTFKeyword::CLSPT, 0 }, + { "clshdng", RTFControlType::VALUE, RTFKeyword::CLSHDNG, 0 }, + { "clshdngraw", RTFControlType::VALUE, RTFKeyword::CLSHDNGRAW, 0 }, + { "clshdrawnil", RTFControlType::FLAG, RTFKeyword::CLSHDRAWNIL, 0 }, + { "clsplit", RTFControlType::FLAG, RTFKeyword::CLSPLIT, 0 }, + { "clsplitr", RTFControlType::FLAG, RTFKeyword::CLSPLITR, 0 }, + { "cltxbtlr", RTFControlType::FLAG, RTFKeyword::CLTXBTLR, 0 }, + { "cltxlrtb", RTFControlType::FLAG, RTFKeyword::CLTXLRTB, 0 }, + { "cltxlrtbv", RTFControlType::FLAG, RTFKeyword::CLTXLRTBV, 0 }, + { "cltxtbrl", RTFControlType::FLAG, RTFKeyword::CLTXTBRL, 0 }, + { "cltxtbrlv", RTFControlType::FLAG, RTFKeyword::CLTXTBRLV, 0 }, + { "clvertalb", RTFControlType::FLAG, RTFKeyword::CLVERTALB, 0 }, + { "clvertalc", RTFControlType::FLAG, RTFKeyword::CLVERTALC, 0 }, + { "clvertalt", RTFControlType::FLAG, RTFKeyword::CLVERTALT, 0 }, + { "clvmgf", RTFControlType::FLAG, RTFKeyword::CLVMGF, 0 }, + { "clvmrg", RTFControlType::FLAG, RTFKeyword::CLVMRG, 0 }, + { "clwWidth", RTFControlType::VALUE, RTFKeyword::CLWWIDTH, 0 }, + { "cmaindarkone", RTFControlType::FLAG, RTFKeyword::CMAINDARKONE, 0 }, + { "cmaindarktwo", RTFControlType::FLAG, RTFKeyword::CMAINDARKTWO, 0 }, + { "cmainlightone", RTFControlType::FLAG, RTFKeyword::CMAINLIGHTONE, 0 }, + { "cmainlighttwo", RTFControlType::FLAG, RTFKeyword::CMAINLIGHTTWO, 0 }, + { "collapsed", RTFControlType::FLAG, RTFKeyword::COLLAPSED, 0 }, + { "colno", RTFControlType::VALUE, RTFKeyword::COLNO, 0 }, + { "colorschememapping", RTFControlType::DESTINATION, RTFKeyword::COLORSCHEMEMAPPING, 0 }, + { "colortbl", RTFControlType::DESTINATION, RTFKeyword::COLORTBL, 0 }, + { "cols", RTFControlType::VALUE, RTFKeyword::COLS, 1 }, + { "colsr", RTFControlType::VALUE, RTFKeyword::COLSR, 0 }, + { "colsx", RTFControlType::VALUE, RTFKeyword::COLSX, 720 }, + { "column", RTFControlType::SYMBOL, RTFKeyword::COLUMN, 0 }, + { "colw", RTFControlType::VALUE, RTFKeyword::COLW, 0 }, + { "comment", RTFControlType::DESTINATION, RTFKeyword::COMMENT, 0 }, + { "company", RTFControlType::DESTINATION, RTFKeyword::COMPANY, 0 }, + { "contextualspace", RTFControlType::FLAG, RTFKeyword::CONTEXTUALSPACE, 0 }, + { "cpg", RTFControlType::VALUE, RTFKeyword::CPG, 0 }, + { "crauth", RTFControlType::VALUE, RTFKeyword::CRAUTH, 0 }, + { "crdate", RTFControlType::VALUE, RTFKeyword::CRDATE, 0 }, + { "creatim", RTFControlType::DESTINATION, RTFKeyword::CREATIM, 0 }, + { "cs", RTFControlType::VALUE, RTFKeyword::CS, 0 }, + { "cshade", RTFControlType::VALUE, RTFKeyword::CSHADE, 0 }, + { "ctextone", RTFControlType::FLAG, RTFKeyword::CTEXTONE, 0 }, + { "ctexttwo", RTFControlType::FLAG, RTFKeyword::CTEXTTWO, 0 }, + { "ctint", RTFControlType::VALUE, RTFKeyword::CTINT, 0 }, + { "ctrl", RTFControlType::FLAG, RTFKeyword::CTRL, 0 }, + { "cts", RTFControlType::VALUE, RTFKeyword::CTS, 0 }, + { "cufi", RTFControlType::VALUE, RTFKeyword::CUFI, 0 }, + { "culi", RTFControlType::VALUE, RTFKeyword::CULI, 0 }, + { "curi", RTFControlType::VALUE, RTFKeyword::CURI, 0 }, + { "cvmme", RTFControlType::FLAG, RTFKeyword::CVMME, 0 }, + { "datafield", RTFControlType::DESTINATION, RTFKeyword::DATAFIELD, 0 }, + { "datastore", RTFControlType::DESTINATION, RTFKeyword::DATASTORE, 0 }, + { "date", RTFControlType::FLAG, RTFKeyword::DATE, 0 }, + { "dbch", RTFControlType::FLAG, RTFKeyword::DBCH, 0 }, + { "defchp", RTFControlType::DESTINATION, RTFKeyword::DEFCHP, 0 }, + { "deff", RTFControlType::VALUE, RTFKeyword::DEFF, 0 }, + { "defformat", RTFControlType::FLAG, RTFKeyword::DEFFORMAT, 0 }, + { "deflang", RTFControlType::VALUE, RTFKeyword::DEFLANG, 0 }, + { "deflangfe", RTFControlType::VALUE, RTFKeyword::DEFLANGFE, 0 }, + { "defpap", RTFControlType::DESTINATION, RTFKeyword::DEFPAP, 0 }, + { "defshp", RTFControlType::FLAG, RTFKeyword::DEFSHP, 0 }, + { "deftab", RTFControlType::VALUE, RTFKeyword::DEFTAB, 720 }, + { "deleted", RTFControlType::TOGGLE, RTFKeyword::DELETED, 1 }, + { "delrsid", RTFControlType::VALUE, RTFKeyword::DELRSID, 0 }, + { "dfrauth", RTFControlType::VALUE, RTFKeyword::DFRAUTH, 0 }, + { "dfrdate", RTFControlType::VALUE, RTFKeyword::DFRDATE, 0 }, + { "dfrmtxtx", RTFControlType::VALUE, RTFKeyword::DFRMTXTX, 0 }, + { "dfrmtxty", RTFControlType::VALUE, RTFKeyword::DFRMTXTY, 0 }, + { "dfrstart", RTFControlType::VALUE, RTFKeyword::DFRSTART, 0 }, + { "dfrstop", RTFControlType::VALUE, RTFKeyword::DFRSTOP, 0 }, + { "dfrxst", RTFControlType::VALUE, RTFKeyword::DFRXST, 0 }, + { "dghorigin", RTFControlType::VALUE, RTFKeyword::DGHORIGIN, 1701 }, + { "dghshow", RTFControlType::VALUE, RTFKeyword::DGHSHOW, 3 }, + { "dghspace", RTFControlType::VALUE, RTFKeyword::DGHSPACE, 120 }, + { "dgmargin", RTFControlType::FLAG, RTFKeyword::DGMARGIN, 0 }, + { "dgsnap", RTFControlType::FLAG, RTFKeyword::DGSNAP, 0 }, + { "dgvorigin", RTFControlType::VALUE, RTFKeyword::DGVORIGIN, 1984 }, + { "dgvshow", RTFControlType::VALUE, RTFKeyword::DGVSHOW, 0 }, + { "dgvspace", RTFControlType::VALUE, RTFKeyword::DGVSPACE, 120 }, + { "dibitmap", RTFControlType::VALUE, RTFKeyword::DIBITMAP, 0 }, + { "disabled", RTFControlType::TOGGLE, RTFKeyword::DISABLED, 1 }, + { "dn", RTFControlType::VALUE, RTFKeyword::DN, 6 }, + { "dntblnsbdb", RTFControlType::FLAG, RTFKeyword::DNTBLNSBDB, 0 }, + { "do", RTFControlType::DESTINATION, RTFKeyword::DO, 0 }, + { "dobxcolumn", RTFControlType::FLAG, RTFKeyword::DOBXCOLUMN, 0 }, + { "dobxmargin", RTFControlType::FLAG, RTFKeyword::DOBXMARGIN, 0 }, + { "dobxpage", RTFControlType::FLAG, RTFKeyword::DOBXPAGE, 0 }, + { "dobymargin", RTFControlType::FLAG, RTFKeyword::DOBYMARGIN, 0 }, + { "dobypage", RTFControlType::FLAG, RTFKeyword::DOBYPAGE, 0 }, + { "dobypara", RTFControlType::FLAG, RTFKeyword::DOBYPARA, 0 }, + { "doccomm", RTFControlType::DESTINATION, RTFKeyword::DOCCOMM, 0 }, + { "doctemp", RTFControlType::FLAG, RTFKeyword::DOCTEMP, 0 }, + { "doctype", RTFControlType::VALUE, RTFKeyword::DOCTYPE, 0 }, + { "docvar", RTFControlType::DESTINATION, RTFKeyword::DOCVAR, 0 }, + { "dodhgt", RTFControlType::VALUE, RTFKeyword::DODHGT, 0 }, + { "dolock", RTFControlType::FLAG, RTFKeyword::DOLOCK, 0 }, + { "donotembedlingdata", RTFControlType::VALUE, RTFKeyword::DONOTEMBEDLINGDATA, 0 }, + { "donotembedsysfont", RTFControlType::VALUE, RTFKeyword::DONOTEMBEDSYSFONT, 0 }, + { "donotshowcomments", RTFControlType::FLAG, RTFKeyword::DONOTSHOWCOMMENTS, 0 }, + { "donotshowinsdel", RTFControlType::FLAG, RTFKeyword::DONOTSHOWINSDEL, 0 }, + { "donotshowmarkup", RTFControlType::FLAG, RTFKeyword::DONOTSHOWMARKUP, 0 }, + { "donotshowprops", RTFControlType::FLAG, RTFKeyword::DONOTSHOWPROPS, 0 }, + { "dpaendhol", RTFControlType::FLAG, RTFKeyword::DPAENDHOL, 0 }, + { "dpaendl", RTFControlType::VALUE, RTFKeyword::DPAENDL, 0 }, + { "dpaendsol", RTFControlType::FLAG, RTFKeyword::DPAENDSOL, 0 }, + { "dpaendw", RTFControlType::VALUE, RTFKeyword::DPAENDW, 0 }, + { "dparc", RTFControlType::FLAG, RTFKeyword::DPARC, 0 }, + { "dparcflipx", RTFControlType::FLAG, RTFKeyword::DPARCFLIPX, 0 }, + { "dparcflipy", RTFControlType::FLAG, RTFKeyword::DPARCFLIPY, 0 }, + { "dpastarthol", RTFControlType::FLAG, RTFKeyword::DPASTARTHOL, 0 }, + { "dpastartl", RTFControlType::VALUE, RTFKeyword::DPASTARTL, 0 }, + { "dpastartsol", RTFControlType::FLAG, RTFKeyword::DPASTARTSOL, 0 }, + { "dpastartw", RTFControlType::VALUE, RTFKeyword::DPASTARTW, 0 }, + { "dpcallout", RTFControlType::FLAG, RTFKeyword::DPCALLOUT, 0 }, + { "dpcoa", RTFControlType::VALUE, RTFKeyword::DPCOA, 0 }, + { "dpcoaccent", RTFControlType::FLAG, RTFKeyword::DPCOACCENT, 0 }, + { "dpcobestfit", RTFControlType::FLAG, RTFKeyword::DPCOBESTFIT, 0 }, + { "dpcoborder", RTFControlType::FLAG, RTFKeyword::DPCOBORDER, 0 }, + { "dpcodabs", RTFControlType::FLAG, RTFKeyword::DPCODABS, 0 }, + { "dpcodbottom", RTFControlType::FLAG, RTFKeyword::DPCODBOTTOM, 0 }, + { "dpcodcenter", RTFControlType::FLAG, RTFKeyword::DPCODCENTER, 0 }, + { "dpcodescent", RTFControlType::VALUE, RTFKeyword::DPCODESCENT, 0 }, + { "dpcodtop", RTFControlType::FLAG, RTFKeyword::DPCODTOP, 0 }, + { "dpcolength", RTFControlType::VALUE, RTFKeyword::DPCOLENGTH, 0 }, + { "dpcominusx", RTFControlType::FLAG, RTFKeyword::DPCOMINUSX, 0 }, + { "dpcominusy", RTFControlType::FLAG, RTFKeyword::DPCOMINUSY, 0 }, + { "dpcooffset", RTFControlType::VALUE, RTFKeyword::DPCOOFFSET, 0 }, + { "dpcosmarta", RTFControlType::FLAG, RTFKeyword::DPCOSMARTA, 0 }, + { "dpcotdouble", RTFControlType::FLAG, RTFKeyword::DPCOTDOUBLE, 0 }, + { "dpcotright", RTFControlType::FLAG, RTFKeyword::DPCOTRIGHT, 0 }, + { "dpcotsingle", RTFControlType::FLAG, RTFKeyword::DPCOTSINGLE, 0 }, + { "dpcottriple", RTFControlType::FLAG, RTFKeyword::DPCOTTRIPLE, 0 }, + { "dpcount", RTFControlType::VALUE, RTFKeyword::DPCOUNT, 0 }, + { "dpellipse", RTFControlType::FLAG, RTFKeyword::DPELLIPSE, 0 }, + { "dpendgroup", RTFControlType::FLAG, RTFKeyword::DPENDGROUP, 0 }, + { "dpfillbgcb", RTFControlType::VALUE, RTFKeyword::DPFILLBGCB, 0 }, + { "dpfillbgcg", RTFControlType::VALUE, RTFKeyword::DPFILLBGCG, 0 }, + { "dpfillbgcr", RTFControlType::VALUE, RTFKeyword::DPFILLBGCR, 0 }, + { "dpfillbggray", RTFControlType::VALUE, RTFKeyword::DPFILLBGGRAY, 0 }, + { "dpfillbgpal", RTFControlType::FLAG, RTFKeyword::DPFILLBGPAL, 0 }, + { "dpfillfgcb", RTFControlType::VALUE, RTFKeyword::DPFILLFGCB, 0 }, + { "dpfillfgcg", RTFControlType::VALUE, RTFKeyword::DPFILLFGCG, 0 }, + { "dpfillfgcr", RTFControlType::VALUE, RTFKeyword::DPFILLFGCR, 0 }, + { "dpfillfggray", RTFControlType::VALUE, RTFKeyword::DPFILLFGGRAY, 0 }, + { "dpfillfgpal", RTFControlType::FLAG, RTFKeyword::DPFILLFGPAL, 0 }, + { "dpfillpat", RTFControlType::VALUE, RTFKeyword::DPFILLPAT, 0 }, + { "dpgroup", RTFControlType::FLAG, RTFKeyword::DPGROUP, 0 }, + { "dpline", RTFControlType::FLAG, RTFKeyword::DPLINE, 0 }, + { "dplinecob", RTFControlType::VALUE, RTFKeyword::DPLINECOB, 0 }, + { "dplinecog", RTFControlType::VALUE, RTFKeyword::DPLINECOG, 0 }, + { "dplinecor", RTFControlType::VALUE, RTFKeyword::DPLINECOR, 0 }, + { "dplinedado", RTFControlType::FLAG, RTFKeyword::DPLINEDADO, 0 }, + { "dplinedadodo", RTFControlType::FLAG, RTFKeyword::DPLINEDADODO, 0 }, + { "dplinedash", RTFControlType::FLAG, RTFKeyword::DPLINEDASH, 0 }, + { "dplinedot", RTFControlType::FLAG, RTFKeyword::DPLINEDOT, 0 }, + { "dplinegray", RTFControlType::VALUE, RTFKeyword::DPLINEGRAY, 0 }, + { "dplinehollow", RTFControlType::FLAG, RTFKeyword::DPLINEHOLLOW, 0 }, + { "dplinepal", RTFControlType::FLAG, RTFKeyword::DPLINEPAL, 0 }, + { "dplinesolid", RTFControlType::FLAG, RTFKeyword::DPLINESOLID, 0 }, + { "dplinew", RTFControlType::VALUE, RTFKeyword::DPLINEW, 0 }, + { "dppolycount", RTFControlType::VALUE, RTFKeyword::DPPOLYCOUNT, 0 }, + { "dppolygon", RTFControlType::FLAG, RTFKeyword::DPPOLYGON, 0 }, + { "dppolyline", RTFControlType::FLAG, RTFKeyword::DPPOLYLINE, 0 }, + { "dpptx", RTFControlType::VALUE, RTFKeyword::DPPTX, 0 }, + { "dppty", RTFControlType::VALUE, RTFKeyword::DPPTY, 0 }, + { "dprect", RTFControlType::FLAG, RTFKeyword::DPRECT, 0 }, + { "dproundr", RTFControlType::FLAG, RTFKeyword::DPROUNDR, 0 }, + { "dpshadow", RTFControlType::FLAG, RTFKeyword::DPSHADOW, 0 }, + { "dpshadx", RTFControlType::VALUE, RTFKeyword::DPSHADX, 0 }, + { "dpshady", RTFControlType::VALUE, RTFKeyword::DPSHADY, 0 }, + { "dptxbtlr", RTFControlType::FLAG, RTFKeyword::DPTXBTLR, 0 }, + { "dptxbx", RTFControlType::FLAG, RTFKeyword::DPTXBX, 0 }, + { "dptxbxmar", RTFControlType::VALUE, RTFKeyword::DPTXBXMAR, 0 }, + { "dptxbxtext", RTFControlType::DESTINATION, RTFKeyword::DPTXBXTEXT, 0 }, + { "dptxlrtb", RTFControlType::FLAG, RTFKeyword::DPTXLRTB, 0 }, + { "dptxlrtbv", RTFControlType::FLAG, RTFKeyword::DPTXLRTBV, 0 }, + { "dptxtbrl", RTFControlType::FLAG, RTFKeyword::DPTXTBRL, 0 }, + { "dptxtbrlv", RTFControlType::FLAG, RTFKeyword::DPTXTBRLV, 0 }, + { "dpx", RTFControlType::VALUE, RTFKeyword::DPX, 0 }, + { "dpxsize", RTFControlType::VALUE, RTFKeyword::DPXSIZE, 0 }, + { "dpy", RTFControlType::VALUE, RTFKeyword::DPY, 0 }, + { "dpysize", RTFControlType::VALUE, RTFKeyword::DPYSIZE, 0 }, + { "dropcapli", RTFControlType::VALUE, RTFKeyword::DROPCAPLI, 0 }, + { "dropcapt", RTFControlType::VALUE, RTFKeyword::DROPCAPT, 0 }, + { "ds", RTFControlType::VALUE, RTFKeyword::DS, 0 }, + { "dxfrtext", RTFControlType::VALUE, RTFKeyword::DXFRTEXT, 0 }, + { "dy", RTFControlType::VALUE, RTFKeyword::DY, 0 }, + { "ebcend", RTFControlType::DESTINATION, RTFKeyword::EBCEND, 0 }, + { "ebcstart", RTFControlType::DESTINATION, RTFKeyword::EBCSTART, 0 }, + { "edmins", RTFControlType::VALUE, RTFKeyword::EDMINS, 0 }, + { "embo", RTFControlType::TOGGLE, RTFKeyword::EMBO, 1 }, + { "emdash", RTFControlType::SYMBOL, RTFKeyword::EMDASH, 0 }, + { "emfblip", RTFControlType::FLAG, RTFKeyword::EMFBLIP, 0 }, + { "emspace", RTFControlType::SYMBOL, RTFKeyword::EMSPACE, 0 }, + { "endash", RTFControlType::SYMBOL, RTFKeyword::ENDASH, 0 }, + { "enddoc", RTFControlType::FLAG, RTFKeyword::ENDDOC, 0 }, + { "endnhere", RTFControlType::FLAG, RTFKeyword::ENDNHERE, 0 }, + { "endnotes", RTFControlType::FLAG, RTFKeyword::ENDNOTES, 0 }, + { "enforceprot", RTFControlType::VALUE, RTFKeyword::ENFORCEPROT, 0 }, + { "enspace", RTFControlType::SYMBOL, RTFKeyword::ENSPACE, 0 }, + { "expnd", RTFControlType::VALUE, RTFKeyword::EXPND, 0 }, + { "expndtw", RTFControlType::VALUE, RTFKeyword::EXPNDTW, 0 }, + { "expshrtn", RTFControlType::FLAG, RTFKeyword::EXPSHRTN, 0 }, + { "f", RTFControlType::VALUE, RTFKeyword::F, 0 }, + { "faauto", RTFControlType::FLAG, RTFKeyword::FAAUTO, 0 }, + { "facenter", RTFControlType::FLAG, RTFKeyword::FACENTER, 0 }, + { "facingp", RTFControlType::TOGGLE, RTFKeyword::FACINGP, 1 }, + { "factoidname", RTFControlType::DESTINATION, RTFKeyword::FACTOIDNAME, 0 }, + { "fafixed", RTFControlType::FLAG, RTFKeyword::FAFIXED, 0 }, + { "fahang", RTFControlType::FLAG, RTFKeyword::FAHANG, 0 }, + { "falt", RTFControlType::DESTINATION, RTFKeyword::FALT, 0 }, + { "faroman", RTFControlType::FLAG, RTFKeyword::FAROMAN, 0 }, + { "favar", RTFControlType::FLAG, RTFKeyword::FAVAR, 0 }, + { "fbias", RTFControlType::VALUE, RTFKeyword::FBIAS, 0 }, + { "fbidi", RTFControlType::FLAG, RTFKeyword::FBIDI, 0 }, + { "fbidis", RTFControlType::FLAG, RTFKeyword::FBIDIS, 0 }, + { "fbimajor", RTFControlType::FLAG, RTFKeyword::FBIMAJOR, 0 }, + { "fbiminor", RTFControlType::FLAG, RTFKeyword::FBIMINOR, 0 }, + { "fchars", RTFControlType::DESTINATION, RTFKeyword::FCHARS, 0 }, + { "fcharset", RTFControlType::VALUE, RTFKeyword::FCHARSET, 0 }, + { "fcs", RTFControlType::VALUE, RTFKeyword::FCS, 0 }, + { "fdbmajor", RTFControlType::FLAG, RTFKeyword::FDBMAJOR, 0 }, + { "fdbminor", RTFControlType::FLAG, RTFKeyword::FDBMINOR, 0 }, + { "fdecor", RTFControlType::FLAG, RTFKeyword::FDECOR, 0 }, + { "felnbrelev", RTFControlType::FLAG, RTFKeyword::FELNBRELEV, 0 }, + { "fet", RTFControlType::VALUE, RTFKeyword::FET, 0 }, + { "fetch", RTFControlType::FLAG, RTFKeyword::FETCH, 0 }, + { "ffdefres", RTFControlType::VALUE, RTFKeyword::FFDEFRES, 0 }, + { "ffdeftext", RTFControlType::DESTINATION, RTFKeyword::FFDEFTEXT, 0 }, + { "ffentrymcr", RTFControlType::DESTINATION, RTFKeyword::FFENTRYMCR, 0 }, + { "ffexitmcr", RTFControlType::DESTINATION, RTFKeyword::FFEXITMCR, 0 }, + { "ffformat", RTFControlType::DESTINATION, RTFKeyword::FFFORMAT, 0 }, + { "ffhaslistbox", RTFControlType::VALUE, RTFKeyword::FFHASLISTBOX, 0 }, + { "ffhelptext", RTFControlType::DESTINATION, RTFKeyword::FFHELPTEXT, 0 }, + { "ffhps", RTFControlType::VALUE, RTFKeyword::FFHPS, 0 }, + { "ffl", RTFControlType::DESTINATION, RTFKeyword::FFL, 0 }, + { "ffmaxlen", RTFControlType::VALUE, RTFKeyword::FFMAXLEN, 0 }, + { "ffname", RTFControlType::DESTINATION, RTFKeyword::FFNAME, 0 }, + { "ffownhelp", RTFControlType::VALUE, RTFKeyword::FFOWNHELP, 0 }, + { "ffownstat", RTFControlType::VALUE, RTFKeyword::FFOWNSTAT, 0 }, + { "ffprot", RTFControlType::VALUE, RTFKeyword::FFPROT, 0 }, + { "ffrecalc", RTFControlType::VALUE, RTFKeyword::FFRECALC, 0 }, + { "ffres", RTFControlType::VALUE, RTFKeyword::FFRES, 0 }, + { "ffsize", RTFControlType::VALUE, RTFKeyword::FFSIZE, 0 }, + { "ffstattext", RTFControlType::DESTINATION, RTFKeyword::FFSTATTEXT, 0 }, + { "fftype", RTFControlType::VALUE, RTFKeyword::FFTYPE, 0 }, + { "fftypetxt", RTFControlType::VALUE, RTFKeyword::FFTYPETXT, 0 }, + { "fhimajor", RTFControlType::FLAG, RTFKeyword::FHIMAJOR, 0 }, + { "fhiminor", RTFControlType::FLAG, RTFKeyword::FHIMINOR, 0 }, + { "fi", RTFControlType::VALUE, RTFKeyword::FI, 0 }, + { "fid", RTFControlType::VALUE, RTFKeyword::FID, 0 }, + { "field", RTFControlType::DESTINATION, RTFKeyword::FIELD, 0 }, + { "file", RTFControlType::DESTINATION, RTFKeyword::FILE, 0 }, + { "filetbl", RTFControlType::DESTINATION, RTFKeyword::FILETBL, 0 }, + { "fittext", RTFControlType::VALUE, RTFKeyword::FITTEXT, 0 }, + { "fjgothic", RTFControlType::FLAG, RTFKeyword::FJGOTHIC, 0 }, + { "fjminchou", RTFControlType::FLAG, RTFKeyword::FJMINCHOU, 0 }, + { "fldalt", RTFControlType::FLAG, RTFKeyword::FLDALT, 0 }, + { "flddirty", RTFControlType::FLAG, RTFKeyword::FLDDIRTY, 0 }, + { "fldedit", RTFControlType::FLAG, RTFKeyword::FLDEDIT, 0 }, + { "fldinst", RTFControlType::DESTINATION, RTFKeyword::FLDINST, 0 }, + { "fldlock", RTFControlType::FLAG, RTFKeyword::FLDLOCK, 0 }, + { "fldpriv", RTFControlType::FLAG, RTFKeyword::FLDPRIV, 0 }, + { "fldrslt", RTFControlType::DESTINATION, RTFKeyword::FLDRSLT, 0 }, + { "fldtype", RTFControlType::DESTINATION, RTFKeyword::FLDTYPE, 0 }, + { "flomajor", RTFControlType::FLAG, RTFKeyword::FLOMAJOR, 0 }, + { "flominor", RTFControlType::FLAG, RTFKeyword::FLOMINOR, 0 }, + { "fmodern", RTFControlType::FLAG, RTFKeyword::FMODERN, 0 }, + { "fn", RTFControlType::VALUE, RTFKeyword::FN, 0 }, + { "fname", RTFControlType::DESTINATION, RTFKeyword::FNAME, 0 }, + { "fnetwork", RTFControlType::FLAG, RTFKeyword::FNETWORK, 0 }, + { "fnil", RTFControlType::FLAG, RTFKeyword::FNIL, 0 }, + { "fnonfilesys", RTFControlType::FLAG, RTFKeyword::FNONFILESYS, 0 }, + { "fontemb", RTFControlType::DESTINATION, RTFKeyword::FONTEMB, 0 }, + { "fontfile", RTFControlType::DESTINATION, RTFKeyword::FONTFILE, 0 }, + { "fonttbl", RTFControlType::DESTINATION, RTFKeyword::FONTTBL, 0 }, + { "footer", RTFControlType::DESTINATION, RTFKeyword::FOOTER, 0 }, + { "footerf", RTFControlType::DESTINATION, RTFKeyword::FOOTERF, 0 }, + { "footerl", RTFControlType::DESTINATION, RTFKeyword::FOOTERL, 0 }, + { "footerr", RTFControlType::DESTINATION, RTFKeyword::FOOTERR, 0 }, + { "footery", RTFControlType::VALUE, RTFKeyword::FOOTERY, 720 }, + { "footnote", RTFControlType::DESTINATION, RTFKeyword::FOOTNOTE, 0 }, + { "forceupgrade", RTFControlType::FLAG, RTFKeyword::FORCEUPGRADE, 0 }, + { "formdisp", RTFControlType::FLAG, RTFKeyword::FORMDISP, 0 }, + { "formfield", RTFControlType::DESTINATION, RTFKeyword::FORMFIELD, 0 }, + { "formprot", RTFControlType::FLAG, RTFKeyword::FORMPROT, 0 }, + { "formshade", RTFControlType::FLAG, RTFKeyword::FORMSHADE, 0 }, + { "fosnum", RTFControlType::VALUE, RTFKeyword::FOSNUM, 0 }, + { "fprq", RTFControlType::VALUE, RTFKeyword::FPRQ, 0 }, + { "fracwidth", RTFControlType::FLAG, RTFKeyword::FRACWIDTH, 0 }, + { "frelative", RTFControlType::VALUE, RTFKeyword::FRELATIVE, 0 }, + { "frmtxbtlr", RTFControlType::FLAG, RTFKeyword::FRMTXBTLR, 0 }, + { "frmtxlrtb", RTFControlType::FLAG, RTFKeyword::FRMTXLRTB, 0 }, + { "frmtxlrtbv", RTFControlType::FLAG, RTFKeyword::FRMTXLRTBV, 0 }, + { "frmtxtbrl", RTFControlType::FLAG, RTFKeyword::FRMTXTBRL, 0 }, + { "frmtxtbrlv", RTFControlType::FLAG, RTFKeyword::FRMTXTBRLV, 0 }, + { "froman", RTFControlType::FLAG, RTFKeyword::FROMAN, 0 }, + { "fromhtml", RTFControlType::VALUE, RTFKeyword::FROMHTML, 0 }, + { "fromtext", RTFControlType::FLAG, RTFKeyword::FROMTEXT, 0 }, + { "fs", RTFControlType::VALUE, RTFKeyword::FS, 24 }, + { "fscript", RTFControlType::FLAG, RTFKeyword::FSCRIPT, 0 }, + { "fswiss", RTFControlType::FLAG, RTFKeyword::FSWISS, 0 }, + { "ftech", RTFControlType::FLAG, RTFKeyword::FTECH, 0 }, + { "ftnalt", RTFControlType::FLAG, RTFKeyword::FTNALT, 0 }, + { "ftnbj", RTFControlType::FLAG, RTFKeyword::FTNBJ, 0 }, + { "ftncn", RTFControlType::DESTINATION, RTFKeyword::FTNCN, 0 }, + { "ftnil", RTFControlType::FLAG, RTFKeyword::FTNIL, 0 }, + { "ftnlytwnine", RTFControlType::FLAG, RTFKeyword::FTNLYTWNINE, 0 }, + { "ftnnalc", RTFControlType::FLAG, RTFKeyword::FTNNALC, 0 }, + { "ftnnar", RTFControlType::FLAG, RTFKeyword::FTNNAR, 0 }, + { "ftnnauc", RTFControlType::FLAG, RTFKeyword::FTNNAUC, 0 }, + { "ftnnchi", RTFControlType::FLAG, RTFKeyword::FTNNCHI, 0 }, + { "ftnnchosung", RTFControlType::FLAG, RTFKeyword::FTNNCHOSUNG, 0 }, + { "ftnncnum", RTFControlType::FLAG, RTFKeyword::FTNNCNUM, 0 }, + { "ftnndbar", RTFControlType::FLAG, RTFKeyword::FTNNDBAR, 0 }, + { "ftnndbnum", RTFControlType::FLAG, RTFKeyword::FTNNDBNUM, 0 }, + { "ftnndbnumd", RTFControlType::FLAG, RTFKeyword::FTNNDBNUMD, 0 }, + { "ftnndbnumk", RTFControlType::FLAG, RTFKeyword::FTNNDBNUMK, 0 }, + { "ftnndbnumt", RTFControlType::FLAG, RTFKeyword::FTNNDBNUMT, 0 }, + { "ftnnganada", RTFControlType::FLAG, RTFKeyword::FTNNGANADA, 0 }, + { "ftnngbnum", RTFControlType::FLAG, RTFKeyword::FTNNGBNUM, 0 }, + { "ftnngbnumd", RTFControlType::FLAG, RTFKeyword::FTNNGBNUMD, 0 }, + { "ftnngbnumk", RTFControlType::FLAG, RTFKeyword::FTNNGBNUMK, 0 }, + { "ftnngbnuml", RTFControlType::FLAG, RTFKeyword::FTNNGBNUML, 0 }, + { "ftnnrlc", RTFControlType::FLAG, RTFKeyword::FTNNRLC, 0 }, + { "ftnnruc", RTFControlType::FLAG, RTFKeyword::FTNNRUC, 0 }, + { "ftnnzodiac", RTFControlType::FLAG, RTFKeyword::FTNNZODIAC, 0 }, + { "ftnnzodiacd", RTFControlType::FLAG, RTFKeyword::FTNNZODIACD, 0 }, + { "ftnnzodiacl", RTFControlType::FLAG, RTFKeyword::FTNNZODIACL, 0 }, + { "ftnrestart", RTFControlType::FLAG, RTFKeyword::FTNRESTART, 0 }, + { "ftnrstcont", RTFControlType::FLAG, RTFKeyword::FTNRSTCONT, 0 }, + { "ftnrstpg", RTFControlType::FLAG, RTFKeyword::FTNRSTPG, 0 }, + { "ftnsep", RTFControlType::DESTINATION, RTFKeyword::FTNSEP, 0 }, + { "ftnsepc", RTFControlType::DESTINATION, RTFKeyword::FTNSEPC, 0 }, + { "ftnstart", RTFControlType::VALUE, RTFKeyword::FTNSTART, 1 }, + { "ftntj", RTFControlType::FLAG, RTFKeyword::FTNTJ, 0 }, + { "fttruetype", RTFControlType::FLAG, RTFKeyword::FTTRUETYPE, 0 }, + { "fvaliddos", RTFControlType::FLAG, RTFKeyword::FVALIDDOS, 0 }, + { "fvalidhpfs", RTFControlType::FLAG, RTFKeyword::FVALIDHPFS, 0 }, + { "fvalidmac", RTFControlType::FLAG, RTFKeyword::FVALIDMAC, 0 }, + { "fvalidntfs", RTFControlType::FLAG, RTFKeyword::FVALIDNTFS, 0 }, + { "g", RTFControlType::DESTINATION, RTFKeyword::G, 0 }, + { "gcw", RTFControlType::VALUE, RTFKeyword::GCW, 0 }, + { "generator", RTFControlType::DESTINATION, RTFKeyword::GENERATOR, 0 }, + { "green", RTFControlType::VALUE, RTFKeyword::GREEN, 0 }, + { "grfdocevents", RTFControlType::VALUE, RTFKeyword::GRFDOCEVENTS, 0 }, + { "gridtbl", RTFControlType::DESTINATION, RTFKeyword::GRIDTBL, 0 }, + { "gutter", RTFControlType::VALUE, RTFKeyword::GUTTER, 0 }, + { "gutterprl", RTFControlType::FLAG, RTFKeyword::GUTTERPRL, 0 }, + { "guttersxn", RTFControlType::VALUE, RTFKeyword::GUTTERSXN, 0 }, + { "header", RTFControlType::DESTINATION, RTFKeyword::HEADER, 0 }, + { "headerf", RTFControlType::DESTINATION, RTFKeyword::HEADERF, 0 }, + { "headerl", RTFControlType::DESTINATION, RTFKeyword::HEADERL, 0 }, + { "headerr", RTFControlType::DESTINATION, RTFKeyword::HEADERR, 0 }, + { "headery", RTFControlType::VALUE, RTFKeyword::HEADERY, 720 }, + { "hich", RTFControlType::FLAG, RTFKeyword::HICH, 0 }, + { "highlight", RTFControlType::VALUE, RTFKeyword::HIGHLIGHT, 0 }, + { "hl", RTFControlType::DESTINATION, RTFKeyword::HL, 0 }, + { "hlfr", RTFControlType::DESTINATION, RTFKeyword::HLFR, 0 }, + { "hlinkbase", RTFControlType::DESTINATION, RTFKeyword::HLINKBASE, 0 }, + { "hlloc", RTFControlType::DESTINATION, RTFKeyword::HLLOC, 0 }, + { "hlsrc", RTFControlType::DESTINATION, RTFKeyword::HLSRC, 0 }, + { "horzdoc", RTFControlType::FLAG, RTFKeyword::HORZDOC, 0 }, + { "horzsect", RTFControlType::FLAG, RTFKeyword::HORZSECT, 0 }, + { "horzvert", RTFControlType::VALUE, RTFKeyword::HORZVERT, 0 }, + { "hr", RTFControlType::VALUE, RTFKeyword::HR, 0 }, + { "hres", RTFControlType::VALUE, RTFKeyword::HRES, 0 }, + { "hrule", RTFControlType::FLAG, RTFKeyword::HRULE, 0 }, + { "hsv", RTFControlType::DESTINATION, RTFKeyword::HSV, 0 }, + { "htmautsp", RTFControlType::FLAG, RTFKeyword::HTMAUTSP, 0 }, + { "htmlbase", RTFControlType::FLAG, RTFKeyword::HTMLBASE, 0 }, + { "htmlrtf", RTFControlType::TOGGLE, RTFKeyword::HTMLRTF, 1 }, + { "htmltag", RTFControlType::DESTINATION, RTFKeyword::HTMLTAG, 0 }, + { "hwelev", RTFControlType::FLAG, RTFKeyword::HWELEV, 0 }, + { "hyphauto", RTFControlType::TOGGLE, RTFKeyword::HYPHAUTO, 1 }, + { "hyphcaps", RTFControlType::TOGGLE, RTFKeyword::HYPHCAPS, 1 }, + { "hyphconsec", RTFControlType::VALUE, RTFKeyword::HYPHCONSEC, 0 }, + { "hyphhotz", RTFControlType::VALUE, RTFKeyword::HYPHHOTZ, 0 }, + { "hyphpar", RTFControlType::TOGGLE, RTFKeyword::HYPHPAR, 1 }, + { "i", RTFControlType::TOGGLE, RTFKeyword::I, 1 }, + { "id", RTFControlType::VALUE, RTFKeyword::ID, 0 }, + { "ignoremixedcontent", RTFControlType::VALUE, RTFKeyword::IGNOREMIXEDCONTENT, 0 }, + { "ilfomacatclnup", RTFControlType::VALUE, RTFKeyword::ILFOMACATCLNUP, 0 }, + { "ilvl", RTFControlType::VALUE, RTFKeyword::ILVL, 0 }, + { "impr", RTFControlType::TOGGLE, RTFKeyword::IMPR, 1 }, + { "indmirror", RTFControlType::FLAG, RTFKeyword::INDMIRROR, 0 }, + { "indrlsweleven", RTFControlType::FLAG, RTFKeyword::INDRLSWELEVEN, 0 }, + { "info", RTFControlType::DESTINATION, RTFKeyword::INFO, 0 }, + { "insrsid", RTFControlType::VALUE, RTFKeyword::INSRSID, 0 }, + { "intbl", RTFControlType::FLAG, RTFKeyword::INTBL, 0 }, + { "ipgp", RTFControlType::VALUE, RTFKeyword::IPGP, 0 }, + { "irowband", RTFControlType::VALUE, RTFKeyword::IROWBAND, 0 }, + { "irow", RTFControlType::VALUE, RTFKeyword::IROW, 0 }, + { "itap", RTFControlType::VALUE, RTFKeyword::ITAP, 1 }, + { "ixe", RTFControlType::FLAG, RTFKeyword::IXE, 0 }, + { "jcompress", RTFControlType::FLAG, RTFKeyword::JCOMPRESS, 0 }, + { "jexpand", RTFControlType::FLAG, RTFKeyword::JEXPAND, 0 }, + { "jis", RTFControlType::FLAG, RTFKeyword::JIS, 0 }, + { "jpegblip", RTFControlType::FLAG, RTFKeyword::JPEGBLIP, 0 }, + { "jsksu", RTFControlType::FLAG, RTFKeyword::JSKSU, 0 }, + { "keep", RTFControlType::FLAG, RTFKeyword::KEEP, 0 }, + { "keepn", RTFControlType::FLAG, RTFKeyword::KEEPN, 0 }, + { "kerning", RTFControlType::VALUE, RTFKeyword::KERNING, 0 }, + { "keycode", RTFControlType::DESTINATION, RTFKeyword::KEYCODE, 0 }, + { "keywords", RTFControlType::DESTINATION, RTFKeyword::KEYWORDS, 0 }, + { "krnprsnet", RTFControlType::FLAG, RTFKeyword::KRNPRSNET, 0 }, + { "ksulang", RTFControlType::VALUE, RTFKeyword::KSULANG, 0 }, + { "jclisttab", RTFControlType::FLAG, RTFKeyword::JCLISTTAB, 0 }, + { "landscape", RTFControlType::FLAG, RTFKeyword::LANDSCAPE, 0 }, + { "lang", RTFControlType::VALUE, RTFKeyword::LANG, 0 }, + { "langfe", RTFControlType::VALUE, RTFKeyword::LANGFE, 0 }, + { "langfenp", RTFControlType::VALUE, RTFKeyword::LANGFENP, 0 }, + { "langnp", RTFControlType::VALUE, RTFKeyword::LANGNP, 0 }, + { "lastrow", RTFControlType::FLAG, RTFKeyword::LASTROW, 0 }, + { "latentstyles", RTFControlType::DESTINATION, RTFKeyword::LATENTSTYLES, 0 }, + { "lbr", RTFControlType::VALUE, RTFKeyword::LBR, 0 }, + { "lchars", RTFControlType::DESTINATION, RTFKeyword::LCHARS, 0 }, + { "ldblquote", RTFControlType::SYMBOL, RTFKeyword::LDBLQUOTE, 0 }, + { "level", RTFControlType::VALUE, RTFKeyword::LEVEL, 0 }, + { "levelfollow", RTFControlType::VALUE, RTFKeyword::LEVELFOLLOW, 0 }, + { "levelindent", RTFControlType::VALUE, RTFKeyword::LEVELINDENT, 0 }, + { "leveljc", RTFControlType::VALUE, RTFKeyword::LEVELJC, 0 }, + { "leveljcn", RTFControlType::VALUE, RTFKeyword::LEVELJCN, 0 }, + { "levellegal", RTFControlType::VALUE, RTFKeyword::LEVELLEGAL, 0 }, + { "levelnfc", RTFControlType::VALUE, RTFKeyword::LEVELNFC, 0 }, + { "levelnfcn", RTFControlType::VALUE, RTFKeyword::LEVELNFCN, 0 }, + { "levelnorestart", RTFControlType::VALUE, RTFKeyword::LEVELNORESTART, 0 }, + { "levelnumbers", RTFControlType::DESTINATION, RTFKeyword::LEVELNUMBERS, 0 }, + { "levelold", RTFControlType::VALUE, RTFKeyword::LEVELOLD, 0 }, + { "levelpicture", RTFControlType::VALUE, RTFKeyword::LEVELPICTURE, 0 }, + { "levelpicturenosize", RTFControlType::FLAG, RTFKeyword::LEVELPICTURENOSIZE, 0 }, + { "levelprev", RTFControlType::VALUE, RTFKeyword::LEVELPREV, 0 }, + { "levelprevspace", RTFControlType::VALUE, RTFKeyword::LEVELPREVSPACE, 0 }, + { "levelspace", RTFControlType::VALUE, RTFKeyword::LEVELSPACE, 0 }, + { "levelstartat", RTFControlType::VALUE, RTFKeyword::LEVELSTARTAT, 0 }, + { "leveltemplateid", RTFControlType::VALUE, RTFKeyword::LEVELTEMPLATEID, 0 }, + { "leveltext", RTFControlType::DESTINATION, RTFKeyword::LEVELTEXT, 0 }, + { "lfolevel", RTFControlType::DESTINATION, RTFKeyword::LFOLEVEL, 0 }, + { "li", RTFControlType::VALUE, RTFKeyword::LI, 0 }, + { "line", RTFControlType::SYMBOL, RTFKeyword::LINE, 0 }, + { "linebetcol", RTFControlType::FLAG, RTFKeyword::LINEBETCOL, 0 }, + { "linecont", RTFControlType::FLAG, RTFKeyword::LINECONT, 0 }, + { "linemod", RTFControlType::VALUE, RTFKeyword::LINEMOD, 1 }, + { "lineppage", RTFControlType::FLAG, RTFKeyword::LINEPPAGE, 0 }, + { "linerestart", RTFControlType::FLAG, RTFKeyword::LINERESTART, 0 }, + { "linestart", RTFControlType::VALUE, RTFKeyword::LINESTART, 1 }, + { "linestarts", RTFControlType::VALUE, RTFKeyword::LINESTARTS, 1 }, + { "linex", RTFControlType::VALUE, RTFKeyword::LINEX, 360 }, + { "linkself", RTFControlType::FLAG, RTFKeyword::LINKSELF, 0 }, + { "linkstyles", RTFControlType::FLAG, RTFKeyword::LINKSTYLES, 0 }, + { "linkval", RTFControlType::DESTINATION, RTFKeyword::LINKVAL, 0 }, + { "lin", RTFControlType::VALUE, RTFKeyword::LIN, 0 }, + { "lisa", RTFControlType::VALUE, RTFKeyword::LISA, 0 }, + { "lisb", RTFControlType::VALUE, RTFKeyword::LISB, 0 }, + { "list", RTFControlType::DESTINATION, RTFKeyword::LIST, 0 }, + { "listhybrid", RTFControlType::FLAG, RTFKeyword::LISTHYBRID, 0 }, + { "listid", RTFControlType::VALUE, RTFKeyword::LISTID, 0 }, + { "listlevel", RTFControlType::DESTINATION, RTFKeyword::LISTLEVEL, 0 }, + { "listname", RTFControlType::DESTINATION, RTFKeyword::LISTNAME, 0 }, + { "listoverride", RTFControlType::DESTINATION, RTFKeyword::LISTOVERRIDE, 0 }, + { "listoverridecount", RTFControlType::VALUE, RTFKeyword::LISTOVERRIDECOUNT, 0 }, + { "listoverrideformat", RTFControlType::VALUE, RTFKeyword::LISTOVERRIDEFORMAT, 0 }, + { "listoverridestartat", RTFControlType::FLAG, RTFKeyword::LISTOVERRIDESTARTAT, 0 }, + { "listoverridetable", RTFControlType::DESTINATION, RTFKeyword::LISTOVERRIDETABLE, 0 }, + { "listpicture", RTFControlType::DESTINATION, RTFKeyword::LISTPICTURE, 0 }, + { "listrestarthdn", RTFControlType::VALUE, RTFKeyword::LISTRESTARTHDN, 0 }, + { "listsimple", RTFControlType::VALUE, RTFKeyword::LISTSIMPLE, 0 }, + { "liststyleid", RTFControlType::VALUE, RTFKeyword::LISTSTYLEID, 0 }, + { "liststylename", RTFControlType::DESTINATION, RTFKeyword::LISTSTYLENAME, 0 }, + { "listtable", RTFControlType::DESTINATION, RTFKeyword::LISTTABLE, 0 }, + { "listtemplateid", RTFControlType::VALUE, RTFKeyword::LISTTEMPLATEID, 0 }, + { "listtext", RTFControlType::DESTINATION, RTFKeyword::LISTTEXT, 0 }, + { "lnbrkrule", RTFControlType::FLAG, RTFKeyword::LNBRKRULE, 0 }, + { "lndscpsxn", RTFControlType::FLAG, RTFKeyword::LNDSCPSXN, 0 }, + { "lnongrid", RTFControlType::FLAG, RTFKeyword::LNONGRID, 0 }, + { "loch", RTFControlType::FLAG, RTFKeyword::LOCH, 0 }, + { "lquote", RTFControlType::SYMBOL, RTFKeyword::LQUOTE, 0 }, + { "ls", RTFControlType::VALUE, RTFKeyword::LS, 0 }, + { "lsdlocked", RTFControlType::VALUE, RTFKeyword::LSDLOCKED, 0 }, + { "lsdlockeddef", RTFControlType::VALUE, RTFKeyword::LSDLOCKEDDEF, 0 }, + { "lsdlockedexcept", RTFControlType::DESTINATION, RTFKeyword::LSDLOCKEDEXCEPT, 0 }, + { "lsdpriority", RTFControlType::VALUE, RTFKeyword::LSDPRIORITY, 0 }, + { "lsdprioritydef", RTFControlType::VALUE, RTFKeyword::LSDPRIORITYDEF, 0 }, + { "lsdqformat", RTFControlType::VALUE, RTFKeyword::LSDQFORMAT, 0 }, + { "lsdqformatdef", RTFControlType::VALUE, RTFKeyword::LSDQFORMATDEF, 0 }, + { "lsdsemihidden", RTFControlType::VALUE, RTFKeyword::LSDSEMIHIDDEN, 0 }, + { "lsdsemihiddendef", RTFControlType::VALUE, RTFKeyword::LSDSEMIHIDDENDEF, 0 }, + { "lsdstimax", RTFControlType::VALUE, RTFKeyword::LSDSTIMAX, 0 }, + { "lsdunhideused", RTFControlType::VALUE, RTFKeyword::LSDUNHIDEUSED, 0 }, + { "lsdunhideuseddef", RTFControlType::VALUE, RTFKeyword::LSDUNHIDEUSEDDEF, 0 }, + { "ltrch", RTFControlType::FLAG, RTFKeyword::LTRCH, 0 }, + { "ltrdoc", RTFControlType::FLAG, RTFKeyword::LTRDOC, 0 }, + { "ltrmark", RTFControlType::SYMBOL, RTFKeyword::LTRMARK, 0 }, + { "ltrpar", RTFControlType::FLAG, RTFKeyword::LTRPAR, 0 }, + { "ltrrow", RTFControlType::FLAG, RTFKeyword::LTRROW, 0 }, + { "ltrsect", RTFControlType::FLAG, RTFKeyword::LTRSECT, 0 }, + { "lvltentative", RTFControlType::FLAG, RTFKeyword::LVLTENTATIVE, 0 }, + { "lytcalctblwd", RTFControlType::FLAG, RTFKeyword::LYTCALCTBLWD, 0 }, + { "lytexcttp", RTFControlType::FLAG, RTFKeyword::LYTEXCTTP, 0 }, + { "lytprtmet", RTFControlType::FLAG, RTFKeyword::LYTPRTMET, 0 }, + { "lyttblrtgr", RTFControlType::FLAG, RTFKeyword::LYTTBLRTGR, 0 }, + { "mac", RTFControlType::FLAG, RTFKeyword::MAC, 0 }, + { "macc", RTFControlType::DESTINATION, RTFKeyword::MACC, 0 }, + { "maccPr", RTFControlType::DESTINATION, RTFKeyword::MACCPR, 0 }, + { "macpict", RTFControlType::FLAG, RTFKeyword::MACPICT, 0 }, + { "mailmerge", RTFControlType::DESTINATION, RTFKeyword::MAILMERGE, 0 }, + { "makebackup", RTFControlType::FLAG, RTFKeyword::MAKEBACKUP, 0 }, + { "maln", RTFControlType::DESTINATION, RTFKeyword::MALN, 0 }, + { "malnScr", RTFControlType::DESTINATION, RTFKeyword::MALNSCR, 0 }, + { "manager", RTFControlType::DESTINATION, RTFKeyword::MANAGER, 0 }, + { "margb", RTFControlType::VALUE, RTFKeyword::MARGB, 1440 }, + { "margbsxn", RTFControlType::VALUE, RTFKeyword::MARGBSXN, 0 }, + { "margl", RTFControlType::VALUE, RTFKeyword::MARGL, 1800 }, + { "marglsxn", RTFControlType::VALUE, RTFKeyword::MARGLSXN, 0 }, + { "margmirror", RTFControlType::FLAG, RTFKeyword::MARGMIRROR, 0 }, + { "margmirsxn", RTFControlType::FLAG, RTFKeyword::MARGMIRSXN, 0 }, + { "margPr", RTFControlType::DESTINATION, RTFKeyword::MARGPR, 0 }, + { "margr", RTFControlType::VALUE, RTFKeyword::MARGR, 1800 }, + { "margrsxn", RTFControlType::VALUE, RTFKeyword::MARGRSXN, 0 }, + { "margSz", RTFControlType::VALUE, RTFKeyword::MARGSZ, 0 }, + { "margt", RTFControlType::VALUE, RTFKeyword::MARGT, 1440 }, + { "margtsxn", RTFControlType::VALUE, RTFKeyword::MARGTSXN, 0 }, + { "mbar", RTFControlType::DESTINATION, RTFKeyword::MBAR, 0 }, + { "mbarPr", RTFControlType::DESTINATION, RTFKeyword::MBARPR, 0 }, + { "mbaseJc", RTFControlType::DESTINATION, RTFKeyword::MBASEJC, 0 }, + { "mbegChr", RTFControlType::DESTINATION, RTFKeyword::MBEGCHR, 0 }, + { "mborderBox", RTFControlType::DESTINATION, RTFKeyword::MBORDERBOX, 0 }, + { "mborderBoxPr", RTFControlType::DESTINATION, RTFKeyword::MBORDERBOXPR, 0 }, + { "mbox", RTFControlType::DESTINATION, RTFKeyword::MBOX, 0 }, + { "mboxPr", RTFControlType::DESTINATION, RTFKeyword::MBOXPR, 0 }, + { "mbrk", RTFControlType::VALUE, RTFKeyword::MBRK, 0 }, + { "mbrkBin", RTFControlType::VALUE, RTFKeyword::MBRKBIN, 0 }, + { "mbrkBinSub", RTFControlType::VALUE, RTFKeyword::MBRKBINSUB, 0 }, + { "mcGp", RTFControlType::VALUE, RTFKeyword::MCGP, 0 }, + { "mcGpRule", RTFControlType::VALUE, RTFKeyword::MCGPRULE, 0 }, + { "mchr", RTFControlType::DESTINATION, RTFKeyword::MCHR, 0 }, + { "mcount", RTFControlType::DESTINATION, RTFKeyword::MCOUNT, 0 }, + { "mcSp", RTFControlType::VALUE, RTFKeyword::MCSP, 0 }, + { "mctrlPr", RTFControlType::DESTINATION, RTFKeyword::MCTRLPR, 0 }, + { "md", RTFControlType::DESTINATION, RTFKeyword::MD, 0 }, + { "mdefJc", RTFControlType::VALUE, RTFKeyword::MDEFJC, 0 }, + { "mdeg", RTFControlType::DESTINATION, RTFKeyword::MDEG, 0 }, + { "mdegHide", RTFControlType::DESTINATION, RTFKeyword::MDEGHIDE, 0 }, + { "mden", RTFControlType::DESTINATION, RTFKeyword::MDEN, 0 }, + { "mdiff", RTFControlType::DESTINATION, RTFKeyword::MDIFF, 0 }, + { "mdiffSty", RTFControlType::VALUE, RTFKeyword::MDIFFSTY, 0 }, + { "mdispdef", RTFControlType::VALUE, RTFKeyword::MDISPDEF, 1 }, + { "mdPr", RTFControlType::DESTINATION, RTFKeyword::MDPR, 0 }, + { "me", RTFControlType::DESTINATION, RTFKeyword::ME, 0 }, + { "mendChr", RTFControlType::DESTINATION, RTFKeyword::MENDCHR, 0 }, + { "meqArr", RTFControlType::DESTINATION, RTFKeyword::MEQARR, 0 }, + { "meqArrPr", RTFControlType::DESTINATION, RTFKeyword::MEQARRPR, 0 }, + { "mf", RTFControlType::DESTINATION, RTFKeyword::MF, 0 }, + { "mfName", RTFControlType::DESTINATION, RTFKeyword::MFNAME, 0 }, + { "mfPr", RTFControlType::DESTINATION, RTFKeyword::MFPR, 0 }, + { "mfunc", RTFControlType::DESTINATION, RTFKeyword::MFUNC, 0 }, + { "mfuncPr", RTFControlType::DESTINATION, RTFKeyword::MFUNCPR, 0 }, + { "mgroupChr", RTFControlType::DESTINATION, RTFKeyword::MGROUPCHR, 0 }, + { "mgroupChrPr", RTFControlType::DESTINATION, RTFKeyword::MGROUPCHRPR, 0 }, + { "mgrow", RTFControlType::DESTINATION, RTFKeyword::MGROW, 0 }, + { "mhideBot", RTFControlType::DESTINATION, RTFKeyword::MHIDEBOT, 0 }, + { "mhideLeft", RTFControlType::DESTINATION, RTFKeyword::MHIDELEFT, 0 }, + { "mhideRight", RTFControlType::DESTINATION, RTFKeyword::MHIDERIGHT, 0 }, + { "mhideTop", RTFControlType::DESTINATION, RTFKeyword::MHIDETOP, 0 }, + { "mhtmltag", RTFControlType::DESTINATION, RTFKeyword::MHTMLTAG, 0 }, + { "min", RTFControlType::VALUE, RTFKeyword::MIN, 0 }, + { "minterSp", RTFControlType::VALUE, RTFKeyword::MINTERSP, 0 }, + { "mintLim", RTFControlType::VALUE, RTFKeyword::MINTLIM, 0 }, + { "mintraSp", RTFControlType::VALUE, RTFKeyword::MINTRASP, 0 }, + { "mjc", RTFControlType::VALUE, RTFKeyword::MJC, 0 }, + { "mlim", RTFControlType::DESTINATION, RTFKeyword::MLIM, 0 }, + { "mlimloc", RTFControlType::DESTINATION, RTFKeyword::MLIMLOC, 0 }, + { "mlimLoc", RTFControlType::DESTINATION, RTFKeyword::MLIMLOC, 0 }, + { "mlimlow", RTFControlType::DESTINATION, RTFKeyword::MLIMLOW, 0 }, + { "mlimLow", RTFControlType::DESTINATION, RTFKeyword::MLIMLOW, 0 }, + { "mlimlowPr", RTFControlType::DESTINATION, RTFKeyword::MLIMLOWPR, 0 }, + { "mlimLowPr", RTFControlType::DESTINATION, RTFKeyword::MLIMLOWPR, 0 }, + { "mlimupp", RTFControlType::DESTINATION, RTFKeyword::MLIMUPP, 0 }, + { "mlimUpp", RTFControlType::DESTINATION, RTFKeyword::MLIMUPP, 0 }, + { "mlimuppPr", RTFControlType::DESTINATION, RTFKeyword::MLIMUPPPR, 0 }, + { "mlimUppPr", RTFControlType::DESTINATION, RTFKeyword::MLIMUPPPR, 0 }, + { "mlit", RTFControlType::FLAG, RTFKeyword::MLIT, 0 }, + { "mlMargin", RTFControlType::VALUE, RTFKeyword::MLMARGIN, 0 }, + { "mm", RTFControlType::DESTINATION, RTFKeyword::MM, 0 }, + { "mmaddfieldname", RTFControlType::DESTINATION, RTFKeyword::MMADDFIELDNAME, 0 }, + { "mmath", RTFControlType::DESTINATION, RTFKeyword::MMATH, 0 }, + { "mmathFont", RTFControlType::VALUE, RTFKeyword::MMATHFONT, 0 }, + { "mmathPict", RTFControlType::DESTINATION, RTFKeyword::MMATHPICT, 0 }, + { "mmathPr", RTFControlType::DESTINATION, RTFKeyword::MMATHPR, 0 }, + { "mmattach", RTFControlType::FLAG, RTFKeyword::MMATTACH, 0 }, + { "mmaxdist", RTFControlType::DESTINATION, RTFKeyword::MMAXDIST, 0 }, + { "mmblanklines", RTFControlType::FLAG, RTFKeyword::MMBLANKLINES, 0 }, + { "mmc", RTFControlType::DESTINATION, RTFKeyword::MMC, 0 }, + { "mmcJc", RTFControlType::DESTINATION, RTFKeyword::MMCJC, 0 }, + { "mmconnectstr", RTFControlType::DESTINATION, RTFKeyword::MMCONNECTSTR, 0 }, + { "mmconnectstrdata", RTFControlType::DESTINATION, RTFKeyword::MMCONNECTSTRDATA, 0 }, + { "mmcPr", RTFControlType::DESTINATION, RTFKeyword::MMCPR, 0 }, + { "mmcs", RTFControlType::DESTINATION, RTFKeyword::MMCS, 0 }, + { "mmdatasource", RTFControlType::DESTINATION, RTFKeyword::MMDATASOURCE, 0 }, + { "mmdatatypeaccess", RTFControlType::FLAG, RTFKeyword::MMDATATYPEACCESS, 0 }, + { "mmdatatypeexcel", RTFControlType::FLAG, RTFKeyword::MMDATATYPEEXCEL, 0 }, + { "mmdatatypefile", RTFControlType::FLAG, RTFKeyword::MMDATATYPEFILE, 0 }, + { "mmdatatypeodbc", RTFControlType::FLAG, RTFKeyword::MMDATATYPEODBC, 0 }, + { "mmdatatypeodso", RTFControlType::FLAG, RTFKeyword::MMDATATYPEODSO, 0 }, + { "mmdatatypeqt", RTFControlType::FLAG, RTFKeyword::MMDATATYPEQT, 0 }, + { "mmdefaultsql", RTFControlType::FLAG, RTFKeyword::MMDEFAULTSQL, 0 }, + { "mmdestemail", RTFControlType::FLAG, RTFKeyword::MMDESTEMAIL, 0 }, + { "mmdestfax", RTFControlType::FLAG, RTFKeyword::MMDESTFAX, 0 }, + { "mmdestnewdoc", RTFControlType::FLAG, RTFKeyword::MMDESTNEWDOC, 0 }, + { "mmdestprinter", RTFControlType::FLAG, RTFKeyword::MMDESTPRINTER, 0 }, + { "mmerrors", RTFControlType::VALUE, RTFKeyword::MMERRORS, 0 }, + { "mmfttypeaddress", RTFControlType::FLAG, RTFKeyword::MMFTTYPEADDRESS, 0 }, + { "mmfttypebarcode", RTFControlType::FLAG, RTFKeyword::MMFTTYPEBARCODE, 0 }, + { "mmfttypedbcolumn", RTFControlType::FLAG, RTFKeyword::MMFTTYPEDBCOLUMN, 0 }, + { "mmfttypemapped", RTFControlType::FLAG, RTFKeyword::MMFTTYPEMAPPED, 0 }, + { "mmfttypenull", RTFControlType::FLAG, RTFKeyword::MMFTTYPENULL, 0 }, + { "mmfttypesalutation", RTFControlType::FLAG, RTFKeyword::MMFTTYPESALUTATION, 0 }, + { "mmheadersource", RTFControlType::DESTINATION, RTFKeyword::MMHEADERSOURCE, 0 }, + { "mmjdsotype", RTFControlType::VALUE, RTFKeyword::MMJDSOTYPE, 0 }, + { "mmlinktoquery", RTFControlType::FLAG, RTFKeyword::MMLINKTOQUERY, 0 }, + { "mmmailsubject", RTFControlType::DESTINATION, RTFKeyword::MMMAILSUBJECT, 0 }, + { "mmmaintypecatalog", RTFControlType::FLAG, RTFKeyword::MMMAINTYPECATALOG, 0 }, + { "mmmaintypeemail", RTFControlType::FLAG, RTFKeyword::MMMAINTYPEEMAIL, 0 }, + { "mmmaintypeenvelopes", RTFControlType::FLAG, RTFKeyword::MMMAINTYPEENVELOPES, 0 }, + { "mmmaintypefax", RTFControlType::FLAG, RTFKeyword::MMMAINTYPEFAX, 0 }, + { "mmmaintypelabels", RTFControlType::FLAG, RTFKeyword::MMMAINTYPELABELS, 0 }, + { "mmmaintypeletters", RTFControlType::FLAG, RTFKeyword::MMMAINTYPELETTERS, 0 }, + { "mmodso", RTFControlType::DESTINATION, RTFKeyword::MMODSO, 0 }, + { "mmodsoactive", RTFControlType::VALUE, RTFKeyword::MMODSOACTIVE, 0 }, + { "mmodsocoldelim", RTFControlType::VALUE, RTFKeyword::MMODSOCOLDELIM, 0 }, + { "mmodsocolumn", RTFControlType::VALUE, RTFKeyword::MMODSOCOLUMN, 0 }, + { "mmodsodynaddr", RTFControlType::VALUE, RTFKeyword::MMODSODYNADDR, 0 }, + { "mmodsofhdr", RTFControlType::VALUE, RTFKeyword::MMODSOFHDR, 0 }, + { "mmodsofilter", RTFControlType::DESTINATION, RTFKeyword::MMODSOFILTER, 0 }, + { "mmodsofldmpdata", RTFControlType::DESTINATION, RTFKeyword::MMODSOFLDMPDATA, 0 }, + { "mmodsofmcolumn", RTFControlType::VALUE, RTFKeyword::MMODSOFMCOLUMN, 0 }, + { "mmodsohash", RTFControlType::VALUE, RTFKeyword::MMODSOHASH, 0 }, + { "mmodsolid", RTFControlType::VALUE, RTFKeyword::MMODSOLID, 0 }, + { "mmodsomappedname", RTFControlType::DESTINATION, RTFKeyword::MMODSOMAPPEDNAME, 0 }, + { "mmodsoname", RTFControlType::DESTINATION, RTFKeyword::MMODSONAME, 0 }, + { "mmodsorecipdata", RTFControlType::DESTINATION, RTFKeyword::MMODSORECIPDATA, 0 }, + { "mmodsosort", RTFControlType::DESTINATION, RTFKeyword::MMODSOSORT, 0 }, + { "mmodsosrc", RTFControlType::DESTINATION, RTFKeyword::MMODSOSRC, 0 }, + { "mmodsotable", RTFControlType::DESTINATION, RTFKeyword::MMODSOTABLE, 0 }, + { "mmodsoudl", RTFControlType::DESTINATION, RTFKeyword::MMODSOUDL, 0 }, + { "mmodsoudldata", RTFControlType::DESTINATION, RTFKeyword::MMODSOUDLDATA, 0 }, + { "mmodsouniquetag", RTFControlType::DESTINATION, RTFKeyword::MMODSOUNIQUETAG, 0 }, + { "mmPr", RTFControlType::DESTINATION, RTFKeyword::MMPR, 0 }, + { "mmquery", RTFControlType::DESTINATION, RTFKeyword::MMQUERY, 0 }, + { "mmr", RTFControlType::DESTINATION, RTFKeyword::MMR, 0 }, + { "mmreccur", RTFControlType::VALUE, RTFKeyword::MMRECCUR, 0 }, + { "mmshowdata", RTFControlType::FLAG, RTFKeyword::MMSHOWDATA, 0 }, + { "mnary", RTFControlType::DESTINATION, RTFKeyword::MNARY, 0 }, + { "mnaryLim", RTFControlType::VALUE, RTFKeyword::MNARYLIM, 0 }, + { "mnaryPr", RTFControlType::DESTINATION, RTFKeyword::MNARYPR, 0 }, + { "mnoBreak", RTFControlType::DESTINATION, RTFKeyword::MNOBREAK, 0 }, + { "mnor", RTFControlType::FLAG, RTFKeyword::MNOR, 0 }, + { "mnum", RTFControlType::DESTINATION, RTFKeyword::MNUM, 0 }, + { "mo", RTFControlType::VALUE, RTFKeyword::MO, 0 }, + { "mobjDist", RTFControlType::DESTINATION, RTFKeyword::MOBJDIST, 0 }, + { "moMath", RTFControlType::DESTINATION, RTFKeyword::MOMATH, 0 }, + { "moMathPara", RTFControlType::DESTINATION, RTFKeyword::MOMATHPARA, 0 }, + { "moMathParaPr", RTFControlType::DESTINATION, RTFKeyword::MOMATHPARAPR, 0 }, + { "mopEmu", RTFControlType::DESTINATION, RTFKeyword::MOPEMU, 0 }, + { "mphant", RTFControlType::DESTINATION, RTFKeyword::MPHANT, 0 }, + { "mphantPr", RTFControlType::DESTINATION, RTFKeyword::MPHANTPR, 0 }, + { "mplcHide", RTFControlType::DESTINATION, RTFKeyword::MPLCHIDE, 0 }, + { "mpos", RTFControlType::DESTINATION, RTFKeyword::MPOS, 0 }, + { "mpostSp", RTFControlType::VALUE, RTFKeyword::MPOSTSP, 0 }, + { "mpreSp", RTFControlType::VALUE, RTFKeyword::MPRESP, 0 }, + { "mr", RTFControlType::DESTINATION, RTFKeyword::MR, 0 }, + { "mrad", RTFControlType::DESTINATION, RTFKeyword::MRAD, 0 }, + { "mradPr", RTFControlType::DESTINATION, RTFKeyword::MRADPR, 0 }, + { "mrMargin", RTFControlType::VALUE, RTFKeyword::MRMARGIN, 0 }, + { "mrPr", RTFControlType::DESTINATION, RTFKeyword::MRPR, 0 }, + { "mrSp", RTFControlType::VALUE, RTFKeyword::MRSP, 0 }, + { "mrSpRule", RTFControlType::VALUE, RTFKeyword::MRSPRULE, 0 }, + { "mscr", RTFControlType::VALUE, RTFKeyword::MSCR, 0 }, + { "msepChr", RTFControlType::DESTINATION, RTFKeyword::MSEPCHR, 0 }, + { "mshow", RTFControlType::DESTINATION, RTFKeyword::MSHOW, 0 }, + { "mshp", RTFControlType::DESTINATION, RTFKeyword::MSHP, 0 }, + { "msmallFrac", RTFControlType::VALUE, RTFKeyword::MSMALLFRAC, 0 }, + { "msmcap", RTFControlType::FLAG, RTFKeyword::MSMCAP, 0 }, + { "msPre", RTFControlType::DESTINATION, RTFKeyword::MSPRE, 0 }, + { "msPrePr", RTFControlType::DESTINATION, RTFKeyword::MSPREPR, 0 }, + { "msSub", RTFControlType::DESTINATION, RTFKeyword::MSSUB, 0 }, + { "msSubPr", RTFControlType::DESTINATION, RTFKeyword::MSSUBPR, 0 }, + { "msSubSup", RTFControlType::DESTINATION, RTFKeyword::MSSUBSUP, 0 }, + { "msSubSupPr", RTFControlType::DESTINATION, RTFKeyword::MSSUBSUPPR, 0 }, + { "msSup", RTFControlType::DESTINATION, RTFKeyword::MSSUP, 0 }, + { "msSupPr", RTFControlType::DESTINATION, RTFKeyword::MSSUPPR, 0 }, + { "mstrikeBLTR", RTFControlType::DESTINATION, RTFKeyword::MSTRIKEBLTR, 0 }, + { "mstrikeH", RTFControlType::DESTINATION, RTFKeyword::MSTRIKEH, 0 }, + { "mstrikeTLBR", RTFControlType::DESTINATION, RTFKeyword::MSTRIKETLBR, 0 }, + { "mstrikeV", RTFControlType::DESTINATION, RTFKeyword::MSTRIKEV, 0 }, + { "msty", RTFControlType::VALUE, RTFKeyword::MSTY, 0 }, + { "msub", RTFControlType::DESTINATION, RTFKeyword::MSUB, 0 }, + { "msubHide", RTFControlType::DESTINATION, RTFKeyword::MSUBHIDE, 0 }, + { "msup", RTFControlType::DESTINATION, RTFKeyword::MSUP, 0 }, + { "msupHide", RTFControlType::DESTINATION, RTFKeyword::MSUPHIDE, 0 }, + { "mtransp", RTFControlType::DESTINATION, RTFKeyword::MTRANSP, 0 }, + { "mtype", RTFControlType::DESTINATION, RTFKeyword::MTYPE, 0 }, + { "muser", RTFControlType::FLAG, RTFKeyword::MUSER, 0 }, + { "mvauth", RTFControlType::VALUE, RTFKeyword::MVAUTH, 0 }, + { "mvdate", RTFControlType::VALUE, RTFKeyword::MVDATE, 0 }, + { "mvertJc", RTFControlType::DESTINATION, RTFKeyword::MVERTJC, 0 }, + { "mvf", RTFControlType::FLAG, RTFKeyword::MVF, 0 }, + { "mvfmf", RTFControlType::DESTINATION, RTFKeyword::MVFMF, 0 }, + { "mvfml", RTFControlType::DESTINATION, RTFKeyword::MVFML, 0 }, + { "mvt", RTFControlType::FLAG, RTFKeyword::MVT, 0 }, + { "mvtof", RTFControlType::DESTINATION, RTFKeyword::MVTOF, 0 }, + { "mvtol", RTFControlType::DESTINATION, RTFKeyword::MVTOL, 0 }, + { "mwrapIndent", RTFControlType::VALUE, RTFKeyword::MWRAPINDENT, 1440 }, + { "mwrapRight", RTFControlType::VALUE, RTFKeyword::MWRAPRIGHT, 0 }, + { "mzeroAsc", RTFControlType::DESTINATION, RTFKeyword::MZEROASC, 0 }, + { "mzeroDesc", RTFControlType::DESTINATION, RTFKeyword::MZERODESC, 0 }, + { "mzeroWid", RTFControlType::DESTINATION, RTFKeyword::MZEROWID, 0 }, + { "nestcell", RTFControlType::SYMBOL, RTFKeyword::NESTCELL, 0 }, + { "nestrow", RTFControlType::SYMBOL, RTFKeyword::NESTROW, 0 }, + { "nesttableprops", RTFControlType::DESTINATION, RTFKeyword::NESTTABLEPROPS, 0 }, + { "newtblstyruls", RTFControlType::FLAG, RTFKeyword::NEWTBLSTYRULS, 0 }, + { "nextfile", RTFControlType::DESTINATION, RTFKeyword::NEXTFILE, 0 }, + { "noafcnsttbl", RTFControlType::FLAG, RTFKeyword::NOAFCNSTTBL, 0 }, + { "nobrkwrptbl", RTFControlType::FLAG, RTFKeyword::NOBRKWRPTBL, 0 }, + { "nocolbal", RTFControlType::FLAG, RTFKeyword::NOCOLBAL, 0 }, + { "nocompatoptions", RTFControlType::FLAG, RTFKeyword::NOCOMPATOPTIONS, 0 }, + { "nocwrap", RTFControlType::FLAG, RTFKeyword::NOCWRAP, 0 }, + { "nocxsptable", RTFControlType::FLAG, RTFKeyword::NOCXSPTABLE, 0 }, + { "noextrasprl", RTFControlType::FLAG, RTFKeyword::NOEXTRASPRL, 0 }, + { "nofchars", RTFControlType::VALUE, RTFKeyword::NOFCHARS, 0 }, + { "nofcharsws", RTFControlType::VALUE, RTFKeyword::NOFCHARSWS, 0 }, + { "nofeaturethrottle", RTFControlType::FLAG, RTFKeyword::NOFEATURETHROTTLE, 0 }, + { "nofpages", RTFControlType::VALUE, RTFKeyword::NOFPAGES, 0 }, + { "nofwords", RTFControlType::VALUE, RTFKeyword::NOFWORDS, 0 }, + { "nogrowautofit", RTFControlType::FLAG, RTFKeyword::NOGROWAUTOFIT, 0 }, + { "noindnmbrts", RTFControlType::FLAG, RTFKeyword::NOINDNMBRTS, 0 }, + { "nojkernpunct", RTFControlType::FLAG, RTFKeyword::NOJKERNPUNCT, 0 }, + { "nolead", RTFControlType::FLAG, RTFKeyword::NOLEAD, 0 }, + { "noline", RTFControlType::FLAG, RTFKeyword::NOLINE, 0 }, + { "nolnhtadjtbl", RTFControlType::FLAG, RTFKeyword::NOLNHTADJTBL, 0 }, + { "nonesttables", RTFControlType::DESTINATION, RTFKeyword::NONESTTABLES, 0 }, + { "nonshppict", RTFControlType::FLAG, RTFKeyword::NONSHPPICT, 0 }, + { "nooverflow", RTFControlType::FLAG, RTFKeyword::NOOVERFLOW, 0 }, + { "noproof", RTFControlType::FLAG, RTFKeyword::NOPROOF, 0 }, + { "noqfpromote", RTFControlType::FLAG, RTFKeyword::NOQFPROMOTE, 0 }, + { "nosectexpand", RTFControlType::FLAG, RTFKeyword::NOSECTEXPAND, 0 }, + { "nosnaplinegrid", RTFControlType::FLAG, RTFKeyword::NOSNAPLINEGRID, 0 }, + { "nospaceforul", RTFControlType::FLAG, RTFKeyword::NOSPACEFORUL, 0 }, + { "nosupersub", RTFControlType::FLAG, RTFKeyword::NOSUPERSUB, 0 }, + { "notabind", RTFControlType::FLAG, RTFKeyword::NOTABIND, 0 }, + { "notbrkcnstfrctbl", RTFControlType::FLAG, RTFKeyword::NOTBRKCNSTFRCTBL, 0 }, + { "notcvasp", RTFControlType::FLAG, RTFKeyword::NOTCVASP, 0 }, + { "notvatxbx", RTFControlType::FLAG, RTFKeyword::NOTVATXBX, 0 }, + { "nouicompat", RTFControlType::FLAG, RTFKeyword::NOUICOMPAT, 0 }, + { "noultrlspc", RTFControlType::FLAG, RTFKeyword::NOULTRLSPC, 0 }, + { "nowidctlpar", RTFControlType::FLAG, RTFKeyword::NOWIDCTLPAR, 0 }, + { "nowrap", RTFControlType::FLAG, RTFKeyword::NOWRAP, 0 }, + { "nowwrap", RTFControlType::FLAG, RTFKeyword::NOWWRAP, 0 }, + { "noxlattoyen", RTFControlType::FLAG, RTFKeyword::NOXLATTOYEN, 0 }, + { "objalias", RTFControlType::DESTINATION, RTFKeyword::OBJALIAS, 0 }, + { "objalign", RTFControlType::VALUE, RTFKeyword::OBJALIGN, 0 }, + { "objattph", RTFControlType::FLAG, RTFKeyword::OBJATTPH, 0 }, + { "objautlink", RTFControlType::FLAG, RTFKeyword::OBJAUTLINK, 0 }, + { "objclass", RTFControlType::DESTINATION, RTFKeyword::OBJCLASS, 0 }, + { "objcropb", RTFControlType::VALUE, RTFKeyword::OBJCROPB, 0 }, + { "objcropl", RTFControlType::VALUE, RTFKeyword::OBJCROPL, 0 }, + { "objcropr", RTFControlType::VALUE, RTFKeyword::OBJCROPR, 0 }, + { "objcropt", RTFControlType::VALUE, RTFKeyword::OBJCROPT, 0 }, + { "objdata", RTFControlType::DESTINATION, RTFKeyword::OBJDATA, 0 }, + { "object", RTFControlType::DESTINATION, RTFKeyword::OBJECT, 0 }, + { "objemb", RTFControlType::FLAG, RTFKeyword::OBJEMB, 0 }, + { "objh", RTFControlType::VALUE, RTFKeyword::OBJH, 0 }, + { "objhtml", RTFControlType::FLAG, RTFKeyword::OBJHTML, 0 }, + { "objicemb", RTFControlType::FLAG, RTFKeyword::OBJICEMB, 0 }, + { "objlink", RTFControlType::FLAG, RTFKeyword::OBJLINK, 0 }, + { "objlock", RTFControlType::FLAG, RTFKeyword::OBJLOCK, 0 }, + { "objname", RTFControlType::DESTINATION, RTFKeyword::OBJNAME, 0 }, + { "objocx", RTFControlType::FLAG, RTFKeyword::OBJOCX, 0 }, + { "objpub", RTFControlType::FLAG, RTFKeyword::OBJPUB, 0 }, + { "objscalex", RTFControlType::VALUE, RTFKeyword::OBJSCALEX, 0 }, + { "objscaley", RTFControlType::VALUE, RTFKeyword::OBJSCALEY, 0 }, + { "objsect", RTFControlType::DESTINATION, RTFKeyword::OBJSECT, 0 }, + { "objsetsize", RTFControlType::FLAG, RTFKeyword::OBJSETSIZE, 0 }, + { "objsub", RTFControlType::FLAG, RTFKeyword::OBJSUB, 0 }, + { "objtime", RTFControlType::DESTINATION, RTFKeyword::OBJTIME, 0 }, + { "objtransy", RTFControlType::VALUE, RTFKeyword::OBJTRANSY, 0 }, + { "objupdate", RTFControlType::FLAG, RTFKeyword::OBJUPDATE, 0 }, + { "objw", RTFControlType::VALUE, RTFKeyword::OBJW, 0 }, + { "ogutter", RTFControlType::VALUE, RTFKeyword::OGUTTER, 0 }, + { "oldas", RTFControlType::FLAG, RTFKeyword::OLDAS, 0 }, + { "oldcprops", RTFControlType::DESTINATION, RTFKeyword::OLDCPROPS, 0 }, + { "oldlinewrap", RTFControlType::FLAG, RTFKeyword::OLDLINEWRAP, 0 }, + { "oldpprops", RTFControlType::DESTINATION, RTFKeyword::OLDPPROPS, 0 }, + { "oldsprops", RTFControlType::DESTINATION, RTFKeyword::OLDSPROPS, 0 }, + { "oldtprops", RTFControlType::DESTINATION, RTFKeyword::OLDTPROPS, 0 }, + { "oleclsid", RTFControlType::DESTINATION, RTFKeyword::OLECLSID, 0 }, + { "operator", RTFControlType::DESTINATION, RTFKeyword::OPERATOR, 0 }, + { "otblrul", RTFControlType::FLAG, RTFKeyword::OTBLRUL, 0 }, + { "outl", RTFControlType::TOGGLE, RTFKeyword::OUTL, 1 }, + { "outlinelevel", RTFControlType::VALUE, RTFKeyword::OUTLINELEVEL, 0 }, + { "overlay", RTFControlType::FLAG, RTFKeyword::OVERLAY, 0 }, + { "page", RTFControlType::SYMBOL, RTFKeyword::PAGE, 0 }, + { "pagebb", RTFControlType::FLAG, RTFKeyword::PAGEBB, 0 }, + { "panose", RTFControlType::DESTINATION, RTFKeyword::PANOSE, 0 }, + { "paperh", RTFControlType::VALUE, RTFKeyword::PAPERH, 15840 }, + { "paperw", RTFControlType::VALUE, RTFKeyword::PAPERW, 12240 }, + { "par", RTFControlType::SYMBOL, RTFKeyword::PAR, 0 }, + { "pararsid", RTFControlType::VALUE, RTFKeyword::PARARSID, 0 }, + { "pard", RTFControlType::FLAG, RTFKeyword::PARD, 0 }, + { "password", RTFControlType::DESTINATION, RTFKeyword::PASSWORD, 0 }, + { "passwordhash", RTFControlType::DESTINATION, RTFKeyword::PASSWORDHASH, 0 }, + { "pc", RTFControlType::FLAG, RTFKeyword::PC, 0 }, + { "pca", RTFControlType::FLAG, RTFKeyword::PCA, 0 }, + { "pgbrdrb", RTFControlType::FLAG, RTFKeyword::PGBRDRB, 0 }, + { "pgbrdrfoot", RTFControlType::FLAG, RTFKeyword::PGBRDRFOOT, 0 }, + { "pgbrdrhead", RTFControlType::FLAG, RTFKeyword::PGBRDRHEAD, 0 }, + { "pgbrdrl", RTFControlType::FLAG, RTFKeyword::PGBRDRL, 0 }, + { "pgbrdropt", RTFControlType::VALUE, RTFKeyword::PGBRDROPT, 0 }, + { "pgbrdrr", RTFControlType::FLAG, RTFKeyword::PGBRDRR, 0 }, + { "pgbrdrsnap", RTFControlType::FLAG, RTFKeyword::PGBRDRSNAP, 0 }, + { "pgbrdrt", RTFControlType::FLAG, RTFKeyword::PGBRDRT, 0 }, + { "pghsxn", RTFControlType::VALUE, RTFKeyword::PGHSXN, 0 }, + { "pgnbidia", RTFControlType::FLAG, RTFKeyword::PGNBIDIA, 0 }, + { "pgnbidib", RTFControlType::FLAG, RTFKeyword::PGNBIDIB, 0 }, + { "pgnchosung", RTFControlType::FLAG, RTFKeyword::PGNCHOSUNG, 0 }, + { "pgncnum", RTFControlType::FLAG, RTFKeyword::PGNCNUM, 0 }, + { "pgncont", RTFControlType::FLAG, RTFKeyword::PGNCONT, 0 }, + { "pgndbnum", RTFControlType::FLAG, RTFKeyword::PGNDBNUM, 0 }, + { "pgndbnumd", RTFControlType::FLAG, RTFKeyword::PGNDBNUMD, 0 }, + { "pgndbnumk", RTFControlType::FLAG, RTFKeyword::PGNDBNUMK, 0 }, + { "pgndbnumt", RTFControlType::FLAG, RTFKeyword::PGNDBNUMT, 0 }, + { "pgndec", RTFControlType::FLAG, RTFKeyword::PGNDEC, 0 }, + { "pgndecd", RTFControlType::FLAG, RTFKeyword::PGNDECD, 0 }, + { "pgnganada", RTFControlType::FLAG, RTFKeyword::PGNGANADA, 0 }, + { "pgngbnum", RTFControlType::FLAG, RTFKeyword::PGNGBNUM, 0 }, + { "pgngbnumd", RTFControlType::FLAG, RTFKeyword::PGNGBNUMD, 0 }, + { "pgngbnumk", RTFControlType::FLAG, RTFKeyword::PGNGBNUMK, 0 }, + { "pgngbnuml", RTFControlType::FLAG, RTFKeyword::PGNGBNUML, 0 }, + { "pgnhindia", RTFControlType::FLAG, RTFKeyword::PGNHINDIA, 0 }, + { "pgnhindib", RTFControlType::FLAG, RTFKeyword::PGNHINDIB, 0 }, + { "pgnhindic", RTFControlType::FLAG, RTFKeyword::PGNHINDIC, 0 }, + { "pgnhindid", RTFControlType::FLAG, RTFKeyword::PGNHINDID, 0 }, + { "pgnhn", RTFControlType::VALUE, RTFKeyword::PGNHN, 0 }, + { "pgnhnsc", RTFControlType::FLAG, RTFKeyword::PGNHNSC, 0 }, + { "pgnhnsh", RTFControlType::FLAG, RTFKeyword::PGNHNSH, 0 }, + { "pgnhnsm", RTFControlType::FLAG, RTFKeyword::PGNHNSM, 0 }, + { "pgnhnsn", RTFControlType::FLAG, RTFKeyword::PGNHNSN, 0 }, + { "pgnhnsp", RTFControlType::FLAG, RTFKeyword::PGNHNSP, 0 }, + { "pgnid", RTFControlType::FLAG, RTFKeyword::PGNID, 0 }, + { "pgnlcltr", RTFControlType::FLAG, RTFKeyword::PGNLCLTR, 0 }, + { "pgnlcrm", RTFControlType::FLAG, RTFKeyword::PGNLCRM, 0 }, + { "pgnrestart", RTFControlType::FLAG, RTFKeyword::PGNRESTART, 0 }, + { "pgnstart", RTFControlType::VALUE, RTFKeyword::PGNSTART, 1 }, + { "pgnstarts", RTFControlType::VALUE, RTFKeyword::PGNSTARTS, 1 }, + { "pgnthaia", RTFControlType::FLAG, RTFKeyword::PGNTHAIA, 0 }, + { "pgnthaib", RTFControlType::FLAG, RTFKeyword::PGNTHAIB, 0 }, + { "pgnthaic", RTFControlType::FLAG, RTFKeyword::PGNTHAIC, 0 }, + { "pgnucltr", RTFControlType::FLAG, RTFKeyword::PGNUCLTR, 0 }, + { "pgnucrm", RTFControlType::FLAG, RTFKeyword::PGNUCRM, 0 }, + { "pgnvieta", RTFControlType::FLAG, RTFKeyword::PGNVIETA, 0 }, + { "pgnx", RTFControlType::VALUE, RTFKeyword::PGNX, 720 }, + { "pgny", RTFControlType::VALUE, RTFKeyword::PGNY, 720 }, + { "pgnzodiac", RTFControlType::FLAG, RTFKeyword::PGNZODIAC, 0 }, + { "pgnzodiacd", RTFControlType::FLAG, RTFKeyword::PGNZODIACD, 0 }, + { "pgnzodiacl", RTFControlType::FLAG, RTFKeyword::PGNZODIACL, 0 }, + { "pgp", RTFControlType::DESTINATION, RTFKeyword::PGP, 0 }, + { "pgptbl", RTFControlType::DESTINATION, RTFKeyword::PGPTBL, 0 }, + { "pgwsxn", RTFControlType::VALUE, RTFKeyword::PGWSXN, 0 }, + { "phcol", RTFControlType::FLAG, RTFKeyword::PHCOL, 0 }, + { "phmrg", RTFControlType::FLAG, RTFKeyword::PHMRG, 0 }, + { "phpg", RTFControlType::FLAG, RTFKeyword::PHPG, 0 }, + { "picbmp", RTFControlType::FLAG, RTFKeyword::PICBMP, 0 }, + { "picbpp", RTFControlType::VALUE, RTFKeyword::PICBPP, 0 }, + { "piccropb", RTFControlType::VALUE, RTFKeyword::PICCROPB, 0 }, + { "piccropl", RTFControlType::VALUE, RTFKeyword::PICCROPL, 0 }, + { "piccropr", RTFControlType::VALUE, RTFKeyword::PICCROPR, 0 }, + { "piccropt", RTFControlType::VALUE, RTFKeyword::PICCROPT, 0 }, + { "pich", RTFControlType::VALUE, RTFKeyword::PICH, 0 }, + { "pichgoal", RTFControlType::VALUE, RTFKeyword::PICHGOAL, 0 }, + { "pichGoal", RTFControlType::VALUE, RTFKeyword::PICHGOAL, 0 }, + { "picprop", RTFControlType::DESTINATION, RTFKeyword::PICPROP, 0 }, + { "picscaled", RTFControlType::FLAG, RTFKeyword::PICSCALED, 0 }, + { "picscalex", RTFControlType::VALUE, RTFKeyword::PICSCALEX, 100 }, + { "picscaley", RTFControlType::VALUE, RTFKeyword::PICSCALEY, 100 }, + { "pict", RTFControlType::DESTINATION, RTFKeyword::PICT, 0 }, + { "picw", RTFControlType::VALUE, RTFKeyword::PICW, 0 }, + { "picwgoal", RTFControlType::VALUE, RTFKeyword::PICWGOAL, 0 }, + { "picwGoal", RTFControlType::VALUE, RTFKeyword::PICWGOAL, 0 }, + { "pindtabqc", RTFControlType::FLAG, RTFKeyword::PINDTABQC, 0 }, + { "pindtabql", RTFControlType::FLAG, RTFKeyword::PINDTABQL, 0 }, + { "pindtabqr", RTFControlType::FLAG, RTFKeyword::PINDTABQR, 0 }, + { "plain", RTFControlType::FLAG, RTFKeyword::PLAIN, 0 }, + { "pmartabqc", RTFControlType::FLAG, RTFKeyword::PMARTABQC, 0 }, + { "pmartabql", RTFControlType::FLAG, RTFKeyword::PMARTABQL, 0 }, + { "pmartabqr", RTFControlType::FLAG, RTFKeyword::PMARTABQR, 0 }, + { "pmmetafile", RTFControlType::VALUE, RTFKeyword::PMMETAFILE, 0 }, + { "pn", RTFControlType::DESTINATION, RTFKeyword::PN, 0 }, + { "pnacross", RTFControlType::FLAG, RTFKeyword::PNACROSS, 0 }, + { "pnaiu", RTFControlType::FLAG, RTFKeyword::PNAIU, 0 }, + { "pnaiud", RTFControlType::FLAG, RTFKeyword::PNAIUD, 0 }, + { "pnaiueo", RTFControlType::FLAG, RTFKeyword::PNAIUEO, 0 }, + { "pnaiueod", RTFControlType::FLAG, RTFKeyword::PNAIUEOD, 0 }, + { "pnb", RTFControlType::TOGGLE, RTFKeyword::PNB, 1 }, + { "pnbidia", RTFControlType::FLAG, RTFKeyword::PNBIDIA, 0 }, + { "pnbidib", RTFControlType::FLAG, RTFKeyword::PNBIDIB, 0 }, + { "pncaps", RTFControlType::TOGGLE, RTFKeyword::PNCAPS, 1 }, + { "pncard", RTFControlType::FLAG, RTFKeyword::PNCARD, 0 }, + { "pncf", RTFControlType::VALUE, RTFKeyword::PNCF, 0 }, + { "pnchosung", RTFControlType::FLAG, RTFKeyword::PNCHOSUNG, 0 }, + { "pncnum", RTFControlType::FLAG, RTFKeyword::PNCNUM, 0 }, + { "pndbnum", RTFControlType::FLAG, RTFKeyword::PNDBNUM, 0 }, + { "pndbnumd", RTFControlType::FLAG, RTFKeyword::PNDBNUMD, 0 }, + { "pndbnumk", RTFControlType::FLAG, RTFKeyword::PNDBNUMK, 0 }, + { "pndbnuml", RTFControlType::FLAG, RTFKeyword::PNDBNUML, 0 }, + { "pndbnumt", RTFControlType::FLAG, RTFKeyword::PNDBNUMT, 0 }, + { "pndec", RTFControlType::FLAG, RTFKeyword::PNDEC, 0 }, + { "pndecd", RTFControlType::FLAG, RTFKeyword::PNDECD, 0 }, + { "pnf", RTFControlType::VALUE, RTFKeyword::PNF, 0 }, + { "pnfs", RTFControlType::VALUE, RTFKeyword::PNFS, 0 }, + { "pnganada", RTFControlType::FLAG, RTFKeyword::PNGANADA, 0 }, + { "pngblip", RTFControlType::FLAG, RTFKeyword::PNGBLIP, 0 }, + { "pngbnum", RTFControlType::FLAG, RTFKeyword::PNGBNUM, 0 }, + { "pngbnumd", RTFControlType::FLAG, RTFKeyword::PNGBNUMD, 0 }, + { "pngbnumk", RTFControlType::FLAG, RTFKeyword::PNGBNUMK, 0 }, + { "pngbnuml", RTFControlType::FLAG, RTFKeyword::PNGBNUML, 0 }, + { "pnhang", RTFControlType::FLAG, RTFKeyword::PNHANG, 0 }, + { "pni", RTFControlType::TOGGLE, RTFKeyword::PNI, 1 }, + { "pnindent", RTFControlType::VALUE, RTFKeyword::PNINDENT, 0 }, + { "pniroha", RTFControlType::FLAG, RTFKeyword::PNIROHA, 0 }, + { "pnirohad", RTFControlType::FLAG, RTFKeyword::PNIROHAD, 0 }, + { "pnlcltr", RTFControlType::FLAG, RTFKeyword::PNLCLTR, 0 }, + { "pnlcrm", RTFControlType::FLAG, RTFKeyword::PNLCRM, 0 }, + { "pnlvl", RTFControlType::VALUE, RTFKeyword::PNLVL, 0 }, + { "pnlvlblt", RTFControlType::FLAG, RTFKeyword::PNLVLBLT, 0 }, + { "pnlvlbody", RTFControlType::FLAG, RTFKeyword::PNLVLBODY, 0 }, + { "pnlvlcont", RTFControlType::FLAG, RTFKeyword::PNLVLCONT, 0 }, + { "pnnumonce", RTFControlType::FLAG, RTFKeyword::PNNUMONCE, 0 }, + { "pnord", RTFControlType::FLAG, RTFKeyword::PNORD, 0 }, + { "pnordt", RTFControlType::FLAG, RTFKeyword::PNORDT, 0 }, + { "pnprev", RTFControlType::FLAG, RTFKeyword::PNPREV, 0 }, + { "pnqc", RTFControlType::FLAG, RTFKeyword::PNQC, 0 }, + { "pnql", RTFControlType::FLAG, RTFKeyword::PNQL, 0 }, + { "pnqr", RTFControlType::FLAG, RTFKeyword::PNQR, 0 }, + { "pnrauth", RTFControlType::VALUE, RTFKeyword::PNRAUTH, 0 }, + { "pnrdate", RTFControlType::VALUE, RTFKeyword::PNRDATE, 0 }, + { "pnrestart", RTFControlType::FLAG, RTFKeyword::PNRESTART, 0 }, + { "pnrnfc", RTFControlType::VALUE, RTFKeyword::PNRNFC, 0 }, + { "pnrnot", RTFControlType::FLAG, RTFKeyword::PNRNOT, 0 }, + { "pnrpnbr", RTFControlType::VALUE, RTFKeyword::PNRPNBR, 0 }, + { "pnrrgb", RTFControlType::VALUE, RTFKeyword::PNRRGB, 0 }, + { "pnrstart", RTFControlType::VALUE, RTFKeyword::PNRSTART, 0 }, + { "pnrstop", RTFControlType::VALUE, RTFKeyword::PNRSTOP, 0 }, + { "pnrxst", RTFControlType::VALUE, RTFKeyword::PNRXST, 0 }, + { "pnscaps", RTFControlType::TOGGLE, RTFKeyword::PNSCAPS, 1 }, + { "pnseclvl", RTFControlType::DESTINATION, RTFKeyword::PNSECLVL, 0 }, + { "pnsp", RTFControlType::VALUE, RTFKeyword::PNSP, 0 }, + { "pnstart", RTFControlType::VALUE, RTFKeyword::PNSTART, 0 }, + { "pnstrike", RTFControlType::TOGGLE, RTFKeyword::PNSTRIKE, 1 }, + { "pntext", RTFControlType::DESTINATION, RTFKeyword::PNTEXT, 0 }, + { "pntxta", RTFControlType::DESTINATION, RTFKeyword::PNTXTA, 0 }, + { "pntxtb", RTFControlType::DESTINATION, RTFKeyword::PNTXTB, 0 }, + { "pnucltr", RTFControlType::FLAG, RTFKeyword::PNUCLTR, 0 }, + { "pnucrm", RTFControlType::FLAG, RTFKeyword::PNUCRM, 0 }, + { "pnul", RTFControlType::TOGGLE, RTFKeyword::PNUL, 1 }, + { "pnuld", RTFControlType::FLAG, RTFKeyword::PNULD, 0 }, + { "pnuldash", RTFControlType::FLAG, RTFKeyword::PNULDASH, 0 }, + { "pnuldashd", RTFControlType::FLAG, RTFKeyword::PNULDASHD, 0 }, + { "pnuldashdd", RTFControlType::FLAG, RTFKeyword::PNULDASHDD, 0 }, + { "pnuldb", RTFControlType::FLAG, RTFKeyword::PNULDB, 0 }, + { "pnulhair", RTFControlType::FLAG, RTFKeyword::PNULHAIR, 0 }, + { "pnulnone", RTFControlType::FLAG, RTFKeyword::PNULNONE, 0 }, + { "pnulth", RTFControlType::FLAG, RTFKeyword::PNULTH, 0 }, + { "pnulw", RTFControlType::FLAG, RTFKeyword::PNULW, 0 }, + { "pnulwave", RTFControlType::FLAG, RTFKeyword::PNULWAVE, 0 }, + { "pnzodiac", RTFControlType::FLAG, RTFKeyword::PNZODIAC, 0 }, + { "pnzodiacd", RTFControlType::FLAG, RTFKeyword::PNZODIACD, 0 }, + { "pnzodiacl", RTFControlType::FLAG, RTFKeyword::PNZODIACL, 0 }, + { "posnegx", RTFControlType::VALUE, RTFKeyword::POSNEGX, 0 }, + { "posnegy", RTFControlType::VALUE, RTFKeyword::POSNEGY, 0 }, + { "posx", RTFControlType::VALUE, RTFKeyword::POSX, 0 }, + { "posxc", RTFControlType::FLAG, RTFKeyword::POSXC, 0 }, + { "posxi", RTFControlType::FLAG, RTFKeyword::POSXI, 0 }, + { "posxl", RTFControlType::FLAG, RTFKeyword::POSXL, 0 }, + { "posxo", RTFControlType::FLAG, RTFKeyword::POSXO, 0 }, + { "posxr", RTFControlType::FLAG, RTFKeyword::POSXR, 0 }, + { "posy", RTFControlType::VALUE, RTFKeyword::POSY, 0 }, + { "posyb", RTFControlType::FLAG, RTFKeyword::POSYB, 0 }, + { "posyc", RTFControlType::FLAG, RTFKeyword::POSYC, 0 }, + { "posyil", RTFControlType::FLAG, RTFKeyword::POSYIL, 0 }, + { "posyin", RTFControlType::FLAG, RTFKeyword::POSYIN, 0 }, + { "posyout", RTFControlType::FLAG, RTFKeyword::POSYOUT, 0 }, + { "posyt", RTFControlType::FLAG, RTFKeyword::POSYT, 0 }, + { "prauth", RTFControlType::VALUE, RTFKeyword::PRAUTH, 0 }, + { "prcolbl", RTFControlType::FLAG, RTFKeyword::PRCOLBL, 0 }, + { "prdate", RTFControlType::VALUE, RTFKeyword::PRDATE, 0 }, + { "printdata", RTFControlType::FLAG, RTFKeyword::PRINTDATA, 0 }, + { "printim", RTFControlType::DESTINATION, RTFKeyword::PRINTIM, 0 }, + { "private", RTFControlType::DESTINATION, RTFKeyword::PRIVATE, 0 }, + { "propname", RTFControlType::DESTINATION, RTFKeyword::PROPNAME, 0 }, + { "proptype", RTFControlType::VALUE, RTFKeyword::PROPTYPE, 0 }, + { "protect", RTFControlType::TOGGLE, RTFKeyword::PROTECT, 1 }, + { "protend", RTFControlType::DESTINATION, RTFKeyword::PROTEND, 0 }, + { "protlevel", RTFControlType::VALUE, RTFKeyword::PROTLEVEL, 0 }, + { "protstart", RTFControlType::DESTINATION, RTFKeyword::PROTSTART, 0 }, + { "protusertbl", RTFControlType::DESTINATION, RTFKeyword::PROTUSERTBL, 0 }, + { "psover", RTFControlType::FLAG, RTFKeyword::PSOVER, 0 }, + { "psz", RTFControlType::VALUE, RTFKeyword::PSZ, 0 }, + { "ptabldot", RTFControlType::FLAG, RTFKeyword::PTABLDOT, 0 }, + { "ptablmdot", RTFControlType::FLAG, RTFKeyword::PTABLMDOT, 0 }, + { "ptablminus", RTFControlType::FLAG, RTFKeyword::PTABLMINUS, 0 }, + { "ptablnone", RTFControlType::FLAG, RTFKeyword::PTABLNONE, 0 }, + { "ptabluscore", RTFControlType::FLAG, RTFKeyword::PTABLUSCORE, 0 }, + { "pubauto", RTFControlType::FLAG, RTFKeyword::PUBAUTO, 0 }, + { "pvmrg", RTFControlType::FLAG, RTFKeyword::PVMRG, 0 }, + { "pvpara", RTFControlType::FLAG, RTFKeyword::PVPARA, 0 }, + { "pvpg", RTFControlType::FLAG, RTFKeyword::PVPG, 0 }, + { "pwd", RTFControlType::VALUE, RTFKeyword::PWD, 0 }, + { "pxe", RTFControlType::DESTINATION, RTFKeyword::PXE, 0 }, + { "qc", RTFControlType::FLAG, RTFKeyword::QC, 0 }, + { "qd", RTFControlType::FLAG, RTFKeyword::QD, 0 }, + { "qj", RTFControlType::FLAG, RTFKeyword::QJ, 0 }, + { "qk", RTFControlType::VALUE, RTFKeyword::QK, 0 }, + { "ql", RTFControlType::FLAG, RTFKeyword::QL, 0 }, + { "qmspace", RTFControlType::SYMBOL, RTFKeyword::QMSPACE, 0 }, + { "qr", RTFControlType::FLAG, RTFKeyword::QR, 0 }, + { "qt", RTFControlType::FLAG, RTFKeyword::QT, 0 }, + { "rawclbgdkbdiag", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKBDIAG, 0 }, + { "rawclbgbdiag", RTFControlType::FLAG, RTFKeyword::RAWCLBGBDIAG, 0 }, + { "rawclbgcross", RTFControlType::FLAG, RTFKeyword::RAWCLBGCROSS, 0 }, + { "rawclbgdcross", RTFControlType::FLAG, RTFKeyword::RAWCLBGDCROSS, 0 }, + { "rawclbgdkcross", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKCROSS, 0 }, + { "rawclbgdkdcross", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKDCROSS, 0 }, + { "rawclbgdkfdiag", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKFDIAG, 0 }, + { "rawclbgdkhor", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKHOR, 0 }, + { "rawclbgdkvert", RTFControlType::FLAG, RTFKeyword::RAWCLBGDKVERT, 0 }, + { "rawclbgfdiag", RTFControlType::FLAG, RTFKeyword::RAWCLBGFDIAG, 0 }, + { "rawclbghoriz", RTFControlType::FLAG, RTFKeyword::RAWCLBGHORIZ, 0 }, + { "rawclbgvert", RTFControlType::FLAG, RTFKeyword::RAWCLBGVERT, 0 }, + { "rdblquote", RTFControlType::SYMBOL, RTFKeyword::RDBLQUOTE, 0 }, + { "readonlyrecommended", RTFControlType::FLAG, RTFKeyword::READONLYRECOMMENDED, 0 }, + { "readprot", RTFControlType::FLAG, RTFKeyword::READPROT, 0 }, + { "red", RTFControlType::VALUE, RTFKeyword::RED, 0 }, + { "relyonvml", RTFControlType::VALUE, RTFKeyword::RELYONVML, 0 }, + { "remdttm", RTFControlType::FLAG, RTFKeyword::REMDTTM, 0 }, + { "rempersonalinfo", RTFControlType::FLAG, RTFKeyword::REMPERSONALINFO, 0 }, + { "result", RTFControlType::DESTINATION, RTFKeyword::RESULT, 0 }, + { "revauth", RTFControlType::VALUE, RTFKeyword::REVAUTH, 0 }, + { "revauthdel", RTFControlType::VALUE, RTFKeyword::REVAUTHDEL, 0 }, + { "revbar", RTFControlType::VALUE, RTFKeyword::REVBAR, 3 }, + { "revdttm", RTFControlType::VALUE, RTFKeyword::REVDTTM, 0 }, + { "revdttmdel", RTFControlType::VALUE, RTFKeyword::REVDTTMDEL, 0 }, + { "revised", RTFControlType::TOGGLE, RTFKeyword::REVISED, 1 }, + { "revisions", RTFControlType::FLAG, RTFKeyword::REVISIONS, 0 }, + { "revprop", RTFControlType::VALUE, RTFKeyword::REVPROP, 3 }, + { "revprot", RTFControlType::FLAG, RTFKeyword::REVPROT, 0 }, + { "revtbl", RTFControlType::DESTINATION, RTFKeyword::REVTBL, 0 }, + { "revtim", RTFControlType::DESTINATION, RTFKeyword::REVTIM, 0 }, + { "ri", RTFControlType::VALUE, RTFKeyword::RI, 0 }, + { "rin", RTFControlType::VALUE, RTFKeyword::RIN, 0 }, + { "row", RTFControlType::SYMBOL, RTFKeyword::ROW, 0 }, + { "rquote", RTFControlType::SYMBOL, RTFKeyword::RQUOTE, 0 }, + { "rsid", RTFControlType::VALUE, RTFKeyword::RSID, 0 }, + { "rsidroot", RTFControlType::VALUE, RTFKeyword::RSIDROOT, 0 }, + { "rsidtbl", RTFControlType::DESTINATION, RTFKeyword::RSIDTBL, 0 }, + { "rsltbmp", RTFControlType::FLAG, RTFKeyword::RSLTBMP, 0 }, + { "rslthtml", RTFControlType::FLAG, RTFKeyword::RSLTHTML, 0 }, + { "rsltmerge", RTFControlType::FLAG, RTFKeyword::RSLTMERGE, 0 }, + { "rsltpict", RTFControlType::FLAG, RTFKeyword::RSLTPICT, 0 }, + { "rsltrtf", RTFControlType::FLAG, RTFKeyword::RSLTRTF, 0 }, + { "rslttxt", RTFControlType::FLAG, RTFKeyword::RSLTTXT, 0 }, + { "rtf", RTFControlType::DESTINATION, RTFKeyword::RTF, 0 }, + { "rtlch", RTFControlType::FLAG, RTFKeyword::RTLCH, 0 }, + { "rtldoc", RTFControlType::FLAG, RTFKeyword::RTLDOC, 0 }, + { "rtlgutter", RTFControlType::FLAG, RTFKeyword::RTLGUTTER, 0 }, + { "rtlmark", RTFControlType::SYMBOL, RTFKeyword::RTLMARK, 0 }, + { "rtlpar", RTFControlType::FLAG, RTFKeyword::RTLPAR, 0 }, + { "rtlrow", RTFControlType::FLAG, RTFKeyword::RTLROW, 0 }, + { "rtlsect", RTFControlType::FLAG, RTFKeyword::RTLSECT, 0 }, + { "rxe", RTFControlType::DESTINATION, RTFKeyword::RXE, 0 }, + { "s", RTFControlType::VALUE, RTFKeyword::S, 0 }, + { "sa", RTFControlType::VALUE, RTFKeyword::SA, 0 }, + { "saauto", RTFControlType::TOGGLE, RTFKeyword::SAAUTO, 1 }, + { "saftnnalc", RTFControlType::FLAG, RTFKeyword::SAFTNNALC, 0 }, + { "saftnnar", RTFControlType::FLAG, RTFKeyword::SAFTNNAR, 0 }, + { "saftnnauc", RTFControlType::FLAG, RTFKeyword::SAFTNNAUC, 0 }, + { "saftnnchi", RTFControlType::FLAG, RTFKeyword::SAFTNNCHI, 0 }, + { "saftnnchosung", RTFControlType::FLAG, RTFKeyword::SAFTNNCHOSUNG, 0 }, + { "saftnncnum", RTFControlType::FLAG, RTFKeyword::SAFTNNCNUM, 0 }, + { "saftnndbar", RTFControlType::FLAG, RTFKeyword::SAFTNNDBAR, 0 }, + { "saftnndbnum", RTFControlType::FLAG, RTFKeyword::SAFTNNDBNUM, 0 }, + { "saftnndbnumd", RTFControlType::FLAG, RTFKeyword::SAFTNNDBNUMD, 0 }, + { "saftnndbnumk", RTFControlType::FLAG, RTFKeyword::SAFTNNDBNUMK, 0 }, + { "saftnndbnumt", RTFControlType::FLAG, RTFKeyword::SAFTNNDBNUMT, 0 }, + { "saftnnganada", RTFControlType::FLAG, RTFKeyword::SAFTNNGANADA, 0 }, + { "saftnngbnum", RTFControlType::FLAG, RTFKeyword::SAFTNNGBNUM, 0 }, + { "saftnngbnumd", RTFControlType::FLAG, RTFKeyword::SAFTNNGBNUMD, 0 }, + { "saftnngbnumk", RTFControlType::FLAG, RTFKeyword::SAFTNNGBNUMK, 0 }, + { "saftnngbnuml", RTFControlType::FLAG, RTFKeyword::SAFTNNGBNUML, 0 }, + { "saftnnrlc", RTFControlType::FLAG, RTFKeyword::SAFTNNRLC, 0 }, + { "saftnnruc", RTFControlType::FLAG, RTFKeyword::SAFTNNRUC, 0 }, + { "saftnnzodiac", RTFControlType::FLAG, RTFKeyword::SAFTNNZODIAC, 0 }, + { "saftnnzodiacd", RTFControlType::FLAG, RTFKeyword::SAFTNNZODIACD, 0 }, + { "saftnnzodiacl", RTFControlType::FLAG, RTFKeyword::SAFTNNZODIACL, 0 }, + { "saftnrestart", RTFControlType::FLAG, RTFKeyword::SAFTNRESTART, 0 }, + { "saftnrstcont", RTFControlType::FLAG, RTFKeyword::SAFTNRSTCONT, 0 }, + { "saftnstart", RTFControlType::VALUE, RTFKeyword::SAFTNSTART, 1 }, + { "sautoupd", RTFControlType::FLAG, RTFKeyword::SAUTOUPD, 0 }, + { "saveinvalidxml", RTFControlType::FLAG, RTFKeyword::SAVEINVALIDXML, 0 }, + { "saveprevpict", RTFControlType::FLAG, RTFKeyword::SAVEPREVPICT, 0 }, + { "sb", RTFControlType::VALUE, RTFKeyword::SB, 0 }, + { "sbasedon", RTFControlType::VALUE, RTFKeyword::SBASEDON, 222 }, + { "sbauto", RTFControlType::TOGGLE, RTFKeyword::SBAUTO, 1 }, + { "sbkcol", RTFControlType::FLAG, RTFKeyword::SBKCOL, 0 }, + { "sbkeven", RTFControlType::FLAG, RTFKeyword::SBKEVEN, 0 }, + { "sbknone", RTFControlType::FLAG, RTFKeyword::SBKNONE, 0 }, + { "sbkodd", RTFControlType::FLAG, RTFKeyword::SBKODD, 0 }, + { "sbkpage", RTFControlType::FLAG, RTFKeyword::SBKPAGE, 0 }, + { "sbys", RTFControlType::FLAG, RTFKeyword::SBYS, 0 }, + { "scaps", RTFControlType::TOGGLE, RTFKeyword::SCAPS, 1 }, + { "scompose", RTFControlType::FLAG, RTFKeyword::SCOMPOSE, 0 }, + { "sec", RTFControlType::VALUE, RTFKeyword::SEC, 0 }, + { "sect", RTFControlType::SYMBOL, RTFKeyword::SECT, 0 }, + { "sectd", RTFControlType::FLAG, RTFKeyword::SECTD, 0 }, + { "sectdefaultcl", RTFControlType::FLAG, RTFKeyword::SECTDEFAULTCL, 0 }, + { "sectexpand", RTFControlType::VALUE, RTFKeyword::SECTEXPAND, 0 }, + { "sectlinegrid", RTFControlType::VALUE, RTFKeyword::SECTLINEGRID, 0 }, + { "sectnum", RTFControlType::SYMBOL, RTFKeyword::SECTNUM, 0 }, + { "sectrsid", RTFControlType::VALUE, RTFKeyword::SECTRSID, 0 }, + { "sectspecifycl", RTFControlType::FLAG, RTFKeyword::SECTSPECIFYCL, 0 }, + { "sectspecifygenN", RTFControlType::FLAG, RTFKeyword::SECTSPECIFYGENN, 0 }, + { "sectspecifyl", RTFControlType::FLAG, RTFKeyword::SECTSPECIFYL, 0 }, + { "sectunlocked", RTFControlType::FLAG, RTFKeyword::SECTUNLOCKED, 0 }, + { "sftnbj", RTFControlType::FLAG, RTFKeyword::SFTNBJ, 0 }, + { "sftnnalc", RTFControlType::FLAG, RTFKeyword::SFTNNALC, 0 }, + { "sftnnar", RTFControlType::FLAG, RTFKeyword::SFTNNAR, 0 }, + { "sftnnauc", RTFControlType::FLAG, RTFKeyword::SFTNNAUC, 0 }, + { "sftnnchi", RTFControlType::FLAG, RTFKeyword::SFTNNCHI, 0 }, + { "sftnnchosung", RTFControlType::FLAG, RTFKeyword::SFTNNCHOSUNG, 0 }, + { "sftnncnum", RTFControlType::FLAG, RTFKeyword::SFTNNCNUM, 0 }, + { "sftnndbar", RTFControlType::FLAG, RTFKeyword::SFTNNDBAR, 0 }, + { "sftnndbnum", RTFControlType::FLAG, RTFKeyword::SFTNNDBNUM, 0 }, + { "sftnndbnumd", RTFControlType::FLAG, RTFKeyword::SFTNNDBNUMD, 0 }, + { "sftnndbnumk", RTFControlType::FLAG, RTFKeyword::SFTNNDBNUMK, 0 }, + { "sftnndbnumt", RTFControlType::FLAG, RTFKeyword::SFTNNDBNUMT, 0 }, + { "sftnnganada", RTFControlType::FLAG, RTFKeyword::SFTNNGANADA, 0 }, + { "sftnngbnum", RTFControlType::FLAG, RTFKeyword::SFTNNGBNUM, 0 }, + { "sftnngbnumd", RTFControlType::FLAG, RTFKeyword::SFTNNGBNUMD, 0 }, + { "sftnngbnumk", RTFControlType::FLAG, RTFKeyword::SFTNNGBNUMK, 0 }, + { "sftnngbnuml", RTFControlType::FLAG, RTFKeyword::SFTNNGBNUML, 0 }, + { "sftnnrlc", RTFControlType::FLAG, RTFKeyword::SFTNNRLC, 0 }, + { "sftnnruc", RTFControlType::FLAG, RTFKeyword::SFTNNRUC, 0 }, + { "sftnnzodiac", RTFControlType::FLAG, RTFKeyword::SFTNNZODIAC, 0 }, + { "sftnnzodiacd", RTFControlType::FLAG, RTFKeyword::SFTNNZODIACD, 0 }, + { "sftnnzodiacl", RTFControlType::FLAG, RTFKeyword::SFTNNZODIACL, 0 }, + { "sftnrestart", RTFControlType::FLAG, RTFKeyword::SFTNRESTART, 0 }, + { "sftnrstcont", RTFControlType::FLAG, RTFKeyword::SFTNRSTCONT, 0 }, + { "sftnrstpg", RTFControlType::FLAG, RTFKeyword::SFTNRSTPG, 0 }, + { "sftnstart", RTFControlType::VALUE, RTFKeyword::SFTNSTART, 1 }, + { "sftntj", RTFControlType::FLAG, RTFKeyword::SFTNTJ, 0 }, + { "shad", RTFControlType::TOGGLE, RTFKeyword::SHAD, 1 }, + { "shading", RTFControlType::VALUE, RTFKeyword::SHADING, 0 }, + { "shidden", RTFControlType::FLAG, RTFKeyword::SHIDDEN, 0 }, + { "shift", RTFControlType::FLAG, RTFKeyword::SHIFT, 0 }, + { "showplaceholdtext", RTFControlType::VALUE, RTFKeyword::SHOWPLACEHOLDTEXT, 0 }, + { "showxmlerrors", RTFControlType::VALUE, RTFKeyword::SHOWXMLERRORS, 0 }, + { "shp", RTFControlType::DESTINATION, RTFKeyword::SHP, 0 }, + { "shpbottom", RTFControlType::VALUE, RTFKeyword::SHPBOTTOM, 0 }, + { "shpbxcolumn", RTFControlType::FLAG, RTFKeyword::SHPBXCOLUMN, 0 }, + { "shpbxignore", RTFControlType::FLAG, RTFKeyword::SHPBXIGNORE, 0 }, + { "shpbxmargin", RTFControlType::FLAG, RTFKeyword::SHPBXMARGIN, 0 }, + { "shpbxpage", RTFControlType::FLAG, RTFKeyword::SHPBXPAGE, 0 }, + { "shpbyignore", RTFControlType::FLAG, RTFKeyword::SHPBYIGNORE, 0 }, + { "shpbymargin", RTFControlType::FLAG, RTFKeyword::SHPBYMARGIN, 0 }, + { "shpbypage", RTFControlType::FLAG, RTFKeyword::SHPBYPAGE, 0 }, + { "shpbypara", RTFControlType::FLAG, RTFKeyword::SHPBYPARA, 0 }, + { "shpfblwtxt", RTFControlType::VALUE, RTFKeyword::SHPFBLWTXT, 0 }, + { "shpfhdr", RTFControlType::VALUE, RTFKeyword::SHPFHDR, 0 }, + { "shpgrp", RTFControlType::DESTINATION, RTFKeyword::SHPGRP, 0 }, + { "shpinst", RTFControlType::DESTINATION, RTFKeyword::SHPINST, 0 }, + { "shpleft", RTFControlType::VALUE, RTFKeyword::SHPLEFT, 0 }, + { "shplid", RTFControlType::VALUE, RTFKeyword::SHPLID, 0 }, + { "shplockanchor", RTFControlType::FLAG, RTFKeyword::SHPLOCKANCHOR, 0 }, + { "shppict", RTFControlType::DESTINATION, RTFKeyword::SHPPICT, 0 }, + { "shpright", RTFControlType::VALUE, RTFKeyword::SHPRIGHT, 0 }, + { "shprslt", RTFControlType::DESTINATION, RTFKeyword::SHPRSLT, 0 }, + { "shptop", RTFControlType::VALUE, RTFKeyword::SHPTOP, 0 }, + { "shptxt", RTFControlType::DESTINATION, RTFKeyword::SHPTXT, 0 }, + { "shpwrk", RTFControlType::VALUE, RTFKeyword::SHPWRK, 0 }, + { "shpwr", RTFControlType::VALUE, RTFKeyword::SHPWR, 0 }, + { "shpz", RTFControlType::VALUE, RTFKeyword::SHPZ, 0 }, + { "sl", RTFControlType::VALUE, RTFKeyword::SL, 0 }, + { "slink", RTFControlType::VALUE, RTFKeyword::SLINK, 0 }, + { "slmult", RTFControlType::VALUE, RTFKeyword::SLMULT, 0 }, + { "slocked", RTFControlType::FLAG, RTFKeyword::SLOCKED, 0 }, + { "sn", RTFControlType::DESTINATION, RTFKeyword::SN, 0 }, + { "snaptogridincell", RTFControlType::FLAG, RTFKeyword::SNAPTOGRIDINCELL, 0 }, + { "snext", RTFControlType::VALUE, RTFKeyword::SNEXT, 0 }, + { "softcol", RTFControlType::FLAG, RTFKeyword::SOFTCOL, 0 }, + { "softlheight", RTFControlType::VALUE, RTFKeyword::SOFTLHEIGHT, 0 }, + { "softline", RTFControlType::FLAG, RTFKeyword::SOFTLINE, 0 }, + { "softpage", RTFControlType::FLAG, RTFKeyword::SOFTPAGE, 0 }, + { "sp", RTFControlType::DESTINATION, RTFKeyword::SP, 0 }, + { "spersonal", RTFControlType::FLAG, RTFKeyword::SPERSONAL, 0 }, + { "spltpgpar", RTFControlType::FLAG, RTFKeyword::SPLTPGPAR, 0 }, + { "splytwnine", RTFControlType::FLAG, RTFKeyword::SPLYTWNINE, 0 }, + { "spriority", RTFControlType::VALUE, RTFKeyword::SPRIORITY, 0 }, + { "sprsbsp", RTFControlType::FLAG, RTFKeyword::SPRSBSP, 0 }, + { "sprslnsp", RTFControlType::FLAG, RTFKeyword::SPRSLNSP, 0 }, + { "sprsspbf", RTFControlType::FLAG, RTFKeyword::SPRSSPBF, 0 }, + { "sprstsm", RTFControlType::FLAG, RTFKeyword::SPRSTSM, 0 }, + { "sprstsp", RTFControlType::FLAG, RTFKeyword::SPRSTSP, 0 }, + { "spv", RTFControlType::FLAG, RTFKeyword::SPV, 0 }, + { "sqformat", RTFControlType::FLAG, RTFKeyword::SQFORMAT, 0 }, + { "srauth", RTFControlType::VALUE, RTFKeyword::SRAUTH, 0 }, + { "srdate", RTFControlType::VALUE, RTFKeyword::SRDATE, 0 }, + { "sreply", RTFControlType::FLAG, RTFKeyword::SREPLY, 0 }, + { "ssemihidden", RTFControlType::VALUE, RTFKeyword::SSEMIHIDDEN, 0 }, + { "staticval", RTFControlType::DESTINATION, RTFKeyword::STATICVAL, 0 }, + { "stextflow", RTFControlType::VALUE, RTFKeyword::STEXTFLOW, 0 }, + { "strike", RTFControlType::TOGGLE, RTFKeyword::STRIKE, 1 }, + { "striked", RTFControlType::TOGGLE, RTFKeyword::STRIKED, 1 }, + { "stshfbi", RTFControlType::VALUE, RTFKeyword::STSHFBI, 0 }, + { "stshfdbch", RTFControlType::VALUE, RTFKeyword::STSHFDBCH, 0 }, + { "stshfhich", RTFControlType::VALUE, RTFKeyword::STSHFHICH, 0 }, + { "stshfloch", RTFControlType::VALUE, RTFKeyword::STSHFLOCH, 0 }, + { "stylelock", RTFControlType::FLAG, RTFKeyword::STYLELOCK, 0 }, + { "stylelockbackcomp", RTFControlType::FLAG, RTFKeyword::STYLELOCKBACKCOMP, 0 }, + { "stylelockenforced", RTFControlType::FLAG, RTFKeyword::STYLELOCKENFORCED, 0 }, + { "stylelockqfset", RTFControlType::FLAG, RTFKeyword::STYLELOCKQFSET, 0 }, + { "stylelocktheme", RTFControlType::FLAG, RTFKeyword::STYLELOCKTHEME, 0 }, + { "stylesheet", RTFControlType::DESTINATION, RTFKeyword::STYLESHEET, 0 }, + { "stylesortmethod", RTFControlType::VALUE, RTFKeyword::STYLESORTMETHOD, 1 }, + { "styrsid", RTFControlType::VALUE, RTFKeyword::STYRSID, 0 }, + { "sub", RTFControlType::FLAG, RTFKeyword::SUB, 0 }, + { "subdocument", RTFControlType::VALUE, RTFKeyword::SUBDOCUMENT, 0 }, + { "subfontbysize", RTFControlType::FLAG, RTFKeyword::SUBFONTBYSIZE, 0 }, + { "subject", RTFControlType::DESTINATION, RTFKeyword::SUBJECT, 0 }, + { "sunhideused", RTFControlType::VALUE, RTFKeyword::SUNHIDEUSED, 0 }, + { "super", RTFControlType::FLAG, RTFKeyword::SUPER, 0 }, + { "sv", RTFControlType::DESTINATION, RTFKeyword::SV, 0 }, + { "svb", RTFControlType::DESTINATION, RTFKeyword::SVB, 0 }, + { "swpbdr", RTFControlType::FLAG, RTFKeyword::SWPBDR, 0 }, + { "tab", RTFControlType::SYMBOL, RTFKeyword::TAB, 0 }, + { "tabsnoovrlp", RTFControlType::FLAG, RTFKeyword::TABSNOOVRLP, 0 }, + { "taprtl", RTFControlType::FLAG, RTFKeyword::TAPRTL, 0 }, + { "tb", RTFControlType::VALUE, RTFKeyword::TB, 0 }, + { "tblind", RTFControlType::VALUE, RTFKeyword::TBLIND, 0 }, + { "tblindtype", RTFControlType::VALUE, RTFKeyword::TBLINDTYPE, 0 }, + { "tbllkbestfit", RTFControlType::FLAG, RTFKeyword::TBLLKBESTFIT, 0 }, + { "tbllkborder", RTFControlType::FLAG, RTFKeyword::TBLLKBORDER, 0 }, + { "tbllkcolor", RTFControlType::FLAG, RTFKeyword::TBLLKCOLOR, 0 }, + { "tbllkfont", RTFControlType::FLAG, RTFKeyword::TBLLKFONT, 0 }, + { "tbllkhdrcols", RTFControlType::FLAG, RTFKeyword::TBLLKHDRCOLS, 0 }, + { "tbllkhdrrows", RTFControlType::FLAG, RTFKeyword::TBLLKHDRROWS, 0 }, + { "tbllklastcol", RTFControlType::FLAG, RTFKeyword::TBLLKLASTCOL, 0 }, + { "tbllklastrow", RTFControlType::FLAG, RTFKeyword::TBLLKLASTROW, 0 }, + { "tbllknocolband", RTFControlType::FLAG, RTFKeyword::TBLLKNOCOLBAND, 0 }, + { "tbllknorowband", RTFControlType::FLAG, RTFKeyword::TBLLKNOROWBAND, 0 }, + { "tbllkshading", RTFControlType::FLAG, RTFKeyword::TBLLKSHADING, 0 }, + { "tblrsid", RTFControlType::VALUE, RTFKeyword::TBLRSID, 0 }, + { "tc", RTFControlType::DESTINATION, RTFKeyword::TC, 0 }, + { "tcelld", RTFControlType::FLAG, RTFKeyword::TCELLD, 0 }, + { "tcf", RTFControlType::VALUE, RTFKeyword::TCF, 67 }, + { "tcl", RTFControlType::VALUE, RTFKeyword::TCL, 0 }, + { "tcn", RTFControlType::FLAG, RTFKeyword::TCN, 0 }, + { "tdfrmtxtBottom", RTFControlType::VALUE, RTFKeyword::TDFRMTXTBOTTOM, 0 }, + { "tdfrmtxtLeft", RTFControlType::VALUE, RTFKeyword::TDFRMTXTLEFT, 0 }, + { "tdfrmtxtRight", RTFControlType::VALUE, RTFKeyword::TDFRMTXTRIGHT, 0 }, + { "tdfrmtxtTop", RTFControlType::VALUE, RTFKeyword::TDFRMTXTTOP, 0 }, + { "template", RTFControlType::DESTINATION, RTFKeyword::TEMPLATE, 0 }, + { "themedata", RTFControlType::DESTINATION, RTFKeyword::THEMEDATA, 0 }, + { "themelang", RTFControlType::VALUE, RTFKeyword::THEMELANG, 0 }, + { "themelangcs", RTFControlType::VALUE, RTFKeyword::THEMELANGCS, 0 }, + { "themelangfe", RTFControlType::VALUE, RTFKeyword::THEMELANGFE, 0 }, + { "time", RTFControlType::FLAG, RTFKeyword::TIME, 0 }, + { "title", RTFControlType::DESTINATION, RTFKeyword::TITLE, 0 }, + { "titlepg", RTFControlType::FLAG, RTFKeyword::TITLEPG, 0 }, + { "tldot", RTFControlType::FLAG, RTFKeyword::TLDOT, 0 }, + { "tleq", RTFControlType::FLAG, RTFKeyword::TLEQ, 0 }, + { "tlhyph", RTFControlType::FLAG, RTFKeyword::TLHYPH, 0 }, + { "tlmdot", RTFControlType::FLAG, RTFKeyword::TLMDOT, 0 }, + { "tlth", RTFControlType::FLAG, RTFKeyword::TLTH, 0 }, + { "tlul", RTFControlType::FLAG, RTFKeyword::TLUL, 0 }, + { "toplinepunct", RTFControlType::FLAG, RTFKeyword::TOPLINEPUNCT, 0 }, + { "tphcol", RTFControlType::FLAG, RTFKeyword::TPHCOL, 0 }, + { "tphmrg", RTFControlType::FLAG, RTFKeyword::TPHMRG, 0 }, + { "tphpg", RTFControlType::FLAG, RTFKeyword::TPHPG, 0 }, + { "tposnegx", RTFControlType::VALUE, RTFKeyword::TPOSNEGX, 0 }, + { "tposnegy", RTFControlType::VALUE, RTFKeyword::TPOSNEGY, 0 }, + { "tposxc", RTFControlType::FLAG, RTFKeyword::TPOSXC, 0 }, + { "tposxi", RTFControlType::FLAG, RTFKeyword::TPOSXI, 0 }, + { "tposxl", RTFControlType::FLAG, RTFKeyword::TPOSXL, 0 }, + { "tposx", RTFControlType::VALUE, RTFKeyword::TPOSX, 0 }, + { "tposxo", RTFControlType::FLAG, RTFKeyword::TPOSXO, 0 }, + { "tposxr", RTFControlType::FLAG, RTFKeyword::TPOSXR, 0 }, + { "tposy", RTFControlType::VALUE, RTFKeyword::TPOSY, 0 }, + { "tposyb", RTFControlType::FLAG, RTFKeyword::TPOSYB, 0 }, + { "tposyc", RTFControlType::FLAG, RTFKeyword::TPOSYC, 0 }, + { "tposyil", RTFControlType::FLAG, RTFKeyword::TPOSYIL, 0 }, + { "tposyin", RTFControlType::FLAG, RTFKeyword::TPOSYIN, 0 }, + { "tposyout", RTFControlType::FLAG, RTFKeyword::TPOSYOUT, 0 }, + { "tposyt", RTFControlType::FLAG, RTFKeyword::TPOSYT, 0 }, + { "tpvmrg", RTFControlType::FLAG, RTFKeyword::TPVMRG, 0 }, + { "tpvpara", RTFControlType::FLAG, RTFKeyword::TPVPARA, 0 }, + { "tpvpg", RTFControlType::FLAG, RTFKeyword::TPVPG, 0 }, + { "tqc", RTFControlType::FLAG, RTFKeyword::TQC, 0 }, + { "tqdec", RTFControlType::FLAG, RTFKeyword::TQDEC, 0 }, + { "tqr", RTFControlType::FLAG, RTFKeyword::TQR, 0 }, + { "trackformatting", RTFControlType::VALUE, RTFKeyword::TRACKFORMATTING, 0 }, + { "trackmoves", RTFControlType::VALUE, RTFKeyword::TRACKMOVES, 0 }, + { "transmf", RTFControlType::FLAG, RTFKeyword::TRANSMF, 0 }, + { "trauth", RTFControlType::VALUE, RTFKeyword::TRAUTH, 0 }, + { "trautofit", RTFControlType::TOGGLE, RTFKeyword::TRAUTOFIT, 1 }, + { "trbgbdiag", RTFControlType::FLAG, RTFKeyword::TRBGBDIAG, 0 }, + { "trbgcross", RTFControlType::FLAG, RTFKeyword::TRBGCROSS, 0 }, + { "trbgdcross", RTFControlType::FLAG, RTFKeyword::TRBGDCROSS, 0 }, + { "trbgdkbdiag", RTFControlType::FLAG, RTFKeyword::TRBGDKBDIAG, 0 }, + { "trbgdkcross", RTFControlType::FLAG, RTFKeyword::TRBGDKCROSS, 0 }, + { "trbgdkdcross", RTFControlType::FLAG, RTFKeyword::TRBGDKDCROSS, 0 }, + { "trbgdkfdiag", RTFControlType::FLAG, RTFKeyword::TRBGDKFDIAG, 0 }, + { "trbgdkhor", RTFControlType::FLAG, RTFKeyword::TRBGDKHOR, 0 }, + { "trbgdkvert", RTFControlType::FLAG, RTFKeyword::TRBGDKVERT, 0 }, + { "trbgfdiag", RTFControlType::FLAG, RTFKeyword::TRBGFDIAG, 0 }, + { "trbghoriz", RTFControlType::FLAG, RTFKeyword::TRBGHORIZ, 0 }, + { "trbgvert", RTFControlType::FLAG, RTFKeyword::TRBGVERT, 0 }, + { "trbrdrb", RTFControlType::FLAG, RTFKeyword::TRBRDRB, 0 }, + { "trbrdrh", RTFControlType::FLAG, RTFKeyword::TRBRDRH, 0 }, + { "trbrdrl", RTFControlType::FLAG, RTFKeyword::TRBRDRL, 0 }, + { "trbrdrr", RTFControlType::FLAG, RTFKeyword::TRBRDRR, 0 }, + { "trbrdrt", RTFControlType::FLAG, RTFKeyword::TRBRDRT, 0 }, + { "trbrdrv", RTFControlType::FLAG, RTFKeyword::TRBRDRV, 0 }, + { "trcbpat", RTFControlType::VALUE, RTFKeyword::TRCBPAT, 0 }, + { "trcfpat", RTFControlType::VALUE, RTFKeyword::TRCFPAT, 0 }, + { "trdate", RTFControlType::VALUE, RTFKeyword::TRDATE, 0 }, + { "trftsWidthA", RTFControlType::VALUE, RTFKeyword::TRFTSWIDTHA, 0 }, + { "trftsWidthB", RTFControlType::VALUE, RTFKeyword::TRFTSWIDTHB, 0 }, + { "trftsWidth", RTFControlType::VALUE, RTFKeyword::TRFTSWIDTH, 0 }, + { "trgaph", RTFControlType::VALUE, RTFKeyword::TRGAPH, 0 }, + { "trhdr", RTFControlType::FLAG, RTFKeyword::TRHDR, 0 }, + { "trkeep", RTFControlType::FLAG, RTFKeyword::TRKEEP, 0 }, + { "trkeepfollow", RTFControlType::FLAG, RTFKeyword::TRKEEPFOLLOW, 0 }, + { "trleft", RTFControlType::VALUE, RTFKeyword::TRLEFT, 0 }, + { "trowd", RTFControlType::FLAG, RTFKeyword::TROWD, 0 }, + { "trpaddb", RTFControlType::VALUE, RTFKeyword::TRPADDB, 0 }, + { "trpaddfb", RTFControlType::VALUE, RTFKeyword::TRPADDFB, 0 }, + { "trpaddfl", RTFControlType::VALUE, RTFKeyword::TRPADDFL, 0 }, + { "trpaddfr", RTFControlType::VALUE, RTFKeyword::TRPADDFR, 0 }, + { "trpaddft", RTFControlType::VALUE, RTFKeyword::TRPADDFT, 0 }, + { "trpaddl", RTFControlType::VALUE, RTFKeyword::TRPADDL, 0 }, + { "trpaddr", RTFControlType::VALUE, RTFKeyword::TRPADDR, 0 }, + { "trpaddt", RTFControlType::VALUE, RTFKeyword::TRPADDT, 0 }, + { "trpadob", RTFControlType::VALUE, RTFKeyword::TRPADOB, 0 }, + { "trpadofb", RTFControlType::VALUE, RTFKeyword::TRPADOFB, 0 }, + { "trpadofl", RTFControlType::VALUE, RTFKeyword::TRPADOFL, 0 }, + { "trpadofr", RTFControlType::VALUE, RTFKeyword::TRPADOFR, 0 }, + { "trpadoft", RTFControlType::VALUE, RTFKeyword::TRPADOFT, 0 }, + { "trpadol", RTFControlType::VALUE, RTFKeyword::TRPADOL, 0 }, + { "trpador", RTFControlType::VALUE, RTFKeyword::TRPADOR, 0 }, + { "trpadot", RTFControlType::VALUE, RTFKeyword::TRPADOT, 0 }, + { "trpat", RTFControlType::VALUE, RTFKeyword::TRPAT, 0 }, + { "trqc", RTFControlType::FLAG, RTFKeyword::TRQC, 0 }, + { "trql", RTFControlType::FLAG, RTFKeyword::TRQL, 0 }, + { "trqr", RTFControlType::FLAG, RTFKeyword::TRQR, 0 }, + { "trrh", RTFControlType::VALUE, RTFKeyword::TRRH, 0 }, + { "trshdng", RTFControlType::VALUE, RTFKeyword::TRSHDNG, 0 }, + { "trspdb", RTFControlType::VALUE, RTFKeyword::TRSPDB, 0 }, + { "trspdfb", RTFControlType::VALUE, RTFKeyword::TRSPDFB, 0 }, + { "trspdfl", RTFControlType::VALUE, RTFKeyword::TRSPDFL, 0 }, + { "trspdfr", RTFControlType::VALUE, RTFKeyword::TRSPDFR, 0 }, + { "trspdft", RTFControlType::VALUE, RTFKeyword::TRSPDFT, 0 }, + { "trspdl", RTFControlType::VALUE, RTFKeyword::TRSPDL, 0 }, + { "trspdr", RTFControlType::VALUE, RTFKeyword::TRSPDR, 0 }, + { "trspdt", RTFControlType::VALUE, RTFKeyword::TRSPDT, 0 }, + { "trspob", RTFControlType::VALUE, RTFKeyword::TRSPOB, 0 }, + { "trspofb", RTFControlType::VALUE, RTFKeyword::TRSPOFB, 0 }, + { "trspofl", RTFControlType::VALUE, RTFKeyword::TRSPOFL, 0 }, + { "trspofr", RTFControlType::VALUE, RTFKeyword::TRSPOFR, 0 }, + { "trspoft", RTFControlType::VALUE, RTFKeyword::TRSPOFT, 0 }, + { "trspol", RTFControlType::VALUE, RTFKeyword::TRSPOL, 0 }, + { "trspor", RTFControlType::VALUE, RTFKeyword::TRSPOR, 0 }, + { "trspot", RTFControlType::VALUE, RTFKeyword::TRSPOT, 0 }, + { "truncatefontheight", RTFControlType::FLAG, RTFKeyword::TRUNCATEFONTHEIGHT, 0 }, + { "truncex", RTFControlType::FLAG, RTFKeyword::TRUNCEX, 0 }, + { "trwWidthA", RTFControlType::VALUE, RTFKeyword::TRWWIDTHA, 0 }, + { "trwWidthB", RTFControlType::VALUE, RTFKeyword::TRWWIDTHB, 0 }, + { "trwWidth", RTFControlType::VALUE, RTFKeyword::TRWWIDTH, 0 }, + { "ts", RTFControlType::VALUE, RTFKeyword::TS, 0 }, + { "tsbgbdiag", RTFControlType::FLAG, RTFKeyword::TSBGBDIAG, 0 }, + { "tsbgcross", RTFControlType::FLAG, RTFKeyword::TSBGCROSS, 0 }, + { "tsbgdcross", RTFControlType::FLAG, RTFKeyword::TSBGDCROSS, 0 }, + { "tsbgdkbdiag", RTFControlType::FLAG, RTFKeyword::TSBGDKBDIAG, 0 }, + { "tsbgdkcross", RTFControlType::FLAG, RTFKeyword::TSBGDKCROSS, 0 }, + { "tsbgdkdcross", RTFControlType::FLAG, RTFKeyword::TSBGDKDCROSS, 0 }, + { "tsbgdkfdiag", RTFControlType::FLAG, RTFKeyword::TSBGDKFDIAG, 0 }, + { "tsbgdkhor", RTFControlType::FLAG, RTFKeyword::TSBGDKHOR, 0 }, + { "tsbgdkvert", RTFControlType::FLAG, RTFKeyword::TSBGDKVERT, 0 }, + { "tsbgfdiag", RTFControlType::FLAG, RTFKeyword::TSBGFDIAG, 0 }, + { "tsbghoriz", RTFControlType::FLAG, RTFKeyword::TSBGHORIZ, 0 }, + { "tsbgvert", RTFControlType::FLAG, RTFKeyword::TSBGVERT, 0 }, + { "tsbrdrb", RTFControlType::FLAG, RTFKeyword::TSBRDRB, 0 }, + { "tsbrdrdgl", RTFControlType::FLAG, RTFKeyword::TSBRDRDGL, 0 }, + { "tsbrdrdgr", RTFControlType::FLAG, RTFKeyword::TSBRDRDGR, 0 }, + { "tsbrdrh", RTFControlType::FLAG, RTFKeyword::TSBRDRH, 0 }, + { "tsbrdrl", RTFControlType::FLAG, RTFKeyword::TSBRDRL, 0 }, + { "tsbrdrr", RTFControlType::FLAG, RTFKeyword::TSBRDRR, 0 }, + { "tsbrdrt", RTFControlType::FLAG, RTFKeyword::TSBRDRT, 0 }, + { "tsbrdrv", RTFControlType::FLAG, RTFKeyword::TSBRDRV, 0 }, + { "tscbandhorzeven", RTFControlType::FLAG, RTFKeyword::TSCBANDHORZEVEN, 0 }, + { "tscbandhorzodd", RTFControlType::FLAG, RTFKeyword::TSCBANDHORZODD, 0 }, + { "tscbandsh", RTFControlType::VALUE, RTFKeyword::TSCBANDSH, 0 }, + { "tscbandsv", RTFControlType::VALUE, RTFKeyword::TSCBANDSV, 0 }, + { "tscbandverteven", RTFControlType::FLAG, RTFKeyword::TSCBANDVERTEVEN, 0 }, + { "tscbandvertodd", RTFControlType::FLAG, RTFKeyword::TSCBANDVERTODD, 0 }, + { "tscellcbpat", RTFControlType::VALUE, RTFKeyword::TSCELLCBPAT, 0 }, + { "tscellcfpat", RTFControlType::VALUE, RTFKeyword::TSCELLCFPAT, 0 }, + { "tscellpaddb", RTFControlType::VALUE, RTFKeyword::TSCELLPADDB, 0 }, + { "tscellpaddfb", RTFControlType::VALUE, RTFKeyword::TSCELLPADDFB, 0 }, + { "tscellpaddfl", RTFControlType::VALUE, RTFKeyword::TSCELLPADDFL, 0 }, + { "tscellpaddfr", RTFControlType::VALUE, RTFKeyword::TSCELLPADDFR, 0 }, + { "tscellpaddft", RTFControlType::VALUE, RTFKeyword::TSCELLPADDFT, 0 }, + { "tscellpaddl", RTFControlType::VALUE, RTFKeyword::TSCELLPADDL, 0 }, + { "tscellpaddr", RTFControlType::VALUE, RTFKeyword::TSCELLPADDR, 0 }, + { "tscellpaddt", RTFControlType::VALUE, RTFKeyword::TSCELLPADDT, 0 }, + { "tscellpct", RTFControlType::VALUE, RTFKeyword::TSCELLPCT, 0 }, + { "tscellwidth", RTFControlType::VALUE, RTFKeyword::TSCELLWIDTH, 0 }, + { "tscellwidthfts", RTFControlType::VALUE, RTFKeyword::TSCELLWIDTHFTS, 0 }, + { "tscfirstcol", RTFControlType::FLAG, RTFKeyword::TSCFIRSTCOL, 0 }, + { "tscfirstrow", RTFControlType::FLAG, RTFKeyword::TSCFIRSTROW, 0 }, + { "tsclastcol", RTFControlType::FLAG, RTFKeyword::TSCLASTCOL, 0 }, + { "tsclastrow", RTFControlType::FLAG, RTFKeyword::TSCLASTROW, 0 }, + { "tscnecell", RTFControlType::FLAG, RTFKeyword::TSCNECELL, 0 }, + { "tscnwcell", RTFControlType::FLAG, RTFKeyword::TSCNWCELL, 0 }, + { "tscsecell", RTFControlType::FLAG, RTFKeyword::TSCSECELL, 0 }, + { "tscswcell", RTFControlType::FLAG, RTFKeyword::TSCSWCELL, 0 }, + { "tsd", RTFControlType::FLAG, RTFKeyword::TSD, 0 }, + { "tsnowrap", RTFControlType::FLAG, RTFKeyword::TSNOWRAP, 0 }, + { "tsrowd", RTFControlType::FLAG, RTFKeyword::TSROWD, 0 }, + { "tsvertalb", RTFControlType::FLAG, RTFKeyword::TSVERTALB, 0 }, + { "tsvertalc", RTFControlType::FLAG, RTFKeyword::TSVERTALC, 0 }, + { "tsvertalt", RTFControlType::FLAG, RTFKeyword::TSVERTALT, 0 }, + { "twoinone", RTFControlType::VALUE, RTFKeyword::TWOINONE, 0 }, + { "twoonone", RTFControlType::FLAG, RTFKeyword::TWOONONE, 0 }, + { "tx", RTFControlType::VALUE, RTFKeyword::TX, 0 }, + { "txbxtwalways", RTFControlType::FLAG, RTFKeyword::TXBXTWALWAYS, 0 }, + { "txbxtwfirst", RTFControlType::FLAG, RTFKeyword::TXBXTWFIRST, 0 }, + { "txbxtwfirstlast", RTFControlType::FLAG, RTFKeyword::TXBXTWFIRSTLAST, 0 }, + { "txbxtwlast", RTFControlType::FLAG, RTFKeyword::TXBXTWLAST, 0 }, + { "txbxtwno", RTFControlType::FLAG, RTFKeyword::TXBXTWNO, 0 }, + { "txe", RTFControlType::DESTINATION, RTFKeyword::TXE, 0 }, + { "u", RTFControlType::VALUE, RTFKeyword::U, 0 }, + { "uc", RTFControlType::VALUE, RTFKeyword::UC, 1 }, + { "ud", RTFControlType::DESTINATION, RTFKeyword::UD, 0 }, + { "ul", RTFControlType::TOGGLE, RTFKeyword::UL, 1 }, + { "ulc", RTFControlType::VALUE, RTFKeyword::ULC, 0 }, + { "uld", RTFControlType::FLAG, RTFKeyword::ULD, 0 }, + { "uldash", RTFControlType::TOGGLE, RTFKeyword::ULDASH, 1 }, + { "uldashd", RTFControlType::TOGGLE, RTFKeyword::ULDASHD, 1 }, + { "uldashdd", RTFControlType::TOGGLE, RTFKeyword::ULDASHDD, 1 }, + { "uldb", RTFControlType::TOGGLE, RTFKeyword::ULDB, 1 }, + { "ulhair", RTFControlType::TOGGLE, RTFKeyword::ULHAIR, 1 }, + { "ulhwave", RTFControlType::TOGGLE, RTFKeyword::ULHWAVE, 1 }, + { "ulldash", RTFControlType::TOGGLE, RTFKeyword::ULLDASH, 1 }, + { "ulnone", RTFControlType::FLAG, RTFKeyword::ULNONE, 0 }, + { "ulth", RTFControlType::TOGGLE, RTFKeyword::ULTH, 1 }, + { "ulthd", RTFControlType::TOGGLE, RTFKeyword::ULTHD, 1 }, + { "ulthdash", RTFControlType::TOGGLE, RTFKeyword::ULTHDASH, 1 }, + { "ulthdashd", RTFControlType::TOGGLE, RTFKeyword::ULTHDASHD, 1 }, + { "ulthdashdd", RTFControlType::TOGGLE, RTFKeyword::ULTHDASHDD, 1 }, + { "ulthldash", RTFControlType::TOGGLE, RTFKeyword::ULTHLDASH, 1 }, + { "ululdbwave", RTFControlType::TOGGLE, RTFKeyword::ULULDBWAVE, 1 }, + { "ulw", RTFControlType::FLAG, RTFKeyword::ULW, 0 }, + { "ulwave", RTFControlType::TOGGLE, RTFKeyword::ULWAVE, 1 }, + { "up", RTFControlType::VALUE, RTFKeyword::UP, 6 }, + { "upr", RTFControlType::DESTINATION, RTFKeyword::UPR, 0 }, + { "urtf", RTFControlType::VALUE, RTFKeyword::URTF, 0 }, + { "useltbaln", RTFControlType::FLAG, RTFKeyword::USELTBALN, 0 }, + { "usenormstyforlist", RTFControlType::FLAG, RTFKeyword::USENORMSTYFORLIST, 0 }, + { "userprops", RTFControlType::DESTINATION, RTFKeyword::USERPROPS, 0 }, + { "usexform", RTFControlType::FLAG, RTFKeyword::USEXFORM, 0 }, + { "utinl", RTFControlType::FLAG, RTFKeyword::UTINL, 0 }, + { "v", RTFControlType::TOGGLE, RTFKeyword::V, 1 }, + { "validatexml", RTFControlType::VALUE, RTFKeyword::VALIDATEXML, 0 }, + { "vern", RTFControlType::VALUE, RTFKeyword::VERN, 0 }, + { "version", RTFControlType::VALUE, RTFKeyword::VERSION, 0 }, + { "vertal", RTFControlType::FLAG, RTFKeyword::VERTAL, 0 }, + { "vertalb", RTFControlType::FLAG, RTFKeyword::VERTALB, 0 }, + { "vertalc", RTFControlType::FLAG, RTFKeyword::VERTALC, 0 }, + { "vertalj", RTFControlType::FLAG, RTFKeyword::VERTALJ, 0 }, + { "vertalt", RTFControlType::FLAG, RTFKeyword::VERTALT, 0 }, + { "vertdoc", RTFControlType::FLAG, RTFKeyword::VERTDOC, 0 }, + { "vertsect", RTFControlType::FLAG, RTFKeyword::VERTSECT, 0 }, + { "viewbksp", RTFControlType::VALUE, RTFKeyword::VIEWBKSP, 0 }, + { "viewkind", RTFControlType::VALUE, RTFKeyword::VIEWKIND, 0 }, + { "viewnobound", RTFControlType::FLAG, RTFKeyword::VIEWNOBOUND, 0 }, + { "viewscale", RTFControlType::VALUE, RTFKeyword::VIEWSCALE, 100 }, + { "viewzk", RTFControlType::VALUE, RTFKeyword::VIEWZK, 0 }, + { "wbitmap", RTFControlType::VALUE, RTFKeyword::WBITMAP, 0 }, + { "wbmbitspixel", RTFControlType::VALUE, RTFKeyword::WBMBITSPIXEL, 1 }, + { "wbmplanes", RTFControlType::VALUE, RTFKeyword::WBMPLANES, 0 }, + { "wbmwidthbyte", RTFControlType::VALUE, RTFKeyword::WBMWIDTHBYTE, 0 }, + { "webhidden", RTFControlType::FLAG, RTFKeyword::WEBHIDDEN, 0 }, + { "wgrffmtfilter", RTFControlType::DESTINATION, RTFKeyword::WGRFFMTFILTER, 0 }, + { "widctlpar", RTFControlType::FLAG, RTFKeyword::WIDCTLPAR, 0 }, + { "widowctrl", RTFControlType::FLAG, RTFKeyword::WIDOWCTRL, 0 }, + { "windowcaption", RTFControlType::DESTINATION, RTFKeyword::WINDOWCAPTION, 0 }, + { "wmetafile", RTFControlType::VALUE, RTFKeyword::WMETAFILE, 1 }, + { "wpeqn", RTFControlType::FLAG, RTFKeyword::WPEQN, 0 }, + { "wpjst", RTFControlType::FLAG, RTFKeyword::WPJST, 0 }, + { "wpsp", RTFControlType::FLAG, RTFKeyword::WPSP, 0 }, + { "wraparound", RTFControlType::FLAG, RTFKeyword::WRAPAROUND, 0 }, + { "wrapdefault", RTFControlType::FLAG, RTFKeyword::WRAPDEFAULT, 0 }, + { "wrapthrough", RTFControlType::FLAG, RTFKeyword::WRAPTHROUGH, 0 }, + { "wraptight", RTFControlType::FLAG, RTFKeyword::WRAPTIGHT, 0 }, + { "wraptrsp", RTFControlType::FLAG, RTFKeyword::WRAPTRSP, 0 }, + { "writereservation", RTFControlType::DESTINATION, RTFKeyword::WRITERESERVATION, 0 }, + { "writereservhash", RTFControlType::DESTINATION, RTFKeyword::WRITERESERVHASH, 0 }, + { "wrppunct", RTFControlType::FLAG, RTFKeyword::WRPPUNCT, 0 }, + { "xe", RTFControlType::DESTINATION, RTFKeyword::XE, 0 }, + { "xef", RTFControlType::VALUE, RTFKeyword::XEF, 0 }, + { "xform", RTFControlType::DESTINATION, RTFKeyword::XFORM, 0 }, + { "xmlattr", RTFControlType::FLAG, RTFKeyword::XMLATTR, 0 }, + { "xmlattrname", RTFControlType::DESTINATION, RTFKeyword::XMLATTRNAME, 0 }, + { "xmlattrns", RTFControlType::VALUE, RTFKeyword::XMLATTRNS, 0 }, + { "xmlattrvalue", RTFControlType::DESTINATION, RTFKeyword::XMLATTRVALUE, 0 }, + { "xmlclose", RTFControlType::DESTINATION, RTFKeyword::XMLCLOSE, 0 }, + { "xmlname", RTFControlType::DESTINATION, RTFKeyword::XMLNAME, 0 }, + { "xmlns", RTFControlType::VALUE, RTFKeyword::XMLNS, 0 }, + { "xmlnstbl", RTFControlType::DESTINATION, RTFKeyword::XMLNSTBL, 0 }, + { "xmlopen", RTFControlType::DESTINATION, RTFKeyword::XMLOPEN, 0 }, + { "xmlsdttcell", RTFControlType::FLAG, RTFKeyword::XMLSDTTCELL, 0 }, + { "xmlsdttpara", RTFControlType::FLAG, RTFKeyword::XMLSDTTPARA, 0 }, + { "xmlsdttregular", RTFControlType::FLAG, RTFKeyword::XMLSDTTREGULAR, 0 }, + { "xmlsdttrow", RTFControlType::FLAG, RTFKeyword::XMLSDTTROW, 0 }, + { "xmlsdttunknown", RTFControlType::FLAG, RTFKeyword::XMLSDTTUNKNOWN, 0 }, + { "yr", RTFControlType::VALUE, RTFKeyword::YR, 0 }, + { "yts", RTFControlType::VALUE, RTFKeyword::YTS, 0 }, + { "yxe", RTFControlType::FLAG, RTFKeyword::YXE, 0 }, + { "zwbo", RTFControlType::SYMBOL, RTFKeyword::ZWBO, 0 }, + { "zwj", RTFControlType::SYMBOL, RTFKeyword::ZWJ, 0 }, + { "zwnbo", RTFControlType::SYMBOL, RTFKeyword::ZWNBO, 0 }, + { "zwnj", RTFControlType::SYMBOL, RTFKeyword::ZWNJ, 0 }, + { "flymaincnt", RTFControlType::DESTINATION, RTFKeyword::FLYMAINCNT, 0 }, + { "flyvert", RTFControlType::VALUE, RTFKeyword::FLYVERT, 0 }, + { "flyhorz", RTFControlType::VALUE, RTFKeyword::FLYHORZ, 0 }, + { "flyanchor", RTFControlType::VALUE, RTFKeyword::FLYANCHOR, 0 }, +}; +const int nRTFControlWords = SAL_N_ELEMENTS(aRTFControlWords); + +RTFMathSymbol const aRTFMathControlWords[] = { + // eKeyword nToken eDestination + { RTFKeyword::MOMATH, M_TOKEN(oMath), Destination::MOMATH }, + { RTFKeyword::MF, M_TOKEN(f), Destination::MF }, + { RTFKeyword::MFPR, M_TOKEN(fPr), Destination::MFPR }, + { RTFKeyword::MCTRLPR, M_TOKEN(ctrlPr), Destination::MCTRLPR }, + { RTFKeyword::MNUM, M_TOKEN(num), Destination::MNUM }, + { RTFKeyword::MDEN, M_TOKEN(den), Destination::MDEN }, + { RTFKeyword::MACC, M_TOKEN(acc), Destination::MACC }, + { RTFKeyword::MACCPR, M_TOKEN(accPr), Destination::MACCPR }, + { RTFKeyword::MBAR, M_TOKEN(bar), Destination::MBAR }, + { RTFKeyword::MBARPR, M_TOKEN(barPr), Destination::MBARPR }, + { RTFKeyword::ME, M_TOKEN(e), Destination::ME }, + { RTFKeyword::MD, M_TOKEN(d), Destination::MD }, + { RTFKeyword::MDPR, M_TOKEN(dPr), Destination::MDPR }, + { RTFKeyword::MFUNC, M_TOKEN(func), Destination::MFUNC }, + { RTFKeyword::MFUNCPR, M_TOKEN(funcPr), Destination::MFUNCPR }, + { RTFKeyword::MFNAME, M_TOKEN(fName), Destination::MFNAME }, + { RTFKeyword::MLIMLOW, M_TOKEN(limLow), Destination::MLIMLOW }, + { RTFKeyword::MLIMLOWPR, M_TOKEN(limLowPr), Destination::MLIMLOWPR }, + { RTFKeyword::MLIM, M_TOKEN(lim), Destination::MLIM }, + { RTFKeyword::MM, M_TOKEN(m), Destination::MM }, + { RTFKeyword::MMPR, M_TOKEN(mPr), Destination::MMPR }, + { RTFKeyword::MMR, M_TOKEN(mr), Destination::MMR }, + { RTFKeyword::MNARY, M_TOKEN(nary), Destination::MNARY }, + { RTFKeyword::MNARYPR, M_TOKEN(naryPr), Destination::MNARYPR }, + { RTFKeyword::MSUB, M_TOKEN(sub), Destination::MSUB }, + { RTFKeyword::MSUP, M_TOKEN(sup), Destination::MSUP }, + { RTFKeyword::MLIMUPP, M_TOKEN(limUpp), Destination::MLIMUPP }, + { RTFKeyword::MLIMUPPPR, M_TOKEN(limUppPr), Destination::MLIMUPPPR }, + { RTFKeyword::MGROUPCHR, M_TOKEN(groupChr), Destination::MGROUPCHR }, + { RTFKeyword::MGROUPCHRPR, M_TOKEN(groupChrPr), Destination::MGROUPCHRPR }, + { RTFKeyword::MBORDERBOX, M_TOKEN(borderBox), Destination::MBORDERBOX }, + { RTFKeyword::MBORDERBOXPR, M_TOKEN(borderBoxPr), Destination::MBORDERBOXPR }, + { RTFKeyword::MRAD, M_TOKEN(rad), Destination::MRAD }, + { RTFKeyword::MRADPR, M_TOKEN(radPr), Destination::MRADPR }, + { RTFKeyword::MDEG, M_TOKEN(deg), Destination::MDEG }, + { RTFKeyword::MSSUB, M_TOKEN(sSub), Destination::MSSUB }, + { RTFKeyword::MSSUBPR, M_TOKEN(sSubPr), Destination::MSSUBPR }, + { RTFKeyword::MSSUP, M_TOKEN(sSup), Destination::MSSUP }, + { RTFKeyword::MSSUPPR, M_TOKEN(sSupPr), Destination::MSSUPPR }, + { RTFKeyword::MSSUBSUP, M_TOKEN(sSubSup), Destination::MSSUBSUP }, + { RTFKeyword::MSSUBSUPPR, M_TOKEN(sSubSupPr), Destination::MSSUBSUPPR }, + { RTFKeyword::MSPRE, M_TOKEN(sPre), Destination::MSPRE }, + { RTFKeyword::MSPREPR, M_TOKEN(sPrePr), Destination::MSPREPR }, + { RTFKeyword::MBOX, M_TOKEN(box), Destination::MBOX }, + { RTFKeyword::MEQARR, M_TOKEN(eqArr), Destination::MEQARR }, +}; +const int nRTFMathControlWords = SAL_N_ELEMENTS(aRTFMathControlWords); + +bool RTFMathSymbol::operator<(const RTFMathSymbol& rOther) const +{ + return m_eKeyword < rOther.m_eKeyword; +} + +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfcontrolwords.hxx b/writerfilter/source/rtftok/rtfcontrolwords.hxx new file mode 100644 index 000000000..c1480ffb0 --- /dev/null +++ b/writerfilter/source/rtftok/rtfcontrolwords.hxx @@ -0,0 +1,2049 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace writerfilter::rtftok +{ +/** + * An RTF destination state is the last open destination control word. + * + * Note that this is not a 1:1 mapping between destination control + * words, e.g. RTF_PICT gets mapped to Destination::PICT or + * Destination::SHAPEPROPERTYVALUEPICT. + */ +enum class Destination +{ + NORMAL, + SKIP, + FONTTABLE, + FONTENTRY, + COLORTABLE, + STYLESHEET, + STYLEENTRY, + FIELD, + FIELDINSTRUCTION, + FIELDRESULT, + LISTTABLE, + LISTPICTURE, + LISTENTRY, + LISTNAME, + LISTOVERRIDETABLE, + LISTOVERRIDEENTRY, + LISTLEVEL, + LEVELTEXT, + LEVELNUMBERS, + SHPPICT, + PICT, + PICPROP, + SHAPEPROPERTY, + SHAPEPROPERTYNAME, + SHAPEPROPERTYVALUE, + SHAPE, + SHAPEINSTRUCTION, + SHAPEPROPERTYVALUEPICT, + NESTEDTABLEPROPERTIES, + FOOTNOTE, + BOOKMARKSTART, + BOOKMARKEND, + REVISIONTABLE, + REVISIONENTRY, + SHAPETEXT, + FORMFIELD, + FORMFIELDNAME, + FORMFIELDLIST, + DATAFIELD, + INFO, + CREATIONTIME, + REVISIONTIME, + PRINTTIME, + AUTHOR, + KEYWORDS, + OPERATOR, + COMPANY, + COMMENT, + OBJECT, + OBJDATA, + OBJCLASS, + RESULT, + ANNOTATIONDATE, + ANNOTATIONAUTHOR, + ANNOTATIONREFERENCE, + FALT, + FLYMAINCONTENT, + DRAWINGOBJECT, + PARAGRAPHNUMBERING, + PARAGRAPHNUMBERING_TEXTBEFORE, + PARAGRAPHNUMBERING_TEXTAFTER, + TITLE, + SUBJECT, + DOCCOMM, + ATNID, + ANNOTATIONREFERENCESTART, + ANNOTATIONREFERENCEEND, + MOMATH, + MR, + MF, + MFPR, + MCTRLPR, + MNUM, + MDEN, + MACC, + MACCPR, + MCHR, + MPOS, + MVERTJC, + MSTRIKEH, + MDEGHIDE, + ME, + MBAR, + MBARPR, + MD, + MDPR, + MBEGCHR, + MSEPCHR, + MENDCHR, + MFUNC, + MFUNCPR, + MFNAME, + MLIMLOW, + MLIMLOWPR, + MLIM, + MM, + MMPR, + MMR, + MNARY, + MNARYPR, + MSUB, + MSUP, + MSUBHIDE, + MSUPHIDE, + MLIMUPP, + MLIMUPPPR, + MGROUPCHR, + MGROUPCHRPR, + MBORDERBOX, + MBORDERBOXPR, + MRAD, + MRADPR, + MDEG, + MSSUB, + MSSUBPR, + MSSUP, + MSSUPPR, + MSSUBSUP, + MSSUBSUPPR, + MSPRE, + MSPREPR, + MTYPE, + MGROW, + MBOX, + MEQARR, + UPR, + LFOLEVEL, + BACKGROUND, + SHAPEGROUP, + FOOTNOTESEPARATOR, + INDEXENTRY, + TOCENTRY, + USERPROPS, + PROPNAME, + STATICVAL, + GENERATOR, + DOCVAR, +}; + +enum class RTFKeyword +{ + invalid = -1, + HEXCHAR, + OPTHYPH, + IGNORE, + SUBENTRY, + BACKSLASH, + NOBRKHYPH, + LBRACE, + FORMULA, + RBRACE, + NOBREAK, + AB, + ABSH, + ABSLOCK, + ABSNOOVRLP, + ABSW, + ACAPS, + ACCCIRCLE, + ACCCOMMA, + ACCDOT, + ACCNONE, + ACCUNDERDOT, + ACF, + ADEFF, + ADDITIVE, + ADEFLANG, + ADJUSTRIGHT, + ADN, + AENDDOC, + AENDNOTES, + AEXPND, + AF, + AFELEV, + AFS, + AFTNBJ, + AFTNCN, + AFTNNALC, + AFTNNAR, + AFTNNAUC, + AFTNNCHI, + AFTNNCHOSUNG, + AFTNNCNUM, + AFTNNDBAR, + AFTNNDBNUM, + AFTNNDBNUMD, + AFTNNDBNUMK, + AFTNNDBNUMT, + AFTNNGANADA, + AFTNNGBNUM, + AFTNNGBNUMD, + AFTNNGBNUMK, + AFTNNGBNUML, + AFTNNRLC, + AFTNNRUC, + AFTNNZODIAC, + AFTNNZODIACD, + AFTNNZODIACL, + AFTNRESTART, + AFTNRSTCONT, + AFTNSEP, + AFTNSEPC, + AFTNSTART, + AFTNTJ, + AI, + ALANG, + ALLOWFIELDENDSEL, + ALLPROT, + ALNTBLIND, + ALT, + ANIMTEXT, + ANNOTATION, + ANNOTPROT, + ANSI, + ANSICPG, + AOUTL, + APPLYBRKRULES, + ASCAPS, + ASHAD, + ASIANBRKRULE, + ASPALPHA, + ASPNUM, + ASTRIKE, + ATNAUTHOR, + ATNDATE, + ATNICN, + ATNID, + ATNPARENT, + ATNREF, + ATNTIME, + ATRFEND, + ATRFSTART, + AUL, + AULD, + AULDB, + AULNONE, + AULW, + AUP, + AUTHOR, + AUTOFMTOVERRIDE, + B, + BACKGROUND, + BDBFHDR, + BDRRLSWSIX, + BGBDIAG, + BGCROSS, + BGDCROSS, + BGDKBDIAG, + BGDKCROSS, + BGDKDCROSS, + BGDKFDIAG, + BGDKHORIZ, + BGDKVERT, + BGFDIAG, + BGHORIZ, + BGVERT, + BIN, + BINFSXN, + BINSXN, + BKMKCOLF, + BKMKCOLL, + BKMKEND, + BKMKPUB, + BKMKSTART, + BLIPTAG, + BLIPUID, + BLIPUPI, + BLUE, + BOOKFOLD, + BOOKFOLDREV, + BOOKFOLDSHEETS, + BOX, + BRDRART, + BRDRB, + BRDRBAR, + BRDRBTW, + BRDRCF, + BRDRDASH, + BRDRDASHD, + BRDRDASHDD, + BRDRDASHDOTSTR, + BRDRDASHSM, + BRDRDB, + BRDRDOT, + BRDREMBOSS, + BRDRENGRAVE, + BRDRFRAME, + BRDRHAIR, + BRDRINSET, + BRDRL, + BRDRNIL, + BRDRNONE, + BRDROUTSET, + BRDRR, + BRDRS, + BRDRSH, + BRDRT, + BRDRTBL, + BRDRTH, + BRDRTHTNLG, + BRDRTHTNMG, + BRDRTHTNSG, + BRDRTNTHLG, + BRDRTNTHMG, + BRDRTNTHSG, + BRDRTNTHTNLG, + BRDRTNTHTNMG, + BRDRTNTHTNSG, + BRDRTRIPLE, + BRDRW, + BRDRWAVY, + BRDRWAVYDB, + BRKFRM, + BRSP, + BULLET, + BUPTIM, + BXE, + CACCENTFIVE, + CACCENTFOUR, + CACCENTONE, + CACCENTSIX, + CACCENTTHREE, + CACCENTTWO, + CACHEDCOLBAL, + CAPS, + CATEGORY, + CB, + CBACKGROUNDONE, + CBACKGROUNDTWO, + CBPAT, + CCHS, + CELL, + CELLX, + CF, + CFOLLOWEDHYPERLINK, + CFPAT, + CGRID, + CHARRSID, + CHARSCALEX, + CHATN, + CHBGBDIAG, + CHBGCROSS, + CHBGDCROSS, + CHBGDKBDIAG, + CHBGDKCROSS, + CHBGDKDCROSS, + CHBGDKFDIAG, + CHBGDKHORIZ, + CHBGDKVERT, + CHBGFDIAG, + CHBGHORIZ, + CHBGVERT, + CHBRDR, + CHCBPAT, + CHCFPAT, + CHDATE, + CHDPA, + CHDPL, + CHFTN, + CHFTNSEP, + CHFTNSEPC, + CHPGN, + CHHRES, + CHSHDNG, + CHTIME, + CHYPERLINK, + CLBGBDIAG, + CLBGCROSS, + CLBGDCROSS, + CLBGDKBDIAG, + CLBGDKCROSS, + CLBGDKDCROSS, + CLBGDKFDIAG, + CLBGDKHOR, + CLBGDKVERT, + CLBGFDIAG, + CLBGHORIZ, + CLBGVERT, + CLBRDRB, + CLBRDRL, + CLBRDRR, + CLBRDRT, + CLCBPAT, + CLCBPATRAW, + CLCFPAT, + CLCFPATRAW, + CLDEL, + CLDELAUTH, + CLDELDTTM, + CLDGLL, + CLDGLU, + CLFITTEXT, + CLFTSWIDTH, + CLHIDEMARK, + CLINS, + CLINSAUTH, + CLINSDTTM, + CLMGF, + CLMRG, + CLMRGD, + CLMRGDAUTH, + CLMRGDDTTM, + CLMRGDR, + CLNOWRAP, + CLPADB, + CLPADFB, + CLPADFL, + CLPADFR, + CLPADFT, + CLPADL, + CLPADR, + CLPADT, + CLSPB, + CLSPFB, + CLSPFL, + CLSPFR, + CLSPFT, + CLSPL, + CLSPR, + CLSPT, + CLSHDNG, + CLSHDNGRAW, + CLSHDRAWNIL, + CLSPLIT, + CLSPLITR, + CLTXBTLR, + CLTXLRTB, + CLTXLRTBV, + CLTXTBRL, + CLTXTBRLV, + CLVERTALB, + CLVERTALC, + CLVERTALT, + CLVMGF, + CLVMRG, + CLWWIDTH, + CMAINDARKONE, + CMAINDARKTWO, + CMAINLIGHTONE, + CMAINLIGHTTWO, + COLLAPSED, + COLNO, + COLORSCHEMEMAPPING, + COLORTBL, + COLS, + COLSR, + COLSX, + COLUMN, + COLW, + COMMENT, + COMPANY, + CONTEXTUALSPACE, + CPG, + CRAUTH, + CRDATE, + CREATIM, + CS, + CSHADE, + CTEXTONE, + CTEXTTWO, + CTINT, + CTRL, + CTS, + CUFI, + CULI, + CURI, + CVMME, + DATAFIELD, + DATASTORE, + DATE, + DBCH, + DEFCHP, + DEFF, + DEFFORMAT, + DEFLANG, + DEFLANGFE, + DEFPAP, + DEFSHP, + DEFTAB, + DELETED, + DELRSID, + DFRAUTH, + DFRDATE, + DFRMTXTX, + DFRMTXTY, + DFRSTART, + DFRSTOP, + DFRXST, + DGHORIGIN, + DGHSHOW, + DGHSPACE, + DGMARGIN, + DGSNAP, + DGVORIGIN, + DGVSHOW, + DGVSPACE, + DIBITMAP, + DISABLED, + DN, + DNTBLNSBDB, + DO, + DOBXCOLUMN, + DOBXMARGIN, + DOBXPAGE, + DOBYMARGIN, + DOBYPAGE, + DOBYPARA, + DOCCOMM, + DOCTEMP, + DOCTYPE, + DOCVAR, + DODHGT, + DOLOCK, + DONOTEMBEDLINGDATA, + DONOTEMBEDSYSFONT, + DONOTSHOWCOMMENTS, + DONOTSHOWINSDEL, + DONOTSHOWMARKUP, + DONOTSHOWPROPS, + DPAENDHOL, + DPAENDL, + DPAENDSOL, + DPAENDW, + DPARC, + DPARCFLIPX, + DPARCFLIPY, + DPASTARTHOL, + DPASTARTL, + DPASTARTSOL, + DPASTARTW, + DPCALLOUT, + DPCOA, + DPCOACCENT, + DPCOBESTFIT, + DPCOBORDER, + DPCODABS, + DPCODBOTTOM, + DPCODCENTER, + DPCODESCENT, + DPCODTOP, + DPCOLENGTH, + DPCOMINUSX, + DPCOMINUSY, + DPCOOFFSET, + DPCOSMARTA, + DPCOTDOUBLE, + DPCOTRIGHT, + DPCOTSINGLE, + DPCOTTRIPLE, + DPCOUNT, + DPELLIPSE, + DPENDGROUP, + DPFILLBGCB, + DPFILLBGCG, + DPFILLBGCR, + DPFILLBGGRAY, + DPFILLBGPAL, + DPFILLFGCB, + DPFILLFGCG, + DPFILLFGCR, + DPFILLFGGRAY, + DPFILLFGPAL, + DPFILLPAT, + DPGROUP, + DPLINE, + DPLINECOB, + DPLINECOG, + DPLINECOR, + DPLINEDADO, + DPLINEDADODO, + DPLINEDASH, + DPLINEDOT, + DPLINEGRAY, + DPLINEHOLLOW, + DPLINEPAL, + DPLINESOLID, + DPLINEW, + DPPOLYCOUNT, + DPPOLYGON, + DPPOLYLINE, + DPPTX, + DPPTY, + DPRECT, + DPROUNDR, + DPSHADOW, + DPSHADX, + DPSHADY, + DPTXBTLR, + DPTXBX, + DPTXBXMAR, + DPTXBXTEXT, + DPTXLRTB, + DPTXLRTBV, + DPTXTBRL, + DPTXTBRLV, + DPX, + DPXSIZE, + DPY, + DPYSIZE, + DROPCAPLI, + DROPCAPT, + DS, + DXFRTEXT, + DY, + EBCEND, + EBCSTART, + EDMINS, + EMBO, + EMDASH, + EMFBLIP, + EMSPACE, + ENDASH, + ENDDOC, + ENDNHERE, + ENDNOTES, + ENFORCEPROT, + ENSPACE, + EXPND, + EXPNDTW, + EXPSHRTN, + F, + FAAUTO, + FACENTER, + FACINGP, + FACTOIDNAME, + FAFIXED, + FAHANG, + FALT, + FAROMAN, + FAVAR, + FBIAS, + FBIDI, + FBIDIS, + FBIMAJOR, + FBIMINOR, + FCHARS, + FCHARSET, + FCS, + FDBMAJOR, + FDBMINOR, + FDECOR, + FELNBRELEV, + FET, + FETCH, + FFDEFRES, + FFDEFTEXT, + FFENTRYMCR, + FFEXITMCR, + FFFORMAT, + FFHASLISTBOX, + FFHELPTEXT, + FFHPS, + FFL, + FFMAXLEN, + FFNAME, + FFOWNHELP, + FFOWNSTAT, + FFPROT, + FFRECALC, + FFRES, + FFSIZE, + FFSTATTEXT, + FFTYPE, + FFTYPETXT, + FHIMAJOR, + FHIMINOR, + FI, + FID, + FIELD, + FILE, + FILETBL, + FITTEXT, + FJGOTHIC, + FJMINCHOU, + FLDALT, + FLDDIRTY, + FLDEDIT, + FLDINST, + FLDLOCK, + FLDPRIV, + FLDRSLT, + FLDTYPE, + FLOMAJOR, + FLOMINOR, + FMODERN, + FN, + FNAME, + FNETWORK, + FNIL, + FNONFILESYS, + FONTEMB, + FONTFILE, + FONTTBL, + FOOTER, + FOOTERF, + FOOTERL, + FOOTERR, + FOOTERY, + FOOTNOTE, + FORCEUPGRADE, + FORMDISP, + FORMFIELD, + FORMPROT, + FORMSHADE, + FOSNUM, + FPRQ, + FRACWIDTH, + FRELATIVE, + FRMTXBTLR, + FRMTXLRTB, + FRMTXLRTBV, + FRMTXTBRL, + FRMTXTBRLV, + FROMAN, + FROMHTML, + FROMTEXT, + FS, + FSCRIPT, + FSWISS, + FTECH, + FTNALT, + FTNBJ, + FTNCN, + FTNIL, + FTNLYTWNINE, + FTNNALC, + FTNNAR, + FTNNAUC, + FTNNCHI, + FTNNCHOSUNG, + FTNNCNUM, + FTNNDBAR, + FTNNDBNUM, + FTNNDBNUMD, + FTNNDBNUMK, + FTNNDBNUMT, + FTNNGANADA, + FTNNGBNUM, + FTNNGBNUMD, + FTNNGBNUMK, + FTNNGBNUML, + FTNNRLC, + FTNNRUC, + FTNNZODIAC, + FTNNZODIACD, + FTNNZODIACL, + FTNRESTART, + FTNRSTCONT, + FTNRSTPG, + FTNSEP, + FTNSEPC, + FTNSTART, + FTNTJ, + FTTRUETYPE, + FVALIDDOS, + FVALIDHPFS, + FVALIDMAC, + FVALIDNTFS, + G, + GCW, + GENERATOR, + GREEN, + GRFDOCEVENTS, + GRIDTBL, + GUTTER, + GUTTERPRL, + GUTTERSXN, + HEADER, + HEADERF, + HEADERL, + HEADERR, + HEADERY, + HICH, + HIGHLIGHT, + HL, + HLFR, + HLINKBASE, + HLLOC, + HLSRC, + HORZDOC, + HORZSECT, + HORZVERT, + HR, + HRES, + HRULE, + HSV, + HTMAUTSP, + HTMLBASE, + HTMLRTF, + HTMLTAG, + HWELEV, + HYPHAUTO, + HYPHCAPS, + HYPHCONSEC, + HYPHHOTZ, + HYPHPAR, + I, + ID, + IGNOREMIXEDCONTENT, + ILFOMACATCLNUP, + ILVL, + IMPR, + INDMIRROR, + INDRLSWELEVEN, + INFO, + INSRSID, + INTBL, + IPGP, + IROWBAND, + IROW, + ITAP, + IXE, + JCOMPRESS, + JEXPAND, + JIS, + JPEGBLIP, + JSKSU, + KEEP, + KEEPN, + KERNING, + KEYCODE, + KEYWORDS, + KRNPRSNET, + KSULANG, + JCLISTTAB, + LANDSCAPE, + LANG, + LANGFE, + LANGFENP, + LANGNP, + LASTROW, + LATENTSTYLES, + LBR, + LCHARS, + LDBLQUOTE, + LEVEL, + LEVELFOLLOW, + LEVELINDENT, + LEVELJC, + LEVELJCN, + LEVELLEGAL, + LEVELNFC, + LEVELNFCN, + LEVELNORESTART, + LEVELNUMBERS, + LEVELOLD, + LEVELPICTURE, + LEVELPICTURENOSIZE, + LEVELPREV, + LEVELPREVSPACE, + LEVELSPACE, + LEVELSTARTAT, + LEVELTEMPLATEID, + LEVELTEXT, + LFOLEVEL, + LI, + LINE, + LINEBETCOL, + LINECONT, + LINEMOD, + LINEPPAGE, + LINERESTART, + LINESTART, + LINESTARTS, + LINEX, + LINKSELF, + LINKSTYLES, + LINKVAL, + LIN, + LISA, + LISB, + LIST, + LISTHYBRID, + LISTID, + LISTLEVEL, + LISTNAME, + LISTOVERRIDE, + LISTOVERRIDECOUNT, + LISTOVERRIDEFORMAT, + LISTOVERRIDESTARTAT, + LISTOVERRIDETABLE, + LISTPICTURE, + LISTRESTARTHDN, + LISTSIMPLE, + LISTSTYLEID, + LISTSTYLENAME, + LISTTABLE, + LISTTEMPLATEID, + LISTTEXT, + LNBRKRULE, + LNDSCPSXN, + LNONGRID, + LOCH, + LQUOTE, + LS, + LSDLOCKED, + LSDLOCKEDDEF, + LSDLOCKEDEXCEPT, + LSDPRIORITY, + LSDPRIORITYDEF, + LSDQFORMAT, + LSDQFORMATDEF, + LSDSEMIHIDDEN, + LSDSEMIHIDDENDEF, + LSDSTIMAX, + LSDUNHIDEUSED, + LSDUNHIDEUSEDDEF, + LTRCH, + LTRDOC, + LTRMARK, + LTRPAR, + LTRROW, + LTRSECT, + LVLTENTATIVE, + LYTCALCTBLWD, + LYTEXCTTP, + LYTPRTMET, + LYTTBLRTGR, + MAC, + MACC, + MACCPR, + MACPICT, + MAILMERGE, + MAKEBACKUP, + MALN, + MALNSCR, + MANAGER, + MARGB, + MARGBSXN, + MARGL, + MARGLSXN, + MARGMIRROR, + MARGMIRSXN, + MARGPR, + MARGR, + MARGRSXN, + MARGSZ, + MARGT, + MARGTSXN, + MBAR, + MBARPR, + MBASEJC, + MBEGCHR, + MBORDERBOX, + MBORDERBOXPR, + MBOX, + MBOXPR, + MBRK, + MBRKBIN, + MBRKBINSUB, + MCGP, + MCGPRULE, + MCHR, + MCOUNT, + MCSP, + MCTRLPR, + MD, + MDEFJC, + MDEG, + MDEGHIDE, + MDEN, + MDIFF, + MDIFFSTY, + MDISPDEF, + MDPR, + ME, + MENDCHR, + MEQARR, + MEQARRPR, + MF, + MFNAME, + MFPR, + MFUNC, + MFUNCPR, + MGROUPCHR, + MGROUPCHRPR, + MGROW, + MHIDEBOT, + MHIDELEFT, + MHIDERIGHT, + MHIDETOP, + MHTMLTAG, + MIN, + MINTERSP, + MINTLIM, + MINTRASP, + MJC, + MLIM, + MLIMLOC, + MLIMLOW, + MLIMLOWPR, + MLIMUPP, + MLIMUPPPR, + MLIT, + MLMARGIN, + MM, + MMADDFIELDNAME, + MMATH, + MMATHFONT, + MMATHPICT, + MMATHPR, + MMATTACH, + MMAXDIST, + MMBLANKLINES, + MMC, + MMCJC, + MMCONNECTSTR, + MMCONNECTSTRDATA, + MMCPR, + MMCS, + MMDATASOURCE, + MMDATATYPEACCESS, + MMDATATYPEEXCEL, + MMDATATYPEFILE, + MMDATATYPEODBC, + MMDATATYPEODSO, + MMDATATYPEQT, + MMDEFAULTSQL, + MMDESTEMAIL, + MMDESTFAX, + MMDESTNEWDOC, + MMDESTPRINTER, + MMERRORS, + MMFTTYPEADDRESS, + MMFTTYPEBARCODE, + MMFTTYPEDBCOLUMN, + MMFTTYPEMAPPED, + MMFTTYPENULL, + MMFTTYPESALUTATION, + MMHEADERSOURCE, + MMJDSOTYPE, + MMLINKTOQUERY, + MMMAILSUBJECT, + MMMAINTYPECATALOG, + MMMAINTYPEEMAIL, + MMMAINTYPEENVELOPES, + MMMAINTYPEFAX, + MMMAINTYPELABELS, + MMMAINTYPELETTERS, + MMODSO, + MMODSOACTIVE, + MMODSOCOLDELIM, + MMODSOCOLUMN, + MMODSODYNADDR, + MMODSOFHDR, + MMODSOFILTER, + MMODSOFLDMPDATA, + MMODSOFMCOLUMN, + MMODSOHASH, + MMODSOLID, + MMODSOMAPPEDNAME, + MMODSONAME, + MMODSORECIPDATA, + MMODSOSORT, + MMODSOSRC, + MMODSOTABLE, + MMODSOUDL, + MMODSOUDLDATA, + MMODSOUNIQUETAG, + MMPR, + MMQUERY, + MMR, + MMRECCUR, + MMSHOWDATA, + MNARY, + MNARYLIM, + MNARYPR, + MNOBREAK, + MNOR, + MNUM, + MO, + MOBJDIST, + MOMATH, + MOMATHPARA, + MOMATHPARAPR, + MOPEMU, + MPHANT, + MPHANTPR, + MPLCHIDE, + MPOS, + MPOSTSP, + MPRESP, + MR, + MRAD, + MRADPR, + MRMARGIN, + MRPR, + MRSP, + MRSPRULE, + MSCR, + MSEPCHR, + MSHOW, + MSHP, + MSMALLFRAC, + MSMCAP, + MSPRE, + MSPREPR, + MSSUB, + MSSUBPR, + MSSUBSUP, + MSSUBSUPPR, + MSSUP, + MSSUPPR, + MSTRIKEBLTR, + MSTRIKEH, + MSTRIKETLBR, + MSTRIKEV, + MSTY, + MSUB, + MSUBHIDE, + MSUP, + MSUPHIDE, + MTRANSP, + MTYPE, + MUSER, + MVAUTH, + MVDATE, + MVERTJC, + MVF, + MVFMF, + MVFML, + MVT, + MVTOF, + MVTOL, + MWRAPINDENT, + MWRAPRIGHT, + MZEROASC, + MZERODESC, + MZEROWID, + NESTCELL, + NESTROW, + NESTTABLEPROPS, + NEWTBLSTYRULS, + NEXTFILE, + NOAFCNSTTBL, + NOBRKWRPTBL, + NOCOLBAL, + NOCOMPATOPTIONS, + NOCWRAP, + NOCXSPTABLE, + NOEXTRASPRL, + NOFCHARS, + NOFCHARSWS, + NOFEATURETHROTTLE, + NOFPAGES, + NOFWORDS, + NOGROWAUTOFIT, + NOINDNMBRTS, + NOJKERNPUNCT, + NOLEAD, + NOLINE, + NOLNHTADJTBL, + NONESTTABLES, + NONSHPPICT, + NOOVERFLOW, + NOPROOF, + NOQFPROMOTE, + NOSECTEXPAND, + NOSNAPLINEGRID, + NOSPACEFORUL, + NOSUPERSUB, + NOTABIND, + NOTBRKCNSTFRCTBL, + NOTCVASP, + NOTVATXBX, + NOUICOMPAT, + NOULTRLSPC, + NOWIDCTLPAR, + NOWRAP, + NOWWRAP, + NOXLATTOYEN, + OBJALIAS, + OBJALIGN, + OBJATTPH, + OBJAUTLINK, + OBJCLASS, + OBJCROPB, + OBJCROPL, + OBJCROPR, + OBJCROPT, + OBJDATA, + OBJECT, + OBJEMB, + OBJH, + OBJHTML, + OBJICEMB, + OBJLINK, + OBJLOCK, + OBJNAME, + OBJOCX, + OBJPUB, + OBJSCALEX, + OBJSCALEY, + OBJSECT, + OBJSETSIZE, + OBJSUB, + OBJTIME, + OBJTRANSY, + OBJUPDATE, + OBJW, + OGUTTER, + OLDAS, + OLDCPROPS, + OLDLINEWRAP, + OLDPPROPS, + OLDSPROPS, + OLDTPROPS, + OLECLSID, + OPERATOR, + OTBLRUL, + OUTL, + OUTLINELEVEL, + OVERLAY, + PAGE, + PAGEBB, + PANOSE, + PAPERH, + PAPERW, + PAR, + PARARSID, + PARD, + PASSWORD, + PASSWORDHASH, + PC, + PCA, + PGBRDRB, + PGBRDRFOOT, + PGBRDRHEAD, + PGBRDRL, + PGBRDROPT, + PGBRDRR, + PGBRDRSNAP, + PGBRDRT, + PGHSXN, + PGNBIDIA, + PGNBIDIB, + PGNCHOSUNG, + PGNCNUM, + PGNCONT, + PGNDBNUM, + PGNDBNUMD, + PGNDBNUMK, + PGNDBNUMT, + PGNDEC, + PGNDECD, + PGNGANADA, + PGNGBNUM, + PGNGBNUMD, + PGNGBNUMK, + PGNGBNUML, + PGNHINDIA, + PGNHINDIB, + PGNHINDIC, + PGNHINDID, + PGNHN, + PGNHNSC, + PGNHNSH, + PGNHNSM, + PGNHNSN, + PGNHNSP, + PGNID, + PGNLCLTR, + PGNLCRM, + PGNRESTART, + PGNSTART, + PGNSTARTS, + PGNTHAIA, + PGNTHAIB, + PGNTHAIC, + PGNUCLTR, + PGNUCRM, + PGNVIETA, + PGNX, + PGNY, + PGNZODIAC, + PGNZODIACD, + PGNZODIACL, + PGP, + PGPTBL, + PGWSXN, + PHCOL, + PHMRG, + PHPG, + PICBMP, + PICBPP, + PICCROPB, + PICCROPL, + PICCROPR, + PICCROPT, + PICH, + PICHGOAL, + PICPROP, + PICSCALED, + PICSCALEX, + PICSCALEY, + PICT, + PICW, + PICWGOAL, + PINDTABQC, + PINDTABQL, + PINDTABQR, + PLAIN, + PMARTABQC, + PMARTABQL, + PMARTABQR, + PMMETAFILE, + PN, + PNACROSS, + PNAIU, + PNAIUD, + PNAIUEO, + PNAIUEOD, + PNB, + PNBIDIA, + PNBIDIB, + PNCAPS, + PNCARD, + PNCF, + PNCHOSUNG, + PNCNUM, + PNDBNUM, + PNDBNUMD, + PNDBNUMK, + PNDBNUML, + PNDBNUMT, + PNDEC, + PNDECD, + PNF, + PNFS, + PNGANADA, + PNGBLIP, + PNGBNUM, + PNGBNUMD, + PNGBNUMK, + PNGBNUML, + PNHANG, + PNI, + PNINDENT, + PNIROHA, + PNIROHAD, + PNLCLTR, + PNLCRM, + PNLVL, + PNLVLBLT, + PNLVLBODY, + PNLVLCONT, + PNNUMONCE, + PNORD, + PNORDT, + PNPREV, + PNQC, + PNQL, + PNQR, + PNRAUTH, + PNRDATE, + PNRESTART, + PNRNFC, + PNRNOT, + PNRPNBR, + PNRRGB, + PNRSTART, + PNRSTOP, + PNRXST, + PNSCAPS, + PNSECLVL, + PNSP, + PNSTART, + PNSTRIKE, + PNTEXT, + PNTXTA, + PNTXTB, + PNUCLTR, + PNUCRM, + PNUL, + PNULD, + PNULDASH, + PNULDASHD, + PNULDASHDD, + PNULDB, + PNULHAIR, + PNULNONE, + PNULTH, + PNULW, + PNULWAVE, + PNZODIAC, + PNZODIACD, + PNZODIACL, + POSNEGX, + POSNEGY, + POSX, + POSXC, + POSXI, + POSXL, + POSXO, + POSXR, + POSY, + POSYB, + POSYC, + POSYIL, + POSYIN, + POSYOUT, + POSYT, + PRAUTH, + PRCOLBL, + PRDATE, + PRINTDATA, + PRINTIM, + PRIVATE, + PROPNAME, + PROPTYPE, + PROTECT, + PROTEND, + PROTLEVEL, + PROTSTART, + PROTUSERTBL, + PSOVER, + PSZ, + PTABLDOT, + PTABLMDOT, + PTABLMINUS, + PTABLNONE, + PTABLUSCORE, + PUBAUTO, + PVMRG, + PVPARA, + PVPG, + PWD, + PXE, + QC, + QD, + QJ, + QK, + QL, + QMSPACE, + QR, + QT, + RAWCLBGDKBDIAG, + RAWCLBGBDIAG, + RAWCLBGCROSS, + RAWCLBGDCROSS, + RAWCLBGDKCROSS, + RAWCLBGDKDCROSS, + RAWCLBGDKFDIAG, + RAWCLBGDKHOR, + RAWCLBGDKVERT, + RAWCLBGFDIAG, + RAWCLBGHORIZ, + RAWCLBGVERT, + RDBLQUOTE, + READONLYRECOMMENDED, + READPROT, + RED, + RELYONVML, + REMDTTM, + REMPERSONALINFO, + RESULT, + REVAUTH, + REVAUTHDEL, + REVBAR, + REVDTTM, + REVDTTMDEL, + REVISED, + REVISIONS, + REVPROP, + REVPROT, + REVTBL, + REVTIM, + RI, + RIN, + ROW, + RQUOTE, + RSID, + RSIDROOT, + RSIDTBL, + RSLTBMP, + RSLTHTML, + RSLTMERGE, + RSLTPICT, + RSLTRTF, + RSLTTXT, + RTF, + RTLCH, + RTLDOC, + RTLGUTTER, + RTLMARK, + RTLPAR, + RTLROW, + RTLSECT, + RXE, + S, + SA, + SAAUTO, + SAFTNNALC, + SAFTNNAR, + SAFTNNAUC, + SAFTNNCHI, + SAFTNNCHOSUNG, + SAFTNNCNUM, + SAFTNNDBAR, + SAFTNNDBNUM, + SAFTNNDBNUMD, + SAFTNNDBNUMK, + SAFTNNDBNUMT, + SAFTNNGANADA, + SAFTNNGBNUM, + SAFTNNGBNUMD, + SAFTNNGBNUMK, + SAFTNNGBNUML, + SAFTNNRLC, + SAFTNNRUC, + SAFTNNZODIAC, + SAFTNNZODIACD, + SAFTNNZODIACL, + SAFTNRESTART, + SAFTNRSTCONT, + SAFTNSTART, + SAUTOUPD, + SAVEINVALIDXML, + SAVEPREVPICT, + SB, + SBASEDON, + SBAUTO, + SBKCOL, + SBKEVEN, + SBKNONE, + SBKODD, + SBKPAGE, + SBYS, + SCAPS, + SCOMPOSE, + SEC, + SECT, + SECTD, + SECTDEFAULTCL, + SECTEXPAND, + SECTLINEGRID, + SECTNUM, + SECTRSID, + SECTSPECIFYCL, + SECTSPECIFYGENN, + SECTSPECIFYL, + SECTUNLOCKED, + SFTNBJ, + SFTNNALC, + SFTNNAR, + SFTNNAUC, + SFTNNCHI, + SFTNNCHOSUNG, + SFTNNCNUM, + SFTNNDBAR, + SFTNNDBNUM, + SFTNNDBNUMD, + SFTNNDBNUMK, + SFTNNDBNUMT, + SFTNNGANADA, + SFTNNGBNUM, + SFTNNGBNUMD, + SFTNNGBNUMK, + SFTNNGBNUML, + SFTNNRLC, + SFTNNRUC, + SFTNNZODIAC, + SFTNNZODIACD, + SFTNNZODIACL, + SFTNRESTART, + SFTNRSTCONT, + SFTNRSTPG, + SFTNSTART, + SFTNTJ, + SHAD, + SHADING, + SHIDDEN, + SHIFT, + SHOWPLACEHOLDTEXT, + SHOWXMLERRORS, + SHP, + SHPBOTTOM, + SHPBXCOLUMN, + SHPBXIGNORE, + SHPBXMARGIN, + SHPBXPAGE, + SHPBYIGNORE, + SHPBYMARGIN, + SHPBYPAGE, + SHPBYPARA, + SHPFBLWTXT, + SHPFHDR, + SHPGRP, + SHPINST, + SHPLEFT, + SHPLID, + SHPLOCKANCHOR, + SHPPICT, + SHPRIGHT, + SHPRSLT, + SHPTOP, + SHPTXT, + SHPWRK, + SHPWR, + SHPZ, + SL, + SLINK, + SLMULT, + SLOCKED, + SN, + SNAPTOGRIDINCELL, + SNEXT, + SOFTCOL, + SOFTLHEIGHT, + SOFTLINE, + SOFTPAGE, + SP, + SPERSONAL, + SPLTPGPAR, + SPLYTWNINE, + SPRIORITY, + SPRSBSP, + SPRSLNSP, + SPRSSPBF, + SPRSTSM, + SPRSTSP, + SPV, + SQFORMAT, + SRAUTH, + SRDATE, + SREPLY, + SSEMIHIDDEN, + STATICVAL, + STEXTFLOW, + STRIKE, + STRIKED, + STSHFBI, + STSHFDBCH, + STSHFHICH, + STSHFLOCH, + STYLELOCK, + STYLELOCKBACKCOMP, + STYLELOCKENFORCED, + STYLELOCKQFSET, + STYLELOCKTHEME, + STYLESHEET, + STYLESORTMETHOD, + STYRSID, + SUB, + SUBDOCUMENT, + SUBFONTBYSIZE, + SUBJECT, + SUNHIDEUSED, + SUPER, + SV, + SVB, + SWPBDR, + TAB, + TABSNOOVRLP, + TAPRTL, + TB, + TBLIND, + TBLINDTYPE, + TBLLKBESTFIT, + TBLLKBORDER, + TBLLKCOLOR, + TBLLKFONT, + TBLLKHDRCOLS, + TBLLKHDRROWS, + TBLLKLASTCOL, + TBLLKLASTROW, + TBLLKNOCOLBAND, + TBLLKNOROWBAND, + TBLLKSHADING, + TBLRSID, + TC, + TCELLD, + TCF, + TCL, + TCN, + TDFRMTXTBOTTOM, + TDFRMTXTLEFT, + TDFRMTXTRIGHT, + TDFRMTXTTOP, + TEMPLATE, + THEMEDATA, + THEMELANG, + THEMELANGCS, + THEMELANGFE, + TIME, + TITLE, + TITLEPG, + TLDOT, + TLEQ, + TLHYPH, + TLMDOT, + TLTH, + TLUL, + TOPLINEPUNCT, + TPHCOL, + TPHMRG, + TPHPG, + TPOSNEGX, + TPOSNEGY, + TPOSXC, + TPOSXI, + TPOSXL, + TPOSX, + TPOSXO, + TPOSXR, + TPOSY, + TPOSYB, + TPOSYC, + TPOSYIL, + TPOSYIN, + TPOSYOUT, + TPOSYT, + TPVMRG, + TPVPARA, + TPVPG, + TQC, + TQDEC, + TQR, + TRACKFORMATTING, + TRACKMOVES, + TRANSMF, + TRAUTH, + TRAUTOFIT, + TRBGBDIAG, + TRBGCROSS, + TRBGDCROSS, + TRBGDKBDIAG, + TRBGDKCROSS, + TRBGDKDCROSS, + TRBGDKFDIAG, + TRBGDKHOR, + TRBGDKVERT, + TRBGFDIAG, + TRBGHORIZ, + TRBGVERT, + TRBRDRB, + TRBRDRH, + TRBRDRL, + TRBRDRR, + TRBRDRT, + TRBRDRV, + TRCBPAT, + TRCFPAT, + TRDATE, + TRFTSWIDTHA, + TRFTSWIDTHB, + TRFTSWIDTH, + TRGAPH, + TRHDR, + TRKEEP, + TRKEEPFOLLOW, + TRLEFT, + TROWD, + TRPADDB, + TRPADDFB, + TRPADDFL, + TRPADDFR, + TRPADDFT, + TRPADDL, + TRPADDR, + TRPADDT, + TRPADOB, + TRPADOFB, + TRPADOFL, + TRPADOFR, + TRPADOFT, + TRPADOL, + TRPADOR, + TRPADOT, + TRPAT, + TRQC, + TRQL, + TRQR, + TRRH, + TRSHDNG, + TRSPDB, + TRSPDFB, + TRSPDFL, + TRSPDFR, + TRSPDFT, + TRSPDL, + TRSPDR, + TRSPDT, + TRSPOB, + TRSPOFB, + TRSPOFL, + TRSPOFR, + TRSPOFT, + TRSPOL, + TRSPOR, + TRSPOT, + TRUNCATEFONTHEIGHT, + TRUNCEX, + TRWWIDTHA, + TRWWIDTHB, + TRWWIDTH, + TS, + TSBGBDIAG, + TSBGCROSS, + TSBGDCROSS, + TSBGDKBDIAG, + TSBGDKCROSS, + TSBGDKDCROSS, + TSBGDKFDIAG, + TSBGDKHOR, + TSBGDKVERT, + TSBGFDIAG, + TSBGHORIZ, + TSBGVERT, + TSBRDRB, + TSBRDRDGL, + TSBRDRDGR, + TSBRDRH, + TSBRDRL, + TSBRDRR, + TSBRDRT, + TSBRDRV, + TSCBANDHORZEVEN, + TSCBANDHORZODD, + TSCBANDSH, + TSCBANDSV, + TSCBANDVERTEVEN, + TSCBANDVERTODD, + TSCELLCBPAT, + TSCELLCFPAT, + TSCELLPADDB, + TSCELLPADDFB, + TSCELLPADDFL, + TSCELLPADDFR, + TSCELLPADDFT, + TSCELLPADDL, + TSCELLPADDR, + TSCELLPADDT, + TSCELLPCT, + TSCELLWIDTH, + TSCELLWIDTHFTS, + TSCFIRSTCOL, + TSCFIRSTROW, + TSCLASTCOL, + TSCLASTROW, + TSCNECELL, + TSCNWCELL, + TSCSECELL, + TSCSWCELL, + TSD, + TSNOWRAP, + TSROWD, + TSVERTALB, + TSVERTALC, + TSVERTALT, + TWOINONE, + TWOONONE, + TX, + TXBXTWALWAYS, + TXBXTWFIRST, + TXBXTWFIRSTLAST, + TXBXTWLAST, + TXBXTWNO, + TXE, + U, + UC, + UD, + UL, + ULC, + ULD, + ULDASH, + ULDASHD, + ULDASHDD, + ULDB, + ULHAIR, + ULHWAVE, + ULLDASH, + ULNONE, + ULTH, + ULTHD, + ULTHDASH, + ULTHDASHD, + ULTHDASHDD, + ULTHLDASH, + ULULDBWAVE, + ULW, + ULWAVE, + UP, + UPR, + URTF, + USELTBALN, + USENORMSTYFORLIST, + USERPROPS, + USEXFORM, + UTINL, + V, + VALIDATEXML, + VERN, + VERSION, + VERTAL, + VERTALB, + VERTALC, + VERTALJ, + VERTALT, + VERTDOC, + VERTSECT, + VIEWBKSP, + VIEWKIND, + VIEWNOBOUND, + VIEWSCALE, + VIEWZK, + WBITMAP, + WBMBITSPIXEL, + WBMPLANES, + WBMWIDTHBYTE, + WEBHIDDEN, + WGRFFMTFILTER, + WIDCTLPAR, + WIDOWCTRL, + WINDOWCAPTION, + WMETAFILE, + WPEQN, + WPJST, + WPSP, + WRAPAROUND, + WRAPDEFAULT, + WRAPTHROUGH, + WRAPTIGHT, + WRAPTRSP, + WRITERESERVATION, + WRITERESERVHASH, + WRPPUNCT, + XE, + XEF, + XFORM, + XMLATTR, + XMLATTRNAME, + XMLATTRNS, + XMLATTRVALUE, + XMLCLOSE, + XMLNAME, + XMLNS, + XMLNSTBL, + XMLOPEN, + XMLSDTTCELL, + XMLSDTTPARA, + XMLSDTTREGULAR, + XMLSDTTROW, + XMLSDTTUNKNOWN, + YR, + YTS, + YXE, + ZWBO, + ZWJ, + ZWNBO, + ZWNJ, + FLYMAINCNT, + FLYVERT, + FLYHORZ, + FLYANCHOR +}; +const char* keywordToString(RTFKeyword nKeyword); + +/// Types of an RTF Control Word +enum class RTFControlType +{ + FLAG, // eg \sbknone takes no parameter + DESTINATION, // eg \fonttbl, if ignored, the whole group should be skipped + SYMBOL, // eg \tab + TOGGLE, // eg \b (between on and off) + VALUE // eg \fs (requires parameter) +}; + +/// Represents an RTF Control Word +class RTFSymbol +{ + const char* m_sKeyword; + RTFControlType m_eControlType; + RTFKeyword m_nIndex; + + int m_nDefValue; ///< Most of the control words default to 0. + +public: + RTFSymbol(const char* sKeyword, RTFControlType nControlType, RTFKeyword nIndex, int nDefValue) + : m_sKeyword(sKeyword) + , m_eControlType(nControlType) + , m_nIndex(nIndex) + , m_nDefValue(nDefValue) + { + } + + const char* GetKeyword() const { return m_sKeyword; } + + RTFControlType GetControlType() const { return m_eControlType; } + + RTFKeyword GetIndex() const { return m_nIndex; } + + int GetDefValue() const { return m_nDefValue; } +}; + +extern RTFSymbol const aRTFControlWords[]; +extern const int nRTFControlWords; + +/// Represents an RTF Math Control Word +class RTFMathSymbol +{ + RTFKeyword m_eKeyword; + int m_nToken; ///< This is the OOXML token equivalent. + Destination m_eDestination; + +public: + RTFMathSymbol(RTFKeyword eKeyword, int nToken = 0, + Destination eDestination = Destination::NORMAL) + : m_eKeyword(eKeyword) + , m_nToken(nToken) + , m_eDestination(eDestination) + { + } + + int GetToken() const { return m_nToken; } + + Destination GetDestination() const { return m_eDestination; } + + bool operator<(const RTFMathSymbol& rOther) const; +}; + +extern RTFMathSymbol const aRTFMathControlWords[]; +extern const int nRTFMathControlWords; + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdispatchdestination.cxx b/writerfilter/source/rtftok/rtfdispatchdestination.cxx new file mode 100644 index 000000000..8789c3f85 --- /dev/null +++ b/writerfilter/source/rtftok/rtfdispatchdestination.cxx @@ -0,0 +1,684 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <string_view> + +#include "rtfdocumentimpl.hxx" + +#include <com/sun/star/document/DocumentProperties.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <filter/msfilter/escherex.hxx> +#include <rtl/character.hxx> +#include <tools/stream.hxx> +#include <sal/log.hxx> + +#include <dmapper/DomainMapperFactory.hxx> +#include <ooxml/resourceids.hxx> + +#include "rtflookahead.hxx" +#include "rtfreferenceproperties.hxx" +#include "rtfsdrimport.hxx" +#include "rtfskipdestination.hxx" +#include "rtftokenizer.hxx" + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFError RTFDocumentImpl::dispatchDestination(RTFKeyword nKeyword) +{ + setNeedSect(true); + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + RTFSkipDestination aSkip(*this); + // special case \upr: ignore everything except nested \ud + if (Destination::UPR == m_aStates.top().getDestination() && RTFKeyword::UD != nKeyword) + { + m_aStates.top().setDestination(Destination::SKIP); + aSkip.setParsed(false); + } + else + switch (nKeyword) + { + case RTFKeyword::RTF: + break; + case RTFKeyword::FONTTBL: + m_aStates.top().setDestination(Destination::FONTTABLE); + break; + case RTFKeyword::COLORTBL: + m_aStates.top().setDestination(Destination::COLORTABLE); + break; + case RTFKeyword::STYLESHEET: + m_aStates.top().setDestination(Destination::STYLESHEET); + break; + case RTFKeyword::FIELD: + m_aStates.top().setDestination(Destination::FIELD); + m_aStates.top().setFieldLocked(false); + break; + case RTFKeyword::DOCVAR: + m_aStates.top().setDestination(Destination::DOCVAR); + break; + case RTFKeyword::FLDINST: + { + // Look for the field type + sal_uInt64 const nPos = Strm().Tell(); + OStringBuffer aBuf; + char ch = 0; + bool bFoundCode = false; + bool bInKeyword = false; + while (!bFoundCode && ch != '}') + { + Strm().ReadChar(ch); + if ('\\' == ch) + bInKeyword = true; + if (!bInKeyword && rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch))) + aBuf.append(ch); + else if (bInKeyword && rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch))) + bInKeyword = false; + if (!aBuf.isEmpty() + && !rtl::isAsciiAlphanumeric(static_cast<unsigned char>(ch))) + bFoundCode = true; + } + + if (std::string_view(aBuf) == "INCLUDEPICTURE") + { + // Extract the field argument of INCLUDEPICTURE: we handle that + // at a tokenizer level, as DOCX has no such field. + aBuf.append(ch); + while (true) + { + Strm().ReadChar(ch); + if (ch == '}') + break; + aBuf.append(ch); + } + OUString aFieldCommand = OStringToOUString(aBuf, RTL_TEXTENCODING_UTF8); + std::tuple<OUString, std::vector<OUString>, std::vector<OUString>> aResult + = writerfilter::dmapper::splitFieldCommand(aFieldCommand); + m_aPicturePath + = std::get<1>(aResult).empty() ? OUString() : std::get<1>(aResult).front(); + } + + Strm().Seek(nPos); + + // Form data should be handled only for form fields if any + if (aBuf.toString().indexOf("FORM") != -1) + m_bFormField = true; + + singleChar(cFieldStart); + m_aStates.top().setDestination(Destination::FIELDINSTRUCTION); + } + break; + case RTFKeyword::FLDRSLT: + m_aStates.top().setDestination(Destination::FIELDRESULT); + break; + case RTFKeyword::LISTTABLE: + m_aStates.top().setDestination(Destination::LISTTABLE); + break; + case RTFKeyword::LISTPICTURE: + m_aStates.top().setDestination(Destination::LISTPICTURE); + m_aStates.top().setInListpicture(true); + break; + case RTFKeyword::LIST: + m_aStates.top().setDestination(Destination::LISTENTRY); + break; + case RTFKeyword::LISTNAME: + m_aStates.top().setDestination(Destination::LISTNAME); + break; + case RTFKeyword::LFOLEVEL: + m_aStates.top().setDestination(Destination::LFOLEVEL); + m_aStates.top().getTableSprms().clear(); + break; + case RTFKeyword::LISTOVERRIDETABLE: + m_aStates.top().setDestination(Destination::LISTOVERRIDETABLE); + break; + case RTFKeyword::LISTOVERRIDE: + m_aStates.top().setDestination(Destination::LISTOVERRIDEENTRY); + break; + case RTFKeyword::LISTLEVEL: + m_aStates.top().setDestination(Destination::LISTLEVEL); + ++m_nListLevel; + break; + case RTFKeyword::LEVELTEXT: + m_aStates.top().setDestination(Destination::LEVELTEXT); + break; + case RTFKeyword::LEVELNUMBERS: + m_aStates.top().setDestination(Destination::LEVELNUMBERS); + break; + case RTFKeyword::SHPPICT: + resetFrame(); + m_aStates.top().setDestination(Destination::SHPPICT); + break; + case RTFKeyword::PICT: + if (m_aStates.top().getDestination() != Destination::SHAPEPROPERTYVALUE) + m_aStates.top().setDestination(Destination::PICT); // as character + else + m_aStates.top().setDestination( + Destination::SHAPEPROPERTYVALUEPICT); // anchored inside a shape + break; + case RTFKeyword::PICPROP: + m_aStates.top().setDestination(Destination::PICPROP); + break; + case RTFKeyword::SP: + m_aStates.top().setDestination(Destination::SHAPEPROPERTY); + break; + case RTFKeyword::SN: + m_aStates.top().setDestination(Destination::SHAPEPROPERTYNAME); + break; + case RTFKeyword::SV: + m_aStates.top().setDestination(Destination::SHAPEPROPERTYVALUE); + break; + case RTFKeyword::SHP: + m_bNeedCrOrig = m_bNeedCr; + m_aStates.top().setDestination(Destination::SHAPE); + m_aStates.top().setInShape(true); + break; + case RTFKeyword::SHPINST: + m_aStates.top().setDestination(Destination::SHAPEINSTRUCTION); + break; + case RTFKeyword::NESTTABLEPROPS: + // do not set any properties of outer table at nested table! + m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms(); + m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes(); + m_aNestedTableCellsSprms.clear(); + m_aNestedTableCellsAttributes.clear(); + m_nNestedCells = 0; + m_aStates.top().setDestination(Destination::NESTEDTABLEPROPERTIES); + break; + case RTFKeyword::HEADER: + case RTFKeyword::FOOTER: + case RTFKeyword::HEADERL: + case RTFKeyword::HEADERR: + case RTFKeyword::HEADERF: + case RTFKeyword::FOOTERL: + case RTFKeyword::FOOTERR: + case RTFKeyword::FOOTERF: + if (!m_pSuperstream) + { + Id nId = 0; + std::size_t nPos = m_nGroupStartPos - 1; + switch (nKeyword) + { + case RTFKeyword::HEADER: + if (!m_hasRHeader) + { + nId = NS_ooxml::LN_headerr; + m_hasRHeader = true; + } + break; + case RTFKeyword::FOOTER: + if (!m_hasRFooter) + { + nId = NS_ooxml::LN_footerr; + m_hasRFooter = true; + } + break; + case RTFKeyword::HEADERL: + nId = NS_ooxml::LN_headerl; + break; + case RTFKeyword::HEADERR: + nId = NS_ooxml::LN_headerr; + break; + case RTFKeyword::HEADERF: + if (!m_hasFHeader) + { + nId = NS_ooxml::LN_headerf; + m_hasFHeader = true; + } + break; + case RTFKeyword::FOOTERL: + nId = NS_ooxml::LN_footerl; + break; + case RTFKeyword::FOOTERR: + nId = NS_ooxml::LN_footerr; + break; + case RTFKeyword::FOOTERF: + if (!m_hasFFooter) + { + nId = NS_ooxml::LN_footerf; + m_hasFFooter = true; + } + break; + default: + break; + } + + if (nId != 0) + m_nHeaderFooterPositions.push(std::make_pair(nId, nPos)); + + m_aStates.top().setDestination(Destination::SKIP); + } + break; + case RTFKeyword::FOOTNOTE: + checkFirstRun(); + if (!m_pSuperstream) + { + Id nId = NS_ooxml::LN_footnote; + + // Check if this is an endnote. + OStringBuffer aBuf; + char ch; + sal_uInt64 const nCurrent = Strm().Tell(); + for (int i = 0; i < 7; ++i) + { + Strm().ReadChar(ch); + aBuf.append(ch); + } + Strm().Seek(nCurrent); + OString aKeyword = aBuf.makeStringAndClear(); + if (aKeyword == "\\ftnalt") + nId = NS_ooxml::LN_endnote; + + if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer) + m_aStates.top().setCurrentBuffer(nullptr); + bool bCustomMark = false; + OUString aCustomMark; + for (auto const& elem : m_aSuperBuffer) + { + if (std::get<0>(elem) == BUFFER_UTEXT) + { + aCustomMark = std::get<1>(elem)->getString(); + bCustomMark = true; + } + } + m_aSuperBuffer.clear(); + m_aStates.top().setDestination(Destination::FOOTNOTE); + Mapper().startCharacterGroup(); + runProps(); + if (!m_aStates.top().getCurrentBuffer()) + resolveSubstream(m_nGroupStartPos - 1, nId, aCustomMark); + else + { + RTFSprms aAttributes; + aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1)); + aAttributes.set(Id(1), new RTFValue(nId)); + aAttributes.set(Id(2), new RTFValue(aCustomMark)); + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr)); + } + if (bCustomMark) + { + m_aStates.top().getCharacterAttributes().clear(); + m_aStates.top().getCharacterSprms().clear(); + auto pValue = new RTFValue(1); + m_aStates.top().getCharacterAttributes().set( + NS_ooxml::LN_CT_FtnEdnRef_customMarkFollows, pValue); + text(aCustomMark); + } + Mapper().endCharacterGroup(); + m_aStates.top().setDestination(Destination::SKIP); + } + break; + case RTFKeyword::BKMKSTART: + m_aStates.top().setDestination(Destination::BOOKMARKSTART); + break; + case RTFKeyword::BKMKEND: + m_aStates.top().setDestination(Destination::BOOKMARKEND); + break; + case RTFKeyword::XE: + m_aStates.top().setDestination(Destination::INDEXENTRY); + break; + case RTFKeyword::TC: + case RTFKeyword::TCN: + m_aStates.top().setDestination(Destination::TOCENTRY); + break; + case RTFKeyword::REVTBL: + m_aStates.top().setDestination(Destination::REVISIONTABLE); + break; + case RTFKeyword::ANNOTATION: + if (!m_pSuperstream) + { + if (!m_aStates.top().getCurrentBuffer()) + { + resolveSubstream(m_nGroupStartPos - 1, NS_ooxml::LN_annotation); + } + else + { + RTFSprms aAttributes; + aAttributes.set(Id(0), new RTFValue(m_nGroupStartPos - 1)); + aAttributes.set(Id(1), new RTFValue(NS_ooxml::LN_annotation)); + aAttributes.set(Id(2), new RTFValue(OUString())); + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_RESOLVESUBSTREAM, new RTFValue(aAttributes), nullptr)); + } + m_aStates.top().setDestination(Destination::SKIP); + } + else + { + // If there is an author set, emit it now. + if (!m_aAuthor.isEmpty() || !m_aAuthorInitials.isEmpty()) + { + RTFSprms aAttributes; + if (!m_aAuthor.isEmpty()) + { + auto pValue = new RTFValue(m_aAuthor); + aAttributes.set(NS_ooxml::LN_CT_TrackChange_author, pValue); + } + if (!m_aAuthorInitials.isEmpty()) + { + auto pValue = new RTFValue(m_aAuthorInitials); + aAttributes.set(NS_ooxml::LN_CT_Comment_initials, pValue); + } + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes)); + Mapper().props(pProperties); + } + } + break; + case RTFKeyword::SHPTXT: + case RTFKeyword::DPTXBXTEXT: + { + bool bPictureFrame = false; + for (const auto& rProperty : m_aStates.top().getShape().getProperties()) + { + if (rProperty.first == "shapeType" + && rProperty.second + == std::u16string_view( + OUString::number(ESCHER_ShpInst_PictureFrame))) + { + bPictureFrame = true; + break; + } + } + if (bPictureFrame) + // Skip text on picture frames. + m_aStates.top().setDestination(Destination::SKIP); + else + { + m_aStates.top().setDestination(Destination::SHAPETEXT); + checkFirstRun(); + dispatchFlag(RTFKeyword::PARD); + m_bNeedPap = true; + if (nKeyword == RTFKeyword::SHPTXT) + { + if (!m_aStates.top().getCurrentBuffer()) + m_pSdrImport->resolve(m_aStates.top().getShape(), false, + RTFSdrImport::SHAPE); + else + { + auto pValue = new RTFValue(m_aStates.top().getShape()); + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_STARTSHAPE, pValue, nullptr)); + } + } + } + } + break; + case RTFKeyword::FORMFIELD: + if (m_aStates.top().getDestination() == Destination::FIELDINSTRUCTION) + m_aStates.top().setDestination(Destination::FORMFIELD); + break; + case RTFKeyword::FFNAME: + m_aStates.top().setDestination(Destination::FORMFIELDNAME); + break; + case RTFKeyword::FFL: + m_aStates.top().setDestination(Destination::FORMFIELDLIST); + break; + case RTFKeyword::DATAFIELD: + m_aStates.top().setDestination(Destination::DATAFIELD); + break; + case RTFKeyword::INFO: + m_aStates.top().setDestination(Destination::INFO); + break; + case RTFKeyword::CREATIM: + m_aStates.top().setDestination(Destination::CREATIONTIME); + break; + case RTFKeyword::REVTIM: + m_aStates.top().setDestination(Destination::REVISIONTIME); + break; + case RTFKeyword::PRINTIM: + m_aStates.top().setDestination(Destination::PRINTTIME); + break; + case RTFKeyword::AUTHOR: + m_aStates.top().setDestination(Destination::AUTHOR); + break; + case RTFKeyword::KEYWORDS: + m_aStates.top().setDestination(Destination::KEYWORDS); + break; + case RTFKeyword::OPERATOR: + m_aStates.top().setDestination(Destination::OPERATOR); + break; + case RTFKeyword::COMPANY: + m_aStates.top().setDestination(Destination::COMPANY); + break; + case RTFKeyword::COMMENT: + m_aStates.top().setDestination(Destination::COMMENT); + break; + case RTFKeyword::OBJECT: + { + // beginning of an OLE Object + m_aStates.top().setDestination(Destination::OBJECT); + + // check if the object is in a special container (e.g. a table) + if (!m_aStates.top().getCurrentBuffer()) + { + // the object is in a table or another container. + // Don't try to treat it as an OLE object (fdo#53594). + // Use the \result (RTFKeyword::RESULT) element of the object instead, + // the result element contain picture representing the OLE Object. + m_bObject = true; + } + } + break; + case RTFKeyword::OBJDATA: + // check if the object is in a special container (e.g. a table) + if (m_aStates.top().getCurrentBuffer()) + { + // the object is in a table or another container. + // Use the \result (RTFKeyword::RESULT) element of the object instead, + // of the \objdata. + m_aStates.top().setDestination(Destination::SKIP); + } + else + { + m_aStates.top().setDestination(Destination::OBJDATA); + } + break; + case RTFKeyword::OBJCLASS: + m_aStates.top().setDestination(Destination::OBJCLASS); + break; + case RTFKeyword::RESULT: + m_aStates.top().setDestination(Destination::RESULT); + break; + case RTFKeyword::ATNDATE: + m_aStates.top().setDestination(Destination::ANNOTATIONDATE); + break; + case RTFKeyword::ATNAUTHOR: + m_aStates.top().setDestination(Destination::ANNOTATIONAUTHOR); + break; + case RTFKeyword::ATNREF: + m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCE); + break; + case RTFKeyword::FALT: + m_aStates.top().setDestination(Destination::FALT); + break; + case RTFKeyword::FLYMAINCNT: + m_aStates.top().setDestination(Destination::FLYMAINCONTENT); + break; + case RTFKeyword::LISTTEXT: + // Should be ignored by any reader that understands Word 97 through Word 2007 numbering. + case RTFKeyword::NONESTTABLES: + // This destination should be ignored by readers that support nested tables. + m_aStates.top().setDestination(Destination::SKIP); + break; + case RTFKeyword::DO: + m_aStates.top().setDestination(Destination::DRAWINGOBJECT); + break; + case RTFKeyword::PN: + m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING); + break; + case RTFKeyword::PNTEXT: + // This destination should be ignored by readers that support paragraph numbering. + m_aStates.top().setDestination(Destination::SKIP); + break; + case RTFKeyword::PNTXTA: + m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTAFTER); + break; + case RTFKeyword::PNTXTB: + m_aStates.top().setDestination(Destination::PARAGRAPHNUMBERING_TEXTBEFORE); + break; + case RTFKeyword::TITLE: + m_aStates.top().setDestination(Destination::TITLE); + break; + case RTFKeyword::SUBJECT: + m_aStates.top().setDestination(Destination::SUBJECT); + break; + case RTFKeyword::DOCCOMM: + m_aStates.top().setDestination(Destination::DOCCOMM); + break; + case RTFKeyword::ATRFSTART: + m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCESTART); + break; + case RTFKeyword::ATRFEND: + m_aStates.top().setDestination(Destination::ANNOTATIONREFERENCEEND); + break; + case RTFKeyword::ATNID: + m_aStates.top().setDestination(Destination::ATNID); + break; + case RTFKeyword::MMATH: + case RTFKeyword::MOMATHPARA: + // Nothing to do here (just enter the destination) till RTFKeyword::MMATHPR is implemented. + break; + case RTFKeyword::MR: + m_aStates.top().setDestination(Destination::MR); + break; + case RTFKeyword::MCHR: + m_aStates.top().setDestination(Destination::MCHR); + break; + case RTFKeyword::MPOS: + m_aStates.top().setDestination(Destination::MPOS); + break; + case RTFKeyword::MVERTJC: + m_aStates.top().setDestination(Destination::MVERTJC); + break; + case RTFKeyword::MSTRIKEH: + m_aStates.top().setDestination(Destination::MSTRIKEH); + break; + case RTFKeyword::MDEGHIDE: + m_aStates.top().setDestination(Destination::MDEGHIDE); + break; + case RTFKeyword::MTYPE: + m_aStates.top().setDestination(Destination::MTYPE); + break; + case RTFKeyword::MGROW: + m_aStates.top().setDestination(Destination::MGROW); + break; + case RTFKeyword::MHIDETOP: + case RTFKeyword::MHIDEBOT: + case RTFKeyword::MHIDELEFT: + case RTFKeyword::MHIDERIGHT: + // SmOoxmlImport::handleBorderBox will ignore these anyway, so silently ignore for now. + m_aStates.top().setDestination(Destination::SKIP); + break; + case RTFKeyword::MSUBHIDE: + m_aStates.top().setDestination(Destination::MSUBHIDE); + break; + case RTFKeyword::MSUPHIDE: + m_aStates.top().setDestination(Destination::MSUPHIDE); + break; + case RTFKeyword::MBEGCHR: + m_aStates.top().setDestination(Destination::MBEGCHR); + break; + case RTFKeyword::MSEPCHR: + m_aStates.top().setDestination(Destination::MSEPCHR); + break; + case RTFKeyword::MENDCHR: + m_aStates.top().setDestination(Destination::MENDCHR); + break; + case RTFKeyword::UPR: + m_aStates.top().setDestination(Destination::UPR); + break; + case RTFKeyword::UD: + // Anything inside \ud is just normal Unicode content. + m_aStates.top().setDestination(Destination::NORMAL); + break; + case RTFKeyword::BACKGROUND: + m_aStates.top().setDestination(Destination::BACKGROUND); + m_aStates.top().setInBackground(true); + break; + case RTFKeyword::SHPGRP: + { + RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart()); + if (!aLookahead.hasTable()) + { + uno::Reference<drawing::XShapes> xGroupShape( + m_xModelFactory->createInstance("com.sun.star.drawing.GroupShape"), + uno::UNO_QUERY); + uno::Reference<drawing::XDrawPageSupplier> xDrawSupplier(m_xDstDoc, + uno::UNO_QUERY); + if (xDrawSupplier.is()) + { + uno::Reference<drawing::XShape> xShape(xGroupShape, uno::UNO_QUERY); + // set default VertOrient before inserting + uno::Reference<beans::XPropertySet>(xShape, uno::UNO_QUERY_THROW) + ->setPropertyValue("VertOrient", uno::Any(text::VertOrientation::NONE)); + xDrawSupplier->getDrawPage()->add(xShape); + } + m_pSdrImport->pushParent(xGroupShape); + m_aStates.top().setCreatedShapeGroup(true); + } + m_aStates.top().setDestination(Destination::SHAPEGROUP); + m_aStates.top().setInShapeGroup(true); + } + break; + case RTFKeyword::FTNSEP: + m_aStates.top().setDestination(Destination::FOOTNOTESEPARATOR); + m_aStates.top().getCharacterAttributes().set( + NS_ooxml::LN_CT_FtnEdn_type, + new RTFValue(NS_ooxml::LN_Value_doc_ST_FtnEdn_separator)); + break; + case RTFKeyword::USERPROPS: + // Container of all user-defined properties. + m_aStates.top().setDestination(Destination::USERPROPS); + if (m_xDocumentProperties.is()) + // Create a custom document properties to be able to process them later all at once. + m_xDocumentProperties = document::DocumentProperties::create(m_xContext); + break; + case RTFKeyword::PROPNAME: + m_aStates.top().setDestination(Destination::PROPNAME); + break; + case RTFKeyword::STATICVAL: + m_aStates.top().setDestination(Destination::STATICVAL); + break; + case RTFKeyword::GENERATOR: + m_aStates.top().setDestination(Destination::GENERATOR); + break; + default: + { + // Check if it's a math token. + RTFMathSymbol aSymbol(nKeyword); + if (RTFTokenizer::lookupMathKeyword(aSymbol)) + { + m_aMathBuffer.appendOpeningTag(aSymbol.GetToken()); + m_aStates.top().setDestination(aSymbol.GetDestination()); + return RTFError::OK; + } + + SAL_INFO("writerfilter", + "TODO handle destination '" << keywordToString(nKeyword) << "'"); + // Make sure we skip destinations (even without \*) till we don't handle them + m_aStates.top().setDestination(Destination::SKIP); + aSkip.setParsed(false); + } + break; + } + + // new destination => use new destination text + m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); + + return RTFError::OK; +} + +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdispatchflag.cxx b/writerfilter/source/rtftok/rtfdispatchflag.cxx new file mode 100644 index 000000000..0ef4f2172 --- /dev/null +++ b/writerfilter/source/rtftok/rtfdispatchflag.cxx @@ -0,0 +1,1258 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <sal/config.h> + +#include <string_view> + +#include "rtfdocumentimpl.hxx" + +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include <filter/msfilter/escherex.hxx> + +#include <ooxml/resourceids.hxx> + +#include <sal/log.hxx> + +#include "rtfsdrimport.hxx" +#include "rtfskipdestination.hxx" + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFError RTFDocumentImpl::dispatchFlag(RTFKeyword nKeyword) +{ + setNeedSect(true); + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + RTFSkipDestination aSkip(*this); + int nParam = -1; + int nSprm = -1; + + // Underline flags. + switch (nKeyword) + { + case RTFKeyword::ULD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dotted; + break; + case RTFKeyword::ULW: + nSprm = NS_ooxml::LN_Value_ST_Underline_words; + break; + default: + break; + } + if (nSprm >= 0) + { + auto pValue = new RTFValue(nSprm); + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue); + return RTFError::OK; + } + + // Indentation + switch (nKeyword) + { + case RTFKeyword::QC: + nParam = NS_ooxml::LN_Value_ST_Jc_center; + break; + case RTFKeyword::QJ: + nParam = NS_ooxml::LN_Value_ST_Jc_both; + break; + case RTFKeyword::QL: + nParam = NS_ooxml::LN_Value_ST_Jc_left; + break; + case RTFKeyword::QR: + nParam = NS_ooxml::LN_Value_ST_Jc_right; + break; + case RTFKeyword::QD: + nParam = NS_ooxml::LN_Value_ST_Jc_distribute; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_jc, pValue); + m_bNeedPap = true; + return RTFError::OK; + } + + // Font Alignment + switch (nKeyword) + { + case RTFKeyword::FAFIXED: + case RTFKeyword::FAAUTO: + nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_auto; + break; + case RTFKeyword::FAHANG: + nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_top; + break; + case RTFKeyword::FACENTER: + nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_center; + break; + case RTFKeyword::FAROMAN: + nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_baseline; + break; + case RTFKeyword::FAVAR: + nParam = NS_ooxml::LN_Value_doc_ST_TextAlignment_bottom; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_textAlignment, pValue); + return RTFError::OK; + } + + // Tab kind. + switch (nKeyword) + { + case RTFKeyword::TQR: + nParam = NS_ooxml::LN_Value_ST_TabJc_right; + break; + case RTFKeyword::TQC: + nParam = NS_ooxml::LN_Value_ST_TabJc_center; + break; + case RTFKeyword::TQDEC: + nParam = NS_ooxml::LN_Value_ST_TabJc_decimal; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getTabAttributes().set(NS_ooxml::LN_CT_TabStop_val, pValue); + return RTFError::OK; + } + + // Tab lead. + switch (nKeyword) + { + case RTFKeyword::TLDOT: + nParam = NS_ooxml::LN_Value_ST_TabTlc_dot; + break; + case RTFKeyword::TLMDOT: + nParam = NS_ooxml::LN_Value_ST_TabTlc_middleDot; + break; + case RTFKeyword::TLHYPH: + nParam = NS_ooxml::LN_Value_ST_TabTlc_hyphen; + break; + case RTFKeyword::TLUL: + case RTFKeyword::TLTH: + nParam = NS_ooxml::LN_Value_ST_TabTlc_underscore; + break; + case RTFKeyword::TLEQ: + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getTabAttributes().set(NS_ooxml::LN_CT_TabStop_leader, pValue); + return RTFError::OK; + } + + // Border types + { + switch (nKeyword) + { + // brdrhair and brdrs are the same, brdrw will make a difference + // map to values in ooxml/model.xml resource ST_Border + case RTFKeyword::BRDRHAIR: + case RTFKeyword::BRDRS: + nParam = NS_ooxml::LN_Value_ST_Border_single; + break; + case RTFKeyword::BRDRDOT: + nParam = NS_ooxml::LN_Value_ST_Border_dotted; + break; + case RTFKeyword::BRDRDASH: + nParam = NS_ooxml::LN_Value_ST_Border_dashed; + break; + case RTFKeyword::BRDRDB: + nParam = NS_ooxml::LN_Value_ST_Border_double; + break; + case RTFKeyword::BRDRTNTHSG: + nParam = NS_ooxml::LN_Value_ST_Border_thinThickSmallGap; + break; + case RTFKeyword::BRDRTNTHMG: + nParam = NS_ooxml::LN_Value_ST_Border_thinThickMediumGap; + break; + case RTFKeyword::BRDRTNTHLG: + nParam = NS_ooxml::LN_Value_ST_Border_thinThickLargeGap; + break; + case RTFKeyword::BRDRTHTNSG: + nParam = NS_ooxml::LN_Value_ST_Border_thickThinSmallGap; + break; + case RTFKeyword::BRDRTHTNMG: + nParam = NS_ooxml::LN_Value_ST_Border_thickThinMediumGap; + break; + case RTFKeyword::BRDRTHTNLG: + nParam = NS_ooxml::LN_Value_ST_Border_thickThinLargeGap; + break; + case RTFKeyword::BRDREMBOSS: + nParam = NS_ooxml::LN_Value_ST_Border_threeDEmboss; + break; + case RTFKeyword::BRDRENGRAVE: + nParam = NS_ooxml::LN_Value_ST_Border_threeDEngrave; + break; + case RTFKeyword::BRDROUTSET: + nParam = NS_ooxml::LN_Value_ST_Border_outset; + break; + case RTFKeyword::BRDRINSET: + nParam = NS_ooxml::LN_Value_ST_Border_inset; + break; + case RTFKeyword::BRDRDASHSM: + nParam = NS_ooxml::LN_Value_ST_Border_dashSmallGap; + break; + case RTFKeyword::BRDRDASHD: + nParam = NS_ooxml::LN_Value_ST_Border_dotDash; + break; + case RTFKeyword::BRDRDASHDD: + nParam = NS_ooxml::LN_Value_ST_Border_dotDotDash; + break; + case RTFKeyword::BRDRNONE: + nParam = NS_ooxml::LN_Value_ST_Border_none; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_val, pValue); + return RTFError::OK; + } + } + + // Section breaks + switch (nKeyword) + { + case RTFKeyword::SBKNONE: + nParam = NS_ooxml::LN_Value_ST_SectionMark_continuous; + break; + case RTFKeyword::SBKCOL: + nParam = NS_ooxml::LN_Value_ST_SectionMark_nextColumn; + break; + case RTFKeyword::SBKPAGE: + nParam = NS_ooxml::LN_Value_ST_SectionMark_nextPage; + break; + case RTFKeyword::SBKEVEN: + nParam = NS_ooxml::LN_Value_ST_SectionMark_evenPage; + break; + case RTFKeyword::SBKODD: + nParam = NS_ooxml::LN_Value_ST_SectionMark_oddPage; + break; + default: + break; + } + if (nParam >= 0) + { + if (m_nResetBreakOnSectBreak != RTFKeyword::invalid) + { + m_nResetBreakOnSectBreak = nKeyword; + } + auto pValue = new RTFValue(nParam); + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type, pValue); + return RTFError::OK; + } + + // Footnote numbering + switch (nKeyword) + { + case RTFKeyword::FTNNAR: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_decimal; + break; + case RTFKeyword::FTNNALC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter; + break; + case RTFKeyword::FTNNAUC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperLetter; + break; + case RTFKeyword::FTNNRLC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman; + break; + case RTFKeyword::FTNNRUC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperRoman; + break; + case RTFKeyword::FTNNCHI: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_chicago; + break; + default: + break; + } + if (nParam >= 0) + { + auto pInner = new RTFValue(nParam); + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_NumFmt_val, pInner); + auto pOuter = new RTFValue(aAttributes); + putNestedSprm(m_aDefaultState.getParagraphSprms(), + NS_ooxml::LN_EG_SectPrContents_footnotePr, NS_ooxml::LN_CT_FtnProps_numFmt, + pOuter); + return RTFError::OK; + } + + // Footnote restart type + switch (nKeyword) + { + case RTFKeyword::FTNRSTPG: + nParam = NS_ooxml::LN_Value_ST_RestartNumber_eachPage; + break; + case RTFKeyword::FTNRESTART: + nParam = NS_ooxml::LN_Value_ST_RestartNumber_eachSect; + break; + case RTFKeyword::FTNRSTCONT: + nParam = NS_ooxml::LN_Value_ST_RestartNumber_continuous; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + putNestedSprm(m_aDefaultState.getParagraphSprms(), + NS_ooxml::LN_EG_SectPrContents_footnotePr, + NS_ooxml::LN_EG_FtnEdnNumProps_numRestart, pValue); + return RTFError::OK; + } + + // Endnote numbering + switch (nKeyword) + { + case RTFKeyword::AFTNNAR: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_decimal; + break; + case RTFKeyword::AFTNNALC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter; + break; + case RTFKeyword::AFTNNAUC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperLetter; + break; + case RTFKeyword::AFTNNRLC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman; + break; + case RTFKeyword::AFTNNRUC: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_upperRoman; + break; + case RTFKeyword::AFTNNCHI: + nParam = NS_ooxml::LN_Value_ST_NumberFormat_chicago; + break; + default: + break; + } + if (nParam >= 0) + { + auto pInner = new RTFValue(nParam); + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_NumFmt_val, pInner); + auto pOuter = new RTFValue(aAttributes); + putNestedSprm(m_aDefaultState.getParagraphSprms(), NS_ooxml::LN_EG_SectPrContents_endnotePr, + NS_ooxml::LN_CT_EdnProps_numFmt, pOuter); + return RTFError::OK; + } + + switch (nKeyword) + { + case RTFKeyword::TRQL: + nParam = NS_ooxml::LN_Value_ST_Jc_left; + break; + case RTFKeyword::TRQC: + nParam = NS_ooxml::LN_Value_ST_Jc_center; + break; + case RTFKeyword::TRQR: + nParam = NS_ooxml::LN_Value_ST_Jc_right; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TrPrBase_jc, pValue); + return RTFError::OK; + } + + // Cell Text Flow + switch (nKeyword) + { + case RTFKeyword::CLTXLRTB: + nParam = NS_ooxml::LN_Value_ST_TextDirection_lrTb; + break; + case RTFKeyword::CLTXTBRL: + nParam = NS_ooxml::LN_Value_ST_TextDirection_tbRl; + break; + case RTFKeyword::CLTXBTLR: + nParam = NS_ooxml::LN_Value_ST_TextDirection_btLr; + break; + case RTFKeyword::CLTXLRTBV: + nParam = NS_ooxml::LN_Value_ST_TextDirection_lrTbV; + break; + case RTFKeyword::CLTXTBRLV: + nParam = NS_ooxml::LN_Value_ST_TextDirection_tbRlV; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(nParam); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_textDirection, pValue); + } + + // Trivial paragraph flags + switch (nKeyword) + { + case RTFKeyword::KEEP: + if (m_aStates.top().getCurrentBuffer() != &m_aTableBufferStack.back()) + nParam = NS_ooxml::LN_CT_PPrBase_keepLines; + break; + case RTFKeyword::KEEPN: + nParam = NS_ooxml::LN_CT_PPrBase_keepNext; + break; + case RTFKeyword::INTBL: + { + m_aStates.top().setCurrentBuffer(&m_aTableBufferStack.back()); + nParam = NS_ooxml::LN_inTbl; + } + break; + case RTFKeyword::PAGEBB: + nParam = NS_ooxml::LN_CT_PPrBase_pageBreakBefore; + break; + default: + break; + } + if (nParam >= 0) + { + auto pValue = new RTFValue(1); + m_aStates.top().getParagraphSprms().set(nParam, pValue); + return RTFError::OK; + } + + switch (nKeyword) + { + case RTFKeyword::FNIL: + case RTFKeyword::FROMAN: + case RTFKeyword::FSWISS: + case RTFKeyword::FMODERN: + case RTFKeyword::FSCRIPT: + case RTFKeyword::FDECOR: + case RTFKeyword::FTECH: + case RTFKeyword::FBIDI: + // TODO ooxml:CT_Font_family seems to be ignored by the domain mapper + break; + case RTFKeyword::ANSI: + m_aStates.top().setCurrentEncoding(RTL_TEXTENCODING_MS_1252); + break; + case RTFKeyword::MAC: + m_aDefaultState.setCurrentEncoding(RTL_TEXTENCODING_APPLE_ROMAN); + m_aStates.top().setCurrentEncoding(m_aDefaultState.getCurrentEncoding()); + break; + case RTFKeyword::PC: + m_aDefaultState.setCurrentEncoding(RTL_TEXTENCODING_IBM_437); + m_aStates.top().setCurrentEncoding(m_aDefaultState.getCurrentEncoding()); + break; + case RTFKeyword::PCA: + m_aDefaultState.setCurrentEncoding(RTL_TEXTENCODING_IBM_850); + m_aStates.top().setCurrentEncoding(m_aDefaultState.getCurrentEncoding()); + break; + case RTFKeyword::PLAIN: + { + m_aStates.top().getCharacterSprms() = getDefaultState().getCharacterSprms(); + m_aStates.top().setCurrentEncoding(getEncoding(getFontIndex(m_nDefaultFontIndex))); + m_aStates.top().getCharacterAttributes() = getDefaultState().getCharacterAttributes(); + m_aStates.top().setCurrentCharacterStyleIndex(-1); + m_aStates.top().setRunType(RTFParserState::RunType::NONE); + } + break; + case RTFKeyword::PARD: + { + if (m_bHadPicture) + dispatchSymbol(RTFKeyword::PAR); + // \pard is allowed between \cell and \row, but in that case it should not reset the fact that we're inside a table. + // It should not reset the paragraph style, either, so remember the old paragraph style. + RTFValue::Pointer_t pOldStyle + = m_aStates.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pStyle); + m_aStates.top().getParagraphSprms() = m_aDefaultState.getParagraphSprms(); + m_aStates.top().getParagraphAttributes() = m_aDefaultState.getParagraphAttributes(); + + if (m_nTopLevelCells == 0 && m_nNestedCells == 0) + { + // Reset that we're in a table. + m_aStates.top().setCurrentBuffer(nullptr); + } + else + { + // We are still in a table. + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_inTbl, new RTFValue(1)); + if (m_bAfterCellBeforeRow && pOldStyle) + // And we still have the same paragraph style. + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_pStyle, + pOldStyle); + // Ideally getDefaultSPRM() would take care of this, but it would not when we're buffering. + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_tabs, + new RTFValue()); + } + resetFrame(); + + // Reset currently selected paragraph style as well, unless we are in the special "after \cell, before \row" state. + // By default the style with index 0 is applied. + if (!m_bAfterCellBeforeRow) + { + OUString const aName = getStyleName(0); + // But only in case it's not a character style. + if (!aName.isEmpty() + && getStyleType(0) != NS_ooxml::LN_Value_ST_StyleType_character) + { + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_pStyle, + new RTFValue(aName)); + } + m_aStates.top().setCurrentStyleIndex(0); + } + // Need to send paragraph properties again, if there will be any. + m_bNeedPap = true; + break; + } + case RTFKeyword::SECTD: + { + m_aStates.top().getSectionSprms() = m_aDefaultState.getSectionSprms(); + m_aStates.top().getSectionAttributes() = m_aDefaultState.getSectionAttributes(); + } + break; + case RTFKeyword::TROWD: + { + // Back these up, in case later we still need this info. + backupTableRowProperties(); + resetTableRowProperties(); + // In case the table definition is in the middle of the row + // (invalid), make sure table definition is emitted. + m_bNeedPap = true; + } + break; + case RTFKeyword::WIDCTLPAR: + case RTFKeyword::NOWIDCTLPAR: + { + auto pValue = new RTFValue(int(nKeyword == RTFKeyword::WIDCTLPAR)); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_widowControl, pValue); + } + break; + case RTFKeyword::BOX: + { + RTFSprms aAttributes; + auto pValue = new RTFValue(aAttributes); + for (int i = 0; i < 4; i++) + m_aStates.top().getParagraphSprms().set(getParagraphBorder(i), pValue); + m_aStates.top().setBorderState(RTFBorderState::PARAGRAPH_BOX); + } + break; + case RTFKeyword::LTRSECT: + case RTFKeyword::RTLSECT: + { + auto pValue = new RTFValue(nKeyword == RTFKeyword::LTRSECT ? 0 : 1); + m_aStates.top().setRunType(RTFParserState::RunType::NONE); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_EG_SectPrContents_textDirection, + pValue); + } + break; + case RTFKeyword::LTRPAR: + case RTFKeyword::RTLPAR: + { + auto pValue = new RTFValue(nKeyword == RTFKeyword::LTRPAR ? 0 : 1); + m_aStates.top().setRunType(RTFParserState::RunType::NONE); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_bidi, pValue); + } + break; + case RTFKeyword::LTRROW: + case RTFKeyword::RTLROW: + m_aStates.top().setRunType(RTFParserState::RunType::NONE); + m_aStates.top().getTableRowSprms().set( + NS_ooxml::LN_CT_TblPrBase_bidiVisual, + new RTFValue(int(nKeyword == RTFKeyword::RTLROW))); + break; + case RTFKeyword::LTRCH: + // dmapper does not support this. + if (m_aStates.top().getRunType() == RTFParserState::RunType::RTLCH_LTRCH_1) + m_aStates.top().setRunType(RTFParserState::RunType::RTLCH_LTRCH_2); + else + m_aStates.top().setRunType(RTFParserState::RunType::LTRCH_RTLCH_1); + break; + case RTFKeyword::RTLCH: + if (m_aStates.top().getRunType() == RTFParserState::RunType::LTRCH_RTLCH_1) + m_aStates.top().setRunType(RTFParserState::RunType::LTRCH_RTLCH_2); + else + m_aStates.top().setRunType(RTFParserState::RunType::RTLCH_LTRCH_1); + + if (m_aDefaultState.getCurrentEncoding() == RTL_TEXTENCODING_MS_1255) + m_aStates.top().setCurrentEncoding(m_aDefaultState.getCurrentEncoding()); + break; + case RTFKeyword::ULNONE: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_Underline_none); + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue); + } + break; + case RTFKeyword::NONSHPPICT: + case RTFKeyword::MMATHPICT: // Picture group used by readers not understanding \moMath group + m_aStates.top().setDestination(Destination::SKIP); + break; + case RTFKeyword::CLBRDRT: + case RTFKeyword::CLBRDRL: + case RTFKeyword::CLBRDRB: + case RTFKeyword::CLBRDRR: + { + RTFSprms aAttributes; + RTFSprms aSprms; + auto pValue = new RTFValue(aAttributes, aSprms); + switch (nKeyword) + { + case RTFKeyword::CLBRDRT: + nSprm = NS_ooxml::LN_CT_TcBorders_top; + break; + case RTFKeyword::CLBRDRL: + nSprm = NS_ooxml::LN_CT_TcBorders_left; + break; + case RTFKeyword::CLBRDRB: + nSprm = NS_ooxml::LN_CT_TcBorders_bottom; + break; + case RTFKeyword::CLBRDRR: + nSprm = NS_ooxml::LN_CT_TcBorders_right; + break; + default: + break; + } + putNestedSprm(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcBorders, + nSprm, pValue); + m_aStates.top().setBorderState(RTFBorderState::CELL); + } + break; + case RTFKeyword::PGBRDRT: + case RTFKeyword::PGBRDRL: + case RTFKeyword::PGBRDRB: + case RTFKeyword::PGBRDRR: + { + RTFSprms aAttributes; + RTFSprms aSprms; + auto pValue = new RTFValue(aAttributes, aSprms); + switch (nKeyword) + { + case RTFKeyword::PGBRDRT: + nSprm = NS_ooxml::LN_CT_PageBorders_top; + break; + case RTFKeyword::PGBRDRL: + nSprm = NS_ooxml::LN_CT_PageBorders_left; + break; + case RTFKeyword::PGBRDRB: + nSprm = NS_ooxml::LN_CT_PageBorders_bottom; + break; + case RTFKeyword::PGBRDRR: + nSprm = NS_ooxml::LN_CT_PageBorders_right; + break; + default: + break; + } + putNestedSprm(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgBorders, nSprm, pValue); + m_aStates.top().setBorderState(RTFBorderState::PAGE); + } + break; + case RTFKeyword::BRDRT: + case RTFKeyword::BRDRL: + case RTFKeyword::BRDRB: + case RTFKeyword::BRDRR: + case RTFKeyword::BRDRBTW: + { + RTFSprms aAttributes; + RTFSprms aSprms; + auto pValue = new RTFValue(aAttributes, aSprms); + switch (nKeyword) + { + case RTFKeyword::BRDRT: + nSprm = getParagraphBorder(0); + break; + case RTFKeyword::BRDRL: + nSprm = getParagraphBorder(1); + break; + case RTFKeyword::BRDRB: + nSprm = getParagraphBorder(2); + break; + case RTFKeyword::BRDRR: + nSprm = getParagraphBorder(3); + break; + case RTFKeyword::BRDRBTW: + nSprm = getParagraphBorder(4); + break; + default: + break; + } + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr, nSprm, + pValue); + m_aStates.top().setBorderState(RTFBorderState::PARAGRAPH); + } + break; + case RTFKeyword::CHBRDR: + { + RTFSprms aAttributes; + auto pValue = new RTFValue(aAttributes); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_bdr, pValue); + m_aStates.top().setBorderState(RTFBorderState::CHARACTER); + } + break; + case RTFKeyword::CLMGF: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_Merge_restart); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_hMerge, pValue); + } + break; + case RTFKeyword::CLMRG: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_Merge_continue); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_hMerge, pValue); + } + break; + case RTFKeyword::CLVMGF: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_Merge_restart); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_vMerge, pValue); + } + break; + case RTFKeyword::CLVMRG: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_Merge_continue); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_vMerge, pValue); + } + break; + case RTFKeyword::CLVERTALT: + case RTFKeyword::CLVERTALC: + case RTFKeyword::CLVERTALB: + { + switch (nKeyword) + { + case RTFKeyword::CLVERTALT: + nParam = NS_ooxml::LN_Value_ST_VerticalJc_top; + break; + case RTFKeyword::CLVERTALC: + nParam = NS_ooxml::LN_Value_ST_VerticalJc_center; + break; + case RTFKeyword::CLVERTALB: + nParam = NS_ooxml::LN_Value_ST_VerticalJc_bottom; + break; + default: + break; + } + auto pValue = new RTFValue(nParam); + m_aStates.top().getTableCellSprms().set(NS_ooxml::LN_CT_TcPrBase_vAlign, pValue); + } + break; + case RTFKeyword::TRKEEP: + { + auto pValue = new RTFValue(1); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TrPrBase_cantSplit, pValue); + } + break; + case RTFKeyword::SECTUNLOCKED: + { + auto pValue = new RTFValue(0); + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_formProt, pValue); + } + break; + case RTFKeyword::PGNBIDIA: + case RTFKeyword::PGNBIDIB: + // These should be mapped to NS_ooxml::LN_EG_SectPrContents_pgNumType, but dmapper has no API for that at the moment. + break; + case RTFKeyword::LOCH: + m_aStates.top().setRunType(RTFParserState::RunType::LOCH); + break; + case RTFKeyword::HICH: + m_aStates.top().setRunType(RTFParserState::RunType::HICH); + break; + case RTFKeyword::DBCH: + m_aStates.top().setRunType(RTFParserState::RunType::DBCH); + break; + case RTFKeyword::TITLEPG: + { + auto pValue = new RTFValue(1); + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_titlePg, pValue); + } + break; + case RTFKeyword::SUPER: + { + // Make sure character properties are not lost if the document + // starts with a footnote. + if (!isStyleSheetImport()) + { + checkFirstRun(); + checkNeedPap(); + } + + if (!m_aStates.top().getCurrentBuffer()) + m_aStates.top().setCurrentBuffer(&m_aSuperBuffer); + + auto pValue = new RTFValue("superscript"); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_vertAlign, pValue); + } + break; + case RTFKeyword::SUB: + { + auto pValue = new RTFValue("subscript"); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_vertAlign, pValue); + } + break; + case RTFKeyword::NOSUPERSUB: + { + if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer) + { + replayBuffer(m_aSuperBuffer, nullptr, nullptr); + m_aStates.top().setCurrentBuffer(nullptr); + } + m_aStates.top().getCharacterSprms().erase(NS_ooxml::LN_EG_RPrBase_vertAlign); + } + break; + case RTFKeyword::LINEPPAGE: + case RTFKeyword::LINECONT: + { + auto pValue = new RTFValue(nKeyword == RTFKeyword::LINEPPAGE + ? NS_ooxml::LN_Value_ST_LineNumberRestart_newPage + : NS_ooxml::LN_Value_ST_LineNumberRestart_continuous); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_lnNumType, + NS_ooxml::LN_CT_LineNumber_restart, pValue); + } + break; + case RTFKeyword::AENDDOC: + // Noop, this is the default in Writer. + case RTFKeyword::AENDNOTES: + // Noop + case RTFKeyword::AFTNRSTCONT: + // Noop, this is the default in Writer. + case RTFKeyword::AFTNRESTART: + // Noop + case RTFKeyword::FTNBJ: + // Noop, this is the default in Writer. + break; + case RTFKeyword::ENDDOC: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_RestartNumber_eachSect); + putNestedSprm(m_aDefaultState.getParagraphSprms(), + NS_ooxml::LN_EG_SectPrContents_footnotePr, + NS_ooxml::LN_EG_FtnEdnNumProps_numRestart, pValue); + } + break; + case RTFKeyword::NOLINE: + eraseNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_lnNumType, + NS_ooxml::LN_CT_LineNumber_distance); + break; + case RTFKeyword::FORMSHADE: + // Noop, this is the default in Writer. + break; + case RTFKeyword::PNGBLIP: + m_aStates.top().getPicture().eStyle = RTFBmpStyle::PNG; + break; + case RTFKeyword::JPEGBLIP: + m_aStates.top().getPicture().eStyle = RTFBmpStyle::JPEG; + break; + case RTFKeyword::POSYT: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_top); + break; + case RTFKeyword::POSYB: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_bottom); + break; + case RTFKeyword::POSYC: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_center); + break; + case RTFKeyword::POSYIN: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_inside); + break; + case RTFKeyword::POSYOUT: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_outside); + break; + case RTFKeyword::POSYIL: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_Value_doc_ST_YAlign_inline); + break; + + case RTFKeyword::PHMRG: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, + NS_ooxml::LN_Value_doc_ST_HAnchor_margin); + break; + case RTFKeyword::PVMRG: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, + NS_ooxml::LN_Value_doc_ST_VAnchor_margin); + break; + case RTFKeyword::PHPG: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, + NS_ooxml::LN_Value_doc_ST_HAnchor_page); + break; + case RTFKeyword::PVPG: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, + NS_ooxml::LN_Value_doc_ST_VAnchor_page); + break; + case RTFKeyword::PHCOL: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, + NS_ooxml::LN_Value_doc_ST_HAnchor_text); + break; + case RTFKeyword::PVPARA: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, + NS_ooxml::LN_Value_doc_ST_VAnchor_text); + break; + + case RTFKeyword::POSXC: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + NS_ooxml::LN_Value_doc_ST_XAlign_center); + break; + case RTFKeyword::POSXI: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + NS_ooxml::LN_Value_doc_ST_XAlign_inside); + break; + case RTFKeyword::POSXO: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + NS_ooxml::LN_Value_doc_ST_XAlign_outside); + break; + case RTFKeyword::POSXL: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + NS_ooxml::LN_Value_doc_ST_XAlign_left); + break; + case RTFKeyword::POSXR: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + NS_ooxml::LN_Value_doc_ST_XAlign_right); + break; + + case RTFKeyword::DPLINE: + case RTFKeyword::DPRECT: + case RTFKeyword::DPELLIPSE: + case RTFKeyword::DPTXBX: + case RTFKeyword::DPPOLYLINE: + case RTFKeyword::DPPOLYGON: + { + sal_Int32 nType = 0; + switch (nKeyword) + { + case RTFKeyword::DPLINE: + { + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.drawing.LineShape"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + break; + } + case RTFKeyword::DPPOLYLINE: + { + // The reason this is not a simple CustomShape is that in the old syntax we have no ViewBox info. + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.drawing.PolyLineShape"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + break; + } + case RTFKeyword::DPPOLYGON: + { + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.drawing.PolyPolygonShape"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + break; + } + case RTFKeyword::DPRECT: + { + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.drawing.RectangleShape"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + break; + } + case RTFKeyword::DPELLIPSE: + nType = ESCHER_ShpInst_Ellipse; + break; + case RTFKeyword::DPTXBX: + { + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.text.TextFrame"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + std::vector<beans::PropertyValue> aDefaults + = RTFSdrImport::getTextFrameDefaults(false); + for (const auto& rDefault : aDefaults) + { + if (!findPropertyName( + m_aStates.top().getDrawingObject().getPendingProperties(), + rDefault.Name)) + m_aStates.top().getDrawingObject().getPendingProperties().push_back( + rDefault); + } + checkFirstRun(); + Mapper().startShape(m_aStates.top().getDrawingObject().getShape()); + m_aStates.top().getDrawingObject().setHadShapeText(true); + } + break; + default: + break; + } + if (nType) + { + uno::Reference<drawing::XShape> xShape( + getModelFactory()->createInstance("com.sun.star.drawing.CustomShape"), + uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setShape(xShape); + } + uno::Reference<drawing::XDrawPageSupplier> xDrawSupplier(m_xDstDoc, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xPropertySet( + m_aStates.top().getDrawingObject().getShape(), uno::UNO_QUERY); + m_aStates.top().getDrawingObject().setPropertySet(xPropertySet); + if (xDrawSupplier.is()) + { + uno::Reference<drawing::XShapes> xShapes = xDrawSupplier->getDrawPage(); + if (xShapes.is() && nKeyword != RTFKeyword::DPTXBX) + { + // set default VertOrient before inserting + m_aStates.top().getDrawingObject().getPropertySet()->setPropertyValue( + "VertOrient", uno::Any(text::VertOrientation::NONE)); + xShapes->add(m_aStates.top().getDrawingObject().getShape()); + } + } + if (nType) + { + uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter( + m_aStates.top().getDrawingObject().getShape(), uno::UNO_QUERY); + xDefaulter->createCustomShapeDefaults(OUString::number(nType)); + } + std::vector<beans::PropertyValue>& rPendingProperties + = m_aStates.top().getDrawingObject().getPendingProperties(); + for (const auto& rPendingProperty : rPendingProperties) + m_aStates.top().getDrawingObject().getPropertySet()->setPropertyValue( + rPendingProperty.Name, rPendingProperty.Value); + m_pSdrImport->resolveDhgt(m_aStates.top().getDrawingObject().getPropertySet(), + m_aStates.top().getDrawingObject().getDhgt(), + /*bOldStyle=*/true); + } + break; + case RTFKeyword::DOBXMARGIN: + case RTFKeyword::DOBYMARGIN: + { + beans::PropertyValue aPropertyValue; + aPropertyValue.Name + = (nKeyword == RTFKeyword::DOBXMARGIN ? std::u16string_view(u"HoriOrientRelation") + : std::u16string_view(u"VertOrientRelation")); + aPropertyValue.Value <<= text::RelOrientation::PAGE_PRINT_AREA; + m_aStates.top().getDrawingObject().getPendingProperties().push_back(aPropertyValue); + } + break; + case RTFKeyword::DOBXPAGE: + case RTFKeyword::DOBYPAGE: + { + beans::PropertyValue aPropertyValue; + aPropertyValue.Name + = (nKeyword == RTFKeyword::DOBXPAGE ? std::u16string_view(u"HoriOrientRelation") + : std::u16string_view(u"VertOrientRelation")); + aPropertyValue.Value <<= text::RelOrientation::PAGE_FRAME; + m_aStates.top().getDrawingObject().getPendingProperties().push_back(aPropertyValue); + } + break; + case RTFKeyword::DOBYPARA: + { + beans::PropertyValue aPropertyValue; + aPropertyValue.Name = "VertOrientRelation"; + aPropertyValue.Value <<= text::RelOrientation::FRAME; + m_aStates.top().getDrawingObject().getPendingProperties().push_back(aPropertyValue); + } + break; + case RTFKeyword::CONTEXTUALSPACE: + { + auto pValue = new RTFValue(1); + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_contextualSpacing, + pValue); + } + break; + case RTFKeyword::LINKSTYLES: + { + auto pValue = new RTFValue(1); + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_linkStyles, pValue); + } + break; + case RTFKeyword::PNLVLBODY: + { + auto pValue = new RTFValue(2); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_AbstractNum_nsid, pValue); + } + break; + case RTFKeyword::PNDEC: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_decimal); + putNestedAttribute(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_Lvl_numFmt, + NS_ooxml::LN_CT_NumFmt_val, pValue); + } + break; + case RTFKeyword::PNLVLBLT: + { + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_AbstractNum_nsid, + new RTFValue(1)); + putNestedAttribute(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_Lvl_numFmt, + NS_ooxml::LN_CT_NumFmt_val, + new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_bullet)); + } + break; + case RTFKeyword::LANDSCAPE: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_PageOrientation_landscape); + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_orient, + pValue); + [[fallthrough]]; // set the default + current value + } + case RTFKeyword::LNDSCPSXN: + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_PageOrientation_landscape); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_orient, + pValue); + } + break; + case RTFKeyword::SHPBXPAGE: + m_aStates.top().getShape().setHoriOrientRelation(text::RelOrientation::PAGE_FRAME); + m_aStates.top().getShape().setHoriOrientRelationToken( + NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromH_page); + break; + case RTFKeyword::SHPBYPAGE: + m_aStates.top().getShape().setVertOrientRelation(text::RelOrientation::PAGE_FRAME); + m_aStates.top().getShape().setVertOrientRelationToken( + NS_ooxml::LN_Value_wordprocessingDrawing_ST_RelFromV_page); + break; + case RTFKeyword::DPLINEHOLLOW: + m_aStates.top().getDrawingObject().setFLine(0); + break; + case RTFKeyword::DPROUNDR: + if (m_aStates.top().getDrawingObject().getPropertySet().is()) + // Seems this old syntax has no way to specify a custom radius, and this is the default + m_aStates.top().getDrawingObject().getPropertySet()->setPropertyValue( + "CornerRadius", uno::Any(sal_Int32(83))); + break; + case RTFKeyword::NOWRAP: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_wrap, + NS_ooxml::LN_Value_doc_ST_Wrap_notBeside); + break; + case RTFKeyword::MNOR: + m_bMathNor = true; + break; + case RTFKeyword::REVISIONS: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_trackRevisions, new RTFValue(1)); + break; + case RTFKeyword::BRDRSH: + putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_shadow, new RTFValue(1)); + break; + case RTFKeyword::NOCOLBAL: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Compat_noColumnBalance, new RTFValue(1)); + break; + case RTFKeyword::MARGMIRROR: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_mirrorMargins, new RTFValue(1)); + break; + case RTFKeyword::SAUTOUPD: + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_autoRedefine, + new RTFValue(1)); + break; + case RTFKeyword::WIDOWCTRL: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_widowControl, new RTFValue(1)); + break; + case RTFKeyword::LINEBETCOL: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_cols, NS_ooxml::LN_CT_Columns_sep, + new RTFValue(1)); + break; + case RTFKeyword::PGNRESTART: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_start, new RTFValue(1)); + break; + case RTFKeyword::PGNUCLTR: + { + auto pIntValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_upperLetter); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_fmt, pIntValue); + } + break; + case RTFKeyword::PGNLCLTR: + { + auto pIntValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_fmt, pIntValue); + } + break; + case RTFKeyword::PGNUCRM: + { + auto pIntValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_upperRoman); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_fmt, pIntValue); + } + break; + case RTFKeyword::PGNLCRM: + { + auto pIntValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_fmt, pIntValue); + } + break; + case RTFKeyword::PGNDEC: + { + auto pIntValue = new RTFValue(NS_ooxml::LN_Value_ST_NumberFormat_decimal); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgNumType, + NS_ooxml::LN_CT_PageNumber_fmt, pIntValue); + } + break; + case RTFKeyword::HTMAUTSP: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Compat_doNotUseHTMLParagraphAutoSpacing, + new RTFValue(0)); + break; + case RTFKeyword::DNTBLNSBDB: + // tdf#128428 switch off longer space sequence + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence, + new RTFValue(0)); + break; + case RTFKeyword::GUTTERPRL: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_gutterAtTop, new RTFValue(1)); + break; + case RTFKeyword::RTLGUTTER: + { + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_rtlGutter, + new RTFValue(1)); + } + break; + case RTFKeyword::FLDLOCK: + { + if (m_aStates.top().getDestination() == Destination::FIELD) + m_aStates.top().setFieldLocked(true); + } + break; + default: + { + SAL_INFO("writerfilter", "TODO handle flag '" << keywordToString(nKeyword) << "'"); + aSkip.setParsed(false); + } + break; + } + return RTFError::OK; +} + +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdispatchsymbol.cxx b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx new file mode 100644 index 000000000..3f9ed20bf --- /dev/null +++ b/writerfilter/source/rtftok/rtfdispatchsymbol.cxx @@ -0,0 +1,442 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfdocumentimpl.hxx" + +#include <com/sun/star/io/WrongFormatException.hpp> +#include <svl/lngmisc.hxx> + +#include <ooxml/resourceids.hxx> + +#include <sal/log.hxx> + +#include "rtfreferenceproperties.hxx" +#include "rtfskipdestination.hxx" + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFError RTFDocumentImpl::dispatchSymbol(RTFKeyword nKeyword) +{ + setNeedSect(true); + if (nKeyword != RTFKeyword::HEXCHAR) + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + else + checkUnicode(/*bUnicode =*/true, /*bHex =*/false); + RTFSkipDestination aSkip(*this); + + if (RTFKeyword::LINE == nKeyword) + { + // very special handling since text() will eat lone '\n' + singleChar('\n', /*bRunProps=*/true); + return RTFError::OK; + } + // Trivial symbols + sal_uInt8 cCh = 0; + switch (nKeyword) + { + case RTFKeyword::TAB: + cCh = '\t'; + break; + case RTFKeyword::BACKSLASH: + cCh = '\\'; + break; + case RTFKeyword::LBRACE: + cCh = '{'; + break; + case RTFKeyword::RBRACE: + cCh = '}'; + break; + case RTFKeyword::EMDASH: + cCh = 151; + break; + case RTFKeyword::ENDASH: + cCh = 150; + break; + case RTFKeyword::BULLET: + cCh = 149; + break; + case RTFKeyword::LQUOTE: + cCh = 145; + break; + case RTFKeyword::RQUOTE: + cCh = 146; + break; + case RTFKeyword::LDBLQUOTE: + cCh = 147; + break; + case RTFKeyword::RDBLQUOTE: + cCh = 148; + break; + default: + break; + } + if (cCh > 0) + { + OUString aStr(OStringToOUString(OStringChar(char(cCh)), RTL_TEXTENCODING_MS_1252)); + text(aStr); + return RTFError::OK; + } + + switch (nKeyword) + { + case RTFKeyword::IGNORE: + { + m_bSkipUnknown = true; + aSkip.setReset(false); + return RTFError::OK; + } + break; + case RTFKeyword::PAR: + { + if (m_aStates.top().getDestination() == Destination::FOOTNOTESEPARATOR) + break; // just ignore it - only thing we read in here is CHFTNSEP + checkFirstRun(); + bool bNeedPap = m_bNeedPap; + checkNeedPap(); + if (bNeedPap) + runProps(); + if (!m_aStates.top().getCurrentBuffer()) + { + parBreak(); + // Not in table? Reset max width. + if (m_nCellxMax) + { + // Was in table, but not anymore -> tblEnd. + RTFSprms aAttributes; + RTFSprms aSprms; + aSprms.set(NS_ooxml::LN_tblEnd, new RTFValue(1)); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); + Mapper().props(pProperties); + } + m_nCellxMax = 0; + } + else if (m_aStates.top().getDestination() != Destination::SHAPETEXT) + { + RTFValue::Pointer_t pValue; + m_aStates.top().getCurrentBuffer()->push_back(Buf_t(BUFFER_PAR, pValue, nullptr)); + } + // but don't emit properties yet, since they may change till the first text token arrives + m_bNeedPap = true; + if (!m_aStates.top().getFrame().inFrame()) + m_bNeedPar = false; + m_bNeedFinalPar = false; + } + break; + case RTFKeyword::SECT: + { + if (m_bNeedCr) + dispatchSymbol(RTFKeyword::PAR); + + m_bHadSect = true; + if (m_bIgnoreNextContSectBreak) + m_bIgnoreNextContSectBreak = false; + else + { + sectBreak(); + if (m_nResetBreakOnSectBreak != RTFKeyword::invalid) + { + // this should run on _second_ \sect after \page + dispatchSymbol(m_nResetBreakOnSectBreak); // lazy reset + m_nResetBreakOnSectBreak = RTFKeyword::invalid; + m_bNeedSect = false; // dispatchSymbol set it + } + } + } + break; + case RTFKeyword::NOBREAK: + { + OUString aStr(SVT_HARD_SPACE); + text(aStr); + } + break; + case RTFKeyword::NOBRKHYPH: + { + OUString aStr(SVT_HARD_HYPHEN); + text(aStr); + } + break; + case RTFKeyword::OPTHYPH: + { + OUString aStr(SVT_SOFT_HYPHEN); + text(aStr); + } + break; + case RTFKeyword::HEXCHAR: + m_aStates.top().setInternalState(RTFInternalState::HEX); + break; + case RTFKeyword::CELL: + case RTFKeyword::NESTCELL: + { + if (nKeyword == RTFKeyword::CELL) + m_bAfterCellBeforeRow = true; + + checkFirstRun(); + if (m_bNeedPap) + { + // There were no runs in the cell, so we need to send paragraph and character properties here. + auto pPValue = new RTFValue(m_aStates.top().getParagraphAttributes(), + m_aStates.top().getParagraphSprms()); + bufferProperties(m_aTableBufferStack.back(), pPValue, nullptr); + auto pCValue = new RTFValue(m_aStates.top().getCharacterAttributes(), + m_aStates.top().getCharacterSprms()); + bufferProperties(m_aTableBufferStack.back(), pCValue, nullptr); + } + + RTFValue::Pointer_t pValue; + m_aTableBufferStack.back().emplace_back(Buf_t(BUFFER_CELLEND, pValue, nullptr)); + m_bNeedPap = true; + } + break; + case RTFKeyword::NESTROW: + { + tools::SvRef<TableRowBuffer> const pBuffer( + new TableRowBuffer(m_aTableBufferStack.back(), m_aNestedTableCellsSprms, + m_aNestedTableCellsAttributes, m_nNestedCells)); + prepareProperties(m_aStates.top(), pBuffer->GetParaProperties(), + pBuffer->GetFrameProperties(), pBuffer->GetRowProperties(), + m_nNestedCells, m_nNestedCurrentCellX - m_nNestedTRLeft); + + if (m_aTableBufferStack.size() == 1 || !m_aStates.top().getCurrentBuffer()) + { + throw io::WrongFormatException("mismatch between \\itap and number of \\nestrow", + nullptr); + } + assert(m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back()); + // note: there may be several states pointing to table buffer! + for (std::size_t i = 0; i < m_aStates.size(); ++i) + { + if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back()) + { + m_aStates[i].setCurrentBuffer( + &m_aTableBufferStack[m_aTableBufferStack.size() - 2]); + } + } + m_aTableBufferStack.pop_back(); + m_aTableBufferStack.back().emplace_back( + Buf_t(BUFFER_NESTROW, RTFValue::Pointer_t(), pBuffer)); + + m_aNestedTableCellsSprms.clear(); + m_aNestedTableCellsAttributes.clear(); + m_nNestedCells = 0; + m_bNeedPap = true; + } + break; + case RTFKeyword::ROW: + { + m_bAfterCellBeforeRow = false; + if (m_aStates.top().getTableRowWidthAfter() > 0) + { + // Add fake cellx / cell, RTF equivalent of + // OOXMLFastContextHandlerTextTableRow::handleGridAfter(). + auto pXValue = new RTFValue(m_aStates.top().getTableRowWidthAfter()); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue, + RTFOverwrite::NO_APPEND); + dispatchSymbol(RTFKeyword::CELL); + + // Adjust total width, which is done in the \cellx handler for normal cells. + m_nTopLevelCurrentCellX += m_aStates.top().getTableRowWidthAfter(); + + m_aStates.top().setTableRowWidthAfter(0); + } + + bool bRestored = false; + // Ending a row, but no cells defined? + // See if there was an invalid table row reset, so we can restore cell infos to help invalid documents. + if (!m_nTopLevelCurrentCellX && m_nBackupTopLevelCurrentCellX) + { + restoreTableRowProperties(); + bRestored = true; + } + + // If the right edge of the last cell (row width) is smaller than the width of some other row, mimic WW8TabDesc::CalcDefaults(): resize the last cell + const int MINLAY = 23; // sw/inc/swtypes.hxx, minimal possible size of frames. + if ((m_nCellxMax - m_nTopLevelCurrentCellX) >= MINLAY) + { + auto pXValueLast = m_aStates.top().getTableRowSprms().find( + NS_ooxml::LN_CT_TblGridBase_gridCol, false); + const int nXValueLast = pXValueLast ? pXValueLast->getInt() : 0; + auto pXValue = new RTFValue(nXValueLast + m_nCellxMax - m_nTopLevelCurrentCellX); + m_aStates.top().getTableRowSprms().eraseLast(NS_ooxml::LN_CT_TblGridBase_gridCol); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue, + RTFOverwrite::NO_APPEND); + m_nTopLevelCurrentCellX = m_nCellxMax; + } + + if (m_nTopLevelCells) + { + // Make a backup before we start popping elements + m_aTableInheritingCellsSprms = m_aTopLevelTableCellsSprms; + m_aTableInheritingCellsAttributes = m_aTopLevelTableCellsAttributes; + m_nInheritingCells = m_nTopLevelCells; + } + else + { + // No table definition? Then inherit from the previous row + m_aTopLevelTableCellsSprms = m_aTableInheritingCellsSprms; + m_aTopLevelTableCellsAttributes = m_aTableInheritingCellsAttributes; + m_nTopLevelCells = m_nInheritingCells; + } + + while (m_aTableBufferStack.size() > 1) + { + SAL_WARN("writerfilter.rtf", "dropping extra table buffer"); + // note: there may be several states pointing to table buffer! + for (std::size_t i = 0; i < m_aStates.size(); ++i) + { + if (m_aStates[i].getCurrentBuffer() == &m_aTableBufferStack.back()) + { + m_aStates[i].setCurrentBuffer(&m_aTableBufferStack.front()); + } + } + m_aTableBufferStack.pop_back(); + } + + replayRowBuffer(m_aTableBufferStack.back(), m_aTopLevelTableCellsSprms, + m_aTopLevelTableCellsAttributes, m_nTopLevelCells); + + // The scope of the table cell defaults is one row. + m_aDefaultState.getTableCellSprms().clear(); + m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms(); + m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes(); + + writerfilter::Reference<Properties>::Pointer_t paraProperties; + writerfilter::Reference<Properties>::Pointer_t frameProperties; + writerfilter::Reference<Properties>::Pointer_t rowProperties; + prepareProperties(m_aStates.top(), paraProperties, frameProperties, rowProperties, + m_nTopLevelCells, m_nTopLevelCurrentCellX - m_nTopLevelTRLeft); + sendProperties(paraProperties, frameProperties, rowProperties); + + m_bNeedPap = true; + m_bNeedFinalPar = true; + m_aTableBufferStack.back().clear(); + m_nTopLevelCells = 0; + + if (bRestored) + // We restored cell definitions, clear these now. + // This is necessary, as later cell definitions want to overwrite the restored ones. + resetTableRowProperties(); + } + break; + case RTFKeyword::COLUMN: + { + bool bColumns = false; // If we have multiple columns + RTFValue::Pointer_t pCols + = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_cols); + if (pCols) + { + RTFValue::Pointer_t pNum = pCols->getAttributes().find(NS_ooxml::LN_CT_Columns_num); + if (pNum && pNum->getInt() > 1) + bColumns = true; + } + checkFirstRun(); + if (bColumns) + { + sal_uInt8 const sBreak[] = { 0xe }; + Mapper().startCharacterGroup(); + Mapper().text(sBreak, 1); + Mapper().endCharacterGroup(); + } + else + dispatchSymbol(RTFKeyword::PAGE); + } + break; + case RTFKeyword::CHFTN: + { + if (m_aStates.top().getCurrentBuffer() == &m_aSuperBuffer) + // Stop buffering, there will be no custom mark for this footnote or endnote. + m_aStates.top().setCurrentBuffer(nullptr); + break; + } + case RTFKeyword::PAGE: + { + // Ignore page breaks inside tables. + if (m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back()) + break; + + // If we're inside a continuous section, we should send a section break, not a page one. + RTFValue::Pointer_t pBreak + = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type); + // Unless we're on a title page. + RTFValue::Pointer_t pTitlePg + = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_titlePg); + if (((pBreak + && pBreak->getInt() + == static_cast<sal_Int32>(NS_ooxml::LN_Value_ST_SectionMark_continuous)) + || m_nResetBreakOnSectBreak == RTFKeyword::SBKNONE) + && !(pTitlePg && pTitlePg->getInt())) + { + if (m_bWasInFrame) + { + dispatchSymbol(RTFKeyword::PAR); + m_bWasInFrame = false; + } + sectBreak(); + // note: this will not affect the following section break + // but the one just pushed + dispatchFlag(RTFKeyword::SBKPAGE); + if (m_bNeedPar) + dispatchSymbol(RTFKeyword::PAR); + m_bIgnoreNextContSectBreak = true; + // arrange to clean up the synthetic RTFKeyword::SBKPAGE + m_nResetBreakOnSectBreak = RTFKeyword::SBKNONE; + } + else + { + bool bFirstRun = m_bFirstRun; + checkFirstRun(); + checkNeedPap(); + sal_uInt8 const sBreak[] = { 0xc }; + Mapper().text(sBreak, 1); + if (bFirstRun || m_bNeedCr) + { + // If we don't have content in the document yet (so the break-before can't move + // to a second layout page) or we already have characters sent (so the paragraph + // properties are already finalized), then continue inserting a fake paragraph. + if (!m_bNeedPap) + { + parBreak(); + m_bNeedPap = true; + } + } + m_bNeedCr = true; + } + } + break; + case RTFKeyword::CHPGN: + { + OUString aStr("PAGE"); + singleChar(cFieldStart); + text(aStr); + singleChar(cFieldSep, true); + singleChar(cFieldEnd); + } + break; + case RTFKeyword::CHFTNSEP: + { + static const sal_Unicode uFtnEdnSep = 0x3; + Mapper().utext(reinterpret_cast<const sal_uInt8*>(&uFtnEdnSep), 1); + } + break; + default: + { + SAL_INFO("writerfilter.rtf", + "TODO handle symbol '" << keywordToString(nKeyword) << "'"); + aSkip.setParsed(false); + } + break; + } + return RTFError::OK; +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdispatchvalue.cxx b/writerfilter/source/rtftok/rtfdispatchvalue.cxx new file mode 100644 index 000000000..2bea9dc9e --- /dev/null +++ b/writerfilter/source/rtftok/rtfdispatchvalue.cxx @@ -0,0 +1,1832 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfdocumentimpl.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/document/XDocumentProperties.hpp> +#include <comphelper/sequence.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <osl/thread.h> +#include <sal/log.hxx> +#include <rtl/tencinfo.h> +#include <tools/UnitConversion.hxx> + +#include <ooxml/resourceids.hxx> + +#include "rtfcharsets.hxx" +#include "rtffly.hxx" +#include "rtfreferenceproperties.hxx" +#include "rtfskipdestination.hxx" + +#include <unotools/defaultencoding.hxx> +#include <unotools/wincodepage.hxx> + +using namespace com::sun::star; + +namespace writerfilter +{ +static int getNumberFormat(int nParam) +{ + static const int aMap[] + = { NS_ooxml::LN_Value_ST_NumberFormat_decimal, + NS_ooxml::LN_Value_ST_NumberFormat_upperRoman, + NS_ooxml::LN_Value_ST_NumberFormat_lowerRoman, + NS_ooxml::LN_Value_ST_NumberFormat_upperLetter, + NS_ooxml::LN_Value_ST_NumberFormat_lowerLetter, + NS_ooxml::LN_Value_ST_NumberFormat_ordinal, + NS_ooxml::LN_Value_ST_NumberFormat_cardinalText, + NS_ooxml::LN_Value_ST_NumberFormat_ordinalText, + NS_ooxml::LN_Value_ST_NumberFormat_none, // Undefined in RTF 1.8 spec. + NS_ooxml::LN_Value_ST_NumberFormat_none, // Undefined in RTF 1.8 spec. + NS_ooxml::LN_Value_ST_NumberFormat_ideographDigital, + NS_ooxml::LN_Value_ST_NumberFormat_japaneseCounting, + NS_ooxml::LN_Value_ST_NumberFormat_aiueo, + NS_ooxml::LN_Value_ST_NumberFormat_iroha, + NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth, + NS_ooxml::LN_Value_ST_NumberFormat_decimalHalfWidth, + NS_ooxml::LN_Value_ST_NumberFormat_japaneseLegal, + NS_ooxml::LN_Value_ST_NumberFormat_japaneseDigitalTenThousand, + NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircleChinese, + NS_ooxml::LN_Value_ST_NumberFormat_decimalFullWidth2, + NS_ooxml::LN_Value_ST_NumberFormat_aiueoFullWidth, + NS_ooxml::LN_Value_ST_NumberFormat_irohaFullWidth, + NS_ooxml::LN_Value_ST_NumberFormat_decimalZero, + NS_ooxml::LN_Value_ST_NumberFormat_bullet, + NS_ooxml::LN_Value_ST_NumberFormat_ganada, + NS_ooxml::LN_Value_ST_NumberFormat_chosung, + NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedFullstop, + NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedParen, + NS_ooxml::LN_Value_ST_NumberFormat_decimalEnclosedCircleChinese, + NS_ooxml::LN_Value_ST_NumberFormat_ideographEnclosedCircle, + NS_ooxml::LN_Value_ST_NumberFormat_ideographTraditional, + NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiac, + NS_ooxml::LN_Value_ST_NumberFormat_ideographZodiacTraditional, + NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCounting, + NS_ooxml::LN_Value_ST_NumberFormat_ideographLegalTraditional, + NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseCountingThousand, + NS_ooxml::LN_Value_ST_NumberFormat_taiwaneseDigital, + NS_ooxml::LN_Value_ST_NumberFormat_chineseCounting, + NS_ooxml::LN_Value_ST_NumberFormat_chineseLegalSimplified, + NS_ooxml::LN_Value_ST_NumberFormat_chineseCountingThousand, + NS_ooxml::LN_Value_ST_NumberFormat_decimal, + NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital, + NS_ooxml::LN_Value_ST_NumberFormat_koreanCounting, + NS_ooxml::LN_Value_ST_NumberFormat_koreanLegal, + NS_ooxml::LN_Value_ST_NumberFormat_koreanDigital2, + NS_ooxml::LN_Value_ST_NumberFormat_hebrew1, + NS_ooxml::LN_Value_ST_NumberFormat_arabicAlpha, + NS_ooxml::LN_Value_ST_NumberFormat_hebrew2, + NS_ooxml::LN_Value_ST_NumberFormat_arabicAbjad }; + const int nLen = SAL_N_ELEMENTS(aMap); + int nValue = 0; + if (nParam >= 0 && nParam < nLen) + nValue = aMap[nParam]; + else // 255 and the other cases. + nValue = NS_ooxml::LN_Value_ST_NumberFormat_none; + return nValue; +} + +namespace rtftok +{ +bool RTFDocumentImpl::dispatchTableSprmValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + tools::SvRef<RTFValue> pIntValue(new RTFValue(nParam)); + switch (nKeyword) + { + case RTFKeyword::LEVELJC: + { + nSprm = NS_ooxml::LN_CT_Lvl_lvlJc; + int nValue = 0; + switch (nParam) + { + case 0: + nValue = NS_ooxml::LN_Value_ST_Jc_left; + break; + case 1: + nValue = NS_ooxml::LN_Value_ST_Jc_center; + break; + case 2: + nValue = NS_ooxml::LN_Value_ST_Jc_right; + break; + } + pIntValue = new RTFValue(nValue); + break; + } + case RTFKeyword::LEVELSTARTAT: + nSprm = NS_ooxml::LN_CT_Lvl_start; + break; + case RTFKeyword::LEVELPICTURE: + nSprm = NS_ooxml::LN_CT_Lvl_lvlPicBulletId; + break; + case RTFKeyword::SBASEDON: + nSprm = NS_ooxml::LN_CT_Style_basedOn; + pIntValue = new RTFValue(getStyleName(nParam)); + break; + case RTFKeyword::SNEXT: + nSprm = NS_ooxml::LN_CT_Style_next; + pIntValue = new RTFValue(getStyleName(nParam)); + break; + default: + break; + } + if (nSprm > 0) + { + m_aStates.top().getTableSprms().set(nSprm, pIntValue); + return true; + } + if (nKeyword == RTFKeyword::LEVELNFC) + { + pIntValue = new RTFValue(getNumberFormat(nParam)); + putNestedAttribute(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_Lvl_numFmt, + NS_ooxml::LN_CT_NumFmt_val, pIntValue); + return true; + } + + return false; +} + +bool RTFDocumentImpl::dispatchCharacterSprmValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + tools::SvRef<RTFValue> pIntValue(new RTFValue(nParam)); + + switch (nKeyword) + { + case RTFKeyword::FS: + case RTFKeyword::AFS: + switch (m_aStates.top().getRunType()) + { + case RTFParserState::RunType::HICH: + case RTFParserState::RunType::RTLCH_LTRCH_1: + case RTFParserState::RunType::LTRCH_RTLCH_2: + nSprm = NS_ooxml::LN_EG_RPrBase_szCs; + break; + case RTFParserState::RunType::NONE: + case RTFParserState::RunType::LOCH: + case RTFParserState::RunType::LTRCH_RTLCH_1: + case RTFParserState::RunType::RTLCH_LTRCH_2: + case RTFParserState::RunType::DBCH: + default: + nSprm = NS_ooxml::LN_EG_RPrBase_sz; + break; + } + break; + case RTFKeyword::EXPNDTW: + nSprm = NS_ooxml::LN_EG_RPrBase_spacing; + break; + case RTFKeyword::KERNING: + nSprm = NS_ooxml::LN_EG_RPrBase_kern; + break; + case RTFKeyword::CHARSCALEX: + nSprm = NS_ooxml::LN_EG_RPrBase_w; + break; + default: + break; + } + if (nSprm > 0) + { + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + m_aStates.top().getTableSprms().set(nSprm, pIntValue); + } + else + { + m_aStates.top().getCharacterSprms().set(nSprm, pIntValue); + } + return true; + } + + return false; +} + +bool RTFDocumentImpl::dispatchCharacterAttributeValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + + switch (nKeyword) + { + case RTFKeyword::LANG: + case RTFKeyword::ALANG: + switch (m_aStates.top().getRunType()) + { + case RTFParserState::RunType::HICH: + case RTFParserState::RunType::RTLCH_LTRCH_1: + case RTFParserState::RunType::LTRCH_RTLCH_2: + nSprm = NS_ooxml::LN_CT_Language_bidi; + break; + case RTFParserState::RunType::DBCH: + nSprm = NS_ooxml::LN_CT_Language_eastAsia; + break; + case RTFParserState::RunType::NONE: + case RTFParserState::RunType::LOCH: + case RTFParserState::RunType::LTRCH_RTLCH_1: + case RTFParserState::RunType::RTLCH_LTRCH_2: + default: + nSprm = NS_ooxml::LN_CT_Language_val; + break; + } + break; + case RTFKeyword::LANGFE: // this one is always CJK apparently + nSprm = NS_ooxml::LN_CT_Language_eastAsia; + break; + default: + break; + } + if (nSprm > 0) + { + LanguageTag aTag((LanguageType(static_cast<sal_uInt16>(nParam)))); + auto pValue = new RTFValue(aTag.getBcp47()); + putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_lang, nSprm, + pValue); + // Language is a character property, but we should store it at a paragraph level as well for fields. + if (nKeyword == RTFKeyword::LANG && m_bNeedPap) + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_EG_RPrBase_lang, + nSprm, pValue); + return true; + } + + return false; +} + +bool RTFDocumentImpl::dispatchParagraphSprmValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + tools::SvRef<RTFValue> pIntValue(new RTFValue(nParam)); + + switch (nKeyword) + { + case RTFKeyword::ITAP: + nSprm = NS_ooxml::LN_tblDepth; + // tdf#117268: If \itap0 is encountered inside tables (between \cellxN and \cell), then + // use the default value (1), as Word apparently does + if (nParam == 0 && (m_nTopLevelCells != 0 || m_nNestedCells != 0)) + { + nParam = 1; + pIntValue = new RTFValue(nParam); + } + break; + default: + break; + } + if (nSprm > 0) + { + m_aStates.top().getParagraphSprms().set(nSprm, pIntValue); + if (nKeyword == RTFKeyword::ITAP && nParam > 0) + { + while (m_aTableBufferStack.size() < sal::static_int_cast<std::size_t>(nParam)) + { + m_aTableBufferStack.emplace_back(); + } + // Invalid tables may omit INTBL after ITAP + dispatchFlag(RTFKeyword::INTBL); // sets newly pushed buffer as current + assert(m_aStates.top().getCurrentBuffer() == &m_aTableBufferStack.back()); + } + return true; + } + + return false; +} + +bool RTFDocumentImpl::dispatchInfoValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + + switch (nKeyword) + { + case RTFKeyword::YR: + { + m_aStates.top().setYear(nParam); + nSprm = 1; + } + break; + case RTFKeyword::MO: + { + m_aStates.top().setMonth(nParam); + nSprm = 1; + } + break; + case RTFKeyword::DY: + { + m_aStates.top().setDay(nParam); + nSprm = 1; + } + break; + case RTFKeyword::HR: + { + m_aStates.top().setHour(nParam); + nSprm = 1; + } + break; + case RTFKeyword::MIN: + { + m_aStates.top().setMinute(nParam); + nSprm = 1; + } + break; + default: + break; + } + + return nSprm > 0; +} + +bool RTFDocumentImpl::dispatchFrameValue(RTFKeyword nKeyword, int nParam) +{ + Id nId = 0; + switch (nKeyword) + { + case RTFKeyword::ABSW: + nId = NS_ooxml::LN_CT_FramePr_w; + break; + case RTFKeyword::ABSH: + nId = NS_ooxml::LN_CT_FramePr_h; + break; + case RTFKeyword::POSX: + { + nId = NS_ooxml::LN_CT_FramePr_x; + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, 0); + } + break; + case RTFKeyword::POSY: + { + nId = NS_ooxml::LN_CT_FramePr_y; + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, 0); + } + break; + default: + break; + } + + if (nId > 0) + { + m_bNeedPap = true; + // Don't try to support text frames inside tables for now. + if (m_aStates.top().getCurrentBuffer() != &m_aTableBufferStack.back()) + m_aStates.top().getFrame().setSprm(nId, nParam); + + return true; + } + + return false; +} + +bool RTFDocumentImpl::dispatchTableValue(RTFKeyword nKeyword, int nParam) +{ + int nSprm = 0; + tools::SvRef<RTFValue> pIntValue(new RTFValue(nParam)); + + switch (nKeyword) + { + case RTFKeyword::CELLX: + { + int& rCurrentCellX( + (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().getDestination()) + ? m_nNestedCurrentCellX + : m_nTopLevelCurrentCellX); + int nCellX = nParam - rCurrentCellX; + const int COL_DFLT_WIDTH + = 41; // sw/source/filter/inc/wrtswtbl.hxx, minimal possible width of cells. + if (!nCellX) + nCellX = COL_DFLT_WIDTH; + + // If there is a negative left margin, then the first cellx is relative to that. + RTFValue::Pointer_t pTblInd + = m_aStates.top().getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblInd); + if (rCurrentCellX == 0 && pTblInd) + { + RTFValue::Pointer_t pWidth + = pTblInd->getAttributes().find(NS_ooxml::LN_CT_TblWidth_w); + if (pWidth && pWidth->getInt() < 0) + nCellX = -1 * (pWidth->getInt() - nParam); + } + + rCurrentCellX = nParam; + auto pXValue = new RTFValue(nCellX); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, pXValue, + RTFOverwrite::NO_APPEND); + if (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().getDestination()) + { + m_nNestedCells++; + // Push cell properties. + m_aNestedTableCellsSprms.push_back(m_aStates.top().getTableCellSprms()); + m_aNestedTableCellsAttributes.push_back(m_aStates.top().getTableCellAttributes()); + } + else + { + m_nTopLevelCells++; + // Push cell properties. + m_aTopLevelTableCellsSprms.push_back(m_aStates.top().getTableCellSprms()); + m_aTopLevelTableCellsAttributes.push_back(m_aStates.top().getTableCellAttributes()); + } + + m_aStates.top().getTableCellSprms() = m_aDefaultState.getTableCellSprms(); + m_aStates.top().getTableCellAttributes() = m_aDefaultState.getTableCellAttributes(); + // We assume text after a row definition always belongs to the table, to handle text before the real INTBL token + dispatchFlag(RTFKeyword::INTBL); + if (!m_nCellxMax) + { + // Wasn't in table, but now is -> tblStart. + RTFSprms aAttributes; + RTFSprms aSprms; + aSprms.set(NS_ooxml::LN_tblStart, new RTFValue(1)); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); + Mapper().props(pProperties); + } + m_nCellxMax = std::max(m_nCellxMax, nParam); + return true; + } + break; + case RTFKeyword::TRRH: + { + OUString hRule("auto"); + if (nParam < 0) + { + tools::SvRef<RTFValue> pAbsValue(new RTFValue(-nParam)); + std::swap(pIntValue, pAbsValue); + + hRule = "exact"; + } + else if (nParam > 0) + hRule = "atLeast"; + + putNestedAttribute(m_aStates.top().getTableRowSprms(), + NS_ooxml::LN_CT_TrPrBase_trHeight, NS_ooxml::LN_CT_Height_val, + pIntValue); + + auto pHRule = new RTFValue(hRule); + putNestedAttribute(m_aStates.top().getTableRowSprms(), + NS_ooxml::LN_CT_TrPrBase_trHeight, NS_ooxml::LN_CT_Height_hRule, + pHRule); + return true; + } + break; + case RTFKeyword::TRLEFT: + { + // the value is in twips + putNestedAttribute(m_aStates.top().getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblInd, + NS_ooxml::LN_CT_TblWidth_type, + new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); + putNestedAttribute(m_aStates.top().getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblInd, + NS_ooxml::LN_CT_TblWidth_w, new RTFValue(nParam)); + auto const aDestination = m_aStates.top().getDestination(); + int& rCurrentTRLeft((Destination::NESTEDTABLEPROPERTIES == aDestination) + ? m_nNestedTRLeft + : m_nTopLevelTRLeft); + int& rCurrentCellX((Destination::NESTEDTABLEPROPERTIES == aDestination) + ? m_nNestedCurrentCellX + : m_nTopLevelCurrentCellX); + rCurrentTRLeft = rCurrentCellX = nParam; + return true; + } + break; + case RTFKeyword::CLSHDNG: + { + int nValue = -1; + + if (nParam < 1) + nValue = NS_ooxml::LN_Value_ST_Shd_clear; + else if (nParam < 750) + // Values in between 1 and 250 visually closer to 0% shading (white) + // But this will mean "no shading" while cell actually have some. + // So lets use minimal available value. + nValue = NS_ooxml::LN_Value_ST_Shd_pct5; + else if (nParam < 1100) + nValue = NS_ooxml::LN_Value_ST_Shd_pct10; + else if (nParam < 1350) + nValue = NS_ooxml::LN_Value_ST_Shd_pct12; + else if (nParam < 1750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct15; + else if (nParam < 2250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct20; + else if (nParam < 2750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct25; + else if (nParam < 3250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct30; + else if (nParam < 3600) + nValue = NS_ooxml::LN_Value_ST_Shd_pct35; + else if (nParam < 3850) + nValue = NS_ooxml::LN_Value_ST_Shd_pct37; + else if (nParam < 4250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct40; + else if (nParam < 4750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct45; + else if (nParam < 5250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct50; + else if (nParam < 5750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct55; + else if (nParam < 6100) + nValue = NS_ooxml::LN_Value_ST_Shd_pct60; + else if (nParam < 6350) + nValue = NS_ooxml::LN_Value_ST_Shd_pct62; + else if (nParam < 6750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct65; + else if (nParam < 7250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct70; + else if (nParam < 7750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct75; + else if (nParam < 8250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct80; + else if (nParam < 8600) + nValue = NS_ooxml::LN_Value_ST_Shd_pct85; + else if (nParam < 8850) + nValue = NS_ooxml::LN_Value_ST_Shd_pct87; + else if (nParam < 9250) + nValue = NS_ooxml::LN_Value_ST_Shd_pct90; + else if (nParam < 9750) + nValue = NS_ooxml::LN_Value_ST_Shd_pct95; + else + // Solid fill + nValue = NS_ooxml::LN_Value_ST_Shd_solid; + + putNestedAttribute(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_shd, + NS_ooxml::LN_CT_Shd_val, new RTFValue(nValue)); + return true; + } + break; + case RTFKeyword::CLPADB: + case RTFKeyword::CLPADL: + case RTFKeyword::CLPADR: + case RTFKeyword::CLPADT: + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, + new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); + aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(nParam)); + // Top and left is swapped, that's what Word does. + switch (nKeyword) + { + case RTFKeyword::CLPADB: + nSprm = NS_ooxml::LN_CT_TcMar_bottom; + break; + case RTFKeyword::CLPADL: + nSprm = NS_ooxml::LN_CT_TcMar_top; + break; + case RTFKeyword::CLPADR: + nSprm = NS_ooxml::LN_CT_TcMar_right; + break; + case RTFKeyword::CLPADT: + nSprm = NS_ooxml::LN_CT_TcMar_left; + break; + default: + break; + } + putNestedSprm(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcMar, + nSprm, new RTFValue(aAttributes)); + return true; + } + break; + case RTFKeyword::TRPADDFB: + case RTFKeyword::TRPADDFL: + case RTFKeyword::TRPADDFR: + case RTFKeyword::TRPADDFT: + { + RTFSprms aAttributes; + switch (nParam) + { + case 3: + aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, + new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); + break; + } + switch (nKeyword) + { + case RTFKeyword::TRPADDFB: + nSprm = NS_ooxml::LN_CT_TcMar_bottom; + break; + case RTFKeyword::TRPADDFL: + nSprm = NS_ooxml::LN_CT_TcMar_left; + break; + case RTFKeyword::TRPADDFR: + nSprm = NS_ooxml::LN_CT_TcMar_right; + break; + case RTFKeyword::TRPADDFT: + nSprm = NS_ooxml::LN_CT_TcMar_top; + break; + default: + break; + } + putNestedAttribute(m_aStates.top().getTableRowSprms(), + NS_ooxml::LN_CT_TblPrBase_tblCellMar, nSprm, + new RTFValue(aAttributes)); + // tdf#74795 also set on current cell, and as default for table cells + // (why isn't this done by domainmapper?) + putNestedAttribute(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcMar, + nSprm, new RTFValue(aAttributes)); + putNestedAttribute(m_aDefaultState.getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcMar, + nSprm, new RTFValue(aAttributes)); + return true; + } + break; + case RTFKeyword::TRPADDB: + case RTFKeyword::TRPADDL: + case RTFKeyword::TRPADDR: + case RTFKeyword::TRPADDT: + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(nParam)); + switch (nKeyword) + { + case RTFKeyword::TRPADDB: + nSprm = NS_ooxml::LN_CT_TcMar_bottom; + break; + case RTFKeyword::TRPADDL: + nSprm = NS_ooxml::LN_CT_TcMar_left; + break; + case RTFKeyword::TRPADDR: + nSprm = NS_ooxml::LN_CT_TcMar_right; + break; + case RTFKeyword::TRPADDT: + nSprm = NS_ooxml::LN_CT_TcMar_top; + break; + default: + break; + } + putNestedSprm(m_aStates.top().getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, + nSprm, new RTFValue(aAttributes)); + // tdf#74795 also set on current cell, and as default for table cells + // (why isn't this done by domainmapper?) + putNestedSprm(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcMar, + nSprm, new RTFValue(aAttributes)); + putNestedSprm(m_aDefaultState.getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_tcMar, + nSprm, new RTFValue(aAttributes)); + return true; + } + case RTFKeyword::TRGAPH: + // Half of the space between the cells of a table row: default left/right table cell margin. + if (nParam > 0) + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, + new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); + aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, pIntValue); + // FIXME: this is wrong, it is half-gap, needs to be distinguished from margin! depending on TRPADDFL/TRPADDFR + putNestedSprm(m_aStates.top().getTableRowSprms(), + NS_ooxml::LN_CT_TblPrBase_tblCellMar, NS_ooxml::LN_CT_TblCellMar_left, + new RTFValue(aAttributes)); + putNestedSprm(m_aStates.top().getTableRowSprms(), + NS_ooxml::LN_CT_TblPrBase_tblCellMar, + NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes)); + } + return true; + case RTFKeyword::TRFTSWIDTH: + putNestedAttribute(m_aStates.top().getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + NS_ooxml::LN_CT_TblWidth_type, pIntValue); + return true; + case RTFKeyword::TRWWIDTH: + putNestedAttribute(m_aStates.top().getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + NS_ooxml::LN_CT_TblWidth_w, pIntValue); + return true; + default: + break; + } + + return false; +} + +RTFError RTFDocumentImpl::dispatchValue(RTFKeyword nKeyword, int nParam) +{ + setNeedSect(true); + checkUnicode(/*bUnicode =*/nKeyword != RTFKeyword::U, /*bHex =*/true); + RTFSkipDestination aSkip(*this); + int nSprm = 0; + tools::SvRef<RTFValue> pIntValue(new RTFValue(nParam)); + // Trivial table sprms. + if (dispatchTableSprmValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Trivial character sprms. + if (dispatchCharacterSprmValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Trivial character attributes. + if (dispatchCharacterAttributeValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Trivial paragraph sprms. + if (dispatchParagraphSprmValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Info group. + if (dispatchInfoValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Frame size / position. + if (dispatchFrameValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Table-related values. + if (dispatchTableValue(nKeyword, nParam)) + { + return RTFError::OK; + } + + // Then check for the more complex ones. + switch (nKeyword) + { + case RTFKeyword::F: + case RTFKeyword::AF: + switch (m_aStates.top().getRunType()) + { + case RTFParserState::RunType::RTLCH_LTRCH_1: + case RTFParserState::RunType::LTRCH_RTLCH_2: + nSprm = NS_ooxml::LN_CT_Fonts_cs; + break; + case RTFParserState::RunType::DBCH: + nSprm = NS_ooxml::LN_CT_Fonts_eastAsia; + break; + case RTFParserState::RunType::NONE: + case RTFParserState::RunType::LOCH: + case RTFParserState::RunType::HICH: + case RTFParserState::RunType::LTRCH_RTLCH_1: + case RTFParserState::RunType::RTLCH_LTRCH_2: + default: + nSprm = NS_ooxml::LN_CT_Fonts_ascii; + break; + } + + if (m_aStates.top().getDestination() == Destination::FONTTABLE + || m_aStates.top().getDestination() == Destination::FONTENTRY) + { + // Some text in buffer? It is font name. So previous font definition is complete + if (m_aStates.top().getCurrentDestinationText()->getLength()) + handleFontTableEntry(); + + m_aFontIndexes.push_back(nParam); + m_nCurrentFontIndex = getFontIndex(nParam); + } + else if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + RTFSprms aFontAttributes; + aFontAttributes.set(nSprm, new RTFValue(m_aFontNames[getFontIndex(nParam)])); + RTFSprms aRunPropsSprms; + aRunPropsSprms.set(NS_ooxml::LN_EG_RPrBase_rFonts, new RTFValue(aFontAttributes)); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_rPr, + new RTFValue(RTFSprms(), aRunPropsSprms), + RTFOverwrite::NO_APPEND); + } + else + { + m_nCurrentFontIndex = getFontIndex(nParam); + auto pValue = new RTFValue(getFontName(m_nCurrentFontIndex)); + putNestedAttribute(m_aStates.top().getCharacterSprms(), + NS_ooxml::LN_EG_RPrBase_rFonts, nSprm, pValue); + if (nKeyword == RTFKeyword::F) + m_aStates.top().setCurrentEncoding(getEncoding(m_nCurrentFontIndex)); + } + break; + case RTFKeyword::RED: + m_aStates.top().getCurrentColor().SetRed(nParam); + break; + case RTFKeyword::GREEN: + m_aStates.top().getCurrentColor().SetGreen(nParam); + break; + case RTFKeyword::BLUE: + m_aStates.top().getCurrentColor().SetBlue(nParam); + break; + case RTFKeyword::FCHARSET: + { + // we always send text to the domain mapper in OUString, so no + // need to send encoding info + int i; + for (i = 0; i < nRTFEncodings; i++) + { + if (aRTFEncodings[i].charset == nParam) + break; + } + if (i == nRTFEncodings) + // not found + return RTFError::OK; + + m_nCurrentEncoding + = aRTFEncodings[i].codepage == 0 // Default (CP_ACP) + ? osl_getThreadTextEncoding() + : rtl_getTextEncodingFromWindowsCodePage(aRTFEncodings[i].codepage); + m_aStates.top().setCurrentEncoding(m_nCurrentEncoding); + } + break; + case RTFKeyword::ANSICPG: + case RTFKeyword::CPG: + { + rtl_TextEncoding nEncoding + = (nParam == 0) + ? utl_getWinTextEncodingFromLangStr(utl_getLocaleForGlobalDefaultEncoding()) + : rtl_getTextEncodingFromWindowsCodePage(nParam); + if (nKeyword == RTFKeyword::ANSICPG) + m_aDefaultState.setCurrentEncoding(nEncoding); + else + m_nCurrentEncoding = nEncoding; + m_aStates.top().setCurrentEncoding(nEncoding); + } + break; + case RTFKeyword::CF: + { + RTFSprms aAttributes; + auto pValue = new RTFValue(sal_uInt32(getColorTable(nParam))); + aAttributes.set(NS_ooxml::LN_CT_Color_val, pValue); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_color, + new RTFValue(aAttributes)); + } + break; + case RTFKeyword::S: + { + m_aStates.top().setCurrentStyleIndex(nParam); + + if (m_aStates.top().getDestination() == Destination::STYLESHEET + || m_aStates.top().getDestination() == Destination::STYLEENTRY) + { + m_nCurrentStyleIndex = nParam; + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, + pValue); // paragraph style + } + else + { + OUString aName = getStyleName(nParam); + if (!aName.isEmpty()) + { + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_pStyle, + new RTFValue(aName)); + else + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_pStyle, + new RTFValue(aName)); + } + } + } + break; + case RTFKeyword::CS: + m_aStates.top().setCurrentCharacterStyleIndex(nParam); + if (m_aStates.top().getDestination() == Destination::STYLESHEET + || m_aStates.top().getDestination() == Destination::STYLEENTRY) + { + m_nCurrentStyleIndex = nParam; + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_character); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, + pValue); // character style + } + else + { + OUString aName = getStyleName(nParam); + if (!aName.isEmpty()) + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_rStyle, + new RTFValue(aName)); + } + break; + case RTFKeyword::DS: + if (m_aStates.top().getDestination() == Destination::STYLESHEET + || m_aStates.top().getDestination() == Destination::STYLEENTRY) + { + m_nCurrentStyleIndex = nParam; + auto pValue = new RTFValue(0); // TODO no value in enum StyleType? + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, + pValue); // section style + } + break; + case RTFKeyword::TS: + if (m_aStates.top().getDestination() == Destination::STYLESHEET + || m_aStates.top().getDestination() == Destination::STYLEENTRY) + { + m_nCurrentStyleIndex = nParam; + // FIXME the correct value would be NS_ooxml::LN_Value_ST_StyleType_table but maybe table styles mess things up in dmapper, be cautious and disable them for now + auto pValue = new RTFValue(0); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, + pValue); // table style + } + break; + case RTFKeyword::DEFF: + m_nDefaultFontIndex = nParam; + break; + case RTFKeyword::STSHFDBCH: + // tdf#123703 switch off longer space sequence except in the case of the fixed compatibility setting font id 31505 + if (nParam != 31505) + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence, + new RTFValue(0)); + break; + case RTFKeyword::DEFLANG: + case RTFKeyword::ADEFLANG: + { + LanguageTag aTag((LanguageType(static_cast<sal_uInt16>(nParam)))); + auto pValue = new RTFValue(aTag.getBcp47()); + putNestedAttribute(m_aStates.top().getCharacterSprms(), + (nKeyword == RTFKeyword::DEFLANG ? NS_ooxml::LN_EG_RPrBase_lang + : NS_ooxml::LN_CT_Language_bidi), + nSprm, pValue); + } + break; + case RTFKeyword::CHCBPAT: + { + auto pValue = new RTFValue(sal_uInt32(nParam ? getColorTable(nParam) : COL_AUTO)); + putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_shd, + NS_ooxml::LN_CT_Shd_fill, pValue); + } + break; + case RTFKeyword::CLCBPAT: + case RTFKeyword::CLCBPATRAW: + { + auto pValue = new RTFValue(sal_uInt32(getColorTable(nParam))); + putNestedAttribute(m_aStates.top().getTableCellSprms(), NS_ooxml::LN_CT_TcPrBase_shd, + NS_ooxml::LN_CT_Shd_fill, pValue); + } + break; + case RTFKeyword::CBPAT: + if (nParam) + { + auto pValue = new RTFValue(sal_uInt32(getColorTable(nParam))); + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_shd, + NS_ooxml::LN_CT_Shd_fill, pValue); + } + break; + case RTFKeyword::ULC: + { + auto pValue = new RTFValue(sal_uInt32(getColorTable(nParam))); + m_aStates.top().getCharacterSprms().set(0x6877, pValue); + } + break; + case RTFKeyword::HIGHLIGHT: + { + auto pValue = new RTFValue(sal_uInt32(nParam ? getColorTable(nParam) : COL_AUTO)); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_highlight, pValue); + } + break; + case RTFKeyword::UP: + case RTFKeyword::DN: + { + auto pValue = new RTFValue(nParam * (nKeyword == RTFKeyword::UP ? 1 : -1)); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_position, pValue); + } + break; + case RTFKeyword::HORZVERT: + { + auto pValue = new RTFValue(int(true)); + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_EastAsianLayout_vert, + pValue); + if (nParam) + // rotate fits to a single line + m_aStates.top().getCharacterAttributes().set( + NS_ooxml::LN_CT_EastAsianLayout_vertCompress, pValue); + } + break; + case RTFKeyword::EXPND: + { + // Convert quarter-points to twentieths of a point + auto pValue = new RTFValue(nParam * 5); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_spacing, pValue); + } + break; + case RTFKeyword::TWOINONE: + { + auto pValue = new RTFValue(int(true)); + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_EastAsianLayout_combine, + pValue); + Id nId = 0; + switch (nParam) + { + case 0: + nId = NS_ooxml::LN_Value_ST_CombineBrackets_none; + break; + case 1: + nId = NS_ooxml::LN_Value_ST_CombineBrackets_round; + break; + case 2: + nId = NS_ooxml::LN_Value_ST_CombineBrackets_square; + break; + case 3: + nId = NS_ooxml::LN_Value_ST_CombineBrackets_angle; + break; + case 4: + nId = NS_ooxml::LN_Value_ST_CombineBrackets_curly; + break; + } + if (nId > 0) + m_aStates.top().getCharacterAttributes().set( + NS_ooxml::LN_CT_EastAsianLayout_combineBrackets, new RTFValue(nId)); + } + break; + case RTFKeyword::SL: + { + // This is similar to RTFKeyword::ABSH, negative value means 'exact', positive means 'at least'. + tools::SvRef<RTFValue> pValue( + new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_atLeast)); + if (nParam < 0) + { + pValue = new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_exact); + pIntValue = new RTFValue(-nParam); + } + m_aStates.top().getParagraphAttributes().set(NS_ooxml::LN_CT_Spacing_lineRule, pValue); + m_aStates.top().getParagraphAttributes().set(NS_ooxml::LN_CT_Spacing_line, pIntValue); + } + break; + case RTFKeyword::SLMULT: + if (nParam > 0) + { + auto pValue = new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto); + m_aStates.top().getParagraphAttributes().set(NS_ooxml::LN_CT_Spacing_lineRule, + pValue); + } + break; + case RTFKeyword::BRDRW: + { + // dmapper expects it in 1/8 pt, we have it in twip - but avoid rounding 1 to 0 + if (nParam > 1) + nParam = nParam * 2 / 5; + auto pValue = new RTFValue(nParam); + putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_sz, pValue); + } + break; + case RTFKeyword::BRDRCF: + { + auto pValue = new RTFValue(sal_uInt32(getColorTable(nParam))); + putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_color, pValue); + } + break; + case RTFKeyword::BRSP: + { + // dmapper expects it in points, we have it in twip + auto pValue = new RTFValue(nParam / 20); + putBorderProperty(m_aStates, NS_ooxml::LN_CT_Border_space, pValue); + } + break; + case RTFKeyword::TX: + { + m_aStates.top().getTabAttributes().set(NS_ooxml::LN_CT_TabStop_pos, pIntValue); + auto pValue = new RTFValue(m_aStates.top().getTabAttributes()); + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + putNestedSprm(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_PPrBase_tabs, + NS_ooxml::LN_CT_Tabs_tab, pValue); + else + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_tabs, + NS_ooxml::LN_CT_Tabs_tab, pValue); + m_aStates.top().getTabAttributes().clear(); + } + break; + case RTFKeyword::ILVL: + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, + NS_ooxml::LN_CT_NumPr_ilvl, pIntValue); + break; + case RTFKeyword::LISTTEMPLATEID: + // This one is not referenced anywhere, so it's pointless to store it at the moment. + break; + case RTFKeyword::LISTID: + { + if (m_aStates.top().getDestination() == Destination::LISTENTRY) + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, + pIntValue); + else if (m_aStates.top().getDestination() == Destination::LISTOVERRIDEENTRY) + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Num_abstractNumId, pIntValue); + m_aStates.top().setCurrentListIndex(nParam); + } + break; + case RTFKeyword::LS: + { + if (m_aStates.top().getDestination() == Destination::LISTOVERRIDEENTRY) + { + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_AbstractNum_nsid, + pIntValue); + m_aStates.top().setCurrentListOverrideIndex(nParam); + } + else + { + // Insert at the start, so properties inherited from the list + // can be overridden by direct formatting. But still allow the + // case when old-style paragraph numbering is already + // tokenized. + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, + NS_ooxml::LN_CT_NumPr_numId, pIntValue, RTFOverwrite::YES_PREPEND); + } + } + break; + case RTFKeyword::UC: + if ((SAL_MIN_INT16 <= nParam) && (nParam <= SAL_MAX_INT16)) + m_aStates.top().setUc(nParam); + break; + case RTFKeyword::U: + // sal_Unicode is unsigned 16-bit, RTF may represent that as a + // signed SAL_MIN_INT16..SAL_MAX_INT16 or 0..SAL_MAX_UINT16. The + // static_cast() will do the right thing. + if ((SAL_MIN_INT16 <= nParam) && (nParam <= SAL_MAX_UINT16)) + { + if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS) + { + if (nParam != ';') + m_aStates.top().getLevelNumbers().push_back(sal_Int32(nParam)); + else + // ';' in \u form is not considered valid. + m_aStates.top().setLevelNumbersValid(false); + } + else + m_aUnicodeBuffer.append(static_cast<sal_Unicode>(nParam)); + m_aStates.top().getCharsToSkip() = m_aStates.top().getUc(); + } + break; + case RTFKeyword::LEVELFOLLOW: + { + OUString sValue; + switch (nParam) + { + case 0: + sValue = "tab"; + break; + case 1: + sValue = "space"; + break; + case 2: + sValue = "nothing"; + break; + } + if (!sValue.isEmpty()) + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_suff, new RTFValue(sValue)); + } + break; + case RTFKeyword::FPRQ: + { + sal_Int32 nValue = 0; + switch (nParam) + { + case 0: + nValue = NS_ooxml::LN_Value_ST_Pitch_default; + break; + case 1: + nValue = NS_ooxml::LN_Value_ST_Pitch_fixed; + break; + case 2: + nValue = NS_ooxml::LN_Value_ST_Pitch_variable; + break; + } + if (nValue) + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_Pitch_val, new RTFValue(nValue)); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Font_pitch, + new RTFValue(aAttributes)); + } + } + break; + case RTFKeyword::LISTOVERRIDECOUNT: + // Ignore this for now, the exporter always emits it with a zero parameter. + break; + case RTFKeyword::PICSCALEX: + m_aStates.top().getPicture().nScaleX = nParam; + break; + case RTFKeyword::PICSCALEY: + m_aStates.top().getPicture().nScaleY = nParam; + break; + case RTFKeyword::PICW: + m_aStates.top().getPicture().nWidth = nParam; + break; + case RTFKeyword::PICH: + m_aStates.top().getPicture().nHeight = nParam; + break; + case RTFKeyword::PICWGOAL: + m_aStates.top().getPicture().nGoalWidth = convertTwipToMm100(nParam); + break; + case RTFKeyword::PICHGOAL: + m_aStates.top().getPicture().nGoalHeight = convertTwipToMm100(nParam); + break; + case RTFKeyword::PICCROPL: + m_aStates.top().getPicture().nCropL = convertTwipToMm100(nParam); + break; + case RTFKeyword::PICCROPR: + m_aStates.top().getPicture().nCropR = convertTwipToMm100(nParam); + break; + case RTFKeyword::PICCROPT: + m_aStates.top().getPicture().nCropT = convertTwipToMm100(nParam); + break; + case RTFKeyword::PICCROPB: + m_aStates.top().getPicture().nCropB = convertTwipToMm100(nParam); + break; + case RTFKeyword::SHPWRK: + { + int nValue = 0; + switch (nParam) + { + case 0: + nValue = NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_bothSides; + break; + case 1: + nValue = NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_left; + break; + case 2: + nValue = NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_right; + break; + case 3: + nValue = NS_ooxml::LN_Value_wordprocessingDrawing_ST_WrapText_largest; + break; + default: + break; + } + auto pValue = new RTFValue(nValue); + RTFValue::Pointer_t pTight + = m_aStates.top().getCharacterSprms().find(NS_ooxml::LN_EG_WrapType_wrapTight); + if (pTight) + pTight->getAttributes().set(NS_ooxml::LN_CT_WrapTight_wrapText, pValue); + else + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_WrapSquare_wrapText, + pValue); + } + break; + case RTFKeyword::SHPWR: + { + switch (nParam) + { + case 1: + m_aStates.top().getShape().setWrap(text::WrapTextMode_NONE); + break; + case 2: + m_aStates.top().getShape().setWrap(text::WrapTextMode_PARALLEL); + break; + case 3: + m_aStates.top().getShape().setWrap(text::WrapTextMode_THROUGH); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_WrapType_wrapNone, + new RTFValue()); + break; + case 4: + m_aStates.top().getShape().setWrap(text::WrapTextMode_PARALLEL); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_WrapType_wrapTight, + new RTFValue()); + break; + case 5: + m_aStates.top().getShape().setWrap(text::WrapTextMode_THROUGH); + break; + } + } + break; + case RTFKeyword::COLS: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_cols, NS_ooxml::LN_CT_Columns_num, + pIntValue); + break; + case RTFKeyword::COLSX: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_cols, NS_ooxml::LN_CT_Columns_space, + pIntValue); + break; + case RTFKeyword::COLNO: + putNestedSprm(m_aStates.top().getSectionSprms(), NS_ooxml::LN_EG_SectPrContents_cols, + NS_ooxml::LN_CT_Columns_col, pIntValue); + break; + case RTFKeyword::COLW: + case RTFKeyword::COLSR: + { + RTFSprms& rAttributes = getLastAttributes(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_cols); + rAttributes.set((nKeyword == RTFKeyword::COLW ? NS_ooxml::LN_CT_Column_w + : NS_ooxml::LN_CT_Column_space), + pIntValue); + } + break; + case RTFKeyword::PAPERH: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_h, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::PGHSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_h, + pIntValue); + break; + case RTFKeyword::PAPERW: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_w, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::PGWSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgSz, NS_ooxml::LN_CT_PageSz_w, + pIntValue); + break; + case RTFKeyword::MARGL: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_left, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::MARGLSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_left, + pIntValue); + break; + case RTFKeyword::MARGR: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_right, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::MARGRSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_right, + pIntValue); + break; + case RTFKeyword::MARGT: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_top, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::MARGTSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_top, + pIntValue); + break; + case RTFKeyword::MARGB: + putNestedAttribute(m_aDefaultState.getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_bottom, + pIntValue); + [[fallthrough]]; // set the default + current value + case RTFKeyword::MARGBSXN: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_bottom, + pIntValue); + break; + case RTFKeyword::HEADERY: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_header, + pIntValue); + break; + case RTFKeyword::FOOTERY: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_footer, + pIntValue); + break; + case RTFKeyword::GUTTER: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgMar, NS_ooxml::LN_CT_PageMar_gutter, + pIntValue); + break; + case RTFKeyword::DEFTAB: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_defaultTabStop, pIntValue); + break; + case RTFKeyword::LINEMOD: + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_lnNumType, + NS_ooxml::LN_CT_LineNumber_countBy, pIntValue); + break; + case RTFKeyword::LINEX: + if (nParam) + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_lnNumType, + NS_ooxml::LN_CT_LineNumber_distance, pIntValue); + break; + case RTFKeyword::LINESTARTS: + { + // OOXML <w:lnNumType w:start="..."/> is 0-based, RTF is 1-based. + auto pStart = tools::make_ref<RTFValue>(nParam - 1); + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_lnNumType, + NS_ooxml::LN_CT_LineNumber_start, pStart); + } + break; + case RTFKeyword::REVAUTH: + case RTFKeyword::REVAUTHDEL: + { + auto pValue = new RTFValue(m_aAuthors[nParam]); + putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange, + NS_ooxml::LN_CT_TrackChange_author, pValue); + } + break; + case RTFKeyword::REVDTTM: + case RTFKeyword::REVDTTMDEL: + { + OUString aStr( + OStringToOUString(DTTM22OString(nParam), m_aStates.top().getCurrentEncoding())); + auto pValue = new RTFValue(aStr); + putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange, + NS_ooxml::LN_CT_TrackChange_date, pValue); + } + break; + case RTFKeyword::SHPLEFT: + m_aStates.top().getShape().setLeft(convertTwipToMm100(nParam)); + break; + case RTFKeyword::SHPTOP: + m_aStates.top().getShape().setTop(convertTwipToMm100(nParam)); + break; + case RTFKeyword::SHPRIGHT: + m_aStates.top().getShape().setRight(convertTwipToMm100(nParam)); + break; + case RTFKeyword::SHPBOTTOM: + m_aStates.top().getShape().setBottom(convertTwipToMm100(nParam)); + break; + case RTFKeyword::SHPZ: + m_aStates.top().getShape().setZ(nParam); + break; + case RTFKeyword::FFTYPE: + switch (nParam) + { + case 0: + m_nFormFieldType = RTFFormFieldType::TEXT; + break; + case 1: + m_nFormFieldType = RTFFormFieldType::CHECKBOX; + break; + case 2: + m_nFormFieldType = RTFFormFieldType::LIST; + break; + default: + m_nFormFieldType = RTFFormFieldType::NONE; + break; + } + break; + case RTFKeyword::FFDEFRES: + if (m_nFormFieldType == RTFFormFieldType::CHECKBOX) + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFCheckBox_default, pIntValue); + else if (m_nFormFieldType == RTFFormFieldType::LIST) + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_default, pIntValue); + break; + case RTFKeyword::FFRES: + // 25 means undefined, see [MS-DOC] 2.9.79, FFDataBits. + if (m_nFormFieldType == RTFFormFieldType::CHECKBOX && nParam != 25) + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFCheckBox_checked, pIntValue); + else if (m_nFormFieldType == RTFFormFieldType::LIST) + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_result, pIntValue); + break; + case RTFKeyword::EDMINS: + if (m_xDocumentProperties.is()) + { + // tdf#116851 some RTF may be malformed + if (nParam < 0) + nParam = -nParam; + m_xDocumentProperties->setEditingDuration(nParam); + } + break; + case RTFKeyword::NOFPAGES: + case RTFKeyword::NOFWORDS: + case RTFKeyword::NOFCHARS: + case RTFKeyword::NOFCHARSWS: + if (m_xDocumentProperties.is()) + { + comphelper::SequenceAsHashMap aSeq = m_xDocumentProperties->getDocumentStatistics(); + OUString aName; + switch (nKeyword) + { + case RTFKeyword::NOFPAGES: + aName = "PageCount"; + nParam = 99; + break; + case RTFKeyword::NOFWORDS: + aName = "WordCount"; + break; + case RTFKeyword::NOFCHARS: + aName = "CharacterCount"; + break; + case RTFKeyword::NOFCHARSWS: + aName = "NonWhitespaceCharacterCount"; + break; + default: + break; + } + if (!aName.isEmpty()) + { + aSeq[aName] <<= sal_Int32(nParam); + m_xDocumentProperties->setDocumentStatistics(aSeq.getAsConstNamedValueList()); + } + } + break; + case RTFKeyword::VERSION: + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setEditingCycles(nParam); + break; + case RTFKeyword::VERN: + // Ignore this for now, later the RTF writer version could be used to add hacks for older buggy writers. + break; + case RTFKeyword::FTNSTART: + putNestedSprm(m_aDefaultState.getParagraphSprms(), + NS_ooxml::LN_EG_SectPrContents_footnotePr, + NS_ooxml::LN_EG_FtnEdnNumProps_numStart, pIntValue); + break; + case RTFKeyword::AFTNSTART: + putNestedSprm(m_aDefaultState.getParagraphSprms(), + NS_ooxml::LN_EG_SectPrContents_endnotePr, + NS_ooxml::LN_EG_FtnEdnNumProps_numStart, pIntValue); + break; + case RTFKeyword::DFRMTXTX: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hSpace, nParam); + break; + case RTFKeyword::DFRMTXTY: + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vSpace, nParam); + break; + case RTFKeyword::DXFRTEXT: + { + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hSpace, nParam); + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vSpace, nParam); + } + break; + case RTFKeyword::FLYVERT: + { + RTFVertOrient aVertOrient(nParam); + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_yAlign, + aVertOrient.GetAlign()); + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_vAnchor, + aVertOrient.GetAnchor()); + } + break; + case RTFKeyword::FLYHORZ: + { + RTFHoriOrient aHoriOrient(nParam); + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_xAlign, + aHoriOrient.GetAlign()); + m_aStates.top().getFrame().setSprm(NS_ooxml::LN_CT_FramePr_hAnchor, + aHoriOrient.GetAnchor()); + } + break; + case RTFKeyword::FLYANCHOR: + break; + case RTFKeyword::WMETAFILE: + m_aStates.top().getPicture().eWMetafile = nParam; + break; + case RTFKeyword::SB: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, + NS_ooxml::LN_CT_Spacing_before, pIntValue); + break; + case RTFKeyword::SA: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, + NS_ooxml::LN_CT_Spacing_after, pIntValue); + break; + case RTFKeyword::DPX: + m_aStates.top().getDrawingObject().setLeft(convertTwipToMm100(nParam)); + break; + case RTFKeyword::DPY: + m_aStates.top().getDrawingObject().setTop(convertTwipToMm100(nParam)); + break; + case RTFKeyword::DPXSIZE: + m_aStates.top().getDrawingObject().setRight(convertTwipToMm100(nParam)); + break; + case RTFKeyword::DPYSIZE: + m_aStates.top().getDrawingObject().setBottom(convertTwipToMm100(nParam)); + break; + case RTFKeyword::PNSTART: + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_start, pIntValue); + break; + case RTFKeyword::PNF: + { + auto pValue = new RTFValue(m_aFontNames[getFontIndex(nParam)]); + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_Fonts_ascii, pValue); + putNestedSprm(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_Lvl_rPr, + NS_ooxml::LN_EG_RPrBase_rFonts, new RTFValue(aAttributes)); + } + break; + case RTFKeyword::VIEWSCALE: + m_aSettingsTableAttributes.set(NS_ooxml::LN_CT_Zoom_percent, pIntValue); + break; + case RTFKeyword::BIN: + { + m_aStates.top().setInternalState(RTFInternalState::BIN); + m_aStates.top().setBinaryToRead(nParam); + } + break; + case RTFKeyword::DPLINECOR: + m_aStates.top().getDrawingObject().setLineColorR(nParam); + m_aStates.top().getDrawingObject().setHasLineColor(true); + break; + case RTFKeyword::DPLINECOG: + m_aStates.top().getDrawingObject().setLineColorG(nParam); + m_aStates.top().getDrawingObject().setHasLineColor(true); + break; + case RTFKeyword::DPLINECOB: + m_aStates.top().getDrawingObject().setLineColorB(nParam); + m_aStates.top().getDrawingObject().setHasLineColor(true); + break; + case RTFKeyword::DPFILLBGCR: + m_aStates.top().getDrawingObject().setFillColorR(nParam); + m_aStates.top().getDrawingObject().setHasFillColor(true); + break; + case RTFKeyword::DPFILLBGCG: + m_aStates.top().getDrawingObject().setFillColorG(nParam); + m_aStates.top().getDrawingObject().setHasFillColor(true); + break; + case RTFKeyword::DPFILLBGCB: + m_aStates.top().getDrawingObject().setFillColorB(nParam); + m_aStates.top().getDrawingObject().setHasFillColor(true); + break; + case RTFKeyword::DODHGT: + m_aStates.top().getDrawingObject().setDhgt(nParam); + break; + case RTFKeyword::DPPOLYCOUNT: + if (nParam >= 0) + { + m_aStates.top().getDrawingObject().setPolyLineCount(nParam); + } + break; + case RTFKeyword::DPPTX: + { + RTFDrawingObject& rDrawingObject = m_aStates.top().getDrawingObject(); + + if (rDrawingObject.getPolyLinePoints().empty()) + dispatchValue(RTFKeyword::DPPOLYCOUNT, 2); + + rDrawingObject.getPolyLinePoints().emplace_back(convertTwipToMm100(nParam), 0); + } + break; + case RTFKeyword::DPPTY: + { + RTFDrawingObject& rDrawingObject = m_aStates.top().getDrawingObject(); + if (!rDrawingObject.getPolyLinePoints().empty()) + { + rDrawingObject.getPolyLinePoints().back().Y = convertTwipToMm100(nParam); + rDrawingObject.setPolyLineCount(rDrawingObject.getPolyLineCount() - 1); + if (rDrawingObject.getPolyLineCount() == 0 && rDrawingObject.getPropertySet().is()) + { + uno::Sequence<uno::Sequence<awt::Point>> aPointSequenceSequence + = { comphelper::containerToSequence(rDrawingObject.getPolyLinePoints()) }; + rDrawingObject.getPropertySet()->setPropertyValue( + "PolyPolygon", uno::Any(aPointSequenceSequence)); + } + } + } + break; + case RTFKeyword::SHPFBLWTXT: + // Shape is below text -> send it to the background. + m_aStates.top().getShape().setInBackground(nParam != 0); + break; + case RTFKeyword::FI: + { + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + if (m_aStates.top().getLevelNumbersValid()) + putNestedAttribute(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_firstLine, pIntValue); + else + m_aInvalidListLevelFirstIndents[m_nListLevel] = nParam; + } + else + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_firstLine, pIntValue); + break; + } + case RTFKeyword::LI: + { + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + if (m_aStates.top().getLevelNumbersValid()) + putNestedAttribute(m_aStates.top().getTableSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_left, pIntValue); + } + else + { + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_left, pIntValue); + } + // It turns out \li should reset the \fi inherited from the stylesheet. + // So set the direct formatting to zero, if we don't have such direct formatting yet. + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_firstLine, new RTFValue(0), + RTFOverwrite::NO_IGNORE); + } + break; + case RTFKeyword::RI: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_right, pIntValue); + break; + case RTFKeyword::LIN: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_start, pIntValue); + break; + case RTFKeyword::RIN: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_ind, + NS_ooxml::LN_CT_Ind_end, pIntValue); + break; + case RTFKeyword::OUTLINELEVEL: + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_outlineLvl, pIntValue); + break; + case RTFKeyword::PROPTYPE: + { + switch (nParam) + { + case 3: + m_aStates.top().setPropType(cppu::UnoType<sal_Int32>::get()); + break; + case 5: + m_aStates.top().setPropType(cppu::UnoType<double>::get()); + break; + case 11: + m_aStates.top().setPropType(cppu::UnoType<bool>::get()); + break; + case 30: + m_aStates.top().setPropType(cppu::UnoType<OUString>::get()); + break; + case 64: + m_aStates.top().setPropType(cppu::UnoType<util::DateTime>::get()); + break; + } + } + break; + case RTFKeyword::DIBITMAP: + m_aStates.top().getPicture().eStyle = RTFBmpStyle::DIBITMAP; + break; + case RTFKeyword::TRWWIDTHA: + m_aStates.top().setTableRowWidthAfter(nParam); + break; + case RTFKeyword::ANIMTEXT: + { + Id nId = 0; + switch (nParam) + { + case 0: + nId = NS_ooxml::LN_Value_ST_TextEffect_none; + break; + case 2: + nId = NS_ooxml::LN_Value_ST_TextEffect_blinkBackground; + break; + } + + if (nId > 0) + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_effect, + new RTFValue(nId)); + break; + } + case RTFKeyword::VIEWBKSP: + { + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_displayBackgroundShape, pIntValue); + // Send this token immediately, if it only appears before the first + // run, it will be too late, we ignored the background shape already by then. + outputSettingsTable(); + break; + } + case RTFKeyword::STEXTFLOW: + { + Id nId = 0; + switch (nParam) + { + case 0: + nId = NS_ooxml::LN_Value_ST_TextDirection_lrTb; + break; + case 1: + nId = NS_ooxml::LN_Value_ST_TextDirection_tbRl; + break; + } + + if (nId > 0) + { + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_textDirection, + new RTFValue(nId)); + } + } + break; + case RTFKeyword::LBR: + { + Id nId = 0; + switch (nParam) + { + case 1: + nId = NS_ooxml::LN_Value_ST_BrClear_left; + break; + case 2: + nId = NS_ooxml::LN_Value_ST_BrClear_right; + break; + case 3: + nId = NS_ooxml::LN_Value_ST_BrClear_all; + break; + } + + if (nId > 0) + { + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Br_clear, + new RTFValue(nId)); + } + } + break; + case RTFKeyword::PGBRDROPT: + { + sal_Int16 nOffsetFrom = (nParam & 0xe0) >> 5; + bool bFromEdge = nOffsetFrom == 1; + if (bFromEdge) + { + Id nId = NS_ooxml::LN_Value_doc_ST_PageBorderOffset_page; + putNestedAttribute(m_aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgBorders, + NS_ooxml::LN_CT_PageBorders_offsetFrom, new RTFValue(nId)); + } + } + break; + default: + { + SAL_INFO("writerfilter", "TODO handle value '" << keywordToString(nKeyword) << "'"); + aSkip.setParsed(false); + } + break; + } + return RTFError::OK; +} + +} // namespace rtftok +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdocumentfactory.cxx b/writerfilter/source/rtftok/rtfdocumentfactory.cxx new file mode 100644 index 000000000..75b109b68 --- /dev/null +++ b/writerfilter/source/rtftok/rtfdocumentfactory.cxx @@ -0,0 +1,28 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfdocumentimpl.hxx" + +namespace writerfilter::rtftok +{ +RTFDocument::Pointer_t RTFDocumentFactory::createDocument( + css::uno::Reference<css::uno::XComponentContext> const& xContext, + css::uno::Reference<css::io::XInputStream> const& xInputStream, + css::uno::Reference<css::lang::XComponent> const& xDstDoc, + css::uno::Reference<css::frame::XFrame> const& xFrame, + css::uno::Reference<css::task::XStatusIndicator> const& xStatusIndicator, + const utl::MediaDescriptor& rMediaDescriptor) +{ + return new RTFDocumentImpl(xContext, xInputStream, xDstDoc, xFrame, xStatusIndicator, + rMediaDescriptor); +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.cxx b/writerfilter/source/rtftok/rtfdocumentimpl.cxx new file mode 100644 index 000000000..4011a17d5 --- /dev/null +++ b/writerfilter/source/rtftok/rtfdocumentimpl.cxx @@ -0,0 +1,3992 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfdocumentimpl.hxx" +#include <algorithm> +#include <memory> +#include <string_view> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/io/WrongFormatException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/text/XDependentTextField.hpp> +#include <i18nlangtag/languagetag.hxx> +#include <unotools/ucbstreamhelper.hxx> +#include <unotools/streamwrap.hxx> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <filter/msfilter/util.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <comphelper/string.hxx> +#include <tools/diagnose_ex.h> +#include <tools/globname.hxx> +#include <tools/datetimeutils.hxx> +#include <comphelper/classids.hxx> +#include <comphelper/embeddedobjectcontainer.hxx> +#include <svl/lngmisc.hxx> +#include <sfx2/sfxbasemodel.hxx> +#include <sfx2/classificationhelper.hxx> +#include <oox/mathml/import.hxx> +#include <ooxml/resourceids.hxx> +#include <oox/token/namespaces.hxx> +#include <oox/drawingml/drawingmltypes.hxx> +#include <rtl/uri.hxx> +#include <rtl/tencinfo.h> +#include <sal/log.hxx> +#include <osl/diagnose.h> +#include <oox/helper/graphichelper.hxx> +#include <vcl/wmfexternal.hxx> +#include <vcl/graph.hxx> +#include <vcl/settings.hxx> +#include <vcl/svapp.hxx> +#include "rtfsdrimport.hxx" +#include "rtfreferenceproperties.hxx" +#include "rtfskipdestination.hxx" +#include "rtftokenizer.hxx" +#include "rtflookahead.hxx" +#include "rtfcharsets.hxx" + +using namespace com::sun::star; + +namespace +{ +/// Returns an util::DateTime from a 'YYYY. MM. DD.' string. +util::DateTime getDateTimeFromUserProp(const OUString& rString) +{ + util::DateTime aRet; + sal_Int32 nLen = rString.getLength(); + if (nLen >= 4) + { + aRet.Year = o3tl::toInt32(rString.subView(0, 4)); + + if (nLen >= 8 && rString.match(". ", 4)) + { + aRet.Month = o3tl::toInt32(rString.subView(6, 2)); + + if (nLen >= 12 && rString.match(". ", 8)) + aRet.Day = o3tl::toInt32(rString.subView(10, 2)); + } + } + return aRet; +} +} // anonymous namespace + +namespace writerfilter::rtftok +{ +Id getParagraphBorder(sal_uInt32 nIndex) +{ + static const Id aBorderIds[] + = { NS_ooxml::LN_CT_PBdr_top, NS_ooxml::LN_CT_PBdr_left, NS_ooxml::LN_CT_PBdr_bottom, + NS_ooxml::LN_CT_PBdr_right, NS_ooxml::LN_CT_PBdr_between }; + + return aBorderIds[nIndex]; +} + +void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, + RTFOverwrite eOverwrite, bool bAttribute) +{ + RTFValue::Pointer_t pParent = rSprms.find(nParent, /*bFirst=*/true, /*bForWrite=*/true); + if (!pParent) + { + RTFSprms aAttributes; + if (nParent == NS_ooxml::LN_CT_TcPrBase_shd) + { + // RTF default is 'auto', see writerfilter::dmapper::CellColorHandler + aAttributes.set(NS_ooxml::LN_CT_Shd_color, new RTFValue(sal_uInt32(COL_AUTO))); + aAttributes.set(NS_ooxml::LN_CT_Shd_fill, new RTFValue(sal_uInt32(COL_AUTO))); + } + auto pParentValue = new RTFValue(aAttributes); + rSprms.set(nParent, pParentValue, eOverwrite); + pParent = pParentValue; + } + RTFSprms& rAttributes = (bAttribute ? pParent->getAttributes() : pParent->getSprms()); + rAttributes.set(nId, pValue, eOverwrite); +} + +void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, + RTFOverwrite eOverwrite) +{ + putNestedAttribute(rSprms, nParent, nId, pValue, eOverwrite, false); +} + +RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId) +{ + RTFValue::Pointer_t pParent = rSprms.find(nParent); + if (!pParent) + return RTFValue::Pointer_t(); + RTFSprms& rAttributes = pParent->getAttributes(); + return rAttributes.find(nId); +} + +RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId) +{ + RTFValue::Pointer_t pParent = rSprms.find(nParent); + if (!pParent) + return RTFValue::Pointer_t(); + RTFSprms& rInner = pParent->getSprms(); + return rInner.find(nId); +} + +bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId) +{ + RTFValue::Pointer_t pParent = rSprms.find(nParent); + if (!pParent) + // It doesn't even have a parent, we're done. + return false; + RTFSprms& rAttributes = pParent->getAttributes(); + return rAttributes.erase(nId); +} + +RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId) +{ + RTFValue::Pointer_t p = rSprms.find(nId); + if (p && !p->getSprms().empty()) + return p->getSprms().back().second->getAttributes(); + + SAL_WARN("writerfilter.rtf", "trying to set property when no type is defined"); + return rSprms; +} + +void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue) +{ + RTFSprms* pAttributes = nullptr; + if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH_BOX) + for (int i = 0; i < 4; i++) + { + RTFValue::Pointer_t p = aStates.top().getParagraphSprms().find(getParagraphBorder(i)); + if (p) + { + RTFSprms& rAttributes = p->getAttributes(); + rAttributes.set(nId, pValue); + } + } + else if (aStates.top().getBorderState() == RTFBorderState::CHARACTER) + { + RTFValue::Pointer_t pPointer + = aStates.top().getCharacterSprms().find(NS_ooxml::LN_EG_RPrBase_bdr); + if (pPointer) + { + RTFSprms& rAttributes = pPointer->getAttributes(); + rAttributes.set(nId, pValue); + } + } + // Attributes of the last border type + else if (aStates.top().getBorderState() == RTFBorderState::PARAGRAPH) + pAttributes + = &getLastAttributes(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr); + else if (aStates.top().getBorderState() == RTFBorderState::CELL) + pAttributes = &getLastAttributes(aStates.top().getTableCellSprms(), + NS_ooxml::LN_CT_TcPrBase_tcBorders); + else if (aStates.top().getBorderState() == RTFBorderState::PAGE) + pAttributes = &getLastAttributes(aStates.top().getSectionSprms(), + NS_ooxml::LN_EG_SectPrContents_pgBorders); + else if (aStates.top().getBorderState() == RTFBorderState::NONE) + { + // this is invalid, but Word apparently clears or overrides all paragraph borders now + for (int i = 0; i < 4; ++i) + { + auto const nBorder = getParagraphBorder(i); + RTFSprms aAttributes; + RTFSprms aSprms; + aAttributes.set(NS_ooxml::LN_CT_Border_val, + new RTFValue(NS_ooxml::LN_Value_ST_Border_none)); + putNestedSprm(aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PrBase_pBdr, nBorder, + new RTFValue(aAttributes, aSprms), RTFOverwrite::YES); + } + } + + if (pAttributes) + pAttributes->set(nId, pValue); +} + +OString DTTM22OString(tools::Long nDTTM) +{ + return DateTimeToOString(msfilter::util::DTTM2DateTime(nDTTM)); +} + +static RTFSprms lcl_getBookmarkProperties(int nPos, const OUString& rString) +{ + RTFSprms aAttributes; + auto pPos = new RTFValue(nPos); + if (!rString.isEmpty()) + { + // If present, this should be sent first. + auto pString = new RTFValue(rString); + aAttributes.set(NS_ooxml::LN_CT_Bookmark_name, pString); + } + aAttributes.set(NS_ooxml::LN_CT_MarkupRangeBookmark_id, pPos); + return aAttributes; +} + +const char* keywordToString(RTFKeyword nKeyword) +{ + for (int i = 0; i < nRTFControlWords; i++) + { + if (nKeyword == aRTFControlWords[i].GetIndex()) + return aRTFControlWords[i].GetKeyword(); + } + return nullptr; +} + +static util::DateTime lcl_getDateTime(RTFParserState const& aState) +{ + return { 0 /*100sec*/, + 0 /*sec*/, + aState.getMinute(), + aState.getHour(), + aState.getDay(), + aState.getMonth(), + static_cast<sal_Int16>(aState.getYear()), + false }; +} + +static void lcl_DestinationToMath(OUStringBuffer* pDestinationText, + oox::formulaimport::XmlStreamBuilder& rMathBuffer, bool& rMathNor) +{ + if (!pDestinationText) + return; + OUString aStr = pDestinationText->makeStringAndClear(); + if (aStr.isEmpty()) + return; + rMathBuffer.appendOpeningTag(M_TOKEN(r)); + if (rMathNor) + { + rMathBuffer.appendOpeningTag(M_TOKEN(rPr)); + // Same as M_TOKEN(lit) + rMathBuffer.appendOpeningTag(M_TOKEN(nor)); + rMathBuffer.appendClosingTag(M_TOKEN(nor)); + rMathBuffer.appendClosingTag(M_TOKEN(rPr)); + rMathNor = false; + } + rMathBuffer.appendOpeningTag(M_TOKEN(t)); + rMathBuffer.appendCharacters(aStr); + rMathBuffer.appendClosingTag(M_TOKEN(t)); + rMathBuffer.appendClosingTag(M_TOKEN(r)); +} + +RTFDocumentImpl::RTFDocumentImpl(uno::Reference<uno::XComponentContext> const& xContext, + uno::Reference<io::XInputStream> const& xInputStream, + uno::Reference<lang::XComponent> const& xDstDoc, + uno::Reference<frame::XFrame> const& xFrame, + uno::Reference<task::XStatusIndicator> const& xStatusIndicator, + const utl::MediaDescriptor& rMediaDescriptor) + : m_xContext(xContext) + , m_xInputStream(xInputStream) + , m_xDstDoc(xDstDoc) + , m_xFrame(xFrame) + , m_xStatusIndicator(xStatusIndicator) + , m_pMapperStream(nullptr) + , m_aDefaultState(this) + , m_bSkipUnknown(false) + , m_bFirstRun(true) + , m_bFirstRunException(false) + , m_bNeedPap(true) + , m_bNeedCr(false) + , m_bNeedCrOrig(false) + , m_bNeedPar(true) + , m_bNeedFinalPar(false) + , m_nNestedCells(0) + , m_nTopLevelCells(0) + , m_nInheritingCells(0) + , m_nNestedTRLeft(0) + , m_nTopLevelTRLeft(0) + , m_nNestedCurrentCellX(0) + , m_nTopLevelCurrentCellX(0) + , m_nBackupTopLevelCurrentCellX(0) + , m_aTableBufferStack(1) // create top-level buffer already + , m_pSuperstream(nullptr) + , m_nStreamType(0) + , m_nGroupStartPos(0) + , m_nFormFieldType(RTFFormFieldType::NONE) + , m_bObject(false) + , m_nCurrentFontIndex(0) + , m_nCurrentEncoding(-1) + , m_nDefaultFontIndex(-1) + , m_pStyleTableEntries(new RTFReferenceTable::Entries_t) + , m_nCurrentStyleIndex(0) + , m_bFormField(false) + , m_bMathNor(false) + , m_bIgnoreNextContSectBreak(false) + , m_nResetBreakOnSectBreak(RTFKeyword::invalid) + , m_bNeedSect(false) // done by checkFirstRun + , m_bWasInFrame(false) + , m_bHadPicture(false) + , m_bHadSect(false) + , m_nCellxMax(0) + , m_nListPictureId(0) + , m_bIsNewDoc(!rMediaDescriptor.getUnpackedValueOrDefault("InsertMode", false)) + , m_rMediaDescriptor(rMediaDescriptor) + , m_hasRHeader(false) + , m_hasFHeader(false) + , m_hasRFooter(false) + , m_hasFFooter(false) + , m_bAfterCellBeforeRow(false) +{ + OSL_ASSERT(xInputStream.is()); + m_pInStream = utl::UcbStreamHelper::CreateStream(xInputStream, true); + + m_xModelFactory.set(m_xDstDoc, uno::UNO_QUERY); + + uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier( + m_xDstDoc, uno::UNO_QUERY); + if (xDocumentPropertiesSupplier.is()) + m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); + + m_pGraphicHelper = std::make_shared<oox::GraphicHelper>(m_xContext, xFrame, oox::StorageRef()); + + m_pTokenizer = new RTFTokenizer(*this, m_pInStream.get(), m_xStatusIndicator); + m_pSdrImport = new RTFSdrImport(*this, m_xDstDoc); +} + +RTFDocumentImpl::~RTFDocumentImpl() = default; + +SvStream& RTFDocumentImpl::Strm() { return *m_pInStream; } + +void RTFDocumentImpl::setSuperstream(RTFDocumentImpl* pSuperstream) +{ + m_pSuperstream = pSuperstream; +} + +bool RTFDocumentImpl::isSubstream() const { return m_pSuperstream != nullptr; } + +void RTFDocumentImpl::finishSubstream() { checkUnicode(/*bUnicode =*/true, /*bHex =*/true); } + +void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId) +{ + resolveSubstream(nPos, nId, OUString()); +} +void RTFDocumentImpl::resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst) +{ + sal_uInt64 const nCurrent = Strm().Tell(); + // Seek to header position, parse, then seek back. + auto pImpl = new RTFDocumentImpl(m_xContext, m_xInputStream, m_xDstDoc, m_xFrame, + m_xStatusIndicator, m_rMediaDescriptor); + pImpl->setSuperstream(this); + pImpl->m_nStreamType = nId; + pImpl->m_aIgnoreFirst = rIgnoreFirst; + if (!m_aAuthor.isEmpty()) + { + pImpl->m_aAuthor = m_aAuthor; + m_aAuthor.clear(); + } + if (!m_aAuthorInitials.isEmpty()) + { + pImpl->m_aAuthorInitials = m_aAuthorInitials; + m_aAuthorInitials.clear(); + } + pImpl->m_nDefaultFontIndex = m_nDefaultFontIndex; + pImpl->m_pStyleTableEntries = m_pStyleTableEntries; + pImpl->Strm().Seek(nPos); + SAL_INFO("writerfilter.rtf", "substream start"); + Mapper().substream(nId, pImpl); + SAL_INFO("writerfilter.rtf", "substream end"); + Strm().Seek(nCurrent); +} + +void RTFDocumentImpl::outputSettingsTable() +{ + // tdf#136740: do not change target document settings when pasting + if (!m_bIsNewDoc) + return; + writerfilter::Reference<Properties>::Pointer_t pProp + = new RTFReferenceProperties(m_aSettingsTableAttributes, m_aSettingsTableSprms); + RTFReferenceTable::Entries_t aSettingsTableEntries; + aSettingsTableEntries.insert(std::make_pair(0, pProp)); + writerfilter::Reference<Table>::Pointer_t pTable + = new RTFReferenceTable(std::move(aSettingsTableEntries)); + Mapper().table(NS_ooxml::LN_settings_settings, pTable); +} + +void RTFDocumentImpl::checkFirstRun() +{ + if (!m_bFirstRun) + return; + + outputSettingsTable(); + // start initial paragraph + m_bFirstRun = false; + assert(!m_bNeedSect || m_bFirstRunException); + setNeedSect(true); // first call that succeeds + + // set the requested default font, if there are none for each state in stack + RTFValue::Pointer_t pFont + = getNestedAttribute(m_aDefaultState.getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, + NS_ooxml::LN_CT_Fonts_ascii); + if (!pFont) + return; + + for (size_t i = 0; i < m_aStates.size(); i++) + { + RTFValue::Pointer_t pCurrentFont + = getNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, + NS_ooxml::LN_CT_Fonts_ascii); + if (!pCurrentFont) + putNestedAttribute(m_aStates[i].getCharacterSprms(), NS_ooxml::LN_EG_RPrBase_rFonts, + NS_ooxml::LN_CT_Fonts_ascii, pFont); + } +} + +void RTFDocumentImpl::setNeedPar(bool bNeedPar) { m_bNeedPar = bNeedPar; } + +void RTFDocumentImpl::setNeedSect(bool bNeedSect) +{ + if (!m_bNeedSect && bNeedSect && m_bFirstRun) + { + RTFLookahead aLookahead(Strm(), m_pTokenizer->getGroupStart()); + if (aLookahead.hasTable() && aLookahead.hasColumns()) + { + m_bFirstRunException = true; + } + } + + // ignore setting before checkFirstRun - every keyword calls setNeedSect! + // except the case of a table in a multicolumn section + if (!m_bNeedSect && bNeedSect && (!m_bFirstRun || m_bFirstRunException)) + { + if (!m_pSuperstream) // no sections in header/footer! + { + Mapper().startSectionGroup(); + } + // set flag in substream too - otherwise multiple startParagraphGroup + m_bNeedSect = bNeedSect; + Mapper().startParagraphGroup(); + setNeedPar(true); + } + else if (m_bNeedSect && !bNeedSect) + { + m_bNeedSect = bNeedSect; + } +} + +/// Copy rProps to rStyleAttributes and rStyleSprms, but in case of nested sprms, copy their children as toplevel sprms/attributes. +static void lcl_copyFlatten(RTFReferenceProperties& rProps, RTFSprms& rStyleAttributes, + RTFSprms& rStyleSprms) +{ + for (auto& rSprm : rProps.getSprms()) + { + // createStyleProperties() puts properties to rPr, but here we need a flat list. + if (rSprm.first == NS_ooxml::LN_CT_Style_rPr) + { + // rPr can have both attributes and SPRMs, copy over both types. + RTFSprms& rRPrSprms = rSprm.second->getSprms(); + for (const auto& rRPrSprm : rRPrSprms) + rStyleSprms.set(rRPrSprm.first, rRPrSprm.second); + + RTFSprms& rRPrAttributes = rSprm.second->getAttributes(); + for (const auto& rRPrAttribute : rRPrAttributes) + rStyleAttributes.set(rRPrAttribute.first, rRPrAttribute.second); + } + else + rStyleSprms.set(rSprm.first, rSprm.second); + } + + RTFSprms& rAttributes = rProps.getAttributes(); + for (const auto& rAttribute : rAttributes) + rStyleAttributes.set(rAttribute.first, rAttribute.second); +} + +writerfilter::Reference<Properties>::Pointer_t +RTFDocumentImpl::getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType) +{ + RTFSprms aSprms(rSprms); + RTFValue::Pointer_t pAbstractList; + int nAbstractListId = -1; + RTFValue::Pointer_t pNumId + = getNestedSprm(aSprms, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_numId); + if (pNumId) + { + // We have a numbering, look up the abstract list for property + // deduplication and duplication. + auto itNumId = m_aListOverrideTable.find(pNumId->getInt()); + if (itNumId != m_aListOverrideTable.end()) + { + nAbstractListId = itNumId->second; + auto itAbstract = m_aListTable.find(nAbstractListId); + if (itAbstract != m_aListTable.end()) + pAbstractList = itAbstract->second; + } + } + + if (pAbstractList) + { + auto it = m_aInvalidListTableFirstIndents.find(nAbstractListId); + if (it != m_aInvalidListTableFirstIndents.end()) + aSprms.deduplicateList(it->second); + } + + int nStyle = 0; + if (!m_aStates.empty()) + nStyle = m_aStates.top().getCurrentStyleIndex(); + auto it = m_pStyleTableEntries->find(nStyle); + if (it != m_pStyleTableEntries->end()) + { + // cloneAndDeduplicate() wants to know about only a single "style", so + // let's merge paragraph and character style properties here. + auto itChar = m_pStyleTableEntries->end(); + if (!m_aStates.empty()) + { + int nCharStyle = m_aStates.top().getCurrentCharacterStyleIndex(); + itChar = m_pStyleTableEntries->find(nCharStyle); + } + + RTFSprms aStyleSprms; + RTFSprms aStyleAttributes; + // Ensure the paragraph style is a flat list. + // Take paragraph style into account for character properties as well, + // as paragraph style may contain character properties. + RTFReferenceProperties& rProps = *static_cast<RTFReferenceProperties*>(it->second.get()); + lcl_copyFlatten(rProps, aStyleAttributes, aStyleSprms); + + if (itChar != m_pStyleTableEntries->end()) + { + // Found active character style, then update aStyleSprms/Attributes. + if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) + { + RTFReferenceProperties& rCharProps + = *static_cast<RTFReferenceProperties*>(itChar->second.get()); + lcl_copyFlatten(rCharProps, aStyleAttributes, aStyleSprms); + } + } + + // Get rid of direct formatting what is already in the style. + RTFSprms sprms(aSprms.cloneAndDeduplicate(aStyleSprms, nStyleType, true, &aSprms)); + RTFSprms attributes(rAttributes.cloneAndDeduplicate(aStyleAttributes, nStyleType, true)); + return new RTFReferenceProperties(std::move(attributes), std::move(sprms)); + } + + if (pAbstractList) + aSprms.duplicateList(pAbstractList); + writerfilter::Reference<Properties>::Pointer_t pRet + = new RTFReferenceProperties(rAttributes, std::move(aSprms)); + return pRet; +} + +void RTFDocumentImpl::checkNeedPap() +{ + if (!m_bNeedPap) + return; + + m_bNeedPap = false; // reset early, so we can avoid recursion when calling ourselves + + if (m_aStates.empty()) + return; + + if (!m_aStates.top().getCurrentBuffer()) + { + writerfilter::Reference<Properties>::Pointer_t const pParagraphProperties(getProperties( + m_aStates.top().getParagraphAttributes(), m_aStates.top().getParagraphSprms(), + NS_ooxml::LN_Value_ST_StyleType_paragraph)); + + // Writer will ignore a page break before a text frame, so guard it with empty paragraphs + bool hasBreakBeforeFrame + = m_aStates.top().getFrame().hasProperties() + && m_aStates.top().getParagraphSprms().find(NS_ooxml::LN_CT_PPrBase_pageBreakBefore); + if (hasBreakBeforeFrame) + { + dispatchSymbol(RTFKeyword::PAR); + m_bNeedPap = false; + } + Mapper().props(pParagraphProperties); + if (hasBreakBeforeFrame) + dispatchSymbol(RTFKeyword::PAR); + + if (m_aStates.top().getFrame().hasProperties()) + { + writerfilter::Reference<Properties>::Pointer_t const pFrameProperties( + new RTFReferenceProperties(RTFSprms(), m_aStates.top().getFrame().getSprms())); + Mapper().props(pFrameProperties); + } + } + else + { + auto pValue = new RTFValue(m_aStates.top().getParagraphAttributes(), + m_aStates.top().getParagraphSprms()); + bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr); + } +} + +void RTFDocumentImpl::runProps() +{ + if (!m_aStates.top().getCurrentBuffer()) + { + Reference<Properties>::Pointer_t const pProperties = getProperties( + m_aStates.top().getCharacterAttributes(), m_aStates.top().getCharacterSprms(), + NS_ooxml::LN_Value_ST_StyleType_character); + Mapper().props(pProperties); + } + else + { + auto pValue = new RTFValue(m_aStates.top().getCharacterAttributes(), + m_aStates.top().getCharacterSprms()); + bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr); + } + + // Delete the sprm, so the trackchange range will be started only once. + // OTOH set a boolean flag, so we'll know we need to end the range later. + RTFValue::Pointer_t pTrackchange + = m_aStates.top().getCharacterSprms().find(NS_ooxml::LN_trackchange); + if (pTrackchange) + { + m_aStates.top().setStartedTrackchange(true); + m_aStates.top().getCharacterSprms().erase(NS_ooxml::LN_trackchange); + } +} + +void RTFDocumentImpl::runBreak() +{ + sal_uInt8 const sBreak[] = { 0xd }; + Mapper().text(sBreak, 1); + m_bNeedCr = false; +} + +void RTFDocumentImpl::tableBreak() +{ + runBreak(); + Mapper().endParagraphGroup(); + Mapper().startParagraphGroup(); +} + +void RTFDocumentImpl::parBreak() +{ + checkFirstRun(); + checkNeedPap(); + // end previous paragraph + Mapper().startCharacterGroup(); + runBreak(); + Mapper().endCharacterGroup(); + Mapper().endParagraphGroup(); + + m_bHadPicture = false; + + // start new one + Mapper().startParagraphGroup(); +} + +void RTFDocumentImpl::sectBreak(bool bFinal) +{ + SAL_INFO("writerfilter.rtf", __func__ << ": final? " << bFinal << ", needed? " << m_bNeedSect); + bool bNeedSect = m_bNeedSect; + RTFValue::Pointer_t pBreak + = m_aStates.top().getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type); + bool bContinuous = pBreak && pBreak->getInt() == NS_ooxml::LN_Value_ST_SectionMark_continuous; + // If there is no paragraph in this section, then insert a dummy one, as required by Writer, + // unless this is the end of the doc, we had nothing since the last section break and this is not a continuous one. + // Also, when pasting, it's fine to not have any paragraph inside the document at all. + if (m_bNeedPar && (!bFinal || m_bNeedSect || bContinuous) && !isSubstream() && m_bIsNewDoc) + dispatchSymbol(RTFKeyword::PAR); + // It's allowed to not have a non-table paragraph at the end of an RTF doc, add it now if required. + if (m_bNeedFinalPar && bFinal) + { + dispatchFlag(RTFKeyword::PARD); + dispatchSymbol(RTFKeyword::PAR); + m_bNeedSect = bNeedSect; + } + while (!m_nHeaderFooterPositions.empty()) + { + std::pair<Id, std::size_t> aPair = m_nHeaderFooterPositions.front(); + m_nHeaderFooterPositions.pop(); + resolveSubstream(aPair.second, aPair.first); + } + + // Normally a section break at the end of the doc is necessary. Unless the + // last control word in the document is a section break itself. + if (!bNeedSect || !m_bHadSect) + { + // In case the last section is a continuous one, we don't need to output a section break. + if (bFinal && bContinuous) + m_aStates.top().getSectionSprms().erase(NS_ooxml::LN_EG_SectPrContents_type); + } + + // Section properties are a paragraph sprm. + auto pValue + = new RTFValue(m_aStates.top().getSectionAttributes(), m_aStates.top().getSectionSprms()); + RTFSprms aAttributes; + RTFSprms aSprms; + aSprms.set(NS_ooxml::LN_CT_PPr_sectPr, pValue); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); + + if (bFinal && !m_pSuperstream) + // This is the end of the document, not just the end of e.g. a header. + // This makes sure that dmapper can set DontBalanceTextColumns=true for this section if necessary. + Mapper().markLastSectionGroup(); + + // The trick is that we send properties of the previous section right now, which will be exactly what dmapper expects. + Mapper().props(pProperties); + Mapper().endParagraphGroup(); + + // End Section + if (!m_pSuperstream) + { + m_hasFHeader = false; + m_hasRHeader = false; + m_hasRFooter = false; + m_hasFFooter = false; + Mapper().endSectionGroup(); + } + m_bNeedPar = false; + m_bNeedSect = false; +} + +Color RTFDocumentImpl::getColorTable(sal_uInt32 nIndex) +{ + if (!m_pSuperstream) + { + if (nIndex < m_aColorTable.size()) + return m_aColorTable[nIndex]; + return 0; + } + + return m_pSuperstream->getColorTable(nIndex); +} + +rtl_TextEncoding RTFDocumentImpl::getEncoding(int nFontIndex) +{ + if (!m_pSuperstream) + { + auto it = m_aFontEncodings.find(nFontIndex); + if (it != m_aFontEncodings.end()) + // We have a font encoding associated to this font. + return it->second; + if (m_aDefaultState.getCurrentEncoding() != rtl_getTextEncodingFromWindowsCharset(0)) + // We have a default encoding. + return m_aDefaultState.getCurrentEncoding(); + // Guess based on locale. + return msfilter::util::getBestTextEncodingFromLocale( + Application::GetSettings().GetLanguageTag().getLocale()); + } + + return m_pSuperstream->getEncoding(nFontIndex); +} + +OUString RTFDocumentImpl::getFontName(int nIndex) +{ + if (!m_pSuperstream) + return m_aFontNames[nIndex]; + + return m_pSuperstream->getFontName(nIndex); +} + +int RTFDocumentImpl::getFontIndex(int nIndex) +{ + if (!m_pSuperstream) + return std::find(m_aFontIndexes.begin(), m_aFontIndexes.end(), nIndex) + - m_aFontIndexes.begin(); + + return m_pSuperstream->getFontIndex(nIndex); +} + +OUString RTFDocumentImpl::getStyleName(int nIndex) +{ + if (!m_pSuperstream) + { + OUString aRet; + if (m_aStyleNames.find(nIndex) != m_aStyleNames.end()) + aRet = m_aStyleNames[nIndex]; + return aRet; + } + + return m_pSuperstream->getStyleName(nIndex); +} + +Id RTFDocumentImpl::getStyleType(int nIndex) +{ + if (!m_pSuperstream) + { + Id nRet = 0; + if (m_aStyleTypes.find(nIndex) != m_aStyleTypes.end()) + nRet = m_aStyleTypes[nIndex]; + return nRet; + } + + return m_pSuperstream->getStyleType(nIndex); +} + +RTFParserState& RTFDocumentImpl::getDefaultState() +{ + if (!m_pSuperstream) + return m_aDefaultState; + + return m_pSuperstream->getDefaultState(); +} + +oox::GraphicHelper& RTFDocumentImpl::getGraphicHelper() { return *m_pGraphicHelper; } + +bool RTFDocumentImpl::isStyleSheetImport() +{ + if (m_aStates.empty()) + return false; + Destination eDestination = m_aStates.top().getDestination(); + return eDestination == Destination::STYLESHEET || eDestination == Destination::STYLEENTRY; +} + +void RTFDocumentImpl::resolve(Stream& rMapper) +{ + m_pMapperStream = &rMapper; + switch (m_pTokenizer->resolveParse()) + { + case RTFError::OK: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: finished without errors"); + break; + case RTFError::GROUP_UNDER: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '}'"); + break; + case RTFError::GROUP_OVER: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unmatched '{'"); + throw io::WrongFormatException(m_pTokenizer->getPosition()); + break; + case RTFError::UNEXPECTED_EOF: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: unexpected end of file"); + throw io::WrongFormatException(m_pTokenizer->getPosition()); + break; + case RTFError::HEX_INVALID: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: invalid hex char"); + throw io::WrongFormatException(m_pTokenizer->getPosition()); + break; + case RTFError::CHAR_OVER: + SAL_INFO("writerfilter.rtf", "RTFDocumentImpl::resolve: characters after last '}'"); + break; + case RTFError::CLASSIFICATION: + SAL_INFO("writerfilter.rtf", + "RTFDocumentImpl::resolve: classification prevented paste"); + break; + } +} + +void RTFDocumentImpl::resolvePict(bool const bInline, uno::Reference<drawing::XShape> const& rShape) +{ + SvMemoryStream aStream; + SvStream* pStream = nullptr; + if (!m_pBinaryData) + { + pStream = &aStream; + int b = 0; + int count = 2; + + // Feed the destination text to a stream. + OString aStr = OUStringToOString(m_aStates.top().getDestinationText().makeStringAndClear(), + RTL_TEXTENCODING_ASCII_US); + for (int i = 0; i < aStr.getLength(); ++i) + { + char ch = aStr[i]; + if (ch != 0x0d && ch != 0x0a && ch != 0x20) + { + b = b << 4; + sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); + if (parsed == -1) + return; + b += parsed; + count--; + if (!count) + { + aStream.WriteChar(static_cast<char>(b)); + count = 2; + b = 0; + } + } + } + } + else + pStream = m_pBinaryData.get(); + + if (!pStream->Tell()) + // No destination text? Then we'll get it later. + return; + + SvMemoryStream aDIBStream; + if (m_aStates.top().getPicture().eStyle == RTFBmpStyle::DIBITMAP) + { + // Construct a BITMAPFILEHEADER structure before the real data. + SvStream& rBodyStream = *pStream; + aDIBStream.WriteChar('B'); + aDIBStream.WriteChar('M'); + // The size of the real data. + aDIBStream.WriteUInt32(rBodyStream.Tell()); + // Reserved. + aDIBStream.WriteUInt32(0); + // The offset of the real data, i.e. the size of the header, including this number. + aDIBStream.WriteUInt32(14); + rBodyStream.Seek(0); + aDIBStream.WriteStream(rBodyStream); + pStream = &aDIBStream; + } + + // Store, and get its URL. + pStream->Seek(0); + uno::Reference<io::XInputStream> xInputStream(new utl::OInputStreamWrapper(pStream)); + WmfExternal aExtHeader; + aExtHeader.mapMode = m_aStates.top().getPicture().eWMetafile; + if (m_aStates.top().getPicture().nGoalWidth == 0 + || m_aStates.top().getPicture().nGoalHeight == 0) + { + // Don't use the values provided by picw and pich if the desired size is provided. + + aExtHeader.xExt = sal_uInt16(std::clamp<sal_Int32>( + m_aStates.top().getPicture().nWidth, 0, + SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values? + aExtHeader.yExt = sal_uInt16(std::clamp<sal_Int32>( + m_aStates.top().getPicture().nHeight, 0, + SAL_MAX_UINT16)); //TODO: better way to handle out-of-bounds values? + } + WmfExternal* pExtHeader = &aExtHeader; + uno::Reference<lang::XServiceInfo> xServiceInfo(m_aStates.top().getDrawingObject().getShape(), + uno::UNO_QUERY); + if (xServiceInfo.is() && xServiceInfo->supportsService("com.sun.star.text.TextFrame")) + pExtHeader = nullptr; + + uno::Reference<graphic::XGraphic> xGraphic + = m_pGraphicHelper->importGraphic(xInputStream, pExtHeader); + + if (m_aStates.top().getPicture().eStyle != RTFBmpStyle::NONE) + { + // In case of PNG/JPEG, the real size is known, don't use the values + // provided by picw and pich. + + Graphic aGraphic(xGraphic); + Size aSize(aGraphic.GetPrefSize()); + MapMode aMap(MapUnit::Map100thMM); + if (aGraphic.GetPrefMapMode().GetMapUnit() == MapUnit::MapPixel) + aSize = Application::GetDefaultDevice()->PixelToLogic(aSize, aMap); + else + aSize = OutputDevice::LogicToLogic(aSize, aGraphic.GetPrefMapMode(), aMap); + m_aStates.top().getPicture().nWidth = aSize.Width(); + m_aStates.top().getPicture().nHeight = aSize.Height(); + } + + uno::Reference<drawing::XShape> xShape(rShape); + if (m_aStates.top().getInShape() && xShape.is()) + { + awt::Size aSize = xShape->getSize(); + if (aSize.Width || aSize.Height) + { + // resolvePict() is processing pib structure inside shape + // So if shape has dimensions we should use them instead of + // \picwN, \pichN, \picscalexN, \picscaleyN given with picture + m_aStates.top().getPicture().nGoalWidth = aSize.Width; + m_aStates.top().getPicture().nGoalHeight = aSize.Height; + m_aStates.top().getPicture().nScaleX = 100; + m_aStates.top().getPicture().nScaleY = 100; + } + } + + // Wrap it in an XShape. + if (xShape.is()) + { + uno::Reference<lang::XServiceInfo> xSI(xShape, uno::UNO_QUERY_THROW); + if (!xSI->supportsService("com.sun.star.drawing.GraphicObjectShape")) + { + // it's sometimes an error to get here - but it's possible to have + // a \pict inside the \shptxt of a \shp of shapeType 202 "TextBox" + // and in that case xShape is the text frame; we actually need a + // new GraphicObject then (example: fdo37691-1.rtf) + SAL_INFO("writerfilter.rtf", + "cannot set graphic on existing shape, creating a new GraphicObjectShape"); + xShape.clear(); + } + } + if (!xShape.is()) + { + if (m_xModelFactory.is()) + xShape.set(m_xModelFactory->createInstance("com.sun.star.drawing.GraphicObjectShape"), + uno::UNO_QUERY); + uno::Reference<drawing::XDrawPageSupplier> const xDrawSupplier(m_xDstDoc, uno::UNO_QUERY); + if (xDrawSupplier.is()) + { + uno::Reference<drawing::XShapes> xShapes = xDrawSupplier->getDrawPage(); + if (xShapes.is()) + xShapes->add(xShape); + } + } + + uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); + + if (xPropertySet.is()) + xPropertySet->setPropertyValue("Graphic", uno::Any(xGraphic)); + + // check if the picture is in an OLE object and if the \objdata element is used + // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination) + if (m_bObject) + { + // Set the object size + awt::Size aSize; + aSize.Width + = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth + : m_aStates.top().getPicture().nWidth); + aSize.Height + = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight + : m_aStates.top().getPicture().nHeight); + xShape->setSize(aSize); + + // Replacement graphic is inline by default, see oox::vml::SimpleShape::implConvertAndInsert(). + xPropertySet->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AS_CHARACTER)); + + auto pShapeValue = new RTFValue(xShape); + m_aObjectAttributes.set(NS_ooxml::LN_shape, pShapeValue); + return; + } + + if (m_aStates.top().getInListpicture()) + { + // Send the shape directly, no section is started, to additional properties will be ignored anyway. + Mapper().startShape(xShape); + Mapper().endShape(); + return; + } + + // Send it to the dmapper. + RTFSprms aSprms; + RTFSprms aAttributes; + // shape attribute + RTFSprms aPicAttributes; + auto pShapeValue = new RTFValue(xShape); + aPicAttributes.set(NS_ooxml::LN_shape, pShapeValue); + // pic sprm + RTFSprms aGraphicDataAttributes; + RTFSprms aGraphicDataSprms; + auto pPicValue = new RTFValue(aPicAttributes); + aGraphicDataSprms.set(NS_ooxml::LN_pic_pic, pPicValue); + // graphicData sprm + RTFSprms aGraphicAttributes; + RTFSprms aGraphicSprms; + auto pGraphicDataValue = new RTFValue(aGraphicDataAttributes, aGraphicDataSprms); + aGraphicSprms.set(NS_ooxml::LN_CT_GraphicalObject_graphicData, pGraphicDataValue); + // graphic sprm + auto pGraphicValue = new RTFValue(aGraphicAttributes, aGraphicSprms); + // extent sprm + RTFSprms aExtentAttributes; + int nXExt = (m_aStates.top().getPicture().nGoalWidth ? m_aStates.top().getPicture().nGoalWidth + : m_aStates.top().getPicture().nWidth); + int nYExt = (m_aStates.top().getPicture().nGoalHeight ? m_aStates.top().getPicture().nGoalHeight + : m_aStates.top().getPicture().nHeight); + if (m_aStates.top().getPicture().nScaleX != 100) + nXExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleX) + * (nXExt + - (m_aStates.top().getPicture().nCropL + m_aStates.top().getPicture().nCropR))) + / 100L; + if (m_aStates.top().getPicture().nScaleY != 100) + nYExt = (static_cast<tools::Long>(m_aStates.top().getPicture().nScaleY) + * (nYExt + - (m_aStates.top().getPicture().nCropT + m_aStates.top().getPicture().nCropB))) + / 100L; + auto pXExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nXExt)); + auto pYExtValue = new RTFValue(oox::drawingml::convertHmmToEmu(nYExt)); + aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cx, pXExtValue); + aExtentAttributes.set(NS_ooxml::LN_CT_PositiveSize2D_cy, pYExtValue); + auto pExtentValue = new RTFValue(aExtentAttributes); + // docpr sprm + RTFSprms aDocprAttributes; + for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes()) + if (rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_name + || rCharacterAttribute.first == NS_ooxml::LN_CT_NonVisualDrawingProps_descr) + aDocprAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second); + auto pDocprValue = new RTFValue(aDocprAttributes); + if (bInline) + { + RTFSprms aInlineAttributes; + aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distT, new RTFValue(0)); + aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distB, new RTFValue(0)); + aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distL, new RTFValue(0)); + aInlineAttributes.set(NS_ooxml::LN_CT_Inline_distR, new RTFValue(0)); + RTFSprms aInlineSprms; + aInlineSprms.set(NS_ooxml::LN_CT_Inline_extent, pExtentValue); + aInlineSprms.set(NS_ooxml::LN_CT_Inline_docPr, pDocprValue); + aInlineSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue); + // inline sprm + auto pValue = new RTFValue(aInlineAttributes, aInlineSprms); + aSprms.set(NS_ooxml::LN_inline_inline, pValue); + } + else // anchored + { + // wrap sprm + RTFSprms aAnchorWrapAttributes; + m_aStates.top().getShape().getAnchorAttributes().set( + NS_ooxml::LN_CT_Anchor_behindDoc, + new RTFValue((m_aStates.top().getShape().getInBackground()) ? 1 : 0)); + RTFSprms aAnchorSprms; + for (const auto& rCharacterAttribute : m_aStates.top().getCharacterAttributes()) + { + if (rCharacterAttribute.first == NS_ooxml::LN_CT_WrapSquare_wrapText) + aAnchorWrapAttributes.set(rCharacterAttribute.first, rCharacterAttribute.second); + } + sal_Int32 nWrap = -1; + for (auto& rCharacterSprm : m_aStates.top().getCharacterSprms()) + { + if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone + || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight) + { + nWrap = rCharacterSprm.first; + + // If there is a wrap polygon prepared by RTFSdrImport, pick it up here. + if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight + && !m_aStates.top().getShape().getWrapPolygonSprms().empty()) + rCharacterSprm.second->getSprms().set( + NS_ooxml::LN_CT_WrapTight_wrapPolygon, + new RTFValue(RTFSprms(), m_aStates.top().getShape().getWrapPolygonSprms())); + + aAnchorSprms.set(rCharacterSprm.first, rCharacterSprm.second); + } + } + + if (m_aStates.top().getShape().getWrapSprm().first != 0) + // Replay of a buffered shape, wrap sprm there has priority over + // character sprms of the current state. + aAnchorSprms.set(m_aStates.top().getShape().getWrapSprm().first, + m_aStates.top().getShape().getWrapSprm().second); + + aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_extent, pExtentValue); + if (!aAnchorWrapAttributes.empty() && nWrap == -1) + aAnchorSprms.set(NS_ooxml::LN_EG_WrapType_wrapSquare, + new RTFValue(aAnchorWrapAttributes)); + + // See OOXMLFastContextHandler::positionOffset(), we can't just put offset values in an RTFValue. + RTFSprms aPoshAttributes; + RTFSprms aPoshSprms; + if (m_aStates.top().getShape().getHoriOrientRelationToken() > 0) + aPoshAttributes.set( + NS_ooxml::LN_CT_PosH_relativeFrom, + new RTFValue(m_aStates.top().getShape().getHoriOrientRelationToken())); + if (m_aStates.top().getShape().getLeft() != 0) + { + Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu( + m_aStates.top().getShape().getLeft())), + /*bVertical=*/false); + aPoshSprms.set(NS_ooxml::LN_CT_PosH_posOffset, new RTFValue()); + } + aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionH, + new RTFValue(aPoshAttributes, aPoshSprms)); + + RTFSprms aPosvAttributes; + RTFSprms aPosvSprms; + if (m_aStates.top().getShape().getVertOrientRelationToken() > 0) + aPosvAttributes.set( + NS_ooxml::LN_CT_PosV_relativeFrom, + new RTFValue(m_aStates.top().getShape().getVertOrientRelationToken())); + if (m_aStates.top().getShape().getTop() != 0) + { + Mapper().positionOffset(OUString::number(oox::drawingml::convertHmmToEmu( + m_aStates.top().getShape().getTop())), + /*bVertical=*/true); + aPosvSprms.set(NS_ooxml::LN_CT_PosV_posOffset, new RTFValue()); + } + aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_positionV, + new RTFValue(aPosvAttributes, aPosvSprms)); + + aAnchorSprms.set(NS_ooxml::LN_CT_Anchor_docPr, pDocprValue); + aAnchorSprms.set(NS_ooxml::LN_graphic_graphic, pGraphicValue); + // anchor sprm + auto pValue = new RTFValue(m_aStates.top().getShape().getAnchorAttributes(), aAnchorSprms); + aSprms.set(NS_ooxml::LN_anchor_anchor, pValue); + } + checkFirstRun(); + + if (!m_aStates.top().getCurrentBuffer()) + { + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes), std::move(aSprms)); + Mapper().props(pProperties); + // Make sure we don't lose these properties with a too early reset. + m_bHadPicture = true; + } + else + { + auto pValue = new RTFValue(aAttributes, aSprms); + bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue, nullptr); + } +} + +RTFError RTFDocumentImpl::resolveChars(char ch) +{ + if (m_aStates.top().getInternalState() == RTFInternalState::BIN) + { + m_pBinaryData = std::make_shared<SvMemoryStream>(); + m_pBinaryData->WriteChar(ch); + for (int i = 0; i < m_aStates.top().getBinaryToRead() - 1; ++i) + { + Strm().ReadChar(ch); + m_pBinaryData->WriteChar(ch); + } + m_aStates.top().setInternalState(RTFInternalState::NORMAL); + return RTFError::OK; + } + + OStringBuffer aBuf(512); + + bool bUnicodeChecked = false; + bool bSkipped = false; + + while (!Strm().eof() + && (m_aStates.top().getInternalState() == RTFInternalState::HEX + || (ch != '{' && ch != '}' && ch != '\\'))) + { + if (m_aStates.top().getInternalState() == RTFInternalState::HEX + || (ch != 0x0d && ch != 0x0a)) + { + if (m_aStates.top().getCharsToSkip() == 0) + { + if (!bUnicodeChecked) + { + checkUnicode(/*bUnicode =*/true, /*bHex =*/false); + bUnicodeChecked = true; + } + aBuf.append(ch); + } + else + { + bSkipped = true; + m_aStates.top().getCharsToSkip()--; + } + } + + // read a single char if we're in hex mode + if (m_aStates.top().getInternalState() == RTFInternalState::HEX) + break; + + if (RTL_TEXTENCODING_MS_932 == m_aStates.top().getCurrentEncoding()) + { + unsigned char uch = ch; + if ((uch >= 0x80 && uch <= 0x9F) || uch >= 0xE0) + { + // read second byte of 2-byte Shift-JIS - may be \ { } + Strm().ReadChar(ch); + if (m_aStates.top().getCharsToSkip() == 0) + { + // fdo#79384: Word will reject Shift-JIS following \loch + // but apparently OOo could read and (worse) write such documents + SAL_INFO_IF(m_aStates.top().getRunType() != RTFParserState::RunType::DBCH, + "writerfilter.rtf", "invalid Shift-JIS without DBCH"); + assert(bUnicodeChecked); + aBuf.append(ch); + } + else + { + assert(bSkipped); + // anybody who uses \ucN with Shift-JIS is insane + m_aStates.top().getCharsToSkip()--; + } + } + } + + Strm().ReadChar(ch); + } + if (m_aStates.top().getInternalState() != RTFInternalState::HEX && !Strm().eof()) + Strm().SeekRel(-1); + + if (m_aStates.top().getInternalState() == RTFInternalState::HEX + && m_aStates.top().getDestination() != Destination::LEVELNUMBERS) + { + if (!bSkipped) + { + // note: apparently \'0d\'0a is interpreted as 2 breaks, not 1 + if ((ch == '\r' || ch == '\n') + && m_aStates.top().getDestination() != Destination::DOCCOMM + && m_aStates.top().getDestination() != Destination::LEVELNUMBERS + && m_aStates.top().getDestination() != Destination::LEVELTEXT) + { + checkUnicode(/*bUnicode =*/false, /*bHex =*/true); + dispatchSymbol(RTFKeyword::PAR); + } + else + { + m_aHexBuffer.append(ch); + } + } + return RTFError::OK; + } + + if (m_aStates.top().getDestination() == Destination::SKIP) + return RTFError::OK; + OString aStr = aBuf.makeStringAndClear(); + if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS) + { + if (aStr.toChar() != ';') + m_aStates.top().getLevelNumbers().push_back(sal_Int32(ch)); + return RTFError::OK; + } + + SAL_INFO("writerfilter.rtf", + "RTFDocumentImpl::resolveChars: collected '" + << OStringToOUString(aStr, m_aStates.top().getCurrentEncoding()) << "'"); + + if (m_aStates.top().getDestination() == Destination::COLORTABLE) + { + // we hit a ';' at the end of each color entry + m_aColorTable.push_back(m_aStates.top().getCurrentColor().GetColor()); + // set components back to zero + m_aStates.top().getCurrentColor() = RTFColorTableEntry(); + } + else if (!aStr.isEmpty()) + m_aHexBuffer.append(aStr); + + checkUnicode(/*bUnicode =*/false, /*bHex =*/true); + return RTFError::OK; +} + +bool RTFFrame::inFrame() const { return m_nW > 0 || m_nH > 0 || m_nX > 0 || m_nY > 0; } + +void RTFDocumentImpl::singleChar(sal_uInt8 nValue, bool bRunProps) +{ + sal_uInt8 sValue[] = { nValue }; + RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); + + if (!pCurrentBuffer) + { + Mapper().startCharacterGroup(); + } + else + { + pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, nullptr, nullptr)); + } + + // Should we send run properties? + if (bRunProps) + runProps(); + + if (!pCurrentBuffer) + { + Mapper().text(sValue, 1); + Mapper().endCharacterGroup(); + } + else + { + auto pValue = new RTFValue(*sValue); + pCurrentBuffer->push_back(Buf_t(BUFFER_TEXT, pValue, nullptr)); + pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, nullptr, nullptr)); + } +} + +void RTFDocumentImpl::handleFontTableEntry() +{ + OUString aName = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + + if (aName.isEmpty()) + return; + + if (aName.endsWith(";")) + { + aName = aName.copy(0, aName.getLength() - 1); + } + + // Old documents can contain no encoding information in fontinfo, + // but there can be font name suffixes: Arial CE is not a special + // font, it is ordinal Arial, but with used cp 1250 encoding. + // Moreover these suffixes have priority over \cpgN and \fcharsetN + // in MS Word. + OUString aFontSuffix; + OUString aNameNoSuffix(aName); + sal_Int32 nLastSpace = aName.lastIndexOf(' '); + if (nLastSpace >= 0) + { + aFontSuffix = aName.copy(nLastSpace + 1); + aNameNoSuffix = aName.copy(0, nLastSpace); + sal_Int32 nEncoding = RTL_TEXTENCODING_DONTKNOW; + for (int i = 0; aRTFFontNameSuffixes[i].codepage != RTL_TEXTENCODING_DONTKNOW; i++) + { + if (aFontSuffix.equalsAscii(aRTFFontNameSuffixes[i].suffix)) + { + nEncoding = aRTFFontNameSuffixes[i].codepage; + break; + } + } + if (nEncoding > RTL_TEXTENCODING_DONTKNOW) + { + m_nCurrentEncoding = nEncoding; + m_aStates.top().setCurrentEncoding(m_nCurrentEncoding); + } + else + { + // Unknown suffix: looks like it is just a part of font name, restore it + aNameNoSuffix = aName; + } + } + + m_aFontNames[m_nCurrentFontIndex] = aNameNoSuffix; + if (m_nCurrentEncoding >= 0) + { + m_aFontEncodings[m_nCurrentFontIndex] = m_nCurrentEncoding; + m_nCurrentEncoding = -1; + } + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Font_name, + new RTFValue(aNameNoSuffix)); + + writerfilter::Reference<Properties>::Pointer_t const pProp(new RTFReferenceProperties( + m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms())); + + //See fdo#47347 initial invalid font entry properties are inserted first, + //so when we attempt to insert the correct ones, there's already an + //entry in the map for them, so the new ones aren't inserted. + auto lb = m_aFontTableEntries.lower_bound(m_nCurrentFontIndex); + if (lb != m_aFontTableEntries.end() + && !(m_aFontTableEntries.key_comp()(m_nCurrentFontIndex, lb->first))) + lb->second = pProp; + else + m_aFontTableEntries.insert(lb, std::make_pair(m_nCurrentFontIndex, pProp)); +} + +void RTFDocumentImpl::text(OUString& rString) +{ + if (rString.getLength() == 1 && m_aStates.top().getDestination() != Destination::DOCCOMM) + { + // No cheating! Tokenizer ignores bare \r and \n, their hex \'0d / \'0a form doesn't count, either. + sal_Unicode ch = rString[0]; + if (ch == 0x0d || ch == 0x0a) + return; + } + + bool bRet = true; + switch (m_aStates.top().getDestination()) + { + // Note: in stylesheet and revtbl groups are mandatory + case Destination::STYLEENTRY: + case Destination::LISTNAME: + case Destination::REVISIONENTRY: + { + // ; is the end of the entry + bool bEnd = false; + if (rString.endsWith(";")) + { + rString = rString.copy(0, rString.getLength() - 1); + bEnd = true; + } + m_aStates.top().appendDestinationText(rString); + if (bEnd) + { + // always clear, necessary in case of group-less fonttable + OUString const aName + = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + switch (m_aStates.top().getDestination()) + { + case Destination::STYLEENTRY: + { + RTFValue::Pointer_t pType + = m_aStates.top().getTableAttributes().find(NS_ooxml::LN_CT_Style_type); + if (pType) + { + // Word strips whitespace around style names. + m_aStyleNames[m_nCurrentStyleIndex] = aName.trim(); + m_aStyleTypes[m_nCurrentStyleIndex] = pType->getInt(); + auto pValue = new RTFValue(aName.trim()); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_styleId, + pValue); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_name, pValue); + + writerfilter::Reference<Properties>::Pointer_t const pProp( + createStyleProperties()); + m_pStyleTableEntries->insert( + std::make_pair(m_nCurrentStyleIndex, pProp)); + } + else + SAL_INFO("writerfilter.rtf", "no RTF style type defined, ignoring"); + break; + } + case Destination::LISTNAME: + // TODO: what can be done with a list name? + break; + case Destination::REVISIONENTRY: + m_aAuthors[m_aAuthors.size()] = aName; + break; + default: + break; + } + resetAttributes(); + resetSprms(); + } + } + break; + case Destination::DOCVAR: + { + m_aStates.top().appendDocVar(rString); + } + break; + case Destination::FONTTABLE: + case Destination::FONTENTRY: + case Destination::LEVELTEXT: + case Destination::SHAPEPROPERTYNAME: + case Destination::SHAPEPROPERTYVALUE: + case Destination::BOOKMARKEND: + case Destination::PICT: + case Destination::SHAPEPROPERTYVALUEPICT: + case Destination::FORMFIELDNAME: + case Destination::FORMFIELDLIST: + case Destination::DATAFIELD: + case Destination::AUTHOR: + case Destination::KEYWORDS: + case Destination::OPERATOR: + case Destination::COMPANY: + case Destination::COMMENT: + case Destination::OBJDATA: + case Destination::OBJCLASS: + case Destination::ANNOTATIONDATE: + case Destination::ANNOTATIONAUTHOR: + case Destination::ANNOTATIONREFERENCE: + case Destination::FALT: + case Destination::PARAGRAPHNUMBERING_TEXTAFTER: + case Destination::PARAGRAPHNUMBERING_TEXTBEFORE: + case Destination::TITLE: + case Destination::SUBJECT: + case Destination::DOCCOMM: + case Destination::ATNID: + case Destination::ANNOTATIONREFERENCESTART: + case Destination::ANNOTATIONREFERENCEEND: + case Destination::MR: + case Destination::MCHR: + case Destination::MPOS: + case Destination::MVERTJC: + case Destination::MSTRIKEH: + case Destination::MDEGHIDE: + case Destination::MBEGCHR: + case Destination::MSEPCHR: + case Destination::MENDCHR: + case Destination::MSUBHIDE: + case Destination::MSUPHIDE: + case Destination::MTYPE: + case Destination::MGROW: + case Destination::INDEXENTRY: + case Destination::TOCENTRY: + case Destination::PROPNAME: + case Destination::STATICVAL: + m_aStates.top().appendDestinationText(rString); + break; + case Destination::GENERATOR: + // don't enlarge space sequences, eg. it was saved in LibreOffice + if (!rString.startsWithIgnoreAsciiCase("Microsoft")) + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_longerSpaceSequence, + new RTFValue(0)); + break; + default: + bRet = false; + break; + } + if (bRet) + return; + + if (!m_aIgnoreFirst.isEmpty() && m_aIgnoreFirst == rString) + { + m_aIgnoreFirst.clear(); + return; + } + + // Are we in the middle of the table definition? (No cell defs yet, but we already have some cell props.) + if (m_aStates.top().getTableCellSprms().find(NS_ooxml::LN_CT_TcPrBase_vAlign) + && m_nTopLevelCells == 0) + { + m_aTableBufferStack.back().emplace_back(BUFFER_UTEXT, new RTFValue(rString), nullptr); + return; + } + + checkFirstRun(); + checkNeedPap(); + + // Don't return earlier, a bookmark start has to be in a paragraph group. + if (m_aStates.top().getDestination() == Destination::BOOKMARKSTART) + { + m_aStates.top().appendDestinationText(rString); + return; + } + + RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); + + if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE) + Mapper().startCharacterGroup(); + else if (pCurrentBuffer) + { + RTFValue::Pointer_t pValue; + pCurrentBuffer->push_back(Buf_t(BUFFER_STARTRUN, pValue, nullptr)); + } + + if (m_aStates.top().getDestination() == Destination::NORMAL + || m_aStates.top().getDestination() == Destination::FIELDRESULT + || m_aStates.top().getDestination() == Destination::SHAPETEXT) + runProps(); + + if (!pCurrentBuffer) + Mapper().utext(reinterpret_cast<sal_uInt8 const*>(rString.getStr()), rString.getLength()); + else + { + auto pValue = new RTFValue(rString); + pCurrentBuffer->push_back(Buf_t(BUFFER_UTEXT, pValue, nullptr)); + } + + m_bNeedCr = true; + + if (!pCurrentBuffer && m_aStates.top().getDestination() != Destination::FOOTNOTE) + Mapper().endCharacterGroup(); + else if (pCurrentBuffer) + { + RTFValue::Pointer_t pValue; + pCurrentBuffer->push_back(Buf_t(BUFFER_ENDRUN, pValue, nullptr)); + } +} + +void RTFDocumentImpl::prepareProperties( + RTFParserState& rState, writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties, + writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties, + writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, int const nCells, + int const nCurrentCellX) +{ + o_rpParagraphProperties + = getProperties(rState.getParagraphAttributes(), rState.getParagraphSprms(), + NS_ooxml::LN_Value_ST_StyleType_paragraph); + + if (rState.getFrame().hasProperties()) + { + o_rpFrameProperties = new RTFReferenceProperties(RTFSprms(), rState.getFrame().getSprms()); + } + + // Table width. + RTFValue::Pointer_t const pTableWidthProps + = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblW); + if (!pTableWidthProps) + { + auto pUnitValue = new RTFValue(3); + putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + NS_ooxml::LN_CT_TblWidth_type, pUnitValue); + auto pWValue = new RTFValue(nCurrentCellX); + putNestedAttribute(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblW, + NS_ooxml::LN_CT_TblWidth_w, pWValue); + } + + if (nCells > 0) + rState.getTableRowSprms().set(NS_ooxml::LN_tblRow, new RTFValue(1)); + + RTFValue::Pointer_t const pCellMar + = rState.getTableRowSprms().find(NS_ooxml::LN_CT_TblPrBase_tblCellMar); + if (!pCellMar) + { + // If no cell margins are defined, the default left/right margin is 0 in Word, but not in Writer. + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_TblWidth_type, + new RTFValue(NS_ooxml::LN_Value_ST_TblWidth_dxa)); + aAttributes.set(NS_ooxml::LN_CT_TblWidth_w, new RTFValue(0)); + putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, + NS_ooxml::LN_CT_TblCellMar_left, new RTFValue(aAttributes)); + putNestedSprm(rState.getTableRowSprms(), NS_ooxml::LN_CT_TblPrBase_tblCellMar, + NS_ooxml::LN_CT_TblCellMar_right, new RTFValue(aAttributes)); + } + + o_rpTableRowProperties + = new RTFReferenceProperties(rState.getTableRowAttributes(), rState.getTableRowSprms()); +} + +void RTFDocumentImpl::sendProperties( + writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties, + writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties, + writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties) +{ + Mapper().props(pParagraphProperties); + + if (pFrameProperties) + { + Mapper().props(pFrameProperties); + } + + Mapper().props(pTableRowProperties); + + tableBreak(); +} + +void RTFDocumentImpl::replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSrpms, + ::std::deque<RTFSprms>& rCellsAttributes, int const nCells) +{ + for (int i = 0; i < nCells; ++i) + { + replayBuffer(rBuffer, &rCellsSrpms.front(), &rCellsAttributes.front()); + rCellsSrpms.pop_front(); + rCellsAttributes.pop_front(); + } + for (Buf_t& i : rBuffer) + { + SAL_WARN_IF(BUFFER_CELLEND == std::get<0>(i), "writerfilter.rtf", "dropping table cell!"); + } + assert(rCellsSrpms.empty()); + assert(rCellsAttributes.empty()); +} + +void RTFDocumentImpl::replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* const pSprms, + RTFSprms const* const pAttributes) +{ + while (!rBuffer.empty()) + { + Buf_t aTuple(rBuffer.front()); + rBuffer.pop_front(); + if (std::get<0>(aTuple) == BUFFER_PROPS) + { + // Construct properties via getProperties() and not directly, to take care of deduplication. + writerfilter::Reference<Properties>::Pointer_t const pProp(getProperties( + std::get<1>(aTuple)->getAttributes(), std::get<1>(aTuple)->getSprms(), 0)); + Mapper().props(pProp); + } + else if (std::get<0>(aTuple) == BUFFER_NESTROW) + { + TableRowBuffer& rRowBuffer(*std::get<2>(aTuple)); + + replayRowBuffer(rRowBuffer.GetBuffer(), rRowBuffer.GetCellsSprms(), + rRowBuffer.GetCellsAttributes(), rRowBuffer.GetCells()); + + sendProperties(rRowBuffer.GetParaProperties(), rRowBuffer.GetFrameProperties(), + rRowBuffer.GetRowProperties()); + } + else if (std::get<0>(aTuple) == BUFFER_CELLEND) + { + assert(pSprms && pAttributes); + auto pValue = new RTFValue(1); + pSprms->set(NS_ooxml::LN_tblCell, pValue); + writerfilter::Reference<Properties>::Pointer_t const pTableCellProperties( + new RTFReferenceProperties(*pAttributes, *pSprms)); + Mapper().props(pTableCellProperties); + tableBreak(); + break; + } + else if (std::get<0>(aTuple) == BUFFER_STARTRUN) + Mapper().startCharacterGroup(); + else if (std::get<0>(aTuple) == BUFFER_TEXT) + { + sal_uInt8 const nValue = std::get<1>(aTuple)->getInt(); + Mapper().text(&nValue, 1); + } + else if (std::get<0>(aTuple) == BUFFER_UTEXT) + { + OUString const aString(std::get<1>(aTuple)->getString()); + Mapper().utext(reinterpret_cast<sal_uInt8 const*>(aString.getStr()), + aString.getLength()); + } + else if (std::get<0>(aTuple) == BUFFER_ENDRUN) + Mapper().endCharacterGroup(); + else if (std::get<0>(aTuple) == BUFFER_PAR) + parBreak(); + else if (std::get<0>(aTuple) == BUFFER_STARTSHAPE) + m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), false, RTFSdrImport::SHAPE); + else if (std::get<0>(aTuple) == BUFFER_RESOLVESHAPE) + { + // Make sure there is no current buffer while replaying the shape, + // otherwise it gets re-buffered. + RTFBuffer_t* pCurrentBuffer = m_aStates.top().getCurrentBuffer(); + m_aStates.top().setCurrentBuffer(nullptr); + + // Set current shape during replay, needed by e.g. wrap in + // background. + RTFShape aShape = m_aStates.top().getShape(); + m_aStates.top().getShape() = std::get<1>(aTuple)->getShape(); + + m_pSdrImport->resolve(std::get<1>(aTuple)->getShape(), true, RTFSdrImport::SHAPE); + m_aStates.top().getShape() = aShape; + m_aStates.top().setCurrentBuffer(pCurrentBuffer); + } + else if (std::get<0>(aTuple) == BUFFER_ENDSHAPE) + m_pSdrImport->close(); + else if (std::get<0>(aTuple) == BUFFER_RESOLVESUBSTREAM) + { + RTFSprms& rAttributes = std::get<1>(aTuple)->getAttributes(); + std::size_t nPos = rAttributes.find(0)->getInt(); + Id nId = rAttributes.find(1)->getInt(); + OUString aCustomMark = rAttributes.find(2)->getString(); + resolveSubstream(nPos, nId, aCustomMark); + } + else if (std::get<0>(aTuple) == BUFFER_PICTURE) + m_aStates.top().getPicture() = std::get<1>(aTuple)->getPicture(); + else if (std::get<0>(aTuple) == BUFFER_SETSTYLE) + { + if (!m_aStates.empty()) + m_aStates.top().setCurrentStyleIndex(std::get<1>(aTuple)->getInt()); + } + else + assert(false); + } +} + +bool findPropertyName(const std::vector<beans::PropertyValue>& rProperties, const OUString& rName) +{ + return std::any_of( + rProperties.begin(), rProperties.end(), + [&rName](const beans::PropertyValue& rProperty) { return rProperty.Name == rName; }); +} + +void RTFDocumentImpl::backupTableRowProperties() +{ + if (m_nTopLevelCurrentCellX) + { + m_aBackupTableRowSprms = m_aStates.top().getTableRowSprms(); + m_aBackupTableRowAttributes = m_aStates.top().getTableRowAttributes(); + m_nBackupTopLevelCurrentCellX = m_nTopLevelCurrentCellX; + } +} + +void RTFDocumentImpl::restoreTableRowProperties() +{ + m_aStates.top().getTableRowSprms() = m_aBackupTableRowSprms; + m_aStates.top().getTableRowAttributes() = m_aBackupTableRowAttributes; + m_nTopLevelCurrentCellX = m_nBackupTopLevelCurrentCellX; +} + +void RTFDocumentImpl::resetTableRowProperties() +{ + m_aStates.top().getTableRowSprms() = m_aDefaultState.getTableRowSprms(); + m_aStates.top().getTableRowSprms().set(NS_ooxml::LN_CT_TblGridBase_gridCol, new RTFValue(-1), + RTFOverwrite::NO_APPEND); + m_aStates.top().getTableRowAttributes() = m_aDefaultState.getTableRowAttributes(); + if (Destination::NESTEDTABLEPROPERTIES == m_aStates.top().getDestination()) + { + m_nNestedTRLeft = 0; + m_nNestedCurrentCellX = 0; + } + else + { + m_nTopLevelTRLeft = 0; + m_nTopLevelCurrentCellX = 0; + } +} + +RTFError RTFDocumentImpl::dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) +{ + setNeedSect(true); + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + RTFSkipDestination aSkip(*this); + int nSprm = -1; + tools::SvRef<RTFValue> pBoolValue(new RTFValue(int(!bParam || nParam != 0))); + + // Underline toggles. + switch (nKeyword) + { + case RTFKeyword::UL: + nSprm = NS_ooxml::LN_Value_ST_Underline_single; + break; + case RTFKeyword::ULDASH: + nSprm = NS_ooxml::LN_Value_ST_Underline_dash; + break; + case RTFKeyword::ULDASHD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dotDash; + break; + case RTFKeyword::ULDASHDD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dotDotDash; + break; + case RTFKeyword::ULDB: + nSprm = NS_ooxml::LN_Value_ST_Underline_double; + break; + case RTFKeyword::ULHWAVE: + nSprm = NS_ooxml::LN_Value_ST_Underline_wavyHeavy; + break; + case RTFKeyword::ULLDASH: + nSprm = NS_ooxml::LN_Value_ST_Underline_dashLong; + break; + case RTFKeyword::ULTH: + nSprm = NS_ooxml::LN_Value_ST_Underline_thick; + break; + case RTFKeyword::ULTHD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dottedHeavy; + break; + case RTFKeyword::ULTHDASH: + nSprm = NS_ooxml::LN_Value_ST_Underline_dashedHeavy; + break; + case RTFKeyword::ULTHDASHD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotHeavy; + break; + case RTFKeyword::ULTHDASHDD: + nSprm = NS_ooxml::LN_Value_ST_Underline_dashDotDotHeavy; + break; + case RTFKeyword::ULTHLDASH: + nSprm = NS_ooxml::LN_Value_ST_Underline_dashLongHeavy; + break; + case RTFKeyword::ULULDBWAVE: + nSprm = NS_ooxml::LN_Value_ST_Underline_wavyDouble; + break; + case RTFKeyword::ULWAVE: + nSprm = NS_ooxml::LN_Value_ST_Underline_wave; + break; + default: + break; + } + if (nSprm >= 0) + { + auto pValue + = new RTFValue((!bParam || nParam != 0) ? nSprm : NS_ooxml::LN_Value_ST_Underline_none); + m_aStates.top().getCharacterAttributes().set(NS_ooxml::LN_CT_Underline_val, pValue); + return RTFError::OK; + } + + // Accent characters (over dot / over comma). + switch (nKeyword) + { + case RTFKeyword::ACCNONE: + nSprm = NS_ooxml::LN_Value_ST_Em_none; + break; + case RTFKeyword::ACCDOT: + nSprm = NS_ooxml::LN_Value_ST_Em_dot; + break; + case RTFKeyword::ACCCOMMA: + nSprm = NS_ooxml::LN_Value_ST_Em_comma; + break; + case RTFKeyword::ACCCIRCLE: + nSprm = NS_ooxml::LN_Value_ST_Em_circle; + break; + case RTFKeyword::ACCUNDERDOT: + nSprm = NS_ooxml::LN_Value_ST_Em_underDot; + break; + default: + break; + } + if (nSprm >= 0) + { + auto pValue = new RTFValue((!bParam || nParam != 0) ? nSprm : 0); + m_aStates.top().getCharacterSprms().set(NS_ooxml::LN_EG_RPrBase_em, pValue); + return RTFError::OK; + } + + // Trivial character sprms. + switch (nKeyword) + { + case RTFKeyword::B: + case RTFKeyword::AB: + switch (m_aStates.top().getRunType()) + { + case RTFParserState::RunType::HICH: + case RTFParserState::RunType::RTLCH_LTRCH_1: + case RTFParserState::RunType::LTRCH_RTLCH_2: + nSprm = NS_ooxml::LN_EG_RPrBase_bCs; + break; + case RTFParserState::RunType::NONE: + case RTFParserState::RunType::LOCH: + case RTFParserState::RunType::LTRCH_RTLCH_1: + case RTFParserState::RunType::RTLCH_LTRCH_2: + case RTFParserState::RunType::DBCH: + default: + nSprm = NS_ooxml::LN_EG_RPrBase_b; + break; + } + break; + case RTFKeyword::I: + case RTFKeyword::AI: + switch (m_aStates.top().getRunType()) + { + case RTFParserState::RunType::HICH: + case RTFParserState::RunType::RTLCH_LTRCH_1: + case RTFParserState::RunType::LTRCH_RTLCH_2: + nSprm = NS_ooxml::LN_EG_RPrBase_iCs; + break; + case RTFParserState::RunType::NONE: + case RTFParserState::RunType::LOCH: + case RTFParserState::RunType::LTRCH_RTLCH_1: + case RTFParserState::RunType::RTLCH_LTRCH_2: + case RTFParserState::RunType::DBCH: + default: + nSprm = NS_ooxml::LN_EG_RPrBase_i; + break; + } + break; + case RTFKeyword::OUTL: + nSprm = NS_ooxml::LN_EG_RPrBase_outline; + break; + case RTFKeyword::SHAD: + nSprm = NS_ooxml::LN_EG_RPrBase_shadow; + break; + case RTFKeyword::V: + nSprm = NS_ooxml::LN_EG_RPrBase_vanish; + break; + case RTFKeyword::STRIKE: + nSprm = NS_ooxml::LN_EG_RPrBase_strike; + break; + case RTFKeyword::STRIKED: + nSprm = NS_ooxml::LN_EG_RPrBase_dstrike; + break; + case RTFKeyword::SCAPS: + nSprm = NS_ooxml::LN_EG_RPrBase_smallCaps; + break; + case RTFKeyword::IMPR: + nSprm = NS_ooxml::LN_EG_RPrBase_imprint; + break; + case RTFKeyword::CAPS: + nSprm = NS_ooxml::LN_EG_RPrBase_caps; + break; + default: + break; + } + if (nSprm >= 0) + { + if (m_aStates.top().getDestination() == Destination::LISTLEVEL) + { + m_aStates.top().getTableSprms().set(nSprm, pBoolValue); + } + else + { + m_aStates.top().getCharacterSprms().set(nSprm, pBoolValue); + } + return RTFError::OK; + } + + switch (nKeyword) + { + case RTFKeyword::ASPALPHA: + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_autoSpaceDE, + pBoolValue); + break; + case RTFKeyword::DELETED: + case RTFKeyword::REVISED: + { + auto pValue + = new RTFValue(nKeyword == RTFKeyword::DELETED ? oox::XML_del : oox::XML_ins); + putNestedAttribute(m_aStates.top().getCharacterSprms(), NS_ooxml::LN_trackchange, + NS_ooxml::LN_token, pValue); + } + break; + case RTFKeyword::SBAUTO: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, + NS_ooxml::LN_CT_Spacing_beforeAutospacing, pBoolValue); + break; + case RTFKeyword::SAAUTO: + putNestedAttribute(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_spacing, + NS_ooxml::LN_CT_Spacing_afterAutospacing, pBoolValue); + break; + case RTFKeyword::FACINGP: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_evenAndOddHeaders, pBoolValue); + break; + case RTFKeyword::HYPHAUTO: + m_aSettingsTableSprms.set(NS_ooxml::LN_CT_Settings_autoHyphenation, pBoolValue); + break; + case RTFKeyword::HYPHPAR: + m_aStates.top().getParagraphSprms().set(NS_ooxml::LN_CT_PPrBase_suppressAutoHyphens, + new RTFValue(int(bParam && nParam == 0))); + break; + default: + { + SAL_INFO("writerfilter.rtf", + "TODO handle toggle '" << keywordToString(nKeyword) << "'"); + aSkip.setParsed(false); + } + break; + } + return RTFError::OK; +} + +RTFError RTFDocumentImpl::pushState() +{ + //SAL_INFO("writerfilter.rtf", __func__ << " before push: " << m_pTokenizer->getGroup()); + + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + m_nGroupStartPos = Strm().Tell(); + + if (m_aStates.empty()) + m_aStates.push(m_aDefaultState); + else + { + // fdo#85812 group resets run type of _current_ and new state (but not RTL) + if (m_aStates.top().getRunType() != RTFParserState::RunType::LTRCH_RTLCH_2 + && m_aStates.top().getRunType() != RTFParserState::RunType::RTLCH_LTRCH_2) + { + m_aStates.top().setRunType(RTFParserState::RunType::NONE); + } + + if (m_aStates.top().getDestination() == Destination::MR) + lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer, + m_bMathNor); + m_aStates.push(m_aStates.top()); + } + m_aStates.top().getDestinationText().setLength(0); // was copied: always reset! + + m_pTokenizer->pushGroup(); + + switch (m_aStates.top().getDestination()) + { + case Destination::FONTTABLE: + // this is a "faked" destination for the font entry + m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); + m_aStates.top().setDestination(Destination::FONTENTRY); + break; + case Destination::STYLESHEET: + // this is a "faked" destination for the style sheet entry + m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); + m_aStates.top().setDestination(Destination::STYLEENTRY); + { + // the *default* is \s0 i.e. paragraph style default + // this will be overwritten by \sN \csN \dsN \tsN + m_nCurrentStyleIndex = 0; + auto pValue = new RTFValue(NS_ooxml::LN_Value_ST_StyleType_paragraph); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_Style_type, pValue); + } + break; + case Destination::FIELDRESULT: + case Destination::SHAPETEXT: + case Destination::FORMFIELD: + case Destination::FIELDINSTRUCTION: + case Destination::PICT: + m_aStates.top().setDestination(Destination::NORMAL); + break; + case Destination::MNUM: + case Destination::MDEN: + case Destination::ME: + case Destination::MFNAME: + case Destination::MLIM: + case Destination::MSUB: + case Destination::MSUP: + case Destination::MDEG: + case Destination::MOMATH: + m_aStates.top().setDestination(Destination::MR); + break; + case Destination::REVISIONTABLE: + // this is a "faked" destination for the revision table entry + m_aStates.top().setCurrentDestinationText(&m_aStates.top().getDestinationText()); + m_aStates.top().setDestination(Destination::REVISIONENTRY); + break; + default: + break; + } + + // If this is true, then ooxml:endtrackchange will be generated. Make sure + // we don't generate more ooxml:endtrackchange than ooxml:trackchange: new + // state does not inherit this flag. + m_aStates.top().setStartedTrackchange(false); + + return RTFError::OK; +} + +writerfilter::Reference<Properties>::Pointer_t RTFDocumentImpl::createStyleProperties() +{ + int nBasedOn = 0; + RTFValue::Pointer_t pBasedOn + = m_aStates.top().getTableSprms().find(NS_ooxml::LN_CT_Style_basedOn); + if (pBasedOn) + nBasedOn = pBasedOn->getInt(); + if (nBasedOn == 0) + { + // No parent style, then mimic what Word does: ignore attributes which + // would set a margin as formatting, but with a default value. + for (const auto& nId : + { NS_ooxml::LN_CT_Ind_firstLine, NS_ooxml::LN_CT_Ind_left, NS_ooxml::LN_CT_Ind_right, + NS_ooxml::LN_CT_Ind_start, NS_ooxml::LN_CT_Ind_end }) + { + RTFValue::Pointer_t pValue = getNestedAttribute(m_aStates.top().getParagraphSprms(), + NS_ooxml::LN_CT_PPrBase_ind, nId); + if (pValue && pValue->getInt() == 0) + eraseNestedAttribute(m_aStates.top().getParagraphSprms(), + NS_ooxml::LN_CT_PPrBase_ind, nId); + } + } + + RTFValue::Pointer_t pParaProps = new RTFValue(m_aStates.top().getParagraphAttributes(), + m_aStates.top().getParagraphSprms()); + RTFValue::Pointer_t pCharProps = new RTFValue(m_aStates.top().getCharacterAttributes(), + m_aStates.top().getCharacterSprms()); + + // resetSprms will clean up this modification + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_pPr, pParaProps); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Style_rPr, pCharProps); + + writerfilter::Reference<Properties>::Pointer_t pProps(new RTFReferenceProperties( + m_aStates.top().getTableAttributes(), m_aStates.top().getTableSprms())); + return pProps; +} + +/** 2 different representations of the styles are needed: + + 1) flat content, as read from the input file: + stored in m_pStyleTableEntries, used as reference input for + deduplication both here and for hard formatting in getProperties() + + 2) real content, with proper override of sprms/attributes where it differs + from parent style; this is produced here and sent to domain mapper + */ +RTFReferenceTable::Entries_t RTFDocumentImpl::deduplicateStyleTable() +{ + RTFReferenceTable::Entries_t ret; + for (auto const& it : *m_pStyleTableEntries) + { + auto pStyle = it.second; + ret[it.first] = pStyle; + // ugly downcasts here, but can't easily replace the members with + // RTFReferenceProperties because dmapper wants SvRef<Properties> anyway + RTFValue::Pointer_t const pBasedOn( + static_cast<RTFReferenceProperties&>(*pStyle).getSprms().find( + NS_ooxml::LN_CT_Style_basedOn)); + if (pBasedOn) + { + int const nBasedOn(pBasedOn->getInt()); + // don't deduplicate yourself - especially a potential problem for the default style. + if (it.first == nBasedOn) + continue; + + auto const itParent(m_pStyleTableEntries->find(nBasedOn)); // definition as read! + if (itParent != m_pStyleTableEntries->end()) + { + auto const pStyleType( + static_cast<RTFReferenceProperties&>(*pStyle).getAttributes().find( + NS_ooxml::LN_CT_Style_type)); + assert(pStyleType); + int const nStyleType(pStyleType->getInt()); + RTFSprms sprms( + static_cast<RTFReferenceProperties&>(*pStyle).getSprms().cloneAndDeduplicate( + static_cast<RTFReferenceProperties&>(*itParent->second).getSprms(), + nStyleType)); + RTFSprms attributes( + static_cast<RTFReferenceProperties&>(*pStyle) + .getAttributes() + .cloneAndDeduplicate( + static_cast<RTFReferenceProperties&>(*itParent->second).getAttributes(), + nStyleType)); + + ret[it.first] = new RTFReferenceProperties(std::move(attributes), std::move(sprms)); + } + else + { + SAL_WARN("writerfilter.rtf", "parent style not found: " << nBasedOn); + } + } + } + assert(ret.size() == m_pStyleTableEntries->size()); + return ret; +} + +void RTFDocumentImpl::resetSprms() +{ + m_aStates.top().getTableSprms().clear(); + m_aStates.top().getCharacterSprms().clear(); + m_aStates.top().getParagraphSprms().clear(); +} + +void RTFDocumentImpl::resetAttributes() +{ + m_aStates.top().getTableAttributes().clear(); + m_aStates.top().getCharacterAttributes().clear(); + m_aStates.top().getParagraphAttributes().clear(); +} + +static bool lcl_containsProperty(const uno::Sequence<beans::Property>& rProperties, + std::u16string_view rName) +{ + return std::any_of(rProperties.begin(), rProperties.end(), + [&](const beans::Property& rProperty) { return rProperty.Name == rName; }); +} + +RTFError RTFDocumentImpl::beforePopState(RTFParserState& rState) +{ + switch (rState.getDestination()) + { + //Note: in fonttbl there may or may not be groups, so process it as no groups + case Destination::FONTTABLE: + case Destination::FONTENTRY: + { + // Some text unhandled? Seems it is last font name + if (m_aStates.top().getCurrentDestinationText()->getLength()) + handleFontTableEntry(); + + if (rState.getDestination() == Destination::FONTTABLE) + { + writerfilter::Reference<Table>::Pointer_t const pTable( + new RTFReferenceTable(m_aFontTableEntries)); + Mapper().table(NS_ooxml::LN_FONTTABLE, pTable); + if (m_nDefaultFontIndex >= 0) + { + auto pValue = new RTFValue(m_aFontNames[getFontIndex(m_nDefaultFontIndex)]); + putNestedAttribute(m_aDefaultState.getCharacterSprms(), + NS_ooxml::LN_EG_RPrBase_rFonts, NS_ooxml::LN_CT_Fonts_ascii, + pValue); + } + } + } + break; + case Destination::STYLESHEET: + { + RTFReferenceTable::Entries_t pStyleTableDeduplicated(deduplicateStyleTable()); + writerfilter::Reference<Table>::Pointer_t const pTable( + new RTFReferenceTable(std::move(pStyleTableDeduplicated))); + Mapper().table(NS_ooxml::LN_STYLESHEET, pTable); + } + break; + case Destination::LISTOVERRIDETABLE: + { + RTFSprms aListTableAttributes; + writerfilter::Reference<Properties>::Pointer_t pProp + = new RTFReferenceProperties(std::move(aListTableAttributes), m_aListTableSprms); + RTFReferenceTable::Entries_t aListTableEntries; + aListTableEntries.insert(std::make_pair(0, pProp)); + writerfilter::Reference<Table>::Pointer_t const pTable( + new RTFReferenceTable(std::move(aListTableEntries))); + Mapper().table(NS_ooxml::LN_NUMBERING, pTable); + } + break; + case Destination::LISTENTRY: + for (const auto& rListLevelEntry : rState.getListLevelEntries()) + rState.getTableSprms().set(rListLevelEntry.first, rListLevelEntry.second, + RTFOverwrite::NO_APPEND); + break; + case Destination::FIELDINSTRUCTION: + { + auto pValue = new RTFValue(m_aFormfieldAttributes, m_aFormfieldSprms); + RTFSprms aFFAttributes; + RTFSprms aFFSprms; + aFFSprms.set(NS_ooxml::LN_ffdata, pValue); + if (!m_aStates.top().getCurrentBuffer()) + { + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aFFAttributes), std::move(aFFSprms)); + Mapper().props(pProperties); + } + else + { + auto pFFValue = new RTFValue(aFFAttributes, aFFSprms); + bufferProperties(*m_aStates.top().getCurrentBuffer(), pFFValue, nullptr); + } + m_aFormfieldAttributes.clear(); + m_aFormfieldSprms.clear(); + + if (m_aStates.top().isFieldLocked()) + singleChar(cFieldLock); + singleChar(cFieldSep); + } + break; + case Destination::FIELDRESULT: + singleChar(cFieldEnd); + + if (!m_aPicturePath.isEmpty()) + { + // Read the picture into m_aStates.top().aDestinationText. + pushState(); + dispatchDestination(RTFKeyword::PICT); + if (m_aPicturePath.endsWith(".png")) + dispatchFlag(RTFKeyword::PNGBLIP); + OUString aFileURL = m_rMediaDescriptor.getUnpackedValueOrDefault( + utl::MediaDescriptor::PROP_URL, OUString()); + OUString aPictureURL; + try + { + aPictureURL = rtl::Uri::convertRelToAbs(aFileURL, m_aPicturePath); + } + catch (const rtl::MalformedUriException& rException) + { + SAL_WARN("writerfilter.rtf", + "rtl::Uri::convertRelToAbs() failed: " << rException.getMessage()); + } + + if (!aPictureURL.isEmpty()) + { + SvFileStream aStream(aPictureURL, StreamMode::READ); + if (aStream.IsOpen()) + { + OUStringBuffer aBuf; + while (aStream.good()) + { + unsigned char ch = 0; + aStream.ReadUChar(ch); + if (ch < 16) + aBuf.append("0"); + aBuf.append(static_cast<sal_Int32>(ch), 16); + } + m_aStates.top().getDestinationText() = aBuf; + } + } + popState(); + m_aPicturePath.clear(); + } + + break; + case Destination::LEVELTEXT: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + + // The first character is the length of the string (the rest should be ignored). + sal_Int32 nLength(aStr.toChar()); + OUString aValue; + if (nLength < aStr.getLength()) + aValue = aStr.copy(1, nLength); + else + aValue = aStr; + auto pValue = new RTFValue(aValue, true); + rState.getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue); + } + break; + case Destination::LEVELNUMBERS: + { + bool bNestedLevelNumbers = false; + if (m_aStates.size() > 1) + // Current destination is levelnumbers and parent destination is levelnumbers as well. + bNestedLevelNumbers + = m_aStates[m_aStates.size() - 2].getDestination() == Destination::LEVELNUMBERS; + if (!bNestedLevelNumbers && rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)) + { + RTFSprms& rAttributes + = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_lvlText)->getAttributes(); + RTFValue::Pointer_t pValue = rAttributes.find(NS_ooxml::LN_CT_LevelText_val); + if (pValue && rState.getLevelNumbersValid()) + { + OUString aOrig = pValue->getString(); + + OUStringBuffer aBuf(aOrig.getLength() * 2); + sal_Int32 nReplaces = 1; + for (int i = 0; i < aOrig.getLength(); i++) + { + if (std::find(rState.getLevelNumbers().begin(), + rState.getLevelNumbers().end(), i + 1) + != rState.getLevelNumbers().end()) + { + aBuf.append('%'); + // '1.1.1' -> '%1.%2.%3', but '1.' (with '2.' prefix omitted) is %2. + aBuf.append(sal_Int32(nReplaces++ + rState.getListLevelNum() + 1 + - rState.getLevelNumbers().size())); + } + else + aBuf.append(aOrig[i]); + } + + pValue->setString(aBuf.makeStringAndClear()); + } + else if (pValue) + // Have a value, but levelnumbers is not valid -> ignore it. + pValue->setString(OUString()); + } + break; + } + case Destination::SHAPEPROPERTYNAME: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + rState.getShape().getProperties().emplace_back( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), OUString()); + break; + case Destination::SHAPEPROPERTYVALUE: + if (!rState.getShape().getProperties().empty()) + { + rState.getShape().getProperties().back().second + = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + if (m_aStates.top().getHadShapeText()) + m_pSdrImport->append(rState.getShape().getProperties().back().first, + rState.getShape().getProperties().back().second); + else if (rState.getInShapeGroup() && !rState.getInShape() + && rState.getShape().getProperties().back().first == "rotation") + { + // Rotation should be applied on the groupshape itself, not on each shape. + rState.getShape().getGroupProperties().push_back( + rState.getShape().getProperties().back()); + rState.getShape().getProperties().pop_back(); + } + } + break; + case Destination::PICPROP: + case Destination::SHAPEINSTRUCTION: + if (m_aStates.size() > 1 + && m_aStates[m_aStates.size() - 2].getDestination() + == Destination::SHAPEINSTRUCTION) + { + // Do not resolve shape if shape instruction destination is inside other shape instruction + } + else if (!m_bObject && !rState.getInListpicture() && !rState.getHadShapeText() + && (!rState.getInShapeGroup() || rState.getInShape())) + { + // Don't trigger a shape import in case we're only leaving the \shpinst of the groupshape itself. + RTFSdrImport::ShapeOrPict eType + = (rState.getDestination() == Destination::SHAPEINSTRUCTION) + ? RTFSdrImport::SHAPE + : RTFSdrImport::PICT; + if (!m_aStates.top().getCurrentBuffer() || eType != RTFSdrImport::SHAPE) + m_pSdrImport->resolve(m_aStates.top().getShape(), true, eType); + else + { + // Shape inside table: buffer the import to have correct anchor position. + // Also buffer the RTFPicture of the state stack as it contains + // the shape size. + auto pPictureValue = new RTFValue(m_aStates.top().getPicture()); + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_PICTURE, pPictureValue, nullptr)); + auto pValue = new RTFValue(m_aStates.top().getShape()); + + // Buffer wrap type. + for (const auto& rCharacterSprm : m_aStates.top().getCharacterSprms()) + { + if (rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapNone + || rCharacterSprm.first == NS_ooxml::LN_EG_WrapType_wrapTight) + { + m_aStates.top().getShape().getWrapSprm() = rCharacterSprm; + break; + } + } + + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_RESOLVESHAPE, pValue, nullptr)); + } + } + else if (rState.getInShapeGroup() && !rState.getInShape()) + { + // End of a groupshape, as we're in shapegroup, but not in a real shape. + for (const auto& rGroupProperty : rState.getShape().getGroupProperties()) + m_pSdrImport->appendGroupProperty(rGroupProperty.first, rGroupProperty.second); + rState.getShape().getGroupProperties().clear(); + } + break; + case Destination::BOOKMARKSTART: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + int nPos = m_aBookmarks.size(); + m_aBookmarks[aStr] = nPos; + if (!m_aStates.top().getCurrentBuffer()) + Mapper().props(new RTFReferenceProperties(lcl_getBookmarkProperties(nPos, aStr))); + else + bufferProperties(*m_aStates.top().getCurrentBuffer(), + new RTFValue(lcl_getBookmarkProperties(nPos, aStr)), nullptr); + } + break; + case Destination::BOOKMARKEND: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + if (!m_aStates.top().getCurrentBuffer()) + Mapper().props(new RTFReferenceProperties( + lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr))); + else + bufferProperties(*m_aStates.top().getCurrentBuffer(), + new RTFValue(lcl_getBookmarkProperties(m_aBookmarks[aStr], aStr)), + nullptr); + } + break; + case Destination::INDEXENTRY: + case Destination::TOCENTRY: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString str(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + // dmapper expects this as a field, so let's fake something... + auto const field((Destination::INDEXENTRY == rState.getDestination()) + ? std::u16string_view(u"XE") + : std::u16string_view(u"TC")); + str = OUString::Concat(field) + " \"" + str.replaceAll("\"", "\\\"") + "\""; + singleChar(cFieldStart); + Mapper().utext(reinterpret_cast<sal_uInt8 const*>(str.getStr()), str.getLength()); + singleChar(cFieldSep); + // no result + singleChar(cFieldEnd); + } + break; + case Destination::FORMFIELDNAME: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + auto pValue + = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pValue); + } + break; + case Destination::FORMFIELDLIST: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + auto pValue + = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + // OOXML puts these into a LN_CT_FFData_ddList but FFDataHandler should handle this too + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFDDList_listEntry, pValue, + RTFOverwrite::NO_APPEND); + } + break; + case Destination::DATAFIELD: + { + if (m_bFormField) + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OString aStr = OUStringToOString( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), + rState.getCurrentEncoding()); + // decode hex dump + OStringBuffer aBuf; + int b = 0; + int count = 2; + for (int i = 0; i < aStr.getLength(); ++i) + { + char ch = aStr[i]; + if (ch != 0x0d && ch != 0x0a) + { + b = b << 4; + sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); + if (parsed == -1) + return RTFError::HEX_INVALID; + b += parsed; + count--; + if (!count) + { + aBuf.append(static_cast<char>(b)); + count = 2; + b = 0; + } + } + } + aStr = aBuf.makeStringAndClear(); + + // ignore the first bytes + if (aStr.getLength() > 8) + aStr = aStr.copy(8); + // extract name + sal_Int32 nLength = aStr.toChar(); + if (!aStr.isEmpty()) + aStr = aStr.copy(1); + nLength = std::min(nLength, aStr.getLength()); + OString aName = aStr.copy(0, nLength); + if (aStr.getLength() > nLength) + aStr = aStr.copy(nLength + 1); // zero-terminated string + else + aStr.clear(); + // extract default text + nLength = aStr.toChar(); + if (!aStr.isEmpty()) + aStr = aStr.copy(1); + auto pNValue = new RTFValue(OStringToOUString(aName, rState.getCurrentEncoding())); + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFData_name, pNValue); + if (nLength > 0) + { + OString aDefaultText = aStr.copy(0, std::min(nLength, aStr.getLength())); + auto pDValue = new RTFValue( + OStringToOUString(aDefaultText, rState.getCurrentEncoding())); + m_aFormfieldSprms.set(NS_ooxml::LN_CT_FFTextInput_default, pDValue); + } + + m_bFormField = false; + } + } + break; + case Destination::CREATIONTIME: + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setCreationDate(lcl_getDateTime(rState)); + break; + case Destination::REVISIONTIME: + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setModificationDate(lcl_getDateTime(rState)); + break; + case Destination::PRINTTIME: + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setPrintDate(lcl_getDateTime(rState)); + break; + case Destination::AUTHOR: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setAuthor( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + break; + case Destination::KEYWORDS: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setKeywords(comphelper::string::convertCommaSeparated( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear())); + break; + case Destination::COMMENT: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setGenerator( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + break; + case Destination::SUBJECT: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setSubject( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + break; + case Destination::TITLE: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setTitle( + rState.getCurrentDestinationText()->makeStringAndClear()); + } + break; + + case Destination::DOCCOMM: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + m_xDocumentProperties->setDescription( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + break; + case Destination::OPERATOR: + case Destination::COMPANY: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aName = rState.getDestination() == Destination::OPERATOR ? OUString("Operator") + : OUString("Company"); + uno::Any aValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + if (m_xDocumentProperties.is()) + { + uno::Reference<beans::XPropertyContainer> xUserDefinedProperties + = m_xDocumentProperties->getUserDefinedProperties(); + uno::Reference<beans::XPropertySet> xPropertySet(xUserDefinedProperties, + uno::UNO_QUERY); + uno::Reference<beans::XPropertySetInfo> xPropertySetInfo + = xPropertySet->getPropertySetInfo(); + if (xPropertySetInfo->hasPropertyByName(aName)) + xPropertySet->setPropertyValue(aName, aValue); + else + xUserDefinedProperties->addProperty(aName, beans::PropertyAttribute::REMOVABLE, + aValue); + } + } + break; + case Destination::OBJDATA: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + + RTFError eError = handleEmbeddedObject(); + if (eError != RTFError::OK) + return eError; + } + break; + case Destination::OBJCLASS: + { + auto pValue + = new RTFValue(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + m_aOLEAttributes.set(NS_ooxml::LN_CT_OLEObject_ProgID, pValue); + break; + } + case Destination::OBJECT: + { + if (!m_bObject) + { + // if the object is in a special container we will use the \result + // element instead of the \objdata + // (see RTFKeyword::OBJECT in RTFDocumentImpl::dispatchDestination) + break; + } + + RTFSprms aObjectSprms; + auto pOLEValue = new RTFValue(m_aOLEAttributes); + aObjectSprms.set(NS_ooxml::LN_OLEObject_OLEObject, pOLEValue); + + RTFSprms aObjAttributes; + RTFSprms aObjSprms; + auto pValue = new RTFValue(m_aObjectAttributes, aObjectSprms); + aObjSprms.set(NS_ooxml::LN_object, pValue); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aObjAttributes), std::move(aObjSprms)); + uno::Reference<drawing::XShape> xShape; + RTFValue::Pointer_t pShape = m_aObjectAttributes.find(NS_ooxml::LN_shape); + OSL_ASSERT(pShape); + if (pShape) + pShape->getAny() >>= xShape; + if (xShape.is()) + { + Mapper().startShape(xShape); + Mapper().props(pProperties); + Mapper().endShape(); + } + m_aObjectAttributes.clear(); + m_aOLEAttributes.clear(); + m_bObject = false; + } + break; + case Destination::ANNOTATIONDATE: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr(OStringToOUString( + DTTM22OString( + m_aStates.top().getCurrentDestinationText()->makeStringAndClear().toInt32()), + rState.getCurrentEncoding())); + auto pValue = new RTFValue(aStr); + RTFSprms aAnnAttributes; + aAnnAttributes.set(NS_ooxml::LN_CT_TrackChange_date, pValue); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAnnAttributes)); + Mapper().props(pProperties); + } + break; + case Destination::ANNOTATIONAUTHOR: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + m_aAuthor = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + break; + case Destination::ATNID: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + m_aAuthorInitials = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + break; + case Destination::ANNOTATIONREFERENCESTART: + case Destination::ANNOTATIONREFERENCEEND: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + auto pValue = new RTFValue(aStr.toInt32()); + RTFSprms aAttributes; + if (rState.getDestination() == Destination::ANNOTATIONREFERENCESTART) + aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeStart, pValue); + else + aAttributes.set(NS_ooxml::LN_EG_RangeMarkupElements_commentRangeEnd, pValue); + if (!m_aStates.top().getCurrentBuffer()) + { + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aAttributes)); + Mapper().props(pProperties); + } + else + { + auto const pValue2 = new RTFValue(aAttributes, RTFSprms()); + bufferProperties(*m_aStates.top().getCurrentBuffer(), pValue2, nullptr); + } + } + break; + case Destination::ANNOTATIONREFERENCE: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + RTFSprms aAnnAttributes; + aAnnAttributes.set(NS_ooxml::LN_CT_Markup_id, new RTFValue(aStr.toInt32())); + Mapper().props(new RTFReferenceProperties(std::move(aAnnAttributes))); + } + break; + case Destination::FALT: + { + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + OUString aStr(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + auto pValue = new RTFValue(aStr); + rState.getTableSprms().set(NS_ooxml::LN_CT_Font_altName, pValue); + } + break; + case Destination::DRAWINGOBJECT: + if (m_aStates.top().getDrawingObject().getShape().is()) + { + RTFDrawingObject& rDrawing = m_aStates.top().getDrawingObject(); + const uno::Reference<drawing::XShape>& xShape(rDrawing.getShape()); + const uno::Reference<beans::XPropertySet>& xPropertySet(rDrawing.getPropertySet()); + + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY); + bool bTextFrame = xServiceInfo->supportsService("com.sun.star.text.TextFrame"); + + // The default is certainly not inline, but then what Word supports is just at-character. + xPropertySet->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AT_CHARACTER)); + + if (bTextFrame) + { + xPropertySet->setPropertyValue("HoriOrientPosition", + uno::Any(rDrawing.getLeft())); + xPropertySet->setPropertyValue("VertOrientPosition", + uno::Any(rDrawing.getTop())); + } + else + { + xShape->setPosition(awt::Point(rDrawing.getLeft(), rDrawing.getTop())); + } + xShape->setSize(awt::Size(rDrawing.getRight(), rDrawing.getBottom())); + + if (rDrawing.getHasLineColor()) + { + uno::Any aLineColor(sal_uInt32((rDrawing.getLineColorR() << 16) + + (rDrawing.getLineColorG() << 8) + + rDrawing.getLineColorB())); + uno::Any aLineWidth; + RTFSdrImport::resolveLineColorAndWidth(bTextFrame, xPropertySet, aLineColor, + aLineWidth); + } + if (rDrawing.getHasFillColor()) + xPropertySet->setPropertyValue( + "FillColor", uno::Any(sal_uInt32((rDrawing.getFillColorR() << 16) + + (rDrawing.getFillColorG() << 8) + + rDrawing.getFillColorB()))); + else if (!bTextFrame) + // If there is no fill, the Word default is 100% transparency. + xPropertySet->setPropertyValue("FillTransparence", uno::Any(sal_Int32(100))); + + RTFSdrImport::resolveFLine(xPropertySet, rDrawing.getFLine()); + + if (!m_aStates.top().getDrawingObject().getHadShapeText()) + { + Mapper().startShape(xShape); + } + Mapper().endShape(); + } + break; + case Destination::PICT: + // fdo#79319 ignore picture data if it's really a shape + if (!m_pSdrImport->isFakePict()) + { + resolvePict(true, m_pSdrImport->getCurrentShape()); + } + m_bNeedFinalPar = true; + break; + case Destination::SHAPE: + m_bNeedFinalPar = true; + m_bNeedCr = m_bNeedCrOrig; + if (rState.getFrame().inFrame()) + { + // parBreak() modifies m_aStates.top() so we can't apply resetFrame() directly on aState + resetFrame(); + parBreak(); + // Save this state for later use, so we only reset frame status only for the first shape inside a frame. + rState = m_aStates.top(); + m_bNeedPap = true; + } + break; + case Destination::MOMATH: + { + m_aMathBuffer.appendClosingTag(M_TOKEN(oMath)); + + SvGlobalName aGlobalName(SO3_SM_CLASSID); + comphelper::EmbeddedObjectContainer aContainer; + OUString aName; + uno::Reference<embed::XEmbeddedObject> xObject + = aContainer.CreateEmbeddedObject(aGlobalName.GetByteSequence(), aName); + if (xObject) // rhbz#1766990 starmath might not be available + { + uno::Reference<util::XCloseable> xComponent(xObject->getComponent(), + uno::UNO_SET_THROW); + // gcc4.4 (and 4.3 and possibly older) have a problem with dynamic_cast directly to the target class, + // so help it with an intermediate cast. I'm not sure what exactly the problem is, seems to be unrelated + // to RTLD_GLOBAL, so most probably a gcc bug. + auto& rImport = dynamic_cast<oox::FormulaImportBase&>( + dynamic_cast<SfxBaseModel&>(*xComponent)); + rImport.readFormulaOoxml(m_aMathBuffer); + + auto pValue = new RTFValue(xObject); + RTFSprms aMathAttributes; + aMathAttributes.set(NS_ooxml::LN_starmath, pValue); + writerfilter::Reference<Properties>::Pointer_t pProperties + = new RTFReferenceProperties(std::move(aMathAttributes)); + Mapper().props(pProperties); + } + + m_aMathBuffer = oox::formulaimport::XmlStreamBuilder(); + } + break; + case Destination::MR: + lcl_DestinationToMath(m_aStates.top().getCurrentDestinationText(), m_aMathBuffer, + m_bMathNor); + break; + case Destination::MF: + m_aMathBuffer.appendClosingTag(M_TOKEN(f)); + break; + case Destination::MFPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(fPr)); + break; + case Destination::MCTRLPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(ctrlPr)); + break; + case Destination::MNUM: + m_aMathBuffer.appendClosingTag(M_TOKEN(num)); + break; + case Destination::MDEN: + m_aMathBuffer.appendClosingTag(M_TOKEN(den)); + break; + case Destination::MACC: + m_aMathBuffer.appendClosingTag(M_TOKEN(acc)); + break; + case Destination::MACCPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(accPr)); + break; + case Destination::MCHR: + case Destination::MPOS: + case Destination::MVERTJC: + case Destination::MSTRIKEH: + case Destination::MDEGHIDE: + case Destination::MBEGCHR: + case Destination::MSEPCHR: + case Destination::MENDCHR: + case Destination::MSUBHIDE: + case Destination::MSUPHIDE: + case Destination::MTYPE: + case Destination::MGROW: + { + sal_Int32 nMathToken = 0; + switch (rState.getDestination()) + { + case Destination::MCHR: + nMathToken = M_TOKEN(chr); + break; + case Destination::MPOS: + nMathToken = M_TOKEN(pos); + break; + case Destination::MVERTJC: + nMathToken = M_TOKEN(vertJc); + break; + case Destination::MSTRIKEH: + nMathToken = M_TOKEN(strikeH); + break; + case Destination::MDEGHIDE: + nMathToken = M_TOKEN(degHide); + break; + case Destination::MBEGCHR: + nMathToken = M_TOKEN(begChr); + break; + case Destination::MSEPCHR: + nMathToken = M_TOKEN(sepChr); + break; + case Destination::MENDCHR: + nMathToken = M_TOKEN(endChr); + break; + case Destination::MSUBHIDE: + nMathToken = M_TOKEN(subHide); + break; + case Destination::MSUPHIDE: + nMathToken = M_TOKEN(supHide); + break; + case Destination::MTYPE: + nMathToken = M_TOKEN(type); + break; + case Destination::MGROW: + nMathToken = M_TOKEN(grow); + break; + default: + break; + } + + oox::formulaimport::XmlStream::AttributeList aAttribs; + aAttribs[M_TOKEN(val)] + = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + m_aMathBuffer.appendOpeningTag(nMathToken, aAttribs); + m_aMathBuffer.appendClosingTag(nMathToken); + } + break; + case Destination::ME: + m_aMathBuffer.appendClosingTag(M_TOKEN(e)); + break; + case Destination::MBAR: + m_aMathBuffer.appendClosingTag(M_TOKEN(bar)); + break; + case Destination::MBARPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(barPr)); + break; + case Destination::MD: + m_aMathBuffer.appendClosingTag(M_TOKEN(d)); + break; + case Destination::MDPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(dPr)); + break; + case Destination::MFUNC: + m_aMathBuffer.appendClosingTag(M_TOKEN(func)); + break; + case Destination::MFUNCPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(funcPr)); + break; + case Destination::MFNAME: + m_aMathBuffer.appendClosingTag(M_TOKEN(fName)); + break; + case Destination::MLIMLOW: + m_aMathBuffer.appendClosingTag(M_TOKEN(limLow)); + break; + case Destination::MLIMLOWPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(limLowPr)); + break; + case Destination::MLIM: + m_aMathBuffer.appendClosingTag(M_TOKEN(lim)); + break; + case Destination::MM: + m_aMathBuffer.appendClosingTag(M_TOKEN(m)); + break; + case Destination::MMPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(mPr)); + break; + case Destination::MMR: + m_aMathBuffer.appendClosingTag(M_TOKEN(mr)); + break; + case Destination::MNARY: + m_aMathBuffer.appendClosingTag(M_TOKEN(nary)); + break; + case Destination::MNARYPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(naryPr)); + break; + case Destination::MSUB: + m_aMathBuffer.appendClosingTag(M_TOKEN(sub)); + break; + case Destination::MSUP: + m_aMathBuffer.appendClosingTag(M_TOKEN(sup)); + break; + case Destination::MLIMUPP: + m_aMathBuffer.appendClosingTag(M_TOKEN(limUpp)); + break; + case Destination::MLIMUPPPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(limUppPr)); + break; + case Destination::MGROUPCHR: + m_aMathBuffer.appendClosingTag(M_TOKEN(groupChr)); + break; + case Destination::MGROUPCHRPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(groupChrPr)); + break; + case Destination::MBORDERBOX: + m_aMathBuffer.appendClosingTag(M_TOKEN(borderBox)); + break; + case Destination::MBORDERBOXPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(borderBoxPr)); + break; + case Destination::MRAD: + m_aMathBuffer.appendClosingTag(M_TOKEN(rad)); + break; + case Destination::MRADPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(radPr)); + break; + case Destination::MDEG: + m_aMathBuffer.appendClosingTag(M_TOKEN(deg)); + break; + case Destination::MSSUB: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSub)); + break; + case Destination::MSSUBPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSubPr)); + break; + case Destination::MSSUP: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSup)); + break; + case Destination::MSSUPPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSupPr)); + break; + case Destination::MSSUBSUP: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSup)); + break; + case Destination::MSSUBSUPPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(sSubSupPr)); + break; + case Destination::MSPRE: + m_aMathBuffer.appendClosingTag(M_TOKEN(sPre)); + break; + case Destination::MSPREPR: + m_aMathBuffer.appendClosingTag(M_TOKEN(sPrePr)); + break; + case Destination::MBOX: + m_aMathBuffer.appendClosingTag(M_TOKEN(box)); + break; + case Destination::MEQARR: + m_aMathBuffer.appendClosingTag(M_TOKEN(eqArr)); + break; + case Destination::SHAPEGROUP: + if (rState.getCreatedShapeGroup()) + m_pSdrImport->popParent(); + break; + case Destination::PROPNAME: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + rState.setPropName(m_aStates.top().getCurrentDestinationText()->makeStringAndClear()); + break; + case Destination::STATICVAL: + if (&m_aStates.top().getDestinationText() + != m_aStates.top().getCurrentDestinationText()) + break; // not for nested group + if (m_xDocumentProperties.is()) + { + // Find out what is the key, value type and value we want to set. + uno::Reference<beans::XPropertyContainer> xPropertyContainer + = m_xDocumentProperties->getUserDefinedProperties(); + const OUString& rKey = m_aStates.top().getPropName(); + OUString aStaticVal + = m_aStates.top().getCurrentDestinationText()->makeStringAndClear(); + uno::Any aAny; + if (m_aStates.top().getPropType() == cppu::UnoType<OUString>::get()) + aAny <<= aStaticVal; + else if (m_aStates.top().getPropType() == cppu::UnoType<sal_Int32>::get()) + aAny <<= aStaticVal.toInt32(); + else if (m_aStates.top().getPropType() == cppu::UnoType<bool>::get()) + aAny <<= aStaticVal.toBoolean(); + else if (m_aStates.top().getPropType() == cppu::UnoType<util::DateTime>::get()) + aAny <<= getDateTimeFromUserProp(aStaticVal); + else if (m_aStates.top().getPropType() == cppu::UnoType<double>::get()) + aAny <<= aStaticVal.toDouble(); + + xPropertyContainer->addProperty(rKey, beans::PropertyAttribute::REMOVABLE, aAny); + } + break; + case Destination::USERPROPS: + { + // These are the imported properties. + uno::Reference<document::XDocumentProperties> xDocumentProperties + = m_xDocumentProperties; + + // These are the real document properties. + uno::Reference<document::XDocumentPropertiesSupplier> xDocumentPropertiesSupplier( + m_xDstDoc, uno::UNO_QUERY); + if (xDocumentPropertiesSupplier.is()) + m_xDocumentProperties = xDocumentPropertiesSupplier->getDocumentProperties(); + + if (m_xDocumentProperties.is()) + { + if (!m_bIsNewDoc) + { + // Check classification. + if (!SfxClassificationHelper::ShowPasteInfo(SfxClassificationHelper::CheckPaste( + xDocumentProperties, m_xDocumentProperties))) + return RTFError::CLASSIFICATION; + } + + uno::Reference<beans::XPropertyContainer> xClipboardPropertyContainer + = xDocumentProperties->getUserDefinedProperties(); + uno::Reference<beans::XPropertyContainer> xDocumentPropertyContainer + = m_xDocumentProperties->getUserDefinedProperties(); + uno::Reference<beans::XPropertySet> xClipboardPropertySet( + xClipboardPropertyContainer, uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xDocumentPropertySet(xDocumentPropertyContainer, + uno::UNO_QUERY); + const uno::Sequence<beans::Property> aClipboardProperties + = xClipboardPropertySet->getPropertySetInfo()->getProperties(); + uno::Sequence<beans::Property> aDocumentProperties + = xDocumentPropertySet->getPropertySetInfo()->getProperties(); + + for (const beans::Property& rProperty : aClipboardProperties) + { + const OUString& rKey = rProperty.Name; + uno::Any aValue = xClipboardPropertySet->getPropertyValue(rKey); + + try + { + if (lcl_containsProperty(aDocumentProperties, rKey)) + { + // When pasting, don't update existing properties. + if (!m_bIsNewDoc) + xDocumentPropertySet->setPropertyValue(rKey, aValue); + } + else + xDocumentPropertyContainer->addProperty( + rKey, beans::PropertyAttribute::REMOVABLE, aValue); + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION("writerfilter.rtf", "failed to set property " << rKey); + } + } + } + } + break; + default: + break; + } + + return RTFError::OK; +} + +void RTFDocumentImpl::afterPopState(RTFParserState& rState) +{ + // list table + switch (rState.getDestination()) + { + case Destination::LISTENTRY: + { + auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); + m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pValue, + RTFOverwrite::NO_APPEND); + m_aListTable[rState.getCurrentListIndex()] = pValue; + m_nListLevel = -1; + m_aInvalidListTableFirstIndents[rState.getCurrentListIndex()] + = m_aInvalidListLevelFirstIndents; + m_aInvalidListLevelFirstIndents.clear(); + } + break; + case Destination::PARAGRAPHNUMBERING: + { + RTFValue::Pointer_t pIdValue + = rState.getTableAttributes().find(NS_ooxml::LN_CT_AbstractNum_nsid); + if (pIdValue && !m_aStates.empty()) + { + // Abstract numbering + RTFSprms aLeveltextAttributes; + OUString aTextValue; + RTFValue::Pointer_t pTextBefore + = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelText_val); + if (pTextBefore) + aTextValue += pTextBefore->getString(); + aTextValue += "%1"; + RTFValue::Pointer_t pTextAfter + = rState.getTableAttributes().find(NS_ooxml::LN_CT_LevelSuffix_val); + if (pTextAfter) + aTextValue += pTextAfter->getString(); + auto pTextValue = new RTFValue(aTextValue); + aLeveltextAttributes.set(NS_ooxml::LN_CT_LevelText_val, pTextValue); + + RTFSprms aLevelAttributes; + RTFSprms aLevelSprms; + auto pIlvlValue = new RTFValue(0); + aLevelAttributes.set(NS_ooxml::LN_CT_Lvl_ilvl, pIlvlValue); + + RTFValue::Pointer_t pFmtValue + = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_numFmt); + if (pFmtValue) + aLevelSprms.set(NS_ooxml::LN_CT_Lvl_numFmt, pFmtValue); + + RTFValue::Pointer_t pStartatValue + = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_start); + if (pStartatValue) + aLevelSprms.set(NS_ooxml::LN_CT_Lvl_start, pStartatValue); + + auto pLeveltextValue = new RTFValue(aLeveltextAttributes); + aLevelSprms.set(NS_ooxml::LN_CT_Lvl_lvlText, pLeveltextValue); + RTFValue::Pointer_t pRunProps + = rState.getTableSprms().find(NS_ooxml::LN_CT_Lvl_rPr); + if (pRunProps) + aLevelSprms.set(NS_ooxml::LN_CT_Lvl_rPr, pRunProps); + + RTFSprms aAbstractAttributes; + RTFSprms aAbstractSprms; + aAbstractAttributes.set(NS_ooxml::LN_CT_AbstractNum_abstractNumId, pIdValue); + auto pLevelValue = new RTFValue(aLevelAttributes, aLevelSprms); + aAbstractSprms.set(NS_ooxml::LN_CT_AbstractNum_lvl, pLevelValue, + RTFOverwrite::NO_APPEND); + + RTFSprms aListTableSprms; + auto pAbstractValue = new RTFValue(aAbstractAttributes, aAbstractSprms); + // It's important that Numbering_abstractNum and Numbering_num never overwrites previous values. + aListTableSprms.set(NS_ooxml::LN_CT_Numbering_abstractNum, pAbstractValue, + RTFOverwrite::NO_APPEND); + + // Numbering + RTFSprms aNumberingAttributes; + RTFSprms aNumberingSprms; + aNumberingAttributes.set(NS_ooxml::LN_CT_AbstractNum_nsid, pIdValue); + aNumberingSprms.set(NS_ooxml::LN_CT_Num_abstractNumId, pIdValue); + auto pNumberingValue = new RTFValue(aNumberingAttributes, aNumberingSprms); + aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pNumberingValue, + RTFOverwrite::NO_APPEND); + + // Table + RTFSprms aListTableAttributes; + writerfilter::Reference<Properties>::Pointer_t pProp = new RTFReferenceProperties( + std::move(aListTableAttributes), std::move(aListTableSprms)); + + RTFReferenceTable::Entries_t aListTableEntries; + aListTableEntries.insert(std::make_pair(0, pProp)); + writerfilter::Reference<Table>::Pointer_t const pTable( + new RTFReferenceTable(std::move(aListTableEntries))); + Mapper().table(NS_ooxml::LN_NUMBERING, pTable); + + // Use it + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, + NS_ooxml::LN_CT_NumPr_ilvl, pIlvlValue, RTFOverwrite::YES_PREPEND); + putNestedSprm(m_aStates.top().getParagraphSprms(), NS_ooxml::LN_CT_PPrBase_numPr, + NS_ooxml::LN_CT_NumPr_numId, pIdValue, RTFOverwrite::YES_PREPEND); + } + } + break; + case Destination::PARAGRAPHNUMBERING_TEXTAFTER: + if (!m_aStates.empty()) + { + // FIXME: don't use pDestinationText, points to popped state + auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelSuffix_val, pValue); + } + break; + case Destination::PARAGRAPHNUMBERING_TEXTBEFORE: + if (!m_aStates.empty()) + { + // FIXME: don't use pDestinationText, points to popped state + auto pValue = new RTFValue(rState.getDestinationText().makeStringAndClear(), true); + m_aStates.top().getTableAttributes().set(NS_ooxml::LN_CT_LevelText_val, pValue); + } + break; + case Destination::LISTNAME: + break; + case Destination::LISTLEVEL: + if (!m_aStates.empty()) + { + auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++); + rState.getTableAttributes().set(NS_ooxml::LN_CT_Lvl_ilvl, pInnerValue); + + auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); + if (m_aStates.top().getDestination() != Destination::LFOLEVEL) + m_aStates.top().getListLevelEntries().set(NS_ooxml::LN_CT_AbstractNum_lvl, + pValue, RTFOverwrite::NO_APPEND); + else + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_NumLvl_lvl, pValue); + } + break; + case Destination::LFOLEVEL: + if (!m_aStates.empty()) + { + auto pInnerValue = new RTFValue(m_aStates.top().getListLevelNum()++); + rState.getTableAttributes().set(NS_ooxml::LN_CT_NumLvl_ilvl, pInnerValue); + + auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Num_lvlOverride, pValue, + RTFOverwrite::NO_APPEND); + } + break; + // list override table + case Destination::LISTOVERRIDEENTRY: + if (!m_aStates.empty()) + { + if (m_aStates.top().getDestination() == Destination::LISTOVERRIDEENTRY) + { + // copy properties upwards so upper popState() inserts it + m_aStates.top().getTableAttributes() = rState.getTableAttributes(); + m_aStates.top().getTableSprms() = rState.getTableSprms(); + } + else + { + auto pValue = new RTFValue(rState.getTableAttributes(), rState.getTableSprms()); + m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_num, pValue, + RTFOverwrite::NO_APPEND); + m_aListOverrideTable[rState.getCurrentListOverrideIndex()] + = rState.getCurrentListIndex(); + } + } + break; + case Destination::LEVELTEXT: + if (!m_aStates.empty()) + { + auto pValue = new RTFValue(rState.getTableAttributes()); + m_aStates.top().getTableSprms().set(NS_ooxml::LN_CT_Lvl_lvlText, pValue); + } + break; + case Destination::LEVELNUMBERS: + if (!m_aStates.empty()) + { + m_aStates.top().getTableSprms() = rState.getTableSprms(); + if (m_aStates.top().getDestination() == Destination::LEVELNUMBERS + || m_aStates.top().getDestination() == Destination::LISTLEVEL) + // Parent state is level number or list level, current state is + // level numbers: mark parent as invalid as well if necessary. + m_aStates.top().setLevelNumbersValid(rState.getLevelNumbersValid()); + } + break; + case Destination::FIELDINSTRUCTION: + if (!m_aStates.empty()) + m_aStates.top().setFieldStatus(RTFFieldStatus::INSTRUCTION); + break; + case Destination::FIELDRESULT: + if (!m_aStates.empty()) + m_aStates.top().setFieldStatus(RTFFieldStatus::RESULT); + break; + case Destination::FIELD: + if (rState.getFieldStatus() == RTFFieldStatus::INSTRUCTION) + singleChar(cFieldEnd); + break; + case Destination::DOCVAR: + if (!m_aStates.empty()) + { + OUString docvar(rState.getDocVar()); + if (m_aStates.top().getDocVarName().isEmpty()) + { + m_aStates.top().setDocVarName(docvar); + } + else + { + uno::Reference<beans::XPropertySet> xMaster( + m_xModelFactory->createInstance("com.sun.star.text.FieldMaster.User"), + uno::UNO_QUERY_THROW); + xMaster->setPropertyValue("Name", uno::Any(m_aStates.top().getDocVarName())); + uno::Reference<text::XDependentTextField> xField( + m_xModelFactory->createInstance("com.sun.star.text.TextField.User"), + uno::UNO_QUERY); + xField->attachTextFieldMaster(xMaster); + xField->getTextFieldMaster()->setPropertyValue("Content", uno::Any(docvar)); + + m_aStates.top().clearDocVarName(); + } + } + break; + case Destination::SHAPEPROPERTYVALUEPICT: + if (!m_aStates.empty()) + { + m_aStates.top().getPicture() = rState.getPicture(); + // both \sp and \sv are destinations, copy the text up-ward for later + m_aStates.top().getDestinationText() = rState.getDestinationText(); + } + break; + case Destination::FALT: + if (!m_aStates.empty()) + m_aStates.top().getTableSprms() = rState.getTableSprms(); + break; + case Destination::SHAPEPROPERTYNAME: + case Destination::SHAPEPROPERTYVALUE: + case Destination::SHAPEPROPERTY: + if (!m_aStates.empty()) + { + m_aStates.top().getShape() = rState.getShape(); + m_aStates.top().getPicture() = rState.getPicture(); + m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes(); + } + break; + case Destination::SHAPEINSTRUCTION: + if (!m_aStates.empty() + && m_aStates.top().getDestination() == Destination::SHAPEINSTRUCTION) + { + // Shape instruction inside other shape instruction: just copy new shape settings: + // it will be resolved on end of topmost shape instruction destination + m_aStates.top().getShape() = rState.getShape(); + m_aStates.top().getPicture() = rState.getPicture(); + m_aStates.top().getCharacterSprms() = rState.getCharacterSprms(); + m_aStates.top().getCharacterAttributes() = rState.getCharacterAttributes(); + } + break; + case Destination::FLYMAINCONTENT: + case Destination::SHPPICT: + case Destination::SHAPE: + if (!m_aStates.empty()) + { + m_aStates.top().getFrame() = rState.getFrame(); + if (rState.getDestination() == Destination::SHPPICT + && m_aStates.top().getDestination() == Destination::LISTPICTURE) + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_NumPicBullet_numPicBulletId, + new RTFValue(m_nListPictureId++)); + RTFSprms aSprms; + // Dummy value, real picture is already sent to dmapper. + aSprms.set(NS_ooxml::LN_CT_NumPicBullet_pict, new RTFValue(0)); + auto pValue = new RTFValue(aAttributes, aSprms); + m_aListTableSprms.set(NS_ooxml::LN_CT_Numbering_numPicBullet, pValue, + RTFOverwrite::NO_APPEND); + } + } + break; + case Destination::SHAPETEXT: + if (!m_aStates.empty()) + { + // If we're leaving the shapetext group (it may have nested ones) and this is a shape, not an old drawingobject. + if (m_aStates.top().getDestination() != Destination::SHAPETEXT + && !m_aStates.top().getDrawingObject().getHadShapeText()) + { + m_aStates.top().setHadShapeText(true); + if (!m_aStates.top().getCurrentBuffer()) + m_pSdrImport->close(); + else + m_aStates.top().getCurrentBuffer()->push_back( + Buf_t(BUFFER_ENDSHAPE, nullptr, nullptr)); + } + + // It's allowed to declare these inside the shape text, and they + // are expected to have an effect for the whole shape. + if (rState.getDrawingObject().getLeft()) + m_aStates.top().getDrawingObject().setLeft(rState.getDrawingObject().getLeft()); + if (rState.getDrawingObject().getTop()) + m_aStates.top().getDrawingObject().setTop(rState.getDrawingObject().getTop()); + if (rState.getDrawingObject().getRight()) + m_aStates.top().getDrawingObject().setRight( + rState.getDrawingObject().getRight()); + if (rState.getDrawingObject().getBottom()) + m_aStates.top().getDrawingObject().setBottom( + rState.getDrawingObject().getBottom()); + } + break; + case Destination::PROPNAME: + if (m_aStates.top().getDestination() == Destination::USERPROPS) + m_aStates.top().setPropName(rState.getPropName()); + break; + default: + { + if (!m_aStates.empty() && m_aStates.top().getDestination() == Destination::PICT) + m_aStates.top().getPicture() = rState.getPicture(); + } + break; + } +} + +RTFError RTFDocumentImpl::popState() +{ + //SAL_INFO("writerfilter", __func__ << " before pop: m_pTokenizer->getGroup() " << m_pTokenizer->getGroup() << + // ", dest state: " << m_aStates.top().eDestination); + + checkUnicode(/*bUnicode =*/true, /*bHex =*/true); + RTFParserState aState(m_aStates.top()); + m_bWasInFrame = aState.getFrame().inFrame(); + + // dmapper expects some content in header/footer, so if there would be nothing, add an empty paragraph. + if (m_pTokenizer->getGroup() == 1 && m_bFirstRun) + { + switch (m_nStreamType) + { + case NS_ooxml::LN_headerl: + case NS_ooxml::LN_headerr: + case NS_ooxml::LN_headerf: + case NS_ooxml::LN_footerl: + case NS_ooxml::LN_footerr: + case NS_ooxml::LN_footerf: + dispatchSymbol(RTFKeyword::PAR); + break; + } + } + + RTFError eError = beforePopState(aState); + if (eError != RTFError::OK) + return eError; + + // See if we need to end a track change + if (aState.getStartedTrackchange()) + { + RTFSprms aTCSprms; + auto pValue = new RTFValue(0); + aTCSprms.set(NS_ooxml::LN_endtrackchange, pValue); + if (!m_aStates.top().getCurrentBuffer()) + Mapper().props(new RTFReferenceProperties(RTFSprms(), std::move(aTCSprms))); + else + bufferProperties(*m_aStates.top().getCurrentBuffer(), + new RTFValue(RTFSprms(), aTCSprms), nullptr); + } + + // This is the end of the doc, see if we need to close the last section. + if (m_pTokenizer->getGroup() == 1 && !m_bFirstRun) + { + // \par means an empty paragraph at the end of footnotes/endnotes, but + // not in case of other substreams, like headers. + if (m_bNeedCr && m_nStreamType != NS_ooxml::LN_footnote + && m_nStreamType != NS_ooxml::LN_endnote && m_bIsNewDoc) + dispatchSymbol(RTFKeyword::PAR); + if (m_bNeedSect) // may be set by dispatchSymbol above! + sectBreak(true); + } + + m_aStates.pop(); + + m_pTokenizer->popGroup(); + + afterPopState(aState); + + if (aState.getCurrentBuffer() == &m_aSuperBuffer) + { + OSL_ASSERT(!m_aStates.empty() && m_aStates.top().getCurrentBuffer() == nullptr); + + if (!m_aSuperBuffer.empty()) + replayBuffer(m_aSuperBuffer, nullptr, nullptr); + } + + if (!m_aStates.empty() && m_aStates.top().getTableRowWidthAfter() > 0 + && aState.getTableRowWidthAfter() == 0) + // An RTFKeyword::ROW in the inner group already parsed nTableRowWidthAfter, + // don't do it again in the outer state later. + m_aStates.top().setTableRowWidthAfter(0); + + if (m_nResetBreakOnSectBreak != RTFKeyword::invalid && !m_aStates.empty()) + { + // Section break type created for \page still has an effect in the + // outer state as well. + RTFValue::Pointer_t pType + = aState.getSectionSprms().find(NS_ooxml::LN_EG_SectPrContents_type); + if (pType) + m_aStates.top().getSectionSprms().set(NS_ooxml::LN_EG_SectPrContents_type, pType); + } + + return RTFError::OK; +} + +RTFError RTFDocumentImpl::handleEmbeddedObject() +{ + OString aStr + = OUStringToOString(m_aStates.top().getCurrentDestinationText()->makeStringAndClear(), + RTL_TEXTENCODING_ASCII_US); + std::unique_ptr<SvStream> pStream(new SvMemoryStream()); + if (!msfilter::rtfutil::ExtractOLE2FromObjdata(aStr, *pStream)) + return RTFError::HEX_INVALID; + + uno::Reference<io::XInputStream> xInputStream( + new utl::OSeekableInputStreamWrapper(pStream.release(), /*_bOwner=*/true)); + auto pStreamValue = new RTFValue(xInputStream); + m_aOLEAttributes.set(NS_ooxml::LN_inputstream, pStreamValue); + + return RTFError::OK; +} + +bool RTFDocumentImpl::isInBackground() { return m_aStates.top().getInBackground(); } + +RTFInternalState RTFDocumentImpl::getInternalState() { return m_aStates.top().getInternalState(); } + +void RTFDocumentImpl::setInternalState(RTFInternalState nInternalState) +{ + m_aStates.top().setInternalState(nInternalState); +} + +Destination RTFDocumentImpl::getDestination() { return m_aStates.top().getDestination(); } + +void RTFDocumentImpl::setDestination(Destination eDestination) +{ + m_aStates.top().setDestination(eDestination); +} + +// this is a questionably named method that is used only in a very special +// situation where it looks like the "current" buffer is needed? +void RTFDocumentImpl::setDestinationText(std::u16string_view rString) +{ + m_aStates.top().getDestinationText().setLength(0); + m_aStates.top().getDestinationText().append(rString); +} + +bool RTFDocumentImpl::getSkipUnknown() { return m_bSkipUnknown; } + +void RTFDocumentImpl::setSkipUnknown(bool bSkipUnknown) { m_bSkipUnknown = bSkipUnknown; } + +static auto FilterControlChars(Destination const destination, OUString const& rString) -> OUString +{ + if (destination == Destination::LEVELNUMBERS || destination == Destination::LEVELTEXT) + { // control characters are magic here! + return rString; + } + OUStringBuffer buf(rString.getLength()); + for (sal_Int32 i = 0; i < rString.getLength(); ++i) + { + sal_Unicode const ch(rString[i]); + if (!linguistic::IsControlChar(ch) || ch == '\r' || ch == '\n' || ch == '\t') + { + buf.append(ch); + } + else + { + SAL_INFO("writerfilter.rtf", "filtering control character"); + } + } + return buf.makeStringAndClear(); +} + +void RTFDocumentImpl::checkUnicode(bool bUnicode, bool bHex) +{ + if (bUnicode && !m_aUnicodeBuffer.isEmpty()) + { + OUString aString = m_aUnicodeBuffer.toString(); + m_aUnicodeBuffer.setLength(0); + aString = FilterControlChars(m_aStates.top().getDestination(), aString); + text(aString); + } + if (bHex && !m_aHexBuffer.isEmpty()) + { + rtl_TextEncoding nEncoding = m_aStates.top().getCurrentEncoding(); + if (m_aStates.top().getDestination() == Destination::FONTENTRY + && m_aStates.top().getCurrentEncoding() == RTL_TEXTENCODING_SYMBOL) + nEncoding = RTL_TEXTENCODING_MS_1252; + OUString aString = OStringToOUString(m_aHexBuffer, nEncoding); + m_aHexBuffer.setLength(0); + aString = FilterControlChars(m_aStates.top().getDestination(), aString); + text(aString); + } +} + +RTFParserState::RTFParserState(RTFDocumentImpl* pDocumentImpl) + : m_pDocumentImpl(pDocumentImpl) + , m_nInternalState(RTFInternalState::NORMAL) + , m_eDestination(Destination::NORMAL) + , m_eFieldStatus(RTFFieldStatus::NONE) + , m_bFieldLocked(false) + , m_nBorderState(RTFBorderState::NONE) + , m_nCurrentEncoding(rtl_getTextEncodingFromWindowsCharset(0)) + , m_nUc(1) + , m_nCharsToSkip(0) + , m_nBinaryToRead(0) + , m_nListLevelNum(0) + , m_bLevelNumbersValid(true) + , m_aFrame(this) + , m_eRunType(RunType::NONE) + , m_nYear(0) + , m_nMonth(0) + , m_nDay(0) + , m_nHour(0) + , m_nMinute(0) + , m_pCurrentDestinationText(nullptr) + , m_nCurrentStyleIndex(0) + , m_nCurrentCharacterStyleIndex(-1) + , m_pCurrentBuffer(nullptr) + , m_bInListpicture(false) + , m_bInBackground(false) + , m_bHadShapeText(false) + , m_bInShapeGroup(false) + , m_bInShape(false) + , m_bCreatedShapeGroup(false) + , m_bStartedTrackchange(false) + , m_nTableRowWidthAfter(0) +{ +} + +void RTFDocumentImpl::resetFrame() { m_aStates.top().getFrame() = RTFFrame(&m_aStates.top()); } + +void RTFDocumentImpl::bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue, + const tools::SvRef<TableRowBuffer>& pTableProperties) +{ + rBuffer.emplace_back(BUFFER_SETSTYLE, new RTFValue(m_aStates.top().getCurrentStyleIndex()), + nullptr); + rBuffer.emplace_back(BUFFER_PROPS, pValue, pTableProperties); +} + +RTFShape::RTFShape() = default; + +RTFDrawingObject::RTFDrawingObject() = default; + +RTFFrame::RTFFrame(RTFParserState* pParserState) + : m_pDocumentImpl(pParserState->getDocumentImpl()) + , m_nX(0) + , m_nY(0) + , m_nW(0) + , m_nH(0) + , m_nHoriPadding(0) + , m_nVertPadding(0) + , m_nHoriAlign(0) + , m_nHoriAnchor(0) + , m_nVertAlign(0) + , m_nVertAnchor(0) + , m_nHRule(NS_ooxml::LN_Value_doc_ST_HeightRule_auto) +{ +} + +void RTFFrame::setSprm(Id nId, Id nValue) +{ + if (m_pDocumentImpl->getFirstRun() && !m_pDocumentImpl->isStyleSheetImport()) + { + m_pDocumentImpl->checkFirstRun(); + m_pDocumentImpl->setNeedPar(false); + } + switch (nId) + { + case NS_ooxml::LN_CT_FramePr_w: + m_nW = nValue; + break; + case NS_ooxml::LN_CT_FramePr_h: + m_nH = nValue; + break; + case NS_ooxml::LN_CT_FramePr_x: + m_nX = nValue; + break; + case NS_ooxml::LN_CT_FramePr_y: + m_nY = nValue; + break; + case NS_ooxml::LN_CT_FramePr_hSpace: + m_nHoriPadding = nValue; + break; + case NS_ooxml::LN_CT_FramePr_vSpace: + m_nVertPadding = nValue; + break; + case NS_ooxml::LN_CT_FramePr_xAlign: + m_nHoriAlign = nValue; + break; + case NS_ooxml::LN_CT_FramePr_hAnchor: + m_nHoriAnchor = nValue; + break; + case NS_ooxml::LN_CT_FramePr_yAlign: + m_nVertAlign = nValue; + break; + case NS_ooxml::LN_CT_FramePr_vAnchor: + m_nVertAnchor = nValue; + break; + case NS_ooxml::LN_CT_FramePr_wrap: + m_oWrap = nValue; + break; + default: + break; + } +} + +RTFSprms RTFFrame::getSprms() +{ + RTFSprms sprms; + + static const Id pNames[] + = { NS_ooxml::LN_CT_FramePr_x, NS_ooxml::LN_CT_FramePr_y, + NS_ooxml::LN_CT_FramePr_hRule, // Make sure nHRule is processed before nH + NS_ooxml::LN_CT_FramePr_h, NS_ooxml::LN_CT_FramePr_w, + NS_ooxml::LN_CT_FramePr_hSpace, NS_ooxml::LN_CT_FramePr_vSpace, + NS_ooxml::LN_CT_FramePr_hAnchor, NS_ooxml::LN_CT_FramePr_vAnchor, + NS_ooxml::LN_CT_FramePr_xAlign, NS_ooxml::LN_CT_FramePr_yAlign, + NS_ooxml::LN_CT_FramePr_wrap, NS_ooxml::LN_CT_FramePr_dropCap, + NS_ooxml::LN_CT_FramePr_lines }; + + for (Id nId : pNames) + { + RTFValue::Pointer_t pValue; + + switch (nId) + { + case NS_ooxml::LN_CT_FramePr_x: + if (m_nX != 0) + pValue = new RTFValue(m_nX); + break; + case NS_ooxml::LN_CT_FramePr_y: + if (m_nY != 0) + pValue = new RTFValue(m_nY); + break; + case NS_ooxml::LN_CT_FramePr_h: + if (m_nH != 0) + { + if (m_nHRule == NS_ooxml::LN_Value_doc_ST_HeightRule_exact) + pValue = new RTFValue(-m_nH); // The negative value just sets nHRule + else + pValue = new RTFValue(m_nH); + } + break; + case NS_ooxml::LN_CT_FramePr_w: + if (m_nW != 0) + pValue = new RTFValue(m_nW); + break; + case NS_ooxml::LN_CT_FramePr_hSpace: + if (m_nHoriPadding != 0) + pValue = new RTFValue(m_nHoriPadding); + break; + case NS_ooxml::LN_CT_FramePr_vSpace: + if (m_nVertPadding != 0) + pValue = new RTFValue(m_nVertPadding); + break; + case NS_ooxml::LN_CT_FramePr_hAnchor: + { + if (m_nHoriAnchor == 0) + m_nHoriAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_margin; + pValue = new RTFValue(m_nHoriAnchor); + } + break; + case NS_ooxml::LN_CT_FramePr_vAnchor: + { + if (m_nVertAnchor == 0) + m_nVertAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin; + pValue = new RTFValue(m_nVertAnchor); + } + break; + case NS_ooxml::LN_CT_FramePr_xAlign: + pValue = new RTFValue(m_nHoriAlign); + break; + case NS_ooxml::LN_CT_FramePr_yAlign: + pValue = new RTFValue(m_nVertAlign); + break; + case NS_ooxml::LN_CT_FramePr_hRule: + { + if (m_nH < 0) + m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_exact; + else if (m_nH > 0) + m_nHRule = NS_ooxml::LN_Value_doc_ST_HeightRule_atLeast; + pValue = new RTFValue(m_nHRule); + } + break; + case NS_ooxml::LN_CT_FramePr_wrap: + if (m_oWrap) + pValue = new RTFValue(*m_oWrap); + break; + default: + break; + } + + if (pValue) + sprms.set(nId, pValue); + } + + RTFSprms frameprSprms; + frameprSprms.set(NS_ooxml::LN_CT_PPrBase_framePr, new RTFValue(sprms)); + return frameprSprms; +} + +bool RTFFrame::hasProperties() const +{ + return m_nX != 0 || m_nY != 0 || m_nW != 0 || m_nH != 0 || m_nHoriPadding != 0 + || m_nVertPadding != 0 || m_nHoriAlign != 0 || m_nHoriAnchor != 0 || m_nVertAlign != 0 + || m_nVertAnchor != 0; +} + +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfdocumentimpl.hxx b/writerfilter/source/rtftok/rtfdocumentimpl.hxx new file mode 100644 index 000000000..e859c01c9 --- /dev/null +++ b/writerfilter/source/rtftok/rtfdocumentimpl.hxx @@ -0,0 +1,994 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <memory> +#include <queue> +#include <tuple> +#include <vector> +#include <optional> + +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/io/WrongFormatException.hpp> +#include <oox/mathml/importutils.hxx> +#include <rtl/strbuf.hxx> +#include <rtl/ustrbuf.hxx> +#include <tools/color.hxx> +#include <tools/long.hxx> + +#include <rtftok/RTFDocument.hxx> +#include "rtfreferencetable.hxx" +#include "rtfsprm.hxx" +#include "rtflistener.hxx" + +class SvStream; +namespace oox +{ +class GraphicHelper; +} +namespace com::sun::star +{ +namespace beans +{ +class XPropertySet; +} +namespace document +{ +class XDocumentProperties; +} +namespace lang +{ +class XMultiServiceFactory; +} +} + +namespace writerfilter::rtftok +{ +class RTFParserState; +class RTFDocumentImpl; +class RTFTokenizer; +class RTFSdrImport; +class TableRowBuffer; + +enum class RTFBorderState +{ + NONE, + PARAGRAPH, + PARAGRAPH_BOX, + CELL, + PAGE, + CHARACTER +}; + +/// Different kind of buffers for table cell contents. +enum RTFBufferTypes +{ + BUFFER_SETSTYLE, + /// Stores properties, should be created only in bufferProperties(). + BUFFER_PROPS, + BUFFER_NESTROW, + BUFFER_CELLEND, + BUFFER_STARTRUN, + BUFFER_TEXT, + BUFFER_UTEXT, + BUFFER_ENDRUN, + BUFFER_PAR, + BUFFER_STARTSHAPE, + /// Imports a shape. + BUFFER_RESOLVESHAPE, + BUFFER_ENDSHAPE, + BUFFER_RESOLVESUBSTREAM, + /// Restores RTFParserState::aPicture. + BUFFER_PICTURE +}; + +/// Form field types +enum class RTFFormFieldType +{ + NONE, + TEXT, + CHECKBOX, + LIST +}; + +enum class RTFBmpStyle +{ + NONE, + PNG, + JPEG, + DIBITMAP +}; + +enum class RTFFieldStatus +{ + NONE, + INSTRUCTION, + RESULT +}; + +/// A buffer storing dmapper calls. +using Buf_t = std::tuple<RTFBufferTypes, RTFValue::Pointer_t, tools::SvRef<TableRowBuffer>>; +using RTFBuffer_t = std::deque<Buf_t>; + +/// holds one nested table row +class TableRowBuffer : public virtual SvRefBase +{ + RTFBuffer_t m_aBuffer; + ::std::deque<RTFSprms> m_aCellsSprms; + ::std::deque<RTFSprms> m_aCellsAttributes; + int m_nCells; + writerfilter::Reference<Properties>::Pointer_t m_pParaProperties; + writerfilter::Reference<Properties>::Pointer_t m_pFrameProperties; + writerfilter::Reference<Properties>::Pointer_t m_pRowProperties; + +public: + TableRowBuffer(RTFBuffer_t aBuffer, std::deque<RTFSprms> aSprms, + std::deque<RTFSprms> aAttributes, int const nCells) + : m_aBuffer(std::move(aBuffer)) + , m_aCellsSprms(std::move(aSprms)) + , m_aCellsAttributes(std::move(aAttributes)) + , m_nCells(nCells) + { + } + + RTFBuffer_t& GetBuffer() { return m_aBuffer; } + std::deque<RTFSprms>& GetCellsSprms() { return m_aCellsSprms; } + std::deque<RTFSprms>& GetCellsAttributes() { return m_aCellsAttributes; } + int GetCells() const { return m_nCells; } + writerfilter::Reference<Properties>::Pointer_t& GetParaProperties() + { + return m_pParaProperties; + } + writerfilter::Reference<Properties>::Pointer_t& GetFrameProperties() + { + return m_pFrameProperties; + } + writerfilter::Reference<Properties>::Pointer_t& GetRowProperties() { return m_pRowProperties; } +}; + +/// An entry in the color table. +class RTFColorTableEntry +{ +public: + void SetRed(sal_uInt8 nRed) + { + m_bAuto = false; + m_nR = nRed; + } + void SetGreen(sal_uInt8 nGreen) + { + m_bAuto = false; + m_nG = nGreen; + } + void SetBlue(sal_uInt8 nBlue) + { + m_bAuto = false; + m_nB = nBlue; + } + Color GetColor() const { return m_bAuto ? COL_AUTO : Color(m_nR, m_nG, m_nB); } + +private: + bool m_bAuto = true; + sal_uInt8 m_nR = 0; + sal_uInt8 m_nG = 0; + sal_uInt8 m_nB = 0; +}; + +/// Stores the properties of a shape. +class RTFShape : public virtual SvRefBase +{ +public: + RTFShape(); + + std::vector<std::pair<OUString, OUString>>& getProperties() { return m_aProperties; } + + const std::vector<std::pair<OUString, OUString>>& getProperties() const + { + return m_aProperties; + } + + std::vector<std::pair<OUString, OUString>>& getGroupProperties() { return m_aGroupProperties; } + + void setLeft(sal_Int32 nLeft) { m_nLeft = nLeft; } + + sal_Int32 getLeft() const { return m_nLeft; } + + void setTop(sal_Int32 nTop) { m_nTop = nTop; } + + sal_Int32 getTop() const { return m_nTop; } + + void setRight(sal_Int32 nRight) { m_nRight = nRight; } + + sal_Int32 getRight() const { return m_nRight; } + + void setBottom(sal_Int32 nBottom) { m_nBottom = nBottom; } + + sal_Int32 getBottom() const { return m_nBottom; } + + void setZ(sal_Int32 nZ) { m_oZ = nZ; } + + bool hasZ() const { return bool(m_oZ); } + + sal_Int32 getZ() const { return *m_oZ; } + + void setHoriOrientRelation(sal_Int16 nHoriOrientRelation) + { + m_nHoriOrientRelation = nHoriOrientRelation; + } + + sal_Int16 getHoriOrientRelation() const { return m_nHoriOrientRelation; } + + void setVertOrientRelation(sal_Int16 nVertOrientRelation) + { + m_nVertOrientRelation = nVertOrientRelation; + } + + sal_Int16 getVertOrientRelation() const { return m_nVertOrientRelation; } + + void setHoriOrientRelationToken(sal_uInt32 nHoriOrientRelationToken) + { + m_nHoriOrientRelationToken = nHoriOrientRelationToken; + } + + sal_uInt32 getHoriOrientRelationToken() const { return m_nHoriOrientRelationToken; } + + void setVertOrientRelationToken(sal_uInt32 nVertOrientRelationToken) + { + m_nVertOrientRelationToken = nVertOrientRelationToken; + } + + sal_uInt32 getVertOrientRelationToken() const { return m_nVertOrientRelationToken; } + + void setWrap(css::text::WrapTextMode nWrap) { m_nWrap = nWrap; } + + css::text::WrapTextMode getWrap() const { return m_nWrap; } + + void setInBackground(bool bInBackground) { m_bInBackground = bInBackground; } + + bool getInBackground() const { return m_bInBackground; } + + RTFSprms& getWrapPolygonSprms() { return m_aWrapPolygonSprms; } + + RTFSprms& getAnchorAttributes() { return m_aAnchorAttributes; } + + std::pair<Id, RTFValue::Pointer_t>& getWrapSprm() { return m_aWrapSprm; } + +private: + std::vector<std::pair<OUString, OUString>> m_aProperties; ///< Properties of a single shape. + std::vector<std::pair<OUString, OUString>> + m_aGroupProperties; ///< Properties applied on the groupshape. + sal_Int32 m_nLeft = 0; + sal_Int32 m_nTop = 0; + sal_Int32 m_nRight = 0; + sal_Int32 m_nBottom = 0; + std::optional<sal_Int32> m_oZ; ///< Z-Order of the shape. + sal_Int16 m_nHoriOrientRelation + = 0; ///< Horizontal text::RelOrientation for drawinglayer shapes. + sal_Int16 m_nVertOrientRelation = 0; ///< Vertical text::RelOrientation for drawinglayer shapes. + sal_uInt32 m_nHoriOrientRelationToken = 0; ///< Horizontal dmapper token for Writer pictures. + sal_uInt32 m_nVertOrientRelationToken = 0; ///< Vertical dmapper token for Writer pictures. + css::text::WrapTextMode m_nWrap = css::text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE; + /// If shape is below text (true) or text is below shape (false). + bool m_bInBackground = false; + /// Wrap polygon, written by RTFSdrImport::resolve(), read by RTFDocumentImpl::resolvePict(). + RTFSprms m_aWrapPolygonSprms; + /// Anchor attributes like wrap distance, written by RTFSdrImport::resolve(), read by RTFDocumentImpl::resolvePict(). + RTFSprms m_aAnchorAttributes; + /// Wrap type, written by RTFDocumentImpl::popState(), read by RTFDocumentImpl::resolvePict(). + std::pair<Id, RTFValue::Pointer_t> m_aWrapSprm{ 0, nullptr }; +}; + +/// Stores the properties of a drawing object. +class RTFDrawingObject : public RTFShape +{ +public: + RTFDrawingObject(); + + void setShape(const css::uno::Reference<css::drawing::XShape>& xShape) { m_xShape = xShape; } + const css::uno::Reference<css::drawing::XShape>& getShape() const { return m_xShape; } + void setPropertySet(const css::uno::Reference<css::beans::XPropertySet>& xPropertySet) + { + m_xPropertySet = xPropertySet; + } + const css::uno::Reference<css::beans::XPropertySet>& getPropertySet() const + { + return m_xPropertySet; + } + std::vector<css::beans::PropertyValue>& getPendingProperties() { return m_aPendingProperties; } + void setLineColorR(sal_uInt8 nLineColorR) { m_nLineColorR = nLineColorR; } + sal_uInt8 getLineColorR() const { return m_nLineColorR; } + void setLineColorG(sal_uInt8 nLineColorG) { m_nLineColorG = nLineColorG; } + sal_uInt8 getLineColorG() const { return m_nLineColorG; } + void setLineColorB(sal_uInt8 nLineColorB) { m_nLineColorB = nLineColorB; } + sal_uInt8 getLineColorB() const { return m_nLineColorB; } + void setHasLineColor(bool bHasLineColor) { m_bHasLineColor = bHasLineColor; } + bool getHasLineColor() const { return m_bHasLineColor; } + void setFillColorR(sal_uInt8 nFillColorR) { m_nFillColorR = nFillColorR; } + sal_uInt8 getFillColorR() const { return m_nFillColorR; } + void setFillColorG(sal_uInt8 nFillColorG) { m_nFillColorG = nFillColorG; } + sal_uInt8 getFillColorG() const { return m_nFillColorG; } + void setFillColorB(sal_uInt8 nFillColorB) { m_nFillColorB = nFillColorB; } + sal_uInt8 getFillColorB() const { return m_nFillColorB; } + void setHasFillColor(bool bHasFillColor) { m_bHasFillColor = bHasFillColor; } + bool getHasFillColor() const { return m_bHasFillColor; } + void setDhgt(sal_Int32 nDhgt) { m_nDhgt = nDhgt; } + sal_Int32 getDhgt() const { return m_nDhgt; } + void setFLine(sal_Int32 nFLine) { m_nFLine = nFLine; } + sal_Int32 getFLine() const { return m_nFLine; } + void setPolyLineCount(sal_Int32 nPolyLineCount) { m_nPolyLineCount = nPolyLineCount; } + sal_Int32 getPolyLineCount() const { return m_nPolyLineCount; } + std::vector<css::awt::Point>& getPolyLinePoints() { return m_aPolyLinePoints; } + void setHadShapeText(bool bHadShapeText) { m_bHadShapeText = bHadShapeText; } + bool getHadShapeText() const { return m_bHadShapeText; } + +private: + css::uno::Reference<css::drawing::XShape> m_xShape; + css::uno::Reference<css::beans::XPropertySet> m_xPropertySet; + std::vector<css::beans::PropertyValue> m_aPendingProperties; + sal_uInt8 m_nLineColorR = 0; + sal_uInt8 m_nLineColorG = 0; + sal_uInt8 m_nLineColorB = 0; + bool m_bHasLineColor = false; + sal_uInt8 m_nFillColorR = 0; + sal_uInt8 m_nFillColorG = 0; + sal_uInt8 m_nFillColorB = 0; + bool m_bHasFillColor = false; + sal_Int32 m_nDhgt = 0; + sal_Int32 m_nFLine = -1; + sal_Int32 m_nPolyLineCount = 0; + std::vector<css::awt::Point> m_aPolyLinePoints; + bool m_bHadShapeText = false; +}; + +/// Stores the properties of a picture. +class RTFPicture : public virtual SvRefBase +{ +public: + sal_Int32 nWidth = 0; + sal_Int32 nHeight = 0; + sal_Int32 nGoalWidth = 0; + sal_Int32 nGoalHeight = 0; + sal_uInt16 nScaleX = 100; + sal_uInt16 nScaleY = 100; + short nCropT = 0; + short nCropB = 0; + short nCropL = 0; + short nCropR = 0; + sal_uInt16 eWMetafile = 0; + RTFBmpStyle eStyle = RTFBmpStyle::NONE; +}; + +/// Stores the properties of a frame +class RTFFrame +{ +private: + RTFDocumentImpl* m_pDocumentImpl; + sal_Int32 m_nX, m_nY, m_nW, m_nH; + sal_Int32 m_nHoriPadding, m_nVertPadding; + sal_Int32 m_nHoriAlign, m_nHoriAnchor, m_nVertAlign, m_nVertAnchor; + Id m_nHRule; + std::optional<Id> m_oWrap; + +public: + explicit RTFFrame(RTFParserState* pParserState); + + /// Convert the stored properties to Sprms + RTFSprms getSprms(); + /// Store a property + void setSprm(Id nId, Id nValue); + bool hasProperties() const; + /// If we got tokens indicating we're in a frame. + bool inFrame() const; +}; + +/// State of the parser, which gets saved / restored when changing groups. +class RTFParserState +{ +public: + /// Maps to OOXML's ascii, cs or eastAsia. + enum class RunType + { + NONE, + LOCH, + HICH, + DBCH, + LTRCH_RTLCH_1, + LTRCH_RTLCH_2, + RTLCH_LTRCH_1, + RTLCH_LTRCH_2 + }; + + explicit RTFParserState(RTFDocumentImpl* pDocumentImpl); + + void appendDestinationText(std::u16string_view rString) + { + if (m_pCurrentDestinationText) + m_pCurrentDestinationText->append(rString); + } + + void setPropName(const OUString& rPropName) { m_aPropName = rPropName; } + OUString const& getPropName() const { return m_aPropName; } + void setPropType(const css::uno::Type& rPropType) { m_aPropType = rPropType; } + css::uno::Type const& getPropType() const { return m_aPropType; } + void setTableRowWidthAfter(int nTableRowWidthAfter) + { + m_nTableRowWidthAfter = nTableRowWidthAfter; + } + int getTableRowWidthAfter() const { return m_nTableRowWidthAfter; } + void setStartedTrackchange(bool bStartedTrackchange) + { + m_bStartedTrackchange = bStartedTrackchange; + } + bool getStartedTrackchange() const { return m_bStartedTrackchange; } + void setCreatedShapeGroup(bool bCreatedShapeGroup) + { + m_bCreatedShapeGroup = bCreatedShapeGroup; + } + bool getCreatedShapeGroup() const { return m_bCreatedShapeGroup; } + void setInShape(bool bInShape) { m_bInShape = bInShape; } + bool getInShape() const { return m_bInShape; } + void setInShapeGroup(bool bInShapeGroup) { m_bInShapeGroup = bInShapeGroup; } + bool getInShapeGroup() const { return m_bInShapeGroup; } + void setHadShapeText(bool bHadShapeText) { m_bHadShapeText = bHadShapeText; } + bool getHadShapeText() const { return m_bHadShapeText; } + void setInBackground(bool bInBackground) { m_bInBackground = bInBackground; } + bool getInBackground() const { return m_bInBackground; } + void setInListpicture(bool bInListpicture) { m_bInListpicture = bInListpicture; } + bool getInListpicture() const { return m_bInListpicture; } + void setCurrentBuffer(RTFBuffer_t* pCurrentBuffer) { m_pCurrentBuffer = pCurrentBuffer; } + RTFBuffer_t* getCurrentBuffer() const { return m_pCurrentBuffer; } + void setCurrentListOverrideIndex(int nCurrentListOverrideIndex) + { + m_nCurrentListOverrideIndex = nCurrentListOverrideIndex; + } + int getCurrentListOverrideIndex() const { return m_nCurrentListOverrideIndex; } + void setCurrentListIndex(int nCurrentListIndex) { m_nCurrentListIndex = nCurrentListIndex; } + int getCurrentListIndex() const { return m_nCurrentListIndex; } + void setCurrentCharacterStyleIndex(int nCurrentCharacterStyleIndex) + { + m_nCurrentCharacterStyleIndex = nCurrentCharacterStyleIndex; + } + int getCurrentCharacterStyleIndex() const { return m_nCurrentCharacterStyleIndex; } + void setCurrentStyleIndex(int nCurrentStyleIndex) { m_nCurrentStyleIndex = nCurrentStyleIndex; } + int getCurrentStyleIndex() const { return m_nCurrentStyleIndex; } + void setCurrentDestinationText(OUStringBuffer* pDestinationText) + { + m_pCurrentDestinationText = pDestinationText; + } + OUStringBuffer* getCurrentDestinationText() const { return m_pCurrentDestinationText; } + OUStringBuffer& getDestinationText() { return m_aDestinationText; } + void setMinute(sal_uInt16 nMinute) { m_nMinute = nMinute; } + sal_uInt16 getMinute() const { return m_nMinute; } + void setHour(sal_uInt16 nHour) { m_nHour = nHour; } + sal_uInt16 getHour() const { return m_nHour; } + void setDay(sal_uInt16 nDay) { m_nDay = nDay; } + sal_uInt16 getDay() const { return m_nDay; } + void setMonth(sal_uInt16 nMonth) { m_nMonth = nMonth; } + sal_uInt16 getMonth() const { return m_nMonth; } + void setYear(sal_uInt16 nYear) { m_nYear = nYear; } + sal_uInt16 getYear() const { return m_nYear; } + void setRunType(RunType eRunType) { m_eRunType = eRunType; } + RunType getRunType() const { return m_eRunType; } + RTFFrame& getFrame() { return m_aFrame; } + RTFDrawingObject& getDrawingObject() { return m_aDrawingObject; } + RTFShape& getShape() { return m_aShape; } + RTFPicture& getPicture() { return m_aPicture; } + void setLevelNumbersValid(bool bLevelNumbersValid) + { + m_bLevelNumbersValid = bLevelNumbersValid; + } + bool getLevelNumbersValid() const { return m_bLevelNumbersValid; } + std::vector<sal_Int32>& getLevelNumbers() { return m_aLevelNumbers; } + RTFSprms& getListLevelEntries() { return m_aListLevelEntries; } + int& getListLevelNum() { return m_nListLevelNum; } + void setBinaryToRead(int nBinaryToRead) { m_nBinaryToRead = nBinaryToRead; } + int getBinaryToRead() const { return m_nBinaryToRead; } + int& getCharsToSkip() { return m_nCharsToSkip; } + void setUc(int nUc) { m_nUc = nUc; } + int getUc() const { return m_nUc; } + void setCurrentEncoding(rtl_TextEncoding nCurrentEncoding) + { + m_nCurrentEncoding = nCurrentEncoding; + } + rtl_TextEncoding getCurrentEncoding() const { return m_nCurrentEncoding; } + RTFColorTableEntry& getCurrentColor() { return m_aCurrentColor; } + RTFSprms& getTabAttributes() { return m_aTabAttributes; } + RTFSprms& getTableCellAttributes() { return m_aTableCellAttributes; } + RTFSprms& getTableCellSprms() { return m_aTableCellSprms; } + RTFSprms& getTableRowAttributes() { return m_aTableRowAttributes; } + RTFSprms& getTableRowSprms() { return m_aTableRowSprms; } + RTFSprms& getSectionAttributes() { return m_aSectionAttributes; } + RTFSprms& getSectionSprms() { return m_aSectionSprms; } + RTFSprms& getParagraphAttributes() { return m_aParagraphAttributes; } + RTFSprms& getParagraphSprms() { return m_aParagraphSprms; } + RTFSprms& getCharacterAttributes() { return m_aCharacterAttributes; } + RTFSprms& getCharacterSprms() { return m_aCharacterSprms; } + RTFSprms& getTableAttributes() { return m_aTableAttributes; } + RTFSprms& getTableSprms() { return m_aTableSprms; } + void setBorderState(RTFBorderState nBorderState) { m_nBorderState = nBorderState; } + RTFBorderState getBorderState() const { return m_nBorderState; } + void setFieldStatus(RTFFieldStatus eFieldStatus) { m_eFieldStatus = eFieldStatus; } + RTFFieldStatus getFieldStatus() const { return m_eFieldStatus; } + void setFieldLocked(bool bSet) { m_bFieldLocked = bSet; } + bool isFieldLocked() const { return m_bFieldLocked; } + void setDestination(Destination eDestination) { m_eDestination = eDestination; } + Destination getDestination() const { return m_eDestination; } + void setInternalState(RTFInternalState nInternalState) { m_nInternalState = nInternalState; } + RTFInternalState getInternalState() const { return m_nInternalState; } + RTFDocumentImpl* getDocumentImpl() { return m_pDocumentImpl; } + OUString getDocVar() { return m_aDocVar; } + void appendDocVar(OUString& aDocVar) { m_aDocVar += aDocVar; }; + OUString getDocVarName() { return m_aDocVarName; } + void setDocVarName(OUString& aDocVarName) { m_aDocVarName = aDocVarName; } + void clearDocVarName() { m_aDocVarName = ""; } + +private: + RTFDocumentImpl* m_pDocumentImpl; + RTFInternalState m_nInternalState; + Destination m_eDestination; + RTFFieldStatus m_eFieldStatus; + bool m_bFieldLocked; + RTFBorderState m_nBorderState; + // font table, stylesheet table + RTFSprms m_aTableSprms; + RTFSprms m_aTableAttributes; + // reset by plain + RTFSprms m_aCharacterSprms; + RTFSprms m_aCharacterAttributes; + // reset by pard + RTFSprms m_aParagraphSprms; + RTFSprms m_aParagraphAttributes; + // reset by sectd + RTFSprms m_aSectionSprms; + RTFSprms m_aSectionAttributes; + // reset by trowd + RTFSprms m_aTableRowSprms; + RTFSprms m_aTableRowAttributes; + // reset by cellx + RTFSprms m_aTableCellSprms; + RTFSprms m_aTableCellAttributes; + // reset by tx + RTFSprms m_aTabAttributes; + + RTFColorTableEntry m_aCurrentColor; + + rtl_TextEncoding m_nCurrentEncoding; + + /// Current \uc value. + int m_nUc; + /// Characters to skip, set to nUc by \u. + int m_nCharsToSkip; + /// Characters to read, once in binary mode. + int m_nBinaryToRead; + + /// Next list level index to use when parsing list table. + int m_nListLevelNum; + /// List level entries, which will form a list entry later. + RTFSprms m_aListLevelEntries; + /// List of character positions in leveltext to replace. + std::vector<sal_Int32> m_aLevelNumbers; + /// If aLevelNumbers should be read at all. + bool m_bLevelNumbersValid; + + RTFPicture m_aPicture; + RTFShape m_aShape; + RTFDrawingObject m_aDrawingObject; + RTFFrame m_aFrame; + + RunType m_eRunType; + + // Info group. + sal_Int16 m_nYear; + sal_uInt16 m_nMonth; + sal_uInt16 m_nDay; + sal_uInt16 m_nHour; + sal_uInt16 m_nMinute; + + /// Text from special destinations. + OUStringBuffer m_aDestinationText{ 512 }; + /// point to the buffer of the current destination + OUStringBuffer* m_pCurrentDestinationText; + + /// Index of the current style. + int m_nCurrentStyleIndex; + /// Index of the current character style. + int m_nCurrentCharacterStyleIndex; + /// Current listid, points to a listtable entry. + int m_nCurrentListIndex = -1; + /// Current ls, points to a listoverridetable entry. + int m_nCurrentListOverrideIndex = -1; + + /// Points to the active buffer, if there is one. + RTFBuffer_t* m_pCurrentBuffer; + + /// If we're inside a \listpicture group. + bool m_bInListpicture; + + /// If we're inside a \background group. + bool m_bInBackground; + + bool m_bHadShapeText; + bool m_bInShapeGroup; ///< If we're inside a \shpgrp group. + bool m_bInShape; ///< If we're inside a \shp group. + bool m_bCreatedShapeGroup; ///< A GroupShape was created and pushed to the parent stack. + bool m_bStartedTrackchange; ///< Track change is started, need to end it before popping. + + /// User-defined property: key name. + OUString m_aPropName; + /// User-defined property: value type. + css::uno::Type m_aPropType; + + /// Width of invisible cell at the end of the row. + int m_nTableRowWidthAfter; + + /// For importing document variables which are not referenced in the document + OUString m_aDocVar; + OUString m_aDocVarName; +}; + +/// An RTF stack is similar to std::stack, except that it has an operator[]. +struct RTFStack +{ +private: + std::deque<RTFParserState> m_Impl; + +public: + RTFParserState& top() + { + if (m_Impl.empty()) + throw css::io::WrongFormatException( + "Parser state is empty! Invalid usage of destination braces in RTF?", nullptr); + return m_Impl.back(); + } + void pop() + { + if (m_Impl.empty()) + throw css::io::WrongFormatException( + "Parser state is empty! Invalid usage of destination braces in RTF?", nullptr); + return m_Impl.pop_back(); + } + void push(RTFParserState const& rState) { return m_Impl.push_back(rState); } + bool empty() const { return m_Impl.empty(); } + size_t size() const { return m_Impl.size(); } + const RTFParserState& operator[](size_t nIndex) const { return m_Impl[nIndex]; } + RTFParserState& operator[](size_t nIndex) { return m_Impl[nIndex]; } +}; + +void putBorderProperty(RTFStack& aStates, Id nId, const RTFValue::Pointer_t& pValue); +void putNestedSprm(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, + RTFOverwrite eOverwrite = RTFOverwrite::NO_APPEND); +Id getParagraphBorder(sal_uInt32 nIndex); +void putNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId, const RTFValue::Pointer_t& pValue, + RTFOverwrite eOverwrite = RTFOverwrite::YES, bool bAttribute = true); +bool eraseNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId); + +/// Looks up the nParent then the nested nId attribute in rSprms. +RTFValue::Pointer_t getNestedAttribute(RTFSprms& rSprms, Id nParent, Id nId); + +/// Looks up the nParent then the nested nId sprm in rSprms. +RTFValue::Pointer_t getNestedSprm(RTFSprms& rSprms, Id nParent, Id nId); + +/// Checks if rName is contained at least once in rProperties as a key. +bool findPropertyName(const std::vector<css::beans::PropertyValue>& rProperties, + const OUString& rName); +RTFSprms& getLastAttributes(RTFSprms& rSprms, Id nId); +OString DTTM22OString(tools::Long nDTTM); + +/// Implementation of the RTFDocument interface. +class RTFDocumentImpl : public RTFDocument, public RTFListener +{ +public: + using Pointer_t = tools::SvRef<RTFDocumentImpl>; + RTFDocumentImpl(css::uno::Reference<css::uno::XComponentContext> const& xContext, + css::uno::Reference<css::io::XInputStream> const& xInputStream, + css::uno::Reference<css::lang::XComponent> const& xDstDoc, + css::uno::Reference<css::frame::XFrame> const& xFrame, + css::uno::Reference<css::task::XStatusIndicator> const& xStatusIndicator, + const utl::MediaDescriptor& rMediaDescriptor); + ~RTFDocumentImpl() override; + + // RTFDocument + void resolve(Stream& rMapper) override; + + // RTFListener + RTFError dispatchDestination(RTFKeyword nKeyword) override; + RTFError dispatchFlag(RTFKeyword nKeyword) override; + RTFError dispatchSymbol(RTFKeyword nKeyword) override; + RTFError dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) override; + RTFError dispatchValue(RTFKeyword nKeyword, int nParam) override; + bool dispatchTableSprmValue(RTFKeyword nKeyword, int nParam); + bool dispatchCharacterSprmValue(RTFKeyword nKeyword, int nParam); + bool dispatchCharacterAttributeValue(RTFKeyword nKeyword, int nParam); + bool dispatchParagraphSprmValue(RTFKeyword nKeyword, int nParam); + bool dispatchInfoValue(RTFKeyword nKeyword, int nParam); + bool dispatchFrameValue(RTFKeyword nKeyword, int nParam); + bool dispatchTableValue(RTFKeyword nKeyword, int nParam); + RTFError resolveChars(char ch) override; + RTFError pushState() override; + RTFError beforePopState(RTFParserState& rState); + RTFError popState() override; + void afterPopState(RTFParserState& rState); + Destination getDestination() override; + void setDestination(Destination eDestination) override; + RTFInternalState getInternalState() override; + void setInternalState(RTFInternalState nInternalState) override; + bool getSkipUnknown() override; + void setSkipUnknown(bool bSkipUnknown) override; + void finishSubstream() override; + bool isSubstream() const override; + + Stream& Mapper() { return *m_pMapperStream; } + void setSuperstream(RTFDocumentImpl* pSuperstream); + const css::uno::Reference<css::lang::XMultiServiceFactory>& getModelFactory() const + { + return m_xModelFactory; + } + bool isInBackground(); + void setDestinationText(std::u16string_view rString); + /// Resolve a picture: If not inline, then anchored. + void resolvePict(bool bInline, css::uno::Reference<css::drawing::XShape> const& rShape); + + /// If this is the first run of the document, starts the initial paragraph. + void checkFirstRun(); + /// Send NS_ooxml::LN_settings_settings to dmapper. + void outputSettingsTable(); + /// If the initial paragraph is started. + bool getFirstRun() const { return m_bFirstRun; } + /// If we need to add a dummy paragraph before a section break. + void setNeedPar(bool bNeedPar); + /// Return the dmapper index of an RTF index for fonts. + int getFontIndex(int nIndex); + /// Return the name of the font, based on a dmapper index. + OUString getFontName(int nIndex); + /// Return the style name of an RTF style index. + OUString getStyleName(int nIndex); + /// Return the style type of an RTF style index. + Id getStyleType(int nIndex); + /// Return the encoding associated with a font index. + rtl_TextEncoding getEncoding(int nFontIndex); + /// Get the default parser state. + RTFParserState& getDefaultState(); + oox::GraphicHelper& getGraphicHelper(); + /// Are we inside the stylesheet table? + bool isStyleSheetImport(); + /// Resets m_aStates.top().aFrame. + void resetFrame(); + /// Buffers properties to be sent later. + void bufferProperties(RTFBuffer_t& rBuffer, const RTFValue::Pointer_t& pValue, + const tools::SvRef<TableRowBuffer>& pTableProperties); + /// implement non-obvious RTF specific style inheritance + RTFReferenceTable::Entries_t deduplicateStyleTable(); + +private: + SvStream& Strm(); + Color getColorTable(sal_uInt32 nIndex); + writerfilter::Reference<Properties>::Pointer_t createStyleProperties(); + void resetSprms(); + void resetAttributes(); + void resolveSubstream(std::size_t nPos, Id nId); + void resolveSubstream(std::size_t nPos, Id nId, OUString const& rIgnoreFirst); + + void text(OUString& rString); + // Sends a single character to dmapper, taking care of buffering. + void singleChar(sal_uInt8 nValue, bool bRunProps = false); + // Sends run properties to dmapper, taking care of buffering. + void runProps(); + void runBreak(); + void parBreak(); + void tableBreak(); + writerfilter::Reference<Properties>::Pointer_t + getProperties(const RTFSprms& rAttributes, RTFSprms const& rSprms, Id nStyleType); + void checkNeedPap(); + void handleFontTableEntry(); + void sectBreak(bool bFinal = false); + void prepareProperties(RTFParserState& rState, + writerfilter::Reference<Properties>::Pointer_t& o_rpParagraphProperties, + writerfilter::Reference<Properties>::Pointer_t& o_rpFrameProperties, + writerfilter::Reference<Properties>::Pointer_t& o_rpTableRowProperties, + int nCells, int nCurrentCellX); + /// Send the passed properties to dmapper. + void sendProperties(writerfilter::Reference<Properties>::Pointer_t const& pParagraphProperties, + writerfilter::Reference<Properties>::Pointer_t const& pFrameProperties, + writerfilter::Reference<Properties>::Pointer_t const& pTableRowProperties); + void replayRowBuffer(RTFBuffer_t& rBuffer, ::std::deque<RTFSprms>& rCellsSrpms, + ::std::deque<RTFSprms>& rCellsAttributes, int nCells); + void replayBuffer(RTFBuffer_t& rBuffer, RTFSprms* pSprms, RTFSprms const* pAttributes); + /// If we have some unicode or hex characters to send. + void checkUnicode(bool bUnicode, bool bHex); + /// If we need a final section break at the end of the document. + void setNeedSect(bool bNeedSect); + void resetTableRowProperties(); + void backupTableRowProperties(); + void restoreTableRowProperties(); + /// Turns the destination text into an input stream of the current OLE attributes. + RTFError handleEmbeddedObject(); + + css::uno::Reference<css::uno::XComponentContext> const& m_xContext; + css::uno::Reference<css::io::XInputStream> const& m_xInputStream; + css::uno::Reference<css::lang::XComponent> const& m_xDstDoc; + css::uno::Reference<css::frame::XFrame> const& m_xFrame; + css::uno::Reference<css::task::XStatusIndicator> const& m_xStatusIndicator; + css::uno::Reference<css::lang::XMultiServiceFactory> m_xModelFactory; + css::uno::Reference<css::document::XDocumentProperties> m_xDocumentProperties; + std::unique_ptr<SvStream> m_pInStream; + Stream* m_pMapperStream; + tools::SvRef<RTFSdrImport> m_pSdrImport; + tools::SvRef<RTFTokenizer> m_pTokenizer; + RTFStack m_aStates; + /// Read by RTF_PARD. + RTFParserState m_aDefaultState; + bool m_bSkipUnknown; + /// Font index <-> encoding map, *not* part of the parser state + std::map<int, rtl_TextEncoding> m_aFontEncodings; + /// Font index <-> name map. + std::map<int, OUString> m_aFontNames; + /// Maps the non-continuous font indexes to the continuous dmapper indexes. + std::vector<int> m_aFontIndexes; + /// Maps style indexes to style names. + std::map<int, OUString> m_aStyleNames; + /// Maps style indexes to style types. + std::map<int, Id> m_aStyleTypes; + /// Color index <-> RGB color value map + std::vector<Color> m_aColorTable; + /// to start initial paragraph / section after font/style tables + bool m_bFirstRun; + /// except in the case of tables in initial multicolumn section (global for assertion) + bool m_bFirstRunException; + /// If paragraph properties should be emitted on next run. + bool m_bNeedPap; + /// If we need to emit a CR at the end of substream. + bool m_bNeedCr; + /// Original value of m_bNeedCr -- saved/restored before/after textframes. + bool m_bNeedCrOrig; + bool m_bNeedPar; + /// If set, an empty paragraph will be added at the end of the document. + bool m_bNeedFinalPar; + /// The list table and list override table combined. + RTFSprms m_aListTableSprms; + /// Maps between listoverridetable and listtable indexes. + std::map<int, int> m_aListOverrideTable; + /// Maps listtable indexes to listtable entries. + std::map<int, RTFValue::Pointer_t> m_aListTable; + /// Index of the current list level in a list table entry. + int m_nListLevel = -1; + /// Maps List level indexes to removed values in the current list entry. + std::map<int, int> m_aInvalidListLevelFirstIndents; + /// Maps list table indexes to levels (and their removed values). + std::map<int, std::map<int, int>> m_aInvalidListTableFirstIndents; + /// The settings table attributes. + RTFSprms m_aSettingsTableAttributes; + /// The settings table sprms. + RTFSprms m_aSettingsTableSprms; + + std::shared_ptr<oox::GraphicHelper> m_pGraphicHelper; + + /// cell props buffer for nested tables, reset by \nestrow + /// the \nesttableprops is a destination and must follow the + /// nested cells, so it should be sufficient to store the + /// currently active one, no need for a stack of them + int m_nNestedCells; + std::deque<RTFSprms> m_aNestedTableCellsSprms; + std::deque<RTFSprms> m_aNestedTableCellsAttributes; + /// cell props buffer for top-level table, reset by \row + int m_nTopLevelCells; + std::deque<RTFSprms> m_aTopLevelTableCellsSprms; + std::deque<RTFSprms> m_aTopLevelTableCellsAttributes; + /// backup of top-level props, to support inheriting cell props + int m_nInheritingCells; + std::deque<RTFSprms> m_aTableInheritingCellsSprms; + std::deque<RTFSprms> m_aTableInheritingCellsAttributes; + + // Left row margin (for nested and top-level rows) + int m_nNestedTRLeft; + int m_nTopLevelTRLeft; + + /// Current cellx value (nested table) + int m_nNestedCurrentCellX; + /// Current cellx value (top-level table) + int m_nTopLevelCurrentCellX; + + // Backup of what \trowd clears, to work around invalid input. + RTFSprms m_aBackupTableRowSprms; + RTFSprms m_aBackupTableRowAttributes; + int m_nBackupTopLevelCurrentCellX; + + /// Buffered table cells, till cell definitions are not reached. + /// for nested table, one buffer per table level + std::deque<RTFBuffer_t> m_aTableBufferStack; + /// Buffered superscript, till footnote is reached (or not). + RTFBuffer_t m_aSuperBuffer; + + /// Superstream of this substream. + RTFDocumentImpl* m_pSuperstream; + /// Type of the stream: header, footer, footnote, etc. + Id m_nStreamType; + std::queue<std::pair<Id, std::size_t>> m_nHeaderFooterPositions; + std::size_t m_nGroupStartPos; + /// Ignore the first occurrence of this text. + OUString m_aIgnoreFirst; + /// Bookmark name <-> index map. + std::map<OUString, int> m_aBookmarks; + /// Revision index <-> author map. + std::map<int, OUString> m_aAuthors; + /// Annotation author of the next annotation. + OUString m_aAuthor; + /// Initials of author of the next annotation. + OUString m_aAuthorInitials; + + RTFSprms m_aFormfieldSprms; + RTFSprms m_aFormfieldAttributes; + RTFFormFieldType m_nFormFieldType; + + /// OLE attributes are attributes of the ooxml:OLEObject_OLEObject sprm. + RTFSprms m_aOLEAttributes; + RTFSprms m_aObjectAttributes; + /** If we are in an object group and if the we use its + * \objdata element. + * (if we don't use the \objdata we use the \result element)*/ + bool m_bObject; + /// If the data for a picture is a binary one, it's stored here. + std::shared_ptr<SvStream> m_pBinaryData; + + RTFReferenceTable::Entries_t m_aFontTableEntries; + int m_nCurrentFontIndex; + /// Used only during font table parsing till we don't know the font name. + int m_nCurrentEncoding; + /// Raw default font index, use getFont() on it to get a real one. + int m_nDefaultFontIndex; + + /// To avoid copying entries between DomainMapper instances it is stored as pointer + std::shared_ptr<RTFReferenceTable::Entries_t> m_pStyleTableEntries; + int m_nCurrentStyleIndex; + bool m_bFormField; + /// For the INCLUDEPICTURE field's argument. + OUString m_aPicturePath; + // Unicode characters are collected here so we don't have to send them one by one. + OUStringBuffer m_aUnicodeBuffer{ 512 }; + /// Same for hex characters. + OStringBuffer m_aHexBuffer{ 512 }; + /// Formula import. + oox::formulaimport::XmlStreamBuilder m_aMathBuffer; + /// Normal text property, that is math italic and math spacing are not applied to the current run. + bool m_bMathNor; + /// If the next continuous section break should be ignored. + bool m_bIgnoreNextContSectBreak; + /// clean up a synthetic page break, see RTF_PAGE + /// if inactive value is -1, otherwise the RTF_SKB* to restore + RTFKeyword m_nResetBreakOnSectBreak; + /// If a section break is needed before the end of the doc (false right after a section break). + bool m_bNeedSect; + /// If aFrame.inFrame() was true in the previous state. + bool m_bWasInFrame; + /// A picture was seen in the current paragraph. + bool m_bHadPicture; + /// The document has multiple sections. + bool m_bHadSect; + /// Max width of the rows in the current table. + int m_nCellxMax; + /// ID of the next \listlevel picture. + int m_nListPictureId; + + /// New document means not pasting into an existing one. + bool m_bIsNewDoc; + /// The media descriptor contains e.g. the base URL of the document. + const utl::MediaDescriptor& m_rMediaDescriptor; + + /// Flags for ensuring that only one header and footer is added per section + bool m_hasRHeader; + bool m_hasFHeader; + bool m_hasRFooter; + bool m_hasFFooter; + + /// Are we after a \cell, but before a \row? + bool m_bAfterCellBeforeRow; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtffly.hxx b/writerfilter/source/rtftok/rtffly.hxx new file mode 100644 index 000000000..b1dec0c77 --- /dev/null +++ b/writerfilter/source/rtftok/rtffly.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/text/VertOrientation.hpp> + +#include <ooxml/resourceids.hxx> +#include <osl/endian.h> + +namespace writerfilter::rtftok +{ +/// Stores the vertical orientation properties of an RTF fly frame. +class RTFVertOrient +{ +public: + explicit RTFVertOrient(sal_uInt16 nValue) + : m_nVal(nValue) + { + } + + sal_uInt16 GetOrient() const { return OSL_LONIBBLE(OSL_LOBYTE(m_nVal)); } + + sal_uInt16 GetRelation() const { return OSL_HINIBBLE(OSL_LOBYTE(m_nVal)); } + + sal_Int32 GetAlign() const + { + sal_Int32 nAlign = 0; + switch (GetOrient()) + { + case css::text::VertOrientation::CENTER: + nAlign = NS_ooxml::LN_Value_doc_ST_YAlign_center; + break; + case css::text::VertOrientation::TOP: + nAlign = NS_ooxml::LN_Value_doc_ST_YAlign_top; + break; + case css::text::VertOrientation::BOTTOM: + nAlign = NS_ooxml::LN_Value_doc_ST_YAlign_bottom; + break; + } + + return nAlign; + } + + sal_Int32 GetAnchor() const + { + sal_Int32 nAnchor = 0; + switch (GetRelation()) + { + case css::text::RelOrientation::FRAME: + nAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_text; + break; + case css::text::RelOrientation::PAGE_FRAME: + nAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_page; + break; + case css::text::RelOrientation::PAGE_PRINT_AREA: + nAnchor = NS_ooxml::LN_Value_doc_ST_VAnchor_margin; + break; + } + + return nAnchor; + } + +private: + sal_uInt16 m_nVal; +}; + +/// Stores the horizontal orientation properties of an RTF fly frame. +class RTFHoriOrient +{ +public: + explicit RTFHoriOrient(sal_uInt16 nValue) + : m_nVal(nValue) + { + } + + sal_uInt16 GetOrient() const { return OSL_LONIBBLE(OSL_LOBYTE(m_nVal)); } + + sal_uInt16 GetRelation() const { return OSL_LONIBBLE(OSL_HIBYTE(m_nVal)); } + + sal_Int32 GetAlign() const + { + sal_Int32 nAlign = 0; + switch (GetOrient()) + { + case css::text::HoriOrientation::CENTER: + nAlign = NS_ooxml::LN_Value_doc_ST_XAlign_center; + break; + case css::text::HoriOrientation::RIGHT: + nAlign = NS_ooxml::LN_Value_doc_ST_XAlign_right; + break; + case css::text::HoriOrientation::LEFT: + nAlign = NS_ooxml::LN_Value_doc_ST_XAlign_left; + break; + case css::text::HoriOrientation::INSIDE: + nAlign = NS_ooxml::LN_Value_doc_ST_XAlign_inside; + break; + case css::text::HoriOrientation::OUTSIDE: + nAlign = NS_ooxml::LN_Value_doc_ST_XAlign_outside; + break; + } + + return nAlign; + } + + sal_Int32 GetAnchor() const + { + sal_Int32 nAnchor = 0; + switch (GetRelation()) + { + case css::text::RelOrientation::FRAME: + nAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_text; + break; + case css::text::RelOrientation::PAGE_FRAME: + nAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_page; + break; + case css::text::RelOrientation::PAGE_PRINT_AREA: + nAnchor = NS_ooxml::LN_Value_doc_ST_HAnchor_margin; + break; + } + + return nAnchor; + } + +private: + sal_uInt16 m_nVal; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtflistener.hxx b/writerfilter/source/rtftok/rtflistener.hxx new file mode 100644 index 000000000..150440afb --- /dev/null +++ b/writerfilter/source/rtftok/rtflistener.hxx @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "rtfcontrolwords.hxx" + +namespace writerfilter::rtftok +{ +enum class RTFInternalState +{ + NORMAL, + BIN, + HEX +}; + +enum class RTFError +{ + OK, + GROUP_UNDER, + GROUP_OVER, + UNEXPECTED_EOF, + HEX_INVALID, + CHAR_OVER, + CLASSIFICATION +}; + +/** + * RTFTokenizer needs a class implementing this interface. While + * RTFTokenizer separates control words (and their arguments) from + * text, the class implementing this interface is expected to map the + * raw RTF tokens to dmapper tokens. + */ +class RTFListener +{ +public: + virtual ~RTFListener() = default; + // Dispatching of control words and characters. + virtual RTFError dispatchDestination(RTFKeyword nKeyword) = 0; + virtual RTFError dispatchFlag(RTFKeyword nKeyword) = 0; + virtual RTFError dispatchSymbol(RTFKeyword nKeyword) = 0; + virtual RTFError dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) = 0; + virtual RTFError dispatchValue(RTFKeyword nKeyword, int nParam) = 0; + virtual RTFError resolveChars(char ch) = 0; + + // State handling. + virtual RTFError pushState() = 0; + virtual RTFError popState() = 0; + + virtual Destination getDestination() = 0; + virtual void setDestination(Destination eDestination) = 0; + virtual RTFInternalState getInternalState() = 0; + virtual void setInternalState(RTFInternalState nInternalState) = 0; + virtual bool getSkipUnknown() = 0; + virtual void setSkipUnknown(bool bSkipUnknown) = 0; + + // Substream handling. + virtual void finishSubstream() = 0; + virtual bool isSubstream() const = 0; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtflookahead.cxx b/writerfilter/source/rtftok/rtflookahead.cxx new file mode 100644 index 000000000..033feacce --- /dev/null +++ b/writerfilter/source/rtftok/rtflookahead.cxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtflookahead.hxx" +#include <com/sun/star/uno/Reference.hxx> +#include <tools/stream.hxx> +#include "rtftokenizer.hxx" + +namespace com::sun::star::task +{ +class XStatusIndicator; +} + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFLookahead::RTFLookahead(SvStream& rStream, sal_uInt64 nGroupStart) + : m_rStream(rStream) + , m_bHasTable(false) + , m_bHasColumns(false) +{ + sal_uInt64 const nPos = m_rStream.Tell(); + m_rStream.Seek(nGroupStart); + uno::Reference<task::XStatusIndicator> xStatusIndicator; + m_pTokenizer = new RTFTokenizer(*this, &m_rStream, xStatusIndicator); + m_pTokenizer->resolveParse(); + m_rStream.Seek(nPos); +} + +RTFLookahead::~RTFLookahead() = default; + +RTFError RTFLookahead::dispatchDestination(RTFKeyword /*nKeyword*/) { return RTFError::OK; } + +RTFError RTFLookahead::dispatchFlag(RTFKeyword nKeyword) +{ + if (nKeyword == RTFKeyword::INTBL) + m_bHasTable = true; + return RTFError::OK; +} + +RTFError RTFLookahead::dispatchSymbol(RTFKeyword /*nKeyword*/) { return RTFError::OK; } + +RTFError RTFLookahead::dispatchToggle(RTFKeyword /*nKeyword*/, bool /*bParam*/, int /*nParam*/) +{ + return RTFError::OK; +} + +RTFError RTFLookahead::dispatchValue(RTFKeyword nKeyword, int nParam) +{ + if (nKeyword == RTFKeyword::COLS && nParam >= 2) + m_bHasColumns = true; + return RTFError::OK; +} + +RTFError RTFLookahead::resolveChars(char ch) +{ + while (!m_rStream.eof() && (ch != '{' && ch != '}' && ch != '\\')) + m_rStream.ReadChar(ch); + if (!m_rStream.eof()) + m_rStream.SeekRel(-1); + return RTFError::OK; +} + +RTFError RTFLookahead::pushState() +{ + m_pTokenizer->pushGroup(); + return RTFError::OK; +} + +RTFError RTFLookahead::popState() +{ + m_pTokenizer->popGroup(); + return RTFError::OK; +} + +Destination RTFLookahead::getDestination() { return Destination::NORMAL; } + +void RTFLookahead::setDestination(Destination /*eDestination*/) {} + +RTFInternalState RTFLookahead::getInternalState() { return RTFInternalState::NORMAL; } + +void RTFLookahead::setInternalState(RTFInternalState /*nInternalState*/) {} + +bool RTFLookahead::getSkipUnknown() { return false; } + +void RTFLookahead::setSkipUnknown(bool /*bSkipUnknown*/) {} + +void RTFLookahead::finishSubstream() {} + +bool RTFLookahead::isSubstream() const { return false; } + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtflookahead.hxx b/writerfilter/source/rtftok/rtflookahead.hxx new file mode 100644 index 000000000..9ec213f62 --- /dev/null +++ b/writerfilter/source/rtftok/rtflookahead.hxx @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <sal/types.h> +#include <tools/ref.hxx> +#include "rtflistener.hxx" + +class SvStream; + +namespace writerfilter::rtftok +{ +class RTFTokenizer; +/** + * This acts like an importer, but used for looking ahead, e.g. to + * determine if the current group contains a table, etc. + */ +class RTFLookahead : public RTFListener +{ +public: + RTFLookahead(SvStream& rStream, sal_uInt64 nGroupStart); + ~RTFLookahead() override; + RTFError dispatchDestination(RTFKeyword nKeyword) override; + RTFError dispatchFlag(RTFKeyword nKeyword) override; + RTFError dispatchSymbol(RTFKeyword nKeyword) override; + RTFError dispatchToggle(RTFKeyword nKeyword, bool bParam, int nParam) override; + RTFError dispatchValue(RTFKeyword nKeyword, int nParam) override; + RTFError resolveChars(char ch) override; + RTFError pushState() override; + RTFError popState() override; + Destination getDestination() override; + void setDestination(Destination eDestination) override; + RTFInternalState getInternalState() override; + void setInternalState(RTFInternalState nInternalState) override; + bool getSkipUnknown() override; + void setSkipUnknown(bool bSkipUnknown) override; + void finishSubstream() override; + bool isSubstream() const override; + bool hasTable() const { return m_bHasTable; } + bool hasColumns() const { return m_bHasColumns; } + +private: + tools::SvRef<RTFTokenizer> m_pTokenizer; + SvStream& m_rStream; + bool m_bHasTable; + bool m_bHasColumns; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfreferenceproperties.cxx b/writerfilter/source/rtftok/rtfreferenceproperties.cxx new file mode 100644 index 000000000..d32557bc7 --- /dev/null +++ b/writerfilter/source/rtftok/rtfreferenceproperties.cxx @@ -0,0 +1,40 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfreferenceproperties.hxx" + +namespace writerfilter::rtftok +{ +RTFReferenceProperties::RTFReferenceProperties(RTFSprms aAttributes, RTFSprms aSprms) + : m_aAttributes(std::move(aAttributes)) + , m_aSprms(std::move(aSprms)) +{ +} + +RTFReferenceProperties::RTFReferenceProperties(RTFSprms aAttributes) + : m_aAttributes(std::move(aAttributes)) +{ +} + +RTFReferenceProperties::~RTFReferenceProperties() = default; + +void RTFReferenceProperties::resolve(Properties& rHandler) +{ + for (auto& rAttribute : m_aAttributes) + rHandler.attribute(rAttribute.first, *rAttribute.second); + for (auto& rSprm : m_aSprms) + { + RTFSprm aSprm(rSprm.first, rSprm.second); + rHandler.sprm(aSprm); + } +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfreferenceproperties.hxx b/writerfilter/source/rtftok/rtfreferenceproperties.hxx new file mode 100644 index 000000000..6a5e5618b --- /dev/null +++ b/writerfilter/source/rtftok/rtfreferenceproperties.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "rtfsprm.hxx" + +namespace writerfilter::rtftok +{ +/// Sends RTFSprm instances to DomainMapper. +class RTFReferenceProperties : public writerfilter::Reference<Properties> +{ +public: + RTFReferenceProperties(RTFSprms aAttributes, RTFSprms aSprms); + explicit RTFReferenceProperties(RTFSprms aAttributes); + ~RTFReferenceProperties() override; + void resolve(Properties& rHandler) override; + RTFSprms& getAttributes() { return m_aAttributes; } + RTFSprms& getSprms() { return m_aSprms; } + +private: + RTFSprms m_aAttributes; + RTFSprms m_aSprms; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfreferencetable.cxx b/writerfilter/source/rtftok/rtfreferencetable.cxx new file mode 100644 index 000000000..1a70a93d9 --- /dev/null +++ b/writerfilter/source/rtftok/rtfreferencetable.cxx @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfreferencetable.hxx" + +namespace writerfilter::rtftok +{ +RTFReferenceTable::RTFReferenceTable(Entries_t aEntries) + : m_aEntries(std::move(aEntries)) +{ +} + +RTFReferenceTable::~RTFReferenceTable() = default; + +void RTFReferenceTable::resolve(Table& rHandler) +{ + for (const auto& rEntry : m_aEntries) + rHandler.entry(rEntry.first, rEntry.second); +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfreferencetable.hxx b/writerfilter/source/rtftok/rtfreferencetable.hxx new file mode 100644 index 000000000..76cbaacf2 --- /dev/null +++ b/writerfilter/source/rtftok/rtfreferencetable.hxx @@ -0,0 +1,32 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <map> +#include <dmapper/resourcemodel.hxx> + +namespace writerfilter::rtftok +{ +/// Sends tables (e.g. font table) to the domain mapper. +class RTFReferenceTable : public writerfilter::Reference<Table> +{ +public: + using Entries_t = std::map<int, writerfilter::Reference<Properties>::Pointer_t>; + using Entry_t = std::pair<int, writerfilter::Reference<Properties>::Pointer_t>; + explicit RTFReferenceTable(Entries_t aEntries); + ~RTFReferenceTable() override; + void resolve(Table& rHandler) override; + +private: + Entries_t m_aEntries; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfsdrimport.cxx b/writerfilter/source/rtftok/rtfsdrimport.cxx new file mode 100644 index 000000000..e928377f9 --- /dev/null +++ b/writerfilter/source/rtftok/rtfsdrimport.cxx @@ -0,0 +1,1182 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfsdrimport.hxx" +#include <cmath> +#include <optional> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/drawing/FillStyle.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeParameterPair.hpp> +#include <com/sun/star/drawing/XEnhancedCustomShapeDefaulter.hpp> +#include <com/sun/star/drawing/XDrawPageSupplier.hpp> +#include <com/sun/star/drawing/LineStyle.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeSegment.hpp> +#include <com/sun/star/drawing/EnhancedCustomShapeSegmentCommand.hpp> +#include <com/sun/star/drawing/ColorMode.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/table/BorderLine2.hpp> +#include <com/sun/star/text/HoriOrientation.hpp> +#include <com/sun/star/text/RelOrientation.hpp> +#include <com/sun/star/text/SizeType.hpp> +#include <com/sun/star/text/VertOrientation.hpp> +#include <com/sun/star/text/WrapTextMode.hpp> +#include <com/sun/star/text/WritingMode.hpp> +#include <com/sun/star/text/WritingMode2.hpp> +#include <com/sun/star/text/TextContentAnchorType.hpp> +#include <com/sun/star/text/XTextRange.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <ooxml/resourceids.hxx> +#include <filter/msfilter/escherex.hxx> +#include <filter/msfilter/util.hxx> +#include <filter/msfilter/rtfutil.hxx> +#include <sal/log.hxx> +#include <svx/svdtrans.hxx> +#include <comphelper/sequence.hxx> +#include <comphelper/propertysequence.hxx> +#include "rtfreferenceproperties.hxx" +#include <oox/vml/vmlformatting.hxx> +#include <oox/helper/modelobjecthelper.hxx> +#include <oox/drawingml/shapepropertymap.hxx> +#include <oox/helper/propertyset.hxx> +#include <basegfx/matrix/b2dhommatrix.hxx> +#include <svx/svdobj.hxx> +#include <tools/UnitConversion.hxx> +#include <o3tl/string_view.hxx> + +#include <dmapper/GraphicZOrderHelper.hxx> +#include "rtfdocumentimpl.hxx" + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFSdrImport::RTFSdrImport(RTFDocumentImpl& rDocument, + uno::Reference<lang::XComponent> const& xDstDoc) + : m_rImport(rDocument) + , m_bTextFrame(false) + , m_bTextGraphicObject(false) + , m_bFakePict(false) +{ + uno::Reference<drawing::XDrawPageSupplier> xDrawings(xDstDoc, uno::UNO_QUERY); + if (xDrawings.is()) + m_aParents.push(xDrawings->getDrawPage()); + m_aGraphicZOrderHelpers.push(writerfilter::dmapper::GraphicZOrderHelper()); +} + +RTFSdrImport::~RTFSdrImport() +{ + if (!m_aGraphicZOrderHelpers.empty()) + m_aGraphicZOrderHelpers.pop(); + if (!m_aParents.empty()) + m_aParents.pop(); +} + +void RTFSdrImport::createShape(const OUString& rService, uno::Reference<drawing::XShape>& xShape, + uno::Reference<beans::XPropertySet>& xPropertySet) +{ + if (m_rImport.getModelFactory().is()) + xShape.set(m_rImport.getModelFactory()->createInstance(rService), uno::UNO_QUERY); + xPropertySet.set(xShape, uno::UNO_QUERY); +} + +std::vector<beans::PropertyValue> RTFSdrImport::getTextFrameDefaults(bool bNew) +{ + std::vector<beans::PropertyValue> aRet; + beans::PropertyValue aPropertyValue; + + aPropertyValue.Name = "HoriOrient"; + aPropertyValue.Value <<= text::HoriOrientation::NONE; + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "HoriOrientRelation"; + aPropertyValue.Value <<= text::RelOrientation::FRAME; + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "VertOrient"; + aPropertyValue.Value <<= text::VertOrientation::NONE; + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "VertOrientRelation"; + aPropertyValue.Value <<= text::RelOrientation::FRAME; + aRet.push_back(aPropertyValue); + if (!bNew) + { + aPropertyValue.Name = "BackColorTransparency"; + aPropertyValue.Value <<= sal_Int32(100); + aRet.push_back(aPropertyValue); + } + // See the spec, new-style frame default margins are specified in EMUs. + aPropertyValue.Name = "LeftBorderDistance"; + aPropertyValue.Value <<= sal_Int32(bNew ? (91440 / 360) : 0); + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "RightBorderDistance"; + aPropertyValue.Value <<= sal_Int32(bNew ? (91440 / 360) : 0); + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "TopBorderDistance"; + aPropertyValue.Value <<= sal_Int32(bNew ? (45720 / 360) : 0); + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "BottomBorderDistance"; + aPropertyValue.Value <<= sal_Int32(bNew ? (45720 / 360) : 0); + aRet.push_back(aPropertyValue); + aPropertyValue.Name = "SizeType"; + aPropertyValue.Value <<= text::SizeType::FIX; + aRet.push_back(aPropertyValue); + return aRet; +} + +void RTFSdrImport::pushParent(uno::Reference<drawing::XShapes> const& xParent) +{ + m_aParents.push(xParent); + m_aGraphicZOrderHelpers.push(writerfilter::dmapper::GraphicZOrderHelper()); +} + +void RTFSdrImport::popParent() +{ + if (!m_aGraphicZOrderHelpers.empty()) + m_aGraphicZOrderHelpers.pop(); + if (!m_aParents.empty()) + m_aParents.pop(); +} + +void RTFSdrImport::resolveDhgt(uno::Reference<beans::XPropertySet> const& xPropertySet, + sal_Int32 const nZOrder, bool const bOldStyle) +{ + if (!m_aGraphicZOrderHelpers.empty()) + { + writerfilter::dmapper::GraphicZOrderHelper& rHelper = m_aGraphicZOrderHelpers.top(); + xPropertySet->setPropertyValue("ZOrder", uno::Any(rHelper.findZOrder(nZOrder, bOldStyle))); + rHelper.addItem(xPropertySet, nZOrder); + } +} + +void RTFSdrImport::resolveLineColorAndWidth(bool bTextFrame, + const uno::Reference<beans::XPropertySet>& xPropertySet, + uno::Any const& rLineColor, uno::Any const& rLineWidth) +{ + if (!bTextFrame) + { + xPropertySet->setPropertyValue("LineColor", rLineColor); + xPropertySet->setPropertyValue("LineWidth", rLineWidth); + } + else + { + static const char* aBorders[] + = { "TopBorder", "LeftBorder", "BottomBorder", "RightBorder" }; + for (const char* pBorder : aBorders) + { + auto aBorderLine = xPropertySet->getPropertyValue(OUString::createFromAscii(pBorder)) + .get<table::BorderLine2>(); + if (rLineColor.hasValue()) + aBorderLine.Color = rLineColor.get<sal_Int32>(); + if (rLineWidth.hasValue()) + aBorderLine.LineWidth = rLineWidth.get<sal_Int32>(); + xPropertySet->setPropertyValue(OUString::createFromAscii(pBorder), + uno::Any(aBorderLine)); + } + } +} + +void RTFSdrImport::resolveFLine(uno::Reference<beans::XPropertySet> const& xPropertySet, + sal_Int32 const nFLine) +{ + if (nFLine == 0) + xPropertySet->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_NONE)); + else + xPropertySet->setPropertyValue("LineStyle", uno::Any(drawing::LineStyle_SOLID)); +} + +void RTFSdrImport::applyProperty(uno::Reference<drawing::XShape> const& xShape, + std::u16string_view aKey, std::u16string_view aValue) const +{ + uno::Reference<beans::XPropertySet> xPropertySet(xShape, uno::UNO_QUERY); + sal_Int16 nHoriOrient = 0; + sal_Int16 nVertOrient = 0; + std::optional<bool> obFitShapeToText; + bool bFilled = true; + + if (aKey == u"posh") + { + switch (o3tl::toInt32(aValue)) + { + case 1: + nHoriOrient = text::HoriOrientation::LEFT; + break; + case 2: + nHoriOrient = text::HoriOrientation::CENTER; + break; + case 3: + nHoriOrient = text::HoriOrientation::RIGHT; + break; + case 4: + nHoriOrient = text::HoriOrientation::INSIDE; + break; + case 5: + nHoriOrient = text::HoriOrientation::OUTSIDE; + break; + default: + break; + } + } + else if (aKey == u"posv") + { + switch (o3tl::toInt32(aValue)) + { + case 1: + nVertOrient = text::VertOrientation::TOP; + break; + case 2: + nVertOrient = text::VertOrientation::CENTER; + break; + case 3: + nVertOrient = text::VertOrientation::BOTTOM; + break; + default: + break; + } + } + else if (aKey == u"fFitShapeToText") + obFitShapeToText = o3tl::toInt32(aValue) == 1; + else if (aKey == u"fFilled") + bFilled = o3tl::toInt32(aValue) == 1; + else if (aKey == u"rotation") + { + // See DffPropertyReader::Fix16ToAngle(): in RTF, positive rotation angles are clockwise, we have them as counter-clockwise. + // Additionally, RTF type is 0..360*2^16, our is 0..360*100. + sal_Int32 nRotation = o3tl::toInt32(aValue) * 100 / RTF_MULTIPLIER; + uno::Reference<lang::XServiceInfo> xServiceInfo(xShape, uno::UNO_QUERY); + if (!xServiceInfo->supportsService("com.sun.star.text.TextFrame")) + xPropertySet->setPropertyValue( + "RotateAngle", uno::Any(NormAngle36000(Degree100(nRotation * -1)).get())); + } + + if (nHoriOrient != 0 && xPropertySet.is()) + xPropertySet->setPropertyValue("HoriOrient", uno::Any(nHoriOrient)); + if (nVertOrient != 0 && xPropertySet.is()) + xPropertySet->setPropertyValue("VertOrient", uno::Any(nVertOrient)); + if (obFitShapeToText.has_value() && xPropertySet.is()) + { + xPropertySet->setPropertyValue( + "SizeType", uno::Any(*obFitShapeToText ? text::SizeType::MIN : text::SizeType::FIX)); + xPropertySet->setPropertyValue("FrameIsAutomaticHeight", uno::Any(*obFitShapeToText)); + } + if (!bFilled && xPropertySet.is()) + { + if (m_bTextFrame) + xPropertySet->setPropertyValue("BackColorTransparency", uno::Any(sal_Int32(100))); + else + xPropertySet->setPropertyValue("FillStyle", uno::Any(drawing::FillStyle_NONE)); + } +} + +int RTFSdrImport::initShape(uno::Reference<drawing::XShape>& o_xShape, + uno::Reference<beans::XPropertySet>& o_xPropSet, bool& o_rIsCustomShape, + RTFShape const& rShape, bool const bClose, + ShapeOrPict const shapeOrPict) +{ + assert(!o_xShape.is()); + assert(!o_xPropSet.is()); + o_rIsCustomShape = false; + m_bFakePict = false; + + // first, find the shape type + int nType = -1; + auto iter = std::find_if(rShape.getProperties().begin(), rShape.getProperties().end(), + [](const std::pair<OUString, OUString>& rProperty) { + return rProperty.first == "shapeType"; + }); + + if (iter == rShape.getProperties().end()) + { + if (SHAPE == shapeOrPict) + { + // The spec doesn't state what is the default for shapeType, + // Word seems to implement it as a rectangle. + nType = ESCHER_ShpInst_Rectangle; + } + else + { + // pict is picture by default but can be a rectangle too fdo#79319 + nType = ESCHER_ShpInst_PictureFrame; + } + } + else + { + nType = iter->second.toInt32(); + if (PICT == shapeOrPict && ESCHER_ShpInst_PictureFrame != nType) + { + m_bFakePict = true; + } + } + + switch (nType) + { + case ESCHER_ShpInst_PictureFrame: + createShape("com.sun.star.drawing.GraphicObjectShape", o_xShape, o_xPropSet); + m_bTextGraphicObject = true; + break; + case ESCHER_ShpInst_Line: + createShape("com.sun.star.drawing.LineShape", o_xShape, o_xPropSet); + break; + case ESCHER_ShpInst_Rectangle: + case ESCHER_ShpInst_TextBox: + // If we're inside a groupshape, can't use text frames. + if (!bClose && m_aParents.size() == 1) + { + createShape("com.sun.star.text.TextFrame", o_xShape, o_xPropSet); + m_bTextFrame = true; + std::vector<beans::PropertyValue> aDefaults = getTextFrameDefaults(true); + for (const beans::PropertyValue& i : aDefaults) + o_xPropSet->setPropertyValue(i.Name, i.Value); + break; + } + [[fallthrough]]; + default: + createShape("com.sun.star.drawing.CustomShape", o_xShape, o_xPropSet); + o_rIsCustomShape = true; + break; + } + + // Defaults + if (o_xPropSet.is() && !m_bTextFrame) + { + o_xPropSet->setPropertyValue( + "FillColor", + uno::Any(sal_uInt32(0xffffff))); // White in Word, kind of blue in Writer. + o_xPropSet->setPropertyValue("VertOrient", uno::Any(text::VertOrientation::NONE)); + } + + return nType; +} + +void RTFSdrImport::resolve(RTFShape& rShape, bool bClose, ShapeOrPict const shapeOrPict) +{ + bool bPib = false; + m_bTextFrame = false; + m_bTextGraphicObject = false; + + uno::Reference<drawing::XShape> xShape; + uno::Reference<beans::XPropertySet> xPropertySet; + uno::Any aAny; + beans::PropertyValue aPropertyValue; + awt::Rectangle aViewBox; + std::vector<beans::PropertyValue> aPath; + // Default line color is black in Word, blue in Writer. + uno::Any aLineColor(COL_BLACK); + // Default line width is 0.75 pt (26 mm100) in Word, 0 in Writer. + uno::Any aLineWidth(sal_Int32(26)); + sal_Int16 eWritingMode = text::WritingMode2::LR_TB; + // Groupshape support + std::optional<sal_Int32> oGroupLeft; + std::optional<sal_Int32> oGroupTop; + std::optional<sal_Int32> oGroupRight; + std::optional<sal_Int32> oGroupBottom; + std::optional<sal_Int32> oRelLeft; + std::optional<sal_Int32> oRelTop; + std::optional<sal_Int32> oRelRight; + std::optional<sal_Int32> oRelBottom; + + // Importing these are not trivial, let the VML import do the hard work. + oox::vml::FillModel aFillModel; // Gradient. + oox::vml::ShadowModel aShadowModel; // Shadow. + + bool bOpaque = true; + + std::optional<sal_Int16> oRelativeWidth; + std::optional<sal_Int16> oRelativeHeight; + sal_Int16 nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME; + sal_Int16 nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME; + std::optional<bool> obRelFlipV; + bool obFlipH(false); + bool obFlipV(false); + + OUString aShapeText = ""; + OUString aFontFamily = ""; + float nFontSize = 1.0; + + sal_Int32 nContrast = 0x10000; + sal_Int16 nBrightness = 0; + + bool bCustom(false); + int const nType = initShape(xShape, xPropertySet, bCustom, rShape, bClose, shapeOrPict); + + for (auto& rProperty : rShape.getProperties()) + { + if (rProperty.first == "shapeType") + { + continue; // ignore: already handled by initShape + } + if (rProperty.first == "wzName") + { + if (m_bTextFrame) + { + uno::Reference<container::XNamed> xNamed(xShape, uno::UNO_QUERY); + xNamed->setName(rProperty.second); + } + else + xPropertySet->setPropertyValue("Name", uno::Any(rProperty.second)); + } + else if (rProperty.first == "wzDescription") + xPropertySet->setPropertyValue("Description", uno::Any(rProperty.second)); + else if (rProperty.first == "gtextUNICODE") + aShapeText = rProperty.second; + else if (rProperty.first == "gtextFont") + aFontFamily = rProperty.second; + else if (rProperty.first == "gtextSize") + { + // RTF size is multiplied by 2^16 + nFontSize = static_cast<float>(rProperty.second.toUInt32()) / RTF_MULTIPLIER; + } + else if (rProperty.first == "pib") + { + m_rImport.setDestinationText(rProperty.second); + bPib = true; + } + else if (rProperty.first == "fillColor" && xPropertySet.is()) + { + aAny <<= msfilter::util::BGRToRGB(rProperty.second.toUInt32()); + if (m_bTextFrame) + xPropertySet->setPropertyValue("BackColor", aAny); + else + xPropertySet->setPropertyValue("FillColor", aAny); + + // fillType will decide, possible it'll be the start color of a gradient. + aFillModel.moColor.set( + "#" + + msfilter::util::ConvertColorOU(Color(ColorTransparency, aAny.get<sal_Int32>()))); + } + else if (rProperty.first == "fillBackColor") + // fillType will decide, possible it'll be the end color of a gradient. + aFillModel.moColor2.set("#" + + msfilter::util::ConvertColorOU( + msfilter::util::BGRToRGB(rProperty.second.toInt32()))); + else if (rProperty.first == "lineColor") + aLineColor <<= msfilter::util::BGRToRGB(rProperty.second.toInt32()); + else if (rProperty.first == "lineBackColor") + ; // Ignore: complementer of lineColor + else if (rProperty.first == "txflTextFlow" && xPropertySet.is()) + { + switch (rProperty.second.toInt32()) + { + case 1: // Top to bottom ASCII font + case 3: // Top to bottom non-ASCII font + eWritingMode = text::WritingMode2::TB_RL; + break; + case 2: // Bottom to top non-ASCII font + eWritingMode = text::WritingMode2::BT_LR; + break; + } + } + else if (rProperty.first == "fLine" && xPropertySet.is()) + resolveFLine(xPropertySet, rProperty.second.toInt32()); + else if (rProperty.first == "fillOpacity" && xPropertySet.is()) + { + int opacity = 100 - (rProperty.second.toInt32()) * 100 / RTF_MULTIPLIER; + xPropertySet->setPropertyValue("FillTransparence", uno::Any(sal_uInt32(opacity))); + } + else if (rProperty.first == "lineWidth") + aLineWidth <<= rProperty.second.toInt32() / 360; + else if (rProperty.first == "pVerticies") + { + std::vector<drawing::EnhancedCustomShapeParameterPair> aCoordinates; + sal_Int32 nSize = 0; // Size of a token (its value is hardwired in the exporter) + sal_Int32 nCount = 0; // Number of tokens + sal_Int32 nCharIndex = 0; // Character index + do + { + std::u16string_view aToken = o3tl::getToken(rProperty.second, 0, ';', nCharIndex); + if (!nSize) + nSize = o3tl::toInt32(aToken); + else if (!nCount) + nCount = o3tl::toInt32(aToken); + else if (!aToken.empty()) + { + // The coordinates are in an (x,y) form. + aToken = aToken.substr(1, aToken.size() - 2); + sal_Int32 nI = 0; + sal_Int32 nX = o3tl::toInt32(o3tl::getToken(aToken, 0, ',', nI)); + sal_Int32 nY + = (nI >= 0) ? o3tl::toInt32(o3tl::getToken(aToken, 0, ',', nI)) : 0; + drawing::EnhancedCustomShapeParameterPair aPair; + aPair.First.Value <<= nX; + aPair.Second.Value <<= nY; + aCoordinates.push_back(aPair); + } + } while (nCharIndex >= 0); + aPropertyValue.Name = "Coordinates"; + aPropertyValue.Value <<= comphelper::containerToSequence(aCoordinates); + aPath.push_back(aPropertyValue); + } + else if (rProperty.first == "pSegmentInfo") + { + std::vector<drawing::EnhancedCustomShapeSegment> aSegments; + sal_Int32 nSize = 0; + sal_Int32 nCount = 0; + sal_Int32 nCharIndex = 0; + do + { + sal_Int32 nSeg + = o3tl::toInt32(o3tl::getToken(rProperty.second, 0, ';', nCharIndex)); + if (!nSize) + nSize = nSeg; + else if (!nCount) + nCount = nSeg; + else + { + sal_Int32 nPoints = 1; + if (nSeg >= 0x2000 && nSeg < 0x20FF) + { + nPoints = nSeg & 0x0FFF; + nSeg &= 0xFF00; + } + + drawing::EnhancedCustomShapeSegment aSegment; + switch (nSeg) + { + case 0x0001: // lineto + aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO; + aSegment.Count = sal_Int32(1); + aSegments.push_back(aSegment); + break; + case 0x4000: // moveto + aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::MOVETO; + aSegment.Count = sal_Int32(1); + aSegments.push_back(aSegment); + break; + case 0x2000: // curveto + aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::CURVETO; + aSegment.Count = nPoints; + aSegments.push_back(aSegment); + break; + case 0xb300: // arcto + aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::ARCTO; + aSegment.Count = sal_Int32(0); + aSegments.push_back(aSegment); + break; + case 0xac00: + case 0xaa00: // nofill + case 0xab00: // nostroke + case 0x6001: // close + break; + case 0x8000: // end + aSegment.Command + = drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH; + aSegment.Count = sal_Int32(0); + aSegments.push_back(aSegment); + break; + default: // given number of lineto elements + aSegment.Command = drawing::EnhancedCustomShapeSegmentCommand::LINETO; + aSegment.Count = nSeg; + aSegments.push_back(aSegment); + break; + } + } + } while (nCharIndex >= 0); + aPropertyValue.Name = "Segments"; + aPropertyValue.Value <<= comphelper::containerToSequence(aSegments); + aPath.push_back(aPropertyValue); + } + else if (rProperty.first == "geoLeft") + aViewBox.X = rProperty.second.toInt32(); + else if (rProperty.first == "geoTop") + aViewBox.Y = rProperty.second.toInt32(); + else if (rProperty.first == "geoRight") + aViewBox.Width = rProperty.second.toInt32(); + else if (rProperty.first == "geoBottom") + aViewBox.Height = rProperty.second.toInt32(); + else if (rProperty.first == "dhgt") + { + // dhgt is Word 2007, \shpz is Word 97-2003, the later has priority. + if (!rShape.hasZ()) + resolveDhgt(xPropertySet, rProperty.second.toInt32(), /*bOldStyle=*/false); + } + // These are in EMU, convert to mm100. + else if (rProperty.first == "dxTextLeft") + { + if (xPropertySet.is()) + xPropertySet->setPropertyValue("LeftBorderDistance", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dyTextTop") + { + if (xPropertySet.is()) + xPropertySet->setPropertyValue("TopBorderDistance", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dxTextRight") + { + if (xPropertySet.is()) + xPropertySet->setPropertyValue("RightBorderDistance", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dyTextBottom") + { + if (xPropertySet.is()) + xPropertySet->setPropertyValue("BottomBorderDistance", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dxWrapDistLeft") + { + if (m_bTextGraphicObject) + rShape.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distL, + new RTFValue(rProperty.second.toInt32())); + else if (xPropertySet.is()) + xPropertySet->setPropertyValue("LeftMargin", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dyWrapDistTop") + { + if (m_bTextGraphicObject) + rShape.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distT, + new RTFValue(rProperty.second.toInt32())); + else if (xPropertySet.is()) + xPropertySet->setPropertyValue("TopMargin", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dxWrapDistRight") + { + if (m_bTextGraphicObject) + rShape.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distR, + new RTFValue(rProperty.second.toInt32())); + else if (xPropertySet.is()) + xPropertySet->setPropertyValue("RightMargin", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "dyWrapDistBottom") + { + if (m_bTextGraphicObject) + rShape.getAnchorAttributes().set(NS_ooxml::LN_CT_Anchor_distB, + new RTFValue(rProperty.second.toInt32())); + else if (xPropertySet.is()) + xPropertySet->setPropertyValue("BottomMargin", + uno::Any(rProperty.second.toInt32() / 360)); + } + else if (rProperty.first == "fillType") + { + switch (rProperty.second.toInt32()) + { + case 7: // Shade using the fillAngle + aFillModel.moType.set(oox::XML_gradient); + break; + default: + SAL_INFO("writerfilter", + "TODO handle fillType value '" << rProperty.second << "'"); + break; + } + } + else if (rProperty.first == "fillFocus") + aFillModel.moFocus.set(rProperty.second.toDouble() / 100); // percent + else if (rProperty.first == "fShadow" && xPropertySet.is()) + { + if (rProperty.second.toInt32() == 1) + aShadowModel.mbHasShadow = true; + } + else if (rProperty.first == "shadowColor") + aShadowModel.moColor.set("#" + + msfilter::util::ConvertColorOU( + msfilter::util::BGRToRGB(rProperty.second.toInt32()))); + else if (rProperty.first == "shadowOffsetX") + // EMUs to points + aShadowModel.moOffset.set(OUString::number(rProperty.second.toDouble() / 12700) + "pt"); + else if (rProperty.first == "posh" || rProperty.first == "posv" + || rProperty.first == "fFitShapeToText" || rProperty.first == "fFilled" + || rProperty.first == "rotation") + applyProperty(xShape, rProperty.first, rProperty.second); + else if (rProperty.first == "posrelh") + { + switch (rProperty.second.toInt32()) + { + case 1: + rShape.setHoriOrientRelation(text::RelOrientation::PAGE_FRAME); + break; + default: + break; + } + } + else if (rProperty.first == "posrelv") + { + switch (rProperty.second.toInt32()) + { + case 1: + rShape.setVertOrientRelation(text::RelOrientation::PAGE_FRAME); + break; + default: + break; + } + } + else if (rProperty.first == "groupLeft") + oGroupLeft = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "groupTop") + oGroupTop = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "groupRight") + oGroupRight = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "groupBottom") + oGroupBottom = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "relLeft") + oRelLeft = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "relTop") + oRelTop = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "relRight") + oRelRight = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "relBottom") + oRelBottom = convertTwipToMm100(rProperty.second.toInt32()); + else if (rProperty.first == "fBehindDocument") + bOpaque = !rProperty.second.toInt32(); + else if (rProperty.first == "pctHoriz" || rProperty.first == "pctVert") + { + sal_Int16 nPercentage = rtl::math::round(rProperty.second.toDouble() / 10); + if (nPercentage) + { + std::optional<sal_Int16>& rPercentage + = rProperty.first == "pctHoriz" ? oRelativeWidth : oRelativeHeight; + rPercentage = nPercentage; + } + } + else if (rProperty.first == "sizerelh") + { + if (xPropertySet.is()) + { + switch (rProperty.second.toInt32()) + { + case 0: // margin + nRelativeWidthRelation = text::RelOrientation::FRAME; + break; + case 1: // page + nRelativeWidthRelation = text::RelOrientation::PAGE_FRAME; + break; + default: + SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelh value: " + << rProperty.second); + break; + } + } + } + else if (rProperty.first == "sizerelv") + { + if (xPropertySet.is()) + { + switch (rProperty.second.toInt32()) + { + case 0: // margin + nRelativeHeightRelation = text::RelOrientation::FRAME; + break; + case 1: // page + nRelativeHeightRelation = text::RelOrientation::PAGE_FRAME; + break; + default: + SAL_WARN("writerfilter", "RTFSdrImport::resolve: unhandled sizerelv value: " + << rProperty.second); + break; + } + } + } + else if (rProperty.first == "fHorizRule") // TODO: what does "fStandardHR" do? + { + // horizontal rule: relative width defaults to 100% of paragraph + // TODO: does it have a default height? + if (!oRelativeWidth) + { + oRelativeWidth = 100; + } + nRelativeWidthRelation = text::RelOrientation::FRAME; + if (xPropertySet.is()) + { + sal_Int16 const nVertOrient = text::VertOrientation::CENTER; + xPropertySet->setPropertyValue("VertOrient", uno::Any(nVertOrient)); + } + } + else if (rProperty.first == "pctHR") + { + // horizontal rule relative width in permille + oRelativeWidth = rProperty.second.toInt32() / 10; + } + else if (rProperty.first == "dxHeightHR") + { + // horizontal rule height + sal_uInt32 const nHeight(convertTwipToMm100(rProperty.second.toInt32())); + rShape.setBottom(rShape.getTop() + nHeight); + } + else if (rProperty.first == "dxWidthHR") + { + // horizontal rule width + sal_uInt32 const nWidth(convertTwipToMm100(rProperty.second.toInt32())); + rShape.setRight(rShape.getLeft() + nWidth); + } + else if (rProperty.first == "alignHR") + { + // horizontal orientation *for horizontal rule* + sal_Int16 nHoriOrient = text::HoriOrientation::NONE; + switch (rProperty.second.toInt32()) + { + case 0: + nHoriOrient = text::HoriOrientation::LEFT; + break; + case 1: + nHoriOrient = text::HoriOrientation::CENTER; + break; + case 2: + nHoriOrient = text::HoriOrientation::RIGHT; + break; + } + if (xPropertySet.is() && text::HoriOrientation::NONE != nHoriOrient) + { + xPropertySet->setPropertyValue("HoriOrient", uno::Any(nHoriOrient)); + } + } + else if (rProperty.first == "pWrapPolygonVertices") + { + RTFSprms aPolygonSprms; + sal_Int32 nSize = 0; // Size of a token + sal_Int32 nCount = 0; // Number of tokens + sal_Int32 nCharIndex = 0; // Character index + do + { + std::u16string_view aToken = o3tl::getToken(rProperty.second, 0, ';', nCharIndex); + if (!nSize) + nSize = o3tl::toInt32(aToken); + else if (!nCount) + nCount = o3tl::toInt32(aToken); + else if (!aToken.empty()) + { + // The coordinates are in an (x,y) form. + aToken = aToken.substr(1, aToken.size() - 2); + sal_Int32 nI = 0; + sal_Int32 nX = o3tl::toInt32(o3tl::getToken(aToken, 0, ',', nI)); + sal_Int32 nY + = (nI >= 0) ? o3tl::toInt32(o3tl::getToken(aToken, 0, ',', nI)) : 0; + RTFSprms aPathAttributes; + aPathAttributes.set(NS_ooxml::LN_CT_Point2D_x, new RTFValue(nX)); + aPathAttributes.set(NS_ooxml::LN_CT_Point2D_y, new RTFValue(nY)); + aPolygonSprms.set(NS_ooxml::LN_CT_WrapPath_lineTo, + new RTFValue(aPathAttributes), RTFOverwrite::NO_APPEND); + } + } while (nCharIndex >= 0); + rShape.getWrapPolygonSprms() = aPolygonSprms; + } + else if (rProperty.first == "fRelFlipV") + obRelFlipV = rProperty.second.toInt32() == 1; + else if (rProperty.first == "fFlipH") + obFlipH = rProperty.second.toInt32() == 1; + else if (rProperty.first == "fFlipV") + obFlipV = rProperty.second.toInt32() == 1; + else if (rProperty.first == "pictureContrast") + { + // Gain / contrast. + nContrast = rProperty.second.toInt32(); + if (nContrast < 0x10000) + { + nContrast *= 101; // 100 + 1 to round + nContrast /= 0x10000; + nContrast -= 100; + } + } + else if (rProperty.first == "pictureBrightness") + { + // Blacklevel / brightness. + nBrightness = rProperty.second.toInt32(); + if (nBrightness != 0) + { + nBrightness /= 327; + } + } + else + SAL_INFO("writerfilter", "TODO handle shape property '" << rProperty.first << "':'" + << rProperty.second << "'"); + } + + if (xPropertySet.is()) + { + resolveLineColorAndWidth(m_bTextFrame, xPropertySet, aLineColor, aLineWidth); + if (rShape.hasZ()) + { + bool bOldStyle = m_aParents.size() > 1; + resolveDhgt(xPropertySet, rShape.getZ(), bOldStyle); + } + if (m_bTextFrame) + xPropertySet->setPropertyValue("WritingMode", uno::Any(eWritingMode)); + else + // Only Writer textframes implement text::WritingMode2. + xPropertySet->setPropertyValue("TextWritingMode", + uno::Any(text::WritingMode(eWritingMode))); + } + + if (!m_aParents.empty() && m_aParents.top().is() && !m_bTextFrame) + m_aParents.top()->add(xShape); + + if (nContrast == -70 && nBrightness == 70 && xPropertySet.is()) + { + // Map MSO 'washout' to our watermark colormode. + xPropertySet->setPropertyValue("GraphicColorMode", uno::Any(drawing::ColorMode_WATERMARK)); + } + + if (bCustom && xShape.is() && !bPib) + { + uno::Reference<drawing::XEnhancedCustomShapeDefaulter> xDefaulter(xShape, uno::UNO_QUERY); + xDefaulter->createCustomShapeDefaults(OUString::number(nType)); + } + + // Set shape text + if (bCustom && !aShapeText.isEmpty()) + { + uno::Reference<text::XTextRange> xTextRange(xShape, uno::UNO_QUERY); + if (xTextRange.is()) + xTextRange->setString(aShapeText); + + xPropertySet->setPropertyValue("CharFontName", uno::Any(aFontFamily)); + xPropertySet->setPropertyValue("CharHeight", uno::Any(nFontSize)); + } + + // Creating CustomShapeGeometry property + if (bCustom && xPropertySet.is()) + { + bool bChanged = false; + comphelper::SequenceAsHashMap aCustomShapeGeometry( + xPropertySet->getPropertyValue("CustomShapeGeometry")); + + if (aViewBox.X || aViewBox.Y || aViewBox.Width || aViewBox.Height) + { + aViewBox.Width -= aViewBox.X; + aViewBox.Height -= aViewBox.Y; + aCustomShapeGeometry["ViewBox"] <<= aViewBox; + bChanged = true; + } + + if (!aPath.empty()) + { + aCustomShapeGeometry["Path"] <<= comphelper::containerToSequence(aPath); + bChanged = true; + } + + if (!aShapeText.isEmpty()) + { + uno::Sequence<beans::PropertyValue> aSequence(comphelper::InitPropertySequence({ + { "TextPath", uno::Any(true) }, + })); + aCustomShapeGeometry["TextPath"] <<= aSequence; + xPropertySet->setPropertyValue("TextAutoGrowHeight", uno::Any(false)); + xPropertySet->setPropertyValue("TextAutoGrowWidth", uno::Any(false)); + bChanged = true; + } + + if (bChanged) + { + xPropertySet->setPropertyValue( + "CustomShapeGeometry", + uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList())); + } + } + + if (obRelFlipV.has_value() && xPropertySet.is()) + { + if (nType == ESCHER_ShpInst_Line) + { + // Line shape inside group shape: get the polygon sequence and transform it. + uno::Sequence<uno::Sequence<awt::Point>> aPolyPolySequence; + if ((xPropertySet->getPropertyValue("PolyPolygon") >>= aPolyPolySequence) + && aPolyPolySequence.hasElements()) + { + uno::Sequence<awt::Point>& rPolygon = aPolyPolySequence.getArray()[0]; + basegfx::B2DPolygon aPoly; + for (const awt::Point& rPoint : std::as_const(rPolygon)) + { + aPoly.append(basegfx::B2DPoint(rPoint.X, rPoint.Y)); + } + basegfx::B2DHomMatrix aTransformation; + aTransformation.scale(1.0, *obRelFlipV ? -1.0 : 1.0); + aPoly.transform(aTransformation); + auto pPolygon = rPolygon.getArray(); + for (sal_Int32 i = 0; i < rPolygon.getLength(); ++i) + { + basegfx::B2DPoint aPoint(aPoly.getB2DPoint(i)); + pPolygon[i] = awt::Point(static_cast<sal_Int32>(aPoint.getX()), + static_cast<sal_Int32>(aPoint.getY())); + } + xPropertySet->setPropertyValue("PolyPolygon", uno::Any(aPolyPolySequence)); + } + } + } + + // Set position and size + if (xShape.is()) + { + sal_Int32 nLeft = rShape.getLeft(); + sal_Int32 nTop = rShape.getTop(); + + bool bInShapeGroup = oGroupLeft && oGroupTop && oGroupRight && oGroupBottom && oRelLeft + && oRelTop && oRelRight && oRelBottom; + awt::Size aSize; + if (bInShapeGroup) + { + // See lclGetAbsPoint() in the VML import: rShape is the group shape, oGroup is its coordinate system, oRel is the relative child shape. + sal_Int32 nShapeWidth = rShape.getRight() - rShape.getLeft(); + sal_Int32 nShapeHeight = rShape.getBottom() - rShape.getTop(); + sal_Int32 nCoordSysWidth = *oGroupRight - *oGroupLeft; + sal_Int32 nCoordSysHeight = *oGroupBottom - *oGroupTop; + double fWidthRatio = static_cast<double>(nShapeWidth) / nCoordSysWidth; + double fHeightRatio = static_cast<double>(nShapeHeight) / nCoordSysHeight; + nLeft = static_cast<sal_Int32>(rShape.getLeft() + + fWidthRatio * (*oRelLeft - *oGroupLeft)); + nTop = static_cast<sal_Int32>(rShape.getTop() + fHeightRatio * (*oRelTop - *oGroupTop)); + + // See lclGetAbsRect() in the VML import. + aSize.Width = std::lround(fWidthRatio * (*oRelRight - *oRelLeft)); + aSize.Height = std::lround(fHeightRatio * (*oRelBottom - *oRelTop)); + } + + if (m_bTextFrame) + { + xPropertySet->setPropertyValue("HoriOrientPosition", uno::Any(nLeft)); + xPropertySet->setPropertyValue("VertOrientPosition", uno::Any(nTop)); + } + else + xShape->setPosition(awt::Point(nLeft, nTop)); + + if (bInShapeGroup) + xShape->setSize(aSize); + else + xShape->setSize(awt::Size(rShape.getRight() - rShape.getLeft(), + rShape.getBottom() - rShape.getTop())); + + if (obFlipH || obFlipV) + { + if (bCustom) + { + // This has to be set after position and size is set, otherwise flip will affect the position. + comphelper::SequenceAsHashMap aCustomShapeGeometry( + xPropertySet->getPropertyValue("CustomShapeGeometry")); + if (obFlipH) + aCustomShapeGeometry["MirroredX"] <<= true; + if (obFlipV) + aCustomShapeGeometry["MirroredY"] <<= true; + xPropertySet->setPropertyValue( + "CustomShapeGeometry", + uno::Any(aCustomShapeGeometry.getAsConstPropertyValueList())); + } + else if (SdrObject* pObject = SdrObject::getSdrObjectFromXShape(xShape)) + { + Point aRef1 = pObject->GetSnapRect().Center(); + Point aRef2(aRef1); + if (obFlipH) + { + // Horizontal mirror means a vertical reference line. + aRef2.AdjustY(1); + } + if (obFlipV) + { + // Vertical mirror means a horizontal reference line. + aRef2.AdjustX(1); + } + pObject->Mirror(aRef1, aRef2); + } + } + + if (rShape.getHoriOrientRelation() != 0) + xPropertySet->setPropertyValue("HoriOrientRelation", + uno::Any(rShape.getHoriOrientRelation())); + if (rShape.getVertOrientRelation() != 0) + xPropertySet->setPropertyValue("VertOrientRelation", + uno::Any(rShape.getVertOrientRelation())); + if (rShape.getWrap() != text::WrapTextMode::WrapTextMode_MAKE_FIXED_SIZE) + xPropertySet->setPropertyValue("Surround", uno::Any(rShape.getWrap())); + oox::ModelObjectHelper aModelObjectHelper(m_rImport.getModelFactory()); + if (aFillModel.moType.has()) + { + oox::drawingml::ShapePropertyMap aPropMap(aModelObjectHelper); + aFillModel.pushToPropMap(aPropMap, m_rImport.getGraphicHelper()); + // Sets the FillStyle and FillGradient UNO properties. + oox::PropertySet(xShape).setProperties(aPropMap); + } + + if (aShadowModel.mbHasShadow) + { + oox::drawingml::ShapePropertyMap aPropMap(aModelObjectHelper); + aShadowModel.pushToPropMap(aPropMap, m_rImport.getGraphicHelper()); + // Sets the ShadowFormat UNO property. + oox::PropertySet(xShape).setProperties(aPropMap); + } + xPropertySet->setPropertyValue("AnchorType", + uno::Any(text::TextContentAnchorType_AT_CHARACTER)); + xPropertySet->setPropertyValue("Opaque", uno::Any(bOpaque)); + if (oRelativeWidth) + { + xPropertySet->setPropertyValue("RelativeWidth", uno::Any(*oRelativeWidth)); + xPropertySet->setPropertyValue("RelativeWidthRelation", + uno::Any(nRelativeWidthRelation)); + } + if (oRelativeHeight) + { + xPropertySet->setPropertyValue("RelativeHeight", uno::Any(*oRelativeHeight)); + xPropertySet->setPropertyValue("RelativeHeightRelation", + uno::Any(nRelativeHeightRelation)); + } + } + + if (bPib) + { + m_rImport.resolvePict(false, xShape); + } + + if (nType == ESCHER_ShpInst_PictureFrame) // picture frame + { + assert(!m_bTextFrame); + if (!bPib) // ??? not sure if the early return should be removed on else? + { + m_xShape = xShape; // store it for later resolvePict call + } + + // Handle horizontal flip. + if (obFlipH && xPropertySet.is()) + xPropertySet->setPropertyValue("IsMirrored", uno::Any(true)); + return; + } + + if (m_rImport.isInBackground()) + { + RTFSprms aAttributes; + aAttributes.set(NS_ooxml::LN_CT_Background_color, + new RTFValue(xPropertySet->getPropertyValue("FillColor").get<sal_Int32>())); + m_rImport.Mapper().props(new RTFReferenceProperties(std::move(aAttributes))); + + uno::Reference<lang::XComponent> xComponent(xShape, uno::UNO_QUERY); + xComponent->dispose(); + return; + } + + // Send it to dmapper + if (xShape.is()) + { + m_rImport.Mapper().startShape(xShape); + if (bClose) + { + m_rImport.Mapper().endShape(); + } + } + + // If the shape has an inner shape, the inner object's properties should not be influenced by + // the outer one. + rShape.getProperties().clear(); + + m_xShape = xShape; +} + +void RTFSdrImport::close() { m_rImport.Mapper().endShape(); } + +void RTFSdrImport::append(std::u16string_view aKey, std::u16string_view aValue) +{ + applyProperty(m_xShape, aKey, aValue); +} + +void RTFSdrImport::appendGroupProperty(std::u16string_view aKey, std::u16string_view aValue) +{ + if (m_aParents.empty()) + return; + uno::Reference<drawing::XShape> xShape(m_aParents.top(), uno::UNO_QUERY); + if (xShape.is()) + applyProperty(xShape, aKey, aValue); +} + +} // namespace writerfilter + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfsdrimport.hxx b/writerfilter/source/rtftok/rtfsdrimport.hxx new file mode 100644 index 000000000..16f7f9c31 --- /dev/null +++ b/writerfilter/source/rtftok/rtfsdrimport.hxx @@ -0,0 +1,103 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <stack> +#include <vector> + +#include <dmapper/GraphicZOrderHelper.hxx> +#include <tools/ref.hxx> + +namespace com::sun::star +{ +namespace beans +{ +class XPropertySet; +struct PropertyValue; +} +namespace drawing +{ +class XShape; +class XShapes; +} +namespace lang +{ +class XComponent; +} +} + +namespace writerfilter::rtftok +{ +class RTFDocumentImpl; +class RTFShape; + +/// Handles the import of drawings using RTF markup. +class RTFSdrImport final : public virtual SvRefBase +{ +public: + RTFSdrImport(RTFDocumentImpl& rDocument, + css::uno::Reference<css::lang::XComponent> const& xDstDoc); + ~RTFSdrImport() override; + + enum ShapeOrPict + { + SHAPE, + PICT + }; + void resolve(RTFShape& rShape, bool bClose, ShapeOrPict shapeOrPict); + void close(); + void append(std::u16string_view aKey, std::u16string_view aValue); + /// Append property on the current parent. + void appendGroupProperty(std::u16string_view aKey, std::u16string_view aValue); + void resolveDhgt(css::uno::Reference<css::beans::XPropertySet> const& xPropertySet, + sal_Int32 nZOrder, bool bOldStyle); + /// Set line color and line width on the shape, using the relevant API depending on if the shape is a text frame or not. + static void + resolveLineColorAndWidth(bool bTextFrame, + const css::uno::Reference<css::beans::XPropertySet>& xPropertySet, + css::uno::Any const& rLineColor, css::uno::Any const& rLineWidth); + static void resolveFLine(css::uno::Reference<css::beans::XPropertySet> const& xPropertySet, + sal_Int32 nFLine); + /** + * These are the default in Word, but not in Writer. + * + * @param bNew if the frame is new-style or old-style. + */ + static std::vector<css::beans::PropertyValue> getTextFrameDefaults(bool bNew); + /// Push a new group shape to the parent stack. + void pushParent(css::uno::Reference<css::drawing::XShapes> const& xParent); + /// Pop the current group shape from the parent stack. + void popParent(); + css::uno::Reference<css::drawing::XShape> const& getCurrentShape() const { return m_xShape; } + bool isFakePict() const { return m_bFakePict; } + +private: + void createShape(const OUString& rService, css::uno::Reference<css::drawing::XShape>& xShape, + css::uno::Reference<css::beans::XPropertySet>& xPropertySet); + void applyProperty(css::uno::Reference<css::drawing::XShape> const& xShape, + std::u16string_view aKey, std::u16string_view aValue) const; + int initShape(css::uno::Reference<css::drawing::XShape>& o_xShape, + css::uno::Reference<css::beans::XPropertySet>& o_xPropSet, bool& o_rIsCustomShape, + RTFShape const& rShape, bool bClose, ShapeOrPict shapeOrPict); + + RTFDocumentImpl& m_rImport; + std::stack<css::uno::Reference<css::drawing::XShapes>> m_aParents; + css::uno::Reference<css::drawing::XShape> m_xShape; + /// If m_xShape is imported as a Writer text frame (instead of a drawinglayer rectangle). + bool m_bTextFrame; + /// If m_xShape is imported as a Writer text graphic object (instead of a drawinglayer shape). + bool m_bTextGraphicObject; + /// if inside \pict, but actually it's a shape (not a picture) + bool m_bFakePict; + std::stack<writerfilter::dmapper::GraphicZOrderHelper> m_aGraphicZOrderHelpers; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfskipdestination.cxx b/writerfilter/source/rtftok/rtfskipdestination.cxx new file mode 100644 index 000000000..ad2122318 --- /dev/null +++ b/writerfilter/source/rtftok/rtfskipdestination.cxx @@ -0,0 +1,42 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfskipdestination.hxx" +#include <sal/log.hxx> +#include "rtflistener.hxx" + +namespace writerfilter::rtftok +{ +RTFSkipDestination::RTFSkipDestination(RTFListener& rImport) + : m_rImport(rImport) + , m_bParsed(true) + , m_bReset(true) +{ +} + +RTFSkipDestination::~RTFSkipDestination() +{ + if (m_rImport.getSkipUnknown() && m_bReset) + { + if (!m_bParsed) + { + SAL_INFO("writerfilter", __func__ << ": skipping destination"); + m_rImport.setDestination(Destination::SKIP); + } + m_rImport.setSkipUnknown(false); + } +} + +void RTFSkipDestination::setParsed(bool bParsed) { m_bParsed = bParsed; } + +void RTFSkipDestination::setReset(bool bReset) { m_bReset = bReset; } + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfskipdestination.hxx b/writerfilter/source/rtftok/rtfskipdestination.hxx new file mode 100644 index 000000000..4a894373f --- /dev/null +++ b/writerfilter/source/rtftok/rtfskipdestination.hxx @@ -0,0 +1,33 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +namespace writerfilter::rtftok +{ +class RTFListener; + +/// Skips a destination after a not parsed control word if it was prefixed with \* +class RTFSkipDestination final +{ +public: + explicit RTFSkipDestination(RTFListener& rImport); + ~RTFSkipDestination(); + void setParsed(bool bParsed); + void setReset(bool bReset); + +private: + RTFListener& m_rImport; + bool m_bParsed; + /// If false, the destructor is a noop, required by the \* symbol itself. + bool m_bReset; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfsprm.cxx b/writerfilter/source/rtftok/rtfsprm.cxx new file mode 100644 index 000000000..90bc97001 --- /dev/null +++ b/writerfilter/source/rtftok/rtfsprm.cxx @@ -0,0 +1,476 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfsprm.hxx" +#include <ooxml/resourceids.hxx> +#include <ooxml/QNameToString.hxx> +#include <rtl/strbuf.hxx> +#include "rtfdocumentimpl.hxx" +#include <algorithm> + +namespace writerfilter::rtftok +{ +RTFSprm::RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue) + : m_nKeyword(nKeyword) + , m_pValue(pValue) +{ +} + +sal_uInt32 RTFSprm::getId() const { return m_nKeyword; } + +Value::Pointer_t RTFSprm::getValue() { return Value::Pointer_t(m_pValue->Clone()); } + +writerfilter::Reference<Properties>::Pointer_t RTFSprm::getProps() +{ + return m_pValue->getProperties(); +} + +#ifdef DBG_UTIL +std::string RTFSprm::getName() const { return "RTFSprm"; } +#endif + +#ifdef DBG_UTIL +std::string RTFSprm::toString() const +{ + OStringBuffer aBuf("RTFSprm"); + + std::string sResult = QNameToString(m_nKeyword); + + aBuf.append(" ('"); + if (sResult.length() == 0) + aBuf.append(sal_Int32(m_nKeyword)); + else + aBuf.append(sResult.c_str()); + aBuf.append("', '"); + aBuf.append(m_pValue->toString().c_str()); + aBuf.append("')"); + + return aBuf.makeStringAndClear().getStr(); +} +#endif + +namespace +{ +class RTFSprms_compare +{ + Id keyword; + +public: + RTFSprms_compare(Id kw) + : keyword{ kw } + { + } + bool operator()(const std::pair<Id, RTFValue::Pointer_t>& raPair) const + { + return raPair.first == keyword; + } +}; +} + +RTFValue::Pointer_t RTFSprms::find(Id nKeyword, bool bFirst, bool bForWrite) +{ + if (bForWrite) + ensureCopyBeforeWrite(); + + RTFSprms_compare cmp{ nKeyword }; + + if (bFirst) + { + auto it = std::find_if(m_pSprms->begin(), m_pSprms->end(), cmp); + if (it != m_pSprms->end()) + return it->second; + } + else + // find last + { + auto rit = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), cmp); + if (rit != m_pSprms->rend()) + return rit->second; + } + + return RTFValue::Pointer_t{}; +} + +void RTFSprms::set(Id nKeyword, const RTFValue::Pointer_t& pValue, RTFOverwrite eOverwrite) +{ + ensureCopyBeforeWrite(); + + switch (eOverwrite) + { + case RTFOverwrite::YES_PREPEND: + { + m_pSprms->erase( + std::remove_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }), + m_pSprms->end()); + m_pSprms->emplace(m_pSprms->cbegin(), nKeyword, pValue); + break; + } + case RTFOverwrite::YES: + { + auto it + = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }); + if (it != m_pSprms->end()) + it->second = pValue; + else + m_pSprms->emplace_back(nKeyword, pValue); + break; + } + case RTFOverwrite::NO_IGNORE: + { + if (std::none_of(m_pSprms->cbegin(), m_pSprms->cend(), RTFSprms_compare{ nKeyword })) + m_pSprms->emplace_back(nKeyword, pValue); + break; + } + case RTFOverwrite::NO_APPEND: + { + m_pSprms->emplace_back(nKeyword, pValue); + break; + } + } +} + +bool RTFSprms::erase(Id nKeyword) +{ + ensureCopyBeforeWrite(); + + auto i = std::find_if(m_pSprms->begin(), m_pSprms->end(), RTFSprms_compare{ nKeyword }); + if (i != m_pSprms->end()) + { + m_pSprms->erase(i); + return true; + } + return false; +} + +void RTFSprms::eraseLast(Id nKeyword) +{ + ensureCopyBeforeWrite(); + + auto i = std::find_if(m_pSprms->rbegin(), m_pSprms->rend(), RTFSprms_compare{ nKeyword }); + if (i != m_pSprms->rend()) + m_pSprms->erase(std::next(i).base()); +} + +static RTFValue::Pointer_t getDefaultSPRM(Id const id, Id nStyleType) +{ + if (nStyleType == NS_ooxml::LN_Value_ST_StyleType_character) + { + switch (id) + { + case NS_ooxml::LN_EG_RPrBase_szCs: + case NS_ooxml::LN_EG_RPrBase_sz: + return new RTFValue(24); + case NS_ooxml::LN_CT_Color_val: + return new RTFValue(0); + case NS_ooxml::LN_EG_RPrBase_b: + case NS_ooxml::LN_EG_RPrBase_i: + return new RTFValue(0); + case NS_ooxml::LN_CT_Underline_val: + return new RTFValue(NS_ooxml::LN_Value_ST_Underline_none); + case NS_ooxml::LN_CT_Fonts_ascii: + case NS_ooxml::LN_CT_Fonts_eastAsia: + case NS_ooxml::LN_CT_Fonts_cs: + return new RTFValue("Times New Roman"); + default: + break; + } + } + + if (!nStyleType || nStyleType == NS_ooxml::LN_Value_ST_StyleType_paragraph) + { + switch (id) + { + case NS_ooxml::LN_CT_Spacing_before: + case NS_ooxml::LN_CT_Spacing_after: + case NS_ooxml::LN_CT_Ind_left: + case NS_ooxml::LN_CT_Ind_right: + case NS_ooxml::LN_CT_Ind_firstLine: + return new RTFValue(0); + + case NS_ooxml::LN_CT_Spacing_lineRule: + return new RTFValue(NS_ooxml::LN_Value_doc_ST_LineSpacingRule_auto); + case NS_ooxml::LN_CT_Spacing_line: + // presumably this means 100%, cf. static const int nSingleLineSpacing = 240; + return new RTFValue(240); + + case NS_ooxml::LN_CT_PrBase_pBdr: + { // tdf#150382 default all paragraph borders to none + RTFSprms attributes; + RTFSprms sprms; + for (int i = 0; i < 4; ++i) + { + auto const nBorder = getParagraphBorder(i); + RTFSprms aAttributes; + RTFSprms aSprms; + aAttributes.set(NS_ooxml::LN_CT_Border_val, + new RTFValue(NS_ooxml::LN_Value_ST_Border_none)); + sprms.set(nBorder, new RTFValue(aAttributes, aSprms)); + } + return new RTFValue(attributes, sprms); + } + + default: + break; + } + } + + return RTFValue::Pointer_t(); +} + +/// Is it problematic to deduplicate this SPRM? +static bool isSPRMDeduplicateDenylist(Id nId, RTFSprms* pDirect) +{ + switch (nId) + { + // See the NS_ooxml::LN_CT_PPrBase_tabs handler in DomainMapper, + // deduplication is explicitly not wanted for these tokens. + case NS_ooxml::LN_CT_TabStop_val: + case NS_ooxml::LN_CT_TabStop_leader: + case NS_ooxml::LN_CT_TabStop_pos: + // \htmautsp arrives after the style table, so only the non-style value is + // correct, keep these. + case NS_ooxml::LN_CT_Spacing_beforeAutospacing: + case NS_ooxml::LN_CT_Spacing_afterAutospacing: + // \chbrdr requires *all* of the border settings to be present, + // otherwise a default (NONE) border is created from the removed + // attributes which then overrides the style-defined border. + // See BorderHandler.cxx and NS_ooxml::LN_EG_RPrBase_bdr in DomainMapper. + // This also is needed for NS_ooxml::LN_CT_PBdr_top etc. + case NS_ooxml::LN_CT_Border_sz: + case NS_ooxml::LN_CT_Border_val: + case NS_ooxml::LN_CT_Border_color: + case NS_ooxml::LN_CT_Border_space: + case NS_ooxml::LN_CT_Border_shadow: + case NS_ooxml::LN_CT_Border_frame: + case NS_ooxml::LN_CT_Border_themeTint: + case NS_ooxml::LN_CT_Border_themeColor: + return true; + // Removing \fi and \li if the style has the same value would mean taking these values from + // \ls, while deduplication would be done to take the values from the style. + case NS_ooxml::LN_CT_Ind_firstLine: + case NS_ooxml::LN_CT_Ind_left: + return pDirect && pDirect->find(NS_ooxml::LN_CT_PPrBase_numPr); + + default: + return false; + } +} + +/// Should this SPRM be removed if all its children are removed? +static bool isSPRMChildrenExpected(Id nId) +{ + switch (nId) + { + case NS_ooxml::LN_CT_PBdr_top: + case NS_ooxml::LN_CT_PBdr_left: + case NS_ooxml::LN_CT_PBdr_bottom: + case NS_ooxml::LN_CT_PBdr_right: + // Expected children are NS_ooxml::LN_CT_Border_*. + case NS_ooxml::LN_CT_PrBase_shd: + // Expected children are NS_ooxml::LN_CT_Shd_*. + case NS_ooxml::LN_CT_PPrBase_ind: + // Expected children are NS_ooxml::LN_CT_Ind_*. + return true; + + default: + return false; + } +} + +/// Does the clone / deduplication of a single sprm. +static void cloneAndDeduplicateSprm(std::pair<Id, RTFValue::Pointer_t> const& rSprm, RTFSprms& ret, + Id nStyleType, RTFSprms* pDirect = nullptr) +{ + RTFValue::Pointer_t const pValue(ret.find(rSprm.first)); + if (pValue) + { + if (rSprm.second->equals(*pValue)) + { + if (!isSPRMDeduplicateDenylist(rSprm.first, pDirect)) + { + ret.erase(rSprm.first); // duplicate to style + } + } + else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty()) + { + RTFSprms const sprms(pValue->getSprms().cloneAndDeduplicate( + rSprm.second->getSprms(), nStyleType, /*bImplicitPPr =*/false, pDirect)); + RTFSprms const attributes(pValue->getAttributes().cloneAndDeduplicate( + rSprm.second->getAttributes(), nStyleType, /*bImplicitPPr =*/false, pDirect)); + // Don't copy the sprm in case we expect it to have children but it doesn't have some. + if (!isSPRMChildrenExpected(rSprm.first) || !sprms.empty() || !attributes.empty()) + ret.set(rSprm.first, + RTFValue::Pointer_t(pValue->CloneWithSprms(attributes, sprms))); + } + } + else + { + // not found - try to override style with default + RTFValue::Pointer_t const pDefault(getDefaultSPRM(rSprm.first, nStyleType)); + if (pDefault) + { + ret.set(rSprm.first, pDefault); + } + else if (!rSprm.second->getSprms().empty() || !rSprm.second->getAttributes().empty()) + { + RTFSprms const sprms( + RTFSprms().cloneAndDeduplicate(rSprm.second->getSprms(), nStyleType)); + RTFSprms const attributes( + RTFSprms().cloneAndDeduplicate(rSprm.second->getAttributes(), nStyleType)); + if (!sprms.empty() || !attributes.empty()) + { + ret.set(rSprm.first, new RTFValue(attributes, sprms)); + } + } + } +} + +/// Extracts the list level matching nLevel from pAbstract. +static RTFValue::Pointer_t getListLevel(const RTFValue::Pointer_t& pAbstract, int nLevel) +{ + for (const auto& rPair : pAbstract->getSprms()) + { + if (rPair.first != NS_ooxml::LN_CT_AbstractNum_lvl) + continue; + + RTFValue::Pointer_t pLevel = rPair.second->getAttributes().find(NS_ooxml::LN_CT_Lvl_ilvl); + if (!pLevel) + continue; + + if (pLevel->getInt() != nLevel) + continue; + + return rPair.second; + } + + return RTFValue::Pointer_t(); +} + +void RTFSprms::deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents) +{ + int nLevel = 0; + RTFValue::Pointer_t pLevelId + = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl); + if (pLevelId) + nLevel = pLevelId->getInt(); + + auto it = rInvalidListLevelFirstIndents.find(nLevel); + if (it == rInvalidListLevelFirstIndents.end()) + return; + + int nListValue = it->second; + + RTFValue::Pointer_t pParagraphValue + = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine); + if (!pParagraphValue) + return; + + int nParagraphValue = pParagraphValue->getInt(); + + if (nParagraphValue == nListValue) + eraseNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, NS_ooxml::LN_CT_Ind_firstLine); +} + +void RTFSprms::duplicateList(const RTFValue::Pointer_t& pAbstract) +{ + int nLevel = 0; + RTFValue::Pointer_t pLevelId + = getNestedSprm(*this, NS_ooxml::LN_CT_PPrBase_numPr, NS_ooxml::LN_CT_NumPr_ilvl); + if (pLevelId) + nLevel = pLevelId->getInt(); + + RTFValue::Pointer_t pLevel = getListLevel(pAbstract, nLevel); + if (!pLevel) + return; + + RTFValue::Pointer_t pLevelInd = pLevel->getSprms().find(NS_ooxml::LN_CT_PPrBase_ind); + if (!pLevelInd) + return; + + for (const auto& rListLevelPair : pLevelInd->getAttributes()) + { + switch (rListLevelPair.first) + { + case NS_ooxml::LN_CT_Ind_left: + case NS_ooxml::LN_CT_Ind_right: + case NS_ooxml::LN_CT_Ind_firstLine: + RTFValue::Pointer_t pParagraphValue + = getNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first); + if (!pParagraphValue) + putNestedAttribute(*this, NS_ooxml::LN_CT_PPrBase_ind, rListLevelPair.first, + getDefaultSPRM(rListLevelPair.first, 0)); + + break; + } + } +} + +RTFSprms RTFSprms::cloneAndDeduplicate(RTFSprms& rReference, Id const nStyleType, + bool const bImplicitPPr, RTFSprms* pDirect) const +{ + RTFSprms ret(*this); + ret.ensureCopyBeforeWrite(); + + // Note: apparently some attributes are set with OVERWRITE_NO_APPEND; + // it is probably a bad idea to mess with those in any way here? + for (auto& rSprm : rReference) + { + // Paragraph formatting sprms are directly contained in case of + // paragraphs, but they are below NS_ooxml::LN_CT_Style_pPr in case of + // styles. So handle those children directly, to avoid unexpected + // addition of direct formatting sprms at the paragraph level. + if (bImplicitPPr && rSprm.first == NS_ooxml::LN_CT_Style_pPr) + { + for (const auto& i : rSprm.second->getSprms()) + cloneAndDeduplicateSprm(i, ret, nStyleType, pDirect); + } + else + cloneAndDeduplicateSprm(rSprm, ret, nStyleType, pDirect); + } + return ret; +} + +bool RTFSprms::equals(const RTFValue& rOther) const +{ + return std::all_of(m_pSprms->cbegin(), m_pSprms->cend(), + [&](const std::pair<Id, RTFValue::Pointer_t>& raPair) -> bool { + return raPair.second->equals(rOther); + }); +} + +void RTFSprms::ensureCopyBeforeWrite() +{ + if (m_pSprms->GetRefCount() > 1) + { + tools::SvRef<RTFSprmsImpl> pClone(new RTFSprmsImpl); + for (auto& rSprm : *m_pSprms) + pClone->push_back( + std::make_pair(rSprm.first, RTFValue::Pointer_t(rSprm.second->Clone()))); + m_pSprms = pClone; + } +} + +RTFSprms::RTFSprms() + : m_pSprms(new RTFSprmsImpl) +{ +} + +RTFSprms::~RTFSprms() = default; + +void RTFSprms::clear() +{ + if (m_pSprms->GetRefCount() == 1) + return m_pSprms->clear(); + + m_pSprms = tools::SvRef<RTFSprmsImpl>(new RTFSprmsImpl); +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfsprm.hxx b/writerfilter/source/rtftok/rtfsprm.hxx new file mode 100644 index 000000000..9f3bbd78b --- /dev/null +++ b/writerfilter/source/rtftok/rtfsprm.hxx @@ -0,0 +1,101 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <string> +#include <utility> +#include <vector> +#include <map> + +#include <tools/ref.hxx> +#include "rtfvalue.hxx" + +namespace writerfilter::rtftok +{ +using RTFSprmsImplBase = std::vector<std::pair<Id, RTFValue::Pointer_t>>; + +/// The payload of RTFSprms which is only copied on write. +class RTFSprmsImpl : public RTFSprmsImplBase, public virtual SvRefBase +{ +}; + +enum class RTFOverwrite +{ + YES, ///< Yes, if an existing key is found, overwrite it. + NO_APPEND, ///< No, always append the value to the end of the list. + NO_IGNORE, ///< No, if the key is already in the list, then ignore, otherwise append. + YES_PREPEND ///< Yes, always prepend the value to the start of the list and remove existing entries. +}; + +/// A list of RTFSprm with a copy constructor that performs a deep copy. +class RTFSprms : public virtual SvRefBase +{ +public: + using Pointer_t = tools::SvRef<RTFSprms>; + using Entry_t = std::pair<Id, RTFValue::Pointer_t>; + using Iterator_t = std::vector<Entry_t>::iterator; + using ReverseIterator_t = std::vector<Entry_t>::reverse_iterator; + RTFSprms(); + ~RTFSprms() override; + + RTFSprms(RTFSprms const&) = default; + RTFSprms(RTFSprms&&) = default; + RTFSprms& operator=(RTFSprms const&) = default; + RTFSprms& operator=(RTFSprms&&) = default; + + RTFValue::Pointer_t find(Id nKeyword, bool bFirst = true, bool bForWrite = false); + /// Does the same as ->push_back(), except that it can overwrite or ignore existing entries. + void set(Id nKeyword, const RTFValue::Pointer_t& pValue, + RTFOverwrite eOverwrite = RTFOverwrite::YES); + bool erase(Id nKeyword); + void eraseLast(Id nKeyword); + /// Removes elements which are already in the reference set. + /// Also insert default values to override attributes of style + /// (yes, really; that's what Word does). + /// @param bImplicitPPr implicit dereference of top-level pPr SPRM + /// @param pDirect pointer to the root of the direct formatting SPRM tree, if any + RTFSprms cloneAndDeduplicate(RTFSprms& rReference, Id nStyleType, bool bImplicitPPr = false, + RTFSprms* pDirect = nullptr) const; + /// Inserts default values to override attributes of pAbstract. + void duplicateList(const RTFValue::Pointer_t& pAbstract); + /// Removes duplicated values based on in-list properties. + void deduplicateList(const std::map<int, int>& rInvalidListLevelFirstIndents); + std::size_t size() const { return m_pSprms->size(); } + bool empty() const { return m_pSprms->empty(); } + Entry_t& back() { return m_pSprms->back(); } + Iterator_t begin() { return m_pSprms->begin(); } + Iterator_t end() { return m_pSprms->end(); } + void clear(); + bool equals(const RTFValue& rOther) const; + +private: + void ensureCopyBeforeWrite(); + tools::SvRef<RTFSprmsImpl> m_pSprms; +}; + +/// RTF keyword with a parameter +class RTFSprm : public Sprm +{ +public: + RTFSprm(Id nKeyword, RTFValue::Pointer_t& pValue); + sal_uInt32 getId() const override; + Value::Pointer_t getValue() override; + writerfilter::Reference<Properties>::Pointer_t getProps() override; +#ifdef DBG_UTIL + std::string getName() const override; + std::string toString() const override; +#endif +private: + Id m_nKeyword; + RTFValue::Pointer_t& m_pValue; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtftokenizer.cxx b/writerfilter/source/rtftok/rtftokenizer.cxx new file mode 100644 index 000000000..4dc80416c --- /dev/null +++ b/writerfilter/source/rtftok/rtftokenizer.cxx @@ -0,0 +1,329 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtftokenizer.hxx" +#include <tools/stream.hxx> +#include <svx/dialmgr.hxx> +#include <svx/strings.hrc> +#include <rtl/strbuf.hxx> +#include <rtl/character.hxx> +#include <sal/log.hxx> +#include "rtfskipdestination.hxx" +#include <com/sun/star/io/BufferSizeExceededException.hpp> +#include <com/sun/star/task/XStatusIndicator.hpp> +#include <filter/msfilter/rtfutil.hxx> + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +std::unordered_map<OString, RTFSymbol> RTFTokenizer::s_aRTFControlWords; +bool RTFTokenizer::s_bControlWordsInitialised; +std::vector<RTFMathSymbol> RTFTokenizer::s_aRTFMathControlWords; +bool RTFTokenizer::s_bMathControlWordsSorted; + +RTFTokenizer::RTFTokenizer(RTFListener& rImport, SvStream* pInStream, + uno::Reference<task::XStatusIndicator> const& xStatusIndicator) + : m_rImport(rImport) + , m_pInStream(pInStream) + , m_xStatusIndicator(xStatusIndicator) + , m_nGroup(0) + , m_nLineNumber(0) + , m_nLineStartPos(0) + , m_nGroupStart(0) +{ + if (!RTFTokenizer::s_bControlWordsInitialised) + { + RTFTokenizer::s_bControlWordsInitialised = true; + for (int i = 0; i < nRTFControlWords; ++i) + s_aRTFControlWords.emplace(OString(aRTFControlWords[i].GetKeyword()), + aRTFControlWords[i]); + } + if (!RTFTokenizer::s_bMathControlWordsSorted) + { + RTFTokenizer::s_bMathControlWordsSorted = true; + s_aRTFMathControlWords = std::vector<RTFMathSymbol>( + aRTFMathControlWords, aRTFMathControlWords + nRTFMathControlWords); + std::sort(s_aRTFMathControlWords.begin(), s_aRTFMathControlWords.end()); + } +} + +RTFTokenizer::~RTFTokenizer() = default; + +RTFError RTFTokenizer::resolveParse() +{ + SAL_INFO("writerfilter.rtf", __func__); + char ch; + RTFError ret; + // for hex chars + int b = 0; + int count = 2; + std::size_t nPercentSize = 0; + sal_uInt64 nLastPos = 0; + + if (m_xStatusIndicator.is()) + { + OUString sDocLoad(SvxResId(RID_SVXSTR_DOC_LOAD)); + + sal_uInt64 const nCurrentPos = Strm().Tell(); + sal_uInt64 const nEndPos = nCurrentPos + Strm().remainingSize(); + m_xStatusIndicator->start(sDocLoad, nEndPos); + nPercentSize = nEndPos / 100; + + nLastPos = nCurrentPos; + m_xStatusIndicator->setValue(nLastPos); + } + + while (Strm().ReadChar(ch), !Strm().eof()) + { + //SAL_INFO("writerfilter", __func__ << ": parsing character '" << ch << "'"); + + sal_uInt64 const nCurrentPos = Strm().Tell(); + if (m_xStatusIndicator.is() && nCurrentPos > (nLastPos + nPercentSize)) + { + nLastPos = nCurrentPos; + m_xStatusIndicator->setValue(nLastPos); + } + + if (m_nGroup < 0) + return RTFError::GROUP_UNDER; + if (m_nGroup > 0 && m_rImport.getInternalState() == RTFInternalState::BIN) + { + ret = m_rImport.resolveChars(ch); + if (ret != RTFError::OK) + return ret; + } + else + { + switch (ch) + { + case '{': + m_nGroupStart = Strm().Tell() - 1; + ret = m_rImport.pushState(); + if (ret != RTFError::OK) + return ret; + break; + case '}': + ret = m_rImport.popState(); + if (ret != RTFError::OK) + return ret; + if (m_nGroup == 0) + { + if (m_rImport.isSubstream()) + m_rImport.finishSubstream(); + return RTFError::OK; + } + break; + case '\\': + ret = resolveKeyword(); + if (ret != RTFError::OK) + return ret; + break; + case 0x0d: + break; // ignore this + case 0x0a: + m_nLineNumber++; + m_nLineStartPos = nCurrentPos; + break; + default: + if (m_nGroup == 0) + return RTFError::CHAR_OVER; + if (m_rImport.getInternalState() == RTFInternalState::NORMAL) + { + ret = m_rImport.resolveChars(ch); + if (ret != RTFError::OK) + return ret; + } + else + { + SAL_INFO("writerfilter.rtf", __func__ << ": hex internal state"); + // Assume that \'<number><junk> means \'0<number>. + if (rtl::isAsciiDigit(static_cast<unsigned char>(ch)) + || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) + { + b = b << 4; + sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); + if (parsed == -1) + return RTFError::HEX_INVALID; + b += parsed; + } + count--; + if (!count) + { + ret = m_rImport.resolveChars(b); + if (ret != RTFError::OK) + return ret; + count = 2; + b = 0; + m_rImport.setInternalState(RTFInternalState::NORMAL); + } + } + break; + } + } + } + + if (m_nGroup < 0) + return RTFError::GROUP_UNDER; + if (m_nGroup > 0) + return RTFError::GROUP_OVER; + return RTFError::OK; +} + +void RTFTokenizer::pushGroup() { m_nGroup++; } + +void RTFTokenizer::popGroup() { m_nGroup--; } + +RTFError RTFTokenizer::resolveKeyword() +{ + char ch; + + Strm().ReadChar(ch); + if (Strm().eof()) + return RTFError::UNEXPECTED_EOF; + + if (!rtl::isAsciiAlpha(static_cast<unsigned char>(ch))) + { + // control symbols aren't followed by a space, so we can return here + // without doing any SeekRel() + return dispatchKeyword(OString(ch), false, 0); + } + OStringBuffer aBuf(32); + while (rtl::isAsciiAlpha(static_cast<unsigned char>(ch))) + { + aBuf.append(ch); + if (aBuf.getLength() > 32) + // See RTF spec v1.9.1, page 7 + // A control word's name cannot be longer than 32 letters. + throw io::BufferSizeExceededException(); + Strm().ReadChar(ch); + if (Strm().eof()) + { + ch = ' '; + break; + } + } + + bool bNeg = false; + if (ch == '-') + { + // in case we'll have a parameter, that will be negative + bNeg = true; + Strm().ReadChar(ch); + if (Strm().eof()) + return RTFError::UNEXPECTED_EOF; + } + bool bParam = false; + int nParam = 0; + if (rtl::isAsciiDigit(static_cast<unsigned char>(ch))) + { + OStringBuffer aParameter; + + // we have a parameter + bParam = true; + while (rtl::isAsciiDigit(static_cast<unsigned char>(ch))) + { + aParameter.append(ch); + Strm().ReadChar(ch); + if (Strm().eof()) + { + ch = ' '; + break; + } + } + nParam = aParameter.makeStringAndClear().toInt32(); + if (bNeg) + nParam = -nParam; + } + if (ch != ' ') + Strm().SeekRel(-1); + OString aKeyword = aBuf.makeStringAndClear(); + return dispatchKeyword(aKeyword, bParam, nParam); +} + +bool RTFTokenizer::lookupMathKeyword(RTFMathSymbol& rSymbol) +{ + auto low + = std::lower_bound(s_aRTFMathControlWords.begin(), s_aRTFMathControlWords.end(), rSymbol); + if (low == s_aRTFMathControlWords.end() || rSymbol < *low) + return false; + rSymbol = *low; + return true; +} + +RTFError RTFTokenizer::dispatchKeyword(OString const& rKeyword, bool bParam, int nParam) +{ + if (m_rImport.getDestination() == Destination::SKIP) + { + // skip binary data explicitly, to not trip over rtf markup + // control characters + if (rKeyword == "bin" && nParam > 0) + Strm().SeekRel(nParam); + return RTFError::OK; + } + SAL_INFO("writerfilter.rtf", __func__ << ": keyword '\\" << rKeyword << "' with param? " + << (bParam ? 1 : 0) << " param val: '" + << (bParam ? nParam : 0) << "'"); + auto findIt = s_aRTFControlWords.find(rKeyword); + if (findIt == s_aRTFControlWords.end()) + { + SAL_INFO("writerfilter.rtf", __func__ << ": unknown keyword '\\" << rKeyword << "'"); + RTFSkipDestination aSkip(m_rImport); + aSkip.setParsed(false); + return RTFError::OK; + } + + RTFError ret; + RTFSymbol const& rSymbol = findIt->second; + switch (rSymbol.GetControlType()) + { + case RTFControlType::FLAG: + // flags ignore any parameter by definition + ret = m_rImport.dispatchFlag(rSymbol.GetIndex()); + if (ret != RTFError::OK) + return ret; + break; + case RTFControlType::DESTINATION: + // same for destinations + ret = m_rImport.dispatchDestination(rSymbol.GetIndex()); + if (ret != RTFError::OK) + return ret; + break; + case RTFControlType::SYMBOL: + // and symbols + ret = m_rImport.dispatchSymbol(rSymbol.GetIndex()); + if (ret != RTFError::OK) + return ret; + break; + case RTFControlType::TOGGLE: + ret = m_rImport.dispatchToggle(rSymbol.GetIndex(), bParam, nParam); + if (ret != RTFError::OK) + return ret; + break; + case RTFControlType::VALUE: + if (!bParam) + nParam = rSymbol.GetDefValue(); + ret = m_rImport.dispatchValue(rSymbol.GetIndex(), nParam); + if (ret != RTFError::OK) + return ret; + break; + } + + return RTFError::OK; +} + +OUString RTFTokenizer::getPosition() +{ + return OUString::number(m_nLineNumber + 1) + "," + + OUString::number(Strm().Tell() - m_nLineStartPos + 1); +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtftokenizer.hxx b/writerfilter/source/rtftok/rtftokenizer.hxx new file mode 100644 index 000000000..feb74fc63 --- /dev/null +++ b/writerfilter/source/rtftok/rtftokenizer.hxx @@ -0,0 +1,72 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include "rtflistener.hxx" + +#include <vector> +#include <unordered_map> + +#include <com/sun/star/uno/Reference.h> + +#include <rtl/ustring.hxx> +#include <tools/ref.hxx> + +namespace com::sun::star::task +{ +class XStatusIndicator; +} +class SvStream; + +namespace writerfilter::rtftok +{ +/// RTF tokenizer that separates control words from text. +class RTFTokenizer final : public virtual SvRefBase +{ +public: + RTFTokenizer(RTFListener& rImport, SvStream* pInStream, + css::uno::Reference<css::task::XStatusIndicator> const& xStatusIndicator); + ~RTFTokenizer() override; + + RTFError resolveParse(); + /// Number of states on the stack. + int getGroup() const { return m_nGroup; } + /// To be invoked by the pushState() callback to signal when the importer enters a group. + void pushGroup(); + /// To be invoked by the popState() callback to signal when the importer leaves a group. + void popGroup(); + OUString getPosition(); + std::size_t getGroupStart() const { return m_nGroupStart; } + /// To look up additional properties of a math symbol. + static bool lookupMathKeyword(RTFMathSymbol& rSymbol); + +private: + SvStream& Strm() { return *m_pInStream; } + RTFError resolveKeyword(); + RTFError dispatchKeyword(OString const& rKeyword, bool bParam, int nParam); + + RTFListener& m_rImport; + SvStream* m_pInStream; + css::uno::Reference<css::task::XStatusIndicator> const& m_xStatusIndicator; + // This is the same as aRTFControlWords, but mapped by token name for fast lookup + static std::unordered_map<OString, RTFSymbol> s_aRTFControlWords; + static bool s_bControlWordsInitialised; + // This is the same as aRTFMathControlWords, but sorted + static std::vector<RTFMathSymbol> s_aRTFMathControlWords; + static bool s_bMathControlWordsSorted; + /// Same as the size of the importer's states, except that this can be negative for invalid input. + int m_nGroup; + sal_Int32 m_nLineNumber; + std::size_t m_nLineStartPos; + std::size_t m_nGroupStart; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfvalue.cxx b/writerfilter/source/rtftok/rtfvalue.cxx new file mode 100644 index 000000000..42f60a1c9 --- /dev/null +++ b/writerfilter/source/rtftok/rtfvalue.cxx @@ -0,0 +1,222 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include "rtfreferenceproperties.hxx" +#include "rtfdocumentimpl.hxx" +#include <com/sun/star/embed/XEmbeddedObject.hpp> + +using namespace com::sun::star; + +namespace writerfilter::rtftok +{ +RTFValue::RTFValue(int nValue, OUString sValue, const RTFSprms* pAttributes, const RTFSprms* pSprms, + uno::Reference<drawing::XShape> xShape, uno::Reference<io::XInputStream> xStream, + uno::Reference<embed::XEmbeddedObject> xObject, bool bForceString, + const RTFShape* pShape, const RTFPicture* pPicture) + : m_nValue(nValue) + , m_sValue(std::move(sValue)) + , m_xShape(std::move(xShape)) + , m_xStream(std::move(xStream)) + , m_xObject(std::move(xObject)) + , m_bForceString(bForceString) +{ + if (pAttributes) + m_pAttributes = new RTFSprms(*pAttributes); + if (pSprms) + m_pSprms = new RTFSprms(*pSprms); + if (pShape) + m_pShape = new RTFShape(*pShape); + if (pPicture) + m_pPicture = new RTFPicture(*pPicture); +} + +RTFValue::RTFValue() {} + +RTFValue::RTFValue(int nValue) + : m_nValue(nValue) +{ +} + +RTFValue::RTFValue(OUString sValue, bool bForce) + : m_sValue(std::move(sValue)) + , m_bForceString(bForce) +{ +} + +RTFValue::RTFValue(const RTFSprms& rAttributes) + : m_pAttributes(new RTFSprms(rAttributes)) +{ +} + +RTFValue::RTFValue(const RTFSprms& rAttributes, const RTFSprms& rSprms) + : m_pAttributes(new RTFSprms(rAttributes)) + , m_pSprms(new RTFSprms(rSprms)) +{ +} + +RTFValue::RTFValue(uno::Reference<drawing::XShape> xShape) + : m_xShape(std::move(xShape)) +{ +} + +RTFValue::RTFValue(uno::Reference<io::XInputStream> xStream) + : m_xStream(std::move(xStream)) +{ +} + +RTFValue::RTFValue(uno::Reference<embed::XEmbeddedObject> xObject) + : m_xObject(std::move(xObject)) +{ +} + +RTFValue::RTFValue(const RTFShape& aShape) + : m_pShape(new RTFShape(aShape)) +{ +} + +RTFValue::RTFValue(const RTFPicture& rPicture) + : m_pPicture(new RTFPicture(rPicture)) +{ +} + +RTFValue::~RTFValue() = default; + +int RTFValue::getInt() const { return m_nValue; } + +OUString RTFValue::getString() const +{ + if (!m_sValue.isEmpty() || m_bForceString) + return m_sValue; + + return OUString::number(m_nValue); +} + +void RTFValue::setString(const OUString& sValue) { m_sValue = sValue; } + +uno::Any RTFValue::getAny() const +{ + uno::Any ret; + if (!m_sValue.isEmpty() || m_bForceString) + ret <<= m_sValue; + else if (m_xShape.is()) + ret <<= m_xShape; + else if (m_xStream.is()) + ret <<= m_xStream; + else if (m_xObject.is()) + ret <<= m_xObject; + else + ret <<= static_cast<sal_Int32>(m_nValue); + return ret; +} + +RTFShape& RTFValue::getShape() const +{ + if (!m_pShape) + m_pShape = new RTFShape(); + return *m_pShape; +} + +RTFPicture& RTFValue::getPicture() const +{ + if (!m_pPicture) + m_pPicture = new RTFPicture; + return *m_pPicture; +} + +writerfilter::Reference<Properties>::Pointer_t RTFValue::getProperties() +{ + return new RTFReferenceProperties(getAttributes(), getSprms()); +} + +writerfilter::Reference<BinaryObj>::Pointer_t RTFValue::getBinary() +{ + return writerfilter::Reference<BinaryObj>::Pointer_t(); +} + +#ifdef DBG_UTIL +std::string RTFValue::toString() const +{ + if (!m_sValue.isEmpty() || m_bForceString) + return OUStringToOString(m_sValue, RTL_TEXTENCODING_UTF8).getStr(); + + return OString::number(m_nValue).getStr(); +} +#endif + +RTFValue* RTFValue::Clone() const +{ + return new RTFValue(m_nValue, m_sValue, m_pAttributes.get(), m_pSprms.get(), m_xShape, + m_xStream, m_xObject, m_bForceString, m_pShape.get(), m_pPicture.get()); +} + +RTFValue* RTFValue::CloneWithSprms(RTFSprms const& rAttributes, RTFSprms const& rSprms) const +{ + return new RTFValue(m_nValue, m_sValue, &rAttributes, &rSprms, m_xShape, m_xStream, m_xObject, + m_bForceString, m_pShape.get(), m_pPicture.get()); +} + +bool RTFValue::equals(const RTFValue& rOther) const +{ + if (m_nValue != rOther.m_nValue) + return false; + if (m_sValue != rOther.m_sValue) + return false; + + if (m_pAttributes && rOther.m_pAttributes) + { + if (m_pAttributes->size() != rOther.m_pAttributes->size()) + return false; + if (!m_pAttributes->equals(rOther)) + return false; + } + else if (m_pAttributes && m_pAttributes->size()) + { + return false; + } + else if (rOther.m_pAttributes && rOther.m_pAttributes->size()) + { + return false; + } + + if (m_pSprms && rOther.m_pSprms) + { + if (m_pSprms->size() != rOther.m_pSprms->size()) + return false; + if (!m_pSprms->equals(rOther)) + return false; + } + else if (m_pSprms && m_pSprms->size()) + { + return false; + } + else if (rOther.m_pSprms && rOther.m_pSprms->size()) + { + return false; + } + + return true; +} + +RTFSprms& RTFValue::getAttributes() const +{ + if (!m_pAttributes) + m_pAttributes = new RTFSprms(); + return *m_pAttributes; +} + +RTFSprms& RTFValue::getSprms() const +{ + if (!m_pSprms) + m_pSprms = new RTFSprms(); + return *m_pSprms; +} + +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/writerfilter/source/rtftok/rtfvalue.hxx b/writerfilter/source/rtftok/rtfvalue.hxx new file mode 100644 index 000000000..4f37a5dcb --- /dev/null +++ b/writerfilter/source/rtftok/rtfvalue.hxx @@ -0,0 +1,85 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#pragma once + +#include <dmapper/resourcemodel.hxx> + +namespace com::sun::star +{ +namespace embed +{ +class XEmbeddedObject; +} +namespace io +{ +class XInputStream; +} +} + +namespace writerfilter::rtftok +{ +class RTFSprms; +class RTFShape; +class RTFPicture; +/// Value of an RTF keyword +class RTFValue : public Value +{ + RTFValue(int nValue, OUString sValue, const RTFSprms* pAttributes, const RTFSprms* pSprms, + css::uno::Reference<css::drawing::XShape> xShape, + css::uno::Reference<css::io::XInputStream> xStream, + css::uno::Reference<css::embed::XEmbeddedObject> xObject, bool bForceString, + const RTFShape* pShape, const RTFPicture* pPicture); + +public: + using Pointer_t = tools::SvRef<RTFValue>; + RTFValue(); + explicit RTFValue(int nValue); + RTFValue(OUString sValue, bool bForce = false); + explicit RTFValue(const RTFSprms& rAttributes); + RTFValue(const RTFSprms& rAttributes, const RTFSprms& rSprms); + explicit RTFValue(css::uno::Reference<css::drawing::XShape> xShape); + explicit RTFValue(css::uno::Reference<css::io::XInputStream> xStream); + explicit RTFValue(css::uno::Reference<css::embed::XEmbeddedObject> xObject); + explicit RTFValue(const RTFShape& aShape); + explicit RTFValue(const RTFPicture& rPicture); + ~RTFValue() override; + void setString(const OUString& sValue); + int getInt() const override; + OUString getString() const override; + css::uno::Any getAny() const override; + writerfilter::Reference<Properties>::Pointer_t getProperties() override; + writerfilter::Reference<BinaryObj>::Pointer_t getBinary() override; +#ifdef DBG_UTIL + std::string toString() const override; +#endif + RTFValue* Clone() const; + RTFValue* CloneWithSprms(RTFSprms const& rAttributes, RTFSprms const& rSprms) const; + RTFSprms& getAttributes() const; + RTFSprms& getSprms() const; + RTFShape& getShape() const; + RTFPicture& getPicture() const; + bool equals(const RTFValue& rOther) const; + RTFValue& operator=(RTFValue const& rOther) = delete; + +private: + int m_nValue = 0; + OUString m_sValue; + mutable tools::SvRef<RTFSprms> m_pAttributes; + mutable tools::SvRef<RTFSprms> m_pSprms; + css::uno::Reference<css::drawing::XShape> m_xShape; + css::uno::Reference<css::io::XInputStream> m_xStream; + css::uno::Reference<css::embed::XEmbeddedObject> m_xObject; + bool m_bForceString = false; + mutable tools::SvRef<RTFShape> m_pShape; + mutable tools::SvRef<RTFPicture> m_pPicture; +}; +} // namespace writerfilter::rtftok + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |