summaryrefslogtreecommitdiffstats
path: root/starmath/source
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /starmath/source
parentInitial commit. (diff)
downloadlibreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz
libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'starmath/source')
-rw-r--r--starmath/source/ElementsDockingWindow.cxx750
-rw-r--r--starmath/source/SmElementsPanel.cxx98
-rw-r--r--starmath/source/SmElementsPanel.hxx56
-rw-r--r--starmath/source/SmPanelFactory.cxx136
-rw-r--r--starmath/source/SmPropertiesPanel.cxx88
-rw-r--r--starmath/source/SmPropertiesPanel.hxx51
-rw-r--r--starmath/source/accessibility.cxx738
-rw-r--r--starmath/source/accessibility.hxx134
-rw-r--r--starmath/source/action.cxx53
-rw-r--r--starmath/source/caret.cxx28
-rw-r--r--starmath/source/cfgitem.cxx1433
-rw-r--r--starmath/source/cursor.cxx1561
-rw-r--r--starmath/source/dialog.cxx2055
-rw-r--r--starmath/source/document.cxx1232
-rw-r--r--starmath/source/edit.cxx863
-rw-r--r--starmath/source/eqnolefilehdr.cxx53
-rw-r--r--starmath/source/eqnolefilehdr.hxx74
-rw-r--r--starmath/source/format.cxx153
-rw-r--r--starmath/source/mathml/attribute.cxx475
-rw-r--r--starmath/source/mathml/def.cxx124
-rw-r--r--starmath/source/mathml/element.cxx136
-rw-r--r--starmath/source/mathml/export.cxx1110
-rw-r--r--starmath/source/mathml/import.cxx1430
-rw-r--r--starmath/source/mathml/iterator.cxx77
-rw-r--r--starmath/source/mathml/mathmlMo.cxx1127
-rw-r--r--starmath/source/mathml/mathmlattr.cxx165
-rw-r--r--starmath/source/mathml/mathmlexport.cxx1457
-rw-r--r--starmath/source/mathml/mathmlimport.cxx2696
-rw-r--r--starmath/source/mathml/starmathdatabase.cxx794
-rw-r--r--starmath/source/mathml/xparsmlbase.cxx2166
-rw-r--r--starmath/source/mathtype.cxx3354
-rw-r--r--starmath/source/mathtype.hxx184
-rw-r--r--starmath/source/node.cxx2486
-rw-r--r--starmath/source/ooxmlexport.cxx598
-rw-r--r--starmath/source/ooxmlexport.hxx46
-rw-r--r--starmath/source/ooxmlimport.cxx686
-rw-r--r--starmath/source/ooxmlimport.hxx51
-rw-r--r--starmath/source/parse.cxx52
-rw-r--r--starmath/source/parse5.cxx2773
-rw-r--r--starmath/source/parsebase.cxx50
-rw-r--r--starmath/source/rect.cxx621
-rw-r--r--starmath/source/rtfexport.cxx502
-rw-r--r--starmath/source/rtfexport.hxx42
-rw-r--r--starmath/source/smdetect.cxx148
-rw-r--r--starmath/source/smdetect.hxx46
-rw-r--r--starmath/source/smdll.cxx90
-rw-r--r--starmath/source/smediteng.cxx158
-rw-r--r--starmath/source/smmod.cxx237
-rw-r--r--starmath/source/symbol.cxx273
-rw-r--r--starmath/source/tmpdevice.cxx66
-rw-r--r--starmath/source/tmpdevice.hxx46
-rw-r--r--starmath/source/typemap.cxx39
-rw-r--r--starmath/source/unodoc.cxx46
-rw-r--r--starmath/source/unofilter.cxx116
-rw-r--r--starmath/source/unomodel.cxx1087
-rw-r--r--starmath/source/utility.cxx247
-rw-r--r--starmath/source/view.cxx2269
-rw-r--r--starmath/source/visitors.cxx2727
-rw-r--r--starmath/source/wordexportbase.cxx184
-rw-r--r--starmath/source/wordexportbase.hxx48
60 files changed, 40585 insertions, 0 deletions
diff --git a/starmath/source/ElementsDockingWindow.cxx b/starmath/source/ElementsDockingWindow.cxx
new file mode 100644
index 000000000..a3a4085a7
--- /dev/null
+++ b/starmath/source/ElementsDockingWindow.cxx
@@ -0,0 +1,750 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <ElementsDockingWindow.hxx>
+
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+#include <parse.hxx>
+#include <view.hxx>
+#include <visitors.hxx>
+#include <document.hxx>
+#include <strings.hxx>
+
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+#include <vcl/uitest/logger.hxx>
+#include <vcl/virdev.hxx>
+
+#include <unordered_map>
+#include <utility>
+
+namespace
+{
+// element, element help, element visual, element visual's translatable
+typedef std::tuple<std::string_view, TranslateId, std::u16string_view, TranslateId> SmElementDescr;
+
+// SmParser 5 elements
+
+const SmElementDescr s_a5UnaryBinaryOperatorsList[] =
+{
+ {RID_PLUSX, RID_PLUSX_HELP, {}, {}},
+ {RID_MINUSX, RID_MINUSX_HELP, {}, {}},
+ {RID_PLUSMINUSX, RID_PLUSMINUSX_HELP, {}, {}},
+ {RID_MINUSPLUSX, RID_MINUSPLUSX_HELP, {}, {}},
+ {},
+ {RID_XPLUSY, RID_XPLUSY_HELP, {}, {}},
+ {RID_XMINUSY, RID_XMINUSY_HELP, {}, {}},
+ {RID_XCDOTY, RID_XCDOTY_HELP, {}, {}},
+ {RID_XTIMESY, RID_XTIMESY_HELP, {}, {}},
+ {RID_XSYMTIMESY, RID_XSYMTIMESY_HELP, {}, {}},
+ {RID_XOVERY, RID_XOVERY_HELP, {}, {}},
+ {RID_FRACXY, RID_FRACXY_HELP, {}, {}},
+ {RID_XDIVY, RID_XDIVY_HELP, {}, {}},
+ {RID_XSYMDIVIDEY, RID_XSYMDIVIDEY_HELP, {}, {}},
+ {RID_XOPLUSY, RID_XOPLUSY_HELP, {}, {}},
+ {RID_XOMINUSY, RID_XOMINUSY_HELP, {}, {}},
+ {RID_XODOTY, RID_XODOTY_HELP, {}, {}},
+ {RID_XOTIMESY, RID_XOTIMESY_HELP, {}, {}},
+ {RID_XODIVIDEY, RID_XODIVIDEY_HELP, {}, {}},
+ {RID_XCIRCY, RID_XCIRCY_HELP, {}, {}},
+ {RID_XWIDESLASHY, RID_XWIDESLASHY_HELP, {}, {}},
+ {RID_XWIDEBSLASHY, RID_XWIDEBSLASHY_HELP, {}, {}},
+ {},
+ {RID_NEGX, RID_NEGX_HELP, {}, {}},
+ {RID_XANDY, RID_XANDY_HELP, {}, {}},
+ {RID_XORY, RID_XORY_HELP, {}, {}}
+};
+
+const SmElementDescr s_a5RelationsList[] =
+{
+ {RID_XEQY, RID_XEQY_HELP, {}, {}},
+ {RID_XNEQY, RID_XNEQY_HELP, {}, {}},
+ {RID_XLTY, RID_XLTY_HELP, {}, {}},
+ {RID_XLEY, RID_XLEY_HELP, {}, {}},
+ {RID_XLESLANTY, RID_XLESLANTY_HELP, {}, {}},
+ {RID_XGTY, RID_XGTY_HELP, {}, {}},
+ {RID_XGEY, RID_XGEY_HELP, {}, {}},
+ {RID_XGESLANTY, RID_XGESLANTY_HELP, {}, {}},
+ {RID_XLLY, RID_XLLY_HELP, {}, {}},
+ {RID_XGGY, RID_XGGY_HELP, {}, {}},
+ {},
+ {RID_XAPPROXY, RID_XAPPROXY_HELP, {}, {}},
+ {RID_XSIMY, RID_XSIMY_HELP, {}, {}},
+ {RID_XSIMEQY, RID_XSIMEQY_HELP, {}, {}},
+ {RID_XEQUIVY, RID_XEQUIVY_HELP, {}, {}},
+ {RID_XPROPY, RID_XPROPY_HELP, {}, {}},
+ {RID_XPARALLELY, RID_XPARALLELY_HELP, {}, {}},
+ {RID_XORTHOY, RID_XORTHOY_HELP, {}, {}},
+ {RID_XDIVIDESY, RID_XDIVIDESY_HELP, {}, {}},
+ {RID_XNDIVIDESY, RID_XNDIVIDESY_HELP, {}, {}},
+ {RID_XTOWARDY, RID_XTOWARDY_HELP, {}, {}},
+ {RID_XTRANSLY, RID_XTRANSLY_HELP, {}, {}},
+ {RID_XTRANSRY, RID_XTRANSRY_HELP, {}, {}},
+ {RID_XDEFY, RID_XDEFY_HELP, {}, {}},
+ {},
+ {RID_DLARROW, RID_DLARROW_HELP, {}, {}},
+ {RID_DLRARROW, RID_DLRARROW_HELP, {}, {}},
+ {RID_DRARROW, RID_DRARROW_HELP, {}, {}},
+ {},
+ {RID_XPRECEDESY, RID_XPRECEDESY_HELP, {}, {}},
+ {RID_XSUCCEEDSY, RID_XSUCCEEDSY_HELP, {}, {}},
+ {RID_XPRECEDESEQUALY, RID_XPRECEDESEQUALY_HELP, {}, {}},
+ {RID_XSUCCEEDSEQUALY, RID_XSUCCEEDSEQUALY_HELP, {}, {}},
+ {RID_XPRECEDESEQUIVY, RID_XPRECEDESEQUIVY_HELP, {}, {}},
+ {RID_XSUCCEEDSEQUIVY, RID_XSUCCEEDSEQUIVY_HELP, {}, {}},
+ {RID_XNOTPRECEDESY, RID_XNOTPRECEDESY_HELP, {}, {}},
+ {RID_XNOTSUCCEEDSY, RID_XNOTSUCCEEDSY_HELP, {}, {}},
+};
+
+const SmElementDescr s_a5SetOperationsList[] =
+{
+ {RID_XINY, RID_XINY_HELP, {}, {}},
+ {RID_XNOTINY, RID_XNOTINY_HELP, {}, {}},
+ {RID_XOWNSY, RID_XOWNSY_HELP, {}, {}},
+ {},
+ {RID_XINTERSECTIONY, RID_XINTERSECTIONY_HELP, {}, {}},
+ {RID_XUNIONY, RID_XUNIONY_HELP, {}, {}},
+ {RID_XSETMINUSY, RID_XSETMINUSY_HELP, {}, {}},
+ {RID_XSETQUOTIENTY, RID_XSETQUOTIENTY_HELP, {}, {}},
+ {RID_XSUBSETY, RID_XSUBSETY_HELP, {}, {}},
+ {RID_XSUBSETEQY, RID_XSUBSETEQY_HELP, {}, {}},
+ {RID_XSUPSETY, RID_XSUPSETY_HELP, {}, {}},
+ {RID_XSUPSETEQY, RID_XSUPSETEQY_HELP, {}, {}},
+ {RID_XNSUBSETY, RID_XNSUBSETY_HELP, {}, {}},
+ {RID_XNSUBSETEQY, RID_XNSUBSETEQY_HELP, {}, {}},
+ {RID_XNSUPSETY, RID_XNSUPSETY_HELP, {}, {}},
+ {RID_XNSUPSETEQY, RID_XNSUPSETEQY_HELP, {}, {}},
+ {},
+ {RID_EMPTYSET, RID_EMPTYSET_HELP, {}, {}},
+ {RID_ALEPH, RID_ALEPH_HELP, {}, {}},
+ {RID_SETN, RID_SETN_HELP, {}, {}},
+ {RID_SETZ, RID_SETZ_HELP, {}, {}},
+ {RID_SETQ, RID_SETQ_HELP, {}, {}},
+ {RID_SETR, RID_SETR_HELP, {}, {}},
+ {RID_SETC, RID_SETC_HELP, {}, {}}
+};
+
+const SmElementDescr s_a5FunctionsList[] =
+{
+ {RID_ABSX, RID_ABSX_HELP, {}, {}},
+ {RID_FACTX, RID_FACTX_HELP, {}, {}},
+ {RID_SQRTX, RID_SQRTX_HELP, {}, {}},
+ {RID_NROOTXY, RID_NROOTXY_HELP, {}, {}},
+ {RID_RSUPX, RID_RSUPX_HELP, {}, {}},
+ {RID_EX, RID_EX_HELP, {}, {}},
+ {RID_LNX, RID_LNX_HELP, {}, {}},
+ {RID_EXPX, RID_EXPX_HELP, {}, {}},
+ {RID_LOGX, RID_LOGX_HELP, {}, {}},
+ {},
+ {RID_SINX, RID_SINX_HELP, {}, {}},
+ {RID_COSX, RID_COSX_HELP, {}, {}},
+ {RID_TANX, RID_TANX_HELP, {}, {}},
+ {RID_COTX, RID_COTX_HELP, {}, {}},
+ {RID_SINHX, RID_SINHX_HELP, {}, {}},
+ {RID_COSHX, RID_COSHX_HELP, {}, {}},
+ {RID_TANHX, RID_TANHX_HELP, {}, {}},
+ {RID_COTHX, RID_COTHX_HELP, {}, {}},
+ {},
+ {RID_ARCSINX, RID_ARCSINX_HELP, {}, {}},
+ {RID_ARCCOSX, RID_ARCCOSX_HELP, {}, {}},
+ {RID_ARCTANX, RID_ARCTANX_HELP, {}, {}},
+ {RID_ARCCOTX, RID_ARCCOTX_HELP, {}, {}},
+ {RID_ARSINHX, RID_ARSINHX_HELP, {}, {}},
+ {RID_ARCOSHX, RID_ARCOSHX_HELP, {}, {}},
+ {RID_ARTANHX, RID_ARTANHX_HELP, {}, {}},
+ {RID_ARCOTHX, RID_ARCOTHX_HELP, {}, {}},
+ {},
+ {RID_FUNCX, RID_FUNCX_HELP, {}, {}},
+};
+
+const SmElementDescr s_a5OperatorsList[] =
+{
+ {RID_LIMX, RID_LIMX_HELP, {}, {}},
+ {RID_LIM_FROMX, RID_LIM_FROMX_HELP, {}, {}},
+ {RID_LIM_TOX, RID_LIM_TOX_HELP, {}, {}},
+ {RID_LIM_FROMTOX, RID_LIM_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_LIMINFX, RID_LIMINFX_HELP, {}, {}},
+ {RID_LIMINF_FROMX, RID_LIMINF_FROMX_HELP, {}, {}},
+ {RID_LIMINF_TOX, RID_LIMINF_TOX_HELP, {}, {}},
+ {RID_LIMINF_FROMTOX, RID_LIMINF_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_LIMSUPX, RID_LIMSUPX_HELP, {}, {}},
+ {RID_LIMSUP_FROMX, RID_LIMSUP_FROMX_HELP, {}, {}},
+ {RID_LIMSUP_TOX, RID_LIMSUP_TOX_HELP, {}, {}},
+ {RID_LIMSUP_FROMTOX, RID_LIMSUP_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_SUMX, RID_SUMX_HELP, {}, {}},
+ {RID_SUM_FROMX, RID_SUM_FROMX_HELP, {}, {}},
+ {RID_SUM_TOX, RID_SUM_TOX_HELP, {}, {}},
+ {RID_SUM_FROMTOX, RID_SUM_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_PRODX, RID_PRODX_HELP, {}, {}},
+ {RID_PROD_FROMX, RID_PROD_FROMX_HELP, {}, {}},
+ {RID_PROD_TOX, RID_PROD_TOX_HELP, {}, {}},
+ {RID_PROD_FROMTOX, RID_PROD_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_COPRODX, RID_COPRODX_HELP, {}, {}},
+ {RID_COPROD_FROMX, RID_COPROD_FROMX_HELP, {}, {}},
+ {RID_COPROD_TOX, RID_COPROD_TOX_HELP, {}, {}},
+ {RID_COPROD_FROMTOX, RID_COPROD_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_INTX, RID_INTX_HELP, {}, {}},
+ {RID_INT_FROMX, RID_INT_FROMX_HELP, {}, {}},
+ {RID_INT_TOX, RID_INT_TOX_HELP, {}, {}},
+ {RID_INT_FROMTOX, RID_INT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_IINTX, RID_IINTX_HELP, {}, {}},
+ {RID_IINT_FROMX, RID_IINT_FROMX_HELP, {}, {}},
+ {RID_IINT_TOX, RID_IINT_TOX_HELP, {}, {}},
+ {RID_IINT_FROMTOX, RID_IINT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_IIINTX, RID_IIINTX_HELP, {}, {}},
+ {RID_IIINT_FROMX, RID_IIINT_FROMX_HELP, {}, {}},
+ {RID_IIINT_TOX, RID_IIINT_TOX_HELP, {}, {}},
+ {RID_IIINT_FROMTOX, RID_IIINT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_LINTX, RID_LINTX_HELP, {}, {}},
+ {RID_LINT_FROMX, RID_LINT_FROMX_HELP, {}, {}},
+ {RID_LINT_TOX, RID_LINT_TOX_HELP, {}, {}},
+ {RID_LINT_FROMTOX, RID_LINT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_LLINTX, RID_LLINTX_HELP, {}, {}},
+ {RID_LLINT_FROMX, RID_LLINT_FROMX_HELP, {}, {}},
+ {RID_LLINT_TOX, RID_LLINT_TOX_HELP, {}, {}},
+ {RID_LLINT_FROMTOX, RID_LLINT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_LLLINTX, RID_LLLINTX_HELP, {}, {}},
+ {RID_LLLINT_FROMX, RID_LLLINT_FROMX_HELP, {}, {}},
+ {RID_LLLINT_TOX, RID_LLLINT_TOX_HELP, {}, {}},
+ {RID_LLLINT_FROMTOX, RID_LLLINT_FROMTOX_HELP, {}, {}},
+ {},
+ {RID_OPERX, RID_OPERX_HELP, u"oper \xE22B <?>", {}},
+ {RID_OPER_FROMX, RID_OPER_FROMX_HELP, u"oper \xE22B from <?> <?>", {}},
+ {RID_OPER_TOX, RID_OPER_TOX_HELP, u"oper \xE22B to <?> <?>", {}},
+ {RID_OPER_FROMTOX, RID_OPER_FROMTOX_HELP, u"oper \xE22B from <?> to <?> <?>", {}},
+};
+
+const SmElementDescr s_a5AttributesList[] =
+{
+ {RID_ACUTEX, RID_ACUTEX_HELP, {}, {}},
+ {RID_GRAVEX, RID_GRAVEX_HELP, {}, {}},
+ {RID_BREVEX, RID_BREVEX_HELP, {}, {}},
+ {RID_CIRCLEX, RID_CIRCLEX_HELP, {}, {}},
+ {RID_DOTX, RID_DOTX_HELP, {}, {}},
+ {RID_DDOTX, RID_DDOTX_HELP, {}, {}},
+ {RID_DDDOTX, RID_DDDOTX_HELP, {}, {}},
+ {RID_BARX, RID_BARX_HELP, {}, {}},
+ {RID_VECX, RID_VECX_HELP, {}, {}},
+ {RID_HARPOONX, RID_HARPOONX_HELP, {}, {}},
+ {RID_TILDEX, RID_TILDEX_HELP, {}, {}},
+ {RID_HATX, RID_HATX_HELP, {}, {}},
+ {RID_CHECKX, RID_CHECKX_HELP, {}, {}},
+ {},
+ {RID_WIDEVECX, RID_WIDEVECX_HELP, {}, {}},
+ {RID_WIDEHARPOONX, RID_WIDEHARPOONX_HELP, {}, {}},
+ {RID_WIDETILDEX, RID_WIDETILDEX_HELP, {}, {}},
+ {RID_WIDEHATX, RID_WIDEHATX_HELP, {}, {}},
+ {RID_OVERLINEX, RID_OVERLINEX_HELP, {}, {}},
+ {RID_UNDERLINEX, RID_UNDERLINEX_HELP, {}, {}},
+ {RID_OVERSTRIKEX, RID_OVERSTRIKEX_HELP, {}, {}},
+ {},
+ {RID_PHANTOMX, RID_PHANTOMX_HELP, u"\"$1\"", STR_HIDE},
+ {RID_BOLDX, RID_BOLDX_HELP, u"bold B", {}},
+ {RID_ITALX, RID_ITALX_HELP, u"ital I", {}},
+ {RID_SIZEXY, RID_SIZEXY_HELP, u"\"$1\"", STR_SIZE},
+ {RID_FONTXY, RID_FONTXY_HELP, u"\"$1\"", STR_FONT},
+ {},
+ {RID_COLORX_BLACK, RID_COLORX_BLACK_HELP, u"color black { \"$1\" }", STR_BLACK},
+ {RID_COLORX_BLUE, RID_COLORX_BLUE_HELP, u"color blue { \"$1\" }", STR_BLUE},
+ {RID_COLORX_GREEN, RID_COLORX_GREEN_HELP, u"color green { \"$1\" }", STR_GREEN},
+ {RID_COLORX_RED, RID_COLORX_RED_HELP, u"color red { \"$1\" }", STR_RED},
+ {RID_COLORX_AQUA, RID_COLORX_AQUA_HELP, u"color aqua { \"$1\" }", STR_AQUA},
+ {RID_COLORX_FUCHSIA, RID_COLORX_FUCHSIA_HELP, u"color fuchsia { \"$1\" }", STR_FUCHSIA},
+ {RID_COLORX_YELLOW, RID_COLORX_YELLOW_HELP, u"color yellow { \"$1\" }", STR_YELLOW},
+ {RID_COLORX_GRAY, RID_COLORX_GRAY_HELP, u"color gray { \"$1\" }", STR_GRAY},
+ {RID_COLORX_LIME, RID_COLORX_LIME_HELP, u"color lime { \"$1\" }", STR_LIME},
+ {RID_COLORX_MAROON, RID_COLORX_MAROON_HELP, u"color maroon { \"$1\" }", STR_MAROON},
+ {RID_COLORX_NAVY, RID_COLORX_NAVY_HELP, u"color navy { \"$1\" }", STR_NAVY},
+ {RID_COLORX_OLIVE, RID_COLORX_OLIVE_HELP, u"color olive { \"$1\" }", STR_OLIVE},
+ {RID_COLORX_PURPLE, RID_COLORX_PURPLE_HELP, u"color purple { \"$1\" }", STR_PURPLE},
+ {RID_COLORX_SILVER, RID_COLORX_SILVER_HELP, u"color silver { \"$1\" }", STR_SILVER},
+ {RID_COLORX_TEAL, RID_COLORX_TEAL_HELP, u"color teal { \"$1\" }", STR_TEAL},
+ {RID_COLORX_RGB, RID_COLORX_RGB_HELP, u"color rgb 0 0 0 { \"$1\" }", STR_RGB},
+ //{RID_COLORX_RGBA, RID_COLORX_RGBA_HELP, u"color rgba 0 0 0 0 { \"$1\" }", STR_RGBA},
+ {RID_COLORX_HEX, RID_COLORX_HEX_HELP, u"color hex 000000 { \"$1\" }", STR_HEX},
+ {},
+ {RID_COLORX_CORAL, RID_COLORX_CORAL_HELP, u"color coral { \"$1\" }", STR_CORAL},
+ {RID_COLORX_MIDNIGHT, RID_COLORX_MIDNIGHT_HELP, u"color midnightblue { \"$1\" }", STR_MIDNIGHT},
+ {RID_COLORX_CRIMSON, RID_COLORX_CRIMSON_HELP, u"color crimson { \"$1\" }", STR_CRIMSON},
+ {RID_COLORX_VIOLET, RID_COLORX_VIOLET_HELP, u"color violet { \"$1\" }", STR_VIOLET},
+ {RID_COLORX_ORANGE, RID_COLORX_ORANGE_HELP, u"color orange { \"$1\" }", STR_ORANGE},
+ {RID_COLORX_ORANGERED, RID_COLORX_ORANGERED_HELP, u"color orangered { \"$1\" }", STR_ORANGERED},
+ {RID_COLORX_SEAGREEN, RID_COLORX_SEAGREEN_HELP, u"color seagreen { \"$1\" }", STR_SEAGREEN},
+ {RID_COLORX_INDIGO, RID_COLORX_INDIGO_HELP, u"color indigo { \"$1\" }", STR_INDIGO},
+ {RID_COLORX_HOTPINK, RID_COLORX_HOTPINK_HELP, u"color hotpink { \"$1\" }", STR_HOTPINK},
+ {RID_COLORX_LAVENDER, RID_COLORX_LAVENDER_HELP, u"color lavender { \"$1\" }", STR_LAVENDER},
+ {RID_COLORX_SNOW, RID_COLORX_SNOW_HELP, u"color snow { \"$1\" }", STR_SNOW},
+};
+
+const SmElementDescr s_a5BracketsList[] =
+{
+ {RID_LRGROUPX, RID_LRGROUPX_HELP, {}, {}},
+ {},
+ {RID_LRPARENTX, RID_LRPARENTX_HELP, {}, {}},
+ {RID_LRBRACKETX, RID_LRBRACKETX_HELP, {}, {}},
+ {RID_LRDBRACKETX, RID_LRDBRACKETX_HELP, {}, {}},
+ {RID_LRBRACEX, RID_LRBRACEX_HELP, {}, {}},
+ {RID_LRANGLEX, RID_LRANGLEX_HELP, {}, {}},
+ {RID_LMRANGLEXY, RID_LMRANGLEXY_HELP, {}, {}},
+ {RID_LRCEILX, RID_LRCEILX_HELP, {}, {}},
+ {RID_LRFLOORX, RID_LRFLOORX_HELP, {}, {}},
+ {RID_LRLINEX, RID_LRLINEX_HELP, {}, {}},
+ {RID_LRDLINEX, RID_LRDLINEX_HELP, {}, {}},
+ {},
+ {RID_SLRPARENTX, RID_SLRPARENTX_HELP, u"left ( binom{<?>}{<?>} right )", {}},
+ {RID_SLRBRACKETX, RID_SLRBRACKETX_HELP, u"left [ binom{<?>}{<?>} right ]", {}},
+ {RID_SLRDBRACKETX, RID_SLRDBRACKETX_HELP, u"left ldbracket binom{<?>}{<?>} right rdbracket", {}},
+ {RID_SLRBRACEX, RID_SLRBRACEX_HELP, u"left lbrace binom{<?>}{<?>} right rbrace", {}},
+ {RID_SLRANGLEX, RID_SLRANGLEX_HELP, u"left langle binom{<?>}{<?>} right rangle", {}},
+ {RID_SLMRANGLEXY, RID_SLMRANGLEXY_HELP, u"left langle binom{<?>}{<?>} mline binom{<?>}{<?>} right rangle", {}},
+ {RID_SLRCEILX, RID_SLRCEILX_HELP, u"left lceil binom{<?>}{<?>} right rceil", {}},
+ {RID_SLRFLOORX, RID_SLRFLOORX_HELP, u"left lfloor binom{<?>}{<?>} right rfloor", {}},
+ {RID_SLRLINEX, RID_SLRLINEX_HELP, u"left lline binom{<?>}{<?>} right rline", {}},
+ {RID_SLRDLINEX, RID_SLRDLINEX_HELP, u"left ldline binom{<?>}{<?>} right rdline", {}},
+ {},
+ {RID_XOVERBRACEY, RID_XOVERBRACEY_HELP, u"{<?><?><?>} overbrace {<?>}", {}},
+ {RID_XUNDERBRACEY, RID_XUNDERBRACEY_HELP, u"{<?><?><?>} underbrace {<?>} ", {}},
+ {},
+ {RID_EVALX, RID_EVALUATEX_HELP, {}, {}},
+ {RID_EVAL_FROMX, RID_EVALUATE_FROMX_HELP, {}, {}},
+ {RID_EVAL_TOX, RID_EVALUATE_TOX_HELP, {}, {}},
+ {RID_EVAL_FROMTOX, RID_EVALUATE_FROMTOX_HELP, {}, {}},
+};
+
+const SmElementDescr s_a5FormatsList[] =
+{
+ {RID_RSUPX, RID_RSUPX_HELP, {}, {}},
+ {RID_RSUBX, RID_RSUBX_HELP, {}, {}},
+ {RID_LSUPX, RID_LSUPX_HELP, {}, {}},
+ {RID_LSUBX, RID_LSUBX_HELP, {}, {}},
+ {RID_CSUPX, RID_CSUPX_HELP, {}, {}},
+ {RID_CSUBX, RID_CSUBX_HELP, {}, {}},
+ {},
+ {RID_NEWLINE, RID_NEWLINE_HELP, u"\u21B5", {}},
+ {RID_SBLANK, RID_SBLANK_HELP, u"\"`\"", {}},
+ {RID_BLANK, RID_BLANK_HELP, u"\"~\"", {}},
+ {RID_NOSPACE, RID_NOSPACE_HELP, {}, {}},
+ {RID_ALIGNLX, RID_ALIGNLX_HELP, u"\"$1\"", STR_ALIGN_LEFT},
+ {RID_ALIGNCX, RID_ALIGNCX_HELP, u"\"$1\"", STR_ALIGN_CENTER},
+ {RID_ALIGNRX, RID_ALIGNRX_HELP, u"\"$1\"", STR_ALIGN_RIGHT},
+ {},
+ {RID_BINOMXY, RID_BINOMXY_HELP, {}, {}},
+ {RID_STACK, RID_STACK_HELP, {}, {}},
+ {RID_MATRIX, RID_MATRIX_HELP, {}, {}},
+};
+
+const SmElementDescr s_a5OthersList[] =
+{
+ {RID_INFINITY, RID_INFINITY_HELP, {}, {}},
+ {RID_PARTIAL, RID_PARTIAL_HELP, {}, {}},
+ {RID_NABLA, RID_NABLA_HELP, {}, {}},
+ {RID_EXISTS, RID_EXISTS_HELP, {}, {}},
+ {RID_NOTEXISTS, RID_NOTEXISTS_HELP, {}, {}},
+ {RID_FORALL, RID_FORALL_HELP, {}, {}},
+ {RID_HBAR, RID_HBAR_HELP, {}, {}},
+ {RID_LAMBDABAR, RID_LAMBDABAR_HELP, {}, {}},
+ {RID_RE, RID_RE_HELP, {}, {}},
+ {RID_IM, RID_IM_HELP, {}, {}},
+ {RID_WP, RID_WP_HELP, {}, {}},
+ {RID_LAPLACE, RID_LAPLACE_HELP, {}, {}},
+ {RID_FOURIER, RID_FOURIER_HELP, {}, {}},
+ {RID_BACKEPSILON, RID_BACKEPSILON_HELP, {}, {}},
+ {},
+ {RID_LEFTARROW, RID_LEFTARROW_HELP, {}, {}},
+ {RID_RIGHTARROW, RID_RIGHTARROW_HELP, {}, {}},
+ {RID_UPARROW, RID_UPARROW_HELP, {}, {}},
+ {RID_DOWNARROW, RID_DOWNARROW_HELP, {}, {}},
+ {},
+ {RID_DOTSLOW, RID_DOTSLOW_HELP, {}, {}},
+ {RID_DOTSAXIS, RID_DOTSAXIS_HELP, {}, {}},
+ {RID_DOTSVERT, RID_DOTSVERT_HELP, {}, {}},
+ {RID_DOTSUP, RID_DOTSUP_HELP, {}, {}},
+ {RID_DOTSDOWN, RID_DOTSDOWN_HELP, {}, {}},
+};
+
+const SmElementDescr s_a5ExamplesList[] =
+{
+ {"{func e}^{i %pi} + 1 = 0", RID_EXAMPLE_EULER_IDENTITY_HELP, {}, {}},
+ {"C = %pi cdot d = 2 cdot %pi cdot r", RID_EXAMPLE_CIRCUMFERENCE_HELP, {}, {}},
+ {"c = sqrt{ a^2 + b^2 }", RID_EXAMPLE_PYTHAGOREAN_THEO_HELP, {}, {}},
+ {"vec F = m times vec a", RID_EXAMPLE_2NEWTON, {}, {}},
+ {"E = m c^2", RID_EXAMPLE_MASS_ENERGY_EQUIV_HELP, {}, {}},
+ {"G_{%mu %nu} + %LAMBDA g_{%mu %nu}= frac{8 %pi G}{c^4} T_{%mu %nu}", RID_EXAMPLE_GENERAL_RELATIVITY_HELP, {}, {}},
+ {"%DELTA t' = { %DELTA t } over sqrt{ 1 - v^2 over c^2 }", RID_EXAMPLE_SPECIAL_RELATIVITY_HELP, {}, {}},
+ {"d over dt left( {partial L}over{partial dot q} right) = {partial L}over{partial q}", RID_EXAMPLE_EULER_LAGRANGE_HELP, {}, {}},
+ {"int from a to b f'(x) dx = f(b) - f(a)", RID_EXAMPLE_FTC_HELP, {}, {}},
+ {"ldline %delta bold{r}(t) rdline approx e^{%lambda t} ldline %delta { bold{r} }_0 rdline", RID_EXAMPLE_CHAOS_HELP, {}, {}},
+ {"f(x) = sum from { n=0 } to { infinity } { {f^{(n)}(x_0) } over { fact{n} } (x-x_0)^n }", RID_EXAMPLE_A_TAYLOR_SERIES_HELP, {}, {}},
+ {"f(x) = {1} over { %sigma sqrt{2 %pi} } func e^-{ {(x-%mu)^2} over {2 %sigma^2} }", RID_EXAMPLE_GAUSS_DISTRIBUTION_HELP, {}, {}},
+};
+
+const std::vector<TranslateId> s_a5Categories{
+ RID_CATEGORY_UNARY_BINARY_OPERATORS,
+ RID_CATEGORY_RELATIONS,
+ RID_CATEGORY_SET_OPERATIONS,
+ RID_CATEGORY_FUNCTIONS,
+ RID_CATEGORY_OPERATORS,
+ RID_CATEGORY_ATTRIBUTES,
+ RID_CATEGORY_BRACKETS,
+ RID_CATEGORY_FORMATS,
+ RID_CATEGORY_OTHERS,
+ RID_CATEGORY_EXAMPLES,
+};
+
+template <size_t N>
+constexpr std::pair<const SmElementDescr*, size_t> asPair(const SmElementDescr (&category)[N])
+{
+ return { category, N };
+}
+
+struct TranslateId_hash
+{
+ size_t operator()(const TranslateId& val) const { return std::hash<std::string_view>()(val.mpId); }
+};
+
+const std::unordered_map<TranslateId, std::pair<const SmElementDescr*, size_t>, TranslateId_hash> s_a5CategoryDescriptions{
+ { RID_CATEGORY_UNARY_BINARY_OPERATORS, asPair(s_a5UnaryBinaryOperatorsList) },
+ { RID_CATEGORY_RELATIONS, asPair(s_a5RelationsList) },
+ { RID_CATEGORY_SET_OPERATIONS, asPair(s_a5SetOperationsList) },
+ { RID_CATEGORY_FUNCTIONS, asPair(s_a5FunctionsList) },
+ { RID_CATEGORY_OPERATORS, asPair(s_a5OperatorsList) },
+ { RID_CATEGORY_ATTRIBUTES, asPair(s_a5AttributesList) },
+ { RID_CATEGORY_BRACKETS, asPair(s_a5BracketsList) },
+ { RID_CATEGORY_FORMATS, asPair(s_a5FormatsList) },
+ { RID_CATEGORY_OTHERS, asPair(s_a5OthersList) },
+ { RID_CATEGORY_EXAMPLES, asPair(s_a5ExamplesList) },
+};
+} // namespace
+
+// static
+const std::vector<TranslateId>& SmElementsControl::categories()
+{
+ return s_a5Categories;
+}
+
+SmElementsControl::SmElementsControl(std::unique_ptr<weld::IconView> pIconView)
+ : mpDocShell(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT))
+ , m_nSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+ , mbVerticalMode(true)
+ , mpIconView(std::move(pIconView))
+{
+ maParser.reset(starmathdatabase::GetVersionSmParser(m_nSmSyntaxVersion));
+ maParser->SetImportSymbolNames(true);
+
+ mpIconView->connect_query_tooltip(LINK(this, SmElementsControl, QueryTooltipHandler));
+ mpIconView->connect_item_activated(LINK(this, SmElementsControl, ElementActivatedHandler));
+}
+
+SmElementsControl::~SmElementsControl()
+{
+ mpDocShell->DoClose();
+}
+
+void SmElementsControl::setVerticalMode(bool bVerticalMode)
+{
+ if (mbVerticalMode == bVerticalMode)
+ return;
+ mbVerticalMode = bVerticalMode;
+ build();
+}
+
+Color SmElementsControl::GetTextColor()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldTextColor();
+}
+
+Color SmElementsControl::GetControlBackground()
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ return rStyleSettings.GetFieldColor();
+}
+
+struct ElementData
+{
+ OUString maElementSource;
+ OUString maHelpText;
+ ElementData(const OUString& aElementSource, const OUString& aHelpText)
+ : maElementSource(aElementSource)
+ , maHelpText(aHelpText)
+ {
+ }
+};
+
+void SmElementsControl::addElement(const OUString& aElementVisual, const OUString& aElementSource, const OUString& aHelpText)
+{
+ std::unique_ptr<SmNode> pNode = maParser->ParseExpression(aElementVisual);
+ ScopedVclPtr<VirtualDevice> pDevice(mpIconView->create_virtual_device());
+ pDevice->SetTextRenderModeForResolutionIndependentLayout(true);
+ pDevice->SetMapMode(MapMode(MapUnit::Map100thMM));
+ pDevice->SetDrawMode(DrawModeFlags::Default);
+ pDevice->SetLayoutMode(vcl::text::ComplexTextLayoutFlags::Default);
+ pDevice->SetDigitLanguage(LANGUAGE_ENGLISH);
+
+ pDevice->SetBackground(GetControlBackground());
+ pDevice->SetTextColor(GetTextColor());
+
+ pNode->Prepare(maFormat, *mpDocShell, 0);
+ pNode->SetSize(Fraction(10,8));
+ pNode->Arrange(*pDevice, maFormat);
+
+ Size aSize = pDevice->LogicToPixel(Size(pNode->GetWidth(), pNode->GetHeight()));
+ aSize.extendBy(10, 0); // Add 5 pixels from both sides to accommodate extending parts of italics
+ pDevice->SetOutputSizePixel(aSize);
+ SmDrawingVisitor(*pDevice, pDevice->PixelToLogic(Point(5, 0)), pNode.get());
+
+ maItemDatas.push_back(std::make_unique<ElementData>(aElementSource, aHelpText));
+ const OUString aId(weld::toId(maItemDatas.back().get()));
+ mpIconView->insert(-1, nullptr, &aId, pDevice, nullptr);
+ if (mpIconView->get_item_width() < aSize.Width())
+ mpIconView->set_item_width(aSize.Width());
+}
+
+OUString SmElementsControl::GetElementSource(const OUString& itemId)
+{
+ return weld::fromId<ElementData*>(itemId)->maElementSource;
+}
+
+OUString SmElementsControl::GetElementHelpText(const OUString& itemId)
+{
+ return weld::fromId<ElementData*>(itemId)->maHelpText;
+}
+
+void SmElementsControl::setElementSetId(TranslateId pSetId)
+{
+ if (msCurrentSetId == pSetId)
+ return;
+ msCurrentSetId = pSetId;
+ build();
+}
+
+void SmElementsControl::addElements(const TranslateId& rCategory)
+{
+ mpIconView->clear(); // tdf#152411 clear before freeze to let gtk a11y drop reference
+ mpIconView->freeze();
+ mpIconView->set_item_width(0);
+ maItemDatas.clear();
+
+ const auto& [aElementsArray, aElementsArraySize] = s_a5CategoryDescriptions.at(rCategory);
+
+ for (size_t i = 0; i < aElementsArraySize; i++)
+ {
+ const auto& [element, elementHelp, elementVisual, visualTranslatable] = aElementsArray[i];
+ if (element.empty())
+ {
+ mpIconView->append_separator({});
+ }
+ else
+ {
+ OUString aElement(OUString::createFromAscii(element));
+ OUString aVisual(elementVisual.empty() ? aElement : OUString(elementVisual));
+ if (visualTranslatable)
+ aVisual = aVisual.replaceFirst("$1", SmResId(visualTranslatable));
+ OUString aHelp(elementHelp ? SmResId(elementHelp) : OUString());
+ addElement(aVisual, aElement, aHelp);
+ }
+ }
+
+ mpIconView->set_size_request(0, 0);
+ mpIconView->thaw();
+}
+
+void SmElementsControl::build()
+{
+ switch(m_nSmSyntaxVersion)
+ {
+ case 5:
+ addElements(msCurrentSetId);
+ break;
+ case 6:
+ default:
+ throw std::range_error("parser version limit");
+ }
+}
+
+void SmElementsControl::setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion)
+{
+ if( m_nSmSyntaxVersion != nSmSyntaxVersion )
+ {
+ m_nSmSyntaxVersion = nSmSyntaxVersion;
+ maParser.reset(starmathdatabase::GetVersionSmParser(nSmSyntaxVersion));
+ maParser->SetImportSymbolNames(true);
+ // Be careful, we need the parser in order to build !!!
+ build();
+ }
+}
+
+IMPL_LINK(SmElementsControl, QueryTooltipHandler, const weld::TreeIter&, iter, OUString)
+{
+ if (const OUString id = mpIconView->get_id(iter); !id.isEmpty())
+ return GetElementHelpText(id);
+ return {};
+}
+
+IMPL_LINK_NOARG(SmElementsControl, ElementActivatedHandler, weld::IconView&, bool)
+{
+ if (const OUString id = mpIconView->get_selected_id(); !id.isEmpty())
+ maSelectHdlLink.Call(GetElementSource(id));
+
+ mpIconView->unselect_all();
+ return true;
+}
+
+SmElementsDockingWindow::SmElementsDockingWindow(SfxBindings* pInputBindings, SfxChildWindow* pChildWindow, vcl::Window* pParent)
+ : SfxDockingWindow(pInputBindings, pChildWindow, pParent, "DockingElements",
+ "modules/smath/ui/dockingelements.ui")
+ , mxElementsControl(std::make_unique<SmElementsControl>(m_xBuilder->weld_icon_view("elements")))
+ , mxElementListBox(m_xBuilder->weld_combo_box("listbox"))
+{
+ // give it an arbitrary small width request so it can shrink in the sidebar
+ mxElementListBox->set_size_request(42, -1);
+
+ for (const auto& category : SmElementsControl::categories())
+ mxElementListBox->append_text(SmResId(category));
+
+ mxElementListBox->connect_changed(LINK(this, SmElementsDockingWindow, ElementSelectedHandle));
+ mxElementListBox->set_active_text(SmResId(RID_CATEGORY_UNARY_BINARY_OPERATORS));
+
+ mxElementsControl->setElementSetId(RID_CATEGORY_UNARY_BINARY_OPERATORS);
+ mxElementsControl->SetSelectHdl(LINK(this, SmElementsDockingWindow, SelectClickHandler));
+}
+
+void SmElementsDockingWindow::GetFocus()
+{
+ SfxDockingWindow::GetFocus();
+ if (mxElementListBox)
+ mxElementListBox->grab_focus();
+}
+
+SmElementsDockingWindow::~SmElementsDockingWindow ()
+{
+ disposeOnce();
+}
+
+void SmElementsDockingWindow::dispose()
+{
+ mxElementsControl.reset();
+ mxElementListBox.reset();
+ SfxDockingWindow::dispose();
+}
+
+void SmElementsDockingWindow::ToggleFloatingMode()
+{
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (GetFloatingWindow())
+ GetFloatingWindow()->SetMinOutputSizePixel( Size(100, 100) );
+
+ Invalidate();
+}
+
+void SmElementsDockingWindow::setSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion)
+{
+ mxElementsControl->setSmSyntaxVersion(nSmSyntaxVersion);
+}
+
+void SmElementsDockingWindow::EndDocking( const tools::Rectangle& rReactangle, bool bFloatMode)
+{
+ SfxDockingWindow::EndDocking(rReactangle, bFloatMode);
+ bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
+ mxElementsControl->setVerticalMode(bVertical);
+}
+
+IMPL_LINK(SmElementsDockingWindow, SelectClickHandler, OUString, sElementSource, void)
+{
+ SmViewShell* pViewSh = GetView();
+
+ if (pViewSh)
+ {
+ SfxStringItem aInsertCommand(SID_INSERTCOMMANDTEXT, sElementSource);
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD, { &aInsertCommand });
+ }
+}
+
+IMPL_LINK( SmElementsDockingWindow, ElementSelectedHandle, weld::ComboBox&, rList, void)
+{
+ for (const auto& category : SmElementsControl::categories())
+ {
+ OUString aCurrentCategoryString = SmResId(category);
+ if (aCurrentCategoryString == rList.get_active_text())
+ {
+ mxElementsControl->setElementSetId(category);
+ setSmSyntaxVersion(GetView()->GetDoc()->GetSmSyntaxVersion());
+ return;
+ }
+ }
+}
+
+SmViewShell* SmElementsDockingWindow::GetView()
+{
+ SfxViewShell* pView = GetBindings().GetDispatcher()->GetFrame()->GetViewShell();
+ return dynamic_cast<SmViewShell*>( pView);
+}
+
+void SmElementsDockingWindow::Resize()
+{
+ bool bVertical = ( GetAlignment() == SfxChildAlignment::TOP || GetAlignment() == SfxChildAlignment::BOTTOM );
+ mxElementsControl->setVerticalMode(bVertical);
+
+ SfxDockingWindow::Resize();
+ Invalidate();
+}
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(SmElementsDockingWindowWrapper, SID_ELEMENTSDOCKINGWINDOW);
+
+SmElementsDockingWindowWrapper::SmElementsDockingWindowWrapper(
+ vcl::Window *pParentWindow, sal_uInt16 nId,
+ SfxBindings *pBindings, SfxChildWinInfo *pInfo) :
+ SfxChildWindow(pParentWindow, nId)
+{
+ VclPtrInstance<SmElementsDockingWindow> pDialog(pBindings, this, pParentWindow);
+ SetWindow(pDialog);
+ pDialog->setDeferredProperties();
+ pDialog->SetPosSizePixel(Point(0, 0), Size(300, 0));
+ pDialog->Show();
+
+ SetAlignment(SfxChildAlignment::LEFT);
+
+ pDialog->Initialize( pInfo );
+}
+
+SmElementsDockingWindowWrapper::~SmElementsDockingWindowWrapper()
+{
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/SmElementsPanel.cxx b/starmath/source/SmElementsPanel.cxx
new file mode 100644
index 000000000..165e950ea
--- /dev/null
+++ b/starmath/source/SmElementsPanel.cxx
@@ -0,0 +1,98 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <sfx2/dispatch.hxx>
+#include <svl/stritem.hxx>
+
+#include "SmElementsPanel.hxx"
+#include <starmath.hrc>
+#include <smmod.hxx>
+#include <strings.hrc>
+#include <view.hxx>
+
+namespace sm::sidebar
+{
+// static
+std::unique_ptr<PanelLayout> SmElementsPanel::Create(weld::Widget& rParent,
+ const SfxBindings& rBindings)
+{
+ return std::make_unique<SmElementsPanel>(rParent, rBindings);
+}
+
+SmElementsPanel::SmElementsPanel(weld::Widget& rParent, const SfxBindings& rBindings)
+ : PanelLayout(&rParent, "MathElementsPanel", "modules/smath/ui/sidebarelements_math.ui")
+ , mrBindings(rBindings)
+ , mxCategoryList(m_xBuilder->weld_tree_view("categorylist"))
+ , mxElementsControl(std::make_unique<SmElementsControl>(m_xBuilder->weld_icon_view("elements")))
+{
+ for (const auto& rCategoryId : SmElementsControl::categories())
+ mxCategoryList->append_text(SmResId(rCategoryId));
+
+ mxCategoryList->set_size_request(-1, mxCategoryList->get_height_rows(6));
+
+ mxCategoryList->connect_changed(LINK(this, SmElementsPanel, CategorySelectedHandle));
+ mxCategoryList->select_text(SmResId(RID_CATEGORY_UNARY_BINARY_OPERATORS));
+
+ mxElementsControl->setElementSetId(RID_CATEGORY_UNARY_BINARY_OPERATORS);
+ mxElementsControl->SetSelectHdl(LINK(this, SmElementsPanel, ElementClickHandler));
+}
+
+SmElementsPanel::~SmElementsPanel()
+{
+ mxElementsControl.reset();
+ mxCategoryList.reset();
+}
+
+IMPL_LINK(SmElementsPanel, CategorySelectedHandle, weld::TreeView&, rList, void)
+{
+ const OUString sSelected = rList.get_selected_text();
+ for (const auto& rCategoryId : SmElementsControl::categories())
+ {
+ OUString aCurrentCategoryString = SmResId(rCategoryId);
+ if (aCurrentCategoryString == sSelected)
+ {
+ mxElementsControl->setElementSetId(rCategoryId);
+ if (SmViewShell* pViewSh = GetView())
+ mxElementsControl->setSmSyntaxVersion(pViewSh->GetDoc()->GetSmSyntaxVersion());
+ return;
+ }
+ }
+}
+
+IMPL_LINK(SmElementsPanel, ElementClickHandler, OUString, ElementSource, void)
+{
+ if (SmViewShell* pViewSh = GetView())
+ {
+ SfxStringItem aInsertCommand(SID_INSERTCOMMANDTEXT, ElementSource);
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_INSERTCOMMANDTEXT, SfxCallMode::RECORD, { &aInsertCommand });
+ }
+}
+
+SmViewShell* SmElementsPanel::GetView() const
+{
+ SfxViewShell* pView = mrBindings.GetDispatcher()->GetFrame()->GetViewShell();
+ return dynamic_cast<SmViewShell*>(pView);
+}
+
+} // end of namespace sm::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/SmElementsPanel.hxx b/starmath/source/SmElementsPanel.hxx
new file mode 100644
index 000000000..d80d53e72
--- /dev/null
+++ b/starmath/source/SmElementsPanel.hxx
@@ -0,0 +1,56 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <sfx2/bindings.hxx>
+#include <sfx2/sidebar/PanelLayout.hxx>
+#include <vcl/customweld.hxx>
+#include <vcl/EnumContext.hxx>
+
+#include <ElementsDockingWindow.hxx>
+
+#include <memory>
+
+namespace sm::sidebar
+{
+class SmElementsPanel : public PanelLayout
+{
+public:
+ static std::unique_ptr<PanelLayout> Create(weld::Widget& rParent, const SfxBindings& rBindings);
+ SmElementsPanel(weld::Widget& rParent, const SfxBindings& rBindings);
+ ~SmElementsPanel();
+
+private:
+ DECL_LINK(CategorySelectedHandle, weld::TreeView&, void);
+ DECL_LINK(ElementClickHandler, OUString, void);
+
+ SmViewShell* GetView() const;
+
+ const SfxBindings& mrBindings;
+
+ std::unique_ptr<weld::TreeView> mxCategoryList;
+ std::unique_ptr<SmElementsControl> mxElementsControl;
+};
+
+} // end of namespace sm::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/SmPanelFactory.cxx b/starmath/source/SmPanelFactory.cxx
new file mode 100644
index 000000000..8e9f146a1
--- /dev/null
+++ b/starmath/source/SmPanelFactory.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
+#include <com/sun/star/ui/XUIElementFactory.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+
+#include <cppuhelper/exc_hlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/namedvaluecollection.hxx>
+#include <sfx2/sidebar/SidebarPanelBase.hxx>
+#include <vcl/weldutils.hxx>
+
+#include "SmElementsPanel.hxx"
+#include "SmPropertiesPanel.hxx"
+
+namespace
+{
+typedef comphelper::WeakComponentImplHelper<css::ui::XUIElementFactory, css::lang::XServiceInfo>
+ PanelFactoryInterfaceBase;
+
+class SmPanelFactory final : public PanelFactoryInterfaceBase
+{
+public:
+ SmPanelFactory() = default;
+
+ SmPanelFactory(const SmPanelFactory&) = delete;
+ const SmPanelFactory& operator=(const SmPanelFactory&) = delete;
+
+ // XUIElementFactory
+ css::uno::Reference<css::ui::XUIElement> SAL_CALL
+ createUIElement(const OUString& ResourceURL,
+ const css::uno::Sequence<css::beans::PropertyValue>& Arguments) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(OUString const& ServiceName) override;
+ css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+
+css::uno::Reference<css::ui::XUIElement> SAL_CALL SmPanelFactory::createUIElement(
+ const OUString& ResourceURL, const css::uno::Sequence<css::beans::PropertyValue>& Arguments)
+{
+ try
+ {
+ const comphelper::NamedValueCollection aArguments(Arguments);
+ auto xFrame(aArguments.getOrDefault("Frame", css::uno::Reference<css::frame::XFrame>()));
+ auto xParentWindow(
+ aArguments.getOrDefault("ParentWindow", css::uno::Reference<css::awt::XWindow>()));
+ const sal_uInt64 nBindingsValue(aArguments.getOrDefault("SfxBindings", sal_uInt64(0)));
+ SfxBindings* pBindings = reinterpret_cast<SfxBindings*>(nBindingsValue);
+
+ weld::Widget* pParent(nullptr);
+ if (auto pTunnel = dynamic_cast<weld::TransportAsXWindow*>(xParentWindow.get()))
+ pParent = pTunnel->getWidget();
+
+ if (!pParent)
+ throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no ParentWindow");
+ if (!xFrame)
+ throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no Frame");
+ if (!pBindings)
+ throw css::uno::RuntimeException("SmPanelFactory::createUIElement: no SfxBindings");
+
+ std::unique_ptr<PanelLayout> pPanel;
+ css::ui::LayoutSize aLayoutSize{ -1, -1, -1 };
+ if (ResourceURL.endsWith("/MathPropertiesPanel"))
+ {
+ pPanel = sm::sidebar::SmPropertiesPanel::Create(*pParent);
+ }
+ else if (ResourceURL.endsWith("/MathElementsPanel"))
+ {
+ pPanel = sm::sidebar::SmElementsPanel::Create(*pParent, *pBindings);
+ aLayoutSize = { 300, -1, -1 };
+ }
+
+ if (pPanel)
+ return sfx2::sidebar::SidebarPanelBase::Create(ResourceURL, xFrame, std::move(pPanel),
+ aLayoutSize);
+ }
+ catch (const css::uno::RuntimeException&)
+ {
+ throw;
+ }
+ catch (const css::uno::Exception&)
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw css::lang::WrappedTargetRuntimeException("SmPanelFactory::createUIElement exception",
+ nullptr, anyEx);
+ }
+
+ return {};
+}
+
+OUString SmPanelFactory::getImplementationName()
+{
+ return "org.libreoffice.comp.Math.sidebar.SmPanelFactory";
+}
+
+sal_Bool SmPanelFactory::supportsService(OUString const& ServiceName)
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+css::uno::Sequence<OUString> SmPanelFactory::getSupportedServiceNames()
+{
+ return { "com.sun.star.ui.UIElementFactory" };
+}
+
+} // end of unnamed namespace
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_libreoffice_comp_Math_sidebar_SmPanelFactory(css::uno::XComponentContext*,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmPanelFactory);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/SmPropertiesPanel.cxx b/starmath/source/SmPropertiesPanel.cxx
new file mode 100644
index 000000000..3ffd25c40
--- /dev/null
+++ b/starmath/source/SmPropertiesPanel.cxx
@@ -0,0 +1,88 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/frame/theUICommandDescription.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <comphelper/dispatchcommand.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+#include "SmPropertiesPanel.hxx"
+
+namespace sm::sidebar
+{
+// static
+std::unique_ptr<PanelLayout> SmPropertiesPanel::Create(weld::Widget& rParent)
+{
+ return std::make_unique<SmPropertiesPanel>(rParent);
+}
+
+SmPropertiesPanel::SmPropertiesPanel(weld::Widget& rParent)
+ : PanelLayout(&rParent, "MathPropertiesPanel", "modules/smath/ui/sidebarproperties_math.ui")
+ , mpFormatFontsButton(m_xBuilder->weld_button("btnFormatFonts"))
+ , mpFormatFontSizeButton(m_xBuilder->weld_button("btnFormatFontSize"))
+ , mpFormatSpacingButton(m_xBuilder->weld_button("btnFormatSpacing"))
+ , mpFormatAlignmentButton(m_xBuilder->weld_button("btnFormatAlignment"))
+ , maButtonCommands{ { mpFormatFontsButton.get(), ".uno:ChangeFont" },
+ { mpFormatFontSizeButton.get(), ".uno:ChangeFontSize" },
+ { mpFormatSpacingButton.get(), ".uno:ChangeDistance" },
+ { mpFormatAlignmentButton.get(), ".uno:ChangeAlignment" } }
+{
+ // Set localized labels to the buttons
+ auto xConfs
+ = css::frame::theUICommandDescription::get(comphelper::getProcessComponentContext());
+ if (css::uno::Reference<css::container::XNameAccess> xConf{
+ xConfs->getByName("com.sun.star.formula.FormulaProperties"), css::uno::UNO_QUERY })
+ {
+ for (const auto & [ button, command ] : maButtonCommands)
+ {
+ comphelper::SequenceAsHashMap props(xConf->getByName(command));
+ button->set_label(props.getUnpackedValueOrDefault("Name", button->get_label()));
+ }
+ }
+
+ mpFormatFontsButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler));
+ mpFormatFontSizeButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler));
+ mpFormatSpacingButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler));
+ mpFormatAlignmentButton->connect_clicked(LINK(this, SmPropertiesPanel, ButtonClickHandler));
+}
+
+SmPropertiesPanel::~SmPropertiesPanel()
+{
+ maButtonCommands.clear();
+
+ mpFormatFontsButton.reset();
+ mpFormatFontSizeButton.reset();
+ mpFormatSpacingButton.reset();
+ mpFormatAlignmentButton.reset();
+}
+
+IMPL_LINK(SmPropertiesPanel, ButtonClickHandler, weld::Button&, rButton, void)
+{
+ if (OUString command = maButtonCommands[&rButton]; !command.isEmpty())
+ comphelper::dispatchCommand(command, {});
+}
+
+} // end of namespace sm::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/SmPropertiesPanel.hxx b/starmath/source/SmPropertiesPanel.hxx
new file mode 100644
index 000000000..e81463f37
--- /dev/null
+++ b/starmath/source/SmPropertiesPanel.hxx
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/config.h>
+
+#include <sfx2/sidebar/PanelLayout.hxx>
+
+#include <map>
+#include <memory>
+
+namespace sm::sidebar
+{
+class SmPropertiesPanel : public PanelLayout
+{
+public:
+ static std::unique_ptr<PanelLayout> Create(weld::Widget& rParent);
+ SmPropertiesPanel(weld::Widget& rParent);
+ ~SmPropertiesPanel();
+
+private:
+ DECL_LINK(ButtonClickHandler, weld::Button&, void);
+
+ std::unique_ptr<weld::Button> mpFormatFontsButton;
+ std::unique_ptr<weld::Button> mpFormatFontSizeButton;
+ std::unique_ptr<weld::Button> mpFormatSpacingButton;
+ std::unique_ptr<weld::Button> mpFormatAlignmentButton;
+
+ std::map<weld::Button*, OUString> maButtonCommands;
+};
+
+} // end of namespace sm::sidebar
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/accessibility.cxx b/starmath/source/accessibility.cxx
new file mode 100644
index 000000000..591bbeafb
--- /dev/null
+++ b/starmath/source/accessibility.cxx
@@ -0,0 +1,738 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/accessibility/AccessibleRole.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/accessibility/AccessibleTextType.hpp>
+#include <com/sun/star/accessibility/AccessibleEventObject.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <unotools/accessiblerelationsethelper.hxx>
+
+#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
+#include <com/sun/star/datatransfer/clipboard/XFlushableClipboard.hpp>
+#include <unotools/accessiblestatesethelper.hxx>
+#include <comphelper/accessibleeventnotifier.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <osl/diagnose.h>
+#include <tools/diagnose_ex.h>
+#include <vcl/svapp.hxx>
+#include <vcl/unohelp2.hxx>
+#include <vcl/settings.hxx>
+
+#include <tools/gen.hxx>
+
+#include <editeng/editobj.hxx>
+
+
+#include "accessibility.hxx"
+#include <document.hxx>
+#include <view.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+
+using namespace com::sun::star;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::accessibility;
+
+SmGraphicAccessible::SmGraphicAccessible(SmGraphicWidget *pGraphicWin) :
+ aAccName (SmResId(RID_DOCUMENTSTR)),
+ nClientId (0),
+ pWin (pGraphicWin)
+{
+ OSL_ENSURE( pWin, "SmGraphicAccessible: window missing" );
+}
+
+SmGraphicAccessible::~SmGraphicAccessible()
+{
+}
+
+SmDocShell * SmGraphicAccessible::GetDoc_Impl()
+{
+ SmViewShell *pView = pWin ? &pWin->GetView() : nullptr;
+ return pView ? pView->GetDoc() : nullptr;
+}
+
+OUString SmGraphicAccessible::GetAccessibleText_Impl()
+{
+ OUString aTxt;
+ SmDocShell *pDoc = GetDoc_Impl();
+ if (pDoc)
+ aTxt = pDoc->GetAccessibleText();
+ return aTxt;
+}
+
+void SmGraphicAccessible::ClearWin()
+{
+ pWin = nullptr; // implicitly results in AccessibleStateType::DEFUNC set
+
+ if ( nClientId )
+ {
+ comphelper::AccessibleEventNotifier::revokeClientNotifyDisposing( std::exchange(nClientId, 0), *this );
+ }
+}
+
+void SmGraphicAccessible::LaunchEvent(
+ const sal_Int16 nAccessibleEventId,
+ const uno::Any &rOldVal,
+ const uno::Any &rNewVal)
+{
+ AccessibleEventObject aEvt;
+ aEvt.Source = static_cast<XAccessible *>(this);
+ aEvt.EventId = nAccessibleEventId;
+ aEvt.OldValue = rOldVal;
+ aEvt.NewValue = rNewVal ;
+
+ // pass event on to event-listener's
+ if (nClientId)
+ comphelper::AccessibleEventNotifier::addEvent( nClientId, aEvt );
+}
+
+uno::Reference< XAccessibleContext > SAL_CALL SmGraphicAccessible::getAccessibleContext()
+{
+ return this;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::containsPoint( const awt::Point& aPoint )
+{
+ //! the arguments coordinates are relative to the current window !
+ //! Thus the top-left point is (0, 0)
+
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ Size aSz( pWin->GetOutputSizePixel() );
+ return aPoint.X >= 0 && aPoint.Y >= 0 &&
+ aPoint.X < aSz.Width() && aPoint.Y < aSz.Height();
+}
+
+uno::Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleAtPoint(
+ const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+ XAccessible *pRes = nullptr;
+ if (containsPoint( aPoint ))
+ pRes = this;
+ return pRes;
+}
+
+awt::Rectangle SAL_CALL SmGraphicAccessible::getBounds()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ const Point aOutPos;
+ const Size aOutSize(pWin->GetOutputSizePixel());
+ css::awt::Rectangle aRet;
+
+ aRet.X = aOutPos.X();
+ aRet.Y = aOutPos.Y();
+ aRet.Width = aOutSize.Width();
+ aRet.Height = aOutSize.Height();
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SmGraphicAccessible::getLocation()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ const css::awt::Rectangle aRect(getBounds());
+ css::awt::Point aRet;
+
+ aRet.X = aRect.X;
+ aRet.Y = aRect.Y;
+
+ return aRet;
+}
+
+awt::Point SAL_CALL SmGraphicAccessible::getLocationOnScreen()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ css::awt::Point aScreenLoc(0, 0);
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (xParent)
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext(
+ xParent->getAccessibleContext());
+ css::uno::Reference<css::accessibility::XAccessibleComponent> xParentComponent(
+ xParentContext, css::uno::UNO_QUERY);
+ OSL_ENSURE(xParentComponent.is(),
+ "WeldEditAccessible::getLocationOnScreen: no parent component!");
+ if (xParentComponent.is())
+ {
+ css::awt::Point aParentScreenLoc(xParentComponent->getLocationOnScreen());
+ css::awt::Point aOwnRelativeLoc(getLocation());
+ aScreenLoc.X = aParentScreenLoc.X + aOwnRelativeLoc.X;
+ aScreenLoc.Y = aParentScreenLoc.Y + aOwnRelativeLoc.Y;
+ }
+ }
+
+ return aScreenLoc;
+}
+
+awt::Size SAL_CALL SmGraphicAccessible::getSize()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+ Size aSz(pWin->GetOutputSizePixel());
+ return css::awt::Size(aSz.Width(), aSz.Height());
+}
+
+void SAL_CALL SmGraphicAccessible::grabFocus()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ pWin->GrabFocus();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getForeground()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ return static_cast<sal_Int32>(rDevice.GetTextColor());
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getBackground()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ Wallpaper aWall(rDevice.GetBackground());
+ Color nCol;
+ if (aWall.IsBitmap() || aWall.IsGradient())
+ nCol = Application::GetSettings().GetStyleSettings().GetWindowColor();
+ else
+ nCol = aWall.GetColor();
+ return static_cast<sal_Int32>(nCol);
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleChildCount()
+{
+ return 0;
+}
+
+Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleChild(
+ sal_Int32 /*i*/ )
+{
+ throw IndexOutOfBoundsException(); // there is no child...
+}
+
+Reference< XAccessible > SAL_CALL SmGraphicAccessible::getAccessibleParent()
+{
+ SolarMutexGuard aGuard;
+ if (!pWin)
+ throw RuntimeException();
+
+ return pWin->GetDrawingArea()->get_accessible_parent();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getAccessibleIndexInParent()
+{
+ SolarMutexGuard aGuard;
+
+ // -1 for child not found/no parent (according to specification)
+ sal_Int32 nRet = -1;
+
+ css::uno::Reference<css::accessibility::XAccessible> xParent(getAccessibleParent());
+ if (!xParent)
+ return nRet;
+
+ try
+ {
+ css::uno::Reference<css::accessibility::XAccessibleContext> xParentContext(
+ xParent->getAccessibleContext());
+
+ // iterate over parent's children and search for this object
+ if (xParentContext.is())
+ {
+ sal_Int32 nChildCount = xParentContext->getAccessibleChildCount();
+ for (sal_Int32 nChild = 0; (nChild < nChildCount) && (-1 == nRet); ++nChild)
+ {
+ css::uno::Reference<css::accessibility::XAccessible> xChild(
+ xParentContext->getAccessibleChild(nChild));
+ if (xChild.get() == this)
+ nRet = nChild;
+ }
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ TOOLS_WARN_EXCEPTION("svx", "WeldEditAccessible::getAccessibleIndexInParent");
+ }
+
+ return nRet;
+}
+
+sal_Int16 SAL_CALL SmGraphicAccessible::getAccessibleRole()
+{
+ return AccessibleRole::DOCUMENT;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getAccessibleDescription()
+{
+ SolarMutexGuard aGuard;
+ SmDocShell *pDoc = GetDoc_Impl();
+ return pDoc ? pDoc->GetText() : OUString();
+}
+
+OUString SAL_CALL SmGraphicAccessible::getAccessibleName()
+{
+ SolarMutexGuard aGuard;
+ return aAccName;
+}
+
+Reference< XAccessibleRelationSet > SAL_CALL SmGraphicAccessible::getAccessibleRelationSet()
+{
+ return new utl::AccessibleRelationSetHelper(); // empty relation set
+}
+
+Reference< XAccessibleStateSet > SAL_CALL SmGraphicAccessible::getAccessibleStateSet()
+{
+ SolarMutexGuard aGuard;
+ rtl::Reference<::utl::AccessibleStateSetHelper> pStateSet =
+ new ::utl::AccessibleStateSetHelper;
+
+ if (!pWin)
+ pStateSet->AddState( AccessibleStateType::DEFUNC );
+ else
+ {
+ pStateSet->AddState( AccessibleStateType::ENABLED );
+ pStateSet->AddState( AccessibleStateType::FOCUSABLE );
+ if (pWin->HasFocus())
+ pStateSet->AddState( AccessibleStateType::FOCUSED );
+ if (pWin->IsActive())
+ pStateSet->AddState( AccessibleStateType::ACTIVE );
+ if (pWin->IsVisible())
+ pStateSet->AddState( AccessibleStateType::SHOWING );
+ if (pWin->IsReallyVisible())
+ pStateSet->AddState( AccessibleStateType::VISIBLE );
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ if (COL_TRANSPARENT != rDevice.GetBackground().GetColor())
+ pStateSet->AddState( AccessibleStateType::OPAQUE );
+ }
+
+ return pStateSet;
+}
+
+Locale SAL_CALL SmGraphicAccessible::getLocale()
+{
+ SolarMutexGuard aGuard;
+ // should be the document language...
+ // We use the language of the localized symbol names here.
+ return Application::GetSettings().GetUILanguageTag().getLocale();
+}
+
+
+void SAL_CALL SmGraphicAccessible::addAccessibleEventListener(
+ const Reference< XAccessibleEventListener >& xListener )
+{
+ if (xListener.is())
+ {
+ SolarMutexGuard aGuard;
+ if (pWin)
+ {
+ if (!nClientId)
+ nClientId = comphelper::AccessibleEventNotifier::registerClient( );
+ comphelper::AccessibleEventNotifier::addEventListener( nClientId, xListener );
+ }
+ }
+}
+
+void SAL_CALL SmGraphicAccessible::removeAccessibleEventListener(
+ const Reference< XAccessibleEventListener >& xListener )
+{
+ if (!(xListener.is() && nClientId))
+ return;
+
+ SolarMutexGuard aGuard;
+ sal_Int32 nListenerCount = comphelper::AccessibleEventNotifier::removeEventListener( nClientId, xListener );
+ if ( !nListenerCount )
+ {
+ // no listeners anymore
+ // -> revoke ourself. This may lead to the notifier thread dying (if we were the last client),
+ // and at least to us not firing any events anymore, in case somebody calls
+ // NotifyAccessibleEvent, again
+ comphelper::AccessibleEventNotifier::revokeClient( std::exchange(nClientId, 0) );
+ }
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getCaretPosition()
+{
+ return 0;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::setCaretPosition( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (nIndex >= aTxt.getLength())
+ throw IndexOutOfBoundsException();
+ return false;
+}
+
+sal_Unicode SAL_CALL SmGraphicAccessible::getCharacter( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (nIndex >= aTxt.getLength())
+ throw IndexOutOfBoundsException();
+ return aTxt[nIndex];
+}
+
+Sequence< beans::PropertyValue > SAL_CALL SmGraphicAccessible::getCharacterAttributes(
+ sal_Int32 nIndex,
+ const uno::Sequence< OUString > & /*rRequestedAttributes*/ )
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nLen = GetAccessibleText_Impl().getLength();
+ if (0 > nIndex || nIndex >= nLen)
+ throw IndexOutOfBoundsException();
+ return Sequence< beans::PropertyValue >();
+}
+
+awt::Rectangle SAL_CALL SmGraphicAccessible::getCharacterBounds( sal_Int32 nIndex )
+{
+ SolarMutexGuard aGuard;
+
+ awt::Rectangle aRes;
+
+ if (!pWin)
+ throw RuntimeException();
+
+ // get accessible text
+ SmDocShell* pDoc = pWin->GetView().GetDoc();
+ if (!pDoc)
+ throw RuntimeException();
+ OUString aTxt( GetAccessibleText_Impl() );
+ if (0 > nIndex || nIndex > aTxt.getLength()) // aTxt.getLength() is valid
+ throw IndexOutOfBoundsException();
+
+ // find a reasonable rectangle for position aTxt.getLength().
+ bool bWasBehindText = (nIndex == aTxt.getLength());
+ if (bWasBehindText && nIndex)
+ --nIndex;
+
+ const SmNode *pTree = pDoc->GetFormulaTree();
+ const SmNode *pNode = pTree->FindNodeWithAccessibleIndex( nIndex );
+ //! pNode may be 0 if the index belongs to a char that was inserted
+ //! only for the accessible text!
+ if (pNode)
+ {
+ sal_Int32 nAccIndex = pNode->GetAccessibleIndex();
+ OSL_ENSURE( nAccIndex >= 0, "invalid accessible index" );
+ OSL_ENSURE( nIndex >= nAccIndex, "index out of range" );
+
+ OUStringBuffer aBuf;
+ pNode->GetAccessibleText(aBuf);
+ OUString aNodeText = aBuf.makeStringAndClear();
+ sal_Int32 nNodeIndex = nIndex - nAccIndex;
+ if (0 <= nNodeIndex && nNodeIndex < aNodeText.getLength())
+ {
+ // get appropriate rectangle
+ Point aOffset(pNode->GetTopLeft() - pTree->GetTopLeft());
+ Point aTLPos (pWin->GetFormulaDrawPos() + aOffset);
+ Size aSize (pNode->GetSize());
+
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ std::vector<sal_Int32> aXAry;
+ rDevice.SetFont( pNode->GetFont() );
+ rDevice.GetTextArray( aNodeText, &aXAry, 0, aNodeText.getLength() );
+ aTLPos.AdjustX(nNodeIndex > 0 ? aXAry[nNodeIndex - 1] : 0 );
+ aSize.setWidth( nNodeIndex > 0 ? aXAry[nNodeIndex] - aXAry[nNodeIndex - 1] : aXAry[nNodeIndex] );
+
+ aTLPos = rDevice.LogicToPixel( aTLPos );
+ aSize = rDevice.LogicToPixel( aSize );
+ aRes.X = aTLPos.X();
+ aRes.Y = aTLPos.Y();
+ aRes.Width = aSize.Width();
+ aRes.Height = aSize.Height();
+ }
+ }
+
+ // take rectangle from last character and move it to the right
+ if (bWasBehindText)
+ aRes.X += aRes.Width;
+
+ return aRes;
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getCharacterCount()
+{
+ SolarMutexGuard aGuard;
+ return GetAccessibleText_Impl().getLength();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getIndexAtPoint( const awt::Point& aPoint )
+{
+ SolarMutexGuard aGuard;
+
+ sal_Int32 nRes = -1;
+ if (pWin)
+ {
+ const SmNode *pTree = pWin->GetView().GetDoc()->GetFormulaTree();
+ // can be NULL! e.g. if one clicks within the window already during loading of the
+ // document (before the parser even started)
+ if (!pTree)
+ return nRes;
+
+ weld::DrawingArea* pDrawingArea = pWin->GetDrawingArea();
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ // get position relative to formula draw position
+ Point aPos( aPoint.X, aPoint.Y );
+ aPos = rDevice.PixelToLogic( aPos );
+ aPos -= pWin->GetFormulaDrawPos();
+
+ // if it was inside the formula then get the appropriate node
+ const SmNode *pNode = nullptr;
+ if (pTree->OrientedDist(aPos) <= 0)
+ pNode = pTree->FindRectClosestTo(aPos);
+
+ if (pNode)
+ {
+ // get appropriate rectangle
+ Point aOffset( pNode->GetTopLeft() - pTree->GetTopLeft() );
+ Point aTLPos ( aOffset );
+ Size aSize( pNode->GetSize() );
+
+ tools::Rectangle aRect( aTLPos, aSize );
+ if (aRect.Contains( aPos ))
+ {
+ OSL_ENSURE( pNode->IsVisible(), "node is not a leaf" );
+ OUStringBuffer aBuf;
+ pNode->GetAccessibleText(aBuf);
+ OUString aTxt = aBuf.makeStringAndClear();
+ OSL_ENSURE( !aTxt.isEmpty(), "no accessible text available" );
+
+ tools::Long nNodeX = pNode->GetLeft();
+
+ std::vector<sal_Int32> aXAry;
+ rDevice.SetFont( pNode->GetFont() );
+ rDevice.GetTextArray( aTxt, &aXAry, 0, aTxt.getLength() );
+ for (sal_Int32 i = 0; i < aTxt.getLength() && nRes == -1; ++i)
+ {
+ if (aXAry[i] + nNodeX > aPos.X())
+ nRes = i;
+ }
+ OSL_ENSURE( nRes >= 0 && nRes < aTxt.getLength(), "index out of range" );
+ OSL_ENSURE( pNode->GetAccessibleIndex() >= 0,
+ "invalid accessible index" );
+
+ nRes = pNode->GetAccessibleIndex() + nRes;
+ }
+ }
+ }
+ return nRes;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getSelectedText()
+{
+ return OUString();
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionStart()
+{
+ return -1;
+}
+
+sal_Int32 SAL_CALL SmGraphicAccessible::getSelectionEnd()
+{
+ return -1;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::setSelection(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ sal_Int32 nLen = GetAccessibleText_Impl().getLength();
+ if (0 > nStartIndex || nStartIndex >= nLen ||
+ 0 > nEndIndex || nEndIndex >= nLen)
+ throw IndexOutOfBoundsException();
+ return false;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getText()
+{
+ SolarMutexGuard aGuard;
+ return GetAccessibleText_Impl();
+}
+
+OUString SAL_CALL SmGraphicAccessible::getTextRange(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ //!! nEndIndex may be the string length per definition of the interface !!
+ //!! text should be copied exclusive that end index though. And arguments
+ //!! may be switched.
+
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ sal_Int32 nStart = std::min(nStartIndex, nEndIndex);
+ sal_Int32 nEnd = std::max(nStartIndex, nEndIndex);
+ if ((nStart > aTxt.getLength()) ||
+ (nEnd > aTxt.getLength()))
+ throw IndexOutOfBoundsException();
+ return aTxt.copy( nStart, nEnd - nStart );
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+ if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex, 1);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex+1;
+ }
+ return aResult;
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ if ( (AccessibleTextType::CHARACTER == aTextType) && nIndex )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex-1, 1);
+ aResult.SegmentStart = nIndex-1;
+ aResult.SegmentEnd = nIndex;
+ }
+ return aResult;
+}
+
+css::accessibility::TextSegment SAL_CALL SmGraphicAccessible::getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType )
+{
+ SolarMutexGuard aGuard;
+ OUString aTxt( GetAccessibleText_Impl() );
+ //!! nIndex is allowed to be the string length
+ if (nIndex > aTxt.getLength())
+ throw IndexOutOfBoundsException();
+
+ css::accessibility::TextSegment aResult;
+ aResult.SegmentStart = -1;
+ aResult.SegmentEnd = -1;
+
+ nIndex++; // text *behind*
+ if ( (AccessibleTextType::CHARACTER == aTextType) && (nIndex < aTxt.getLength()) )
+ {
+ aResult.SegmentText = aTxt.copy(nIndex, 1);
+ aResult.SegmentStart = nIndex;
+ aResult.SegmentEnd = nIndex+1;
+ }
+ return aResult;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::copyText(
+ sal_Int32 nStartIndex,
+ sal_Int32 nEndIndex )
+{
+ SolarMutexGuard aGuard;
+ bool bReturn = false;
+
+ if (!pWin)
+ throw RuntimeException();
+
+ Reference< datatransfer::clipboard::XClipboard > xClipboard = pWin->GetClipboard();
+ if ( xClipboard.is() )
+ {
+ OUString sText( getTextRange(nStartIndex, nEndIndex) );
+
+ rtl::Reference<vcl::unohelper::TextDataObject> pDataObj = new vcl::unohelper::TextDataObject( sText );
+ SolarMutexReleaser aReleaser;
+ xClipboard->setContents( pDataObj, nullptr );
+
+ Reference< datatransfer::clipboard::XFlushableClipboard > xFlushableClipboard( xClipboard, uno::UNO_QUERY );
+ if( xFlushableClipboard.is() )
+ xFlushableClipboard->flushClipboard();
+
+ bReturn = true;
+ }
+
+
+ return bReturn;
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::scrollSubstringTo( sal_Int32, sal_Int32, AccessibleScrollType )
+{
+ return false;
+}
+
+OUString SAL_CALL SmGraphicAccessible::getImplementationName()
+{
+ return "SmGraphicAccessible";
+}
+
+sal_Bool SAL_CALL SmGraphicAccessible::supportsService(
+ const OUString& rServiceName )
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+Sequence< OUString > SAL_CALL SmGraphicAccessible::getSupportedServiceNames()
+{
+ return {
+ "css::accessibility::Accessible",
+ "css::accessibility::AccessibleComponent",
+ "css::accessibility::AccessibleContext",
+ "css::accessibility::AccessibleText"
+ };
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/accessibility.hxx b/starmath/source/accessibility.hxx
new file mode 100644
index 000000000..0ea2c2f36
--- /dev/null
+++ b/starmath/source/accessibility.hxx
@@ -0,0 +1,134 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <com/sun/star/accessibility/AccessibleScrollType.hpp>
+#include <com/sun/star/accessibility/XAccessible.hpp>
+#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
+#include <com/sun/star/accessibility/XAccessibleContext.hpp>
+#include <com/sun/star/accessibility/XAccessibleText.hpp>
+#include <com/sun/star/accessibility/XAccessibleEventBroadcaster.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/uno/Reference.h>
+#include <cppuhelper/implbase.hxx>
+
+#include <view.hxx>
+
+class SmDocShell;
+
+namespace accessibility { class AccessibleTextHelper; }
+
+// classes and helper-classes used for accessibility in the graphic-window
+
+
+typedef
+cppu::WeakImplHelper
+ <
+ css::lang::XServiceInfo,
+ css::accessibility::XAccessible,
+ css::accessibility::XAccessibleComponent,
+ css::accessibility::XAccessibleContext,
+ css::accessibility::XAccessibleText,
+ css::accessibility::XAccessibleEventBroadcaster
+ >
+SmGraphicAccessibleBaseClass;
+
+class SmGraphicAccessible final :
+ public SmGraphicAccessibleBaseClass
+{
+ OUString aAccName;
+ /// client id in the AccessibleEventNotifier queue
+ sal_uInt32 nClientId;
+
+ SmGraphicWidget* pWin;
+
+ SmGraphicAccessible( const SmGraphicAccessible & ) = delete;
+ SmGraphicAccessible & operator = ( const SmGraphicAccessible & ) = delete;
+
+ SmDocShell * GetDoc_Impl();
+ OUString GetAccessibleText_Impl();
+
+public:
+ explicit SmGraphicAccessible( SmGraphicWidget *pGraphicWin );
+ virtual ~SmGraphicAccessible() override;
+
+ void ClearWin(); // to be called when view is destroyed
+ void LaunchEvent(
+ const sal_Int16 nAccessibleEventId,
+ const css::uno::Any &rOldVal,
+ const css::uno::Any &rNewVal);
+
+ // XAccessible
+ virtual css::uno::Reference< css::accessibility::XAccessibleContext > SAL_CALL getAccessibleContext( ) override;
+
+ // XAccessibleComponent
+ virtual sal_Bool SAL_CALL containsPoint( const css::awt::Point& aPoint ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleAtPoint( const css::awt::Point& aPoint ) override;
+ virtual css::awt::Rectangle SAL_CALL getBounds( ) override;
+ virtual css::awt::Point SAL_CALL getLocation( ) override;
+ virtual css::awt::Point SAL_CALL getLocationOnScreen( ) override;
+ virtual css::awt::Size SAL_CALL getSize( ) override;
+ virtual void SAL_CALL grabFocus( ) override;
+ virtual sal_Int32 SAL_CALL getForeground( ) override;
+ virtual sal_Int32 SAL_CALL getBackground( ) override;
+
+ // XAccessibleContext
+ virtual sal_Int32 SAL_CALL getAccessibleChildCount( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleChild( sal_Int32 i ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessible > SAL_CALL getAccessibleParent( ) override;
+ virtual sal_Int32 SAL_CALL getAccessibleIndexInParent( ) override;
+ virtual sal_Int16 SAL_CALL getAccessibleRole( ) override;
+ virtual OUString SAL_CALL getAccessibleDescription( ) override;
+ virtual OUString SAL_CALL getAccessibleName( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleRelationSet > SAL_CALL getAccessibleRelationSet( ) override;
+ virtual css::uno::Reference< css::accessibility::XAccessibleStateSet > SAL_CALL getAccessibleStateSet( ) override;
+ virtual css::lang::Locale SAL_CALL getLocale( ) override;
+
+ // XAccessibleEventBroadcaster
+ virtual void SAL_CALL addAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+ virtual void SAL_CALL removeAccessibleEventListener( const css::uno::Reference< css::accessibility::XAccessibleEventListener >& xListener ) override;
+
+ // XAccessibleText
+ virtual sal_Int32 SAL_CALL getCaretPosition( ) override;
+ virtual sal_Bool SAL_CALL setCaretPosition ( sal_Int32 nIndex ) override;
+ virtual sal_Unicode SAL_CALL getCharacter( sal_Int32 nIndex ) override;
+ virtual css::uno::Sequence< css::beans::PropertyValue > SAL_CALL getCharacterAttributes( sal_Int32 nIndex, const css::uno::Sequence< OUString >& aRequestedAttributes ) override;
+ virtual css::awt::Rectangle SAL_CALL getCharacterBounds( sal_Int32 nIndex ) override;
+ virtual sal_Int32 SAL_CALL getCharacterCount( ) override;
+ virtual sal_Int32 SAL_CALL getIndexAtPoint( const css::awt::Point& aPoint ) override;
+ virtual OUString SAL_CALL getSelectedText( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionStart( ) override;
+ virtual sal_Int32 SAL_CALL getSelectionEnd( ) override;
+ virtual sal_Bool SAL_CALL setSelection( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual OUString SAL_CALL getText( ) override;
+ virtual OUString SAL_CALL getTextRange( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextAtIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBeforeIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual css::accessibility::TextSegment SAL_CALL getTextBehindIndex( sal_Int32 nIndex, sal_Int16 aTextType ) override;
+ virtual sal_Bool SAL_CALL copyText( sal_Int32 nStartIndex, sal_Int32 nEndIndex ) override;
+ virtual sal_Bool SAL_CALL scrollSubstringTo( sal_Int32 nStartIndex, sal_Int32 nEndIndex, css::accessibility::AccessibleScrollType aScrollType) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName( ) override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/action.cxx b/starmath/source/action.cxx
new file mode 100644
index 000000000..37e643c85
--- /dev/null
+++ b/starmath/source/action.cxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <action.hxx>
+#include <document.hxx>
+#include <strings.hxx>
+
+SmFormatAction::SmFormatAction(SmDocShell *pDocSh,
+ const SmFormat& rOldFormat,
+ const SmFormat& rNewFormat) :
+ pDoc( pDocSh ),
+ aOldFormat( rOldFormat ),
+ aNewFormat( rNewFormat )
+{
+}
+
+void SmFormatAction::Undo()
+{
+ pDoc->SetFormat(aOldFormat);
+}
+
+void SmFormatAction::Redo()
+{
+ pDoc->SetFormat(aNewFormat);
+}
+
+void SmFormatAction::Repeat(SfxRepeatTarget& rDocSh)
+{
+ dynamic_cast< SmDocShell & >(rDocSh).SetFormat(aNewFormat);
+}
+
+OUString SmFormatAction::GetComment() const
+{
+ return RID_UNDOFORMATNAME;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/caret.cxx b/starmath/source/caret.cxx
new file mode 100644
index 000000000..4e2effaa2
--- /dev/null
+++ b/starmath/source/caret.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 <caret.hxx>
+
+SmCaretPosGraph::SmCaretPosGraph() = default;
+
+SmCaretPosGraph::~SmCaretPosGraph() = default;
+
+SmCaretPosGraphEntry* SmCaretPosGraph::Add(SmCaretPos pos,
+ SmCaretPosGraphEntry* left)
+{
+ assert(pos.nIndex >= 0);
+ auto entry = std::make_unique<SmCaretPosGraphEntry>(pos, left, nullptr);
+ SmCaretPosGraphEntry* e = entry.get();
+ //Set Left and Right to point to the entry itself if they are NULL
+ entry->Left = entry->Left ? entry->Left : e;
+ entry->Right = entry->Right ? entry->Right : e;
+ mvEntries.push_back(std::move(entry));
+ return e;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/cfgitem.cxx b/starmath/source/cfgitem.cxx
new file mode 100644
index 000000000..7692aaa3f
--- /dev/null
+++ b/starmath/source/cfgitem.cxx
@@ -0,0 +1,1433 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <svl/itemset.hxx>
+#include <svl/intitem.hxx>
+#include <svl/eitem.hxx>
+#include <svl/languageoptions.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/settings.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <i18nlangtag/languagetag.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <cfgitem.hxx>
+
+#include <starmath.hrc>
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <format.hxx>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+
+constexpr OUStringLiteral SYMBOL_LIST = u"SymbolList";
+constexpr OUStringLiteral FONT_FORMAT_LIST = u"FontFormatList";
+
+static Sequence< OUString > lcl_GetFontPropertyNames()
+{
+ return Sequence< OUString > {
+ "Name",
+ "CharSet",
+ "Family",
+ "Pitch",
+ "Weight",
+ "Italic"
+ };
+}
+
+static Sequence< OUString > lcl_GetSymbolPropertyNames()
+{
+ return Sequence< OUString > {
+ "Char",
+ "Set",
+ "Predefined",
+ "FontFormatId"
+ };
+}
+
+static Sequence<OUString> lcl_GetOtherPropertyNames()
+{
+ return Sequence<OUString>{ "LoadSave/IsSaveOnlyUsedSymbols",
+ "Misc/AutoCloseBrackets",
+ "Misc/DefaultSmSyntaxVersion",
+ "Misc/IgnoreSpacesRight",
+ "Misc/SmEditWindowZoomFactor",
+ "Print/FormulaText",
+ "Print/Frame",
+ "Print/Size",
+ "Print/Title",
+ "Print/ZoomFactor",
+ "View/AutoRedraw",
+ "View/FormulaCursor",
+ "View/ToolboxVisible" };
+}
+
+static Sequence< OUString > lcl_GetFormatPropertyNames()
+{
+ //! Beware of order according to *_BEGIN *_END defines in format.hxx !
+ //! see respective load/save routines here
+ return Sequence< OUString > {
+ "StandardFormat/Textmode",
+ "StandardFormat/GreekCharStyle",
+ "StandardFormat/ScaleNormalBracket",
+ "StandardFormat/HorizontalAlignment",
+ "StandardFormat/BaseSize",
+ "StandardFormat/TextSize",
+ "StandardFormat/IndexSize",
+ "StandardFormat/FunctionSize",
+ "StandardFormat/OperatorSize",
+ "StandardFormat/LimitsSize",
+ "StandardFormat/Distance/Horizontal",
+ "StandardFormat/Distance/Vertical",
+ "StandardFormat/Distance/Root",
+ "StandardFormat/Distance/SuperScript",
+ "StandardFormat/Distance/SubScript",
+ "StandardFormat/Distance/Numerator",
+ "StandardFormat/Distance/Denominator",
+ "StandardFormat/Distance/Fraction",
+ "StandardFormat/Distance/StrokeWidth",
+ "StandardFormat/Distance/UpperLimit",
+ "StandardFormat/Distance/LowerLimit",
+ "StandardFormat/Distance/BracketSize",
+ "StandardFormat/Distance/BracketSpace",
+ "StandardFormat/Distance/MatrixRow",
+ "StandardFormat/Distance/MatrixColumn",
+ "StandardFormat/Distance/OrnamentSize",
+ "StandardFormat/Distance/OrnamentSpace",
+ "StandardFormat/Distance/OperatorSize",
+ "StandardFormat/Distance/OperatorSpace",
+ "StandardFormat/Distance/LeftSpace",
+ "StandardFormat/Distance/RightSpace",
+ "StandardFormat/Distance/TopSpace",
+ "StandardFormat/Distance/BottomSpace",
+ "StandardFormat/Distance/NormalBracketSize",
+ "StandardFormat/VariableFont",
+ "StandardFormat/FunctionFont",
+ "StandardFormat/NumberFont",
+ "StandardFormat/TextFont",
+ "StandardFormat/SerifFont",
+ "StandardFormat/SansFont",
+ "StandardFormat/FixedFont"
+ };
+}
+
+struct SmCfgOther
+{
+ SmPrintSize ePrintSize;
+ sal_uInt16 nPrintZoomFactor;
+ sal_uInt16 nSmEditWindowZoomFactor;
+ sal_uInt16 nSmSyntaxVersion;
+ bool bPrintTitle;
+ bool bPrintFormulaText;
+ bool bPrintFrame;
+ bool bIsSaveOnlyUsedSymbols;
+ bool bIsAutoCloseBrackets;
+ bool bIgnoreSpacesRight;
+ bool bToolboxVisible;
+ bool bAutoRedraw;
+ bool bFormulaCursor;
+
+ SmCfgOther();
+};
+
+constexpr sal_uInt16 nDefaultSmSyntaxVersion(5);
+
+SmCfgOther::SmCfgOther()
+ : ePrintSize(PRINT_SIZE_NORMAL)
+ , nPrintZoomFactor(100)
+ , nSmEditWindowZoomFactor(100)
+ // Defaulted as 5 so I have time to code the parser 6
+ , nSmSyntaxVersion(nDefaultSmSyntaxVersion)
+ , bPrintTitle(true)
+ , bPrintFormulaText(true)
+ , bPrintFrame(true)
+ , bIsSaveOnlyUsedSymbols(true)
+ , bIsAutoCloseBrackets(true)
+ , bIgnoreSpacesRight(true)
+ , bToolboxVisible(true)
+ , bAutoRedraw(true)
+ , bFormulaCursor(true)
+{
+}
+
+
+SmFontFormat::SmFontFormat()
+ : aName(FONTNAME_MATH)
+ , nCharSet(RTL_TEXTENCODING_UNICODE)
+ , nFamily(FAMILY_DONTKNOW)
+ , nPitch(PITCH_DONTKNOW)
+ , nWeight(WEIGHT_DONTKNOW)
+ , nItalic(ITALIC_NONE)
+{
+}
+
+
+SmFontFormat::SmFontFormat( const vcl::Font &rFont )
+ : aName(rFont.GetFamilyName())
+ , nCharSet(static_cast<sal_Int16>(rFont.GetCharSet()))
+ , nFamily(static_cast<sal_Int16>(rFont.GetFamilyType()))
+ , nPitch(static_cast<sal_Int16>(rFont.GetPitch()))
+ , nWeight(static_cast<sal_Int16>(rFont.GetWeight()))
+ , nItalic(static_cast<sal_Int16>(rFont.GetItalic()))
+{
+}
+
+
+vcl::Font SmFontFormat::GetFont() const
+{
+ vcl::Font aRes;
+ aRes.SetFamilyName( aName );
+ aRes.SetCharSet( static_cast<rtl_TextEncoding>(nCharSet) );
+ aRes.SetFamily( static_cast<FontFamily>(nFamily) );
+ aRes.SetPitch( static_cast<FontPitch>(nPitch) );
+ aRes.SetWeight( static_cast<FontWeight>(nWeight) );
+ aRes.SetItalic( static_cast<FontItalic>(nItalic) );
+ return aRes;
+}
+
+
+bool SmFontFormat::operator == ( const SmFontFormat &rFntFmt ) const
+{
+ return aName == rFntFmt.aName &&
+ nCharSet == rFntFmt.nCharSet &&
+ nFamily == rFntFmt.nFamily &&
+ nPitch == rFntFmt.nPitch &&
+ nWeight == rFntFmt.nWeight &&
+ nItalic == rFntFmt.nItalic;
+}
+
+
+SmFntFmtListEntry::SmFntFmtListEntry( const OUString &rId, const SmFontFormat &rFntFmt ) :
+ aId (rId),
+ aFntFmt (rFntFmt)
+{
+}
+
+
+SmFontFormatList::SmFontFormatList()
+ : bModified(false)
+{
+}
+
+
+void SmFontFormatList::Clear()
+{
+ if (!aEntries.empty())
+ {
+ aEntries.clear();
+ SetModified( true );
+ }
+}
+
+
+void SmFontFormatList::AddFontFormat( const OUString &rFntFmtId,
+ const SmFontFormat &rFntFmt )
+{
+ const SmFontFormat *pFntFmt = GetFontFormat( rFntFmtId );
+ OSL_ENSURE( !pFntFmt, "FontFormatId already exists" );
+ if (!pFntFmt)
+ {
+ SmFntFmtListEntry aEntry( rFntFmtId, rFntFmt );
+ aEntries.push_back( aEntry );
+ SetModified( true );
+ }
+}
+
+
+void SmFontFormatList::RemoveFontFormat( std::u16string_view rFntFmtId )
+{
+
+ // search for entry
+ for (size_t i = 0; i < aEntries.size(); ++i)
+ {
+ if (aEntries[i].aId == rFntFmtId)
+ {
+ // remove entry if found
+ aEntries.erase( aEntries.begin() + i );
+ SetModified( true );
+ break;
+ }
+ }
+}
+
+
+const SmFontFormat * SmFontFormatList::GetFontFormat( std::u16string_view rFntFmtId ) const
+{
+ const SmFontFormat *pRes = nullptr;
+
+ for (const auto & rEntry : aEntries)
+ {
+ if (rEntry.aId == rFntFmtId)
+ {
+ pRes = &rEntry.aFntFmt;
+ break;
+ }
+ }
+
+ return pRes;
+}
+
+
+const SmFontFormat * SmFontFormatList::GetFontFormat( size_t nPos ) const
+{
+ const SmFontFormat *pRes = nullptr;
+ if (nPos < aEntries.size())
+ pRes = &aEntries[nPos].aFntFmt;
+ return pRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( const SmFontFormat &rFntFmt ) const
+{
+ OUString aRes;
+
+ for (const auto & rEntry : aEntries)
+ {
+ if (rEntry.aFntFmt == rFntFmt)
+ {
+ aRes = rEntry.aId;
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( const SmFontFormat &rFntFmt, bool bAdd )
+{
+ OUString aRes( GetFontFormatId( rFntFmt) );
+ if (aRes.isEmpty() && bAdd)
+ {
+ aRes = GetNewFontFormatId();
+ AddFontFormat( aRes, rFntFmt );
+ }
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetFontFormatId( size_t nPos ) const
+{
+ OUString aRes;
+ if (nPos < aEntries.size())
+ aRes = aEntries[nPos].aId;
+ return aRes;
+}
+
+
+OUString SmFontFormatList::GetNewFontFormatId() const
+{
+ // returns first unused FormatId
+
+ sal_Int32 nCnt = GetCount();
+ for (sal_Int32 i = 1; i <= nCnt + 1; ++i)
+ {
+ OUString aTmpId = "Id" + OUString::number(i);
+ if (!GetFontFormat(aTmpId))
+ return aTmpId;
+ }
+ OSL_ENSURE( false, "failed to create new FontFormatId" );
+
+ return OUString();
+}
+
+
+SmMathConfig::SmMathConfig() :
+ ConfigItem("Office.Math")
+ , bIsOtherModified(false)
+ , bIsFormatModified(false)
+{
+ EnableNotification({ {} }); // Listen to everything under the node
+}
+
+
+SmMathConfig::~SmMathConfig()
+{
+ Save();
+}
+
+
+void SmMathConfig::SetOtherModified( bool bVal )
+{
+ bIsOtherModified = bVal;
+}
+
+
+void SmMathConfig::SetFormatModified( bool bVal )
+{
+ bIsFormatModified = bVal;
+}
+
+
+void SmMathConfig::ReadSymbol( SmSym &rSymbol,
+ const OUString &rSymbolName,
+ std::u16string_view rBaseNode ) const
+{
+ Sequence< OUString > aNames = lcl_GetSymbolPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ OUString aDelim( "/" );
+ for (auto& rName : asNonConstRange(aNames))
+ rName = rBaseNode + aDelim + rSymbolName + aDelim + rName;
+
+ const Sequence< Any > aValues = const_cast<SmMathConfig*>(this)->GetProperties(aNames);
+
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any * pValue = aValues.getConstArray();
+ vcl::Font aFont;
+ sal_UCS4 cChar = '\0';
+ OUString aSet;
+ bool bPredefined = false;
+
+ OUString aTmpStr;
+ sal_Int32 nTmp32 = 0;
+ bool bTmp = false;
+
+ bool bOK = true;
+ if (pValue->hasValue() && (*pValue >>= nTmp32))
+ cChar = static_cast< sal_UCS4 >( nTmp32 );
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ aSet = aTmpStr;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= bTmp))
+ bPredefined = bTmp;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ {
+ const SmFontFormat *pFntFmt = GetFontFormatList().GetFontFormat( aTmpStr );
+ OSL_ENSURE( pFntFmt, "unknown FontFormat" );
+ if (pFntFmt)
+ aFont = pFntFmt->GetFont();
+ }
+ else
+ bOK = false;
+ ++pValue;
+
+ if (bOK)
+ {
+ OUString aUiName( rSymbolName );
+ OUString aUiSetName( aSet );
+ if (bPredefined)
+ {
+ OUString aTmp;
+ aTmp = SmLocalizedSymbolData::GetUiSymbolName( rSymbolName );
+ OSL_ENSURE( !aTmp.isEmpty(), "localized symbol-name not found" );
+ if (!aTmp.isEmpty())
+ aUiName = aTmp;
+ aTmp = SmLocalizedSymbolData::GetUiSymbolSetName( aSet );
+ OSL_ENSURE( !aTmp.isEmpty(), "localized symbolset-name not found" );
+ if (!aTmp.isEmpty())
+ aUiSetName = aTmp;
+ }
+
+ rSymbol = SmSym( aUiName, aFont, cChar, aUiSetName, bPredefined );
+ if (aUiName != rSymbolName)
+ rSymbol.SetExportName( rSymbolName );
+ }
+ else
+ {
+ SAL_WARN("starmath", "symbol read error");
+ }
+}
+
+
+SmSymbolManager & SmMathConfig::GetSymbolManager()
+{
+ if (!pSymbolMgr)
+ {
+ pSymbolMgr.reset(new SmSymbolManager);
+ pSymbolMgr->Load();
+ }
+ return *pSymbolMgr;
+}
+
+
+void SmMathConfig::ImplCommit()
+{
+ Save();
+}
+
+
+void SmMathConfig::Save()
+{
+ SaveOther();
+ SaveFormat();
+ SaveFontFormatList();
+}
+
+
+void SmMathConfig::UnlockCommit()
+{
+ if (--m_nCommitLock == 0)
+ Commit();
+}
+
+
+void SmMathConfig::Clear()
+{
+ // Re-read data on next request
+ pOther.reset();
+ pFormat.reset();
+ pFontFormatList.reset();
+}
+
+
+void SmMathConfig::GetSymbols( std::vector< SmSym > &rSymbols ) const
+{
+ Sequence< OUString > aNodes(const_cast<SmMathConfig*>(this)->GetNodeNames(SYMBOL_LIST));
+ const OUString *pNode = aNodes.getConstArray();
+ sal_Int32 nNodes = aNodes.getLength();
+
+ rSymbols.resize( nNodes );
+ for (auto& rSymbol : rSymbols)
+ {
+ ReadSymbol( rSymbol, *pNode++, SYMBOL_LIST );
+ }
+}
+
+
+void SmMathConfig::SetSymbols( const std::vector< SmSym > &rNewSymbols )
+{
+ CommitLocker aLock(*this);
+ auto nCount = sal::static_int_cast<sal_Int32>(rNewSymbols.size());
+
+ Sequence< OUString > aNames = lcl_GetSymbolPropertyNames();
+ const OUString *pNames = aNames.getConstArray();
+ sal_Int32 nSymbolProps = aNames.getLength();
+
+ Sequence< PropertyValue > aValues( nCount * nSymbolProps );
+ PropertyValue *pValues = aValues.getArray();
+
+ PropertyValue *pVal = pValues;
+ OUString aDelim( "/" );
+ for (const SmSym& rSymbol : rNewSymbols)
+ {
+ OUString aNodeNameDelim = SYMBOL_LIST +
+ aDelim +
+ rSymbol.GetExportName() +
+ aDelim;
+
+ const OUString *pName = pNames;
+
+ // Char
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= rSymbol.GetCharacter();
+ pVal++;
+ // Set
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ OUString aTmp( rSymbol.GetSymbolSetName() );
+ if (rSymbol.IsPredefined())
+ aTmp = SmLocalizedSymbolData::GetExportSymbolSetName( aTmp );
+ pVal->Value <<= aTmp;
+ pVal++;
+ // Predefined
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= rSymbol.IsPredefined();
+ pVal++;
+ // FontFormatId
+ SmFontFormat aFntFmt( rSymbol.GetFace() );
+ OUString aFntFmtId( GetFontFormatList().GetFontFormatId( aFntFmt, true ) );
+ OSL_ENSURE( !aFntFmtId.isEmpty(), "FontFormatId not found" );
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmtId;
+ pVal++;
+ }
+ OSL_ENSURE( pVal - pValues == sal::static_int_cast< ptrdiff_t >(nCount * nSymbolProps), "properties missing" );
+ ReplaceSetProperties( SYMBOL_LIST, aValues );
+
+ StripFontFormatList( rNewSymbols );
+}
+
+
+SmFontFormatList & SmMathConfig::GetFontFormatList()
+{
+ if (!pFontFormatList)
+ {
+ LoadFontFormatList();
+ }
+ return *pFontFormatList;
+}
+
+
+void SmMathConfig::LoadFontFormatList()
+{
+ if (!pFontFormatList)
+ pFontFormatList.reset(new SmFontFormatList);
+ else
+ pFontFormatList->Clear();
+
+ const Sequence< OUString > aNodes( GetNodeNames( FONT_FORMAT_LIST ) );
+
+ for (const OUString& rNode : aNodes)
+ {
+ SmFontFormat aFntFmt;
+ ReadFontFormat( aFntFmt, rNode, FONT_FORMAT_LIST );
+ if (!pFontFormatList->GetFontFormat( rNode ))
+ pFontFormatList->AddFontFormat( rNode, aFntFmt );
+ }
+ pFontFormatList->SetModified( false );
+}
+
+
+void SmMathConfig::ReadFontFormat( SmFontFormat &rFontFormat,
+ std::u16string_view rSymbolName, std::u16string_view rBaseNode ) const
+{
+ Sequence< OUString > aNames = lcl_GetFontPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ OUString aDelim( "/" );
+ for (auto& rName : asNonConstRange(aNames))
+ rName = rBaseNode + aDelim + rSymbolName + aDelim + rName;
+
+ const Sequence< Any > aValues = const_cast<SmMathConfig*>(this)->GetProperties(aNames);
+
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any * pValue = aValues.getConstArray();
+
+ OUString aTmpStr;
+ sal_Int16 nTmp16 = 0;
+
+ bool bOK = true;
+ if (pValue->hasValue() && (*pValue >>= aTmpStr))
+ rFontFormat.aName = aTmpStr;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nCharSet = nTmp16; // 6.0 file-format GetSOLoadTextEncoding not needed
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nFamily = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nPitch = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nWeight = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+ if (pValue->hasValue() && (*pValue >>= nTmp16))
+ rFontFormat.nItalic = nTmp16;
+ else
+ bOK = false;
+ ++pValue;
+
+ OSL_ENSURE( bOK, "read FontFormat failed" );
+}
+
+
+void SmMathConfig::SaveFontFormatList()
+{
+ SmFontFormatList &rFntFmtList = GetFontFormatList();
+
+ if (!rFntFmtList.IsModified())
+ return;
+
+ Sequence< OUString > aNames = lcl_GetFontPropertyNames();
+ sal_Int32 nSymbolProps = aNames.getLength();
+
+ size_t nCount = rFntFmtList.GetCount();
+
+ Sequence< PropertyValue > aValues( nCount * nSymbolProps );
+ PropertyValue *pValues = aValues.getArray();
+
+ PropertyValue *pVal = pValues;
+ OUString aDelim( "/" );
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ OUString aFntFmtId(rFntFmtList.GetFontFormatId(i));
+ const SmFontFormat *pFntFmt = rFntFmtList.GetFontFormat(i);
+ assert(pFntFmt);
+ const SmFontFormat aFntFmt(*pFntFmt);
+
+ OUString aNodeNameDelim = FONT_FORMAT_LIST +
+ aDelim +
+ aFntFmtId +
+ aDelim;
+
+ const OUString *pName = aNames.getConstArray();
+
+ // Name
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.aName;
+ pVal++;
+ // CharSet
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nCharSet; // 6.0 file-format GetSOStoreTextEncoding not needed
+ pVal++;
+ // Family
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nFamily;
+ pVal++;
+ // Pitch
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nPitch;
+ pVal++;
+ // Weight
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nWeight;
+ pVal++;
+ // Italic
+ pVal->Name = aNodeNameDelim;
+ pVal->Name += *pName++;
+ pVal->Value <<= aFntFmt.nItalic;
+ pVal++;
+ }
+ OSL_ENSURE( sal::static_int_cast<size_t>(pVal - pValues) == nCount * nSymbolProps, "properties missing" );
+ ReplaceSetProperties( FONT_FORMAT_LIST, aValues );
+
+ rFntFmtList.SetModified( false );
+}
+
+
+void SmMathConfig::StripFontFormatList( const std::vector< SmSym > &rSymbols )
+{
+ size_t i;
+
+ // build list of used font-formats only
+ //!! font-format IDs may be different !!
+ SmFontFormatList aUsedList;
+ for (i = 0; i < rSymbols.size(); ++i)
+ {
+ OSL_ENSURE( rSymbols[i].GetName().getLength() > 0, "non named symbol" );
+ aUsedList.GetFontFormatId( SmFontFormat( rSymbols[i].GetFace() ) , true );
+ }
+ const SmFormat & rStdFmt = GetStandardFormat();
+ for (i = FNT_BEGIN; i <= FNT_END; ++i)
+ {
+ aUsedList.GetFontFormatId( SmFontFormat( rStdFmt.GetFont( i ) ) , true );
+ }
+
+ // remove unused font-formats from list
+ SmFontFormatList &rFntFmtList = GetFontFormatList();
+ size_t nCnt = rFntFmtList.GetCount();
+ std::unique_ptr<SmFontFormat[]> pTmpFormat(new SmFontFormat[ nCnt ]);
+ std::unique_ptr<OUString[]> pId(new OUString[ nCnt ]);
+ size_t k;
+ for (k = 0; k < nCnt; ++k)
+ {
+ pTmpFormat[k] = *rFntFmtList.GetFontFormat( k );
+ pId[k] = rFntFmtList.GetFontFormatId( k );
+ }
+ for (k = 0; k < nCnt; ++k)
+ {
+ if (aUsedList.GetFontFormatId( pTmpFormat[k] ).isEmpty())
+ {
+ rFntFmtList.RemoveFontFormat( pId[k] );
+ }
+ }
+}
+
+
+void SmMathConfig::LoadOther()
+{
+ if (!pOther)
+ pOther.reset(new SmCfgOther);
+
+ const Sequence<OUString> aNames(lcl_GetOtherPropertyNames());
+ const Sequence<Any> aValues(GetProperties(aNames));
+ if (aNames.getLength() != aValues.getLength())
+ return;
+
+ const Any* pValues = aValues.getConstArray();
+ const Any* pVal = pValues;
+
+ // LoadSave/IsSaveOnlyUsedSymbols
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bIsSaveOnlyUsedSymbols = bTmp;
+ ++pVal;
+ // Misc/AutoCloseBrackets
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bIsAutoCloseBrackets = bTmp;
+ ++pVal;
+ // Misc/DefaultSmSyntaxVersion
+ if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp))
+ pOther->nSmSyntaxVersion = nTmp;
+ ++pVal;
+ // Misc/IgnoreSpacesRight
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bIgnoreSpacesRight = bTmp;
+ ++pVal;
+ // Misc/SmEditWindowZoomFactor
+ if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp))
+ pOther->nSmEditWindowZoomFactor = nTmp;
+ ++pVal;
+ // Print/FormulaText
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bPrintFormulaText = bTmp;
+ ++pVal;
+ // Print/Frame
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bPrintFrame = bTmp;
+ ++pVal;
+ // Print/Size:
+ if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp))
+ pOther->ePrintSize = static_cast<SmPrintSize>(nTmp);
+ ++pVal;
+ // Print/Title
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bPrintTitle = bTmp;
+ ++pVal;
+ // Print/ZoomFactor
+ if (sal_Int16 nTmp; pVal->hasValue() && (*pVal >>= nTmp))
+ pOther->nPrintZoomFactor = nTmp;
+ ++pVal;
+ // View/AutoRedraw
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bAutoRedraw = bTmp;
+ ++pVal;
+ // View/FormulaCursor
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bFormulaCursor = bTmp;
+ ++pVal;
+ // View/ToolboxVisible
+ if (bool bTmp; pVal->hasValue() && (*pVal >>= bTmp))
+ pOther->bToolboxVisible = bTmp;
+ ++pVal;
+
+ OSL_ENSURE(pVal - pValues == aNames.getLength(), "property mismatch");
+ SetOtherModified( false );
+}
+
+
+void SmMathConfig::SaveOther()
+{
+ if (!pOther || !IsOtherModified())
+ return;
+
+ const Sequence<OUString> aNames(lcl_GetOtherPropertyNames());
+ Sequence<Any> aValues(aNames.getLength());
+
+ Any* pValues = aValues.getArray();
+ Any* pVal = pValues;
+
+ // LoadSave/IsSaveOnlyUsedSymbols
+ *pVal++ <<= pOther->bIsSaveOnlyUsedSymbols;
+ // Misc/AutoCloseBrackets
+ *pVal++ <<= pOther->bIsAutoCloseBrackets;
+ // Misc/DefaultSmSyntaxVersion
+ *pVal++ <<= pOther->nSmSyntaxVersion;
+ // Misc/IgnoreSpacesRight
+ *pVal++ <<= pOther->bIgnoreSpacesRight;
+ // Misc/SmEditWindowZoomFactor
+ *pVal++ <<= pOther->nSmEditWindowZoomFactor;
+ // Print/FormulaText
+ *pVal++ <<= pOther->bPrintFormulaText;
+ // Print/Frame
+ *pVal++ <<= pOther->bPrintFrame;
+ // Print/Size:
+ *pVal++ <<= static_cast<sal_Int16>(pOther->ePrintSize);
+ // Print/Title
+ *pVal++ <<= pOther->bPrintTitle;
+ // Print/ZoomFactor
+ *pVal++ <<= pOther->nPrintZoomFactor;
+ // View/AutoRedraw
+ *pVal++ <<= pOther->bAutoRedraw;
+ // View/FormulaCursor
+ *pVal++ <<= pOther->bFormulaCursor;
+ // View/ToolboxVisible
+ *pVal++ <<= pOther->bToolboxVisible;
+
+ OSL_ENSURE(pVal - pValues == aNames.getLength(), "property mismatch");
+ PutProperties(aNames, aValues);
+
+ SetOtherModified( false );
+}
+
+namespace {
+
+// Latin default-fonts
+const DefaultFontType aLatinDefFnts[FNT_END] =
+{
+ DefaultFontType::SERIF, // FNT_VARIABLE
+ DefaultFontType::SERIF, // FNT_FUNCTION
+ DefaultFontType::SERIF, // FNT_NUMBER
+ DefaultFontType::SERIF, // FNT_TEXT
+ DefaultFontType::SERIF, // FNT_SERIF
+ DefaultFontType::SANS, // FNT_SANS
+ DefaultFontType::FIXED // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+// CJK default-fonts
+//! we use non-asian fonts for variables, functions and numbers since they
+//! look better and even in asia only latin letters will be used for those.
+//! At least that's what I was told...
+const DefaultFontType aCJKDefFnts[FNT_END] =
+{
+ DefaultFontType::SERIF, // FNT_VARIABLE
+ DefaultFontType::SERIF, // FNT_FUNCTION
+ DefaultFontType::SERIF, // FNT_NUMBER
+ DefaultFontType::CJK_TEXT, // FNT_TEXT
+ DefaultFontType::CJK_TEXT, // FNT_SERIF
+ DefaultFontType::CJK_DISPLAY, // FNT_SANS
+ DefaultFontType::CJK_TEXT // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+// CTL default-fonts
+const DefaultFontType aCTLDefFnts[FNT_END] =
+{
+ DefaultFontType::CTL_TEXT, // FNT_VARIABLE
+ DefaultFontType::CTL_TEXT, // FNT_FUNCTION
+ DefaultFontType::CTL_TEXT, // FNT_NUMBER
+ DefaultFontType::CTL_TEXT, // FNT_TEXT
+ DefaultFontType::CTL_TEXT, // FNT_SERIF
+ DefaultFontType::CTL_TEXT, // FNT_SANS
+ DefaultFontType::CTL_TEXT // FNT_FIXED
+ //OpenSymbol, // FNT_MATH
+};
+
+
+OUString lcl_GetDefaultFontName( LanguageType nLang, sal_uInt16 nIdent )
+{
+ assert(nIdent < FNT_END);
+ const DefaultFontType *pTable;
+ switch ( SvtLanguageOptions::GetScriptTypeOfLanguage( nLang ) )
+ {
+ case SvtScriptType::LATIN : pTable = aLatinDefFnts; break;
+ case SvtScriptType::ASIAN : pTable = aCJKDefFnts; break;
+ case SvtScriptType::COMPLEX : pTable = aCTLDefFnts; break;
+ default :
+ pTable = aLatinDefFnts;
+ SAL_WARN("starmath", "unknown script-type");
+ }
+
+ return OutputDevice::GetDefaultFont(pTable[ nIdent ], nLang,
+ GetDefaultFontFlags::OnlyOne ).GetFamilyName();
+}
+
+}
+
+
+void SmMathConfig::LoadFormat()
+{
+ if (!pFormat)
+ pFormat.reset(new SmFormat);
+
+
+ Sequence< OUString > aNames = lcl_GetFormatPropertyNames();
+
+ sal_Int32 nProps = aNames.getLength();
+
+ Sequence< Any > aValues( GetProperties( aNames ) );
+ if (!(nProps && aValues.getLength() == nProps))
+ return;
+
+ const Any *pValues = aValues.getConstArray();
+ const Any *pVal = pValues;
+
+ OUString aTmpStr;
+ sal_Int16 nTmp16 = 0;
+ bool bTmp = false;
+
+ // StandardFormat/Textmode
+ if (pVal->hasValue() && (*pVal >>= bTmp))
+ pFormat->SetTextmode( bTmp );
+ ++pVal;
+ // StandardFormat/GreekCharStyle
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetGreekCharStyle( nTmp16 );
+ ++pVal;
+ // StandardFormat/ScaleNormalBracket
+ if (pVal->hasValue() && (*pVal >>= bTmp))
+ pFormat->SetScaleNormalBrackets( bTmp );
+ ++pVal;
+ // StandardFormat/HorizontalAlignment
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetHorAlign( static_cast<SmHorAlign>(nTmp16) );
+ ++pVal;
+ // StandardFormat/BaseSize
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetBaseSize( Size(0, SmPtsTo100th_mm( nTmp16 )) );
+ ++pVal;
+
+ sal_uInt16 i;
+ for (i = SIZ_BEGIN; i <= SIZ_END; ++i)
+ {
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetRelSize( i, nTmp16 );
+ ++pVal;
+ }
+
+ for (i = DIS_BEGIN; i <= DIS_END; ++i)
+ {
+ if (pVal->hasValue() && (*pVal >>= nTmp16))
+ pFormat->SetDistance( i, nTmp16 );
+ ++pVal;
+ }
+
+ LanguageType nLang = Application::GetSettings().GetUILanguageTag().getLanguageType();
+ for (i = FNT_BEGIN; i < FNT_END; ++i)
+ {
+ vcl::Font aFnt;
+ bool bUseDefaultFont = true;
+ if (pVal->hasValue() && (*pVal >>= aTmpStr))
+ {
+ bUseDefaultFont = aTmpStr.isEmpty();
+ if (bUseDefaultFont)
+ {
+ aFnt = pFormat->GetFont( i );
+ aFnt.SetFamilyName( lcl_GetDefaultFontName( nLang, i ) );
+ }
+ else
+ {
+ const SmFontFormat *pFntFmt = GetFontFormatList().GetFontFormat( aTmpStr );
+ OSL_ENSURE( pFntFmt, "unknown FontFormat" );
+ if (pFntFmt)
+ aFnt = pFntFmt->GetFont();
+ }
+ }
+ ++pVal;
+
+ aFnt.SetFontSize( pFormat->GetBaseSize() );
+ pFormat->SetFont( i, aFnt, bUseDefaultFont );
+ }
+
+ OSL_ENSURE( pVal - pValues == nProps, "property mismatch" );
+ SetFormatModified( false );
+}
+
+
+void SmMathConfig::SaveFormat()
+{
+ if (!pFormat || !IsFormatModified())
+ return;
+
+ const Sequence< OUString > aNames = lcl_GetFormatPropertyNames();
+ sal_Int32 nProps = aNames.getLength();
+
+ Sequence< Any > aValues( nProps );
+ Any *pValues = aValues.getArray();
+ Any *pValue = pValues;
+
+ // StandardFormat/Textmode
+ *pValue++ <<= pFormat->IsTextmode();
+ // StandardFormat/GreekCharStyle
+ *pValue++ <<= pFormat->GetGreekCharStyle();
+ // StandardFormat/ScaleNormalBracket
+ *pValue++ <<= pFormat->IsScaleNormalBrackets();
+ // StandardFormat/HorizontalAlignment
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetHorAlign());
+ // StandardFormat/BaseSize
+ *pValue++ <<= static_cast<sal_Int16>(SmRoundFraction( Sm100th_mmToPts(
+ pFormat->GetBaseSize().Height() ) ));
+
+ sal_uInt16 i;
+ for (i = SIZ_BEGIN; i <= SIZ_END; ++i)
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetRelSize( i ));
+
+ for (i = DIS_BEGIN; i <= DIS_END; ++i)
+ *pValue++ <<= static_cast<sal_Int16>(pFormat->GetDistance( i ));
+
+ for (i = FNT_BEGIN; i < FNT_END; ++i)
+ {
+ OUString aFntFmtId;
+
+ if (!pFormat->IsDefaultFont( i ))
+ {
+ SmFontFormat aFntFmt( pFormat->GetFont( i ) );
+ aFntFmtId = GetFontFormatList().GetFontFormatId( aFntFmt, true );
+ OSL_ENSURE( !aFntFmtId.isEmpty(), "FontFormatId not found" );
+ }
+
+ *pValue++ <<= aFntFmtId;
+ }
+
+ OSL_ENSURE( pValue - pValues == nProps, "property mismatch" );
+ PutProperties( aNames , aValues );
+
+ SetFormatModified( false );
+}
+
+
+const SmFormat & SmMathConfig::GetStandardFormat() const
+{
+ if (!pFormat)
+ const_cast<SmMathConfig*>(this)->LoadFormat();
+ return *pFormat;
+}
+
+
+void SmMathConfig::SetStandardFormat( const SmFormat &rFormat, bool bSaveFontFormatList )
+{
+ if (!pFormat)
+ LoadFormat();
+ if (rFormat == *pFormat)
+ return;
+
+ CommitLocker aLock(*this);
+ *pFormat = rFormat;
+ SetFormatModified( true );
+
+ if (bSaveFontFormatList)
+ {
+ // needed for SmFontTypeDialog's DefaultButtonClickHdl
+ if (pFontFormatList)
+ pFontFormatList->SetModified( true );
+ }
+}
+
+
+SmPrintSize SmMathConfig::GetPrintSize() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->ePrintSize;
+}
+
+
+void SmMathConfig::SetPrintSize( SmPrintSize eSize )
+{
+ if (!pOther)
+ LoadOther();
+ if (eSize != pOther->ePrintSize)
+ {
+ CommitLocker aLock(*this);
+ pOther->ePrintSize = eSize;
+ SetOtherModified( true );
+ }
+}
+
+
+sal_uInt16 SmMathConfig::GetPrintZoomFactor() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->nPrintZoomFactor;
+}
+
+
+void SmMathConfig::SetPrintZoomFactor( sal_uInt16 nVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (nVal != pOther->nPrintZoomFactor)
+ {
+ CommitLocker aLock(*this);
+ pOther->nPrintZoomFactor = nVal;
+ SetOtherModified( true );
+ }
+}
+
+
+sal_uInt16 SmMathConfig::GetSmEditWindowZoomFactor() const
+{
+ sal_uInt16 smzoomfactor;
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ smzoomfactor = pOther->nSmEditWindowZoomFactor;
+ return smzoomfactor < 10 || smzoomfactor > 1000 ? 100 : smzoomfactor;
+}
+
+
+void SmMathConfig::SetSmEditWindowZoomFactor( sal_uInt16 nVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (nVal != pOther->nSmEditWindowZoomFactor)
+ {
+ CommitLocker aLock(*this);
+ pOther->nSmEditWindowZoomFactor = nVal;
+ SetOtherModified( true );
+ }
+}
+
+
+bool SmMathConfig::SetOtherIfNotEqual( bool &rbItem, bool bNewVal )
+{
+ if (bNewVal != rbItem)
+ {
+ CommitLocker aLock(*this);
+ rbItem = bNewVal;
+ SetOtherModified( true );
+ return true;
+ }
+ return false;
+}
+
+
+bool SmMathConfig::IsPrintTitle() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintTitle;
+}
+
+
+void SmMathConfig::SetPrintTitle( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintTitle, bVal );
+}
+
+
+bool SmMathConfig::IsPrintFormulaText() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintFormulaText;
+}
+
+
+void SmMathConfig::SetPrintFormulaText( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintFormulaText, bVal );
+}
+
+bool SmMathConfig::IsSaveOnlyUsedSymbols() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIsSaveOnlyUsedSymbols;
+}
+
+bool SmMathConfig::IsAutoCloseBrackets() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIsAutoCloseBrackets;
+}
+
+sal_uInt16 SmMathConfig::GetDefaultSmSyntaxVersion() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return nDefaultSmSyntaxVersion;
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->nSmSyntaxVersion;
+}
+
+bool SmMathConfig::IsPrintFrame() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bPrintFrame;
+}
+
+
+void SmMathConfig::SetPrintFrame( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bPrintFrame, bVal );
+}
+
+
+void SmMathConfig::SetSaveOnlyUsedSymbols( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bIsSaveOnlyUsedSymbols, bVal );
+}
+
+
+void SmMathConfig::SetAutoCloseBrackets( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bIsAutoCloseBrackets, bVal );
+}
+
+void SmMathConfig::SetDefaultSmSyntaxVersion( sal_uInt16 nVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (nVal != pOther->nSmSyntaxVersion)
+ {
+ CommitLocker aLock(*this);
+ pOther->nSmSyntaxVersion = nVal;
+ SetOtherModified( true );
+ }
+}
+
+bool SmMathConfig::IsIgnoreSpacesRight() const
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return false;
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bIgnoreSpacesRight;
+}
+
+
+void SmMathConfig::SetIgnoreSpacesRight( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ if (SetOtherIfNotEqual( pOther->bIgnoreSpacesRight, bVal ))
+ {
+ // reformat (displayed) formulas accordingly
+ Broadcast(SfxHint(SfxHintId::MathFormatChanged));
+ }
+
+}
+
+
+bool SmMathConfig::IsAutoRedraw() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bAutoRedraw;
+}
+
+
+void SmMathConfig::SetAutoRedraw( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bAutoRedraw, bVal );
+}
+
+
+bool SmMathConfig::IsShowFormulaCursor() const
+{
+ if (!pOther)
+ const_cast<SmMathConfig*>(this)->LoadOther();
+ return pOther->bFormulaCursor;
+}
+
+
+void SmMathConfig::SetShowFormulaCursor( bool bVal )
+{
+ if (!pOther)
+ LoadOther();
+ SetOtherIfNotEqual( pOther->bFormulaCursor, bVal );
+}
+
+void SmMathConfig::Notify( const css::uno::Sequence< OUString >& rNames )
+{
+ Clear();
+ if (std::find(rNames.begin(), rNames.end(), "Misc/IgnoreSpacesRight") != rNames.end())
+ Broadcast(SfxHint(SfxHintId::MathFormatChanged));
+}
+
+
+void SmMathConfig::ItemSetToConfig(const SfxItemSet &rSet)
+{
+ CommitLocker aLock(*this);
+
+ sal_uInt16 nU16;
+ bool bVal;
+ if (const SfxUInt16Item* pPrintSizeItem = rSet.GetItemIfSet(SID_PRINTSIZE))
+ { nU16 = pPrintSizeItem->GetValue();
+ SetPrintSize( static_cast<SmPrintSize>(nU16) );
+ }
+ if (const SfxUInt16Item* pPrintZoomItem = rSet.GetItemIfSet(SID_PRINTZOOM))
+ { nU16 = pPrintZoomItem->GetValue();
+ SetPrintZoomFactor( nU16 );
+ }
+ if (const SfxUInt16Item* pPrintZoomItem = rSet.GetItemIfSet(SID_SMEDITWINDOWZOOM))
+ { nU16 = pPrintZoomItem->GetValue();
+ SetSmEditWindowZoomFactor( nU16 );
+ }
+ if (const SfxBoolItem* pPrintTitleItem = rSet.GetItemIfSet(SID_PRINTTITLE))
+ { bVal = pPrintTitleItem->GetValue();
+ SetPrintTitle( bVal );
+ }
+ if (const SfxBoolItem* pPrintTextItem = rSet.GetItemIfSet(SID_PRINTTEXT))
+ { bVal = pPrintTextItem->GetValue();
+ SetPrintFormulaText( bVal );
+ }
+ if (const SfxBoolItem* pPrintZoomItem = rSet.GetItemIfSet(SID_PRINTFRAME))
+ { bVal = pPrintZoomItem->GetValue();
+ SetPrintFrame( bVal );
+ }
+ if (const SfxBoolItem* pRedrawItem = rSet.GetItemIfSet(SID_AUTOREDRAW))
+ { bVal = pRedrawItem->GetValue();
+ SetAutoRedraw( bVal );
+ }
+ if (const SfxBoolItem* pSpacesItem = rSet.GetItemIfSet(SID_NO_RIGHT_SPACES))
+ { bVal = pSpacesItem->GetValue();
+ SetIgnoreSpacesRight( bVal );
+ }
+ if (const SfxBoolItem* pSymbolsItem = rSet.GetItemIfSet(SID_SAVE_ONLY_USED_SYMBOLS))
+ { bVal = pSymbolsItem->GetValue();
+ SetSaveOnlyUsedSymbols( bVal );
+ }
+
+ if (const SfxBoolItem* pBracketsItem = rSet.GetItemIfSet(SID_AUTO_CLOSE_BRACKETS))
+ {
+ bVal = pBracketsItem->GetValue();
+ SetAutoCloseBrackets( bVal );
+ }
+
+ if (const SfxUInt16Item* pSyntaxItem = rSet.GetItemIfSet(SID_DEFAULT_SM_SYNTAX_VERSION))
+ {
+ nU16 = pSyntaxItem->GetValue();
+ SetDefaultSmSyntaxVersion( nU16 );
+ }
+}
+
+
+void SmMathConfig::ConfigToItemSet(SfxItemSet &rSet) const
+{
+ rSet.Put(SfxUInt16Item(SID_PRINTSIZE,
+ sal::static_int_cast<sal_uInt16>(GetPrintSize())));
+ rSet.Put(SfxUInt16Item(SID_PRINTZOOM,
+ GetPrintZoomFactor()));
+ rSet.Put(SfxUInt16Item(SID_SMEDITWINDOWZOOM,
+ GetSmEditWindowZoomFactor()));
+
+ rSet.Put(SfxBoolItem(SID_PRINTTITLE, IsPrintTitle()));
+ rSet.Put(SfxBoolItem(SID_PRINTTEXT, IsPrintFormulaText()));
+ rSet.Put(SfxBoolItem(SID_PRINTFRAME, IsPrintFrame()));
+ rSet.Put(SfxBoolItem(SID_AUTOREDRAW, IsAutoRedraw()));
+ rSet.Put(SfxBoolItem(SID_NO_RIGHT_SPACES, IsIgnoreSpacesRight()));
+ rSet.Put(SfxBoolItem(SID_SAVE_ONLY_USED_SYMBOLS, IsSaveOnlyUsedSymbols()));
+ rSet.Put(SfxBoolItem(SID_AUTO_CLOSE_BRACKETS, IsAutoCloseBrackets()));
+ rSet.Put(SfxBoolItem(SID_DEFAULT_SM_SYNTAX_VERSION, GetDefaultSmSyntaxVersion()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/cursor.cxx b/starmath/source/cursor.cxx
new file mode 100644
index 000000000..d5479ab42
--- /dev/null
+++ b/starmath/source/cursor.cxx
@@ -0,0 +1,1561 @@
+/* -*- 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 <cursor.hxx>
+#include <visitors.hxx>
+#include <document.hxx>
+#include <view.hxx>
+#include <comphelper/string.hxx>
+#include <editeng/editeng.hxx>
+#include <osl/diagnose.h>
+
+void SmCursor::Move(OutputDevice* pDev, SmMovementDirection direction, bool bMoveAnchor){
+ SmCaretPosGraphEntry* NewPos = nullptr;
+ switch(direction)
+ {
+ case MoveLeft:
+ if (mpPosition)
+ NewPos = mpPosition->Left;
+ OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
+ break;
+ case MoveRight:
+ if (mpPosition)
+ NewPos = mpPosition->Right;
+ OSL_ENSURE(NewPos, "NewPos shouldn't be NULL here!");
+ break;
+ case MoveUp:
+ //Implementation is practically identical to MoveDown, except for a single if statement
+ //so I've implemented them together and added a direction == MoveDown to the if statements.
+ case MoveDown:
+ if (mpPosition)
+ {
+ SmCaretLine from_line = SmCaretPos2LineVisitor(pDev, mpPosition->CaretPos).GetResult(),
+ best_line, //Best approximated line found so far
+ curr_line; //Current line
+ tools::Long dbp_sq = 0; //Distance squared to best line
+ for(const auto &pEntry : *mpGraph)
+ {
+ //Reject it if it's the current position
+ if(pEntry->CaretPos == mpPosition->CaretPos) continue;
+ //Compute caret line
+ curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
+ //Reject anything above if we're moving down
+ if(curr_line.GetTop() <= from_line.GetTop() && direction == MoveDown) continue;
+ //Reject anything below if we're moving up
+ if(curr_line.GetTop() + curr_line.GetHeight() >= from_line.GetTop() + from_line.GetHeight()
+ && direction == MoveUp) continue;
+ //Compare if it to what we have, if we have anything yet
+ if(NewPos){
+ //Compute distance to current line squared, multiplied with a horizontal factor
+ tools::Long dp_sq = curr_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
+ curr_line.SquaredDistanceY(from_line);
+ //Discard current line if best line is closer
+ if(dbp_sq <= dp_sq) continue;
+ }
+ //Take current line as the best
+ best_line = curr_line;
+ NewPos = pEntry.get();
+ //Update distance to best line
+ dbp_sq = best_line.SquaredDistanceX(from_line) * HORIZONTICAL_DISTANCE_FACTOR +
+ best_line.SquaredDistanceY(from_line);
+ }
+ }
+ break;
+ default:
+ assert(false);
+ }
+ if(NewPos){
+ mpPosition = NewPos;
+ if(bMoveAnchor)
+ mpAnchor = NewPos;
+ RequestRepaint();
+ }
+}
+
+void SmCursor::MoveTo(OutputDevice* pDev, const Point& pos, bool bMoveAnchor)
+{
+ SmCaretPosGraphEntry* NewPos = nullptr;
+ tools::Long dp_sq = 0, //Distance to current line squared
+ dbp_sq = 1; //Distance to best line squared
+ for(const auto &pEntry : *mpGraph)
+ {
+ OSL_ENSURE(pEntry->CaretPos.IsValid(), "The caret position graph may not have invalid positions!");
+ //Compute current line
+ SmCaretLine curr_line = SmCaretPos2LineVisitor(pDev, pEntry->CaretPos).GetResult();
+ //Compute squared distance to current line
+ dp_sq = curr_line.SquaredDistanceX(pos) + curr_line.SquaredDistanceY(pos);
+ //If we have a position compare to it
+ if(NewPos){
+ //If best line is closer, reject current line
+ if(dbp_sq <= dp_sq) continue;
+ }
+ //Accept current position as the best
+ NewPos = pEntry.get();
+ //Update distance to best line
+ dbp_sq = dp_sq;
+ }
+ if(NewPos){
+ mpPosition = NewPos;
+ if(bMoveAnchor)
+ mpAnchor = NewPos;
+ RequestRepaint();
+ }
+}
+
+void SmCursor::BuildGraph(){
+ //Save the current anchor and position
+ SmCaretPos _anchor, _position;
+ //Release mpGraph if allocated
+ if(mpGraph){
+ if(mpAnchor)
+ _anchor = mpAnchor->CaretPos;
+ if(mpPosition)
+ _position = mpPosition->CaretPos;
+ mpGraph.reset();
+ //Reset anchor and position as they point into an old graph
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ }
+
+ //Build the new graph
+ mpGraph.reset(SmCaretPosGraphBuildingVisitor(mpTree).takeGraph());
+
+ //Restore anchor and position pointers
+ if(_anchor.IsValid() || _position.IsValid()){
+ for(const auto &pEntry : *mpGraph)
+ {
+ if(_anchor == pEntry->CaretPos)
+ mpAnchor = pEntry.get();
+ if(_position == pEntry->CaretPos)
+ mpPosition = pEntry.get();
+ }
+ }
+ //Set position and anchor to first caret position
+ auto it = mpGraph->begin();
+ assert(it != mpGraph->end());
+ if(!mpPosition)
+ mpPosition = it->get();
+ if(!mpAnchor)
+ mpAnchor = mpPosition;
+
+ assert(mpPosition);
+ assert(mpAnchor);
+ OSL_ENSURE(mpPosition->CaretPos.IsValid(), "Position must be valid");
+ OSL_ENSURE(mpAnchor->CaretPos.IsValid(), "Anchor must be valid");
+}
+
+bool SmCursor::SetCaretPosition(SmCaretPos pos){
+ for(const auto &pEntry : *mpGraph)
+ {
+ if(pEntry->CaretPos == pos)
+ {
+ mpPosition = pEntry.get();
+ mpAnchor = pEntry.get();
+ return true;
+ }
+ }
+ return false;
+}
+
+void SmCursor::AnnotateSelection(){
+ //TODO: Manage a state, reset it upon modification and optimize this call
+ SmSetSelectionVisitor(mpAnchor->CaretPos, mpPosition->CaretPos, mpTree);
+}
+
+void SmCursor::Draw(OutputDevice& pDev, Point Offset, bool isCaretVisible){
+ SmCaretDrawingVisitor(pDev, GetPosition(), Offset, isCaretVisible);
+}
+
+void SmCursor::DeletePrev(OutputDevice* pDev){
+ //Delete only a selection if there's a selection
+ if(HasSelection()){
+ Delete();
+ return;
+ }
+
+ SmNode* pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+ SmStructureNode* pLineParent = pLine->GetParent();
+ int nLineOffsetIdx = pLineParent->IndexOfSubNode(pLine);
+ assert(nLineOffsetIdx >= 0);
+
+ //If we're in front of a node who's parent is a TABLE
+ if (pLineParent->GetType() == SmNodeType::Table && mpPosition->CaretPos.nIndex == 0 && nLineOffsetIdx > 0)
+ {
+ size_t nLineOffset = nLineOffsetIdx;
+ //Now we can merge with nLineOffset - 1
+ BeginEdit();
+ //Line to merge things into, so we can delete pLine
+ SmNode* pMergeLine = pLineParent->GetSubNode(nLineOffset-1);
+ OSL_ENSURE(pMergeLine, "pMergeLine cannot be NULL!");
+ SmCaretPos PosAfterDelete;
+ //Convert first line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pMergeLine, *pLineList);
+ if(!pLineList->empty()){
+ //Find iterator to patch
+ SmNodeList::iterator patchPoint = pLineList->end();
+ --patchPoint;
+ //Convert second line to list
+ NodeToList(pLine, *pLineList);
+ //Patch the line list
+ ++patchPoint;
+ PosAfterDelete = PatchLineList(pLineList.get(), patchPoint);
+ //Parse the line
+ pLine = SmNodeListParser().Parse(pLineList.get());
+ }
+ pLineList.reset();
+ pLineParent->SetSubNode(nLineOffset-1, pLine);
+ //Delete the removed line slot
+ SmNodeArray lines(pLineParent->GetNumSubNodes()-1);
+ for (size_t i = 0; i < pLineParent->GetNumSubNodes(); ++i)
+ {
+ if(i < nLineOffset)
+ lines[i] = pLineParent->GetSubNode(i);
+ else if(i > nLineOffset)
+ lines[i-1] = pLineParent->GetSubNode(i);
+ }
+ pLineParent->SetSubNodes(std::move(lines));
+ //Rebuild graph
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ BuildGraph();
+ AnnotateSelection();
+ //Set caret position
+ if(!SetCaretPosition(PosAfterDelete))
+ SetCaretPosition(SmCaretPos(pLine, 0));
+ //Finish editing
+ EndEdit();
+
+ //TODO: If we're in an empty (sub/super/*) script
+ /*}else if(pLineParent->GetType() == SmNodeType::SubSup &&
+ nLineOffset != 0 &&
+ pLine->GetType() == SmNodeType::Expression &&
+ pLine->GetNumSubNodes() == 0){
+ //There's a (sub/super) script we can delete
+ //Consider selecting the entire script if GetNumSubNodes() != 0 or pLine->GetType() != SmNodeType::Expression
+ //TODO: Handle case where we delete a limit
+ */
+
+ //Else move select, and delete if not complex
+ }else{
+ Move(pDev, MoveLeft, false);
+ if(!HasComplexSelection())
+ Delete();
+ }
+}
+
+void SmCursor::Delete(){
+ //Return if we don't have a selection to delete
+ if(!HasSelection())
+ return;
+
+ //Enter edit section
+ BeginEdit();
+
+ //Set selected on nodes
+ AnnotateSelection();
+
+ //Find an arbitrary selected node
+ SmNode* pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+
+ //Find the topmost node of the line that holds the selection
+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
+ OSL_ENSURE(pLine != mpTree, "Shouldn't be able to select the entire tree");
+
+ //Get the parent of the line
+ SmStructureNode* pLineParent = pLine->GetParent();
+ //Find line offset in parent
+ int nLineOffset = pLineParent->IndexOfSubNode(pLine);
+ assert(nLineOffset >= 0);
+
+ //Position after delete
+ SmCaretPos PosAfterDelete;
+
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selected nodes and delete them...
+ SmNodeList::iterator patchIt = TakeSelectedNodesFromList(pLineList.get());
+
+ //Get the position to set after delete
+ PosAfterDelete = PatchLineList(pLineList.get(), patchIt);
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nLineOffset, PosAfterDelete);
+}
+
+void SmCursor::InsertNodes(std::unique_ptr<SmNodeList> pNewNodes){
+ if(pNewNodes->empty()){
+ return;
+ }
+
+ //Begin edit section
+ BeginEdit();
+
+ //Get the current position
+ const SmCaretPos pos = mpPosition->CaretPos;
+
+ //Find top most of line that holds position
+ SmNode* pLine = FindTopMostNodeInLine(pos.pSelectedNode);
+
+ //Find line parent and line index in parent
+ SmStructureNode* pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Find iterator for place to insert nodes
+ SmNodeList::iterator it = FindPositionInLineList(pLineList.get(), pos);
+
+ //Insert all new nodes
+ SmNodeList::iterator newIt,
+ patchIt = it, // (pointless default value, fixes compiler warnings)
+ insIt;
+ for(newIt = pNewNodes->begin(); newIt != pNewNodes->end(); ++newIt){
+ insIt = pLineList->insert(it, *newIt);
+ if(newIt == pNewNodes->begin())
+ patchIt = insIt;
+ }
+ //Patch the places we've changed stuff
+ PatchLineList(pLineList.get(), patchIt);
+ SmCaretPos PosAfterInsert = PatchLineList(pLineList.get(), it);
+ //Release list, we've taken the nodes
+ pNewNodes.reset();
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+}
+
+SmNodeList::iterator SmCursor::FindPositionInLineList(SmNodeList* pLineList,
+ const SmCaretPos& rCaretPos)
+{
+ //Find iterator for position
+ SmNodeList::iterator it = std::find(pLineList->begin(), pLineList->end(), rCaretPos.pSelectedNode);
+ if (it != pLineList->end())
+ {
+ if((*it)->GetType() == SmNodeType::Text)
+ {
+ //Split textnode if needed
+ if(rCaretPos.nIndex > 0)
+ {
+ SmTextNode* pText = static_cast<SmTextNode*>(rCaretPos.pSelectedNode);
+ if (rCaretPos.nIndex == pText->GetText().getLength())
+ return ++it;
+ OUString str1 = pText->GetText().copy(0, rCaretPos.nIndex);
+ OUString str2 = pText->GetText().copy(rCaretPos.nIndex);
+ pText->ChangeText(str1);
+ ++it;
+ //Insert str2 as new text node
+ assert(!str2.isEmpty());
+ SmTextNode* pNewText = new SmTextNode(pText->GetToken(), pText->GetFontDesc());
+ pNewText->ChangeText(str2);
+ it = pLineList->insert(it, pNewText);
+ }
+ }else
+ ++it;
+ //it now pointer to the node following pos, so pLineList->insert(it, ...) will insert correctly
+ return it;
+ }
+ //If we didn't find pSelectedNode, it must be because the caret is in front of the line
+ return pLineList->begin();
+}
+
+SmCaretPos SmCursor::PatchLineList(SmNodeList* pLineList, SmNodeList::iterator aIter) {
+ //The nodes we should consider merging
+ SmNode *prev = nullptr,
+ *next = nullptr;
+ if(aIter != pLineList->end())
+ next = *aIter;
+ if(aIter != pLineList->begin()) {
+ --aIter;
+ prev = *aIter;
+ ++aIter;
+ }
+
+ //Check if there's textnodes to merge
+ if( prev &&
+ next &&
+ prev->GetType() == SmNodeType::Text &&
+ next->GetType() == SmNodeType::Text &&
+ ( prev->GetToken().eType != TNUMBER ||
+ next->GetToken().eType == TNUMBER) ){
+ SmTextNode *pText = static_cast<SmTextNode*>(prev),
+ *pOldN = static_cast<SmTextNode*>(next);
+ SmCaretPos retval(pText, pText->GetText().getLength());
+ OUString newText = pText->GetText() + pOldN->GetText();
+ pText->ChangeText(newText);
+ delete pOldN;
+ pLineList->erase(aIter);
+ return retval;
+ }
+
+ //Check if there's a SmPlaceNode to remove:
+ if(prev && next && prev->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(next->GetToken())){
+ --aIter;
+ aIter = pLineList->erase(aIter);
+ delete prev;
+ //Return caret pos in front of aIter
+ if(aIter != pLineList->begin())
+ --aIter; //Thus find node before aIter
+ if(aIter == pLineList->begin())
+ return SmCaretPos();
+ return SmCaretPos::GetPosAfter(*aIter);
+ }
+ if(prev && next && next->GetType() == SmNodeType::Place && !SmNodeListParser::IsOperator(prev->GetToken())){
+ aIter = pLineList->erase(aIter);
+ delete next;
+ return SmCaretPos::GetPosAfter(prev);
+ }
+
+ //If we didn't do anything return
+ if(!prev) //return an invalid to indicate we're in front of line
+ return SmCaretPos();
+ return SmCaretPos::GetPosAfter(prev);
+}
+
+SmNodeList::iterator SmCursor::TakeSelectedNodesFromList(SmNodeList *pLineList,
+ SmNodeList *pSelectedNodes) {
+ SmNodeList::iterator retval;
+ SmNodeList::iterator it = pLineList->begin();
+ while(it != pLineList->end()){
+ if((*it)->IsSelected()){
+ //Split text nodes
+ if((*it)->GetType() == SmNodeType::Text) {
+ SmTextNode* pText = static_cast<SmTextNode*>(*it);
+ OUString aText = pText->GetText();
+ //Start and lengths of the segments, 2 is the selected segment
+ int start2 = pText->GetSelectionStart(),
+ start3 = pText->GetSelectionEnd(),
+ len1 = start2 - 0,
+ len2 = start3 - start2,
+ len3 = aText.getLength() - start3;
+ SmToken aToken = pText->GetToken();
+ sal_uInt16 eFontDesc = pText->GetFontDesc();
+ //If we need make segment 1
+ if(len1 > 0) {
+ OUString str = aText.copy(0, len1);
+ pText->ChangeText(str);
+ ++it;
+ } else {//Remove it if not needed
+ it = pLineList->erase(it);
+ delete pText;
+ }
+ //Set retval to be right after the selection
+ retval = it;
+ //if we need make segment 3
+ if(len3 > 0) {
+ OUString str = aText.copy(start3, len3);
+ SmTextNode* pSeg3 = new SmTextNode(aToken, eFontDesc);
+ pSeg3->ChangeText(str);
+ retval = pLineList->insert(it, pSeg3);
+ }
+ //If we need to save the selected text
+ if(pSelectedNodes && len2 > 0) {
+ OUString str = aText.copy(start2, len2);
+ SmTextNode* pSeg2 = new SmTextNode(aToken, eFontDesc);
+ pSeg2->ChangeText(str);
+ pSelectedNodes->push_back(pSeg2);
+ }
+ } else { //if it's not textnode
+ SmNode* pNode = *it;
+ retval = it = pLineList->erase(it);
+ if(pSelectedNodes)
+ pSelectedNodes->push_back(pNode);
+ else
+ delete pNode;
+ }
+ } else
+ ++it;
+ }
+ return retval;
+}
+
+void SmCursor::InsertSubSup(SmSubSup eSubSup) {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find Parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //TODO: Consider handling special cases where parent is an SmOperNode,
+ // Maybe this method should be able to add limits to an SmOperNode...
+
+ //We begin modifying the tree here
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //Find node that this should be applied to
+ SmNode* pSubject;
+ bool bPatchLine = !pSelectedNodesList->empty(); //If the line should be patched later
+ if(it != pLineList->begin()) {
+ --it;
+ pSubject = *it;
+ ++it;
+ } else {
+ //Create a new place node
+ pSubject = new SmPlaceNode();
+ pSubject->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+ it = pLineList->insert(it, pSubject);
+ ++it;
+ bPatchLine = true; //We've modified the line it should be patched later.
+ }
+
+ //Wrap the subject in a SmSubSupNode
+ SmSubSupNode* pSubSup;
+ if(pSubject->GetType() != SmNodeType::SubSup){
+ SmToken token;
+ token.nGroup = TG::Power;
+ pSubSup = new SmSubSupNode(token);
+ pSubSup->SetBody(pSubject);
+ *(--it) = pSubSup;
+ ++it;
+ }else
+ pSubSup = static_cast<SmSubSupNode*>(pSubject);
+ //pSubject shouldn't be referenced anymore, pSubSup is the SmSubSupNode in pLineList we wish to edit.
+ //and it pointer to the element following pSubSup in pLineList.
+ pSubject = nullptr;
+
+ //Patch the line if we noted that was needed previously
+ if(bPatchLine)
+ PatchLineList(pLineList.get(), it);
+
+ //Convert existing, if any, sub-/superscript line to list
+ SmNode *pScriptLine = pSubSup->GetSubSup(eSubSup);
+ std::unique_ptr<SmNodeList> pScriptLineList(new SmNodeList);
+ NodeToList(pScriptLine, *pScriptLineList);
+
+ //Add selection to pScriptLineList
+ unsigned int nOldSize = pScriptLineList->size();
+ pScriptLineList->insert(pScriptLineList->end(), pSelectedNodesList->begin(), pSelectedNodesList->end());
+ pSelectedNodesList.reset();
+
+ //Patch pScriptLineList if needed
+ if(0 < nOldSize && nOldSize < pScriptLineList->size()) {
+ SmNodeList::iterator iPatchPoint = pScriptLineList->begin();
+ std::advance(iPatchPoint, nOldSize);
+ PatchLineList(pScriptLineList.get(), iPatchPoint);
+ }
+
+ //Find caret pos, that should be used after sub-/superscription.
+ SmCaretPos PosAfterScript; //Leave invalid for first position
+ if (!pScriptLineList->empty())
+ PosAfterScript = SmCaretPos::GetPosAfter(pScriptLineList->back());
+
+ //Parse pScriptLineList
+ pScriptLine = SmNodeListParser().Parse(pScriptLineList.get());
+ pScriptLineList.reset();
+
+ //Insert pScriptLine back into the tree
+ pSubSup->SetSubSup(eSubSup, pScriptLine);
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterScript, pScriptLine);
+}
+
+void SmCursor::InsertBrackets(SmBracketType eBracketType) {
+ BeginEdit();
+
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //If there's no selected nodes, create a place node
+ std::unique_ptr<SmNode> pBodyNode;
+ SmCaretPos PosAfterInsert;
+ if(pSelectedNodesList->empty()) {
+ pBodyNode.reset(new SmPlaceNode());
+ PosAfterInsert = SmCaretPos(pBodyNode.get(), 1);
+ } else
+ pBodyNode.reset(SmNodeListParser().Parse(pSelectedNodesList.get()));
+
+ pSelectedNodesList.reset();
+
+ //Create SmBraceNode
+ SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
+ SmBraceNode *pBrace = new SmBraceNode(aTok);
+ pBrace->SetScaleMode(SmScaleMode::Height);
+ std::unique_ptr<SmNode> pLeft( CreateBracket(eBracketType, true) ),
+ pRight( CreateBracket(eBracketType, false) );
+ std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
+ pBody->SetSubNodes(std::move(pBodyNode), nullptr);
+ pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert into line
+ pLineList->insert(it, pBrace);
+ //Patch line (I think this is good enough)
+ SmCaretPos aAfter = PatchLineList(pLineList.get(), it);
+ if( !PosAfterInsert.IsValid() )
+ PosAfterInsert = aAfter;
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+}
+
+SmNode *SmCursor::CreateBracket(SmBracketType eBracketType, bool bIsLeft) {
+ SmToken aTok;
+ if(bIsLeft){
+ switch(eBracketType){
+ case SmBracketType::Round:
+ aTok = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ break;
+ case SmBracketType::Square:
+ aTok = SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ break;
+ case SmBracketType::Curly:
+ aTok = SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ break;
+ }
+ } else {
+ switch(eBracketType) {
+ case SmBracketType::Round:
+ aTok = SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ break;
+ case SmBracketType::Square:
+ aTok = SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ break;
+ case SmBracketType::Curly:
+ aTok = SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ break;
+ }
+ }
+ SmNode* pRetVal = new SmMathSymbolNode(aTok);
+ pRetVal->SetScaleMode(SmScaleMode::Height);
+ return pRetVal;
+}
+
+bool SmCursor::InsertRow() {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //Discover the context of this command
+ SmTableNode *pTable = nullptr;
+ SmMatrixNode *pMatrix = nullptr;
+ int nTableIndex = nParentIndex;
+ if(pLineParent->GetType() == SmNodeType::Table)
+ pTable = static_cast<SmTableNode*>(pLineParent);
+ //If it's wrapped in a SmLineNode, we can still insert a newline
+ else if(pLineParent->GetType() == SmNodeType::Line &&
+ pLineParent->GetParent() &&
+ pLineParent->GetParent()->GetType() == SmNodeType::Table) {
+ //NOTE: This hack might give problems if we stop ignoring SmAlignNode
+ pTable = static_cast<SmTableNode*>(pLineParent->GetParent());
+ nTableIndex = pTable->IndexOfSubNode(pLineParent);
+ assert(nTableIndex >= 0);
+ }
+ if(pLineParent->GetType() == SmNodeType::Matrix)
+ pMatrix = static_cast<SmMatrixNode*>(pLineParent);
+
+ //If we're not in a context that supports InsertRow, return sal_False
+ if(!pTable && !pMatrix)
+ return false;
+
+ //Now we start editing
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Find position in line
+ SmNodeList::iterator it;
+ if(HasSelection()) {
+ //Take the selected nodes and delete them...
+ it = TakeSelectedNodesFromList(pLineList.get());
+ } else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //New caret position after inserting the newline/row in whatever context
+ SmCaretPos PosAfterInsert;
+
+ //If we're in the context of a table
+ if(pTable) {
+ std::unique_ptr<SmNodeList> pNewLineList(new SmNodeList);
+ //Move elements from pLineList to pNewLineList
+ SmNodeList& rLineList = *pLineList;
+ pNewLineList->splice(pNewLineList->begin(), rLineList, it, rLineList.end());
+ //Make sure it is valid again
+ it = pLineList->end();
+ if(it != pLineList->begin())
+ --it;
+ if(pNewLineList->empty())
+ pNewLineList->push_front(new SmPlaceNode());
+ //Parse new line
+ std::unique_ptr<SmNode> pNewLine(SmNodeListParser().Parse(pNewLineList.get()));
+ pNewLineList.reset();
+ //Wrap pNewLine in SmLineNode if needed
+ if(pLineParent->GetType() == SmNodeType::Line) {
+ std::unique_ptr<SmLineNode> pNewLineNode(new SmLineNode(SmToken(TNEWLINE, '\0', "newline")));
+ pNewLineNode->SetSubNodes(std::move(pNewLine), nullptr);
+ pNewLine = std::move(pNewLineNode);
+ }
+ //Get position
+ PosAfterInsert = SmCaretPos(pNewLine.get(), 0);
+ //Move other nodes if needed
+ for( int i = pTable->GetNumSubNodes(); i > nTableIndex + 1; i--)
+ pTable->SetSubNode(i, pTable->GetSubNode(i-1));
+
+ //Insert new line
+ pTable->SetSubNode(nTableIndex + 1, pNewLine.release());
+
+ //Check if we need to change token type:
+ if(pTable->GetNumSubNodes() > 2 && pTable->GetToken().eType == TBINOM) {
+ SmToken tok = pTable->GetToken();
+ tok.eType = TSTACK;
+ pTable->SetToken(tok);
+ }
+ }
+ //If we're in the context of a matrix
+ else {
+ //Find position after insert and patch the list
+ PosAfterInsert = PatchLineList(pLineList.get(), it);
+ //Move other children
+ sal_uInt16 rows = pMatrix->GetNumRows();
+ sal_uInt16 cols = pMatrix->GetNumCols();
+ int nRowStart = (nParentIndex - nParentIndex % cols) + cols;
+ for( int i = pMatrix->GetNumSubNodes() + cols - 1; i >= nRowStart + cols; i--)
+ pMatrix->SetSubNode(i, pMatrix->GetSubNode(i - cols));
+ for( int i = nRowStart; i < nRowStart + cols; i++) {
+ SmPlaceNode *pNewLine = new SmPlaceNode();
+ if(i == nParentIndex + cols)
+ PosAfterInsert = SmCaretPos(pNewLine, 0);
+ pMatrix->SetSubNode(i, pNewLine);
+ }
+ pMatrix->SetRowCol(rows + 1, cols);
+ }
+
+ //Finish editing
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, PosAfterInsert);
+ //FinishEdit is actually used to handle situations where parent is an instance of
+ //SmSubSupNode. In this case parent should always be a table or matrix, however, for
+ //code reuse we just use FinishEdit() here too.
+ return true;
+}
+
+void SmCursor::InsertFraction() {
+ AnnotateSelection();
+
+ //Find line
+ SmNode *pLine;
+ if(HasSelection()) {
+ SmNode *pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ pLine = FindTopMostNodeInLine(pSNode, true);
+ } else
+ pLine = FindTopMostNodeInLine(mpPosition->CaretPos.pSelectedNode);
+
+ //Find Parent and offset in parent
+ SmStructureNode *pLineParent = pLine->GetParent();
+ int nParentIndex = pLineParent->IndexOfSubNode(pLine);
+ assert(nParentIndex >= 0);
+
+ //We begin modifying the tree here
+ BeginEdit();
+
+ //Convert line to list
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pLine, *pLineList);
+
+ //Take the selection, and/or find iterator for current position
+ std::unique_ptr<SmNodeList> pSelectedNodesList(new SmNodeList);
+ SmNodeList::iterator it;
+ if(HasSelection())
+ it = TakeSelectedNodesFromList(pLineList.get(), pSelectedNodesList.get());
+ else
+ it = FindPositionInLineList(pLineList.get(), mpPosition->CaretPos);
+
+ //Create pNum, and pDenom
+ bool bEmptyFraction = pSelectedNodesList->empty();
+ std::unique_ptr<SmNode> pNum( bEmptyFraction
+ ? new SmPlaceNode()
+ : SmNodeListParser().Parse(pSelectedNodesList.get()) );
+ std::unique_ptr<SmNode> pDenom(new SmPlaceNode());
+ pSelectedNodesList.reset();
+
+ //Create new fraction
+ SmBinVerNode *pFrac = new SmBinVerNode(SmToken(TOVER, '\0', "over", TG::Product, 0));
+ std::unique_ptr<SmNode> pRect(new SmRectangleNode(SmToken()));
+ pFrac->SetSubNodes(std::move(pNum), std::move(pRect), std::move(pDenom));
+
+ //Insert in pLineList
+ SmNodeList::iterator patchIt = pLineList->insert(it, pFrac);
+ PatchLineList(pLineList.get(), patchIt);
+ PatchLineList(pLineList.get(), it);
+
+ //Finish editing
+ SmNode *pSelectedNode = bEmptyFraction ? pFrac->GetSubNode(0) : pFrac->GetSubNode(2);
+ FinishEdit(std::move(pLineList), pLineParent, nParentIndex, SmCaretPos(pSelectedNode, 1));
+}
+
+void SmCursor::InsertText(const OUString& aString)
+{
+ BeginEdit();
+
+ Delete();
+
+ SmToken token;
+ token.eType = TIDENT;
+ token.cMathChar = u"";
+ token.nGroup = TG::NONE;
+ token.nLevel = 5;
+ token.aText = aString;
+
+ SmTextNode* pText = new SmTextNode(token, FNT_VARIABLE);
+ pText->SetText(aString);
+ pText->AdjustFontDesc();
+ pText->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pText);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertElement(SmFormulaElement element){
+ BeginEdit();
+
+ Delete();
+
+ //Create new node
+ SmNode* pNewNode = nullptr;
+ switch(element){
+ case BlankElement:
+ {
+ SmToken token;
+ token.eType = TBLANK;
+ token.nGroup = TG::Blank;
+ token.aText = "~";
+ SmBlankNode* pBlankNode = new SmBlankNode(token);
+ pBlankNode->IncreaseBy(token);
+ pNewNode = pBlankNode;
+ }break;
+ case FactorialElement:
+ {
+ SmToken token(TFACT, MS_FACT, "fact", TG::UnOper, 5);
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case PlusElement:
+ {
+ SmToken token;
+ token.eType = TPLUS;
+ token.setChar(MS_PLUS);
+ token.nGroup = TG::UnOper | TG::Sum;
+ token.nLevel = 5;
+ token.aText = "+";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case MinusElement:
+ {
+ SmToken token;
+ token.eType = TMINUS;
+ token.setChar(MS_MINUS);
+ token.nGroup = TG::UnOper | TG::Sum;
+ token.nLevel = 5;
+ token.aText = "-";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case CDotElement:
+ {
+ SmToken token;
+ token.eType = TCDOT;
+ token.setChar(MS_CDOT);
+ token.nGroup = TG::Product;
+ token.aText = "cdot";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case EqualElement:
+ {
+ SmToken token;
+ token.eType = TASSIGN;
+ token.setChar(MS_ASSIGN);
+ token.nGroup = TG::Relation;
+ token.aText = "=";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case LessThanElement:
+ {
+ SmToken token;
+ token.eType = TLT;
+ token.setChar(MS_LT);
+ token.nGroup = TG::Relation;
+ token.aText = "<";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case GreaterThanElement:
+ {
+ SmToken token;
+ token.eType = TGT;
+ token.setChar(MS_GT);
+ token.nGroup = TG::Relation;
+ token.aText = ">";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ case PercentElement:
+ {
+ SmToken token;
+ token.eType = TTEXT;
+ token.setChar(MS_PERCENT);
+ token.nGroup = TG::NONE;
+ token.aText = "\"%\"";
+ pNewNode = new SmMathSymbolNode(token);
+ }break;
+ }
+ assert(pNewNode);
+
+ //Prepare the new node
+ pNewNode->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert new node
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pNewNode);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertSpecial(std::u16string_view _aString)
+{
+ BeginEdit();
+ Delete();
+
+ OUString aString( comphelper::string::strip(_aString, ' ') );
+
+ //Create instance of special node
+ SmToken token;
+ token.eType = TSPECIAL;
+ token.cMathChar = u"";
+ token.nGroup = TG::NONE;
+ token.nLevel = 5;
+ token.aText = aString;
+ SmSpecialNode* pSpecial = new SmSpecialNode(token);
+
+ //Prepare the special node
+ pSpecial->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Insert the node
+ std::unique_ptr<SmNodeList> pList(new SmNodeList);
+ pList->push_front(pSpecial);
+ InsertNodes(std::move(pList));
+
+ EndEdit();
+}
+
+void SmCursor::InsertCommandText(const OUString& aCommandText) {
+ //Parse the sub expression
+ auto xSubExpr = mpDocShell->GetParser()->ParseExpression(aCommandText);
+
+ //Prepare the subtree
+ xSubExpr->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+
+ //Convert subtree to list
+ SmNode* pSubExpr = xSubExpr.release();
+ std::unique_ptr<SmNodeList> pLineList(new SmNodeList);
+ NodeToList(pSubExpr, *pLineList);
+
+ BeginEdit();
+
+ //Delete any selection
+ Delete();
+
+ //Insert it
+ InsertNodes(std::move(pLineList));
+
+ EndEdit();
+}
+
+void SmCursor::Copy(){
+ if(!HasSelection())
+ return;
+
+ AnnotateSelection();
+ //Find selected node
+ SmNode* pSNode = FindSelectedNode(mpTree);
+ assert(pSNode);
+ //Find visual line
+ SmNode* pLine = FindTopMostNodeInLine(pSNode, true);
+ assert(pLine);
+
+ //Clone selected nodes
+ SmClipboard aClipboard;
+ if(IsLineCompositionNode(pLine))
+ CloneLineToClipboard(static_cast<SmStructureNode*>(pLine), &aClipboard);
+ else{
+ //Special care to only clone selected text
+ if(pLine->GetType() == SmNodeType::Text) {
+ SmTextNode *pText = static_cast<SmTextNode*>(pLine);
+ std::unique_ptr<SmTextNode> pClone(new SmTextNode( pText->GetToken(), pText->GetFontDesc() ));
+ int start = pText->GetSelectionStart(),
+ length = pText->GetSelectionEnd() - pText->GetSelectionStart();
+ pClone->ChangeText(pText->GetText().copy(start, length));
+ pClone->SetScaleMode(pText->GetScaleMode());
+ aClipboard.push_front(std::move(pClone));
+ } else {
+ SmCloningVisitor aCloneFactory;
+ aClipboard.push_front(std::unique_ptr<SmNode>(aCloneFactory.Clone(pLine)));
+ }
+ }
+
+ //Set clipboard
+ if (!aClipboard.empty())
+ maClipboard = std::move(aClipboard);
+}
+
+void SmCursor::Paste() {
+ BeginEdit();
+ Delete();
+
+ if (!maClipboard.empty())
+ InsertNodes(CloneList(maClipboard));
+
+ EndEdit();
+}
+
+std::unique_ptr<SmNodeList> SmCursor::CloneList(SmClipboard &rClipboard){
+ SmCloningVisitor aCloneFactory;
+ std::unique_ptr<SmNodeList> pClones(new SmNodeList);
+
+ for(auto &xNode : rClipboard){
+ SmNode *pClone = aCloneFactory.Clone(xNode.get());
+ pClones->push_back(pClone);
+ }
+
+ return pClones;
+}
+
+SmNode* SmCursor::FindTopMostNodeInLine(SmNode* pSNode, bool MoveUpIfSelected){
+ assert(pSNode);
+ //Move up parent until we find a node who's
+ //parent is NULL or isn't selected and not a type of:
+ // SmExpressionNode
+ // SmLineNode
+ // SmBinHorNode
+ // SmUnHorNode
+ // SmAlignNode
+ // SmFontNode
+ while(pSNode->GetParent() &&
+ ((MoveUpIfSelected &&
+ pSNode->GetParent()->IsSelected()) ||
+ IsLineCompositionNode(pSNode->GetParent())))
+ pSNode = pSNode->GetParent();
+ //Now we have the selection line node
+ return pSNode;
+}
+
+SmNode* SmCursor::FindSelectedNode(SmNode* pNode){
+ if(pNode->GetNumSubNodes() == 0)
+ return nullptr;
+ for(auto pChild : *static_cast<SmStructureNode*>(pNode))
+ {
+ if(!pChild)
+ continue;
+ if(pChild->IsSelected())
+ return pChild;
+ SmNode* pRetVal = FindSelectedNode(pChild);
+ if(pRetVal)
+ return pRetVal;
+ }
+ return nullptr;
+}
+
+void SmCursor::LineToList(SmStructureNode* pLine, SmNodeList& list){
+ for(auto pChild : *pLine)
+ {
+ if (!pChild)
+ continue;
+ switch(pChild->GetType()){
+ case SmNodeType::Line:
+ case SmNodeType::UnHor:
+ case SmNodeType::Expression:
+ case SmNodeType::BinHor:
+ case SmNodeType::Align:
+ case SmNodeType::Font:
+ LineToList(static_cast<SmStructureNode*>(pChild), list);
+ break;
+ case SmNodeType::Error:
+ delete pChild;
+ break;
+ default:
+ list.push_back(pChild);
+ }
+ }
+ pLine->ClearSubNodes();
+ delete pLine;
+}
+
+void SmCursor::CloneLineToClipboard(SmStructureNode* pLine, SmClipboard* pClipboard){
+ SmCloningVisitor aCloneFactory;
+ for(auto pChild : *pLine)
+ {
+ if (!pChild)
+ continue;
+ if( IsLineCompositionNode( pChild ) )
+ CloneLineToClipboard( static_cast<SmStructureNode*>(pChild), pClipboard );
+ else if( pChild->IsSelected() && pChild->GetType() != SmNodeType::Error ) {
+ //Only clone selected text from SmTextNode
+ if(pChild->GetType() == SmNodeType::Text) {
+ SmTextNode *pText = static_cast<SmTextNode*>(pChild);
+ std::unique_ptr<SmTextNode> pClone(new SmTextNode( pChild->GetToken(), pText->GetFontDesc() ));
+ int start = pText->GetSelectionStart(),
+ length = pText->GetSelectionEnd() - pText->GetSelectionStart();
+ pClone->ChangeText(pText->GetText().copy(start, length));
+ pClone->SetScaleMode(pText->GetScaleMode());
+ pClipboard->push_back(std::move(pClone));
+ } else
+ pClipboard->push_back(std::unique_ptr<SmNode>(aCloneFactory.Clone(pChild)));
+ }
+ }
+}
+
+bool SmCursor::IsLineCompositionNode(SmNode const * pNode){
+ switch(pNode->GetType()){
+ case SmNodeType::Line:
+ case SmNodeType::UnHor:
+ case SmNodeType::Expression:
+ case SmNodeType::BinHor:
+ case SmNodeType::Align:
+ case SmNodeType::Font:
+ return true;
+ default:
+ return false;
+ }
+}
+
+int SmCursor::CountSelectedNodes(SmNode* pNode){
+ if(pNode->GetNumSubNodes() == 0)
+ return 0;
+ int nCount = 0;
+ for(auto pChild : *static_cast<SmStructureNode*>(pNode))
+ {
+ if (!pChild)
+ continue;
+ if(pChild->IsSelected() && !IsLineCompositionNode(pChild))
+ nCount++;
+ nCount += CountSelectedNodes(pChild);
+ }
+ return nCount;
+}
+
+bool SmCursor::HasComplexSelection(){
+ if(!HasSelection())
+ return false;
+ AnnotateSelection();
+
+ return CountSelectedNodes(mpTree) > 1;
+}
+
+void SmCursor::FinishEdit(std::unique_ptr<SmNodeList> pLineList,
+ SmStructureNode* pParent,
+ int nParentIndex,
+ SmCaretPos PosAfterEdit,
+ SmNode* pStartLine) {
+ //Store number of nodes in line for later
+ int entries = pLineList->size();
+
+ //Parse list of nodes to a tree
+ SmNodeListParser parser;
+ std::unique_ptr<SmNode> pLine(parser.Parse(pLineList.get()));
+ pLineList.reset();
+
+ //Check if we're making the body of a subsup node bigger than one
+ if(pParent->GetType() == SmNodeType::SubSup &&
+ nParentIndex == 0 &&
+ entries > 1) {
+ //Wrap pLine in scalable round brackets
+ SmToken aTok(TLEFT, '\0', "left", TG::NONE, 5);
+ std::unique_ptr<SmBraceNode> pBrace(new SmBraceNode(aTok));
+ pBrace->SetScaleMode(SmScaleMode::Height);
+ std::unique_ptr<SmNode> pLeft( CreateBracket(SmBracketType::Round, true) ),
+ pRight( CreateBracket(SmBracketType::Round, false) );
+ std::unique_ptr<SmBracebodyNode> pBody(new SmBracebodyNode(SmToken()));
+ pBody->SetSubNodes(std::move(pLine), nullptr);
+ pBrace->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pBrace->Prepare(mpDocShell->GetFormat(), *mpDocShell, 0);
+ pLine = std::move(pBrace);
+ //TODO: Consider the following alternative behavior:
+ //Consider the line: A + {B + C}^D lsub E
+ //Here pLineList is B, + and C and pParent is a subsup node with
+ //both RSUP and LSUB set. Imagine the user just inserted "B +" in
+ //the body of the subsup node...
+ //The most natural thing to do would be to make the line like this:
+ //A + B lsub E + C ^ D
+ //E.g. apply LSUB and LSUP to the first element in pLineList and RSUP
+ //and RSUB to the last element in pLineList. But how should this act
+ //for CSUP and CSUB ???
+ //For this reason and because brackets was faster to implement, this solution
+ //have been chosen. It might be worth working on the other solution later...
+ }
+
+ //Set pStartLine if NULL
+ if(!pStartLine)
+ pStartLine = pLine.get();
+
+ //Insert it back into the parent
+ pParent->SetSubNode(nParentIndex, pLine.release());
+
+ //Rebuild graph of caret position
+ mpAnchor = nullptr;
+ mpPosition = nullptr;
+ BuildGraph();
+ AnnotateSelection(); //Update selection annotation!
+
+ //Set caret position
+ if(!SetCaretPosition(PosAfterEdit))
+ SetCaretPosition(SmCaretPos(pStartLine, 0));
+
+ //End edit section
+ EndEdit();
+}
+
+void SmCursor::BeginEdit(){
+ if(mnEditSections++ > 0) return;
+
+ mbIsEnabledSetModifiedSmDocShell = mpDocShell->IsEnableSetModified();
+ if( mbIsEnabledSetModifiedSmDocShell )
+ mpDocShell->EnableSetModified( false );
+}
+
+void SmCursor::EndEdit(){
+ if(--mnEditSections > 0) return;
+
+ mpDocShell->SetFormulaArranged(false);
+ //Okay, I don't know what this does... :)
+ //It's used in SmDocShell::SetText and with places where everything is modified.
+ //I think it does some magic, with sfx, but everything is totally undocumented so
+ //it's kinda hard to tell...
+ if ( mbIsEnabledSetModifiedSmDocShell )
+ mpDocShell->EnableSetModified( mbIsEnabledSetModifiedSmDocShell );
+ //I think this notifies people around us that we've modified this document...
+ mpDocShell->SetModified();
+ //I think SmDocShell uses this value when it sends an update graphics event
+ //Anyway comments elsewhere suggests it needs to be updated...
+ mpDocShell->mnModifyCount++;
+
+ //TODO: Consider copying the update accessibility code from SmDocShell::SetText in here...
+ //This somehow updates the size of SmGraphicView if it is running in embedded mode
+ if( mpDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ mpDocShell->OnDocumentPrinterChanged(nullptr);
+
+ //Request a repaint...
+ RequestRepaint();
+
+ //Update the edit engine and text of the document
+ OUString formula;
+ SmNodeToTextVisitor(mpTree, formula);
+ mpDocShell->maText = formula;
+ mpDocShell->GetEditEngine().QuickInsertText( formula, ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
+ mpDocShell->GetEditEngine().QuickFormatDoc();
+}
+
+void SmCursor::RequestRepaint(){
+ if (SmViewShell *pViewSh = SmGetActiveView())
+ {
+ if ( SfxObjectCreateMode::EMBEDDED == mpDocShell->GetCreateMode() )
+ mpDocShell->Repaint();
+ else
+ pViewSh->GetGraphicWidget().Invalidate();
+ }
+}
+
+bool SmCursor::IsAtTailOfBracket(SmBracketType eBracketType) const
+{
+ const SmCaretPos pos = GetPosition();
+ if (!pos.IsValid()) {
+ return false;
+ }
+
+ SmNode* pNode = pos.pSelectedNode;
+
+ if (pNode->GetType() == SmNodeType::Text) {
+ SmTextNode* pTextNode = static_cast<SmTextNode*>(pNode);
+ if (pos.nIndex < pTextNode->GetText().getLength()) {
+ // The cursor is on a text node and at the middle of it.
+ return false;
+ }
+ } else {
+ if (pos.nIndex < 1) {
+ return false;
+ }
+ }
+
+ while (true) {
+ SmStructureNode* pParentNode = pNode->GetParent();
+ if (!pParentNode) {
+ // There's no brace body node in the ancestors.
+ return false;
+ }
+
+ int index = pParentNode->IndexOfSubNode(pNode);
+ assert(index >= 0);
+ if (static_cast<size_t>(index + 1) != pParentNode->GetNumSubNodes()) {
+ // The cursor is not at the tail at one of ancestor nodes.
+ return false;
+ }
+
+ pNode = pParentNode;
+ if (pNode->GetType() == SmNodeType::Bracebody) {
+ // Found the brace body node.
+ break;
+ }
+ }
+
+ SmStructureNode* pBraceNodeTmp = pNode->GetParent();
+ if (!pBraceNodeTmp || pBraceNodeTmp->GetType() != SmNodeType::Brace) {
+ // Brace node is invalid.
+ return false;
+ }
+
+ SmBraceNode* pBraceNode = static_cast<SmBraceNode*>(pBraceNodeTmp);
+ SmMathSymbolNode* pClosingNode = pBraceNode->ClosingBrace();
+ if (!pClosingNode) {
+ // Couldn't get closing symbol node.
+ return false;
+ }
+
+ // Check if the closing brace matches eBracketType.
+ SmTokenType eClosingTokenType = pClosingNode->GetToken().eType;
+ switch (eBracketType) {
+ case SmBracketType::Round: if (eClosingTokenType != TRPARENT) { return false; } break;
+ case SmBracketType::Square: if (eClosingTokenType != TRBRACKET) { return false; } break;
+ case SmBracketType::Curly: if (eClosingTokenType != TRBRACE) { return false; } break;
+ default:
+ return false;
+ }
+
+ return true;
+}
+
+/////////////////////////////////////// SmNodeListParser
+
+SmNode* SmNodeListParser::Parse(SmNodeList* list){
+ pList = list;
+ //Delete error nodes
+ SmNodeList::iterator it = pList->begin();
+ while(it != pList->end()) {
+ if((*it)->GetType() == SmNodeType::Error){
+ //Delete and erase
+ delete *it;
+ it = pList->erase(it);
+ }else
+ ++it;
+ }
+ SmNode* retval = Expression();
+ pList = nullptr;
+ return retval;
+}
+
+SmNode* SmNodeListParser::Expression(){
+ SmNodeArray NodeArray;
+ //Accept as many relations as there is
+ while(Terminal())
+ NodeArray.push_back(Relation());
+
+ //Create SmExpressionNode, I hope SmToken() will do :)
+ SmStructureNode* pExpr = new SmExpressionNode(SmToken());
+ pExpr->SetSubNodes(std::move(NodeArray));
+ return pExpr;
+}
+
+SmNode* SmNodeListParser::Relation(){
+ //Read a sum
+ std::unique_ptr<SmNode> pLeft(Sum());
+ //While we have tokens and the next is a relation
+ while(Terminal() && IsRelationOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the relation
+ std::unique_ptr<SmNode> pRight(Sum());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Sum(){
+ //Read a product
+ std::unique_ptr<SmNode> pLeft(Product());
+ //While we have tokens and the next is a sum
+ while(Terminal() && IsSumOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the sum
+ std::unique_ptr<SmNode> pRight(Product());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Product(){
+ //Read a Factor
+ std::unique_ptr<SmNode> pLeft(Factor());
+ //While we have tokens and the next is a product
+ while(Terminal() && IsProductOperator(Terminal()->GetToken())){
+ //Take the operator
+ std::unique_ptr<SmNode> pOper(Take());
+ //Find the right side of the operation
+ std::unique_ptr<SmNode> pRight(Factor());
+ //Create new SmBinHorNode
+ std::unique_ptr<SmStructureNode> pNewNode(new SmBinHorNode(SmToken()));
+ pNewNode->SetSubNodes(std::move(pLeft), std::move(pOper), std::move(pRight));
+ pLeft = std::move(pNewNode);
+ }
+ return pLeft.release();
+}
+
+SmNode* SmNodeListParser::Factor(){
+ //Read unary operations
+ if(!Terminal())
+ return Error();
+ //Take care of unary operators
+ else if(IsUnaryOperator(Terminal()->GetToken()))
+ {
+ SmStructureNode *pUnary = new SmUnHorNode(SmToken());
+ std::unique_ptr<SmNode> pOper(Terminal()),
+ pArg;
+
+ if(Next())
+ pArg.reset(Factor());
+ else
+ pArg.reset(Error());
+
+ pUnary->SetSubNodes(std::move(pOper), std::move(pArg));
+ return pUnary;
+ }
+ return Postfix();
+}
+
+SmNode* SmNodeListParser::Postfix(){
+ if(!Terminal())
+ return Error();
+ std::unique_ptr<SmNode> pArg;
+ if(IsPostfixOperator(Terminal()->GetToken()))
+ pArg.reset(Error());
+ else if(IsOperator(Terminal()->GetToken()))
+ return Error();
+ else
+ pArg.reset(Take());
+ while(Terminal() && IsPostfixOperator(Terminal()->GetToken())) {
+ std::unique_ptr<SmStructureNode> pUnary(new SmUnHorNode(SmToken()) );
+ std::unique_ptr<SmNode> pOper(Take());
+ pUnary->SetSubNodes(std::move(pArg), std::move(pOper));
+ pArg = std::move(pUnary);
+ }
+ return pArg.release();
+}
+
+SmNode* SmNodeListParser::Error(){
+ return new SmErrorNode(SmToken());
+}
+
+bool SmNodeListParser::IsOperator(const SmToken &token) {
+ return IsRelationOperator(token) ||
+ IsSumOperator(token) ||
+ IsProductOperator(token) ||
+ IsUnaryOperator(token) ||
+ IsPostfixOperator(token);
+}
+
+bool SmNodeListParser::IsRelationOperator(const SmToken &token) {
+ return bool(token.nGroup & TG::Relation);
+}
+
+bool SmNodeListParser::IsSumOperator(const SmToken &token) {
+ return bool(token.nGroup & TG::Sum);
+}
+
+bool SmNodeListParser::IsProductOperator(const SmToken &token) {
+ return token.nGroup & TG::Product &&
+ token.eType != TWIDESLASH &&
+ token.eType != TWIDEBACKSLASH &&
+ token.eType != TUNDERBRACE &&
+ token.eType != TOVERBRACE &&
+ token.eType != TOVER;
+}
+
+bool SmNodeListParser::IsUnaryOperator(const SmToken &token) {
+ return token.nGroup & TG::UnOper &&
+ (token.eType == TPLUS ||
+ token.eType == TMINUS ||
+ token.eType == TPLUSMINUS ||
+ token.eType == TMINUSPLUS ||
+ token.eType == TNEG ||
+ token.eType == TUOPER);
+}
+
+bool SmNodeListParser::IsPostfixOperator(const SmToken &token) {
+ return token.eType == TFACT;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/dialog.cxx b/starmath/source/dialog.cxx
new file mode 100644
index 000000000..19992b341
--- /dev/null
+++ b/starmath/source/dialog.cxx
@@ -0,0 +1,2055 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <comphelper/string.hxx>
+#include <o3tl/temporary.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/weld.hxx>
+#include <svtools/ctrltool.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/fontcharmap.hxx>
+#include <sfx2/dispatch.hxx>
+#include <svx/charmap.hxx>
+#include <svx/ucsubset.hxx>
+
+#include <dialog.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <helpids.h>
+#include <cfgitem.hxx>
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <view.hxx>
+
+#include <algorithm>
+
+namespace
+{
+
+void lclGetSettingColors(Color& rBackgroundColor, Color& rTextColor)
+{
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ if (rStyleSettings.GetHighContrastMode())
+ {
+ rBackgroundColor = rStyleSettings.GetFieldColor();
+ rTextColor = rStyleSettings.GetFieldTextColor();
+ }
+ else
+ {
+ rBackgroundColor = COL_WHITE;
+ rTextColor = COL_BLACK;
+ }
+}
+
+// Since it's better to set/query the FontStyle via its attributes rather
+// than via the StyleName we create a way to translate
+// Attribute <-> StyleName
+
+class SmFontStyles
+{
+ OUString aNormal;
+ OUString aBold;
+ OUString aItalic;
+ OUString aBoldItalic;
+
+public:
+ SmFontStyles();
+
+ static sal_uInt16 GetCount() { return 4; }
+ const OUString& GetStyleName(const vcl::Font& rFont) const;
+ const OUString& GetStyleName(sal_uInt16 nIdx) const;
+};
+
+} // end anonymous namespace
+
+SmFontStyles::SmFontStyles()
+ : aNormal(SmResId(RID_FONTREGULAR))
+ , aBold(SmResId(RID_FONTBOLD))
+ , aItalic(SmResId(RID_FONTITALIC))
+{
+ aBoldItalic = aBold;
+ aBoldItalic += ", ";
+ aBoldItalic += aItalic;
+}
+
+const OUString& SmFontStyles::GetStyleName(const vcl::Font& rFont) const
+{
+ //! compare also SmSpecialNode::Prepare
+ bool bBold = IsBold( rFont ),
+ bItalic = IsItalic( rFont );
+
+ if (bBold && bItalic)
+ return aBoldItalic;
+ else if (bItalic)
+ return aItalic;
+ else if (bBold)
+ return aBold;
+ return aNormal;
+}
+
+const OUString& SmFontStyles::GetStyleName( sal_uInt16 nIdx ) const
+{
+ // 0 = "normal", 1 = "italic",
+ // 2 = "bold", 3 = "bold italic"
+
+ assert( nIdx < GetCount() );
+ switch (nIdx)
+ {
+ case 0 : return aNormal;
+ case 1 : return aItalic;
+ case 2 : return aBold;
+ default: /*case 3:*/ return aBoldItalic;
+ }
+}
+
+static const SmFontStyles & GetFontStyles()
+{
+ static const SmFontStyles aImpl;
+ return aImpl;
+}
+
+void SetFontStyle(std::u16string_view rStyleName, vcl::Font &rFont)
+{
+ // Find index related to StyleName. For an empty StyleName it's assumed to be
+ // 0 (neither bold nor italic).
+ sal_uInt16 nIndex = 0;
+ if (!rStyleName.empty())
+ {
+ sal_uInt16 i;
+ const SmFontStyles &rStyles = GetFontStyles();
+ for (i = 0; i < SmFontStyles::GetCount(); ++i)
+ if (rStyleName == rStyles.GetStyleName(i))
+ break;
+ assert(i < SmFontStyles::GetCount() && "style-name unknown");
+ nIndex = i;
+ }
+
+ rFont.SetItalic((nIndex & 0x1) ? ITALIC_NORMAL : ITALIC_NONE);
+ rFont.SetWeight((nIndex & 0x2) ? WEIGHT_BOLD : WEIGHT_NORMAL);
+}
+
+IMPL_LINK_NOARG(SmPrintOptionsTabPage, SizeButtonClickHdl, weld::Toggleable&, void)
+{
+ m_xZoom->set_sensitive(m_xSizeZoomed->get_active());
+}
+
+SmPrintOptionsTabPage::SmPrintOptionsTabPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rOptions)
+ : SfxTabPage(pPage, pController, "modules/smath/ui/smathsettings.ui", "SmathSettings", &rOptions)
+ , m_xTitle(m_xBuilder->weld_check_button("title"))
+ , m_xText(m_xBuilder->weld_check_button("text"))
+ , m_xFrame(m_xBuilder->weld_check_button("frame"))
+ , m_xSizeNormal(m_xBuilder->weld_radio_button("sizenormal"))
+ , m_xSizeScaled(m_xBuilder->weld_radio_button("sizescaled"))
+ , m_xSizeZoomed(m_xBuilder->weld_radio_button("sizezoomed"))
+ , m_xZoom(m_xBuilder->weld_metric_spin_button("zoom", FieldUnit::PERCENT))
+ , m_xNoRightSpaces(m_xBuilder->weld_check_button("norightspaces"))
+ , m_xSaveOnlyUsedSymbols(m_xBuilder->weld_check_button("saveonlyusedsymbols"))
+ , m_xAutoCloseBrackets(m_xBuilder->weld_check_button("autoclosebrackets"))
+ , m_xSmZoom(m_xBuilder->weld_metric_spin_button("smzoom", FieldUnit::PERCENT))
+{
+ m_xSizeNormal->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+ m_xSizeScaled->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+ m_xSizeZoomed->connect_toggled(LINK(this, SmPrintOptionsTabPage, SizeButtonClickHdl));
+
+ Reset(&rOptions);
+}
+
+SmPrintOptionsTabPage::~SmPrintOptionsTabPage()
+{
+ if (SmViewShell *pViewSh = SmGetActiveView())
+ pViewSh->GetEditWindow()->UpdateStatus();
+}
+
+bool SmPrintOptionsTabPage::FillItemSet(SfxItemSet* rSet)
+{
+ sal_uInt16 nPrintSize;
+ if (m_xSizeNormal->get_active())
+ nPrintSize = PRINT_SIZE_NORMAL;
+ else if (m_xSizeScaled->get_active())
+ nPrintSize = PRINT_SIZE_SCALED;
+ else
+ nPrintSize = PRINT_SIZE_ZOOMED;
+
+ rSet->Put(SfxUInt16Item(SID_PRINTSIZE, nPrintSize));
+ rSet->Put(SfxUInt16Item(SID_PRINTZOOM, sal::static_int_cast<sal_uInt16>(m_xZoom->get_value(FieldUnit::PERCENT))));
+ rSet->Put(SfxBoolItem(SID_PRINTTITLE, m_xTitle->get_active()));
+ rSet->Put(SfxBoolItem(SID_PRINTTEXT, m_xText->get_active()));
+ rSet->Put(SfxBoolItem(SID_PRINTFRAME, m_xFrame->get_active()));
+ rSet->Put(SfxBoolItem(SID_NO_RIGHT_SPACES, m_xNoRightSpaces->get_active()));
+ rSet->Put(SfxBoolItem(SID_SAVE_ONLY_USED_SYMBOLS, m_xSaveOnlyUsedSymbols->get_active()));
+ rSet->Put(SfxBoolItem(SID_AUTO_CLOSE_BRACKETS, m_xAutoCloseBrackets->get_active()));
+ rSet->Put(SfxUInt16Item(SID_SMEDITWINDOWZOOM, sal::static_int_cast<sal_uInt16>(m_xSmZoom->get_value(FieldUnit::PERCENT))));
+
+ if (SmViewShell *pViewSh = SmGetActiveView())
+ pViewSh->GetEditWindow()->UpdateStatus();
+
+ return true;
+}
+
+void SmPrintOptionsTabPage::Reset(const SfxItemSet* rSet)
+{
+ SmPrintSize ePrintSize = static_cast<SmPrintSize>(rSet->Get(SID_PRINTSIZE).GetValue());
+
+ m_xSizeNormal->set_active(ePrintSize == PRINT_SIZE_NORMAL);
+ m_xSizeScaled->set_active(ePrintSize == PRINT_SIZE_SCALED);
+ m_xSizeZoomed->set_active(ePrintSize == PRINT_SIZE_ZOOMED);
+
+ m_xZoom->set_sensitive(m_xSizeZoomed->get_active());
+
+ m_xZoom->set_value(rSet->Get(SID_PRINTZOOM).GetValue(), FieldUnit::PERCENT);
+
+ m_xSmZoom->set_sensitive(true);
+ m_xSmZoom->set_value(rSet->Get(SID_SMEDITWINDOWZOOM).GetValue(), FieldUnit::PERCENT);
+
+ m_xTitle->set_active(rSet->Get(SID_PRINTTITLE).GetValue());
+ m_xNoRightSpaces->set_active(rSet->Get(SID_NO_RIGHT_SPACES).GetValue());
+ m_xSaveOnlyUsedSymbols->set_active(rSet->Get(SID_SAVE_ONLY_USED_SYMBOLS).GetValue());
+ m_xAutoCloseBrackets->set_active(rSet->Get(SID_AUTO_CLOSE_BRACKETS).GetValue());
+}
+
+std::unique_ptr<SfxTabPage> SmPrintOptionsTabPage::Create(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet)
+{
+ return std::make_unique<SmPrintOptionsTabPage>(pPage, pController, rSet);
+}
+
+void SmShowFont::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/)
+{
+ Color aBackColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackColor, aTextColor);
+
+ rRenderContext.SetBackground(Wallpaper(aBackColor));
+
+ vcl::Font aFont(maFont);
+ aFont.SetFontSize(Size(0, 24 * rRenderContext.GetDPIScaleFactor()));
+ aFont.SetAlignment(ALIGN_TOP);
+ rRenderContext.SetFont(aFont);
+ rRenderContext.SetTextColor(aTextColor);
+
+ OUString sText(rRenderContext.GetFont().GetFamilyName());
+ Size aTextSize(rRenderContext.GetTextWidth(sText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((rRenderContext.GetOutputSize().Width() - aTextSize.Width()) / 2,
+ (rRenderContext.GetOutputSize().Height() - aTextSize.Height()) / 2), sText);
+}
+
+void SmShowFont::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ CustomWidgetController::SetDrawingArea(pDrawingArea);
+ Size aSize(pDrawingArea->get_ref_device().LogicToPixel(Size(111 , 31), MapMode(MapUnit::MapAppFont)));
+ pDrawingArea->set_size_request(aSize.Width(), aSize.Height());
+}
+
+void SmShowFont::SetFont(const vcl::Font& rFont)
+{
+ maFont = rFont;
+ Invalidate();
+}
+
+IMPL_LINK( SmFontDialog, FontSelectHdl, weld::ComboBox&, rComboBox, void )
+{
+ maFont.SetFamilyName(rComboBox.get_active_text());
+ m_aShowFont.SetFont(maFont);
+}
+
+IMPL_LINK_NOARG(SmFontDialog, AttrChangeHdl, weld::Toggleable&, void)
+{
+ if (m_xBoldCheckBox->get_active())
+ maFont.SetWeight(WEIGHT_BOLD);
+ else
+ maFont.SetWeight(WEIGHT_NORMAL);
+
+ if (m_xItalicCheckBox->get_active())
+ maFont.SetItalic(ITALIC_NORMAL);
+ else
+ maFont.SetItalic(ITALIC_NONE);
+
+ m_aShowFont.SetFont(maFont);
+}
+
+void SmFontDialog::SetFont(const vcl::Font &rFont)
+{
+ maFont = rFont;
+
+ m_xFontBox->set_active_text(maFont.GetFamilyName());
+ m_xBoldCheckBox->set_active(IsBold(maFont));
+ m_xItalicCheckBox->set_active(IsItalic(maFont));
+ m_aShowFont.SetFont(maFont);
+}
+
+SmFontDialog::SmFontDialog(weld::Window * pParent, OutputDevice *pFntListDevice, bool bHideCheckboxes)
+ : GenericDialogController(pParent, "modules/smath/ui/fontdialog.ui", "FontDialog")
+ , m_xFontBox(m_xBuilder->weld_entry_tree_view("fontgrid", "font", "fonts"))
+ , m_xAttrFrame(m_xBuilder->weld_widget("attrframe"))
+ , m_xBoldCheckBox(m_xBuilder->weld_check_button("bold"))
+ , m_xItalicCheckBox(m_xBuilder->weld_check_button("italic"))
+ , m_xShowFont(new weld::CustomWeld(*m_xBuilder, "preview", m_aShowFont))
+{
+ m_xFontBox->set_height_request_by_rows(8);
+
+ {
+ weld::WaitObject aWait(pParent);
+
+ FontList aFontList( pFntListDevice );
+
+ sal_uInt16 nCount = aFontList.GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ {
+ m_xFontBox->append_text(aFontList.GetFontName(i).GetFamilyName());
+ }
+ maFont.SetFontSize(Size(0, 24));
+ maFont.SetWeight(WEIGHT_NORMAL);
+ maFont.SetItalic(ITALIC_NONE);
+ maFont.SetFamily(FAMILY_DONTKNOW);
+ maFont.SetPitch(PITCH_DONTKNOW);
+ maFont.SetCharSet(RTL_TEXTENCODING_DONTKNOW);
+ maFont.SetTransparent(true);
+ }
+
+ m_xFontBox->connect_changed(LINK(this, SmFontDialog, FontSelectHdl));
+ m_xBoldCheckBox->connect_toggled(LINK(this, SmFontDialog, AttrChangeHdl));
+ m_xItalicCheckBox->connect_toggled(LINK(this, SmFontDialog, AttrChangeHdl));
+
+ if (bHideCheckboxes)
+ {
+ m_xBoldCheckBox->set_active(false);
+ m_xBoldCheckBox->set_sensitive(false);
+ m_xItalicCheckBox->set_active(false);
+ m_xItalicCheckBox->set_sensitive(false);
+ m_xAttrFrame->hide();
+ }
+}
+
+SmFontDialog::~SmFontDialog()
+{
+}
+
+namespace {
+
+class SaveDefaultsQuery : public weld::MessageDialogController
+{
+public:
+ explicit SaveDefaultsQuery(weld::Widget* pParent)
+ : MessageDialogController(pParent, "modules/smath/ui/savedefaultsdialog.ui",
+ "SaveDefaultsDialog")
+ {
+ }
+};
+
+}
+
+IMPL_LINK_NOARG( SmFontSizeDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+SmFontSizeDialog::SmFontSizeDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/fontsizedialog.ui", "FontSizeDialog")
+ , m_xBaseSize(m_xBuilder->weld_metric_spin_button("spinB_baseSize", FieldUnit::POINT))
+ , m_xTextSize(m_xBuilder->weld_metric_spin_button("spinB_text", FieldUnit::PERCENT))
+ , m_xIndexSize(m_xBuilder->weld_metric_spin_button("spinB_index", FieldUnit::PERCENT))
+ , m_xFunctionSize(m_xBuilder->weld_metric_spin_button("spinB_function", FieldUnit::PERCENT))
+ , m_xOperatorSize(m_xBuilder->weld_metric_spin_button("spinB_operator", FieldUnit::PERCENT))
+ , m_xBorderSize(m_xBuilder->weld_metric_spin_button("spinB_limit", FieldUnit::PERCENT))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmFontSizeDialog, DefaultButtonClickHdl));
+}
+
+SmFontSizeDialog::~SmFontSizeDialog()
+{
+}
+
+void SmFontSizeDialog::ReadFrom(const SmFormat &rFormat)
+{
+ //! watch out: round properly!
+ m_xBaseSize->set_value( SmRoundFraction(
+ Sm100th_mmToPts( rFormat.GetBaseSize().Height() ) ), FieldUnit::NONE );
+
+ m_xTextSize->set_value( rFormat.GetRelSize(SIZ_TEXT), FieldUnit::NONE );
+ m_xIndexSize->set_value( rFormat.GetRelSize(SIZ_INDEX), FieldUnit::NONE );
+ m_xFunctionSize->set_value( rFormat.GetRelSize(SIZ_FUNCTION), FieldUnit::NONE );
+ m_xOperatorSize->set_value( rFormat.GetRelSize(SIZ_OPERATOR), FieldUnit::NONE );
+ m_xBorderSize->set_value( rFormat.GetRelSize(SIZ_LIMITS), FieldUnit::NONE );
+}
+
+void SmFontSizeDialog::WriteTo(SmFormat &rFormat) const
+{
+ rFormat.SetBaseSize( Size(0, SmPtsTo100th_mm( static_cast< tools::Long >(m_xBaseSize->get_value(FieldUnit::NONE)))) );
+
+ rFormat.SetRelSize(SIZ_TEXT, sal::static_int_cast<sal_uInt16>(m_xTextSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_INDEX, sal::static_int_cast<sal_uInt16>(m_xIndexSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_FUNCTION, sal::static_int_cast<sal_uInt16>(m_xFunctionSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_OPERATOR, sal::static_int_cast<sal_uInt16>(m_xOperatorSize->get_value(FieldUnit::NONE)));
+ rFormat.SetRelSize(SIZ_LIMITS, sal::static_int_cast<sal_uInt16>(m_xBorderSize->get_value(FieldUnit::NONE)));
+
+ const Size aTmp (rFormat.GetBaseSize());
+ for (sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++)
+ rFormat.SetFontSize(i, aTmp);
+
+ rFormat.RequestApplyChanges();
+}
+
+IMPL_LINK(SmFontTypeDialog, MenuSelectHdl, const OString&, rIdent, void)
+{
+ SmFontPickListBox *pActiveListBox;
+
+ bool bHideCheckboxes = false;
+ if (rIdent == "variables")
+ pActiveListBox = m_xVariableFont.get();
+ else if (rIdent == "functions")
+ pActiveListBox = m_xFunctionFont.get();
+ else if (rIdent == "numbers")
+ pActiveListBox = m_xNumberFont.get();
+ else if (rIdent == "text")
+ pActiveListBox = m_xTextFont.get();
+ else if (rIdent == "serif")
+ {
+ pActiveListBox = m_xSerifFont.get();
+ bHideCheckboxes = true;
+ }
+ else if (rIdent == "sansserif")
+ {
+ pActiveListBox = m_xSansFont.get();
+ bHideCheckboxes = true;
+ }
+ else if (rIdent == "fixedwidth")
+ {
+ pActiveListBox = m_xFixedFont.get();
+ bHideCheckboxes = true;
+ }
+ else
+ pActiveListBox = nullptr;
+
+ if (pActiveListBox)
+ {
+ SmFontDialog aFontDialog(m_xDialog.get(), pFontListDev, bHideCheckboxes);
+
+ pActiveListBox->WriteTo(aFontDialog);
+ if (aFontDialog.run() == RET_OK)
+ pActiveListBox->ReadFrom(aFontDialog);
+ }
+}
+
+IMPL_LINK_NOARG(SmFontTypeDialog, DefaultButtonClickHdl, weld::Button&, void)
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt, true );
+ }
+}
+
+SmFontTypeDialog::SmFontTypeDialog(weld::Window* pParent, OutputDevice *pFntListDevice)
+ : GenericDialogController(pParent, "modules/smath/ui/fonttypedialog.ui", "FontsDialog")
+ , pFontListDev(pFntListDevice)
+ , m_xVariableFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("variableCB")))
+ , m_xFunctionFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("functionCB")))
+ , m_xNumberFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("numberCB")))
+ , m_xTextFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("textCB")))
+ , m_xSerifFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("serifCB")))
+ , m_xSansFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("sansCB")))
+ , m_xFixedFont(new SmFontPickListBox(m_xBuilder->weld_combo_box("fixedCB")))
+ , m_xMenuButton(m_xBuilder->weld_menu_button("modify"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmFontTypeDialog, DefaultButtonClickHdl));
+ m_xMenuButton->connect_selected(LINK(this, SmFontTypeDialog, MenuSelectHdl));
+}
+
+SmFontTypeDialog::~SmFontTypeDialog()
+{
+}
+
+void SmFontTypeDialog::ReadFrom(const SmFormat &rFormat)
+{
+ SmModule *pp = SM_MOD();
+
+ *m_xVariableFont = pp->GetConfig()->GetFontPickList(FNT_VARIABLE);
+ *m_xFunctionFont = pp->GetConfig()->GetFontPickList(FNT_FUNCTION);
+ *m_xNumberFont = pp->GetConfig()->GetFontPickList(FNT_NUMBER);
+ *m_xTextFont = pp->GetConfig()->GetFontPickList(FNT_TEXT);
+ *m_xSerifFont = pp->GetConfig()->GetFontPickList(FNT_SERIF);
+ *m_xSansFont = pp->GetConfig()->GetFontPickList(FNT_SANS);
+ *m_xFixedFont = pp->GetConfig()->GetFontPickList(FNT_FIXED);
+
+ m_xVariableFont->Insert( rFormat.GetFont(FNT_VARIABLE) );
+ m_xFunctionFont->Insert( rFormat.GetFont(FNT_FUNCTION) );
+ m_xNumberFont->Insert( rFormat.GetFont(FNT_NUMBER) );
+ m_xTextFont->Insert( rFormat.GetFont(FNT_TEXT) );
+ m_xSerifFont->Insert( rFormat.GetFont(FNT_SERIF) );
+ m_xSansFont->Insert( rFormat.GetFont(FNT_SANS) );
+ m_xFixedFont->Insert( rFormat.GetFont(FNT_FIXED) );
+}
+
+
+void SmFontTypeDialog::WriteTo(SmFormat &rFormat) const
+{
+ SmModule *pp = SM_MOD();
+
+ pp->GetConfig()->GetFontPickList(FNT_VARIABLE) = *m_xVariableFont;
+ pp->GetConfig()->GetFontPickList(FNT_FUNCTION) = *m_xFunctionFont;
+ pp->GetConfig()->GetFontPickList(FNT_NUMBER) = *m_xNumberFont;
+ pp->GetConfig()->GetFontPickList(FNT_TEXT) = *m_xTextFont;
+ pp->GetConfig()->GetFontPickList(FNT_SERIF) = *m_xSerifFont;
+ pp->GetConfig()->GetFontPickList(FNT_SANS) = *m_xSansFont;
+ pp->GetConfig()->GetFontPickList(FNT_FIXED) = *m_xFixedFont;
+
+ rFormat.SetFont( FNT_VARIABLE, m_xVariableFont->Get() );
+ rFormat.SetFont( FNT_FUNCTION, m_xFunctionFont->Get() );
+ rFormat.SetFont( FNT_NUMBER, m_xNumberFont->Get() );
+ rFormat.SetFont( FNT_TEXT, m_xTextFont->Get() );
+ rFormat.SetFont( FNT_SERIF, m_xSerifFont->Get() );
+ rFormat.SetFont( FNT_SANS, m_xSansFont->Get() );
+ rFormat.SetFont( FNT_FIXED, m_xFixedFont->Get() );
+
+ rFormat.RequestApplyChanges();
+}
+
+/**************************************************************************/
+
+namespace {
+
+struct FieldMinMax
+{
+ sal_uInt16 nMin, nMax;
+};
+
+}
+
+// Data for min and max values of the 4 metric fields
+// for each of the 10 categories
+const FieldMinMax pMinMaxData[10][4] =
+{
+ // 0
+ {{ 0, 200 }, { 0, 200 }, { 0, 100 }, { 0, 0 }},
+ // 1
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 2
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 3
+ {{ 0, 100 }, { 1, 100 }, { 0, 0 }, { 0, 0 }},
+ // 4
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 5
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 100 }},
+ // 6
+ {{ 0, 300 }, { 0, 300 }, { 0, 0 }, { 0, 0 }},
+ // 7
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 8
+ {{ 0, 100 }, { 0, 100 }, { 0, 0 }, { 0, 0 }},
+ // 9
+ {{ 0, 10000 }, { 0, 10000 }, { 0, 10000 }, { 0, 10000 }}
+};
+
+SmCategoryDesc::SmCategoryDesc(weld::Builder& rBuilder, sal_uInt16 nCategoryIdx)
+{
+ ++nCategoryIdx;
+ std::unique_ptr<weld::Label> xTitle(rBuilder.weld_label(OString::number(nCategoryIdx)+"title"));
+ if (xTitle)
+ {
+ Name = xTitle->get_label();
+ }
+ for (int i = 0; i < 4; ++i)
+ {
+ std::unique_ptr<weld::Label> xLabel(rBuilder.weld_label(OString::number(nCategoryIdx)+"label"+OString::number(i+1)));
+
+ if (xLabel)
+ {
+ Strings[i] = xLabel->get_label();
+ Graphics[i] = rBuilder.weld_widget(OString::number(nCategoryIdx)+"image"+OString::number(i+1));
+ }
+ else
+ {
+ Strings[i].clear();
+ Graphics[i].reset();
+ }
+
+ const FieldMinMax& rMinMax = pMinMaxData[ nCategoryIdx-1 ][i];
+ Value[i] = Minimum[i] = rMinMax.nMin;
+ Maximum[i] = rMinMax.nMax;
+ }
+}
+
+SmCategoryDesc::~SmCategoryDesc()
+{
+}
+
+/**************************************************************************/
+
+IMPL_LINK( SmDistanceDialog, GetFocusHdl, weld::Widget&, rControl, void )
+{
+ if (!m_xCategories[nActiveCategory])
+ return;
+
+ sal_uInt16 i;
+
+ if (&rControl == &m_xMetricField1->get_widget())
+ i = 0;
+ else if (&rControl == &m_xMetricField2->get_widget())
+ i = 1;
+ else if (&rControl == &m_xMetricField3->get_widget())
+ i = 2;
+ else if (&rControl == &m_xMetricField4->get_widget())
+ i = 3;
+ else
+ return;
+ if (m_pCurrentImage)
+ m_pCurrentImage->hide();
+ m_pCurrentImage = m_xCategories[nActiveCategory]->GetGraphic(i);
+ m_pCurrentImage->show();
+}
+
+IMPL_LINK(SmDistanceDialog, MenuSelectHdl, const OString&, rId, void)
+{
+ assert(rId.startsWith("menuitem"));
+ SetCategory(rId.replaceFirst("menuitem", "").toInt32() - 1);
+}
+
+IMPL_LINK_NOARG( SmDistanceDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+IMPL_LINK( SmDistanceDialog, CheckBoxClickHdl, weld::Toggleable&, rCheckBox, void )
+{
+ if (&rCheckBox == m_xCheckBox1.get())
+ {
+ bool bChecked = m_xCheckBox1->get_active();
+ m_xFixedText4->set_sensitive( bChecked );
+ m_xMetricField4->set_sensitive( bChecked );
+ }
+}
+
+void SmDistanceDialog::SetCategory(sal_uInt16 nCategory)
+{
+ assert(nCategory < NOCATEGORIES && "Sm: wrong category number in SmDistanceDialog");
+
+ // array to convert category- and metricfield-number in help ids.
+ // 0 is used in case of unused combinations.
+ assert(NOCATEGORIES == 10 && "Sm : array doesn't fit into the number of categories");
+ static const char * aCatMf2Hid[10][4] =
+ {
+ { HID_SMA_DEFAULT_DIST, HID_SMA_LINE_DIST, HID_SMA_ROOT_DIST, nullptr },
+ { HID_SMA_SUP_DIST, HID_SMA_SUB_DIST , nullptr, nullptr },
+ { HID_SMA_NUMERATOR_DIST, HID_SMA_DENOMINATOR_DIST, nullptr, nullptr },
+ { HID_SMA_FRACLINE_EXCWIDTH, HID_SMA_FRACLINE_LINEWIDTH, nullptr, nullptr },
+ { HID_SMA_UPPERLIMIT_DIST, HID_SMA_LOWERLIMIT_DIST, nullptr, nullptr },
+ { HID_SMA_BRACKET_EXCHEIGHT, HID_SMA_BRACKET_DIST, nullptr, HID_SMA_BRACKET_EXCHEIGHT2 },
+ { HID_SMA_MATRIXROW_DIST, HID_SMA_MATRIXCOL_DIST, nullptr, nullptr },
+ { HID_SMA_ATTRIBUT_DIST, HID_SMA_INTERATTRIBUT_DIST, nullptr, nullptr },
+ { HID_SMA_OPERATOR_EXCHEIGHT, HID_SMA_OPERATOR_DIST, nullptr, nullptr },
+ { HID_SMA_LEFTBORDER_DIST, HID_SMA_RIGHTBORDER_DIST, HID_SMA_UPPERBORDER_DIST, HID_SMA_LOWERBORDER_DIST }
+ };
+
+ // array to help iterate over the controls
+ std::pair<weld::Label*, weld::MetricSpinButton*> const aWin[4] =
+ {
+ { m_xFixedText1.get(), m_xMetricField1.get() },
+ { m_xFixedText2.get(), m_xMetricField2.get() },
+ { m_xFixedText3.get(), m_xMetricField3.get() },
+ { m_xFixedText4.get(), m_xMetricField4.get() }
+ };
+
+ SmCategoryDesc *pCat;
+
+ // remember the (maybe new) settings of the active SmCategoryDesc
+ // before switching to the new one
+ if (nActiveCategory != CATEGORY_NONE)
+ {
+ pCat = m_xCategories[nActiveCategory].get();
+ pCat->SetValue(0, sal::static_int_cast<sal_uInt16>(m_xMetricField1->get_value(FieldUnit::NONE)));
+ pCat->SetValue(1, sal::static_int_cast<sal_uInt16>(m_xMetricField2->get_value(FieldUnit::NONE)));
+ pCat->SetValue(2, sal::static_int_cast<sal_uInt16>(m_xMetricField3->get_value(FieldUnit::NONE)));
+ pCat->SetValue(3, sal::static_int_cast<sal_uInt16>(m_xMetricField4->get_value(FieldUnit::NONE)));
+
+ if (nActiveCategory == 5)
+ bScaleAllBrackets = m_xCheckBox1->get_active();
+
+ m_xMenuButton->set_item_active("menuitem" + OString::number(nActiveCategory + 1), false);
+ }
+
+ // activation/deactivation of the associated controls depending on the chosen category
+ bool bActive;
+ for (sal_uInt16 i = 0; i < 4; i++)
+ {
+ weld::Label *pFT = aWin[i].first;
+ weld::MetricSpinButton *pMF = aWin[i].second;
+
+ // To determine which Controls should be active, the existence
+ // of an associated HelpID is checked
+ bActive = aCatMf2Hid[nCategory][i] != nullptr;
+
+ pFT->set_visible(bActive);
+ pFT->set_sensitive(bActive);
+ pMF->set_visible(bActive);
+ pMF->set_sensitive(bActive);
+
+ // set measurement unit and number of decimal places
+ FieldUnit eUnit;
+ sal_uInt16 nDigits;
+ if (nCategory < 9)
+ {
+ eUnit = FieldUnit::PERCENT;
+ nDigits = 0;
+ }
+ else
+ {
+ eUnit = FieldUnit::MM_100TH;
+ nDigits = 2;
+ }
+ pMF->set_unit(eUnit); // changes the value
+ pMF->set_digits(nDigits);
+
+ if (bActive)
+ {
+ pCat = m_xCategories[nCategory].get();
+ pFT->set_label(pCat->GetString(i));
+
+ pMF->set_range(pCat->GetMinimum(i), pCat->GetMaximum(i), FieldUnit::NONE);
+ pMF->set_value(pCat->GetValue(i), FieldUnit::NONE);
+
+ pMF->set_help_id(aCatMf2Hid[nCategory][i]);
+ }
+ }
+ // activate the CheckBox and the associated MetricField if we're dealing with the brackets menu
+ bActive = nCategory == 5;
+ m_xCheckBox1->set_visible(bActive);
+ m_xCheckBox1->set_sensitive(bActive);
+ if (bActive)
+ {
+ m_xCheckBox1->set_active(bScaleAllBrackets);
+
+ bool bChecked = m_xCheckBox1->get_active();
+ m_xFixedText4->set_sensitive( bChecked );
+ m_xMetricField4->set_sensitive( bChecked );
+ }
+
+ m_xMenuButton->set_item_active("menuitem" + OString::number(nCategory + 1), true);
+ m_xFrame->set_label(m_xCategories[nCategory]->GetName());
+
+ nActiveCategory = nCategory;
+
+ m_xMetricField1->grab_focus();
+}
+
+SmDistanceDialog::SmDistanceDialog(weld::Window *pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/spacingdialog.ui", "SpacingDialog")
+ , m_xFrame(m_xBuilder->weld_frame("template"))
+ , m_xFixedText1(m_xBuilder->weld_label("label1"))
+ , m_xMetricField1(m_xBuilder->weld_metric_spin_button("spinbutton1", FieldUnit::CM))
+ , m_xFixedText2(m_xBuilder->weld_label("label2"))
+ , m_xMetricField2(m_xBuilder->weld_metric_spin_button("spinbutton2", FieldUnit::CM))
+ , m_xFixedText3(m_xBuilder->weld_label("label3"))
+ , m_xMetricField3(m_xBuilder->weld_metric_spin_button("spinbutton3", FieldUnit::CM))
+ , m_xCheckBox1(m_xBuilder->weld_check_button("checkbutton"))
+ , m_xFixedText4(m_xBuilder->weld_label("label4"))
+ , m_xMetricField4(m_xBuilder->weld_metric_spin_button("spinbutton4", FieldUnit::CM))
+ , m_xMenuButton(m_xBuilder->weld_menu_button("category"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+ , m_xBitmap(m_xBuilder->weld_widget("image"))
+ , m_pCurrentImage(m_xBitmap.get())
+{
+ for (sal_uInt16 i = 0; i < NOCATEGORIES; ++i)
+ m_xCategories[i].reset( new SmCategoryDesc(*m_xBuilder, i) );
+ nActiveCategory = CATEGORY_NONE;
+ bScaleAllBrackets = false;
+
+ m_xMetricField1->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField2->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField3->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xMetricField4->connect_focus_in(LINK(this, SmDistanceDialog, GetFocusHdl));
+ m_xCheckBox1->connect_toggled(LINK(this, SmDistanceDialog, CheckBoxClickHdl));
+ m_xMenuButton->connect_selected(LINK(this, SmDistanceDialog, MenuSelectHdl));
+ m_xDefaultButton->connect_clicked(LINK(this, SmDistanceDialog, DefaultButtonClickHdl));
+
+ //set the initial size, with max visible widgets visible, as preferred size
+ m_xDialog->set_size_request(-1, m_xDialog->get_preferred_size().Height());
+}
+
+SmDistanceDialog::~SmDistanceDialog()
+{
+}
+
+void SmDistanceDialog::ReadFrom(const SmFormat &rFormat)
+{
+ m_xCategories[0]->SetValue(0, rFormat.GetDistance(DIS_HORIZONTAL));
+ m_xCategories[0]->SetValue(1, rFormat.GetDistance(DIS_VERTICAL));
+ m_xCategories[0]->SetValue(2, rFormat.GetDistance(DIS_ROOT));
+ m_xCategories[1]->SetValue(0, rFormat.GetDistance(DIS_SUPERSCRIPT));
+ m_xCategories[1]->SetValue(1, rFormat.GetDistance(DIS_SUBSCRIPT));
+ m_xCategories[2]->SetValue(0, rFormat.GetDistance(DIS_NUMERATOR));
+ m_xCategories[2]->SetValue(1, rFormat.GetDistance(DIS_DENOMINATOR));
+ m_xCategories[3]->SetValue(0, rFormat.GetDistance(DIS_FRACTION));
+ m_xCategories[3]->SetValue(1, rFormat.GetDistance(DIS_STROKEWIDTH));
+ m_xCategories[4]->SetValue(0, rFormat.GetDistance(DIS_UPPERLIMIT));
+ m_xCategories[4]->SetValue(1, rFormat.GetDistance(DIS_LOWERLIMIT));
+ m_xCategories[5]->SetValue(0, rFormat.GetDistance(DIS_BRACKETSIZE));
+ m_xCategories[5]->SetValue(1, rFormat.GetDistance(DIS_BRACKETSPACE));
+ m_xCategories[5]->SetValue(3, rFormat.GetDistance(DIS_NORMALBRACKETSIZE));
+ m_xCategories[6]->SetValue(0, rFormat.GetDistance(DIS_MATRIXROW));
+ m_xCategories[6]->SetValue(1, rFormat.GetDistance(DIS_MATRIXCOL));
+ m_xCategories[7]->SetValue(0, rFormat.GetDistance(DIS_ORNAMENTSIZE));
+ m_xCategories[7]->SetValue(1, rFormat.GetDistance(DIS_ORNAMENTSPACE));
+ m_xCategories[8]->SetValue(0, rFormat.GetDistance(DIS_OPERATORSIZE));
+ m_xCategories[8]->SetValue(1, rFormat.GetDistance(DIS_OPERATORSPACE));
+ m_xCategories[9]->SetValue(0, rFormat.GetDistance(DIS_LEFTSPACE));
+ m_xCategories[9]->SetValue(1, rFormat.GetDistance(DIS_RIGHTSPACE));
+ m_xCategories[9]->SetValue(2, rFormat.GetDistance(DIS_TOPSPACE));
+ m_xCategories[9]->SetValue(3, rFormat.GetDistance(DIS_BOTTOMSPACE));
+
+ bScaleAllBrackets = rFormat.IsScaleNormalBrackets();
+
+ // force update (even of category 0) by setting nActiveCategory to a
+ // non-existent category number
+ nActiveCategory = CATEGORY_NONE;
+ SetCategory(0);
+}
+
+
+void SmDistanceDialog::WriteTo(SmFormat &rFormat) /*const*/
+{
+ // TODO can they actually be different?
+ // if that's not the case 'const' could be used above!
+ SetCategory(nActiveCategory);
+
+ rFormat.SetDistance( DIS_HORIZONTAL, m_xCategories[0]->GetValue(0) );
+ rFormat.SetDistance( DIS_VERTICAL, m_xCategories[0]->GetValue(1) );
+ rFormat.SetDistance( DIS_ROOT, m_xCategories[0]->GetValue(2) );
+ rFormat.SetDistance( DIS_SUPERSCRIPT, m_xCategories[1]->GetValue(0) );
+ rFormat.SetDistance( DIS_SUBSCRIPT, m_xCategories[1]->GetValue(1) );
+ rFormat.SetDistance( DIS_NUMERATOR, m_xCategories[2]->GetValue(0) );
+ rFormat.SetDistance( DIS_DENOMINATOR, m_xCategories[2]->GetValue(1) );
+ rFormat.SetDistance( DIS_FRACTION, m_xCategories[3]->GetValue(0) );
+ rFormat.SetDistance( DIS_STROKEWIDTH, m_xCategories[3]->GetValue(1) );
+ rFormat.SetDistance( DIS_UPPERLIMIT, m_xCategories[4]->GetValue(0) );
+ rFormat.SetDistance( DIS_LOWERLIMIT, m_xCategories[4]->GetValue(1) );
+ rFormat.SetDistance( DIS_BRACKETSIZE, m_xCategories[5]->GetValue(0) );
+ rFormat.SetDistance( DIS_BRACKETSPACE, m_xCategories[5]->GetValue(1) );
+ rFormat.SetDistance( DIS_MATRIXROW, m_xCategories[6]->GetValue(0) );
+ rFormat.SetDistance( DIS_MATRIXCOL, m_xCategories[6]->GetValue(1) );
+ rFormat.SetDistance( DIS_ORNAMENTSIZE, m_xCategories[7]->GetValue(0) );
+ rFormat.SetDistance( DIS_ORNAMENTSPACE, m_xCategories[7]->GetValue(1) );
+ rFormat.SetDistance( DIS_OPERATORSIZE, m_xCategories[8]->GetValue(0) );
+ rFormat.SetDistance( DIS_OPERATORSPACE, m_xCategories[8]->GetValue(1) );
+ rFormat.SetDistance( DIS_LEFTSPACE, m_xCategories[9]->GetValue(0) );
+ rFormat.SetDistance( DIS_RIGHTSPACE, m_xCategories[9]->GetValue(1) );
+ rFormat.SetDistance( DIS_TOPSPACE, m_xCategories[9]->GetValue(2) );
+ rFormat.SetDistance( DIS_BOTTOMSPACE, m_xCategories[9]->GetValue(3) );
+ rFormat.SetDistance( DIS_NORMALBRACKETSIZE, m_xCategories[5]->GetValue(3) );
+
+ rFormat.SetScaleNormalBrackets( bScaleAllBrackets );
+
+ rFormat.RequestApplyChanges();
+}
+
+IMPL_LINK_NOARG( SmAlignDialog, DefaultButtonClickHdl, weld::Button&, void )
+{
+ SaveDefaultsQuery aQuery(m_xDialog.get());
+ if (aQuery.run() == RET_YES)
+ {
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+ }
+}
+
+SmAlignDialog::SmAlignDialog(weld::Window* pParent)
+ : GenericDialogController(pParent, "modules/smath/ui/alignmentdialog.ui", "AlignmentDialog")
+ , m_xLeft(m_xBuilder->weld_radio_button("left"))
+ , m_xCenter(m_xBuilder->weld_radio_button("center"))
+ , m_xRight(m_xBuilder->weld_radio_button("right"))
+ , m_xDefaultButton(m_xBuilder->weld_button("default"))
+{
+ m_xDefaultButton->connect_clicked(LINK(this, SmAlignDialog, DefaultButtonClickHdl));
+}
+
+SmAlignDialog::~SmAlignDialog()
+{
+}
+
+void SmAlignDialog::ReadFrom(const SmFormat &rFormat)
+{
+ switch (rFormat.GetHorAlign())
+ {
+ case SmHorAlign::Left:
+ m_xLeft->set_active(true);
+ break;
+ case SmHorAlign::Center:
+ m_xCenter->set_active(true);
+ break;
+ case SmHorAlign::Right:
+ m_xRight->set_active(true);
+ break;
+ }
+}
+
+void SmAlignDialog::WriteTo(SmFormat &rFormat) const
+{
+ if (m_xLeft->get_active())
+ rFormat.SetHorAlign(SmHorAlign::Left);
+ else if (m_xRight->get_active())
+ rFormat.SetHorAlign(SmHorAlign::Right);
+ else
+ rFormat.SetHorAlign(SmHorAlign::Center);
+
+ rFormat.RequestApplyChanges();
+}
+
+SmShowSymbolSet::SmShowSymbolSet(std::unique_ptr<weld::ScrolledWindow> pScrolledWindow)
+ : nLen(0)
+ , nRows(0)
+ , nColumns(0)
+ , nXOffset(0)
+ , nYOffset(0)
+ , nSelectSymbol(SYMBOL_NONE)
+ , m_xScrolledWindow(std::move(pScrolledWindow))
+{
+ m_xScrolledWindow->connect_vadjustment_changed(LINK(this, SmShowSymbolSet, ScrollHdl));
+}
+
+Point SmShowSymbolSet::OffsetPoint(const Point &rPoint) const
+{
+ return Point(rPoint.X() + nXOffset, rPoint.Y() + nYOffset);
+}
+
+void SmShowSymbolSet::Resize()
+{
+ CustomWidgetController::Resize();
+ Size aWinSize(GetOutputSizePixel());
+ if (aWinSize != m_aOldSize)
+ {
+ calccols(GetDrawingArea()->get_ref_device());
+ m_aOldSize = aWinSize;
+ }
+}
+
+void SmShowSymbolSet::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aBackgroundColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackgroundColor, aTextColor);
+
+ rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
+ rRenderContext.SetTextColor(aTextColor);
+
+ rRenderContext.Push(vcl::PushFlags::MAPMODE);
+
+ // set MapUnit for which 'nLen' has been calculated
+ rRenderContext.SetMapMode(MapMode(MapUnit::MapPixel));
+
+ sal_uInt16 v = sal::static_int_cast< sal_uInt16 >(m_xScrolledWindow->vadjustment_get_value() * nColumns);
+ size_t nSymbols = aSymbolSet.size();
+
+ Color aTxtColor(rRenderContext.GetTextColor());
+ for (size_t i = v; i < nSymbols ; i++)
+ {
+ SmSym aSymbol(*aSymbolSet[i]);
+ vcl::Font aFont(aSymbol.GetFace());
+ aFont.SetAlignment(ALIGN_TOP);
+
+ // taking a FontSize which is a bit smaller (compared to nLen) in order to have a buffer
+ // (hopefully enough for left and right, too)
+ aFont.SetFontSize(Size(0, nLen - (nLen / 3)));
+ rRenderContext.SetFont(aFont);
+ // keep text color
+ rRenderContext.SetTextColor(aTxtColor);
+
+ int nIV = i - v;
+ sal_UCS4 cChar = aSymbol.GetCharacter();
+ OUString aText(&cChar, 1);
+ Size aSize(rRenderContext.GetTextWidth( aText ), rRenderContext.GetTextHeight());
+
+ Point aPoint((nIV % nColumns) * nLen + (nLen - aSize.Width()) / 2,
+ (nIV / nColumns) * nLen + (nLen - aSize.Height()) / 2);
+
+ rRenderContext.DrawText(OffsetPoint(aPoint), aText);
+ }
+
+ if (nSelectSymbol != SYMBOL_NONE)
+ {
+ Point aPoint(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen);
+
+ rRenderContext.Invert(tools::Rectangle(OffsetPoint(aPoint), Size(nLen, nLen)));
+
+ }
+
+ rRenderContext.Pop();
+}
+
+bool SmShowSymbolSet::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ GrabFocus();
+
+ Size aOutputSize(nColumns * nLen, nRows * nLen);
+ aOutputSize.AdjustWidth(nXOffset );
+ aOutputSize.AdjustHeight(nYOffset );
+ Point aPoint(rMEvt.GetPosPixel());
+ aPoint.AdjustX( -nXOffset );
+ aPoint.AdjustY( -nYOffset );
+
+ if (rMEvt.IsLeft() && tools::Rectangle(Point(0, 0), aOutputSize).Contains(rMEvt.GetPosPixel()))
+ {
+ tools::Long nPos = (aPoint.Y() / nLen) * nColumns + (aPoint.X() / nLen) +
+ m_xScrolledWindow->vadjustment_get_value() * nColumns;
+ SelectSymbol( sal::static_int_cast< sal_uInt16 >(nPos) );
+
+ aSelectHdlLink.Call(*this);
+
+ if (rMEvt.GetClicks() > 1)
+ aDblClickHdlLink.Call(*this);
+ }
+
+ return true;
+}
+
+bool SmShowSymbolSet::KeyInput(const KeyEvent& rKEvt)
+{
+ sal_uInt16 n = nSelectSymbol;
+
+ if (n != SYMBOL_NONE)
+ {
+ switch (rKEvt.GetKeyCode().GetCode())
+ {
+ case KEY_DOWN: n = n + nColumns; break;
+ case KEY_UP: n = n - nColumns; break;
+ case KEY_LEFT: n -= 1; break;
+ case KEY_RIGHT: n += 1; break;
+ case KEY_HOME: n = 0; break;
+ case KEY_END: n = static_cast< sal_uInt16 >(aSymbolSet.size() - 1); break;
+ case KEY_PAGEUP: n -= nColumns * nRows; break;
+ case KEY_PAGEDOWN: n += nColumns * nRows; break;
+ default:
+ return false;
+ }
+ }
+ else
+ n = 0;
+
+ if (n >= aSymbolSet.size())
+ n = nSelectSymbol;
+
+ // adjust scrollbar
+ if ((n < sal::static_int_cast<sal_uInt16>(m_xScrolledWindow->vadjustment_get_value() * nColumns)) ||
+ (n >= sal::static_int_cast<sal_uInt16>((m_xScrolledWindow->vadjustment_get_value() + nRows) * nColumns)))
+ {
+ m_xScrolledWindow->vadjustment_set_value(n / nColumns);
+ Invalidate();
+ }
+
+ SelectSymbol(n);
+ aSelectHdlLink.Call(*this);
+
+ return true;
+}
+
+void SmShowSymbolSet::calccols(const vcl::RenderContext& rRenderContext)
+{
+ // Height of 16pt in pixels (matching 'aOutputSize')
+ nLen = rRenderContext.LogicToPixel(Size(0, 16), MapMode(MapUnit::MapPoint)).Height();
+
+ Size aOutputSize(GetOutputSizePixel());
+
+ nColumns = aOutputSize.Width() / nLen;
+ nRows = aOutputSize.Height() / nLen;
+ nColumns = std::max<tools::Long>(1, nColumns);
+ nRows = std::max<tools::Long>(1, nRows);
+
+ nXOffset = (aOutputSize.Width() - (nColumns * nLen)) / 2;
+ nYOffset = (aOutputSize.Height() - (nRows * nLen)) / 2;
+
+ SetScrollBarRange();
+}
+
+void SmShowSymbolSet::SetSymbolSet(const SymbolPtrVec_t & rSymbolSet)
+{
+ aSymbolSet = rSymbolSet;
+ SetScrollBarRange();
+ Invalidate();
+}
+
+void SmShowSymbolSet::SetScrollBarRange()
+{
+ const int nLastRow = (aSymbolSet.size() - 1 + nColumns) / nColumns;
+ m_xScrolledWindow->vadjustment_configure(m_xScrolledWindow->vadjustment_get_value(), 0, nLastRow, 1, nRows - 1, nRows);
+ Invalidate();
+}
+
+void SmShowSymbolSet::SelectSymbol(sal_uInt16 nSymbol)
+{
+ int v = m_xScrolledWindow->vadjustment_get_value() * nColumns;
+
+ if (nSelectSymbol != SYMBOL_NONE && nColumns)
+ {
+ Point aPoint(OffsetPoint(Point(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen)));
+ Invalidate(tools::Rectangle(aPoint, Size(nLen, nLen)));
+ }
+
+ if (nSymbol < aSymbolSet.size())
+ nSelectSymbol = nSymbol;
+
+ if (aSymbolSet.empty())
+ nSelectSymbol = SYMBOL_NONE;
+
+ if (nSelectSymbol != SYMBOL_NONE && nColumns)
+ {
+ Point aPoint(OffsetPoint(Point(((nSelectSymbol - v) % nColumns) * nLen,
+ ((nSelectSymbol - v) / nColumns) * nLen)));
+ Invalidate(tools::Rectangle(aPoint, Size(nLen, nLen)));
+ }
+
+ if (!nColumns)
+ Invalidate();
+}
+
+IMPL_LINK_NOARG(SmShowSymbolSet, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ Invalidate();
+}
+
+SmShowSymbol::SmShowSymbol()
+{
+}
+
+void SmShowSymbol::setFontSize(vcl::Font &rFont) const
+{
+ Size aSize(GetOutputSizePixel());
+ rFont.SetFontSize(Size(0, aSize.Height() - aSize.Height() / 3));
+}
+
+void SmShowSymbol::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aBackgroundColor;
+ Color aTextColor;
+ lclGetSettingColors(aBackgroundColor, aTextColor);
+ rRenderContext.SetBackground(Wallpaper(aBackgroundColor));
+ rRenderContext.SetTextColor(aTextColor);
+ rRenderContext.Erase();
+
+ vcl::Font aFont(GetFont());
+ setFontSize(aFont);
+ rRenderContext.SetFont(aFont);
+
+ const OUString &rText = GetText();
+ Size aTextSize(rRenderContext.GetTextWidth(rText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((rRenderContext.GetOutputSize().Width() - aTextSize.Width()) / 2,
+ (rRenderContext.GetOutputSize().Height() * 7 / 10)), rText);
+}
+
+bool SmShowSymbol::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ if (rMEvt.GetClicks() > 1)
+ aDblClickHdlLink.Call(*this);
+ return true;
+}
+
+void SmShowSymbol::SetSymbol(const SmSym *pSymbol)
+{
+ if (pSymbol)
+ {
+ vcl::Font aFont(pSymbol->GetFace());
+ aFont.SetAlignment(ALIGN_BASELINE);
+ SetFont(aFont);
+
+ sal_UCS4 cChar = pSymbol->GetCharacter();
+ OUString aText(&cChar, 1);
+ SetText( aText );
+ }
+
+ Invalidate();
+}
+
+void SmSymbolDialog::FillSymbolSets()
+ // populate the entries of possible SymbolsSets in the dialog with
+ // current values of the SymbolSet manager but selects none of those
+{
+ m_xSymbolSets->clear();
+ m_xSymbolSets->set_active(-1);
+
+ std::set< OUString > aSymbolSetNames( rSymbolMgr.GetSymbolSetNames() );
+ for (const auto& rSymbolSetName : aSymbolSetNames)
+ m_xSymbolSets->append_text(rSymbolSetName);
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolSetChangeHdl, weld::ComboBox&, void )
+{
+ SelectSymbolSet(m_xSymbolSets->get_active_text());
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolChangeHdl, SmShowSymbolSet&, void )
+{
+ SelectSymbol(m_xSymbolSetDisplay->GetSelectSymbol());
+}
+
+IMPL_LINK_NOARG(SmSymbolDialog, EditClickHdl, weld::Button&, void)
+{
+ SmSymDefineDialog aDialog(m_xDialog.get(), pFontListDev, rSymbolMgr);
+
+ // set current symbol and SymbolSet for the new dialog
+ const OUString aSymSetName (m_xSymbolSets->get_active_text()),
+ aSymName (m_xSymbolName->get_label());
+ aDialog.SelectOldSymbolSet(aSymSetName);
+ aDialog.SelectOldSymbol(aSymName);
+ aDialog.SelectSymbolSet(aSymSetName);
+ aDialog.SelectSymbol(aSymName);
+
+ // remember old SymbolSet
+ OUString aOldSymbolSet (m_xSymbolSets->get_active_text());
+
+ sal_uInt16 nSymPos = m_xSymbolSetDisplay->GetSelectSymbol();
+
+ // adapt dialog to data of the SymbolSet manager, which might have changed
+ if (aDialog.run() == RET_OK && rSymbolMgr.IsModified())
+ {
+ rSymbolMgr.Save();
+ FillSymbolSets();
+ }
+
+ // if the old SymbolSet doesn't exist anymore, go to the first one SymbolSet (if one exists)
+ if (!SelectSymbolSet(aOldSymbolSet) && m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+ else
+ {
+ // just update display of current symbol set
+ assert(aSymSetName == aSymSetName); //unexpected change in symbol set name
+ aSymbolSet = rSymbolMgr.GetSymbolSet( aSymbolSetName );
+ m_xSymbolSetDisplay->SetSymbolSet( aSymbolSet );
+ }
+
+ if (nSymPos >= aSymbolSet.size())
+ nSymPos = static_cast< sal_uInt16 >(aSymbolSet.size()) - 1;
+ SelectSymbol( nSymPos );
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolDblClickHdl2, SmShowSymbolSet&, void )
+{
+ SymbolDblClickHdl();
+}
+
+IMPL_LINK_NOARG( SmSymbolDialog, SymbolDblClickHdl, SmShowSymbol&, void )
+{
+ SymbolDblClickHdl();
+}
+
+void SmSymbolDialog::SymbolDblClickHdl()
+{
+ GetClickHdl(*m_xGetBtn);
+ m_xDialog->response(RET_OK);
+}
+
+IMPL_LINK_NOARG(SmSymbolDialog, GetClickHdl, weld::Button&, void)
+{
+ const SmSym *pSym = GetSymbol();
+ if (pSym)
+ {
+ OUString aText = "%" + pSym->GetName() + " ";
+
+ rViewSh.GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_INSERTSPECIAL, SfxCallMode::RECORD,
+ { new SfxStringItem(SID_INSERTSPECIAL, aText) });
+ }
+}
+
+SmSymbolDialog::SmSymbolDialog(weld::Window *pParent, OutputDevice *pFntListDevice,
+ SmSymbolManager &rMgr, SmViewShell &rViewShell)
+ : GenericDialogController(pParent, "modules/smath/ui/catalogdialog.ui", "CatalogDialog")
+ , rViewSh(rViewShell)
+ , rSymbolMgr(rMgr)
+ , pFontListDev(pFntListDevice)
+ , m_xSymbolSets(m_xBuilder->weld_combo_box("symbolset"))
+ , m_xSymbolSetDisplay(new SmShowSymbolSet(m_xBuilder->weld_scrolled_window("scrolledwindow", true)))
+ , m_xSymbolSetDisplayArea(new weld::CustomWeld(*m_xBuilder, "symbolsetdisplay", *m_xSymbolSetDisplay))
+ , m_xSymbolName(m_xBuilder->weld_label("symbolname"))
+ , m_xSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "preview", m_aSymbolDisplay))
+ , m_xGetBtn(m_xBuilder->weld_button("ok"))
+ , m_xEditBtn(m_xBuilder->weld_button("edit"))
+{
+ m_xSymbolSets->make_sorted();
+
+ aSymbolSetName.clear();
+ aSymbolSet.clear();
+ FillSymbolSets();
+ if (m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+
+ m_xSymbolSets->connect_changed(LINK(this, SmSymbolDialog, SymbolSetChangeHdl));
+ m_xSymbolSetDisplay->SetSelectHdl(LINK(this, SmSymbolDialog, SymbolChangeHdl));
+ m_xSymbolSetDisplay->SetDblClickHdl(LINK(this, SmSymbolDialog, SymbolDblClickHdl2));
+ m_aSymbolDisplay.SetDblClickHdl(LINK(this, SmSymbolDialog, SymbolDblClickHdl));
+ m_xEditBtn->connect_clicked(LINK(this, SmSymbolDialog, EditClickHdl));
+ m_xGetBtn->connect_clicked(LINK(this, SmSymbolDialog, GetClickHdl));
+}
+
+SmSymbolDialog::~SmSymbolDialog()
+{
+}
+
+bool SmSymbolDialog::SelectSymbolSet(const OUString &rSymbolSetName)
+{
+ bool bRet = false;
+ sal_Int32 nPos = m_xSymbolSets->find_text(rSymbolSetName);
+
+ aSymbolSetName.clear();
+ aSymbolSet.clear();
+ if (nPos != -1)
+ {
+ m_xSymbolSets->set_active(nPos);
+
+ aSymbolSetName = rSymbolSetName;
+ aSymbolSet = rSymbolMgr.GetSymbolSet( aSymbolSetName );
+
+ // sort symbols by Unicode position (useful for displaying Greek characters alphabetically)
+ std::sort( aSymbolSet.begin(), aSymbolSet.end(),
+ [](const SmSym *pSym1, const SmSym *pSym2)
+ {
+ return pSym1->GetCharacter() < pSym2->GetCharacter();
+ } );
+
+ const bool bEmptySymbolSet = aSymbolSet.empty();
+ m_xSymbolSetDisplay->SetSymbolSet( aSymbolSet );
+ if (!bEmptySymbolSet)
+ SelectSymbol(0);
+
+ bRet = true;
+ }
+ else
+ m_xSymbolSets->set_active(-1);
+
+ return bRet;
+}
+
+void SmSymbolDialog::SelectSymbol(sal_uInt16 nSymbolNo)
+{
+ const SmSym *pSym = nullptr;
+ if (!aSymbolSetName.isEmpty() && nSymbolNo < static_cast< sal_uInt16 >(aSymbolSet.size()))
+ pSym = aSymbolSet[ nSymbolNo ];
+
+ m_xSymbolSetDisplay->SelectSymbol(nSymbolNo);
+ m_aSymbolDisplay.SetSymbol(pSym);
+ m_xSymbolName->set_label(pSym ? pSym->GetName() : OUString());
+}
+
+const SmSym* SmSymbolDialog::GetSymbol() const
+{
+ sal_uInt16 nSymbolNo = m_xSymbolSetDisplay->GetSelectSymbol();
+ bool bValid = !aSymbolSetName.isEmpty() && nSymbolNo < static_cast< sal_uInt16 >(aSymbolSet.size());
+ return bValid ? aSymbolSet[ nSymbolNo ] : nullptr;
+}
+
+void SmShowChar::Resize()
+{
+ const OUString &rText = GetText();
+ if (rText.isEmpty())
+ return;
+ sal_UCS4 cChar = rText.iterateCodePoints(&o3tl::temporary(sal_Int32(0)));
+ SetSymbol(cChar, GetFont()); //force recalculation of size
+}
+
+void SmShowChar::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ Color aTextCol = rRenderContext.GetTextColor();
+ Color aFillCol = rRenderContext.GetFillColor();
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ const Color aWindowTextColor(rStyleSettings.GetDialogTextColor());
+ const Color aWindowColor(rStyleSettings.GetWindowColor());
+ rRenderContext.SetTextColor(aWindowTextColor);
+ rRenderContext.SetFillColor(aWindowColor);
+
+ Size aSize(GetOutputSizePixel());
+ rRenderContext.DrawRect(tools::Rectangle(Point(0, 0), aSize));
+
+ OUString aText(GetText());
+ if (!aText.isEmpty())
+ {
+ vcl::Font aFont(m_aFont);
+ aFont.SetAlignment(ALIGN_TOP);
+ rRenderContext.SetFont(aFont);
+
+ Size aTextSize(rRenderContext.GetTextWidth(aText), rRenderContext.GetTextHeight());
+
+ rRenderContext.DrawText(Point((aSize.Width() - aTextSize.Width()) / 2,
+ (aSize.Height() - aTextSize.Height()) / 2), aText);
+ }
+
+ rRenderContext.SetTextColor(aTextCol);
+ rRenderContext.SetFillColor(aFillCol);
+}
+
+void SmShowChar::SetSymbol( const SmSym *pSym )
+{
+ if (pSym)
+ SetSymbol( pSym->GetCharacter(), pSym->GetFace() );
+}
+
+
+void SmShowChar::SetSymbol( sal_UCS4 cChar, const vcl::Font &rFont )
+{
+ vcl::Font aFont( rFont );
+ Size aSize(GetOutputSizePixel());
+ aFont.SetFontSize(Size(0, aSize.Height() - aSize.Height() / 3));
+ aFont.SetAlignment(ALIGN_BASELINE);
+ SetFont(aFont);
+
+ OUString aText(&cChar, 1);
+ SetText( aText );
+
+ Invalidate();
+}
+
+void SmSymDefineDialog::FillSymbols(weld::ComboBox& rComboBox, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong ComboBox");
+
+ rComboBox.clear();
+ if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ weld::ComboBox& rBox = &rComboBox == m_xOldSymbols.get() ? *m_xOldSymbolSets : *m_xSymbolSets;
+ SymbolPtrVec_t aSymSet(m_aSymbolMgrCopy.GetSymbolSet(rBox.get_active_text()));
+ for (const SmSym* i : aSymSet)
+ rComboBox.append_text(i->GetName());
+}
+
+void SmSymDefineDialog::FillSymbolSets(weld::ComboBox& rComboBox, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbolSets.get() || &rComboBox == m_xSymbolSets.get()) && "Sm : wrong ComboBox");
+
+ rComboBox.clear();
+ if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ const std::set< OUString > aSymbolSetNames( m_aSymbolMgrCopy.GetSymbolSetNames() );
+ for (const auto& rSymbolSetName : aSymbolSetNames)
+ rComboBox.append_text(rSymbolSetName);
+}
+
+void SmSymDefineDialog::FillFonts()
+{
+ m_xFonts->clear();
+ m_xFonts->set_active(-1);
+
+ // Include all fonts of FontList into the font list.
+ // If there are duplicates, only include one entry of each font since the style will be
+ // already selected using the FontStyleBox.
+ if (m_xFontList)
+ {
+ sal_uInt16 nCount = m_xFontList->GetFontNameCount();
+ for (sal_uInt16 i = 0; i < nCount; ++i)
+ m_xFonts->append_text(m_xFontList->GetFontName(i).GetFamilyName());
+ }
+}
+
+void SmSymDefineDialog::FillStyles()
+{
+ m_xStyles->clear();
+// pStyles->SetText(OUString());
+
+ OUString aText(m_xFonts->get_active_text());
+ if (!aText.isEmpty())
+ {
+ // use own StyleNames
+ const SmFontStyles &rStyles = GetFontStyles();
+ for (sal_uInt16 i = 0; i < SmFontStyles::GetCount(); ++i)
+ m_xStyles->append_text(rStyles.GetStyleName(i));
+
+ assert(m_xStyles->get_count() > 0 && "Sm : no styles available");
+ m_xStyles->set_active(0);
+ }
+}
+
+SmSym* SmSymDefineDialog::GetSymbol(const weld::ComboBox& rComboBox)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong combobox");
+ return m_aSymbolMgrCopy.GetSymbolByName(rComboBox.get_active_text());
+}
+
+IMPL_LINK(SmSymDefineDialog, OldSymbolChangeHdl, weld::ComboBox&, rComboBox, void)
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xOldSymbols.get() && "Sm : wrong argument");
+ SelectSymbol(*m_xOldSymbols, m_xOldSymbols->get_active_text(), false);
+}
+
+IMPL_LINK( SmSymDefineDialog, OldSymbolSetChangeHdl, weld::ComboBox&, rComboBox, void )
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xOldSymbolSets.get() && "Sm : wrong argument");
+ SelectSymbolSet(*m_xOldSymbolSets, m_xOldSymbolSets->get_active_text(), false);
+}
+
+IMPL_LINK(SmSymDefineDialog, ModifyHdl, weld::ComboBox&, rComboBox, void)
+{
+ // remember cursor position for later restoring of it
+ int nStartPos, nEndPos;
+ rComboBox.get_entry_selection_bounds(nStartPos, nEndPos);
+
+ if (&rComboBox == m_xSymbols.get())
+ SelectSymbol(*m_xSymbols, m_xSymbols->get_active_text(), false);
+ else if (&rComboBox == m_xSymbolSets.get())
+ SelectSymbolSet(*m_xSymbolSets, m_xSymbolSets->get_active_text(), false);
+ else if (&rComboBox == m_xOldSymbols.get())
+ // allow only names from the list
+ SelectSymbol(*m_xOldSymbols, m_xOldSymbols->get_active_text(), true);
+ else if (&rComboBox == m_xOldSymbolSets.get())
+ // allow only names from the list
+ SelectSymbolSet(*m_xOldSymbolSets, m_xOldSymbolSets->get_active_text(), true);
+ else if (&rComboBox == m_xStyles.get())
+ // allow only names from the list (that's the case here anyway)
+ SelectStyle(m_xStyles->get_active_text(), true);
+ else
+ SAL_WARN("starmath", "wrong combobox argument");
+
+ rComboBox.select_entry_region(nStartPos, nEndPos);
+
+ UpdateButtons();
+}
+
+IMPL_LINK(SmSymDefineDialog, FontChangeHdl, weld::ComboBox&, rListBox, void)
+{
+ (void) rListBox;
+ assert(&rListBox == m_xFonts.get() && "Sm : wrong argument");
+
+ SelectFont(m_xFonts->get_active_text());
+}
+
+IMPL_LINK_NOARG(SmSymDefineDialog, SubsetChangeHdl, weld::ComboBox&, void)
+{
+ int nPos = m_xFontsSubsetLB->get_active();
+ if (nPos != -1)
+ {
+ const Subset* pSubset = weld::fromId<const Subset*>(m_xFontsSubsetLB->get_active_id());
+ if (pSubset)
+ {
+ m_xCharsetDisplay->SelectCharacter( pSubset->GetRangeMin() );
+ }
+ }
+}
+
+IMPL_LINK( SmSymDefineDialog, StyleChangeHdl, weld::ComboBox&, rComboBox, void )
+{
+ (void) rComboBox;
+ assert(&rComboBox == m_xStyles.get() && "Sm : wrong argument");
+
+ SelectStyle(m_xStyles->get_active_text());
+}
+
+IMPL_LINK_NOARG(SmSymDefineDialog, CharHighlightHdl, SvxShowCharSet*, void)
+{
+ sal_UCS4 cChar = m_xCharsetDisplay->GetSelectCharacter();
+
+ if (m_xSubsetMap)
+ {
+ const Subset* pSubset = m_xSubsetMap->GetSubsetByUnicode(cChar);
+ if (pSubset)
+ m_xFontsSubsetLB->set_active_text(pSubset->GetName());
+ else
+ m_xFontsSubsetLB->set_active(-1);
+ }
+
+ m_aSymbolDisplay.SetSymbol(cChar, m_xCharsetDisplay->GetFont());
+
+ UpdateButtons();
+
+ // display Unicode position as symbol name while iterating over characters
+ const OUString aHex(OUString::number(cChar, 16).toAsciiUpperCase());
+ const OUString aPattern( (aHex.getLength() > 4) ? OUString("Ux000000") : OUString("Ux0000") );
+ OUString aUnicodePos = aPattern.subView( 0, aPattern.getLength() - aHex.getLength() ) +
+ aHex;
+ m_xSymbols->set_entry_text(aUnicodePos);
+ m_xSymbolName->set_label(aUnicodePos);
+}
+
+IMPL_LINK( SmSymDefineDialog, AddClickHdl, weld::Button&, rButton, void )
+{
+ (void) rButton;
+ assert(&rButton == m_xAddBtn.get() && "Sm : wrong argument");
+ assert(rButton.get_sensitive() && "Sm : requirements met ??");
+
+ // add symbol
+ const SmSym aNewSymbol(m_xSymbols->get_active_text(), m_xCharsetDisplay->GetFont(),
+ m_xCharsetDisplay->GetSelectCharacter(), m_xSymbolSets->get_active_text());
+ //OSL_ENSURE( m_aSymbolMgrCopy.GetSymbolByName(aTmpSymbolName) == NULL, "symbol already exists" );
+ m_aSymbolMgrCopy.AddOrReplaceSymbol( aNewSymbol );
+
+ // update display of new symbol
+ m_aSymbolDisplay.SetSymbol( &aNewSymbol );
+ m_xSymbolName->set_label(aNewSymbol.GetName());
+ m_xSymbolSetName->set_label(aNewSymbol.GetSymbolSetName());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols, false);
+ FillSymbols(*m_xSymbols, false);
+
+ UpdateButtons();
+}
+
+IMPL_LINK( SmSymDefineDialog, ChangeClickHdl, weld::Button&, rButton, void )
+{
+ (void) rButton;
+ assert(&rButton == m_xChangeBtn.get() && "Sm : wrong argument");
+ assert(m_xChangeBtn->get_sensitive() && "Sm : requirements met ??");
+
+ // get new Symbol to use
+ //! get font from symbol-disp lay since charset-display does not keep
+ //! the bold attribute.
+ const SmSym aNewSymbol(m_xSymbols->get_active_text(), m_xCharsetDisplay->GetFont(),
+ m_xCharsetDisplay->GetSelectCharacter(), m_xSymbolSets->get_active_text());
+
+ // remove old symbol if the name was changed then add new one
+ const bool bNameChanged = m_xOldSymbols->get_active_text() != m_xSymbols->get_active_text();
+ if (bNameChanged)
+ m_aSymbolMgrCopy.RemoveSymbol(m_xOldSymbols->get_active_text());
+ m_aSymbolMgrCopy.AddOrReplaceSymbol( aNewSymbol, true );
+
+ // clear display for original symbol if necessary
+ if (bNameChanged)
+ SetOrigSymbol(nullptr, OUString());
+
+ // update display of new symbol
+ m_aSymbolDisplay.SetSymbol(&aNewSymbol);
+ m_xSymbolName->set_label(aNewSymbol.GetName());
+ m_xSymbolSetName->set_label(aNewSymbol.GetSymbolSetName());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols, false);
+ FillSymbols(*m_xSymbols, false);
+
+ UpdateButtons();
+}
+
+IMPL_LINK(SmSymDefineDialog, DeleteClickHdl, weld::Button&, rButton, void)
+{
+ (void) rButton;
+ assert(&rButton == m_xDeleteBtn.get() && "Sm : wrong argument");
+ assert(m_xDeleteBtn->get_sensitive() && "Sm : requirements met ??");
+
+ if (m_xOrigSymbol)
+ {
+ m_aSymbolMgrCopy.RemoveSymbol(m_xOrigSymbol->GetName());
+
+ // clear display for original symbol
+ SetOrigSymbol(nullptr, OUString());
+
+ // update list box entries
+ FillSymbolSets(*m_xOldSymbolSets, false);
+ FillSymbolSets(*m_xSymbolSets, false);
+ FillSymbols(*m_xOldSymbols ,false);
+ FillSymbols(*m_xSymbols ,false);
+ }
+
+ UpdateButtons();
+}
+
+void SmSymDefineDialog::UpdateButtons()
+{
+ bool bAdd = false,
+ bChange = false,
+ bDelete = false;
+ OUString aTmpSymbolName(m_xSymbols->get_active_text()),
+ aTmpSymbolSetName(m_xSymbolSets->get_active_text());
+
+ if (!aTmpSymbolName.isEmpty() && !aTmpSymbolSetName.isEmpty())
+ {
+ // are all settings equal?
+ //! (Font-, Style- and SymbolSet name comparison is not case sensitive)
+ bool bEqual = m_xOrigSymbol
+ && aTmpSymbolSetName.equalsIgnoreAsciiCase(m_xOldSymbolSetName->get_label())
+ && aTmpSymbolName == m_xOrigSymbol->GetName()
+ && m_xFonts->get_active_text().equalsIgnoreAsciiCase(
+ m_xOrigSymbol->GetFace().GetFamilyName())
+ && m_xStyles->get_active_text().equalsIgnoreAsciiCase(
+ GetFontStyles().GetStyleName(m_xOrigSymbol->GetFace()))
+ && m_xCharsetDisplay->GetSelectCharacter() == m_xOrigSymbol->GetCharacter();
+
+ // only add it if there isn't already a symbol with the same name
+ bAdd = m_aSymbolMgrCopy.GetSymbolByName(aTmpSymbolName) == nullptr;
+
+ // only delete it if all settings are equal
+ bDelete = bool(m_xOrigSymbol);
+
+ // only change it if the old symbol exists and the new one is different
+ bChange = m_xOrigSymbol && !bEqual;
+ }
+
+ m_xAddBtn->set_sensitive(bAdd);
+ m_xChangeBtn->set_sensitive(bChange);
+ m_xDeleteBtn->set_sensitive(bDelete);
+}
+
+SmSymDefineDialog::SmSymDefineDialog(weld::Window* pParent, OutputDevice *pFntListDevice, SmSymbolManager &rMgr)
+ : GenericDialogController(pParent, "modules/smath/ui/symdefinedialog.ui", "EditSymbols")
+ , m_xVirDev(VclPtr<VirtualDevice>::Create())
+ , m_rSymbolMgr(rMgr)
+ , m_xFontList(new FontList(pFntListDevice))
+ , m_xOldSymbols(m_xBuilder->weld_combo_box("oldSymbols"))
+ , m_xOldSymbolSets(m_xBuilder->weld_combo_box("oldSymbolSets"))
+ , m_xSymbols(m_xBuilder->weld_combo_box("symbols"))
+ , m_xSymbolSets(m_xBuilder->weld_combo_box("symbolSets"))
+ , m_xFonts(m_xBuilder->weld_combo_box("fonts"))
+ , m_xFontsSubsetLB(m_xBuilder->weld_combo_box("fontsSubsetLB"))
+ , m_xStyles(m_xBuilder->weld_combo_box("styles"))
+ , m_xOldSymbolName(m_xBuilder->weld_label("oldSymbolName"))
+ , m_xOldSymbolSetName(m_xBuilder->weld_label("oldSymbolSetName"))
+ , m_xSymbolName(m_xBuilder->weld_label("symbolName"))
+ , m_xSymbolSetName(m_xBuilder->weld_label("symbolSetName"))
+ , m_xAddBtn(m_xBuilder->weld_button("add"))
+ , m_xChangeBtn(m_xBuilder->weld_button("modify"))
+ , m_xDeleteBtn(m_xBuilder->weld_button("delete"))
+ , m_xOldSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "oldSymbolDisplay", m_aOldSymbolDisplay))
+ , m_xSymbolDisplay(new weld::CustomWeld(*m_xBuilder, "symbolDisplay", m_aSymbolDisplay))
+ , m_xCharsetDisplay(new SvxShowCharSet(m_xBuilder->weld_scrolled_window("showscroll", true), m_xVirDev))
+ , m_xCharsetDisplayArea(new weld::CustomWeld(*m_xBuilder, "charsetDisplay", *m_xCharsetDisplay))
+{
+ // auto completion is troublesome since that symbols character also gets automatically selected in the
+ // display and if the user previously selected a character to define/redefine that one this is bad
+ m_xOldSymbols->set_entry_completion(false);
+ m_xSymbols->set_entry_completion(false);
+
+ FillFonts();
+ if (m_xFonts->get_count() > 0)
+ SelectFont(m_xFonts->get_text(0));
+
+ SetSymbolSetManager(m_rSymbolMgr);
+
+ m_xOldSymbols->connect_changed(LINK(this, SmSymDefineDialog, OldSymbolChangeHdl));
+ m_xOldSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, OldSymbolSetChangeHdl));
+ m_xSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xOldSymbolSets->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xSymbols->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xOldSymbols->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xStyles->connect_changed(LINK(this, SmSymDefineDialog, ModifyHdl));
+ m_xFonts->connect_changed(LINK(this, SmSymDefineDialog, FontChangeHdl));
+ m_xFontsSubsetLB->connect_changed(LINK(this, SmSymDefineDialog, SubsetChangeHdl));
+ m_xStyles->connect_changed(LINK(this, SmSymDefineDialog, StyleChangeHdl));
+ m_xAddBtn->connect_clicked(LINK(this, SmSymDefineDialog, AddClickHdl));
+ m_xChangeBtn->connect_clicked(LINK(this, SmSymDefineDialog, ChangeClickHdl));
+ m_xDeleteBtn->connect_clicked(LINK(this, SmSymDefineDialog, DeleteClickHdl));
+ m_xCharsetDisplay->SetHighlightHdl( LINK( this, SmSymDefineDialog, CharHighlightHdl ) );
+}
+
+SmSymDefineDialog::~SmSymDefineDialog()
+{
+}
+
+short SmSymDefineDialog::run()
+{
+ short nResult = GenericDialogController::run();
+
+ // apply changes if dialog was closed by clicking OK
+ if (m_aSymbolMgrCopy.IsModified() && nResult == RET_OK)
+ m_rSymbolMgr = m_aSymbolMgrCopy;
+
+ return nResult;
+}
+
+void SmSymDefineDialog::SetSymbolSetManager(const SmSymbolManager &rMgr)
+{
+ m_aSymbolMgrCopy = rMgr;
+
+ // Set the modified flag of the copy to false so that
+ // we can check later on if anything has been changed
+ m_aSymbolMgrCopy.SetModified(false);
+
+ FillSymbolSets(*m_xOldSymbolSets);
+ if (m_xOldSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xOldSymbolSets->get_text(0));
+ FillSymbolSets(*m_xSymbolSets);
+ if (m_xSymbolSets->get_count() > 0)
+ SelectSymbolSet(m_xSymbolSets->get_text(0));
+ FillSymbols(*m_xOldSymbols);
+ if (m_xOldSymbols->get_count() > 0)
+ SelectSymbol(m_xOldSymbols->get_text(0));
+ FillSymbols(*m_xSymbols);
+ if (m_xSymbols->get_count() > 0)
+ SelectSymbol(m_xSymbols->get_text(0));
+
+ UpdateButtons();
+}
+
+bool SmSymDefineDialog::SelectSymbolSet(weld::ComboBox& rComboBox,
+ std::u16string_view rSymbolSetName, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbolSets.get() || &rComboBox == m_xSymbolSets.get()) && "Sm : wrong ComboBox");
+
+ // trim SymbolName (no leading and trailing blanks)
+ OUString aNormName( comphelper::string::strip(rSymbolSetName, ' ') );
+ // and remove possible deviations within the input
+ rComboBox.set_entry_text(aNormName);
+
+ bool bRet = false;
+ int nPos = rComboBox.find_text(aNormName);
+
+ if (nPos != -1)
+ {
+ rComboBox.set_active(nPos);
+ bRet = true;
+ }
+ else if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ bool bIsOld = &rComboBox == m_xOldSymbolSets.get();
+
+ // setting the SymbolSet name at the associated display
+ weld::Label& rFT = bIsOld ? *m_xOldSymbolSetName : *m_xSymbolSetName;
+ rFT.set_label(rComboBox.get_active_text());
+
+ // set the symbol name which belongs to the SymbolSet at the associated combobox
+ weld::ComboBox& rCB = bIsOld ? *m_xOldSymbols : *m_xSymbols;
+ FillSymbols(rCB, false);
+
+ // display a valid respectively no symbol when changing the SymbolSets
+ if (bIsOld)
+ {
+ OUString aTmpOldSymbolName;
+ if (m_xOldSymbols->get_count() > 0)
+ aTmpOldSymbolName = m_xOldSymbols->get_text(0);
+ SelectSymbol(*m_xOldSymbols, aTmpOldSymbolName, true);
+ }
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+void SmSymDefineDialog::SetOrigSymbol(const SmSym *pSymbol,
+ const OUString &rSymbolSetName)
+{
+ // clear old symbol
+ m_xOrigSymbol.reset();
+
+ OUString aSymName,
+ aSymSetName;
+ if (pSymbol)
+ {
+ // set new symbol
+ m_xOrigSymbol.reset(new SmSym(*pSymbol));
+
+ aSymName = pSymbol->GetName();
+ aSymSetName = rSymbolSetName;
+ m_aOldSymbolDisplay.SetSymbol( pSymbol );
+ }
+ else
+ { // delete displayed symbols
+ m_aOldSymbolDisplay.SetText(OUString());
+ m_aOldSymbolDisplay.Invalidate();
+ }
+ m_xOldSymbolName->set_label(aSymName);
+ m_xOldSymbolSetName->set_label(aSymSetName);
+}
+
+
+bool SmSymDefineDialog::SelectSymbol(weld::ComboBox& rComboBox,
+ const OUString &rSymbolName, bool bDeleteText)
+{
+ assert((&rComboBox == m_xOldSymbols.get() || &rComboBox == m_xSymbols.get()) && "Sm : wrong ComboBox");
+
+ // trim SymbolName (no blanks)
+ OUString aNormName = rSymbolName.replaceAll(" ", "");
+ // and remove possible deviations within the input
+ rComboBox.set_entry_text(aNormName);
+
+ bool bRet = false;
+ int nPos = rComboBox.find_text(aNormName);
+
+ bool bIsOld = &rComboBox == m_xOldSymbols.get();
+
+ if (nPos != -1)
+ {
+ rComboBox.set_active(nPos);
+
+ if (!bIsOld)
+ {
+ const SmSym *pSymbol = GetSymbol(*m_xSymbols);
+ if (pSymbol)
+ {
+ // choose font and style accordingly
+ const vcl::Font &rFont = pSymbol->GetFace();
+ SelectFont(rFont.GetFamilyName(), false);
+ SelectStyle(GetFontStyles().GetStyleName(rFont), false);
+
+ // Since setting the Font via the Style name of the SymbolFonts doesn't
+ // work really well (e.g. it can be empty even though the font itself is
+ // bold or italic) we're manually setting the Font with respect to the Symbol
+ m_xCharsetDisplay->SetFont(rFont);
+ m_aSymbolDisplay.SetFont(rFont);
+
+ // select associated character
+ SelectChar(pSymbol->GetCharacter());
+
+ // since SelectChar will also set the unicode point as text in the
+ // symbols box, we have to set the symbol name again to get that one displayed
+ m_xSymbols->set_entry_text(pSymbol->GetName());
+ }
+ }
+
+ bRet = true;
+ }
+ else if (bDeleteText)
+ rComboBox.set_entry_text(OUString());
+
+ if (bIsOld)
+ {
+ // if there's a change of the old symbol, show only the available ones, otherwise show none
+ const SmSym *pOldSymbol = nullptr;
+ OUString aTmpOldSymbolSetName;
+ if (nPos != -1)
+ {
+ pOldSymbol = m_aSymbolMgrCopy.GetSymbolByName(aNormName);
+ aTmpOldSymbolSetName = m_xOldSymbolSets->get_active_text();
+ }
+ SetOrigSymbol(pOldSymbol, aTmpOldSymbolSetName);
+ }
+ else
+ m_xSymbolName->set_label(rComboBox.get_active_text());
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+
+void SmSymDefineDialog::SetFont(const OUString &rFontName, std::u16string_view rStyleName)
+{
+ // get Font (FontInfo) matching name and style
+ FontMetric aFontMetric;
+ if (m_xFontList)
+ aFontMetric = m_xFontList->Get(rFontName, WEIGHT_NORMAL, ITALIC_NONE);
+ SetFontStyle(rStyleName, aFontMetric);
+
+ m_xCharsetDisplay->SetFont(aFontMetric);
+ m_aSymbolDisplay.SetFont(aFontMetric);
+
+ // update subset listbox for new font's unicode subsets
+ FontCharMapRef xFontCharMap = m_xCharsetDisplay->GetFontCharMap();
+ m_xSubsetMap.reset(new SubsetMap( xFontCharMap ));
+
+ m_xFontsSubsetLB->clear();
+ bool bFirst = true;
+ for (auto & subset : m_xSubsetMap->GetSubsetMap())
+ {
+ m_xFontsSubsetLB->append(weld::toId(&subset), subset.GetName());
+ // subset must live at least as long as the selected font !!!
+ if (bFirst)
+ m_xFontsSubsetLB->set_active(0);
+ bFirst = false;
+ }
+ if (bFirst)
+ m_xFontsSubsetLB->set_active(-1);
+ m_xFontsSubsetLB->set_sensitive(!bFirst);
+}
+
+bool SmSymDefineDialog::SelectFont(const OUString &rFontName, bool bApplyFont)
+{
+ bool bRet = false;
+ int nPos = m_xFonts->find_text(rFontName);
+
+ if (nPos != -1)
+ {
+ m_xFonts->set_active(nPos);
+ if (m_xStyles->get_count() > 0)
+ SelectStyle(m_xStyles->get_text(0));
+ if (bApplyFont)
+ {
+ SetFont(m_xFonts->get_active_text(), m_xStyles->get_active_text());
+ m_aSymbolDisplay.SetSymbol(m_xCharsetDisplay->GetSelectCharacter(), m_xCharsetDisplay->GetFont());
+ }
+ bRet = true;
+ }
+ else
+ m_xFonts->set_active(-1);
+ FillStyles();
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+
+bool SmSymDefineDialog::SelectStyle(const OUString &rStyleName, bool bApplyFont)
+{
+ bool bRet = false;
+ int nPos = m_xStyles->find_text(rStyleName);
+
+ // if the style is not available take the first available one (if existent)
+ if (nPos == -1 && m_xStyles->get_count() > 0)
+ nPos = 0;
+
+ if (nPos != -1)
+ {
+ m_xStyles->set_active(nPos);
+ if (bApplyFont)
+ {
+ SetFont(m_xFonts->get_active_text(), m_xStyles->get_active_text());
+ m_aSymbolDisplay.SetSymbol(m_xCharsetDisplay->GetSelectCharacter(), m_xCharsetDisplay->GetFont());
+ }
+ bRet = true;
+ }
+ else
+ m_xStyles->set_entry_text(OUString());
+
+ UpdateButtons();
+
+ return bRet;
+}
+
+void SmSymDefineDialog::SelectChar(sal_Unicode cChar)
+{
+ m_xCharsetDisplay->SelectCharacter( cChar );
+ m_aSymbolDisplay.SetSymbol(cChar, m_xCharsetDisplay->GetFont());
+
+ UpdateButtons();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/document.cxx b/starmath/source/document.cxx
new file mode 100644
index 000000000..59353879d
--- /dev/null
+++ b/starmath/source/document.cxx
@@ -0,0 +1,1232 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/uno/Any.h>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/accessibletexthelper.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <unotools/eventcfg.hxx>
+#include <sfx2/event.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <comphelper/classids.hxx>
+#include <sot/formats.hxx>
+#include <sot/storage.hxx>
+#include <svl/eitem.hxx>
+#include <svl/intitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/slstitm.hxx>
+#include <svl/hint.hxx>
+#include <svl/stritem.hxx>
+#include <svl/undo.hxx>
+#include <svl/whiter.hxx>
+#include <vcl/mapmod.hxx>
+#include <vcl/virdev.hxx>
+#include <tools/mapunit.hxx>
+#include <vcl/settings.hxx>
+
+#include <document.hxx>
+#include <action.hxx>
+#include <dialog.hxx>
+#include <format.hxx>
+#include <parse.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <symbol.hxx>
+#include <unomodel.hxx>
+#include <utility.hxx>
+#include <view.hxx>
+#include "mathtype.hxx"
+#include "ooxmlexport.hxx"
+#include "ooxmlimport.hxx"
+#include "rtfexport.hxx"
+#include <mathmlimport.hxx>
+#include <mathmlexport.hxx>
+#include <svx/svxids.hrc>
+#include <cursor.hxx>
+#include <tools/diagnose_ex.h>
+#include <visitors.hxx>
+#include "accessibility.hxx"
+#include <cfgitem.hxx>
+#include <utility>
+#include <oox/mathml/export.hxx>
+#include <ElementsDockingWindow.hxx>
+#include <smediteng.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::accessibility;
+using namespace ::com::sun::star::uno;
+
+#define ShellClass_SmDocShell
+#include <smslots.hxx>
+
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SmDocShell, SfxObjectShell)
+
+void SmDocShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterPopupMenu("view");
+}
+
+void SmDocShell::SetSmSyntaxVersion(sal_uInt16 nSmSyntaxVersion)
+{
+ mnSmSyntaxVersion = nSmSyntaxVersion;
+ maParser.reset(starmathdatabase::GetVersionSmParser(mnSmSyntaxVersion));
+ SmViewShell* pViewSh = SmGetActiveView();
+ if (pViewSh)
+ {
+ SmElementsDockingWindow* dockingWindow = pViewSh->GetDockingWindow();
+ if(dockingWindow)
+ {
+ dockingWindow->setSmSyntaxVersion(nSmSyntaxVersion);
+ }
+ }
+}
+
+SFX_IMPL_OBJECTFACTORY(SmDocShell, SvGlobalName(SO3_SM_CLASSID), "smath" )
+
+void SmDocShell::Notify(SfxBroadcaster&, const SfxHint& rHint)
+{
+ if (rHint.GetId() == SfxHintId::MathFormatChanged)
+ {
+ SetFormulaArranged(false);
+
+ mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState
+
+ Repaint();
+ }
+}
+
+void SmDocShell::LoadSymbols()
+{
+ SmModule *pp = SM_MOD();
+ pp->GetSymbolManager().Load();
+}
+
+
+OUString SmDocShell::GetComment() const
+{
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(
+ GetModel(), uno::UNO_QUERY_THROW);
+ uno::Reference<document::XDocumentProperties> xDocProps(
+ xDPS->getDocumentProperties());
+ return xDocProps->getDescription();
+}
+
+
+void SmDocShell::SetText(const OUString& rBuffer)
+{
+ if (rBuffer == maText)
+ return;
+
+ bool bIsEnabled = IsEnableSetModified();
+ if( bIsEnabled )
+ EnableSetModified( false );
+
+ maText = rBuffer;
+ SetFormulaArranged( false );
+
+ Parse();
+
+ SmViewShell *pViewSh = SmGetActiveView();
+ if (pViewSh)
+ {
+ pViewSh->GetViewFrame()->GetBindings().Invalidate(SID_TEXT);
+ if ( SfxObjectCreateMode::EMBEDDED == GetCreateMode() )
+ {
+ // have SwOleClient::FormatChanged() to align the modified formula properly
+ // even if the visible area does not change (e.g. when formula text changes from
+ // "{a over b + c} over d" to "d over {a over b + c}"
+ SfxGetpApp()->NotifyEvent(SfxEventHint( SfxEventHintId::VisAreaChanged, GlobalEventConfig::GetEventName(GlobalEventId::VISAREACHANGED), this));
+
+ Repaint();
+ }
+ else
+ pViewSh->GetGraphicWidget().Invalidate();
+ }
+
+ if ( bIsEnabled )
+ EnableSetModified( bIsEnabled );
+ SetModified();
+
+ // launch accessible event if necessary
+ SmGraphicAccessible *pAcc = pViewSh ? pViewSh->GetGraphicWidget().GetAccessible_Impl() : nullptr;
+ if (pAcc)
+ {
+ Any aOldValue, aNewValue;
+ if ( comphelper::OCommonAccessibleText::implInitTextChangedEvent( maText, rBuffer, aOldValue, aNewValue ) )
+ {
+ pAcc->LaunchEvent( AccessibleEventId::TEXT_CHANGED,
+ aOldValue, aNewValue );
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ OnDocumentPrinterChanged(nullptr);
+}
+
+void SmDocShell::SetFormat(SmFormat const & rFormat)
+{
+ maFormat = rFormat;
+ SetFormulaArranged( false );
+ SetModified();
+
+ mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState
+
+ // don't use SmGetActiveView since the view shell might not be active (0 pointer)
+ // if for example the Basic Macro dialog currently has the focus. Thus:
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ while (pFrm)
+ {
+ pFrm->GetBindings().Invalidate(SID_GRAPHIC_SM);
+ pFrm = SfxViewFrame::GetNext( *pFrm, this );
+ }
+}
+
+OUString const & SmDocShell::GetAccessibleText()
+{
+ ArrangeFormula();
+ if (maAccText.isEmpty())
+ {
+ OSL_ENSURE( mpTree, "Tree missing" );
+ if (mpTree)
+ {
+ OUStringBuffer aBuf;
+ mpTree->GetAccessibleText(aBuf);
+ maAccText = aBuf.makeStringAndClear();
+ }
+ }
+ return maAccText;
+}
+
+void SmDocShell::Parse()
+{
+ mpTree.reset();
+ ReplaceBadChars();
+ mpTree = maParser->Parse(maText);
+ mnModifyCount++; //! see comment for SID_GRAPHIC_SM in SmDocShell::GetState
+ SetFormulaArranged( false );
+ InvalidateCursor();
+ maUsedSymbols = maParser->GetUsedSymbols();
+}
+
+
+void SmDocShell::ArrangeFormula()
+{
+ if (mbFormulaArranged)
+ return;
+
+ // Only for the duration of the existence of this object the correct settings
+ // at the printer are guaranteed!
+ SmPrinterAccess aPrtAcc(*this);
+ OutputDevice* pOutDev = aPrtAcc.GetRefDev();
+
+ SAL_WARN_IF( !pOutDev, "starmath", "!! SmDocShell::ArrangeFormula: reference device missing !!");
+
+ // if necessary get another OutputDevice for which we format
+ if (!pOutDev)
+ {
+ if (SmViewShell *pView = SmGetActiveView())
+ pOutDev = &pView->GetGraphicWidget().GetDrawingArea()->get_ref_device();
+ else
+ {
+ pOutDev = &SM_MOD()->GetDefaultVirtualDev();
+ pOutDev->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ }
+ }
+ OSL_ENSURE(pOutDev->GetMapMode().GetMapUnit() == MapUnit::Map100thMM,
+ "Sm : wrong MapMode");
+
+ const SmFormat &rFormat = GetFormat();
+ mpTree->Prepare(rFormat, *this, 0);
+
+ // format/draw formulas always from left to right,
+ // and numbers should not be converted
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = pOutDev->GetLayoutMode();
+ pOutDev->SetLayoutMode( vcl::text::ComplexTextLayoutFlags::Default );
+ LanguageType nDigitLang = pOutDev->GetDigitLanguage();
+ pOutDev->SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ mpTree->Arrange(*pOutDev, rFormat);
+
+ pOutDev->SetLayoutMode( nLayoutMode );
+ pOutDev->SetDigitLanguage( nDigitLang );
+
+ SetFormulaArranged(true);
+
+ // invalidate accessible text
+ maAccText.clear();
+}
+
+void SmDocShell::UpdateEditEngineDefaultFonts()
+{
+ SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions);
+}
+
+EditEngine& SmDocShell::GetEditEngine()
+{
+ if (!mpEditEngine)
+ {
+ //!
+ //! see also SmEditWindow::DataChanged !
+ //!
+ mpEditEngineItemPool = EditEngine::CreatePool();
+ SmEditEngine::setSmItemPool(mpEditEngineItemPool.get(), maLinguOptions);
+ mpEditEngine.reset( new SmEditEngine( mpEditEngineItemPool.get() ) );
+ mpEditEngine->EraseVirtualDevice();
+
+ // set initial text if the document already has some...
+ // (may be the case when reloading a doc)
+ OUString aTxt( GetText() );
+ if (!aTxt.isEmpty())
+ mpEditEngine->SetText( aTxt );
+ mpEditEngine->ClearModifyFlag();
+ }
+ return *mpEditEngine;
+}
+
+
+void SmDocShell::DrawFormula(OutputDevice &rDev, Point &rPosition, bool bDrawSelection)
+{
+ if (!mpTree)
+ Parse();
+ OSL_ENSURE(mpTree, "Sm : NULL pointer");
+
+ ArrangeFormula();
+
+ // Problem: What happens to WYSIWYG? While we're active inplace, we don't have a reference
+ // device and aren't aligned to that either. So now there can be a difference between the
+ // VisArea (i.e. the size within the client) and the current size.
+ // Idea: The difference could be adapted with SmNod::SetSize (no long-term solution)
+
+ rPosition.AdjustX(maFormat.GetDistance( DIS_LEFTSPACE ) );
+ rPosition.AdjustY(maFormat.GetDistance( DIS_TOPSPACE ) );
+
+ //! in case of high contrast-mode (accessibility option!)
+ //! the draw mode needs to be set to default, because when embedding
+ //! Math for example in Calc in "a over b" the fraction bar may not
+ //! be visible else. More generally: the FillColor may have been changed.
+ DrawModeFlags nOldDrawMode = DrawModeFlags::Default;
+ bool bRestoreDrawMode = false;
+ if (OUTDEV_WINDOW == rDev.GetOutDevType() &&
+ rDev.GetOwnerWindow()->GetSettings().GetStyleSettings().GetHighContrastMode())
+ {
+ nOldDrawMode = rDev.GetDrawMode();
+ rDev.SetDrawMode( DrawModeFlags::Default );
+ bRestoreDrawMode = true;
+ }
+
+ // format/draw formulas always from left to right
+ // and numbers should not be converted
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = rDev.GetLayoutMode();
+ rDev.SetLayoutMode( vcl::text::ComplexTextLayoutFlags::Default );
+ LanguageType nDigitLang = rDev.GetDigitLanguage();
+ rDev.SetDigitLanguage( LANGUAGE_ENGLISH );
+
+ //Set selection if any
+ if(mpCursor && bDrawSelection){
+ mpCursor->AnnotateSelection();
+ SmSelectionDrawingVisitor(rDev, mpTree.get(), rPosition);
+ }
+
+ //Drawing using visitor
+ SmDrawingVisitor(rDev, rPosition, mpTree.get());
+
+
+ rDev.SetLayoutMode( nLayoutMode );
+ rDev.SetDigitLanguage( nDigitLang );
+
+ if (bRestoreDrawMode)
+ rDev.SetDrawMode( nOldDrawMode );
+}
+
+Size SmDocShell::GetSize()
+{
+ Size aRet;
+
+ if (!mpTree)
+ Parse();
+
+ if (mpTree)
+ {
+ ArrangeFormula();
+ aRet = mpTree->GetSize();
+
+ if ( !aRet.Width() )
+ aRet.setWidth( 2000 );
+ else
+ aRet.AdjustWidth(maFormat.GetDistance( DIS_LEFTSPACE ) +
+ maFormat.GetDistance( DIS_RIGHTSPACE ) );
+ if ( !aRet.Height() )
+ aRet.setHeight( 1000 );
+ else
+ aRet.AdjustHeight(maFormat.GetDistance( DIS_TOPSPACE ) +
+ maFormat.GetDistance( DIS_BOTTOMSPACE ) );
+ }
+
+ return aRet;
+}
+
+void SmDocShell::InvalidateCursor(){
+ mpCursor.reset();
+}
+
+SmCursor& SmDocShell::GetCursor(){
+ if(!mpCursor)
+ mpCursor.reset(new SmCursor(mpTree.get(), this));
+ return *mpCursor;
+}
+
+bool SmDocShell::HasCursor() const { return mpCursor != nullptr; }
+
+SmPrinterAccess::SmPrinterAccess( SmDocShell &rDocShell )
+{
+ pPrinter = rDocShell.GetPrt();
+ if ( pPrinter )
+ {
+ pPrinter->Push( vcl::PushFlags::MAPMODE );
+ if ( SfxObjectCreateMode::EMBEDDED == rDocShell.GetCreateMode() )
+ {
+ // if it is an embedded object (without its own printer)
+ // we change the MapMode temporarily.
+ //!If it is a document with its own printer the MapMode should
+ //!be set correct (once) elsewhere(!), in order to avoid numerous
+ //!superfluous pushing and popping of the MapMode when using
+ //!this class.
+
+ const MapUnit eOld = pPrinter->GetMapMode().GetMapUnit();
+ if ( MapUnit::Map100thMM != eOld )
+ {
+ MapMode aMap( pPrinter->GetMapMode() );
+ aMap.SetMapUnit( MapUnit::Map100thMM );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, MapUnit::Map100thMM ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, MapUnit::Map100thMM ) );
+ aMap.SetOrigin( aTmp );
+ pPrinter->SetMapMode( aMap );
+ }
+ }
+ }
+ pRefDev = rDocShell.GetRefDev();
+ if ( !pRefDev || pPrinter.get() == pRefDev.get() )
+ return;
+
+ pRefDev->Push( vcl::PushFlags::MAPMODE );
+ if ( SfxObjectCreateMode::EMBEDDED != rDocShell.GetCreateMode() )
+ return;
+
+ // if it is an embedded object (without its own printer)
+ // we change the MapMode temporarily.
+ //!If it is a document with its own printer the MapMode should
+ //!be set correct (once) elsewhere(!), in order to avoid numerous
+ //!superfluous pushing and popping of the MapMode when using
+ //!this class.
+
+ const MapUnit eOld = pRefDev->GetMapMode().GetMapUnit();
+ if ( MapUnit::Map100thMM != eOld )
+ {
+ MapMode aMap( pRefDev->GetMapMode() );
+ aMap.SetMapUnit( MapUnit::Map100thMM );
+ Point aTmp( aMap.GetOrigin() );
+ aTmp.setX( OutputDevice::LogicToLogic( aTmp.X(), eOld, MapUnit::Map100thMM ) );
+ aTmp.setY( OutputDevice::LogicToLogic( aTmp.Y(), eOld, MapUnit::Map100thMM ) );
+ aMap.SetOrigin( aTmp );
+ pRefDev->SetMapMode( aMap );
+ }
+}
+
+SmPrinterAccess::~SmPrinterAccess()
+{
+ if ( pPrinter )
+ pPrinter->Pop();
+ if ( pRefDev && pRefDev != pPrinter )
+ pRefDev->Pop();
+}
+
+Printer* SmDocShell::GetPrt()
+{
+ if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
+ {
+ // Normally the server provides the printer. But if it doesn't provide one (e.g. because
+ // there is no connection) it still can be the case that we know the printer because it
+ // has been passed on by the server in OnDocumentPrinterChanged and being kept temporarily.
+ Printer* pPrt = GetDocumentPrinter();
+ if (!pPrt && mpTmpPrinter)
+ pPrt = mpTmpPrinter;
+ return pPrt;
+ }
+ else if (!mpPrinter)
+ {
+ auto pOptions = std::make_unique<SfxItemSetFixed<
+ SID_PRINTTITLE, SID_PRINTZOOM,
+ SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>>(GetPool());
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ConfigToItemSet(*pOptions);
+ mpPrinter = VclPtr<SfxPrinter>::Create(std::move(pOptions));
+ mpPrinter->SetMapMode(MapMode(MapUnit::Map100thMM));
+ }
+ return mpPrinter;
+}
+
+OutputDevice* SmDocShell::GetRefDev()
+{
+ if (SfxObjectCreateMode::EMBEDDED == GetCreateMode())
+ {
+ OutputDevice* pOutDev = GetDocumentRefDev();
+ if (pOutDev)
+ return pOutDev;
+ }
+
+ return GetPrt();
+}
+
+void SmDocShell::SetPrinter( SfxPrinter *pNew )
+{
+ mpPrinter.disposeAndClear();
+ mpPrinter = pNew; //Transfer ownership
+ mpPrinter->SetMapMode( MapMode(MapUnit::Map100thMM) );
+ SetFormulaArranged(false);
+ Repaint();
+}
+
+void SmDocShell::OnDocumentPrinterChanged( Printer *pPrt )
+{
+ mpTmpPrinter = pPrt;
+ SetFormulaArranged(false);
+ Size aOldSize = GetVisArea().GetSize();
+ Repaint();
+ if( aOldSize != GetVisArea().GetSize() && !maText.isEmpty() )
+ SetModified();
+ mpTmpPrinter = nullptr;
+}
+
+void SmDocShell::Repaint()
+{
+ bool bIsEnabled = IsEnableSetModified();
+ if (bIsEnabled)
+ EnableSetModified( false );
+
+ SetFormulaArranged(false);
+
+ Size aVisSize = GetSize();
+ SetVisAreaSize(aVisSize);
+ if (SmViewShell* pViewSh = SmGetActiveView())
+ pViewSh->GetGraphicWidget().Invalidate();
+
+ if (bIsEnabled)
+ EnableSetModified(bIsEnabled);
+}
+
+SmDocShell::SmDocShell( SfxModelFlags i_nSfxCreationFlags )
+ : SfxObjectShell(i_nSfxCreationFlags)
+ , m_pMlElementTree(nullptr)
+ , mpPrinter(nullptr)
+ , mpTmpPrinter(nullptr)
+ , mnModifyCount(0)
+ , mbFormulaArranged(false)
+ , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+{
+ SvtLinguConfig().GetOptions(maLinguOptions);
+
+ SetPool(&SfxGetpApp()->GetPool());
+
+ SmModule *pp = SM_MOD();
+ maFormat = pp->GetConfig()->GetStandardFormat();
+
+ StartListening(maFormat);
+ StartListening(*pp->GetConfig());
+
+ SetBaseModel(new SmModel(this));
+ SetSmSyntaxVersion(mnSmSyntaxVersion);
+}
+
+SmDocShell::~SmDocShell()
+{
+ SmModule *pp = SM_MOD();
+
+ EndListening(maFormat);
+ EndListening(*pp->GetConfig());
+
+ mpCursor.reset();
+ mpEditEngine.reset();
+ mpEditEngineItemPool.clear();
+ mpPrinter.disposeAndClear();
+
+ mathml::SmMlIteratorFree(m_pMlElementTree);
+}
+
+bool SmDocShell::ConvertFrom(SfxMedium &rMedium)
+{
+ bool bSuccess = false;
+ const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
+
+ OSL_ENSURE( rFltName != STAROFFICE_XML, "Wrong filter!");
+
+ if ( rFltName == MATHML_XML )
+ {
+ if (mpTree)
+ {
+ mpTree.reset();
+ InvalidateCursor();
+ }
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLImportWrapper aEquation(xModel);
+ aEquation.useHTMLMLEntities(true);
+ bSuccess = ( ERRCODE_NONE == aEquation.Import(rMedium) );
+ }
+ else
+ {
+ SvStream *pStream = rMedium.GetInStream();
+ if ( pStream )
+ {
+ if ( SotStorage::IsStorageFile( pStream ) )
+ {
+ tools::SvRef<SotStorage> aStorage = new SotStorage( pStream, false );
+ if ( aStorage->IsStream("Equation Native") )
+ {
+ // is this a MathType Storage?
+ OUStringBuffer aBuffer;
+ MathType aEquation(aBuffer);
+ bSuccess = aEquation.Parse( aStorage.get() );
+ if ( bSuccess )
+ {
+ maText = aBuffer.makeStringAndClear();
+ Parse();
+ }
+ }
+ }
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ SetFormulaArranged( false );
+ Repaint();
+ }
+
+ FinishedLoading();
+ return bSuccess;
+}
+
+
+bool SmDocShell::InitNew( const uno::Reference < embed::XStorage >& xStorage )
+{
+ bool bRet = false;
+ if ( SfxObjectShell::InitNew( xStorage ) )
+ {
+ bRet = true;
+ SetVisArea(tools::Rectangle(Point(0, 0), Size(2000, 1000)));
+ }
+ return bRet;
+}
+
+
+bool SmDocShell::Load( SfxMedium& rMedium )
+{
+ bool bRet = false;
+ if( SfxObjectShell::Load( rMedium ))
+ {
+ uno::Reference < embed::XStorage > xStorage = GetMedium()->GetStorage();
+ if (xStorage->hasByName("content.xml") && xStorage->isStreamElement("content.xml"))
+ {
+ // is this a fabulous math package ?
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLImportWrapper aEquation(xModel);
+ auto nError = aEquation.Import(rMedium);
+ bRet = ERRCODE_NONE == nError;
+ SetError(nError);
+ }
+ }
+
+ if ( GetCreateMode() == SfxObjectCreateMode::EMBEDDED )
+ {
+ SetFormulaArranged( false );
+ Repaint();
+ }
+
+ FinishedLoading();
+ return bRet;
+}
+
+
+bool SmDocShell::Save()
+{
+ //! apply latest changes if necessary
+ UpdateText();
+
+ if ( SfxObjectShell::Save() )
+ {
+ if (!mpTree)
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ return aEquation.Export(*GetMedium());
+ }
+
+ return false;
+}
+
+/*
+ * replace bad characters that can not be saved. (#i74144)
+ * */
+void SmDocShell::ReplaceBadChars()
+{
+ bool bReplace = false;
+
+ if (!mpEditEngine)
+ return;
+
+ OUStringBuffer aBuf( mpEditEngine->GetText() );
+
+ for (sal_Int32 i = 0; i < aBuf.getLength(); ++i)
+ {
+ if (aBuf[i] < ' ' && aBuf[i] != '\r' && aBuf[i] != '\n' && aBuf[i] != '\t')
+ {
+ aBuf[i] = ' ';
+ bReplace = true;
+ }
+ }
+
+ if (bReplace)
+ maText = aBuf.makeStringAndClear();
+}
+
+
+void SmDocShell::UpdateText()
+{
+ if (mpEditEngine && mpEditEngine->IsModified())
+ {
+ OUString aEngTxt( mpEditEngine->GetText() );
+ if (GetText() != aEngTxt)
+ SetText( aEngTxt );
+ }
+}
+
+
+bool SmDocShell::SaveAs( SfxMedium& rMedium )
+{
+ bool bRet = false;
+
+ //! apply latest changes if necessary
+ UpdateText();
+
+ if ( SfxObjectShell::SaveAs( rMedium ) )
+ {
+ if (!mpTree)
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ bRet = aEquation.Export(rMedium);
+ }
+ return bRet;
+}
+
+bool SmDocShell::ConvertTo( SfxMedium &rMedium )
+{
+ bool bRet = false;
+ std::shared_ptr<const SfxFilter> pFlt = rMedium.GetFilter();
+ if( pFlt )
+ {
+ if( !mpTree )
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+
+ const OUString& rFltName = pFlt->GetFilterName();
+ if(rFltName == STAROFFICE_XML)
+ {
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(false);
+ bRet = aEquation.Export(rMedium);
+ }
+ else if(rFltName == MATHML_XML)
+ {
+ Reference<css::frame::XModel> xModel(GetModel());
+ SmXMLExportWrapper aEquation(xModel);
+ aEquation.SetFlat(true);
+ aEquation.SetUseHTMLMLEntities(true);
+ bRet = aEquation.Export(rMedium);
+ }
+ else if (pFlt->GetFilterName() == "MathType 3.x")
+ bRet = WriteAsMathType3( rMedium );
+ }
+ return bRet;
+}
+
+void SmDocShell::writeFormulaOoxml(
+ ::sax_fastparser::FSHelperPtr const& pSerializer,
+ oox::core::OoxmlVersion const version,
+ oox::drawingml::DocumentType const documentType,
+ const sal_Int8 nAlign)
+{
+ if( !mpTree )
+ Parse();
+ if( mpTree )
+ ArrangeFormula();
+ SmOoxmlExport aEquation(mpTree.get(), version, documentType);
+ if(documentType == oox::drawingml::DOCUMENT_DOCX)
+ aEquation.ConvertFromStarMath( pSerializer, nAlign);
+ else
+ aEquation.ConvertFromStarMath(pSerializer, oox::FormulaExportBase::eFormulaAlign::INLINE);
+}
+
+void SmDocShell::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ if (!mpTree)
+ Parse();
+ if (mpTree)
+ ArrangeFormula();
+ SmRtfExport aEquation(mpTree.get());
+ aEquation.ConvertFromStarMath(rBuffer, nEncoding);
+}
+
+void SmDocShell::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
+{
+ SmOoxmlImport aEquation( stream );
+ SetText( aEquation.ConvertToStarMath());
+}
+
+void SmDocShell::Execute(SfxRequest& rReq)
+{
+ switch (rReq.GetSlot())
+ {
+ case SID_TEXTMODE:
+ {
+ SmFormat aOldFormat = GetFormat();
+ SmFormat aNewFormat( aOldFormat );
+ aNewFormat.SetTextmode(!aOldFormat.IsTextmode());
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ break;
+
+ case SID_AUTO_REDRAW :
+ {
+ SmModule *pp = SM_MOD();
+ bool bRedraw = pp->GetConfig()->IsAutoRedraw();
+ pp->GetConfig()->SetAutoRedraw(!bRedraw);
+ }
+ break;
+
+ case SID_LOADSYMBOLS:
+ LoadSymbols();
+ break;
+
+ case SID_SAVESYMBOLS:
+ SaveSymbols();
+ break;
+
+ case SID_FONT:
+ {
+ // get device used to retrieve the FontList
+ OutputDevice *pDev = GetPrinter();
+ if (!pDev || pDev->GetFontFaceCollectionCount() == 0)
+ pDev = &SM_MOD()->GetDefaultVirtualDev();
+ OSL_ENSURE (pDev, "device for font list missing" );
+
+ SmFontTypeDialog aFontTypeDialog(rReq.GetFrameWeld(), pDev);
+
+ SmFormat aOldFormat = GetFormat();
+ aFontTypeDialog.ReadFrom( aOldFormat );
+ if (aFontTypeDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aFontTypeDialog.WriteTo(aNewFormat);
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_FONTSIZE:
+ {
+ SmFontSizeDialog aFontSizeDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aFontSizeDialog.ReadFrom( aOldFormat );
+ if (aFontSizeDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aFontSizeDialog.WriteTo(aNewFormat);
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_DISTANCE:
+ {
+ SmDistanceDialog aDistanceDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aDistanceDialog.ReadFrom( aOldFormat );
+ if (aDistanceDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aDistanceDialog.WriteTo(aNewFormat);
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_ALIGN:
+ {
+ SmAlignDialog aAlignDialog(rReq.GetFrameWeld());
+
+ SmFormat aOldFormat = GetFormat();
+ aAlignDialog.ReadFrom( aOldFormat );
+ if (aAlignDialog.run() == RET_OK)
+ {
+ SmFormat aNewFormat( aOldFormat );
+
+ aAlignDialog.WriteTo(aNewFormat);
+
+ SmModule *pp = SM_MOD();
+ SmFormat aFmt( pp->GetConfig()->GetStandardFormat() );
+ aAlignDialog.WriteTo( aFmt );
+ pp->GetConfig()->SetStandardFormat( aFmt );
+
+ SfxUndoManager *pTmpUndoMgr = GetUndoManager();
+ if (pTmpUndoMgr)
+ pTmpUndoMgr->AddUndoAction(
+ std::make_unique<SmFormatAction>(this, aOldFormat, aNewFormat));
+
+ SetFormat( aNewFormat );
+ Repaint();
+ }
+ }
+ break;
+
+ case SID_TEXT:
+ {
+ const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXT));
+ if (GetText() != rItem.GetValue())
+ SetText(rItem.GetValue());
+ }
+ break;
+
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ SfxUndoManager* pTmpUndoMgr = GetUndoManager();
+ if( pTmpUndoMgr )
+ {
+ sal_uInt16 nId = rReq.GetSlot(), nCnt = 1;
+ const SfxItemSet* pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+ if( pArgs && SfxItemState::SET == pArgs->GetItemState( nId, false, &pItem ))
+ nCnt = static_cast<const SfxUInt16Item*>(pItem)->GetValue();
+
+ bool (SfxUndoManager::*fnDo)();
+
+ size_t nCount;
+ if( SID_UNDO == rReq.GetSlot() )
+ {
+ nCount = pTmpUndoMgr->GetUndoActionCount();
+ fnDo = &SfxUndoManager::Undo;
+ }
+ else
+ {
+ nCount = pTmpUndoMgr->GetRedoActionCount();
+ fnDo = &SfxUndoManager::Redo;
+ }
+
+ try
+ {
+ for( ; nCnt && nCount; --nCnt, --nCount )
+ (pTmpUndoMgr->*fnDo)();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ }
+ Repaint();
+ UpdateText();
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ while( pFrm )
+ {
+ SfxBindings& rBind = pFrm->GetBindings();
+ rBind.Invalidate(SID_UNDO);
+ rBind.Invalidate(SID_REDO);
+ rBind.Invalidate(SID_REPEAT);
+ rBind.Invalidate(SID_CLEARHISTORY);
+ pFrm = SfxViewFrame::GetNext( *pFrm, this );
+ }
+ }
+ break;
+ }
+
+ rReq.Done();
+}
+
+
+void SmDocShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWh = aIter.FirstWhich(); 0 != nWh; nWh = aIter.NextWhich())
+ {
+ switch (nWh)
+ {
+ case SID_TEXTMODE:
+ rSet.Put(SfxBoolItem(SID_TEXTMODE, GetFormat().IsTextmode()));
+ break;
+
+ case SID_DOCTEMPLATE :
+ rSet.DisableItem(SID_DOCTEMPLATE);
+ break;
+
+ case SID_AUTO_REDRAW :
+ {
+ SmModule *pp = SM_MOD();
+ bool bRedraw = pp->GetConfig()->IsAutoRedraw();
+
+ rSet.Put(SfxBoolItem(SID_AUTO_REDRAW, bRedraw));
+ }
+ break;
+
+ case SID_MODIFYSTATUS:
+ {
+ sal_Unicode cMod = ' ';
+ if (IsModified())
+ cMod = '*';
+ rSet.Put(SfxStringItem(SID_MODIFYSTATUS, OUString(cMod)));
+ }
+ break;
+
+ case SID_TEXT:
+ rSet.Put(SfxStringItem(SID_TEXT, GetText()));
+ break;
+
+ case SID_GRAPHIC_SM:
+ //! very old (pre UNO) and ugly hack to invalidate the SmGraphicWidget.
+ //! If mnModifyCount gets changed then the call below will implicitly notify
+ //! SmGraphicController::StateChanged and there the window gets invalidated.
+ //! Thus all the 'mnModifyCount++' before invalidating this slot.
+ rSet.Put(SfxInt16Item(SID_GRAPHIC_SM, mnModifyCount));
+ break;
+
+ case SID_UNDO:
+ case SID_REDO:
+ {
+ SfxViewFrame* pFrm = SfxViewFrame::GetFirst( this );
+ if( pFrm )
+ pFrm->GetSlotState( nWh, nullptr, &rSet );
+ else
+ rSet.DisableItem( nWh );
+ }
+ break;
+
+ case SID_GETUNDOSTRINGS:
+ case SID_GETREDOSTRINGS:
+ {
+ SfxUndoManager* pTmpUndoMgr = GetUndoManager();
+ if( pTmpUndoMgr )
+ {
+ OUString(SfxUndoManager::*fnGetComment)( size_t, bool const ) const;
+
+ size_t nCount;
+ if( SID_GETUNDOSTRINGS == nWh )
+ {
+ nCount = pTmpUndoMgr->GetUndoActionCount();
+ fnGetComment = &SfxUndoManager::GetUndoActionComment;
+ }
+ else
+ {
+ nCount = pTmpUndoMgr->GetRedoActionCount();
+ fnGetComment = &SfxUndoManager::GetRedoActionComment;
+ }
+ if (nCount)
+ {
+ OUStringBuffer aBuf;
+ for (size_t n = 0; n < nCount; ++n)
+ {
+ aBuf.append((pTmpUndoMgr->*fnGetComment)( n, SfxUndoManager::TopLevel ));
+ aBuf.append('\n');
+ }
+
+ SfxStringListItem aItem( nWh );
+ aItem.SetString( aBuf.makeStringAndClear() );
+ rSet.Put( aItem );
+ }
+ }
+ else
+ rSet.DisableItem( nWh );
+ }
+ break;
+ }
+ }
+}
+
+
+SfxUndoManager *SmDocShell::GetUndoManager()
+{
+ if (!mpEditEngine)
+ GetEditEngine();
+ return &mpEditEngine->GetUndoManager();
+}
+
+
+void SmDocShell::SaveSymbols()
+{
+ SmModule *pp = SM_MOD();
+ pp->GetSymbolManager().Save();
+}
+
+
+void SmDocShell::Draw(OutputDevice *pDevice,
+ const JobSetup &,
+ sal_uInt16 /*nAspect*/)
+{
+ pDevice->IntersectClipRegion(GetVisArea());
+ Point atmppoint;
+ DrawFormula(*pDevice, atmppoint);
+}
+
+SfxItemPool& SmDocShell::GetPool()
+{
+ return SfxGetpApp()->GetPool();
+}
+
+void SmDocShell::SetVisArea(const tools::Rectangle & rVisArea)
+{
+ tools::Rectangle aNewRect(rVisArea);
+
+ aNewRect.SetPos(Point());
+
+ if (aNewRect.IsWidthEmpty())
+ aNewRect.SetRight( 2000 );
+ if (aNewRect.IsHeightEmpty())
+ aNewRect.SetBottom( 1000 );
+
+ bool bIsEnabled = IsEnableSetModified();
+ if ( bIsEnabled )
+ EnableSetModified( false );
+
+ //TODO/LATER: it's unclear how this interacts with the SFX code
+ // If outplace editing, then don't resize the OutplaceWindow. But the
+ // ObjectShell has to resize.
+ bool bUnLockFrame;
+ if( GetCreateMode() == SfxObjectCreateMode::EMBEDDED && !IsInPlaceActive() && GetFrame() )
+ {
+ GetFrame()->LockAdjustPosSizePixel();
+ bUnLockFrame = true;
+ }
+ else
+ bUnLockFrame = false;
+
+ SfxObjectShell::SetVisArea( aNewRect );
+
+ if( bUnLockFrame )
+ GetFrame()->UnlockAdjustPosSizePixel();
+
+ if ( bIsEnabled )
+ EnableSetModified( bIsEnabled );
+}
+
+
+void SmDocShell::FillClass(SvGlobalName* pClassName,
+ SotClipboardFormatId* pFormat,
+ OUString* pFullTypeName,
+ sal_Int32 nFileFormat,
+ bool bTemplate /* = false */) const
+{
+ if (nFileFormat == SOFFICE_FILEFORMAT_60 )
+ {
+ *pClassName = SvGlobalName(SO3_SM_CLASSID_60);
+ *pFormat = SotClipboardFormatId::STARMATH_60;
+ *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
+ }
+ else if (nFileFormat == SOFFICE_FILEFORMAT_8 )
+ {
+ *pClassName = SvGlobalName(SO3_SM_CLASSID_60);
+ *pFormat = bTemplate ? SotClipboardFormatId::STARMATH_8_TEMPLATE : SotClipboardFormatId::STARMATH_8;
+ *pFullTypeName = SmResId(STR_MATH_DOCUMENT_FULLTYPE_CURRENT);
+ }
+}
+
+void SmDocShell::SetModified(bool bModified)
+{
+ if( IsEnableSetModified() )
+ {
+ SfxObjectShell::SetModified( bModified );
+ Broadcast(SfxHint(SfxHintId::DocChanged));
+ }
+}
+
+bool SmDocShell::WriteAsMathType3( SfxMedium& rMedium )
+{
+ OUStringBuffer aTextAsBuffer(maText);
+ MathType aEquation(aTextAsBuffer, mpTree.get());
+ return aEquation.ConvertFromStarMath( rMedium );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/edit.cxx b/starmath/source/edit.cxx
new file mode 100644
index 000000000..1c92b03fb
--- /dev/null
+++ b/starmath/source/edit.cxx
@@ -0,0 +1,863 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <starmath.hrc>
+#include <helpids.h>
+
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/settings.hxx>
+
+#include <editeng/editview.hxx>
+#include <editeng/editeng.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/stritem.hxx>
+#include <sfx2/viewfrm.hxx>
+#include <osl/diagnose.h>
+#include <o3tl/string_view.hxx>
+
+#include <edit.hxx>
+#include <smmod.hxx>
+#include <view.hxx>
+#include <document.hxx>
+#include <cfgitem.hxx>
+#include <smediteng.hxx>
+
+using namespace com::sun::star::accessibility;
+using namespace com::sun::star;
+
+
+void SmGetLeftSelectionPart(const ESelection &rSel,
+ sal_Int32 &nPara, sal_uInt16 &nPos)
+ // returns paragraph number and position of the selections left part
+{
+ // compare start and end of selection and use the one that comes first
+ if ( rSel.nStartPara < rSel.nEndPara
+ || (rSel.nStartPara == rSel.nEndPara && rSel.nStartPos < rSel.nEndPos) )
+ { nPara = rSel.nStartPara;
+ nPos = rSel.nStartPos;
+ }
+ else
+ { nPara = rSel.nEndPara;
+ nPos = rSel.nEndPos;
+ }
+}
+
+SmEditTextWindow::SmEditTextWindow(SmEditWindow& rEditWindow)
+ : mrEditWindow(rEditWindow)
+ , aModifyIdle("SmEditWindow ModifyIdle")
+ , aCursorMoveIdle("SmEditWindow CursorMoveIdle")
+{
+ SetAcceptsTab(true);
+
+ aModifyIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, ModifyTimerHdl));
+ aModifyIdle.SetPriority(TaskPriority::LOWEST);
+
+ if (!SmViewShell::IsInlineEditEnabled())
+ {
+ aCursorMoveIdle.SetInvokeHandler(LINK(this, SmEditTextWindow, CursorMoveTimerHdl));
+ aCursorMoveIdle.SetPriority(TaskPriority::LOWEST);
+ }
+}
+
+SmEditTextWindow::~SmEditTextWindow()
+{
+ aModifyIdle.Stop();
+ StartCursorMove();
+}
+
+EditEngine* SmEditTextWindow::GetEditEngine() const
+{
+ SmDocShell *pDoc = mrEditWindow.GetDoc();
+ assert(pDoc);
+ return &pDoc->GetEditEngine();
+}
+
+void SmEditTextWindow::EditViewScrollStateChange()
+{
+ mrEditWindow.SetScrollBarRanges();
+}
+
+void SmEditTextWindow::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+ Color aBgColor = rStyleSettings.GetWindowColor();
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+ rDevice.SetBackground(aBgColor);
+
+ SetHelpId(HID_SMA_COMMAND_WIN_EDIT);
+
+ EnableRTL(false);
+
+ EditEngine* pEditEngine = GetEditEngine();
+
+ m_xEditView.reset(new EditView(pEditEngine, nullptr));
+ m_xEditView->setEditViewCallbacks(this);
+
+ pEditEngine->InsertView(m_xEditView.get());
+
+ m_xEditView->SetOutputArea(mrEditWindow.AdjustScrollBars());
+
+ m_xEditView->SetBackgroundColor(aBgColor);
+
+ pDrawingArea->set_cursor(PointerStyle::Text);
+
+ pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl));
+
+ InitAccessible();
+
+ //Apply zoom to smeditwindow text
+ if(GetEditView())
+ static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
+}
+
+SmEditWindow::SmEditWindow(SmCmdBoxWindow &rMyCmdBoxWin, weld::Builder& rBuilder)
+ : rCmdBox(rMyCmdBoxWin)
+ , mxScrolledWindow(rBuilder.weld_scrolled_window("scrolledwindow", true))
+{
+ mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmEditWindow, ScrollHdl));
+
+ CreateEditView(rBuilder);
+}
+
+SmEditWindow::~SmEditWindow() COVERITY_NOEXCEPT_FALSE
+{
+ DeleteEditView();
+ mxScrolledWindow.reset();
+}
+
+weld::Window* SmEditWindow::GetFrameWeld() const
+{
+ return rCmdBox.GetFrameWeld();
+}
+
+void SmEditTextWindow::StartCursorMove()
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ aCursorMoveIdle.Stop();
+}
+
+void SmEditWindow::InvalidateSlots()
+{
+ SfxBindings& rBind = GetView()->GetViewFrame()->GetBindings();
+ rBind.Invalidate(SID_COPY);
+ rBind.Invalidate(SID_CUT);
+ rBind.Invalidate(SID_DELETE);
+}
+
+SmViewShell * SmEditWindow::GetView()
+{
+ return rCmdBox.GetView();
+}
+
+SmDocShell * SmEditWindow::GetDoc()
+{
+ SmViewShell *pView = rCmdBox.GetView();
+ return pView ? pView->GetDoc() : nullptr;
+}
+
+EditView * SmEditWindow::GetEditView() const
+{
+ return mxTextControl ? mxTextControl->GetEditView() : nullptr;
+}
+
+EditEngine * SmEditWindow::GetEditEngine()
+{
+ if (SmDocShell *pDoc = GetDoc())
+ return &pDoc->GetEditEngine();
+ return nullptr;
+}
+
+void SmEditTextWindow::StyleUpdated()
+{
+ WeldEditView::StyleUpdated();
+ EditEngine *pEditEngine = GetEditEngine();
+ SmDocShell *pDoc = mrEditWindow.GetDoc();
+
+ if (pEditEngine && pDoc)
+ {
+ //!
+ //! see also SmDocShell::GetEditEngine() !
+ //!
+ const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings();
+
+ pDoc->UpdateEditEngineDefaultFonts();
+ pEditEngine->SetBackgroundColor(rStyleSettings.GetFieldColor());
+ pEditEngine->SetDefTab(sal_uInt16(GetTextWidth("XXXX")));
+
+ // forces new settings to be used
+ // unfortunately this resets the whole edit engine
+ // thus we need to save at least the text
+ OUString aTxt( pEditEngine->GetText() );
+ pEditEngine->Clear(); //incorrect font size
+ pEditEngine->SetText( aTxt );
+
+ Resize();
+ }
+
+ // Apply zoom to smeditwindow text
+ static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
+}
+
+IMPL_LINK_NOARG(SmEditTextWindow, ModifyTimerHdl, Timer *, void)
+{
+ UpdateStatus(false);
+ aModifyIdle.Stop();
+}
+
+IMPL_LINK_NOARG(SmEditTextWindow, CursorMoveTimerHdl, Timer *, void)
+ // every once in a while check cursor position (selection) of edit
+ // window and if it has changed (try to) set the formula-cursor
+ // according to that.
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return;
+
+ ESelection aNewSelection(GetSelection());
+
+ if (aNewSelection != aOldSelection)
+ {
+ if (SmViewShell *pViewSh = mrEditWindow.GetView())
+ {
+ // get row and column to look for
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ SmGetLeftSelectionPart(aNewSelection, nRow, nCol);
+ pViewSh->GetGraphicWidget().SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
+ aOldSelection = aNewSelection;
+ }
+ }
+ aCursorMoveIdle.Stop();
+}
+
+bool SmEditTextWindow::MouseButtonUp(const MouseEvent &rEvt)
+{
+ bool bRet = WeldEditView::MouseButtonUp(rEvt);
+ if (!SmViewShell::IsInlineEditEnabled())
+ CursorMoveTimerHdl(&aCursorMoveIdle);
+ mrEditWindow.InvalidateSlots();
+ return bRet;
+}
+
+bool SmEditTextWindow::Command(const CommandEvent& rCEvt)
+{
+ // no zooming in Command window
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if (pWData && CommandWheelMode::ZOOM == pWData->GetMode())
+ return true;
+
+ //pass alt press/release to parent impl
+ if (rCEvt.GetCommand() == CommandEventId::ModKeyChange)
+ return false;
+
+ if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+ {
+ ReleaseMouse();
+ SmCmdBoxWindow& rCmdBox = mrEditWindow.GetCmdBox();
+ rCmdBox.ShowContextMenu(rCmdBox.WidgetToWindowPos(*GetDrawingArea(), rCEvt.GetMousePosPixel()));
+ GrabFocus();
+ return true;
+ }
+
+ bool bConsumed = WeldEditView::Command(rCEvt);
+ if (bConsumed)
+ UserPossiblyChangedText();
+ return bConsumed;
+}
+
+bool SmEditTextWindow::KeyInput(const KeyEvent& rKEvt)
+{
+ if (rKEvt.GetKeyCode().GetCode() == KEY_ESCAPE)
+ {
+ bool bCallBase = true;
+ SfxViewShell* pViewShell = mrEditWindow.GetView();
+ if ( dynamic_cast<const SmViewShell *>(pViewShell) )
+ {
+ // Terminate possible InPlace mode
+ bCallBase = !pViewShell->Escape();
+ }
+ return !bCallBase;
+ }
+
+ StartCursorMove();
+
+ bool autoClose = false;
+ EditView* pEditView = GetEditView();
+ ESelection aSelection = pEditView->GetSelection();
+ // as we don't support RTL in Math, we need to swap values from selection when they were done
+ // in RTL form
+ aSelection.Adjust();
+ OUString selected = pEditView->GetEditEngine()->GetText(aSelection);
+
+ // Check is auto close brackets/braces is disabled
+ SmModule *pMod = SM_MOD();
+ if (pMod && !pMod->GetConfig()->IsAutoCloseBrackets())
+ autoClose = false;
+ else if (o3tl::trim(selected) == u"<?>")
+ autoClose = true;
+ else if (selected.isEmpty() && !aSelection.HasRange())
+ {
+ selected = pEditView->GetEditEngine()->GetText(aSelection.nEndPara);
+ if (!selected.isEmpty())
+ {
+ sal_Int32 index = selected.indexOf("\n", aSelection.nEndPos);
+ if (index != -1)
+ {
+ selected = selected.copy(index, sal_Int32(aSelection.nEndPos-index));
+ if (selected.trim().isEmpty())
+ autoClose = true;
+ }
+ else
+ {
+ sal_Int32 length = selected.getLength();
+ if (aSelection.nEndPos == length)
+ autoClose = true;
+ else
+ {
+ selected = selected.copy(aSelection.nEndPos);
+ if (selected.trim().isEmpty())
+ autoClose = true;
+ }
+ }
+ }
+ else
+ autoClose = true;
+ }
+
+ bool bConsumed = WeldEditView::KeyInput(rKEvt);
+ if (!bConsumed)
+ {
+ SmViewShell *pView = mrEditWindow.GetView();
+ if (pView)
+ bConsumed = pView->KeyInput(rKEvt);
+ if (pView && !bConsumed)
+ {
+ // F1 (help) leads to the destruction of this
+ Flush();
+ if ( aModifyIdle.IsActive() )
+ aModifyIdle.Stop();
+ }
+ else
+ {
+ // SFX has maybe called a slot of the view and thus (because of a hack in SFX)
+ // set the focus to the view
+ SmViewShell* pVShell = mrEditWindow.GetView();
+ if ( pVShell && pVShell->GetGraphicWidget().HasFocus() )
+ {
+ GrabFocus();
+ }
+ }
+ }
+ else
+ {
+ UserPossiblyChangedText();
+ }
+
+ // get the current char of the key event
+ sal_Unicode cCharCode = rKEvt.GetCharCode();
+ OUString sClose;
+
+ if (cCharCode == '{')
+ sClose = " }";
+ else if (cCharCode == '[')
+ sClose = " ]";
+ else if (cCharCode == '(')
+ sClose = " )";
+
+ // auto close the current character only when needed
+ if (!sClose.isEmpty() && autoClose)
+ {
+ pEditView->InsertText(sClose);
+ // position it at center of brackets
+ aSelection.nStartPos += 2;
+ aSelection.nEndPos = aSelection.nStartPos;
+ pEditView->SetSelection(aSelection);
+ }
+
+ mrEditWindow.InvalidateSlots();
+ return bConsumed;
+}
+
+void SmEditTextWindow::UserPossiblyChangedText()
+{
+ // have doc-shell modified only for formula input/change and not
+ // cursor travelling and such things...
+ SmDocShell *pDocShell = mrEditWindow.GetDoc();
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pDocShell && pEditEngine && pEditEngine->IsModified())
+ pDocShell->SetModified(true);
+ aModifyIdle.Start();
+}
+
+void SmEditWindow::CreateEditView(weld::Builder& rBuilder)
+{
+ assert(!mxTextControl);
+
+ EditEngine *pEditEngine = GetEditEngine();
+ //! pEditEngine may be 0.
+ //! For example when the program is used by the document-converter
+ if (!pEditEngine)
+ return;
+
+ mxTextControl.reset(new SmEditTextWindow(*this));
+ mxTextControlWin.reset(new weld::CustomWeld(rBuilder, "editview", *mxTextControl));
+
+ SetScrollBarRanges();
+}
+
+IMPL_LINK_NOARG(SmEditTextWindow, EditStatusHdl, EditStatus&, void)
+{
+ Resize();
+}
+
+IMPL_LINK(SmEditWindow, ScrollHdl, weld::ScrolledWindow&, rScrolledWindow, void)
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ pEditView->SetVisArea(tools::Rectangle(
+ Point(0,
+ rScrolledWindow.vadjustment_get_value()),
+ pEditView->GetVisArea().GetSize()));
+ pEditView->Invalidate();
+ }
+}
+
+tools::Rectangle SmEditWindow::AdjustScrollBars()
+{
+ tools::Rectangle aRect(Point(), rCmdBox.GetOutputSizePixel());
+
+ if (mxScrolledWindow)
+ {
+ const auto nScrollSize = mxScrolledWindow->get_scroll_thickness();
+ const auto nMargin = nScrollSize + 2;
+ aRect.AdjustRight(-nMargin);
+ aRect.AdjustBottom(-nMargin);
+ }
+
+ return aRect;
+}
+
+void SmEditWindow::SetScrollBarRanges()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (!pEditEngine)
+ return;
+ if (!mxScrolledWindow)
+ return;
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return;
+
+ int nVUpper = pEditEngine->GetTextHeight();
+ int nVCurrentDocPos = pEditView->GetVisArea().Top();
+ const Size aOut(pEditView->GetOutputArea().GetSize());
+ int nVStepIncrement = aOut.Height() * 2 / 10;
+ int nVPageIncrement = aOut.Height() * 8 / 10;
+ int nVPageSize = aOut.Height();
+
+ /* limit the page size to below nUpper because gtk's gtk_scrolled_window_start_deceleration has
+ effectively...
+
+ lower = gtk_adjustment_get_lower
+ upper = gtk_adjustment_get_upper - gtk_adjustment_get_page_size
+
+ and requires that upper > lower or the deceleration animation never ends
+ */
+ nVPageSize = std::min(nVPageSize, nVUpper);
+
+ mxScrolledWindow->vadjustment_configure(nVCurrentDocPos, 0, nVUpper,
+ nVStepIncrement, nVPageIncrement, nVPageSize);
+}
+
+OUString SmEditWindow::GetText() const
+{
+ OUString aText;
+ EditEngine *pEditEngine = const_cast< SmEditWindow* >(this)->GetEditEngine();
+ OSL_ENSURE( pEditEngine, "EditEngine missing" );
+ if (pEditEngine)
+ aText = pEditEngine->GetText();
+ return aText;
+}
+
+void SmEditWindow::SetText(const OUString& rText)
+{
+ if (!mxTextControl)
+ return;
+ mxTextControl->SetText(rText);
+}
+
+void SmEditWindow::Flush()
+{
+ if (!mxTextControl)
+ return;
+ mxTextControl->Flush();
+}
+
+void SmEditWindow::GrabFocus()
+{
+ if (!mxTextControl)
+ return;
+ mxTextControl->GrabFocus();
+}
+
+void SmEditTextWindow::SetText(const OUString& rText)
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ OSL_ENSURE( pEditEngine, "EditEngine missing" );
+ if (!pEditEngine || pEditEngine->IsModified())
+ return;
+
+ EditView* pEditView = GetEditView();
+ ESelection eSelection = pEditView->GetSelection();
+
+ pEditEngine->SetText(rText);
+ pEditEngine->ClearModifyFlag();
+
+ // Restarting the timer here, prevents calling the handlers for other (currently inactive)
+ // math tasks
+ aModifyIdle.Start();
+
+ // Apply zoom to smeditwindow text
+ static_cast<SmEditEngine*>(pEditView->GetEditEngine())->executeZoom(pEditView);
+ pEditView->SetSelection(eSelection);
+}
+
+void SmEditTextWindow::GetFocus()
+{
+ WeldEditView::GetFocus();
+
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetStatusEventHdl(LINK(this, SmEditTextWindow, EditStatusHdl));
+
+ //Let SmViewShell know we got focus
+ if (mrEditWindow.GetView() && SmViewShell::IsInlineEditEnabled())
+ mrEditWindow.GetView()->SetInsertIntoEditWindow(true);
+}
+
+void SmEditTextWindow::LoseFocus()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine)
+ pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+
+ WeldEditView::LoseFocus();
+}
+
+bool SmEditWindow::IsAllSelected() const
+{
+ EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
+ if (!pEditEngine)
+ return false;
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return false;
+ bool bRes = false;
+ ESelection eSelection( pEditView->GetSelection() );
+ sal_Int32 nParaCnt = pEditEngine->GetParagraphCount();
+ if (!(nParaCnt - 1))
+ {
+ sal_Int32 nTextLen = pEditEngine->GetText().getLength();
+ bRes = !eSelection.nStartPos && (eSelection.nEndPos == nTextLen - 1);
+ }
+ else
+ {
+ bRes = !eSelection.nStartPara && (eSelection.nEndPara == nParaCnt - 1);
+ }
+ return bRes;
+}
+
+void SmEditWindow::SelectAll()
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ // ALL as last two parameters refers to the end of the text
+ pEditView->SetSelection( ESelection( 0, 0, EE_PARA_ALL, EE_TEXTPOS_ALL ) );
+ }
+}
+
+void SmEditWindow::MarkError(const Point &rPos)
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ const sal_uInt16 nCol = sal::static_int_cast< sal_uInt16 >(rPos.X());
+ const sal_uInt16 nRow = sal::static_int_cast< sal_uInt16 >(rPos.Y() - 1);
+
+ pEditView->SetSelection(ESelection(nRow, nCol - 1, nRow, nCol));
+ GrabFocus();
+ }
+}
+
+void SmEditWindow::SelNextMark()
+{
+ if (!mxTextControl)
+ return;
+ mxTextControl->SelNextMark();
+}
+
+// Makes selection to next <?> symbol
+void SmEditTextWindow::SelNextMark()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (!pEditEngine)
+ return;
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return;
+
+ ESelection eSelection = pEditView->GetSelection();
+ sal_Int32 nPos = eSelection.nEndPos;
+ sal_Int32 nCounts = pEditEngine->GetParagraphCount();
+
+ while (eSelection.nEndPara < nCounts)
+ {
+ OUString aText = pEditEngine->GetText(eSelection.nEndPara);
+ nPos = aText.indexOf("<?>", nPos);
+ if (nPos != -1)
+ {
+ pEditView->SetSelection(ESelection(
+ eSelection.nEndPara, nPos, eSelection.nEndPara, nPos + 3));
+ break;
+ }
+
+ nPos = 0;
+ eSelection.nEndPara++;
+ }
+}
+
+void SmEditWindow::SelPrevMark()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (!pEditEngine)
+ return;
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return;
+
+ ESelection eSelection = pEditView->GetSelection();
+ sal_Int32 nPara = eSelection.nStartPara;
+ sal_Int32 nMax = eSelection.nStartPos;
+ OUString aText(pEditEngine->GetText(nPara));
+ static const OUStringLiteral aMark(u"<?>");
+ sal_Int32 nPos;
+
+ while ( (nPos = aText.lastIndexOf(aMark, nMax)) < 0 )
+ {
+ if (--nPara < 0)
+ return;
+ aText = pEditEngine->GetText(nPara);
+ nMax = aText.getLength();
+ }
+ pEditView->SetSelection(ESelection(nPara, nPos, nPara, nPos + 3));
+}
+
+// returns true iff 'rText' contains a mark
+static bool HasMark(std::u16string_view rText)
+{
+ return rText.find(u"<?>") != std::u16string_view::npos;
+}
+
+ESelection SmEditWindow::GetSelection() const
+{
+ if (mxTextControl)
+ return mxTextControl->GetSelection();
+ return ESelection();
+}
+
+ESelection SmEditTextWindow::GetSelection() const
+{
+ // pointer may be 0 when reloading a document and the old view
+ // was already destroyed
+ if (EditView* pEditView = GetEditView())
+ return pEditView->GetSelection();
+ return ESelection();
+}
+
+void SmEditWindow::SetSelection(const ESelection &rSel)
+{
+ if (EditView* pEditView = GetEditView())
+ pEditView->SetSelection(rSel);
+ InvalidateSlots();
+}
+
+bool SmEditWindow::IsEmpty() const
+{
+ EditEngine *pEditEngine = const_cast<SmEditWindow *>(this)->GetEditEngine();
+ bool bEmpty = ( pEditEngine && pEditEngine->GetTextLen() == 0 );
+ return bEmpty;
+}
+
+bool SmEditWindow::IsSelected() const
+{
+ EditView* pEditView = GetEditView();
+ return pEditView && pEditView->HasSelection();
+}
+
+void SmEditTextWindow::UpdateStatus(bool bSetDocModified)
+{
+ SmModule *pMod = SM_MOD();
+ if (pMod && pMod->GetConfig()->IsAutoRedraw())
+ Flush();
+
+ if (SmDocShell *pModifyDoc = bSetDocModified ? mrEditWindow.GetDoc() : nullptr)
+ pModifyDoc->SetModified();
+
+ static_cast<SmEditEngine*>(GetEditEngine())->executeZoom(GetEditView());
+}
+
+void SmEditWindow::UpdateStatus()
+{
+ mxTextControl->UpdateStatus(/*bSetDocModified*/false);
+}
+
+void SmEditWindow::Cut()
+{
+ if (mxTextControl)
+ {
+ mxTextControl->Cut();
+ mxTextControl->UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::Copy()
+{
+ if (mxTextControl)
+ mxTextControl->Copy();
+}
+
+void SmEditWindow::Paste()
+{
+ if (mxTextControl)
+ {
+ mxTextControl->Paste();
+ mxTextControl->UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::Delete()
+{
+ if (mxTextControl)
+ {
+ mxTextControl->Delete();
+ mxTextControl->UpdateStatus(true);
+ }
+}
+
+void SmEditWindow::InsertText(const OUString& rText)
+{
+ if (!mxTextControl)
+ return;
+ mxTextControl->InsertText(rText);
+}
+
+void SmEditTextWindow::InsertText(const OUString& rText)
+{
+ EditView* pEditView = GetEditView();
+ if (!pEditView)
+ return;
+
+ // Note: Insertion of a space in front of commands is done here and
+ // in SmEditWindow::InsertCommand.
+ ESelection aSelection = pEditView->GetSelection();
+ OUString aCurrentFormula = pEditView->GetEditEngine()->GetText();
+ sal_Int32 nStartIndex = 0;
+
+ // get the start position (when we get a multi line formula)
+ for (sal_Int32 nParaPos = 0; nParaPos < aSelection.nStartPara; nParaPos++)
+ nStartIndex = aCurrentFormula.indexOf("\n", nStartIndex) + 1;
+
+ nStartIndex += aSelection.nStartPos;
+
+ // TODO: unify this function with the InsertCommand: The do the same thing for different
+ // callers
+ OUString string(rText);
+
+ OUString selected(pEditView->GetSelected());
+ // if we have text selected, use it in the first placeholder
+ if (!selected.isEmpty())
+ string = string.replaceFirst("<?>", selected);
+
+ // put a space before a new command if not in the beginning of a line
+ if (aSelection.nStartPos > 0 && aCurrentFormula[nStartIndex - 1] != ' ')
+ string = " " + string;
+
+ pEditView->InsertText(string);
+
+ // Remember start of the selection and move the cursor there afterwards.
+ aSelection.nEndPara = aSelection.nStartPara;
+ if (HasMark(string))
+ {
+ aSelection.nEndPos = aSelection.nStartPos;
+ pEditView->SetSelection(aSelection);
+ SelNextMark();
+ }
+ else
+ { // set selection after inserted text
+ aSelection.nEndPos = aSelection.nStartPos + string.getLength();
+ aSelection.nStartPos = aSelection.nEndPos;
+ pEditView->SetSelection(aSelection);
+ }
+
+ aModifyIdle.Start();
+ StartCursorMove();
+
+ GrabFocus();
+}
+
+void SmEditTextWindow::Flush()
+{
+ EditEngine *pEditEngine = GetEditEngine();
+ if (pEditEngine && pEditEngine->IsModified())
+ {
+ pEditEngine->ClearModifyFlag();
+ if (SmViewShell *pViewSh = mrEditWindow.GetView())
+ {
+ std::unique_ptr<SfxStringItem> pTextToFlush = std::make_unique<SfxStringItem>(SID_TEXT, GetText());
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_TEXT, SfxCallMode::RECORD,
+ { pTextToFlush.get() });
+ }
+ }
+ if (aCursorMoveIdle.IsActive())
+ {
+ aCursorMoveIdle.Stop();
+ CursorMoveTimerHdl(&aCursorMoveIdle);
+ }
+}
+
+void SmEditWindow::DeleteEditView()
+{
+ if (EditView* pEditView = GetEditView())
+ {
+ if (EditEngine* pEditEngine = pEditView->GetEditEngine())
+ {
+ pEditEngine->SetStatusEventHdl( Link<EditStatus&,void>() );
+ pEditEngine->RemoveView(pEditView);
+ }
+ mxTextControlWin.reset();
+ mxTextControl.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/eqnolefilehdr.cxx b/starmath/source/eqnolefilehdr.cxx
new file mode 100644
index 000000000..f211f1aaa
--- /dev/null
+++ b/starmath/source/eqnolefilehdr.cxx
@@ -0,0 +1,53 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "eqnolefilehdr.hxx"
+#include <sot/storage.hxx>
+
+
+bool GetMathTypeVersion( SotStorage* pStor, sal_uInt8 &nVersion )
+{
+ sal_uInt8 nVer = 0;
+ bool bSuccess = false;
+
+
+ // code snippet copied from MathType::Parse
+
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream(
+ "Equation Native",
+ StreamMode::STD_READ);
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return bSuccess;
+ SotStorageStream *pS = xSrc.get();
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ EQNOLEFILEHDR aHdr;
+ aHdr.Read(pS);
+ pS->ReadUChar( nVer );
+
+ if (!pS->GetError())
+ {
+ nVersion = nVer;
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/eqnolefilehdr.hxx b/starmath/source/eqnolefilehdr.hxx
new file mode 100644
index 000000000..7fd3a476a
--- /dev/null
+++ b/starmath/source/eqnolefilehdr.hxx
@@ -0,0 +1,74 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <sal/types.h>
+#include <tools/stream.hxx>
+
+class SotStorage;
+
+#define EQNOLEFILEHDR_SIZE 28
+
+class EQNOLEFILEHDR
+{
+public:
+ EQNOLEFILEHDR() : nCBHdr(0),nVersion(0),
+ nCf(0),nCBObject(0),nReserved1(0),nReserved2(0),
+ nReserved3(0), nReserved4(0) {}
+ explicit EQNOLEFILEHDR(sal_uInt32 nLenMTEF) : nCBHdr(0x1c),nVersion(0x20000),
+ nCf(0xc1c6),nCBObject(nLenMTEF),nReserved1(0),nReserved2(0x0014F690),
+ nReserved3(0x0014EBB4), nReserved4(0) {}
+
+ sal_uInt16 nCBHdr; // length of header, sizeof(EQNOLEFILEHDR) = 28
+ sal_uInt32 nVersion; // hiword = 2, loword = 0
+ sal_uInt16 nCf; // clipboard format ("MathType EF")
+ sal_uInt32 nCBObject; // length of MTEF data following this header
+ sal_uInt32 nReserved1; // not used
+ sal_uInt32 nReserved2; // not used
+ sal_uInt32 nReserved3; // not used
+ sal_uInt32 nReserved4; // not used
+
+ void Read(SvStream* pS)
+ {
+ pS->ReadUInt16( nCBHdr );
+ pS->ReadUInt32( nVersion );
+ pS->ReadUInt16( nCf );
+ pS->ReadUInt32( nCBObject );
+ pS->ReadUInt32( nReserved1 );
+ pS->ReadUInt32( nReserved2 );
+ pS->ReadUInt32( nReserved3 );
+ pS->ReadUInt32( nReserved4 );
+ }
+ void Write(SvStream* pS)
+ {
+ pS->WriteUInt16( nCBHdr );
+ pS->WriteUInt32( nVersion );
+ pS->WriteUInt16( nCf );
+ pS->WriteUInt32( nCBObject );
+ pS->WriteUInt32( nReserved1 );
+ pS->WriteUInt32( nReserved2 );
+ pS->WriteUInt32( nReserved3 );
+ pS->WriteUInt32( nReserved4 );
+ }
+};
+
+bool GetMathTypeVersion( SotStorage* pStor, sal_uInt8 &nVersion );
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/format.cxx b/starmath/source/format.cxx
new file mode 100644
index 000000000..65ec09558
--- /dev/null
+++ b/starmath/source/format.cxx
@@ -0,0 +1,153 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <format.hxx>
+
+
+SmFormat::SmFormat()
+: aBaseSize(0, SmPtsTo100th_mm(12))
+{
+ eHorAlign = SmHorAlign::Center;
+ nGreekCharStyle = 0;
+ bIsTextmode = bScaleNormalBrackets = false;
+
+ vSize[SIZ_TEXT] = 100;
+ vSize[SIZ_INDEX] = 60;
+ vSize[SIZ_FUNCTION] =
+ vSize[SIZ_OPERATOR] = 100;
+ vSize[SIZ_LIMITS] = 60;
+
+ vDist[DIS_HORIZONTAL] = 10;
+ vDist[DIS_VERTICAL] = 5;
+ vDist[DIS_ROOT] = 0;
+ vDist[DIS_SUPERSCRIPT] =
+ vDist[DIS_SUBSCRIPT] = 20;
+ vDist[DIS_NUMERATOR] =
+ vDist[DIS_DENOMINATOR] = 0;
+ vDist[DIS_FRACTION] = 10;
+ vDist[DIS_STROKEWIDTH] = 5;
+ vDist[DIS_UPPERLIMIT] =
+ vDist[DIS_LOWERLIMIT] = 0;
+ vDist[DIS_BRACKETSIZE] =
+ vDist[DIS_BRACKETSPACE] = 5;
+ vDist[DIS_MATRIXROW] = 3;
+ vDist[DIS_MATRIXCOL] = 30;
+ vDist[DIS_ORNAMENTSIZE] =
+ vDist[DIS_ORNAMENTSPACE] = 0;
+ vDist[DIS_OPERATORSIZE] = 50;
+ vDist[DIS_OPERATORSPACE] = 20;
+ vDist[DIS_LEFTSPACE] =
+ vDist[DIS_RIGHTSPACE] = 0;
+ vDist[DIS_TOPSPACE] =
+ vDist[DIS_BOTTOMSPACE] =
+ vDist[DIS_NORMALBRACKETSIZE] = 0;
+
+ vFont[FNT_VARIABLE] =
+ vFont[FNT_FUNCTION] =
+ vFont[FNT_NUMBER] =
+ vFont[FNT_TEXT] =
+ vFont[FNT_SERIF] = SmFace(FNTNAME_TIMES, aBaseSize);
+ vFont[FNT_SANS] = SmFace(FNTNAME_HELV, aBaseSize);
+ vFont[FNT_FIXED] = SmFace(FNTNAME_COUR, aBaseSize);
+ vFont[FNT_MATH] = SmFace(FNTNAME_MATH, aBaseSize);
+
+ vFont[FNT_MATH].SetCharSet( RTL_TEXTENCODING_UNICODE );
+
+ vFont[FNT_VARIABLE].SetItalic(ITALIC_NORMAL);
+ vFont[FNT_FUNCTION].SetItalic(ITALIC_NONE);
+ vFont[FNT_NUMBER] .SetItalic(ITALIC_NONE);
+ vFont[FNT_TEXT] .SetItalic(ITALIC_NONE);
+ vFont[FNT_SERIF] .SetItalic(ITALIC_NONE);
+ vFont[FNT_SANS] .SetItalic(ITALIC_NONE);
+ vFont[FNT_FIXED] .SetItalic(ITALIC_NONE);
+
+ for ( sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++ )
+ {
+ SmFace &rFace = vFont[i];
+ rFace.SetTransparent( true );
+ rFace.SetAlignment( ALIGN_BASELINE );
+ rFace.SetColor( COL_AUTO );
+ bDefaultFont[i] = false;
+ }
+}
+
+
+void SmFormat::SetFont(sal_uInt16 nIdent, const SmFace &rFont, bool bDefault )
+{
+ vFont[nIdent] = rFont;
+ vFont[nIdent].SetTransparent( true );
+ vFont[nIdent].SetAlignment( ALIGN_BASELINE );
+
+ bDefaultFont[nIdent] = bDefault;
+}
+
+SmFormat & SmFormat::operator = (const SmFormat &rFormat)
+{
+ SetBaseSize(rFormat.GetBaseSize());
+ SetHorAlign(rFormat.GetHorAlign());
+ SetTextmode(rFormat.IsTextmode());
+ SetGreekCharStyle(rFormat.GetGreekCharStyle());
+ SetScaleNormalBrackets(rFormat.IsScaleNormalBrackets());
+
+ sal_uInt16 i;
+ for (i = FNT_BEGIN; i <= FNT_END; i++)
+ {
+ SetFont(i, rFormat.GetFont(i));
+ SetDefaultFont(i, rFormat.IsDefaultFont(i));
+ }
+ for (i = SIZ_BEGIN; i <= SIZ_END; i++)
+ SetRelSize(i, rFormat.GetRelSize(i));
+ for (i = DIS_BEGIN; i <= DIS_END; i++)
+ SetDistance(i, rFormat.GetDistance(i));
+
+ return *this;
+}
+
+
+bool SmFormat::operator == (const SmFormat &rFormat) const
+{
+ bool bRes = aBaseSize == rFormat.aBaseSize &&
+ eHorAlign == rFormat.eHorAlign &&
+ nGreekCharStyle == rFormat.nGreekCharStyle &&
+ bIsTextmode == rFormat.bIsTextmode &&
+ bScaleNormalBrackets == rFormat.bScaleNormalBrackets;
+
+ sal_uInt16 i;
+ for (i = 0; i <= SIZ_END && bRes; ++i)
+ {
+ if (vSize[i] != rFormat.vSize[i])
+ bRes = false;
+ }
+ for (i = 0; i <= DIS_END && bRes; ++i)
+ {
+ if (vDist[i] != rFormat.vDist[i])
+ bRes = false;
+ }
+ for (i = 0; i <= FNT_END && bRes; ++i)
+ {
+ if (vFont[i] != rFormat.vFont[i] ||
+ bDefaultFont[i] != rFormat.bDefaultFont[i])
+ bRes = false;
+ }
+
+ return bRes;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathml/attribute.cxx b/starmath/source/mathml/attribute.cxx
new file mode 100644
index 000000000..a1be708ae
--- /dev/null
+++ b/starmath/source/mathml/attribute.cxx
@@ -0,0 +1,475 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <mathml/attribute.hxx>
+
+void SmMlAttribute::clearPreviousAttributeValue()
+{
+ switch (m_aSmMlAttributeValueType)
+ {
+ case SmMlAttributeValueType::NMlEmpty:
+ break;
+ case SmMlAttributeValueType::MlHref:
+ if (m_aAttributeValue.m_aHref.m_aLnk)
+ delete m_aAttributeValue.m_aHref.m_aLnk;
+ break;
+ case SmMlAttributeValueType::MlLspace:
+ if (m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText)
+ delete m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText;
+ break;
+ case SmMlAttributeValueType::MlMathsize:
+ if (m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText)
+ delete m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText;
+ break;
+ case SmMlAttributeValueType::MlMaxsize:
+ if (m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText)
+ delete m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText;
+ break;
+ case SmMlAttributeValueType::MlMinsize:
+ if (m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText)
+ delete m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText;
+ break;
+ case SmMlAttributeValueType::MlRspace:
+ if (m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText)
+ delete m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText;
+ break;
+ default:
+ break;
+ }
+}
+
+void SmMlAttribute::setDefaultAttributeValue()
+{
+ switch (m_aSmMlAttributeValueType)
+ {
+ case SmMlAttributeValueType::NMlEmpty:
+ break;
+ case SmMlAttributeValueType::MlAccent:
+ m_aAttributeValue.m_aAccent.m_aAccent = SmMlAttributeValueAccent::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlDir:
+ m_aAttributeValue.m_aDir.m_aDir = SmMlAttributeValueDir::MlLtr;
+ break;
+ case SmMlAttributeValueType::MlDisplaystyle:
+ m_aAttributeValue.m_aDisplaystyle.m_aDisplaystyle
+ = SmMlAttributeValueDisplaystyle::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlFence:
+ m_aAttributeValue.m_aFence.m_aFence = SmMlAttributeValueFence::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlForm:
+ m_aAttributeValue.m_aForm.m_aForm = SmMlAttributeValueForm::MlInfix;
+ break;
+ case SmMlAttributeValueType::MlHref:
+ m_aAttributeValue.m_aHref.m_aHref = SmMlAttributeValueHref::NMlEmpty;
+ m_aAttributeValue.m_aHref.m_aLnk = new OUString(u"");
+ break;
+ case SmMlAttributeValueType::MlLspace:
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlEm;
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthValue = 5.0 / 18;
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText = new OUString(u"5/18em");
+ break;
+ case SmMlAttributeValueType::MlMathbackground:
+ m_aAttributeValue.m_aMathbackground.m_aMathbackground
+ = SmMlAttributeValueMathbackground::MlTransparent;
+ break;
+ case SmMlAttributeValueType::MlMathcolor:
+ m_aAttributeValue.m_aMathcolor.m_aMathcolor = SmMlAttributeValueMathcolor::MlDefault;
+ break;
+ case SmMlAttributeValueType::MlMathsize:
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP;
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthValue = 100;
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText = new OUString(u"100%");
+ break;
+ case SmMlAttributeValueType::MlMathvariant:
+ m_aAttributeValue.m_aMathvariant.m_aMathvariant = SmMlAttributeValueMathvariant::normal;
+ break;
+ case SmMlAttributeValueType::MlMaxsize:
+ m_aAttributeValue.m_aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlInfinity;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthValue = 10000;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText = new OUString(u"10000%");
+ break;
+ case SmMlAttributeValueType::MlMinsize:
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlP;
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthValue = 1;
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText = new OUString(u"1%");
+ break;
+ case SmMlAttributeValueType::MlMovablelimits:
+ m_aAttributeValue.m_aMovablelimits.m_aMovablelimits
+ = SmMlAttributeValueMovablelimits::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlRspace:
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthUnit = SmLengthUnit::MlEm;
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthValue = 5.0 / 18;
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText = new OUString(u"5/18em");
+ break;
+ case SmMlAttributeValueType::MlSeparator:
+ m_aAttributeValue.m_aSeparator.m_aSeparator = SmMlAttributeValueSeparator::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlStretchy:
+ m_aAttributeValue.m_aStretchy.m_aStretchy = SmMlAttributeValueStretchy::MlFalse;
+ break;
+ case SmMlAttributeValueType::MlSymmetric:
+ m_aAttributeValue.m_aSymmetric.m_aSymmetric = SmMlAttributeValueSymmetric::MlFalse;
+ break;
+ }
+}
+
+void SmMlAttribute::setAttributeValue(const SmMlAttribute* aAttribute)
+{
+ switch (aAttribute->getMlAttributeValueType())
+ {
+ case SmMlAttributeValueType::NMlEmpty:
+ clearPreviousAttributeValue();
+ m_aSmMlAttributeValueType = SmMlAttributeValueType::NMlEmpty;
+ break;
+ case SmMlAttributeValueType::MlAccent:
+ setMlAccent(aAttribute->getMlAccent());
+ break;
+ case SmMlAttributeValueType::MlDir:
+ setMlDir(aAttribute->getMlDir());
+ break;
+ case SmMlAttributeValueType::MlDisplaystyle:
+ setMlDisplaystyle(aAttribute->getMlDisplaystyle());
+ break;
+ case SmMlAttributeValueType::MlFence:
+ setMlFence(aAttribute->getMlFence());
+ break;
+ case SmMlAttributeValueType::MlForm:
+ setMlForm(aAttribute->getMlForm());
+ break;
+ case SmMlAttributeValueType::MlHref:
+ setMlHref(aAttribute->getMlHref());
+ break;
+ case SmMlAttributeValueType::MlLspace:
+ setMlLspace(aAttribute->getMlLspace());
+ break;
+ case SmMlAttributeValueType::MlMathbackground:
+ setMlMathbackground(aAttribute->getMlMathbackground());
+ break;
+ case SmMlAttributeValueType::MlMathcolor:
+ setMlMathcolor(aAttribute->getMlMathcolor());
+ break;
+ case SmMlAttributeValueType::MlMathsize:
+ setMlMathsize(aAttribute->getMlMathsize());
+ break;
+ case SmMlAttributeValueType::MlMathvariant:
+ setMlMathvariant(aAttribute->getMlMathvariant());
+ break;
+ case SmMlAttributeValueType::MlMaxsize:
+ setMlMaxsize(aAttribute->getMlMaxsize());
+ break;
+ case SmMlAttributeValueType::MlMinsize:
+ setMlMinsize(aAttribute->getMlMinsize());
+ break;
+ case SmMlAttributeValueType::MlMovablelimits:
+ setMlMovablelimits(aAttribute->getMlMovablelimits());
+ break;
+ case SmMlAttributeValueType::MlRspace:
+ setMlRspace(aAttribute->getMlRspace());
+ break;
+ case SmMlAttributeValueType::MlSeparator:
+ setMlSeparator(aAttribute->getMlSeparator());
+ break;
+ case SmMlAttributeValueType::MlStretchy:
+ setMlStretchy(aAttribute->getMlStretchy());
+ break;
+ case SmMlAttributeValueType::MlSymmetric:
+ setMlSymmetric(aAttribute->getMlSymmetric());
+ break;
+ }
+}
+
+/* get values */
+/*************************************************************************************************/
+
+const struct SmMlAccent* SmMlAttribute::getMlAccent() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlAccent)
+ return &m_aAttributeValue.m_aAccent;
+ return nullptr;
+}
+
+const struct SmMlDir* SmMlAttribute::getMlDir() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlDir)
+ return &m_aAttributeValue.m_aDir;
+ return nullptr;
+}
+
+const struct SmMlDisplaystyle* SmMlAttribute::getMlDisplaystyle() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlDisplaystyle)
+ return &m_aAttributeValue.m_aDisplaystyle;
+ return nullptr;
+}
+
+const struct SmMlFence* SmMlAttribute::getMlFence() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlFence)
+ return &m_aAttributeValue.m_aFence;
+ return nullptr;
+}
+
+const struct SmMlForm* SmMlAttribute::getMlForm() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlForm)
+ return &m_aAttributeValue.m_aForm;
+ return nullptr;
+}
+
+const struct SmMlHref* SmMlAttribute::getMlHref() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlHref)
+ return &m_aAttributeValue.m_aHref;
+ return nullptr;
+}
+
+const struct SmMlLspace* SmMlAttribute::getMlLspace() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlLspace)
+ return &m_aAttributeValue.m_aLspace;
+ return nullptr;
+}
+
+const struct SmMlMathbackground* SmMlAttribute::getMlMathbackground() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathbackground)
+ return &m_aAttributeValue.m_aMathbackground;
+ return nullptr;
+}
+
+const struct SmMlMathcolor* SmMlAttribute::getMlMathcolor() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathcolor)
+ return &m_aAttributeValue.m_aMathcolor;
+ return nullptr;
+}
+
+const struct SmMlMathsize* SmMlAttribute::getMlMathsize() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlAccent)
+ return &m_aAttributeValue.m_aMathsize;
+ return nullptr;
+}
+
+const struct SmMlMathvariant* SmMlAttribute::getMlMathvariant() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMathvariant)
+ return &m_aAttributeValue.m_aMathvariant;
+ return nullptr;
+}
+
+const struct SmMlMaxsize* SmMlAttribute::getMlMaxsize() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMaxsize)
+ return &m_aAttributeValue.m_aMaxsize;
+ return nullptr;
+}
+
+const struct SmMlMinsize* SmMlAttribute::getMlMinsize() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMinsize)
+ return &m_aAttributeValue.m_aMinsize;
+ return nullptr;
+}
+
+const struct SmMlMovablelimits* SmMlAttribute::getMlMovablelimits() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlMovablelimits)
+ return &m_aAttributeValue.m_aMovablelimits;
+ return nullptr;
+}
+
+const struct SmMlRspace* SmMlAttribute::getMlRspace() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlRspace)
+ return &m_aAttributeValue.m_aRspace;
+ return nullptr;
+}
+
+const struct SmMlSeparator* SmMlAttribute::getMlSeparator() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlSeparator)
+ return &m_aAttributeValue.m_aSeparator;
+ return nullptr;
+}
+
+const struct SmMlStretchy* SmMlAttribute::getMlStretchy() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlStretchy)
+ return &m_aAttributeValue.m_aStretchy;
+ return nullptr;
+}
+
+const struct SmMlSymmetric* SmMlAttribute::getMlSymmetric() const
+{
+ if (m_aSmMlAttributeValueType == SmMlAttributeValueType::MlSymmetric)
+ return &m_aAttributeValue.m_aSymmetric;
+ return nullptr;
+}
+
+/* set values */
+/*************************************************************************************************/
+
+void SmMlAttribute::setMlAccent(const SmMlAccent* aAccent)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aAccent.m_aAccent = aAccent->m_aAccent;
+}
+
+void SmMlAttribute::setMlDir(const SmMlDir* aDir)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aDir.m_aDir = aDir->m_aDir;
+}
+
+void SmMlAttribute::setMlDisplaystyle(const SmMlDisplaystyle* aDisplaystyle)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aDisplaystyle.m_aDisplaystyle = aDisplaystyle->m_aDisplaystyle;
+}
+
+void SmMlAttribute::setMlFence(const SmMlFence* aFence)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aFence.m_aFence = aFence->m_aFence;
+}
+
+void SmMlAttribute::setMlForm(const SmMlForm* aForm)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aForm.m_aForm = aForm->m_aForm;
+}
+
+void SmMlAttribute::setMlHref(const SmMlHref* aHref)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aHref.m_aHref = aHref->m_aHref;
+ m_aAttributeValue.m_aHref.m_aLnk = new OUString(*aHref->m_aLnk);
+}
+
+void SmMlAttribute::setMlLspace(const SmMlLspace* aLspace)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthUnit
+ = aLspace->m_aLengthValue.m_aLengthUnit;
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aLengthValue
+ = aLspace->m_aLengthValue.m_aLengthValue;
+ m_aAttributeValue.m_aLspace.m_aLengthValue.m_aOriginalText
+ = new OUString(*aLspace->m_aLengthValue.m_aOriginalText);
+}
+
+void SmMlAttribute::setMlMathbackground(const SmMlMathbackground* aMathbackground)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMathbackground.m_aMathbackground = aMathbackground->m_aMathbackground;
+}
+
+void SmMlAttribute::setMlMathcolor(const SmMlMathcolor* aMathcolor)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMathcolor.m_aMathcolor = aMathcolor->m_aMathcolor;
+}
+
+void SmMlAttribute::setMlMathsize(const SmMlMathsize* aMathsize)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthUnit
+ = aMathsize->m_aLengthValue.m_aLengthUnit;
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aLengthValue
+ = aMathsize->m_aLengthValue.m_aLengthValue;
+ m_aAttributeValue.m_aMathsize.m_aLengthValue.m_aOriginalText
+ = new OUString(*aMathsize->m_aLengthValue.m_aOriginalText);
+}
+
+void SmMlAttribute::setMlMathvariant(const SmMlMathvariant* aMathvariant)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMathvariant.m_aMathvariant = aMathvariant->m_aMathvariant;
+}
+
+void SmMlAttribute::setMlMaxsize(const SmMlMaxsize* aMaxsize)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMaxsize.m_aMaxsize = aMaxsize->m_aMaxsize;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthUnit
+ = aMaxsize->m_aLengthValue.m_aLengthUnit;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aLengthValue
+ = aMaxsize->m_aLengthValue.m_aLengthValue;
+ m_aAttributeValue.m_aMaxsize.m_aLengthValue.m_aOriginalText
+ = new OUString(*aMaxsize->m_aLengthValue.m_aOriginalText);
+}
+
+void SmMlAttribute::setMlMinsize(const SmMlMinsize* aMinsize)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthUnit
+ = aMinsize->m_aLengthValue.m_aLengthUnit;
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aLengthValue
+ = aMinsize->m_aLengthValue.m_aLengthValue;
+ m_aAttributeValue.m_aMinsize.m_aLengthValue.m_aOriginalText
+ = new OUString(*aMinsize->m_aLengthValue.m_aOriginalText);
+}
+
+void SmMlAttribute::setMlMovablelimits(const SmMlMovablelimits* aMovablelimits)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aMovablelimits.m_aMovablelimits = aMovablelimits->m_aMovablelimits;
+}
+
+void SmMlAttribute::setMlRspace(const SmMlRspace* aRspace)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthUnit
+ = aRspace->m_aLengthValue.m_aLengthUnit;
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aLengthValue
+ = aRspace->m_aLengthValue.m_aLengthValue;
+ m_aAttributeValue.m_aRspace.m_aLengthValue.m_aOriginalText
+ = new OUString(*aRspace->m_aLengthValue.m_aOriginalText);
+}
+
+void SmMlAttribute::setMlSeparator(const SmMlSeparator* aSeparator)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aSeparator.m_aSeparator = aSeparator->m_aSeparator;
+}
+
+void SmMlAttribute::setMlStretchy(const SmMlStretchy* aStretchy)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aStretchy.m_aStretchy = aStretchy->m_aStretchy;
+}
+
+void SmMlAttribute::setMlSymmetric(const SmMlSymmetric* aSymmetric)
+{
+ m_bSet = true;
+ clearPreviousAttributeValue();
+ m_aAttributeValue.m_aSymmetric.m_aSymmetric = aSymmetric->m_aSymmetric;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/def.cxx b/starmath/source/mathml/def.cxx
new file mode 100644
index 000000000..484dcd665
--- /dev/null
+++ b/starmath/source/mathml/def.cxx
@@ -0,0 +1,124 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <mathml/attribute.hxx>
+
+SmMlAttributePos starmathdatabase::MlAttributeListEmpty[] = {
+ // clang-format off
+ { SmMlAttributeValueType::NMlEmpty, 0 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMath[] = {
+ // clang-format off
+ { SmMlAttributeValueType::NMlEmpty, 0 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMi[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 },
+ { SmMlAttributeValueType::MlDisplaystyle, 4 },
+ { SmMlAttributeValueType::MlMathsize, 5 },
+ { SmMlAttributeValueType::MlMathvariant, 6 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMerror[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlMathbackground, 1 },
+ { SmMlAttributeValueType::MlMathcolor, 2 },
+ { SmMlAttributeValueType::MlDisplaystyle, 3 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMn[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 },
+ { SmMlAttributeValueType::MlDisplaystyle, 4 },
+ { SmMlAttributeValueType::MlMathsize, 5 },
+ { SmMlAttributeValueType::MlMathvariant, 6 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMo[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 },
+ { SmMlAttributeValueType::MlDisplaystyle, 4 },
+ { SmMlAttributeValueType::MlMathsize, 5 },
+ { SmMlAttributeValueType::MlMathvariant, 6 },
+ { SmMlAttributeValueType::MlFence, 7 },
+ { SmMlAttributeValueType::MlForm, 8 },
+ { SmMlAttributeValueType::MlMaxsize, 9 },
+ { SmMlAttributeValueType::MlMinsize, 10 },
+ { SmMlAttributeValueType::MlMovablelimits, 11 },
+ { SmMlAttributeValueType::MlLspace, 12 },
+ { SmMlAttributeValueType::MlRspace, 13 },
+ { SmMlAttributeValueType::MlAccent, 14 },
+ { SmMlAttributeValueType::MlStretchy, 15 },
+ { SmMlAttributeValueType::MlSeparator, 16 },
+ { SmMlAttributeValueType::MlSymmetric, 17 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMrow[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMtext[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 },
+ { SmMlAttributeValueType::MlDisplaystyle, 4 },
+ { SmMlAttributeValueType::MlMathsize, 5 },
+ { SmMlAttributeValueType::MlMathvariant, 6 }
+ // clang-format on
+};
+
+SmMlAttributePos starmathdatabase::MlAttributeListMstyle[] = {
+ // clang-format off
+ { SmMlAttributeValueType::MlHref, 0 },
+ { SmMlAttributeValueType::MlDir, 1 },
+ { SmMlAttributeValueType::MlMathbackground, 2 },
+ { SmMlAttributeValueType::MlMathcolor, 3 },
+ { SmMlAttributeValueType::MlDisplaystyle, 4 },
+ { SmMlAttributeValueType::MlMathsize, 5 },
+ { SmMlAttributeValueType::MlMathvariant, 6 },
+ { SmMlAttributeValueType::MlFence, 7 },
+ { SmMlAttributeValueType::MlForm, 8 },
+ { SmMlAttributeValueType::MlMaxsize, 9 },
+ { SmMlAttributeValueType::MlMinsize, 10 },
+ { SmMlAttributeValueType::MlMovablelimits, 11 },
+ { SmMlAttributeValueType::MlLspace, 12 },
+ { SmMlAttributeValueType::MlRspace, 13 },
+ { SmMlAttributeValueType::MlAccent, 14 },
+ { SmMlAttributeValueType::MlStretchy, 15 },
+ { SmMlAttributeValueType::MlSeparator, 16 },
+ { SmMlAttributeValueType::MlSymmetric, 17 }
+ // clang-format on
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/element.cxx b/starmath/source/mathml/element.cxx
new file mode 100644
index 000000000..4f8f2a64f
--- /dev/null
+++ b/starmath/source/mathml/element.cxx
@@ -0,0 +1,136 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <mathml/element.hxx>
+
+void SmMlElement::SmImplAttributeType()
+{
+ switch (m_aElementType)
+ {
+ case SmMlElementType::NMlEmpty:
+ m_aAttributePosList = std::vector<SmMlAttributePos>(0);
+ break;
+ case SmMlElementType::NMlStructural:
+ m_aAttributePosList = std::vector<SmMlAttributePos>(0);
+ break;
+ case SmMlElementType::NMlSmNode:
+ m_aAttributePosList = std::vector<SmMlAttributePos>(0);
+ break;
+ case SmMlElementType::MlMath:
+ m_aAttributePosList = std::vector<SmMlAttributePos>(0);
+ //m_aAttributePosList = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMath), std::end(starmathdatabase::MlAttributeListMath));
+ break;
+ case SmMlElementType::MlMi:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMi),
+ std::end(starmathdatabase::MlAttributeListMi));
+ break;
+ case SmMlElementType::MlMerror:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMerror),
+ std::end(starmathdatabase::MlAttributeListMerror));
+ break;
+ case SmMlElementType::MlMn:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMn),
+ std::end(starmathdatabase::MlAttributeListMn));
+ break;
+ case SmMlElementType::MlMo:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMo),
+ std::end(starmathdatabase::MlAttributeListMo));
+ break;
+ case SmMlElementType::MlMrow:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMrow),
+ std::end(starmathdatabase::MlAttributeListMrow));
+ break;
+ case SmMlElementType::MlMtext:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMtext),
+ std::end(starmathdatabase::MlAttributeListMtext));
+ break;
+ case SmMlElementType::MlMstyle:
+ m_aAttributePosList
+ = std::vector<SmMlAttributePos>(std::begin(starmathdatabase::MlAttributeListMstyle),
+ std::end(starmathdatabase::MlAttributeListMstyle));
+ break;
+ default:
+ break;
+ }
+ // Create attribute vector with given pattern
+ m_aAttributeList = starmathdatabase::makeMlAttributeList(m_aAttributePosList);
+}
+
+SmMlAttribute SmMlElement::getAttribute(SmMlAttributeValueType aAttributeType) const
+{
+ // Look for the attribute position and return if exists
+ for (size_t i = 0; i < m_aAttributePosList.size(); ++i)
+ {
+ if (m_aAttributePosList[i].m_aAttributeValueType == aAttributeType)
+ return m_aAttributeList[m_aAttributePosList[i].m_nPos];
+ }
+ return SmMlAttribute();
+}
+
+bool SmMlElement::isAttributeSet(SmMlAttributeValueType aAttributeType) const
+{
+ // Look for the attribute position and return if exists
+ for (size_t i = 0; i < m_aAttributePosList.size(); ++i)
+ {
+ if (m_aAttributePosList[i].m_aAttributeValueType == aAttributeType)
+ return m_aAttributeList[m_aAttributePosList[i].m_nPos].isSet();
+ }
+ return false;
+}
+
+void SmMlElement::setAttribute(const SmMlAttribute* aAttribute)
+{
+ // Look for the attribute position and assign if exists
+ for (size_t i = 0; i < m_aAttributePosList.size(); ++i)
+ {
+ if (m_aAttributePosList[i].m_aAttributeValueType == aAttribute->getMlAttributeValueType())
+ {
+ m_aAttributeList[m_aAttributePosList[i].m_nPos].setMlAttributeValue(aAttribute);
+ break;
+ }
+ }
+}
+
+void SmMlElement::setSubElement(size_t nPos, SmMlElement* aElement)
+{
+ // This is the new parent element
+ aElement->setParentElement(this);
+ aElement->setSubElementId(nPos);
+ // Check if the vector is long enough
+ // Careful nOldSize can be 0 and -1 will underflow
+ // We must put something on the empty locations
+ size_t nOldSize = m_aSubElements.size();
+ if (nPos + 1 > nOldSize)
+ {
+ m_aSubElements.resize(nPos + 1);
+ for (; nOldSize < nPos; ++nOldSize)
+ m_aSubElements[nOldSize] = nullptr;
+ }
+ // Assign value
+ m_aSubElements[nPos] = aElement;
+}
+
+std::vector<SmMlAttribute>
+starmathdatabase::makeMlAttributeList(std::vector<SmMlAttributePos> aAttributePosList)
+{
+ std::vector<SmMlAttribute> aAttributeList(aAttributePosList.size());
+ for (size_t i = 0; i < aAttributePosList.size(); ++i)
+ {
+ aAttributeList[i].setMlAttributeValueType(aAttributePosList[i].m_aAttributeValueType);
+ }
+ return aAttributeList;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/export.cxx b/starmath/source/mathml/export.cxx
new file mode 100644
index 000000000..b55be5092
--- /dev/null
+++ b/starmath/source/mathml/export.cxx
@@ -0,0 +1,1110 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+// Our mathml
+#include <mathml/export.hxx>
+#include <mathml/iterator.hxx>
+
+// LO tools to use
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+
+// Extra LO tools
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/streamwrap.hxx>
+#include <xmloff/namespacemap.hxx>
+
+// Our starmath tools
+#include <document.hxx>
+#include <smmod.hxx>
+#include <strings.hrc>
+#include <unomodel.hxx>
+#include <xparsmlbase.hxx>
+#include <starmathdatabase.hxx>
+
+// Old parser
+#include <mathmlexport.hxx>
+
+using namespace ::com::sun::star;
+using namespace xmloff::token;
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+// SmMLExportWrapper
+/*************************************************************************************************/
+
+bool SmMLExportWrapper::Export(SfxMedium& rMedium)
+{
+ bool bRet = true;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ // Check all fine
+ SAL_WARN_IF(m_xModel == nullptr, "starmath", "Missing model");
+ SAL_WARN_IF(xContext == nullptr, "starmath", "Missing context");
+ if (m_xModel == nullptr || xContext == nullptr)
+ return false;
+
+ //Get model
+ uno::Reference<lang::XComponent> xModelComp = m_xModel;
+ SAL_WARN_IF(xModelComp == nullptr, "starmath", "Missing model component");
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel);
+ SAL_WARN_IF(pModel == nullptr, "starmath", "Failed to get threw uno tunnel");
+ if (xModelComp == nullptr || pModel == nullptr)
+ return false;
+
+ // Get doc shell
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch sm document");
+ return false;
+ }
+
+ // Check if it is a standalone window or embed object
+ bool bEmbedded = SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode();
+
+ // Medium item set
+ SfxItemSet* pMediumItemSet = rMedium.GetItemSet();
+ if (pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to get medium item set");
+ return false;
+ }
+
+ // Progress bar ~
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ if (!bEmbedded)
+ {
+ // Extra check to ensure everything is fine
+ if (pDocShell->GetMedium() != &rMedium)
+ {
+ SAL_WARN("starmath", "Input medium and sm document medium do not match");
+ //return false;
+ }
+
+ // Fetch progress bar
+ const SfxUnoAnyItem* pItem = pMediumItemSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ {
+ // set progress range and start status indicator
+ pItem->GetValue() >>= xStatusIndicator;
+ xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), 3);
+ xStatusIndicator->setValue(0);
+ }
+ }
+
+ // create XPropertySet with three properties for status indicator
+ static const comphelper::PropertyMapEntry aInfoMap[]{
+ { OUString("UsePrettyPrinting"), 0, cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 }
+ };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Always print pretty
+ xInfoSet->setPropertyValue("UsePrettyPrinting", Any(true));
+
+ // Set base URI
+ xInfoSet->setPropertyValue(u"BaseURI", Any(rMedium.GetBaseURL(true)));
+
+ if (!m_bFlat) //Storage (Package) of Stream
+ {
+ // Fetch the output storage
+ uno::Reference<embed::XStorage> xStg = rMedium.GetOutputStorage();
+ if (xStg == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch output storage");
+ return false;
+ }
+
+ // TODO/LATER: handle the case of embedded links gracefully
+ if (bEmbedded) //&& !pStg->IsRoot() )
+ {
+ const SfxStringItem* pDocHierarchItem
+ = pMediumItemSet->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem != nullptr)
+ {
+ OUString aName = pDocHierarchItem->GetValue();
+ if (!aName.isEmpty())
+ xInfoSet->setPropertyValue("StreamRelPath", Any(aName));
+ }
+ }
+ else
+ {
+ // Write file metadata ( data, LO version ... )
+ // Note: export through an XML exporter component (storage version)
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(1);
+
+ bRet = WriteThroughComponentS(xStg, xModelComp, u"meta.xml", xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisMetaExporter", 6);
+ }
+
+ // Write starmath formula
+ // Note: export through an XML exporter component (storage version)
+ if (bRet)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(2);
+
+ if (pDocShell->GetSmSyntaxVersion() == 5)
+ bRet = WriteThroughComponentS(xStg, xModelComp, u"content.xml", xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLContentExporter", 5);
+ else
+ bRet = WriteThroughComponentS(xStg, xModelComp, u"content.xml", xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLContentExporter", 6);
+ }
+
+ // Write starmath settings
+ // Note: export through an XML exporter component (storage version)
+ if (bRet)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(3);
+
+ bRet = WriteThroughComponentS(xStg, xModelComp, u"settings.xml", xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisSettingsExporter", 6);
+ }
+ }
+ else
+ {
+ // Fetch the output stream
+ SvStream* pStream = rMedium.GetOutStream();
+ if (pStream == nullptr)
+ {
+ SAL_WARN("starmath", "Missing output stream");
+ return false;
+ }
+ uno::Reference<io::XOutputStream> xOut(new utl::OOutputStreamWrapper(*pStream));
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(1);
+
+ // Write everything in the same place
+ // Note: export through an XML exporter component (output stream version)
+ if (pDocShell->GetSmSyntaxVersion() == 5)
+ bRet = WriteThroughComponentOS(xOut, xModelComp, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLContentExporter", 5);
+ else
+ bRet = WriteThroughComponentOS(xOut, xModelComp, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLContentExporter", 6);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return bRet;
+}
+
+OUString SmMLExportWrapper::Export(SmMlElement* pElementTree)
+{
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ // Check all fine
+ m_pElementTree = nullptr;
+ SAL_WARN_IF(m_xModel == nullptr, "starmath", "Missing model");
+ SAL_WARN_IF(xContext == nullptr, "starmath", "Missing context");
+ if (m_xModel == nullptr || xContext == nullptr)
+ return u"";
+
+ //Get model
+ uno::Reference<lang::XComponent> xModelComp = m_xModel;
+ SAL_WARN_IF(xModelComp == nullptr, "starmath", "Missing model component");
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel);
+ SAL_WARN_IF(pModel == nullptr, "starmath", "Failed to get threw uno tunnel");
+ if (xModelComp == nullptr || pModel == nullptr)
+ return u"";
+
+ // Get doc shell
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch sm document");
+ return u"";
+ }
+
+ // create XPropertySet with three properties for status indicator
+ static const comphelper::PropertyMapEntry aInfoMap[]{
+ { OUString("UsePrettyPrinting"), 0, cppu::UnoType<bool>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 }
+ };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Always print pretty
+ xInfoSet->setPropertyValue("UsePrettyPrinting", Any(true));
+
+ // Fetch mathml tree
+ m_pElementTree = pElementTree;
+
+ // Write stuff
+ // Note: export through an XML exporter component (memory stream version)
+ return WriteThroughComponentMS(xModelComp, xContext, xInfoSet);
+}
+
+// export through an XML exporter component (output stream version)
+bool SmMLExportWrapper::WriteThroughComponentOS(const Reference<io::XOutputStream>& xOutputStream,
+ const Reference<XComponent>& xComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char16_t* pComponentName,
+ int_fast16_t nSyntaxVersion)
+{
+ // We need a output stream but it is already checked by caller
+ // We need a component but it is already checked by caller
+ // We need a context but it is already checked by caller
+ // We need a property set but it is already checked by caller
+ // We need a component name but it is already checked by caller
+
+ // get sax writer
+ Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext);
+
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream(xOutputStream);
+ if (m_bUseHTMLMLEntities)
+ xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport);
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) };
+
+ // get filter component
+ auto xExporterData = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString(pComponentName), aArgs, rxContext);
+ Reference<document::XExporter> xExporter(xExporterData, UNO_QUERY);
+
+ // Check everything is fine
+ if (!xExporter.is())
+ {
+ SAL_WARN("starmath", "can't instantiate export filter component");
+ return false;
+ }
+
+ // connect model and filter
+ xExporter->setSourceDocument(xComponent);
+ Reference<XFilter> xFilter(xExporter, UNO_QUERY);
+ uno::Sequence<PropertyValue> aProps(0);
+
+ // filter
+ if (nSyntaxVersion == 5)
+ {
+ SmXMLExport* pFilter = comphelper::getFromUnoTunnel<SmXMLExport>(xFilter);
+ if (pFilter == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch SmMLExport");
+ return false;
+ }
+ xFilter->filter(aProps);
+ return pFilter->GetSuccess();
+ }
+
+ // filter
+ SmMLExport* pFilter = comphelper::getFromUnoTunnel<SmMLExport>(xFilter);
+
+ // Setup filter
+ if (pFilter == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch SmMLExport");
+ return false;
+ }
+ pFilter->setUseExportTag(m_bUseExportTag);
+ pFilter->setElementTree(m_pElementTree);
+
+ // Execute operation
+ xFilter->filter(aProps);
+ return pFilter->getSuccess();
+}
+
+// export through an XML exporter component (storage version)
+bool SmMLExportWrapper::WriteThroughComponentS(const Reference<embed::XStorage>& xStorage,
+ const Reference<XComponent>& xComponent,
+ const char16_t* pStreamName,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char16_t* pComponentName,
+ int_fast16_t nSyntaxVersion)
+{
+ // We need a storage name but it is already checked by caller
+ // We need a component name but it is already checked by caller
+ // We need a stream name but it is already checked by caller
+ // We need a context but it is already checked by caller
+ // We need a property set but it is already checked by caller
+ // We need a component but it is already checked by caller
+
+ // open stream
+ Reference<io::XStream> xStream;
+ try
+ {
+ xStream = xStorage->openStreamElement(
+ OUString(pStreamName), embed::ElementModes::READWRITE | embed::ElementModes::TRUNCATE);
+ }
+ catch (const uno::Exception&)
+ {
+ SAL_WARN("starmath", "Can't create output stream in package");
+ return false;
+ }
+
+ // Set stream as text / xml
+ uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
+ xSet->setPropertyValue("MediaType", Any(OUString(u"text/xml")));
+
+ // all streams must be encrypted in encrypted document
+ xSet->setPropertyValue("UseCommonStoragePasswordEncryption", Any(true));
+
+ // set Base URL
+ rPropSet->setPropertyValue("StreamName", Any(OUString(pStreamName)));
+
+ // write the stuff
+ // Note: export through an XML exporter component (output stream version)
+ return WriteThroughComponentOS(xStream->getOutputStream(), xComponent, rxContext, rPropSet,
+ pComponentName, nSyntaxVersion);
+}
+
+// export through an XML exporter component (memory stream version)
+OUString
+SmMLExportWrapper::WriteThroughComponentMS(const Reference<XComponent>& xComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet)
+{
+ // We need a component but it is already checked by caller
+ // We need a context but it is already checked by caller
+ // We need a property set it is already checked by caller
+
+ // open stream
+ SvMemoryStream aMemoryStream(8192, 1024);
+ uno::Reference<io::XOutputStream> xStream(new utl::OOutputStreamWrapper(aMemoryStream));
+
+ // Set the stream as text
+ uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
+ xSet->setPropertyValue("MediaType", Any(OUString("text/xml")));
+
+ // write the stuff
+ // Note: export through an XML exporter component (output stream version)
+ bool bOk = WriteThroughComponentOS(xStream, xComponent, rxContext, rPropSet,
+ u"com.sun.star.comp.Mathml.MLContentExporter", 6);
+
+ // We don't want to read uninitialized data
+ if (!bOk)
+ return u"";
+
+ // Recover data and generate string
+ OString aString(static_cast<const char*>(aMemoryStream.GetData()),
+ aMemoryStream.GetSize() / sizeof(char));
+ return OStringToOUString(aString, RTL_TEXTENCODING_UTF8);
+}
+
+// SmMLExport technical
+/*************************************************************************************************/
+
+sal_Int64 SAL_CALL SmMLExport::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvXMLExport>{});
+}
+
+const uno::Sequence<sal_Int8>& SmMLExport::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSmMLExportUnoTunnelId;
+ return theSmMLExportUnoTunnelId.getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_MLExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_MLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_MLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_MLContentExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmMLExport(context, "com.sun.star.comp.Math.XMLContentExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::CONTENT));
+}
+
+SmDocShell* SmMLExport::getSmDocShell()
+{
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(GetModel());
+ if (pModel != nullptr)
+ return static_cast<SmDocShell*>(pModel->GetObjectShell());
+ return nullptr;
+}
+
+ErrCode SmMLExport::exportDoc(enum XMLTokenEnum eClass)
+{
+ if (!(getExportFlags() & SvXMLExportFlags::CONTENT))
+ {
+ // Everything that isn't the formula itself get's default export
+ SvXMLExport::exportDoc(eClass);
+ return ERRCODE_NONE;
+ }
+
+ // Checks if it has to export a particular tree
+ if (m_pElementTree == nullptr)
+ {
+ // Set element tree
+ SmDocShell* pDocShell = getSmDocShell();
+ if (pDocShell != nullptr)
+ m_pElementTree = pDocShell->GetMlElementTree();
+ else
+ {
+ m_bSuccess = false;
+ return SVSTREAM_INVALID_PARAMETER;
+ }
+ }
+
+ // Start document and encrypt if necessary
+ GetDocHandler()->startDocument();
+ addChaffWhenEncryptedStorage();
+
+ // make use of a default namespace
+ // Math doesn't need namespaces from xmloff, since it now uses default namespaces
+ // Because that is common with current MathML usage in the web -> ResetNamespaceMap();
+ GetNamespaceMap_().Add(OUString(u""), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH);
+
+ // Add xmlns line
+ if (m_bUseExportTag)
+ {
+ GetAttrList().AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
+ GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH));
+ }
+
+ // Export and close document
+ ExportContent_();
+ GetDocHandler()->endDocument();
+
+ return ERRCODE_NONE;
+}
+
+void SmMLExport::GetViewSettings(Sequence<PropertyValue>& aProps)
+{
+ // Get the document shell
+ SmDocShell* pDocShell = getSmDocShell();
+ if (pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Missing document shell so no view settings");
+ return;
+ }
+
+ // Allocate enough memory
+ aProps.realloc(4);
+ PropertyValue* pValue = aProps.getArray();
+
+ // The view settings are the formula display settings
+ tools::Rectangle aRect(pDocShell->GetVisArea());
+
+ pValue[0].Name = "ViewAreaTop";
+ pValue[0].Value <<= aRect.Top();
+
+ pValue[1].Name = "ViewAreaLeft";
+ pValue[1].Value <<= aRect.Left();
+
+ pValue[2].Name = "ViewAreaWidth";
+ pValue[2].Value <<= aRect.GetWidth();
+
+ pValue[3].Name = "ViewAreaHeight";
+ pValue[3].Value <<= aRect.GetHeight();
+}
+
+void SmMLExport::GetConfigurationSettings(Sequence<PropertyValue>& rProps)
+{
+ // Get model property set (settings)
+ Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
+ if (!xProps.is())
+ {
+ SAL_WARN("starmath", "Missing model properties so no configuration settings");
+ return;
+ }
+
+ // Get model property set info (settings values)
+ Reference<XPropertySetInfo> xPropertySetInfo = xProps->getPropertySetInfo();
+ if (!xPropertySetInfo.is())
+ {
+ SAL_WARN("starmath", "Missing model properties info so no configuration settings");
+ return;
+ }
+
+ // Allocate to store the properties
+ Sequence<Property> aProps = xPropertySetInfo->getProperties();
+ const sal_Int32 nCount = aProps.getLength();
+ rProps.realloc(nCount);
+ auto pProps = rProps.getArray();
+
+ // Copy properties
+ // This needs further revision
+ // Based in code mathmlexport.cxx::GetConfigurationSettings
+ for (sal_Int32 i = 0; i < nCount; ++i)
+ {
+ if (aProps[i].Name != "Formula" && aProps[i].Name != "BasicLibraries"
+ && aProps[i].Name != "DialogLibraries" && aProps[i].Name != "RuntimeUID")
+ {
+ pProps[i].Name = aProps[i].Name;
+ pProps[i].Value = xProps->getPropertyValue(aProps[i].Name);
+ }
+ }
+}
+
+SmMLExport::SmMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
+ OUString const& implementationName, SvXMLExportFlags nExportFlags)
+ : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags)
+ , m_pElementTree(nullptr)
+ , m_bSuccess(true)
+ , m_bUseExportTag(true)
+{
+}
+
+// SmMLExport
+/*************************************************************************************************/
+
+void SmMLExport::declareMlError()
+{
+ SAL_WARN("starmath", "Invalid use of mathml.");
+ m_bSuccess = false;
+}
+
+void SmMLExport::exportMlAttributeLength(xmloff::token::XMLTokenEnum pAttribute,
+ const SmLengthValue& aLengthValue)
+{
+ if (!aLengthValue.m_aOriginalText->isEmpty())
+ {
+ addAttribute(pAttribute, *aLengthValue.m_aOriginalText);
+ }
+ else
+ {
+ OUStringBuffer aSizeBuffer(64);
+ aSizeBuffer.append(aLengthValue.m_aLengthValue);
+ switch (aLengthValue.m_aLengthUnit)
+ {
+ case SmLengthUnit::MlEm:
+ aSizeBuffer.append(u"em");
+ break;
+ case SmLengthUnit::MlEx:
+ aSizeBuffer.append(u"ex");
+ break;
+ case SmLengthUnit::MlPx:
+ aSizeBuffer.append(u"px");
+ break;
+ case SmLengthUnit::MlIn:
+ aSizeBuffer.append(u"in");
+ break;
+ case SmLengthUnit::MlCm:
+ aSizeBuffer.append(u"cm");
+ break;
+ case SmLengthUnit::MlMm:
+ aSizeBuffer.append(u"mm");
+ break;
+ case SmLengthUnit::MlPt:
+ aSizeBuffer.append(u"pt");
+ break;
+ case SmLengthUnit::MlPc:
+ aSizeBuffer.append(u"pc");
+ break;
+ case SmLengthUnit::MlP:
+ aSizeBuffer.append(u"%");
+ break;
+ case SmLengthUnit::MlM:
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ addAttribute(pAttribute, aSizeBuffer.makeStringAndClear());
+ }
+}
+
+void SmMLExport::exportMlAttributes(const SmMlElement* pMlElement)
+{
+ size_t nAttributeCount = pMlElement->getAttributeCount();
+ for (size_t i = 0; i < nAttributeCount; ++i)
+ {
+ SmMlAttribute aAttribute = pMlElement->getAttribute(i);
+ if (!aAttribute.isSet())
+ continue;
+
+ switch (aAttribute.getMlAttributeValueType())
+ {
+ case SmMlAttributeValueType::MlAccent:
+ {
+ auto aAttributeValue = aAttribute.getMlAccent();
+ switch (aAttributeValue->m_aAccent)
+ {
+ case SmMlAttributeValueAccent::MlFalse:
+ addAttribute(XML_ACCENT, XML_FALSE);
+ break;
+ case SmMlAttributeValueAccent::MlTrue:
+ addAttribute(XML_ACCENT, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlDir:
+ {
+ auto aAttributeValue = aAttribute.getMlDir();
+ switch (aAttributeValue->m_aDir)
+ {
+ case SmMlAttributeValueDir::MlLtr:
+ addAttribute(XML_DIR, XML_LTR);
+ break;
+ case SmMlAttributeValueDir::MlRtl:
+ addAttribute(XML_DIR, XML_RTL);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlDisplaystyle:
+ {
+ auto aAttributeValue = aAttribute.getMlDisplaystyle();
+ switch (aAttributeValue->m_aDisplaystyle)
+ {
+ case SmMlAttributeValueDisplaystyle::MlTrue:
+ addAttribute(XML_DISPLAYSTYLE, XML_FALSE);
+ break;
+ case SmMlAttributeValueDisplaystyle::MlFalse:
+ addAttribute(XML_DISPLAYSTYLE, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlFence:
+ {
+ auto aAttributeValue = aAttribute.getMlFence();
+ switch (aAttributeValue->m_aFence)
+ {
+ case SmMlAttributeValueFence::MlTrue:
+ addAttribute(XML_FENCE, XML_FALSE);
+ break;
+ case SmMlAttributeValueFence::MlFalse:
+ addAttribute(XML_FENCE, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlHref:
+ {
+ auto aAttributeValue = aAttribute.getMlHref();
+ switch (aAttributeValue->m_aHref)
+ {
+ case SmMlAttributeValueHref::NMlEmpty:
+ break;
+ case SmMlAttributeValueHref::NMlValid:
+ addAttribute(XML_HREF, *aAttributeValue->m_aLnk);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlLspace:
+ {
+ auto aSizeData = aAttribute.getMlLspace();
+ auto aLengthData = aSizeData->m_aLengthValue;
+ exportMlAttributeLength(XML_LSPACE, aLengthData);
+ break;
+ }
+ case SmMlAttributeValueType::MlMathbackground:
+ {
+ auto aAttributeValue = aAttribute.getMlMathbackground();
+ switch (aAttributeValue->m_aMathbackground)
+ {
+ case SmMlAttributeValueMathbackground::MlTransparent:
+ addAttribute(XML_MATHBACKGROUND, "transparent");
+ break;
+ case SmMlAttributeValueMathbackground::MlRgb:
+ {
+ const OUString& rTextColor = starmathdatabase::Identify_Color_MATHML(
+ sal_uInt32(aAttributeValue->m_aCol))
+ .aIdent;
+ addAttribute(XML_MATHBACKGROUND, rTextColor);
+ break;
+ }
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlMathcolor:
+ {
+ auto aAttributeValue = aAttribute.getMlMathcolor();
+ switch (aAttributeValue->m_aMathcolor)
+ {
+ case SmMlAttributeValueMathcolor::MlDefault:
+ break;
+ case SmMlAttributeValueMathcolor::MlRgb:
+ {
+ const OUString& rTextColor = starmathdatabase::Identify_Color_MATHML(
+ sal_uInt32(aAttributeValue->m_aCol))
+ .aIdent;
+ addAttribute(XML_MATHCOLOR, rTextColor);
+ break;
+ }
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlMathsize:
+ {
+ auto aSizeData = aAttribute.getMlMathsize();
+ auto aLengthData = aSizeData->m_aLengthValue;
+ exportMlAttributeLength(XML_MATHSIZE, aLengthData);
+ break;
+ }
+ case SmMlAttributeValueType::MlMathvariant:
+ {
+ auto aAttributeValue = aAttribute.getMlMathvariant();
+ switch (aAttributeValue->m_aMathvariant)
+ {
+ case SmMlAttributeValueMathvariant::normal:
+ addAttribute(XML_MATHVARIANT, "normal");
+ break;
+ case SmMlAttributeValueMathvariant::bold:
+ addAttribute(XML_MATHVARIANT, "bold");
+ break;
+ case SmMlAttributeValueMathvariant::italic:
+ addAttribute(XML_MATHVARIANT, "italic");
+ break;
+ case SmMlAttributeValueMathvariant::double_struck:
+ addAttribute(XML_MATHVARIANT, "double-struck");
+ break;
+ case SmMlAttributeValueMathvariant::script:
+ addAttribute(XML_MATHVARIANT, "script");
+ break;
+ case SmMlAttributeValueMathvariant::fraktur:
+ addAttribute(XML_MATHVARIANT, "fraktur");
+ break;
+ case SmMlAttributeValueMathvariant::sans_serif:
+ addAttribute(XML_MATHVARIANT, "sans-serif");
+ break;
+ case SmMlAttributeValueMathvariant::monospace:
+ addAttribute(XML_MATHVARIANT, "monospace");
+ break;
+ case SmMlAttributeValueMathvariant::bold_italic:
+ addAttribute(XML_MATHVARIANT, "bold-italic");
+ break;
+ case SmMlAttributeValueMathvariant::bold_fraktur:
+ addAttribute(XML_MATHVARIANT, "bold-fracktur");
+ break;
+ case SmMlAttributeValueMathvariant::bold_script:
+ addAttribute(XML_MATHVARIANT, "bold-script");
+ break;
+ case SmMlAttributeValueMathvariant::bold_sans_serif:
+ addAttribute(XML_MATHVARIANT, "bold-sans-serif");
+ break;
+ case SmMlAttributeValueMathvariant::sans_serif_italic:
+ addAttribute(XML_MATHVARIANT, "sans-serif-italic");
+ break;
+ case SmMlAttributeValueMathvariant::sans_serif_bold_italic:
+ addAttribute(XML_MATHVARIANT, "sans-serif-bold-italic");
+ break;
+ case SmMlAttributeValueMathvariant::initial:
+ addAttribute(XML_MATHVARIANT, "initial");
+ break;
+ case SmMlAttributeValueMathvariant::tailed:
+ addAttribute(XML_MATHVARIANT, "tailed");
+ break;
+ case SmMlAttributeValueMathvariant::looped:
+ addAttribute(XML_MATHVARIANT, "looped");
+ break;
+ case SmMlAttributeValueMathvariant::stretched:
+ addAttribute(XML_MATHVARIANT, "stretched");
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlMaxsize:
+ {
+ auto aSizeData = aAttribute.getMlMaxsize();
+ auto aLengthData = aSizeData->m_aLengthValue;
+ switch (aSizeData->m_aMaxsize)
+ {
+ case SmMlAttributeValueMaxsize::MlInfinity:
+ {
+ addAttribute(XML_MAXSIZE, XML_INFINITY);
+ break;
+ }
+ case SmMlAttributeValueMaxsize::MlFinite:
+ {
+ exportMlAttributeLength(XML_MAXSIZE, aLengthData);
+ break;
+ }
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlMinsize:
+ {
+ auto aSizeData = aAttribute.getMlMinsize();
+ auto aLengthData = aSizeData->m_aLengthValue;
+ exportMlAttributeLength(XML_MINSIZE, aLengthData);
+ break;
+ }
+ case SmMlAttributeValueType::MlMovablelimits:
+ {
+ auto aAttributeValue = aAttribute.getMlMovablelimits();
+ switch (aAttributeValue->m_aMovablelimits)
+ {
+ case SmMlAttributeValueMovablelimits::MlFalse:
+ addAttribute(XML_MOVABLELIMITS, XML_FALSE);
+ break;
+ case SmMlAttributeValueMovablelimits::MlTrue:
+ addAttribute(XML_MOVABLELIMITS, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlRspace:
+ {
+ auto aSizeData = aAttribute.getMlRspace();
+ auto aLengthData = aSizeData->m_aLengthValue;
+ exportMlAttributeLength(XML_RSPACE, aLengthData);
+ break;
+ }
+ case SmMlAttributeValueType::MlSeparator:
+ {
+ auto aAttributeValue = aAttribute.getMlSeparator();
+ switch (aAttributeValue->m_aSeparator)
+ {
+ case SmMlAttributeValueSeparator::MlFalse:
+ addAttribute(XML_SEPARATOR, XML_FALSE);
+ break;
+ case SmMlAttributeValueSeparator::MlTrue:
+ addAttribute(XML_SEPARATOR, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlStretchy:
+ {
+ auto aAttributeValue = aAttribute.getMlStretchy();
+ switch (aAttributeValue->m_aStretchy)
+ {
+ case SmMlAttributeValueStretchy::MlFalse:
+ addAttribute(XML_STRETCHY, XML_FALSE);
+ break;
+ case SmMlAttributeValueStretchy::MlTrue:
+ addAttribute(XML_STRETCHY, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ case SmMlAttributeValueType::MlSymmetric:
+ {
+ auto aAttributeValue = aAttribute.getMlSymmetric();
+ switch (aAttributeValue->m_aSymmetric)
+ {
+ case SmMlAttributeValueSymmetric::MlFalse:
+ addAttribute(XML_SYMMETRIC, XML_FALSE);
+ break;
+ case SmMlAttributeValueSymmetric::MlTrue:
+ addAttribute(XML_SYMMETRIC, XML_TRUE);
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ break;
+ }
+ default:
+ declareMlError();
+ break;
+ }
+ }
+}
+
+SvXMLElementExport* SmMLExport::exportMlElement(const SmMlElement* pMlElement)
+{
+ SvXMLElementExport* pElementExport;
+ switch (pMlElement->getMlElementType())
+ {
+ case SmMlElementType::MlMath:
+ pElementExport = createElementExport(XML_MATH);
+ break;
+ case SmMlElementType::MlMi:
+ pElementExport = createElementExport(XML_MI);
+ break;
+ case SmMlElementType::MlMerror:
+ pElementExport = createElementExport(XML_MERROR);
+ break;
+ case SmMlElementType::MlMn:
+ pElementExport = createElementExport(XML_MN);
+ break;
+ case SmMlElementType::MlMo:
+ pElementExport = createElementExport(XML_MO);
+ break;
+ case SmMlElementType::MlMrow:
+ pElementExport = createElementExport(XML_MROW);
+ break;
+ case SmMlElementType::MlMtext:
+ pElementExport = createElementExport(XML_MTEXT);
+ break;
+ case SmMlElementType::MlMstyle:
+ pElementExport = createElementExport(XML_MSTYLE);
+ break;
+ default:
+ pElementExport = nullptr;
+ }
+ const OUString& aElementText = pMlElement->getText();
+ exportMlAttributes(pMlElement);
+ if (aElementText.isEmpty())
+ GetDocHandler()->characters(aElementText);
+ return pElementExport;
+}
+
+namespace
+{
+struct exportMlElementTreeExecData
+{
+private:
+ SmMLExport* m_pSmMLExport;
+ std::vector<SvXMLElementExport*> m_aSvXMLElementExportList;
+ size_t m_nDepth;
+
+public:
+ inline exportMlElementTreeExecData(SmMLExport* pSmMLExport)
+ : m_pSmMLExport(pSmMLExport)
+ , m_aSvXMLElementExportList(1024)
+ , m_nDepth(0)
+ {
+ }
+
+ inline void deleteDepthData()
+ {
+ delete m_aSvXMLElementExportList[m_nDepth];
+ --m_nDepth;
+ }
+
+ inline void setDepthData(SvXMLElementExport* aSvXMLElementExportList)
+ {
+ if (m_nDepth == m_aSvXMLElementExportList.size())
+ m_aSvXMLElementExportList.resize(m_aSvXMLElementExportList.size() + 1024);
+ m_aSvXMLElementExportList[m_nDepth] = aSvXMLElementExportList;
+ }
+
+ inline void incrementDepth() { ++m_nDepth; }
+
+ inline SmMLExport* getSmMLExport() { return m_pSmMLExport; };
+};
+
+} // end unnamed namespace
+
+static inline void exportMlElementTreeExec(SmMlElement* aSmMlElement, void* aData)
+{
+ // Prepare data
+ exportMlElementTreeExecData* pData = static_cast<exportMlElementTreeExecData*>(aData);
+ pData->setDepthData(pData->getSmMLExport()->exportMlElement(aSmMlElement));
+
+ // Prepare for following
+ // If it has sub elements, then it will be the next
+ if (aSmMlElement->getSubElementsCount() != 0)
+ pData->incrementDepth();
+ else // Otherwise remounts up to where it should be
+ {
+ while (aSmMlElement->getParentElement() != nullptr)
+ {
+ // get parent
+ SmMlElement* pParent = aSmMlElement->getParentElement();
+ pData->deleteDepthData();
+ // was this the last branch ?
+ if (aSmMlElement->getSubElementId() + 1 != pParent->getSubElementsCount()) // yes -> up
+ break; // no -> stop going up
+ // Prepare for next round
+ aSmMlElement = pParent;
+ }
+ }
+}
+
+void SmMLExport::exportMlElementTree()
+{
+ exportMlElementTreeExecData* aData = new exportMlElementTreeExecData(this);
+ mathml::SmMlIteratorTopToBottom(m_pElementTree, exportMlElementTreeExec, aData);
+ delete aData;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathml/import.cxx b/starmath/source/mathml/import.cxx
new file mode 100644
index 000000000..52d47028e
--- /dev/null
+++ b/starmath/source/mathml/import.cxx
@@ -0,0 +1,1430 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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/.
+ */
+
+// Our mathml
+#include <mathml/import.hxx>
+
+// LO tools to use
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+
+// Extra LO tools
+#include <comphelper/fileformat.h>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <rtl/character.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sot/storage.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <unotools/streamwrap.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <o3tl/string_view.hxx>
+
+// Our starmath tools
+#include <cfgitem.hxx>
+#include <document.hxx>
+#include <xparsmlbase.hxx>
+#include <smmod.hxx>
+#include <starmathdatabase.hxx>
+#include <unomodel.hxx>
+
+// Old parser
+#include <mathmlimport.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace com::sun::star::xml::sax;
+using namespace ::xmloff::token;
+
+// SmMLImportContext
+/*************************************************************************************************/
+
+SmMlElement* SmMLImportWrapper::getElementTree()
+{
+ return m_pMlImport == nullptr ? nullptr : m_pMlImport->getElementTree();
+}
+
+ErrCode SmMLImportWrapper::Import(SfxMedium& rMedium)
+{
+ // Fetch context
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ if (!xContext.is())
+ {
+ SAL_WARN("starmath", "Failed to fetch model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Check model
+ if (!m_xModel.is())
+ {
+ SAL_WARN("starmath", "Failed to fetch model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Make a model component from our SmModel
+ uno::Reference<lang::XComponent> xModelComp = m_xModel;
+ if (!xModelComp.is())
+ {
+ SAL_WARN("starmath", "Failed to make model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ // Get model via uno
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel);
+ if (pModel == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch sm model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Get doc shell
+ m_pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (m_pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch smdoc shell while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Check if it is an embed object
+ bool bEmbedded = m_pDocShell->GetCreateMode() == SfxObjectCreateMode::EMBEDDED;
+
+ if (!bEmbedded)
+ {
+ // Extra check to ensure everything is fine
+ if (m_pDocShell->GetMedium() != &rMedium)
+ {
+ SAL_WARN("starmath", "Given medium and doc shell medium differ while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Fetch the item set
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem != nullptr)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+ }
+
+ // Create property list
+ static const comphelper::PropertyMapEntry aInfoMap[]
+ = { { u"PrivateData", 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"BaseURI", 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID,
+ 0 },
+ { u"StreamRelPath", 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamName", 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 } };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Set base URI
+ // needed for relative URLs; but it's OK to import e.g. MathML from the clipboard without one
+ SAL_INFO_IF(rMedium.GetBaseURL().isEmpty(), "starmath", "SmMLImportWrapper: no base URL");
+ xInfoSet->setPropertyValue("BaseURI", Any(rMedium.GetBaseURL()));
+
+ // Fetch progress range
+ sal_Int32 nProgressRange(rMedium.IsStorage() ? 3 : 1);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ xStatusIndicator->setValue(0);
+ }
+
+ // Get storage
+ if (rMedium.IsStorage())
+ {
+ // TODO/LATER: handle the case of embedded links gracefully
+ if (bEmbedded) // && !rMedium.GetStorage()->IsRoot() )
+ {
+ OUString aName(u"dummyObjName");
+ if (rMedium.GetItemSet())
+ {
+ const SfxStringItem* pDocHierarchItem
+ = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem != nullptr)
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if (!aName.isEmpty())
+ xInfoSet->setPropertyValue("StreamRelPath", Any(aName));
+ }
+
+ // Check if use OASIS ( new document format )
+ bool bOASIS = SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(1);
+
+ // Error code in case of needed
+ ErrCode nWarn = ERRCODE_NONE;
+
+ // Read metadata
+ // read a component from storage
+ if (!bEmbedded)
+ {
+ if (bOASIS)
+ nWarn = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"meta.xml",
+ xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisMetaImporter", 6);
+ else
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"meta.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.XMLMetaImporter", 5);
+ }
+
+ // Check if successful
+ if (nWarn != ERRCODE_NONE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ SAL_WARN("starmath", "Failed to read file");
+ return nWarn;
+ }
+
+ // Increase success indicator
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(2);
+
+ // Read settings
+ // read a component from storage
+ if (bOASIS)
+ nWarn = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"settings.xml",
+ xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLOasisSettingsImporter", 6);
+ else
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"settings.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.XMLSettingsImporter", 5);
+
+ // Check if successful
+ if (nWarn == ERRCODE_IO_BROKENPACKAGE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ SAL_WARN("starmath", "Failed to read file");
+ return nWarn;
+ }
+
+ // Increase success indicator
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(3);
+
+ // Read document
+ // read a component from storage
+ if (m_pDocShell->GetSmSyntaxVersion() == 5)
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"content.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.XMLImporter", 5);
+ else
+ nWarn
+ = ReadThroughComponentS(rMedium.GetStorage(), xModelComp, u"content.xml", xContext,
+ xInfoSet, u"com.sun.star.comp.Math.MLImporter", 6);
+ // Check if successful
+ if (nWarn != ERRCODE_NONE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ SAL_WARN("starmath", "Failed to read file");
+ return nWarn;
+ }
+
+ // Finish
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return ERRCODE_NONE;
+ }
+ else
+ {
+ // Create input stream
+ Reference<io::XInputStream> xInputStream
+ = new utl::OInputStreamWrapper(rMedium.GetInStream());
+
+ // Increase success indicator
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(1);
+
+ // Read data
+ // read a component from input stream
+ ErrCode nError = ERRCODE_NONE;
+ if (m_pDocShell->GetSmSyntaxVersion() == 5)
+ nError = ReadThroughComponentIS(xInputStream, xModelComp, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.XMLImporter", false, 5);
+ else
+ nError = ReadThroughComponentIS(xInputStream, xModelComp, xContext, xInfoSet,
+ u"com.sun.star.comp.Math.MLImporter", false, 6);
+
+ // Finish
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ // Declare any error
+ if (nError != ERRCODE_NONE)
+ SAL_WARN("starmath", "Failed to read file");
+
+ return nError;
+ }
+}
+
+ErrCode SmMLImportWrapper::Import(std::u16string_view aSource)
+{
+ // Fetch context
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ if (!xContext.is())
+ {
+ SAL_WARN("starmath", "Failed to fetch model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Check model
+ if (!m_xModel.is())
+ {
+ SAL_WARN("starmath", "Failed to fetch model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Make a model component from our SmModel
+ uno::Reference<lang::XComponent> xModelComp = m_xModel;
+ if (!xModelComp.is())
+ {
+ SAL_WARN("starmath", "Failed to make model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Get model via uno
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(m_xModel);
+ if (pModel == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch sm model while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Get doc shell
+ m_pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (m_pDocShell == nullptr)
+ {
+ SAL_WARN("starmath", "Failed to fetch smdoc shell while file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Create property list
+ static const comphelper::PropertyMapEntry aInfoMap[]
+ = { { u"PrivateData", 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"BaseURI", 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID,
+ 0 },
+ { u"StreamRelPath", 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { u"StreamName", 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 } };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Read data
+ // read a component from text
+ ErrCode nError = ReadThroughComponentMS(aSource, xModelComp, xContext, xInfoSet);
+
+ // Declare any error
+ if (nError != ERRCODE_NONE)
+ {
+ SAL_WARN("starmath", "Failed to read file");
+ return nError;
+ }
+
+ return ERRCODE_NONE;
+}
+
+// read a component from input stream
+ErrCode SmMLImportWrapper::ReadThroughComponentIS(
+ const Reference<io::XInputStream>& xInputStream, const Reference<XComponent>& xModelComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet, const char16_t* pFilterName, bool bEncrypted,
+ int_fast16_t nSyntaxVersion)
+{
+ // Needs an input stream but checked by caller
+ // Needs a context but checked by caller
+ // Needs property set but checked by caller
+ // Needs a filter name but checked by caller
+
+ // Prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ // Prepare property list
+ Sequence<Any> aArgs{ Any(rPropSet) };
+
+ // Get filter
+ Reference<XInterface> xFilter
+ = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString(pFilterName), aArgs, rxContext);
+ if (!xFilter.is())
+ {
+ SAL_WARN("starmath", "Can't instantiate filter component " << OUString(pFilterName));
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+
+ // Connect model and filter
+ Reference<XImporter> xImporter(xFilter, UNO_QUERY);
+ xImporter->setTargetDocument(xModelComponent);
+
+ // Finally, parser the stream
+ try
+ {
+ Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY);
+ Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY);
+ if (xFastParser)
+ {
+ xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xFastParser->parseStream(aParserInput);
+ }
+ else if (xFastDocHandler)
+ {
+ Reference<css::xml::sax::XFastParser> xParser
+ = css::xml::sax::FastParser::create(rxContext);
+ xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xParser->setFastDocumentHandler(xFastDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+ else
+ {
+ Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY);
+ assert(xDocHandler);
+ Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext);
+ xParser->setDocumentHandler(xDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+
+ if (nSyntaxVersion == 5)
+ {
+ SmXMLImport* pXMlImport = comphelper::getFromUnoTunnel<SmXMLImport>(xFilter);
+ if (pXMlImport != nullptr && pXMlImport->GetSuccess())
+ return ERRCODE_NONE;
+ else
+ {
+ SAL_WARN("starmath", "Filter failed on file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+ }
+
+ m_pMlImport = comphelper::getFromUnoTunnel<SmMLImport>(xFilter);
+ if (m_pMlImport != nullptr && m_pMlImport->getSuccess())
+ return ERRCODE_NONE;
+ else
+ {
+ SAL_WARN("starmath", "Filter failed on file input");
+ return ERRCODE_SFX_DOLOADFAILED;
+ }
+ }
+ catch (const xml::sax::SAXParseException& r)
+ {
+ // Sax parser sends wrapped exceptions, try to find the original one
+ xml::sax::SAXException aTmp;
+ xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r);
+ while (aSaxEx.WrappedException >>= aTmp)
+ aSaxEx = aTmp;
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if (aSaxEx.WrappedException >>= aBrokenPackage)
+ {
+ SAL_WARN("starmath", "Failed to read file SAXParseException");
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+
+ if (bEncrypted)
+ {
+ SAL_WARN("starmath", "Wrong file password SAXParseException");
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ }
+ catch (const xml::sax::SAXException& r)
+ {
+ packages::zip::ZipIOException aBrokenPackage;
+ if (r.WrappedException >>= aBrokenPackage)
+ {
+ SAL_WARN("starmath", "Failed to read file SAXException");
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+
+ if (bEncrypted)
+ {
+ SAL_WARN("starmath", "Wrong file password SAXException");
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ }
+ catch (const packages::zip::ZipIOException&)
+ {
+ SAL_WARN("starmath", "Failed to unzip file ZipIOException");
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (const io::IOException&)
+ {
+ SAL_WARN("starmath", "Failed to read file ZipIOException");
+ return ERRCODE_IO_UNKNOWN;
+ }
+ catch (const std::range_error&)
+ {
+ SAL_WARN("starmath", "Failed to read file");
+ return ERRCODE_ABORT;
+ }
+
+ return ERRCODE_ABORT;
+}
+
+// read a component from storage
+ErrCode SmMLImportWrapper::ReadThroughComponentS(const uno::Reference<embed::XStorage>& xStorage,
+ const Reference<XComponent>& xModelComponent,
+ const char16_t* pStreamName,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char16_t* pFilterName,
+ int_fast16_t nSyntaxVersion)
+{
+ // Needs a storage but checked by caller
+ // Needs a model but checked by caller
+ // Needs a stream name but checked by caller
+ // Needs a context but checked by caller
+ // Needs a property set but checked by caller
+ // Needs a filter name but checked by caller
+
+ // Get the input stream
+ try
+ {
+ // Create the stream for the event read
+ uno::Reference<io::XStream> xEventsStream
+ = xStorage->openStreamElement(OUString(pStreamName), embed::ElementModes::READ);
+
+ // Determine if stream is encrypted or not
+ uno::Reference<beans::XPropertySet> xProps(xEventsStream, uno::UNO_QUERY);
+ Any aAny = xProps->getPropertyValue("Encrypted");
+ bool bEncrypted = false;
+ aAny >>= bEncrypted;
+
+ // Set base URL and open stream
+ rPropSet->setPropertyValue("StreamName", Any(OUString(pStreamName)));
+ Reference<io::XInputStream> xStream = xEventsStream->getInputStream();
+
+ // Execute read
+ return ReadThroughComponentIS(xStream, xModelComponent, rxContext, rPropSet, pFilterName,
+ bEncrypted, nSyntaxVersion);
+ }
+ catch (packages::WrongPasswordException&)
+ {
+ SAL_WARN("starmath", "Wrong file password");
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (packages::zip::ZipIOException&)
+ {
+ SAL_WARN("starmath", "Failed to unzip file");
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (uno::Exception&)
+ {
+ }
+
+ return ERRCODE_SFX_DOLOADFAILED;
+}
+
+// read a component from text
+ErrCode SmMLImportWrapper::ReadThroughComponentMS(
+ std::u16string_view aText, const css::uno::Reference<css::lang::XComponent>& xModelComponent,
+ css::uno::Reference<css::uno::XComponentContext> const& rxContext,
+ css::uno::Reference<css::beans::XPropertySet> const& rPropSet)
+{
+ // Needs a storage but checked by caller
+ // Needs a model but checked by caller
+ // Needs a stream name but checked by caller
+ // Needs a context but checked by caller
+ // Needs a property set but checked by caller
+ // Needs a filter name but checked by caller
+
+ // Get the input stream
+ try
+ {
+ // Generate input memory stream
+ SvMemoryStream aMemoryStream;
+ aMemoryStream.WriteOString(OUStringToOString(aText, RTL_TEXTENCODING_UTF8));
+ uno::Reference<io::XInputStream> xStream(new utl::OInputStreamWrapper(aMemoryStream));
+
+ // Execute read
+ return ReadThroughComponentIS(xStream, xModelComponent, rxContext, rPropSet,
+ u"com.sun.star.comp.Math.MLImporter", false, 6);
+ }
+ catch (packages::WrongPasswordException&)
+ {
+ SAL_WARN("starmath", "Wrong file password");
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (packages::zip::ZipIOException&)
+ {
+ SAL_WARN("starmath", "Failed to unzip file");
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (uno::Exception&)
+ {
+ }
+
+ return ERRCODE_SFX_DOLOADFAILED;
+}
+
+// SmMLImport technical
+/*************************************************************************************************/
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_MLImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(
+ new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLImporter", SvXMLImportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_MLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisMetaImporter",
+ SvXMLImportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_MLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS));
+}
+
+// SmMLImportContext
+/*************************************************************************************************/
+
+namespace
+{
+class SmMLImportContext : public SvXMLImportContext
+{
+private:
+ SmMlElement** m_pParent;
+ SmMlElement* m_pElement;
+ SmMlElement* m_pStyle;
+
+public:
+ SmMLImportContext(SmMLImport& rImport, SmMlElement** pParent)
+ : SvXMLImportContext(rImport)
+ , m_pParent(pParent)
+ , m_pElement(nullptr)
+ , m_pStyle(nullptr)
+ {
+ }
+
+private:
+ void declareMlError();
+
+public:
+ /** Handles characters (text)
+ */
+ virtual void SAL_CALL characters(const OUString& aChars) override;
+
+ /** Starts the mathml element
+ */
+ virtual void SAL_CALL startFastElement(
+ sal_Int32 nElement, const Reference<XFastAttributeList>& aAttributeList) override;
+
+ /** Ends the mathml element
+ */
+ virtual void SAL_CALL endFastElement(sal_Int32 Element) override;
+
+ /** Creates child element
+ */
+ virtual uno::Reference<XFastContextHandler>
+ SAL_CALL createFastChildContext(sal_Int32 nElement,
+ const uno::Reference<XFastAttributeList>& Attribs) override;
+
+ /** Inherits the style from it's parents
+ */
+ void inheritStyle();
+
+ /** Inherits the style from it's parents on end
+ */
+ void inheritStyleEnd();
+
+ /** Handle mathml attributes
+ */
+ void handleAttributes(const Reference<XFastAttributeList>& aAttributeList);
+
+ /** Handle mathml length attributes
+ */
+ SmLengthValue handleLengthAttribute(const OUString& aAttribute);
+};
+
+uno::Reference<XFastContextHandler> SAL_CALL
+SmMLImportContext::createFastChildContext(sal_Int32, const uno::Reference<XFastAttributeList>&)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+ xContext = new SmMLImportContext(static_cast<SmMLImport&>(GetImport()), &m_pElement);
+ return xContext;
+}
+
+void SmMLImportContext::declareMlError()
+{
+ SmMLImport& aSmMLImport = static_cast<SmMLImport&>(GetImport());
+ aSmMLImport.declareMlError();
+}
+
+void SmMLImportContext::inheritStyle()
+{
+ while ((m_pStyle = m_pStyle->getParentElement()) != nullptr)
+ {
+ if (m_pStyle->getParentElement()->getMlElementType() == SmMlElementType::MlMstyle
+ || m_pStyle->getParentElement()->getMlElementType() == SmMlElementType::MlMath)
+ break;
+ }
+
+ // Parent inheritation
+ // Mathcolor, mathsize, dir and displaystyle are inherited from parent
+ SmMlElement* pParent = *m_pParent;
+ m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlMathcolor));
+ m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlMathsize));
+ m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlDir));
+ m_pElement->setAttribute(pParent->getAttribute(SmMlAttributeValueType::MlDisplaystyle));
+
+ // Inherit operator dictionary overwrites
+ if (m_pStyle != nullptr
+ && (m_pElement->getMlElementType() == SmMlElementType::MlMo
+ || m_pElement->getMlElementType() == SmMlElementType::MlMstyle
+ || m_pElement->getMlElementType() == SmMlElementType::MlMath))
+ {
+ // TODO fetch operator dictionary first and then overwrite
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlAccent))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlAccent));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlFence))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlFence));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlLspace))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlLspace));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMaxsize))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMaxsize));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMinsize))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMinsize));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMovablelimits))
+ m_pElement->setAttribute(
+ m_pStyle->getAttribute(SmMlAttributeValueType::MlMovablelimits));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlRspace))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlRspace));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlSeparator))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlSeparator));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlStretchy))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlStretchy));
+ if (m_pStyle->isAttributeSet(SmMlAttributeValueType::MlSymmetric))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlSymmetric));
+
+ if (m_pElement->getMlElementType() == SmMlElementType::MlMo)
+ {
+ // Set form based in position
+ SmMlAttribute aAttribute(SmMlAttributeValueType::MlForm);
+ SmMlForm aForm;
+ if (m_pElement->getSubElementId() == 0)
+ aForm = { SmMlAttributeValueForm::MlPrefix };
+ else
+ aForm = { SmMlAttributeValueForm::MlInfix };
+ aAttribute.setMlForm(&aForm);
+ m_pElement->setAttribute(aAttribute);
+ }
+ }
+
+ // Inherit mathvariant
+ if (m_pStyle && m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMathvariant))
+ m_pElement->setAttribute(m_pStyle->getAttribute(SmMlAttributeValueType::MlMathvariant));
+}
+
+void SmMLImportContext::inheritStyleEnd()
+{
+ // Mo: check it is the end: postfix
+ if (m_pElement->getMlElementType() == SmMlElementType::MlMo)
+ {
+ if ((*m_pParent)->getSubElementsCount() == m_pElement->getSubElementId())
+ {
+ // Set form based in position
+ SmMlAttribute aAttribute(SmMlAttributeValueType::MlForm);
+ SmMlForm aForm = { SmMlAttributeValueForm::MlPosfix };
+ aAttribute.setMlForm(&aForm);
+ m_pElement->setAttribute(aAttribute);
+ }
+ }
+
+ // Mi: 1 char -> italic
+ if (m_pElement->getMlElementType() != SmMlElementType::MlMi)
+ return;
+
+ // Inherit mathvariant
+ if (!m_pStyle->isAttributeSet(SmMlAttributeValueType::MlMathvariant))
+ {
+ sal_Int32 nIndexUtf16 = 0;
+ // Check if there is only one code point
+ m_pElement->getText().iterateCodePoints(&nIndexUtf16, 1);
+ // Mathml says that 1 code point -> italic
+ if (nIndexUtf16 == m_pElement->getText().getLength())
+ {
+ SmMlAttribute aAttribute(SmMlAttributeValueType::MlMathvariant);
+ SmMlMathvariant aMathvariant = { SmMlAttributeValueMathvariant::italic };
+ aAttribute.setMlMathvariant(&aMathvariant);
+ aAttribute.setSet(false);
+ m_pElement->setAttribute(aAttribute);
+ }
+ }
+}
+
+SmLengthValue SmMLImportContext::handleLengthAttribute(const OUString& aAttribute)
+{
+ // Locate unit indication
+ int32_t nUnitPos;
+ for (nUnitPos = 0;
+ nUnitPos < aAttribute.getLength()
+ && (rtl::isAsciiHexDigit(aAttribute[nUnitPos]) || aAttribute[nUnitPos] == '.');
+ ++nUnitPos)
+ ;
+
+ // Find unit
+ SmLengthUnit nUnit = SmLengthUnit::MlM;
+ if (nUnitPos != aAttribute.getLength())
+ {
+ OUString aUnit = aAttribute.copy(nUnitPos);
+ if (aUnit.compareToIgnoreAsciiCaseAscii("ex"))
+ nUnit = SmLengthUnit::MlEx;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("px"))
+ nUnit = SmLengthUnit::MlPx;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("in"))
+ nUnit = SmLengthUnit::MlIn;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("cm"))
+ nUnit = SmLengthUnit::MlCm;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("mm"))
+ nUnit = SmLengthUnit::MlMm;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("pt"))
+ nUnit = SmLengthUnit::MlPt;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("pc"))
+ nUnit = SmLengthUnit::MlPc;
+ if (aUnit.compareToIgnoreAsciiCaseAscii("%"))
+ nUnit = SmLengthUnit::MlP;
+ else
+ declareMlError();
+ }
+
+ // Get value
+ std::u16string_view aValue = aAttribute.subView(0, nUnitPos);
+ double nValue = o3tl::toDouble(aValue);
+ if (nValue == 0)
+ {
+ nUnit = SmLengthUnit::MlM;
+ nValue = 1.0;
+ declareMlError();
+ }
+
+ // Return
+ SmLengthValue aLengthValue = { nUnit, nValue, new OUString(aAttribute) };
+ return aLengthValue;
+}
+
+void SmMLImportContext::handleAttributes(const Reference<XFastAttributeList>& aAttributeList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(aAttributeList))
+ {
+ SmMlAttribute aAttribute(SmMlAttributeValueType::NMlEmpty);
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_ACCENT:
+ {
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlAccent);
+ SmMlAccent aAccent = { SmMlAttributeValueAccent::MlTrue };
+ aAttribute.setMlAccent(&aAccent);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlAccent);
+ SmMlAccent aAccent = { SmMlAttributeValueAccent::MlFalse };
+ aAttribute.setMlAccent(&aAccent);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ }
+ case XML_DIR:
+ {
+ if (IsXMLToken(aIter, XML_RTL))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDir);
+ SmMlDir aDir = { SmMlAttributeValueDir::MlRtl };
+ aAttribute.setMlDir(&aDir);
+ }
+ else if (IsXMLToken(aIter, XML_LTR))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDir);
+ SmMlDir aDir = { SmMlAttributeValueDir::MlLtr };
+ aAttribute.setMlDir(&aDir);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ }
+ case XML_DISPLAYSTYLE:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDisplaystyle);
+ SmMlDisplaystyle aDisplaystyle = { SmMlAttributeValueDisplaystyle::MlTrue };
+ aAttribute.setMlDisplaystyle(&aDisplaystyle);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlDisplaystyle);
+ SmMlDisplaystyle aDisplaystyle = { SmMlAttributeValueDisplaystyle::MlFalse };
+ aAttribute.setMlDisplaystyle(&aDisplaystyle);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ case XML_FENCE:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlFence);
+ SmMlFence aFence = { SmMlAttributeValueFence::MlTrue };
+ aAttribute.setMlFence(&aFence);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlFence);
+ SmMlFence aFence = { SmMlAttributeValueFence::MlFalse };
+ aAttribute.setMlFence(&aFence);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ case XML_HREF:
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlHref);
+ OUString* aRef = new OUString(aIter.toString());
+ SmMlHref aHref = { SmMlAttributeValueHref::NMlValid, aRef };
+ aAttribute.setMlHref(&aHref);
+ break;
+ }
+ case XML_LSPACE:
+ {
+ SmMlLspace aLspace;
+ aLspace.m_aLengthValue = handleLengthAttribute(aIter.toString());
+ aAttribute.setMlLspace(&aLspace);
+ break;
+ }
+ case XML_MATHBACKGROUND:
+ {
+ if (IsXMLToken(aIter, XML_TRANSPARENT))
+ {
+ SmMlMathbackground aMathbackground
+ = { SmMlAttributeValueMathbackground::MlTransparent, COL_TRANSPARENT };
+ aAttribute.setMlMathbackground(&aMathbackground);
+ }
+ else
+ {
+ Color aColor
+ = starmathdatabase::Identify_ColorName_HTML(aIter.toString()).cColor;
+ SmMlMathbackground aMathbackground
+ = { SmMlAttributeValueMathbackground::MlRgb, aColor };
+ aAttribute.setMlMathbackground(&aMathbackground);
+ }
+ break;
+ }
+ case XML_MATHCOLOR:
+ {
+ if (IsXMLToken(aIter, XML_DEFAULT))
+ {
+ SmMlMathcolor aMathcolor
+ = { SmMlAttributeValueMathcolor::MlDefault, COL_BLACK };
+ aAttribute.setMlMathcolor(&aMathcolor);
+ }
+ else
+ {
+ Color aColor
+ = starmathdatabase::Identify_ColorName_HTML(aIter.toString()).cColor;
+ SmMlMathcolor aMathcolor = { SmMlAttributeValueMathcolor::MlRgb, aColor };
+ aAttribute.setMlMathcolor(&aMathcolor);
+ }
+ break;
+ }
+ case XML_MATHSIZE:
+ {
+ SmMlMathsize aMathsize;
+ aMathsize.m_aLengthValue = handleLengthAttribute(aIter.toString());
+ aAttribute.setMlMathsize(&aMathsize);
+ break;
+ }
+ case XML_MATHVARIANT:
+ {
+ OUString aVariant = aIter.toString();
+ SmMlAttributeValueMathvariant nVariant = SmMlAttributeValueMathvariant::normal;
+ if (aVariant.compareTo(u"normal"))
+ nVariant = SmMlAttributeValueMathvariant::normal;
+ else if (aVariant.compareTo(u"bold"))
+ nVariant = SmMlAttributeValueMathvariant::bold;
+ else if (aVariant.compareTo(u"italic"))
+ nVariant = SmMlAttributeValueMathvariant::italic;
+ else if (aVariant.compareTo(u"double-struck"))
+ nVariant = SmMlAttributeValueMathvariant::double_struck;
+ else if (aVariant.compareTo(u"script"))
+ nVariant = SmMlAttributeValueMathvariant::script;
+ else if (aVariant.compareTo(u"fraktur"))
+ nVariant = SmMlAttributeValueMathvariant::fraktur;
+ else if (aVariant.compareTo(u"sans-serif"))
+ nVariant = SmMlAttributeValueMathvariant::sans_serif;
+ else if (aVariant.compareTo(u"monospace"))
+ nVariant = SmMlAttributeValueMathvariant::monospace;
+ else if (aVariant.compareTo(u"bold-italic"))
+ nVariant = SmMlAttributeValueMathvariant::bold_italic;
+ else if (aVariant.compareTo(u"bold-fracktur"))
+ nVariant = SmMlAttributeValueMathvariant::bold_fraktur;
+ else if (aVariant.compareTo(u"bold-script"))
+ nVariant = SmMlAttributeValueMathvariant::bold_script;
+ else if (aVariant.compareTo(u"bold-sans-serif"))
+ nVariant = SmMlAttributeValueMathvariant::bold_sans_serif;
+ else if (aVariant.compareTo(u"sans-serif-italic"))
+ nVariant = SmMlAttributeValueMathvariant::sans_serif_italic;
+ else if (aVariant.compareTo(u"sans-serif-bold-italic"))
+ nVariant = SmMlAttributeValueMathvariant::sans_serif_bold_italic;
+ else if (aVariant.compareTo(u"initial"))
+ nVariant = SmMlAttributeValueMathvariant::initial;
+ else if (aVariant.compareTo(u"tailed"))
+ nVariant = SmMlAttributeValueMathvariant::tailed;
+ else if (aVariant.compareTo(u"looped"))
+ nVariant = SmMlAttributeValueMathvariant::looped;
+ else if (aVariant.compareTo(u"stretched"))
+ nVariant = SmMlAttributeValueMathvariant::stretched;
+ else
+ declareMlError();
+ SmMlMathvariant aMathvariant = { nVariant };
+ aAttribute.setMlMathvariant(&aMathvariant);
+ break;
+ }
+ case XML_MAXSIZE:
+ {
+ SmMlMaxsize aMaxsize;
+ if (IsXMLToken(aIter, XML_INFINITY))
+ {
+ aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlInfinity;
+ aMaxsize.m_aLengthValue = { SmLengthUnit::MlP, 10000, new OUString(u"10000%") };
+ }
+ else
+ {
+ aMaxsize.m_aMaxsize = SmMlAttributeValueMaxsize::MlFinite;
+ aMaxsize.m_aLengthValue = handleLengthAttribute(aIter.toString());
+ }
+ aAttribute.setMlMaxsize(&aMaxsize);
+ break;
+ }
+ case XML_MINSIZE:
+ {
+ SmMlMinsize aMinsize;
+ aMinsize.m_aLengthValue = handleLengthAttribute(aIter.toString());
+ aAttribute.setMlMinsize(&aMinsize);
+ break;
+ }
+ case XML_MOVABLELIMITS:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlMovablelimits);
+ SmMlMovablelimits aMovablelimits = { SmMlAttributeValueMovablelimits::MlTrue };
+ aAttribute.setMlMovablelimits(&aMovablelimits);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlMovablelimits);
+ SmMlMovablelimits aMovablelimits = { SmMlAttributeValueMovablelimits::MlFalse };
+ aAttribute.setMlMovablelimits(&aMovablelimits);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ case XML_RSPACE:
+ {
+ SmMlRspace aRspace;
+ aRspace.m_aLengthValue = handleLengthAttribute(aIter.toString());
+ aAttribute.setMlRspace(&aRspace);
+ break;
+ }
+ case XML_SEPARATOR:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSeparator);
+ SmMlSeparator aSeparator = { SmMlAttributeValueSeparator::MlTrue };
+ aAttribute.setMlSeparator(&aSeparator);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSeparator);
+ SmMlSeparator aSeparator = { SmMlAttributeValueSeparator::MlFalse };
+ aAttribute.setMlSeparator(&aSeparator);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ case XML_STRETCHY:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlStretchy);
+ SmMlStretchy aStretchy = { SmMlAttributeValueStretchy::MlTrue };
+ aAttribute.setMlStretchy(&aStretchy);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlStretchy);
+ SmMlStretchy aStretchy = { SmMlAttributeValueStretchy::MlFalse };
+ aAttribute.setMlStretchy(&aStretchy);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ case XML_SYMMETRIC:
+ if (IsXMLToken(aIter, XML_TRUE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSymmetric);
+ SmMlSymmetric aSymmetric = { SmMlAttributeValueSymmetric::MlTrue };
+ aAttribute.setMlSymmetric(&aSymmetric);
+ }
+ else if (IsXMLToken(aIter, XML_FALSE))
+ {
+ aAttribute.setMlAttributeValueType(SmMlAttributeValueType::MlSymmetric);
+ SmMlSymmetric aSymmetric = { SmMlAttributeValueSymmetric::MlFalse };
+ aAttribute.setMlSymmetric(&aSymmetric);
+ }
+ else
+ {
+ declareMlError();
+ }
+ break;
+ default:
+ declareMlError();
+ break;
+ }
+ if (aAttribute.isNullAttribute())
+ declareMlError();
+ else
+ m_pElement->setAttribute(aAttribute);
+ }
+}
+
+void SmMLImportContext::characters(const OUString& aChars) { m_pElement->setText(aChars); }
+
+void SmMLImportContext::startFastElement(sal_Int32 nElement,
+ const Reference<XFastAttributeList>& aAttributeList)
+{
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MATH):
+ m_pElement = new SmMlElement(SmMlElementType::MlMath);
+ break;
+ case XML_ELEMENT(MATH, XML_MI):
+ m_pElement = new SmMlElement(SmMlElementType::MlMi);
+ break;
+ case XML_ELEMENT(MATH, XML_MERROR):
+ m_pElement = new SmMlElement(SmMlElementType::MlMerror);
+ break;
+ case XML_ELEMENT(MATH, XML_MN):
+ m_pElement = new SmMlElement(SmMlElementType::MlMn);
+ break;
+ case XML_ELEMENT(MATH, XML_MO):
+ m_pElement = new SmMlElement(SmMlElementType::MlMo);
+ break;
+ case XML_ELEMENT(MATH, XML_MROW):
+ m_pElement = new SmMlElement(SmMlElementType::MlMrow);
+ break;
+ case XML_ELEMENT(MATH, XML_MTEXT):
+ m_pElement = new SmMlElement(SmMlElementType::MlMtext);
+ break;
+ case XML_ELEMENT(MATH, XML_MSTYLE):
+ m_pElement = new SmMlElement(SmMlElementType::MlMstyle);
+ break;
+ default:
+ m_pElement = new SmMlElement(SmMlElementType::NMlEmpty);
+ declareMlError();
+ break;
+ }
+ SmMlElement* pParent = *m_pParent;
+ pParent->setSubElement(pParent->getSubElementsCount(), m_pElement);
+ inheritStyle();
+ handleAttributes(aAttributeList);
+}
+
+void SmMLImportContext::endFastElement(sal_Int32) { inheritStyleEnd(); }
+}
+
+// SmMLImport
+/*************************************************************************************************/
+
+const uno::Sequence<sal_Int8>& SmMLImport::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSmMLImportUnoTunnelId;
+ return theSmMLImportUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SmMLImport::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvXMLImport>{});
+}
+
+SvXMLImportContext*
+SmMLImport::CreateFastContext(sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT):
+ {
+ if (m_pElementTree == nullptr)
+ m_pElementTree = new SmMlElement(SmMlElementType::NMlEmpty);
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
+ uno::UNO_QUERY_THROW);
+ pContext = new SmMLImportContext(*this, &m_pElementTree);
+ break;
+ }
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_META):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
+ uno::UNO_QUERY_THROW);
+ pContext = new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties());
+ break;
+ }
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_SETTINGS):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
+ uno::UNO_QUERY_THROW);
+ pContext = new XMLDocumentSettingsContext(*this);
+ break;
+ }
+ default:
+ declareMlError();
+ break;
+ }
+ return pContext;
+}
+
+void SmMLImport::endDocument()
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing model");
+ SvXMLImport::endDocument();
+ return;
+ }
+
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+ if (!pModel)
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing sm model");
+ SvXMLImport::endDocument();
+ return;
+ }
+
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (!pDocShell)
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing sm doc shell");
+ SvXMLImport::endDocument();
+ return;
+ }
+
+ // Check if there is element tree
+ if (m_pElementTree == nullptr)
+ {
+ m_bSuccess = true;
+ SvXMLImport::endDocument();
+ return;
+ }
+
+ // Get element tree and setup
+
+ if (m_pElementTree->getSubElementsCount() == 0)
+ {
+ delete m_pElementTree;
+ m_pElementTree = nullptr;
+ }
+ else
+ {
+ SmMlElement* pTmpElememt = m_pElementTree->getSubElement(0);
+ delete m_pElementTree;
+ m_pElementTree = pTmpElememt;
+ }
+ pDocShell->SetMlElementTree(m_pElementTree);
+
+ m_bSuccess = true;
+ SvXMLImport::endDocument();
+}
+
+void SmMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps)
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing model");
+ return;
+ }
+
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+ if (!pModel)
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing sm model");
+ return;
+ }
+
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (!pDocShell)
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing sm doc shell");
+ return;
+ }
+
+ tools::Rectangle aRect(pDocShell->GetVisArea());
+
+ tools::Long nTmp = 0;
+
+ for (const PropertyValue& rValue : aViewProps)
+ {
+ if (rValue.Name == "ViewAreaTop")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosY(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaLeft")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosX(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaWidth")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setWidth(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ else if (rValue.Name == "ViewAreaHeight")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setHeight(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ }
+
+ pDocShell->SetVisArea(aRect);
+}
+
+void SmMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps)
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing model");
+ return;
+ }
+
+ uno::Reference<XPropertySet> xProps(xModel, UNO_QUERY);
+ if (!xProps.is())
+ {
+ SAL_WARN("starmath", "Failed to set view settings because missing model properties");
+ return;
+ }
+
+ Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
+ if (!xInfo.is())
+ {
+ SAL_WARN("starmath",
+ "Failed to set view settings because missing model properties information");
+ return;
+ }
+
+ static const OUStringLiteral sFormula(u"Formula");
+ static const OUStringLiteral sBasicLibraries(u"BasicLibraries");
+ static const OUStringLiteral sDialogLibraries(u"DialogLibraries");
+ for (const PropertyValue& rValue : aConfProps)
+ {
+ if (rValue.Name != sFormula && rValue.Name != sBasicLibraries
+ && rValue.Name != sDialogLibraries)
+ {
+ try
+ {
+ if (xInfo->hasPropertyByName(rValue.Name))
+ xProps->setPropertyValue(rValue.Name, rValue.Value);
+ }
+ catch (const beans::PropertyVetoException&)
+ {
+ // dealing with read-only properties here. Nothing to do...
+ }
+ catch (const Exception&)
+ {
+ SAL_WARN("starmath", "Unexpected issue while loading document properties");
+ }
+ }
+ }
+}
+
+SmMLImport::SmMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
+ OUString const& implementationName, SvXMLImportFlags nImportFlags)
+ : SvXMLImport(rContext, implementationName, nImportFlags)
+ , m_pElementTree(nullptr)
+ , m_bSuccess(false)
+ , m_nSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+{
+}
+
+/** Handles an error on the mathml structure
+ */
+void SmMLImport::declareMlError()
+{
+ m_bSuccess = false;
+ SAL_WARN("starmath", "MathML error");
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/iterator.cxx b/starmath/source/mathml/iterator.cxx
new file mode 100644
index 000000000..24de35c18
--- /dev/null
+++ b/starmath/source/mathml/iterator.cxx
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <mathml/iterator.hxx>
+
+/** The purpose of this iterator is to be able to iterate threw an infinite element tree
+ * infinite -> as much as your memory can hold
+ * No call-backs that will end up in out of stack
+ */
+
+namespace mathml
+{
+static inline void deleteElement(SmMlElement* aSmMlElement, void*) { delete aSmMlElement; }
+
+static inline void cloneElement(SmMlElement* aSmMlElement, void* aData)
+{
+ // Prepare data
+ SmMlElement* aNewSmMlElement = new SmMlElement(*aSmMlElement);
+ SmMlElement* aCopyTree = *static_cast<SmMlElement**>(aData);
+
+ // Append data
+ aCopyTree->setSubElement(aCopyTree->getSubElementsCount(), aNewSmMlElement);
+
+ // Prepare for following
+ // If it has sub elements, then it will be the next
+ if (aSmMlElement->getSubElementsCount() != 0)
+ aCopyTree = aNewSmMlElement;
+ else // Otherwise remounts up to where it should be
+ {
+ while (aSmMlElement->getParentElement() != nullptr)
+ {
+ // get parent
+ SmMlElement* pParent = aSmMlElement->getParentElement();
+ aCopyTree = aCopyTree->getParentElement();
+ // was this the last branch ?
+ if (aSmMlElement->getSubElementId() + 1 != pParent->getSubElementsCount())
+ break; // no -> stop going up
+ // Prepare for next round
+ aSmMlElement = pParent;
+ }
+ }
+
+ // Closing extras
+ *static_cast<SmMlElement**>(aData) = aCopyTree;
+}
+
+void SmMlIteratorFree(SmMlElement* pMlElementTree)
+{
+ if (pMlElementTree == nullptr)
+ return;
+ for (size_t i = 0; i < pMlElementTree->getSubElementsCount(); ++i)
+ {
+ SmMlIteratorFree(pMlElementTree->getSubElement(i));
+ }
+ deleteElement(pMlElementTree, nullptr);
+}
+
+SmMlElement* SmMlIteratorCopy(SmMlElement* pMlElementTree)
+{
+ if (pMlElementTree == nullptr)
+ return nullptr;
+ SmMlElement* aDummyElement = new SmMlElement();
+ SmMlIteratorTopToBottom(pMlElementTree, cloneElement, &aDummyElement);
+ SmMlElement* aResultElement = aDummyElement->getSubElement(0);
+ delete aDummyElement;
+ return aResultElement;
+}
+
+} // end namespace mathml
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/mathmlMo.cxx b/starmath/source/mathml/mathmlMo.cxx
new file mode 100644
index 000000000..fb76b070e
--- /dev/null
+++ b/starmath/source/mathml/mathmlMo.cxx
@@ -0,0 +1,1127 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <mathmlMo.hxx>
+
+static moOperatorData moOperatorDataDictionaryData[starmathdatabase::MATHML_MO_COUNT]
+ = { { u"\u2018", moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u2019", moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u201C", moOpDF::prefix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"\u201D", moOpDF::postfix, 10, 0, 0, moOpDP::fence | moOpDP::nonedp },
+ { u"(", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u")", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"[", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"]", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"{", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"||", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|||", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"}", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2016", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence },
+ { u"\u2308", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2309", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u230A", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u230B", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2329", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u232A", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2772", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2773", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E6", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E7", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E8", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27E9", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EA", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EB", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EC", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27ED", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EE", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u27EF", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2980", moOpDF::prepostfix, 20, 0, 0, moOpDP::stretchyfence },
+ { u"\u2983", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2984", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2985", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2986", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2987", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2988", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2989", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298A", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298B", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298C", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298D", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298E", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u298F", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2990", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2991", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2992", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2993", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2994", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2995", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2996", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2997", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2998", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u29FC", moOpDF::prefix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u29FD", moOpDF::postfix, 20, 0, 0, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u";", moOpDF::infix, 30, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u",", moOpDF::infix, 40, 0, 3, moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u"\u2063", moOpDF::infix, 40, 0, 0, moOpDP::separator | moOpDP::linebreakstyleAfter },
+ { u"\u2234", moOpDF::infix, 70, 5, 5, moOpDP::nonedp },
+ { u"\u2235", moOpDF::infix, 70, 5, 5, moOpDP::nonedp },
+ { u"->", moOpDF::infix, 90, 5, 5, moOpDP::nonedp },
+ { u"..", moOpDF::postfix, 100, 0, 0, moOpDP::nonedp },
+ { u"...", moOpDF::postfix, 100, 0, 0, moOpDP::nonedp },
+ { u":", moOpDF::infix, 100, 1, 2, moOpDP::nonedp },
+ { u"\u03F6", moOpDF::infix, 110, 5, 5, moOpDP::nonedp },
+ { u"\u2026", moOpDF::infix, 150, 0, 0, moOpDP::nonedp },
+ { u"\u22EE", moOpDF::infix, 150, 5, 5, moOpDP::nonedp },
+ { u"\u22EF", moOpDF::infix, 150, 0, 0, moOpDP::nonedp },
+ { u"\u22F1", moOpDF::infix, 150, 5, 5, moOpDP::nonedp },
+ { u"\u220B", moOpDF::infix, 160, 5, 5, moOpDP::nonedp },
+ { u"\u22A2", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A3", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A4", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A8", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22A9", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AC", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AD", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AE", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u22AF", moOpDF::infix, 170, 5, 5, moOpDP::nonedp },
+ { u"\u2228", moOpDF::infix, 190, 4, 4, moOpDP::nonedp },
+ { u"&&", moOpDF::infix, 200, 4, 4, moOpDP::nonedp },
+ { u"\u2227", moOpDF::infix, 200, 4, 4, moOpDP::nonedp },
+ { u"\u2200", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2203", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2204", moOpDF::prefix, 230, 2, 1, moOpDP::nonedp },
+ { u"\u2201", moOpDF::infix, 240, 1, 2, moOpDP::nonedp },
+ { u"\u2208", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2209", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u220C", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2282", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2282\u20D2", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2283", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2283\u20D2", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2284", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2285", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2286", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2287", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2288", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u2289", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u228A", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"\u228B", moOpDF::infix, 240, 5, 5, moOpDP::nonedp },
+ { u"<=", moOpDF::infix, 241, 5, 5, moOpDP::nonedp },
+ { u"\u2264", moOpDF::infix, 241, 5, 5, moOpDP::nonedp },
+ { u"\u2265", moOpDF::infix, 242, 5, 5, moOpDP::nonedp },
+ { u">", moOpDF::infix, 243, 5, 5, moOpDP::nonedp },
+ { u">=", moOpDF::infix, 243, 5, 5, moOpDP::nonedp },
+ { u"\u226F", moOpDF::infix, 244, 5, 5, moOpDP::nonedp },
+ { u"&lt", moOpDF::infix, 245, 5, 5, moOpDP::nonedp },
+ { u"\u226E", moOpDF::infix, 246, 5, 5, moOpDP::nonedp },
+ { u"\u2248", moOpDF::infix, 247, 5, 5, moOpDP::nonedp },
+ { u"\u223C", moOpDF::infix, 250, 5, 5, moOpDP::nonedp },
+ { u"\u2249", moOpDF::infix, 250, 5, 5, moOpDP::nonedp },
+ { u"\u2262", moOpDF::infix, 252, 5, 5, moOpDP::nonedp },
+ { u"\u2260", moOpDF::infix, 255, 5, 5, moOpDP::nonedp },
+ { u"!=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"*=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"+=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"-=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"/=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u":=", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"=", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"==", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u221D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2224", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2225", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2226", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2241", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2243", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2244", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2245", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2246", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2247", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u224D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2254", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2257", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2259", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225C", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u225F", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2261", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2268", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2269", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226A\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226B\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u226D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2270", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2271", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227A", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227B", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227C", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u227D", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2280", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2281", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22A5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22B4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22B5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22C9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CA", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CB", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22CC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u22D4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D6", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D7", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D8", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22D9", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EA", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EB", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22EC", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u22ED", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u25A0", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25A1", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AA", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AB", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AD", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AE", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25AF", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B0", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B1", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u25B3", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B4", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B5", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B8", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25B9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BD", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BE", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25BF", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C0", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C1", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C2", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C3", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C4", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C5", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C8", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25C9", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CC", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CD", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CE", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25CF", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25D6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25D7", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u25E6", moOpDF::infix, 260, 4, 4, moOpDP::nonedp },
+ { u"\u29C0", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29C1", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E3", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E4", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E5", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29E6", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u29F3", moOpDF::infix, 260, 3, 3, moOpDP::nonedp },
+ { u"\u2A87", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2A88", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AAF", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AAF\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AB0", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2AB0\u0338", moOpDF::infix, 260, 5, 5, moOpDP::nonedp },
+ { u"\u2044", moOpDF::infix, 265, 4, 4, moOpDP::stretchy },
+ { u"\u2206", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u220A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u220D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u220E", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2215", moOpDF::infix, 265, 4, 4, moOpDP::stretchy },
+ { u"\u2217", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2218", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2219", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u221F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2223", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2236", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2237", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2238", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2239", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u223B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223D\u0331", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u223E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u223F", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2242", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2242\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224E\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u224F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2250", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2251", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2252", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2253", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2255", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2256", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2258", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u225D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u225E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2263", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2266", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2266\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2267", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u226C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2272", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2273", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2274", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2275", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2276", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2277", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2278", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2279", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u227F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u228C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u228F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u228F\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2290", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2290\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2291", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2292", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2293", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2294", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u229D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22A6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22A7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22AA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22AB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22B9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22BA", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BC", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22BE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u22BF", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u22C4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22C8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22CD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22CE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22CF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22D1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22D2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D3", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u22D5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22DF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22E9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22F9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u22FF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u25B2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2758", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2981", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2982", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A4", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A5", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A6", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A7", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29A9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29AF", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B4", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B5", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29B6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B8", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29B9", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BA", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BC", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29BF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29C3", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29C4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C8", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29C9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29CE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29CF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29CF\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D0\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29D6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29D7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29D8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29D9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DD", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29DE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29E0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u29E2", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29E7", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29E9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EC", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29ED", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29EE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F0", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F1", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F2", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F7", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29F8", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29F9", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FA", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FB", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u29FE", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u29FF", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A1D", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A1E", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A1F", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A20", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A21", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"\u2A22", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A23", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A24", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A25", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A26", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A27", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A28", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A29", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A2E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A30", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A31", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A32", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A33", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A34", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A35", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A36", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A37", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A38", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A39", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A3E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A40", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A41", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A42", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A43", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A44", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A45", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A46", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A47", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A48", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A49", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A4F", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A50", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A51", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A52", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A53", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A54", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A55", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A56", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A57", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A58", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A59", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A5A", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5B", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5C", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5D", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5E", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A5F", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A60", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A61", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A62", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A63", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A64", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A65", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A66", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A67", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A68", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A69", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A6F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A70", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A71", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A72", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2A73", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A74", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A75", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A76", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A77", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A78", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A79", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7D\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7E\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A7F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A80", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A81", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A82", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A83", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A84", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A85", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A86", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A89", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A8F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A90", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A91", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A92", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A93", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A94", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A95", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A96", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A97", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A98", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A99", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9A", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9B", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9C", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9D", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9E", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2A9F", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA1\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA2\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AA9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AAE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AB9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ABF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AC9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ACF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AD9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADD", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADD\u0338", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2ADF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE4", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE5", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE6", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AE9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEB", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEC", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AED", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEE", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AEF", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF0", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF1", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF2", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF3", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF4", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF5", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF6", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AF7", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF8", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AF9", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AFA", moOpDF::infix, 265, 5, 5, moOpDP::nonedp },
+ { u"\u2AFB", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AFD", moOpDF::infix, 265, 4, 4, moOpDP::nonedp },
+ { u"\u2AFE", moOpDF::infix, 265, 3, 3, moOpDP::nonedp },
+ { u"|", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"||", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"|||", moOpDF::infix, 270, 2, 2, moOpDP::stretchyfence | moOpDP::symmetric },
+ { u"\u2190", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2191", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2192", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2193", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2194", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2195", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2196", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2197", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2198", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2199", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u219A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u219B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u219C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u219F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21A7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21A9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21AE", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21AF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21B6", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21B7", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21B8", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21B9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BA", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21BB", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21BC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21BE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21BF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21C8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21C9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21CB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21CD", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21CE", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21CF", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21D0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21D5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21D9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21DA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21DE", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21DF", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21E0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E2", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E4", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21E8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21E9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21ED", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21EF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21F1", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21F2", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u21F3", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F4", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u21F6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21F7", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F8", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21F9", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FA", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FB", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FC", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u21FD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21FE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u21FF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u22B8", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u27F0", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u27F1", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u27F5", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F6", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F7", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F8", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27F9", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FA", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FB", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FC", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FD", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FE", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u27FF", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2900", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2901", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2902", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2903", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2904", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2905", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2906", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2907", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2908", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2909", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u290A", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u290B", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u290C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u290F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2910", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2911", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2912", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2913", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2914", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2915", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2916", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2917", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2918", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2919", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291C", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291D", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291E", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u291F", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2920", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2921", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2922", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2923", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2924", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2925", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2926", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2927", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2928", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2929", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292A", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292B", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292C", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292D", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u292F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2930", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2931", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2932", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2933", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2934", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2935", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2936", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2937", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2938", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2939", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u293A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293C", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293D", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u293E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u293F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2940", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2941", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2942", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2943", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2944", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2945", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2946", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2947", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2948", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2949", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u294B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u294C", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294D", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u294E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u294F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2950", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2951", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2952", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2953", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2954", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2955", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2956", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2957", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2958", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2959", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295A", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295B", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295C", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295D", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u295E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u295F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2960", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2961", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2962", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2963", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2964", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2965", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2966", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2967", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2968", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2969", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296C", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296D", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u296E", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u296F", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2970", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2971", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2972", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2973", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2974", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2975", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2976", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2977", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2978", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u2979", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297A", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297B", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297C", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297D", moOpDF::infix, 270, 5, 5, moOpDP::accent },
+ { u"\u297E", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u297F", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2999", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299A", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299B", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299C", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299D", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299E", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u299F", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29DF", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29EF", moOpDF::infix, 270, 3, 3, moOpDP::nonedp },
+ { u"\u29F4", moOpDF::infix, 270, 5, 5, moOpDP::nonedp },
+ { u"\u2B45", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"\u2B46", moOpDF::infix, 270, 5, 5, moOpDP::stretchy },
+ { u"+", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"+", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"-", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"-", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u00B1", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u00B1", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2212", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2212", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2213", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2213", moOpDF::prefix, 275, 0, 1, moOpDP::nonedp },
+ { u"\u2214", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u229E", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u229F", moOpDF::infix, 275, 4, 4, moOpDP::nonedp },
+ { u"\u2211", moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A0A", moOpDF::prefix, 290, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A0B", moOpDF::prefix, 290, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222C", moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222D", moOpDF::prefix, 300, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2295", moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2296", moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2298", moOpDF::infix, 300, 4, 4, moOpDP::nonedp },
+ { u"\u2A01", moOpDF::prefix, 300, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u222B", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222E", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u222F", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2230", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2231", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2232", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2233", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0C", moOpDF::prefix, 310, 0, 1, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0D", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0E", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A0F", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A10", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A11", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A12", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A13", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A14", moOpDF::prefix, 310, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A15", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A16", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A17", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A18", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A19", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1A", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1B", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u2A1C", moOpDF::prefix, 310, 1, 2, moOpDP::largeop | moOpDP::symmetric },
+ { u"\u22C3", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A03", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A04", moOpDF::prefix, 320, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C0", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C1", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u22C2", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A00", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A02", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A05", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A06", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A07", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A08", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2A09", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2AFC", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2AFF", moOpDF::prefix, 330, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2240", moOpDF::infix, 340, 4, 4, moOpDP::nonedp },
+ { u"\u220F", moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2210", moOpDF::prefix, 350, 1, 2, moOpDP::movablelargeop | moOpDP::symmetric },
+ { u"\u2229", moOpDF::infix, 350, 4, 4, moOpDP::nonedp },
+ { u"\u222A", moOpDF::infix, 350, 4, 4, moOpDP::nonedp },
+ { u"*", moOpDF::infix, 390, 3, 3, moOpDP::nonedp },
+ { u".", moOpDF::infix, 390, 3, 3, moOpDP::nonedp },
+ { u"\u00D7", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2022", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2043", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2062", moOpDF::infix, 390, 0, 0, moOpDP::nonedp },
+ { u"\u22A0", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u22A1", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u22C5", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2A2F", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u2A3F", moOpDF::infix, 390, 4, 4, moOpDP::nonedp },
+ { u"\u00B7", moOpDF::infix, 400, 4, 4, moOpDP::nonedp },
+ { u"\u2297", moOpDF::infix, 410, 4, 4, moOpDP::nonedp },
+ { u"%", moOpDF::infix, 640, 3, 3, moOpDP::nonedp },
+ { u"\\", moOpDF::infix, 650, 0, 0, moOpDP::nonedp },
+ { u"\u2216", moOpDF::infix, 650, 4, 4, moOpDP::nonedp },
+ { u"/", moOpDF::infix, 660, 1, 1, moOpDP::nonedp },
+ { u"\u00F7", moOpDF::infix, 660, 4, 4, moOpDP::nonedp },
+ { u"\u2220", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u2221", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u2222", moOpDF::prefix, 670, 0, 0, moOpDP::nonedp },
+ { u"\u00AC", moOpDF::prefix, 680, 2, 1, moOpDP::nonedp },
+ { u"\u2299", moOpDF::infix, 710, 4, 4, moOpDP::nonedp },
+ { u"\u2202", moOpDF::prefix, 740, 2, 1, moOpDP::nonedp },
+ { u"\u2207", moOpDF::prefix, 740, 2, 1, moOpDP::nonedp },
+ { u"**", moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"<>", moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"^", moOpDF::infix, 780, 1, 1, moOpDP::nonedp },
+ { u"\u2032", moOpDF::postfix, 800, 0, 0, moOpDP::nonedp },
+ { u"\u266D", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"\u266E", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"\u266F", moOpDF::postfix, 800, 0, 2, moOpDP::nonedp },
+ { u"!", moOpDF::postfix, 810, 1, 0, moOpDP::nonedp },
+ { u"!!", moOpDF::postfix, 810, 1, 0, moOpDP::nonedp },
+ { u"//", moOpDF::infix, 820, 1, 1, moOpDP::nonedp },
+ { u"@", moOpDF::infix, 825, 1, 1, moOpDP::nonedp },
+ { u"?", moOpDF::infix, 835, 1, 1, moOpDP::nonedp },
+ { u"\u2145", moOpDF::prefix, 845, 2, 1, moOpDP::nonedp },
+ { u"\u2146", moOpDF::prefix, 845, 2, 0, moOpDP::nonedp },
+ { u"\u221A", moOpDF::prefix, 845, 1, 1, moOpDP::stretchy },
+ { u"\u221B", moOpDF::prefix, 845, 1, 1, moOpDP::nonedp },
+ { u"\u221C", moOpDF::prefix, 845, 1, 1, moOpDP::nonedp },
+ { u"\u2061", moOpDF::infix, 850, 0, 0, moOpDP::nonedp },
+ { u"\"", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"&", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"\'", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"++", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"--", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"^", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"_", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"`", moOpDF::postfix, 880, 0, 00, moOpDP::accent },
+ { u"~", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u00A8", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00AA", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00AF", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u00B0", moOpDF::postfix, 880, 0, 0, moOpDP::nonedp },
+ { u"\u00B2", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B3", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B4", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B8", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00B9", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u00BA", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02C6", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02C7", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02C9", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02CA", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02CB", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02CD", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02D8", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02D9", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02DA", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02DC", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u02DD", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u02F7", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u0302", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u0311", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201A", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201B", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201E", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u201F", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2033", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2034", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2035", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2036", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2037", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u203E", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u2057", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u2064", moOpDF::infix, 880, 0, 0, moOpDP::nonedp },
+ { u"\u20DB", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u20DC", moOpDF::postfix, 880, 0, 0, moOpDP::accent },
+ { u"\u23B4", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23B5", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DC", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DD", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DE", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23DF", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23E0", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"\u23E1", moOpDF::postfix, 880, 0, 0, moOpDP::stretchy | moOpDP::accent },
+ { u"_", moOpDF::infix, 900, 1, 1, moOpDP::nonedp } };
+
+std::vector<moOperatorData> starmathdatabase::moOperatorDataDictionary(
+ moOperatorDataDictionaryData, moOperatorDataDictionaryData + starmathdatabase::MATHML_MO_COUNT);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathml/mathmlattr.cxx b/starmath/source/mathml/mathmlattr.cxx
new file mode 100644
index 000000000..5e54f0d92
--- /dev/null
+++ b/starmath/source/mathml/mathmlattr.cxx
@@ -0,0 +1,165 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <mathmlattr.hxx>
+
+#include <o3tl/safeint.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/math.h>
+
+#include <cstddef>
+#include <string_view>
+#include <unordered_map>
+
+static std::size_t ParseMathMLUnsignedNumber(std::u16string_view rStr, Fraction& rUN)
+{
+ auto nLen = rStr.length();
+ std::size_t nDecimalPoint = std::u16string_view::npos;
+ std::size_t nIdx;
+ sal_Int64 nom = 0;
+ sal_Int64 den = 1;
+ bool validNomDen = true;
+ for (nIdx = 0; nIdx < nLen; nIdx++)
+ {
+ auto cD = rStr[nIdx];
+ if (cD == u'.')
+ {
+ if (nDecimalPoint != std::u16string_view::npos)
+ return std::u16string_view::npos;
+ nDecimalPoint = nIdx;
+ continue;
+ }
+ if (cD < u'0' || u'9' < cD)
+ break;
+ if (validNomDen
+ && (o3tl::checked_multiply(nom, sal_Int64(10), nom)
+ || o3tl::checked_add(nom, sal_Int64(cD - u'0'), nom)
+ || nom >= std::numeric_limits<sal_Int32>::max()
+ || (nDecimalPoint != std::u16string_view::npos
+ && o3tl::checked_multiply(den, sal_Int64(10), den))))
+ {
+ validNomDen = false;
+ }
+ }
+ if (nIdx == 0 || (nIdx == 1 && nDecimalPoint == 0))
+ return std::u16string_view::npos;
+
+ // If the input "xx.yyy" can be represented with nom = xx*10^n + yyy and den = 10^n in sal_Int64
+ // (where n is the length of "yyy"), then use that to create an accurate Fraction (and TODO: we
+ // could even ignore trailing "0" characters in "yyy", for a smaller n and thus a greater chance
+ // of validNomDen); if not, use the less accurate approach of creating a Fraction from double:
+ if (validNomDen)
+ {
+ rUN = Fraction(nom, den);
+ }
+ else
+ {
+ rUN = Fraction(
+ rtl_math_uStringToDouble(rStr.data(), rStr.data() + nIdx, '.', 0, nullptr, nullptr));
+ }
+
+ return nIdx;
+}
+
+static std::size_t ParseMathMLNumber(std::u16string_view rStr, Fraction& rN)
+{
+ if (rStr.empty())
+ return std::u16string_view::npos;
+ bool bNegative = (rStr[0] == '-');
+ std::size_t nOffset = bNegative ? 1 : 0;
+ auto nIdx = ParseMathMLUnsignedNumber(rStr.substr(nOffset), rN);
+ if (nIdx == std::u16string_view::npos || !rN.IsValid())
+ return std::u16string_view::npos;
+ if (bNegative)
+ rN *= -1;
+ return nOffset + nIdx;
+}
+
+bool ParseMathMLAttributeLengthValue(std::u16string_view rStr, MathMLAttributeLengthValue& rV)
+{
+ auto nIdx = ParseMathMLNumber(rStr, rV.aNumber);
+ if (nIdx == std::u16string_view::npos)
+ return false;
+ std::u16string_view sRest = rStr.substr(nIdx);
+ if (sRest.empty())
+ {
+ rV.eUnit = MathMLLengthUnit::None;
+ }
+ if (o3tl::starts_with(sRest, u"em"))
+ {
+ rV.eUnit = MathMLLengthUnit::Em;
+ }
+ if (o3tl::starts_with(sRest, u"ex"))
+ {
+ rV.eUnit = MathMLLengthUnit::Ex;
+ }
+ if (o3tl::starts_with(sRest, u"px"))
+ {
+ rV.eUnit = MathMLLengthUnit::Px;
+ }
+ if (o3tl::starts_with(sRest, u"in"))
+ {
+ rV.eUnit = MathMLLengthUnit::In;
+ }
+ if (o3tl::starts_with(sRest, u"cm"))
+ {
+ rV.eUnit = MathMLLengthUnit::Cm;
+ }
+ if (o3tl::starts_with(sRest, u"mm"))
+ {
+ rV.eUnit = MathMLLengthUnit::Mm;
+ }
+ if (o3tl::starts_with(sRest, u"pt"))
+ {
+ rV.eUnit = MathMLLengthUnit::Pt;
+ }
+ if (o3tl::starts_with(sRest, u"pc"))
+ {
+ rV.eUnit = MathMLLengthUnit::Pc;
+ }
+ if (sRest[0] == u'%')
+ {
+ rV.eUnit = MathMLLengthUnit::Percent;
+ }
+ return true;
+}
+
+bool GetMathMLMathvariantValue(const OUString& rStr, MathMLMathvariantValue& rV)
+{
+ static const std::unordered_map<OUString, MathMLMathvariantValue> aMap{
+ { "normal", MathMLMathvariantValue::Normal },
+ { "bold", MathMLMathvariantValue::Bold },
+ { "italic", MathMLMathvariantValue::Italic },
+ { "bold-italic", MathMLMathvariantValue::BoldItalic },
+ { "double-struck", MathMLMathvariantValue::DoubleStruck },
+ { "bold-fraktur", MathMLMathvariantValue::BoldFraktur },
+ { "script", MathMLMathvariantValue::Script },
+ { "bold-script", MathMLMathvariantValue::BoldScript },
+ { "fraktur", MathMLMathvariantValue::Fraktur },
+ { "sans-serif", MathMLMathvariantValue::SansSerif },
+ { "bold-sans-serif", MathMLMathvariantValue::BoldSansSerif },
+ { "sans-serif-italic", MathMLMathvariantValue::SansSerifItalic },
+ { "sans-serif-bold-italic", MathMLMathvariantValue::SansSerifBoldItalic },
+ { "monospace", MathMLMathvariantValue::Monospace },
+ { "initial", MathMLMathvariantValue::Initial },
+ { "tailed", MathMLMathvariantValue::Tailed },
+ { "looped", MathMLMathvariantValue::Looped },
+ { "stretched", MathMLMathvariantValue::Stretched }
+ };
+
+ auto it = aMap.find(rStr);
+ if (it != aMap.end())
+ {
+ rV = it->second;
+ return true;
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/mathml/mathmlexport.cxx b/starmath/source/mathml/mathmlexport.cxx
new file mode 100644
index 000000000..187dd1953
--- /dev/null
+++ b/starmath/source/mathml/mathmlexport.cxx
@@ -0,0 +1,1457 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*
+ Warning: The SvXMLElementExport helper class creates the beginning and
+ closing tags of xml elements in its constructor and destructor, so there's
+ hidden stuff going on, on occasion the ordering of these classes declarations
+ may be significant
+*/
+
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/util/MeasureUnit.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/uno/Any.h>
+
+#include <officecfg/Office/Common.hxx>
+#include <rtl/math.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <comphelper/fileformat.h>
+#include <comphelper/processfactory.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/namespacemap.hxx>
+#include <xmloff/attrlist.hxx>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <tools/diagnose_ex.h>
+#include <sal/log.hxx>
+
+#include <stack>
+
+#include <mathmlexport.hxx>
+#include <xparsmlbase.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <unomodel.hxx>
+#include <document.hxx>
+#include <utility.hxx>
+#include <cfgitem.hxx>
+#include <starmathdatabase.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace
+{
+bool IsInPrivateUseArea(sal_Unicode cChar) { return 0xE000 <= cChar && cChar <= 0xF8FF; }
+
+sal_Unicode ConvertMathToMathML(sal_Unicode cChar)
+{
+ sal_Unicode cRes = cChar;
+ if (IsInPrivateUseArea(cChar))
+ {
+ SAL_WARN("starmath", "Error: private use area characters should no longer be in use!");
+ cRes = u'@'; // just some character that should easily be notice as odd in the context
+ }
+ return cRes;
+}
+}
+
+bool SmXMLExportWrapper::Export(SfxMedium& rMedium)
+{
+ bool bRet = true;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ //Get model
+ uno::Reference<lang::XComponent> xModelComp = xModel;
+
+ bool bEmbedded = false;
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if (pDocShell && SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
+ bEmbedded = true;
+
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+ if (!bEmbedded)
+ {
+ if (pDocShell /*&& pDocShell->GetMedium()*/)
+ {
+ OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+ }
+
+ // set progress range and start status indicator
+ if (xStatusIndicator.is())
+ {
+ sal_Int32 nProgressRange = bFlat ? 1 : 3;
+ xStatusIndicator->start(SmResId(STR_STATSTR_WRITING), nProgressRange);
+ }
+ }
+
+ static constexpr OUStringLiteral sUsePrettyPrinting(u"UsePrettyPrinting");
+ static constexpr OUStringLiteral sBaseURI(u"BaseURI");
+ static constexpr OUStringLiteral sStreamRelPath(u"StreamRelPath");
+ static constexpr OUStringLiteral sStreamName(u"StreamName");
+
+ // create XPropertySet with three properties for status indicator
+ static const comphelper::PropertyMapEntry aInfoMap[] = {
+ { sUsePrettyPrinting, 0, cppu::UnoType<bool>::get(), beans::PropertyAttribute::MAYBEVOID,
+ 0 },
+ { sBaseURI, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 },
+ { sStreamRelPath, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID,
+ 0 },
+ { sStreamName, 0, ::cppu::UnoType<OUString>::get(), beans::PropertyAttribute::MAYBEVOID, 0 }
+ };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ bool bUsePrettyPrinting
+ = bFlat || officecfg::Office::Common::Save::Document::PrettyPrinting::get();
+ xInfoSet->setPropertyValue(sUsePrettyPrinting, Any(bUsePrettyPrinting));
+
+ // Set base URI
+ xInfoSet->setPropertyValue(sBaseURI, Any(rMedium.GetBaseURL(true)));
+
+ sal_Int32 nSteps = 0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+ if (!bFlat) //Storage (Package) of Stream
+ {
+ uno::Reference<embed::XStorage> xStg = rMedium.GetOutputStorage();
+ bool bOASIS = (SotStorage::GetVersion(xStg) > SOFFICE_FILEFORMAT_60);
+
+ // TODO/LATER: handle the case of embedded links gracefully
+ if (bEmbedded) //&& !pStg->IsRoot() )
+ {
+ OUString aName;
+ if (rMedium.GetItemSet())
+ {
+ const SfxStringItem* pDocHierarchItem
+ = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem)
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if (!aName.isEmpty())
+ {
+ xInfoSet->setPropertyValue(sStreamRelPath, Any(aName));
+ }
+ }
+
+ if (!bEmbedded)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(xStg, xModelComp, "meta.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaExporter"
+ : "com.sun.star.comp.Math.XMLMetaExporter"));
+ }
+ if (bRet)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(xStg, xModelComp, "content.xml", xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if (bRet)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(xStg, xModelComp, "settings.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsExporter"
+ : "com.sun.star.comp.Math.XMLSettingsExporter"));
+ }
+ }
+ else
+ {
+ SvStream* pStream = rMedium.GetOutStream();
+ uno::Reference<io::XOutputStream> xOut(new utl::OOutputStreamWrapper(*pStream));
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ bRet = WriteThroughComponent(xOut, xModelComp, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLContentExporter");
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+
+ return bRet;
+}
+
+/// export through an XML exporter component (output stream version)
+bool SmXMLExportWrapper::WriteThroughComponent(const Reference<io::XOutputStream>& xOutputStream,
+ const Reference<XComponent>& xComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pComponentName)
+{
+ OSL_ENSURE(xOutputStream.is(), "I really need an output stream!");
+ OSL_ENSURE(xComponent.is(), "Need component!");
+ OSL_ENSURE(nullptr != pComponentName, "Need component name!");
+
+ // get component
+ Reference<xml::sax::XWriter> xSaxWriter = xml::sax::Writer::create(rxContext);
+
+ // connect XML writer to output stream
+ xSaxWriter->setOutputStream(xOutputStream);
+ if (m_bUseHTMLMLEntities)
+ xSaxWriter->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntitiesExport);
+
+ // prepare arguments (prepend doc handler to given arguments)
+ Sequence<Any> aArgs{ Any(xSaxWriter), Any(rPropSet) };
+
+ // get filter component
+ Reference<document::XExporter> xExporter(
+ rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pComponentName), aArgs, rxContext),
+ UNO_QUERY);
+ OSL_ENSURE(xExporter.is(), "can't instantiate export filter component");
+ if (!xExporter.is())
+ return false;
+
+ // connect model and filter
+ xExporter->setSourceDocument(xComponent);
+
+ // filter!
+ Reference<XFilter> xFilter(xExporter, UNO_QUERY);
+ uno::Sequence<PropertyValue> aProps(0);
+ xFilter->filter(aProps);
+
+ auto pFilter = comphelper::getFromUnoTunnel<SmXMLExport>(xFilter);
+ return pFilter == nullptr || pFilter->GetSuccess();
+}
+
+/// export through an XML exporter component (storage version)
+bool SmXMLExportWrapper::WriteThroughComponent(const Reference<embed::XStorage>& xStorage,
+ const Reference<XComponent>& xComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pComponentName)
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Need stream name!");
+
+ // open stream
+ Reference<io::XStream> xStream;
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+ try
+ {
+ xStream = xStorage->openStreamElement(sStreamName, embed::ElementModes::READWRITE
+ | embed::ElementModes::TRUNCATE);
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath", "Can't create output stream in package");
+ return false;
+ }
+
+ uno::Reference<beans::XPropertySet> xSet(xStream, uno::UNO_QUERY);
+ static const OUStringLiteral sMediaType = u"MediaType";
+ static const OUStringLiteral sTextXml = u"text/xml";
+ xSet->setPropertyValue(sMediaType, Any(OUString(sTextXml)));
+
+ // all streams must be encrypted in encrypted document
+ static const OUStringLiteral sUseCommonStoragePasswordEncryption
+ = u"UseCommonStoragePasswordEncryption";
+ xSet->setPropertyValue(sUseCommonStoragePasswordEncryption, Any(true));
+
+ // set Base URL
+ if (rPropSet.is())
+ {
+ rPropSet->setPropertyValue("StreamName", Any(sStreamName));
+ }
+
+ // write the stuff
+ bool bRet = WriteThroughComponent(xStream->getOutputStream(), xComponent, rxContext, rPropSet,
+ pComponentName);
+
+ return bRet;
+}
+
+SmXMLExport::SmXMLExport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
+ OUString const& implementationName, SvXMLExportFlags nExportFlags)
+ : SvXMLExport(rContext, implementationName, util::MeasureUnit::INCH, XML_MATH, nExportFlags)
+ , pTree(nullptr)
+ , bSuccess(false)
+{
+}
+
+sal_Int64 SAL_CALL SmXMLExport::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvXMLExport>{});
+}
+
+const uno::Sequence<sal_Int8>& SmXMLExport::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSmXMLExportUnoTunnelId;
+ return theSmXMLExportUnoTunnelId.getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLMetaExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(
+ new SmXMLExport(context, "com.sun.star.comp.Math.XMLMetaExporter", SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisMetaExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisMetaExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLSettingsExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLSettingsExporter",
+ SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLOasisSettingsExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLOasisSettingsExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::SETTINGS));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_XMLContentExporter_get_implementation(css::uno::XComponentContext* context,
+ css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new SmXMLExport(context, "com.sun.star.comp.Math.XMLContentExporter",
+ SvXMLExportFlags::OASIS | SvXMLExportFlags::CONTENT));
+}
+
+ErrCode SmXMLExport::exportDoc(enum XMLTokenEnum eClass)
+{
+ if (!(getExportFlags() & SvXMLExportFlags::CONTENT))
+ {
+ SvXMLExport::exportDoc(eClass);
+ }
+ else
+ {
+ uno::Reference<frame::XModel> xModel = GetModel();
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ pTree = pDocShell->GetFormulaTree();
+ aText = pDocShell->GetText();
+ }
+
+ GetDocHandler()->startDocument();
+
+ addChaffWhenEncryptedStorage();
+
+ /*Add xmlns line*/
+ SvXMLAttributeList& rList = GetAttrList();
+
+ // make use of a default namespace
+ ResetNamespaceMap(); // Math doesn't need namespaces from xmloff, since it now uses default namespaces (because that is common with current MathML usage in the web)
+ GetNamespaceMap_().Add(OUString(), GetXMLToken(XML_N_MATH), XML_NAMESPACE_MATH);
+
+ rList.AddAttribute(GetNamespaceMap().GetAttrNameByKey(XML_NAMESPACE_MATH),
+ GetNamespaceMap().GetNameByKey(XML_NAMESPACE_MATH));
+
+ //I think we need something like ImplExportEntities();
+ ExportContent_();
+ GetDocHandler()->endDocument();
+ }
+
+ bSuccess = true;
+ return ERRCODE_NONE;
+}
+
+void SmXMLExport::ExportContent_()
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+ SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ OSL_ENSURE(pDocShell, "doc shell missing");
+
+ if (pDocShell && !pDocShell->GetFormat().IsTextmode())
+ {
+ // If the Math equation is not in text mode, we attach a display="block"
+ // attribute on the <math> root. We don't do anything if it is in
+ // text mode, the default display="inline" value will be used.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DISPLAY, XML_BLOCK);
+ }
+ SvXMLElementExport aEquation(*this, XML_NAMESPACE_MATH, XML_MATH, true, true);
+ std::unique_ptr<SvXMLElementExport> pSemantics;
+
+ if (!aText.isEmpty())
+ {
+ pSemantics.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_SEMANTICS, true, true));
+ }
+
+ ExportNodes(pTree, 0);
+
+ if (aText.isEmpty())
+ return;
+
+ SmModule* pMod = SM_MOD();
+ sal_uInt16 nSmSyntaxVersion = pMod->GetConfig()->GetDefaultSmSyntaxVersion();
+
+ // Convert symbol names
+ if (pDocShell)
+ {
+ nSmSyntaxVersion = pDocShell->GetSmSyntaxVersion();
+ AbstractSmParser* rParser = pDocShell->GetParser();
+ bool bVal = rParser->IsExportSymbolNames();
+ rParser->SetExportSymbolNames(true);
+ auto pTmpTree = rParser->Parse(aText);
+ aText = rParser->GetText();
+ pTmpTree.reset();
+ rParser->SetExportSymbolNames(bVal);
+ }
+
+ OUStringBuffer sStrBuf(12);
+ sStrBuf.append(u"StarMath ");
+ if (nSmSyntaxVersion == 5)
+ sStrBuf.append(u"5.0");
+ else
+ sStrBuf.append(static_cast<sal_Int32>(nSmSyntaxVersion));
+
+ AddAttribute(XML_NAMESPACE_MATH, XML_ENCODING, sStrBuf.makeStringAndClear());
+ SvXMLElementExport aAnnotation(*this, XML_NAMESPACE_MATH, XML_ANNOTATION, true, false);
+ GetDocHandler()->characters(aText);
+}
+
+void SmXMLExport::GetViewSettings(Sequence<PropertyValue>& aProps)
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ return;
+
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (!pModel)
+ return;
+
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (!pDocShell)
+ return;
+
+ aProps.realloc(4);
+ PropertyValue* pValue = aProps.getArray();
+ sal_Int32 nIndex = 0;
+
+ tools::Rectangle aRect(pDocShell->GetVisArea());
+
+ pValue[nIndex].Name = "ViewAreaTop";
+ pValue[nIndex++].Value <<= aRect.Top();
+
+ pValue[nIndex].Name = "ViewAreaLeft";
+ pValue[nIndex++].Value <<= aRect.Left();
+
+ pValue[nIndex].Name = "ViewAreaWidth";
+ pValue[nIndex++].Value <<= aRect.GetWidth();
+
+ pValue[nIndex].Name = "ViewAreaHeight";
+ pValue[nIndex++].Value <<= aRect.GetHeight();
+}
+
+void SmXMLExport::GetConfigurationSettings(Sequence<PropertyValue>& rProps)
+{
+ Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
+ if (!xProps.is())
+ return;
+
+ Reference<XPropertySetInfo> xPropertySetInfo = xProps->getPropertySetInfo();
+ if (!xPropertySetInfo.is())
+ return;
+
+ const Sequence<Property> aProps = xPropertySetInfo->getProperties();
+ const sal_Int32 nCount = aProps.getLength();
+ if (!nCount)
+ return;
+
+ rProps.realloc(nCount);
+ SmMathConfig* pConfig = SM_MOD()->GetConfig();
+ const bool bUsedSymbolsOnly = pConfig && pConfig->IsSaveOnlyUsedSymbols();
+
+ std::transform(aProps.begin(), aProps.end(), rProps.getArray(),
+ [bUsedSymbolsOnly, &xProps](const Property& prop) {
+ PropertyValue aRet;
+ if (prop.Name != "Formula" && prop.Name != "BasicLibraries"
+ && prop.Name != "DialogLibraries" && prop.Name != "RuntimeUID")
+ {
+ aRet.Name = prop.Name;
+ OUString aActualName(prop.Name);
+ // handle 'save used symbols only'
+ static constexpr OUStringLiteral sUserDefinedSymbolsInUse
+ = u"UserDefinedSymbolsInUse";
+ if (bUsedSymbolsOnly && prop.Name == "Symbols")
+ aActualName = sUserDefinedSymbolsInUse;
+ aRet.Value = xProps->getPropertyValue(aActualName);
+ }
+ return aRet;
+ });
+}
+
+void SmXMLExport::ExportLine(const SmNode* pNode, int nLevel) { ExportExpression(pNode, nLevel); }
+
+void SmXMLExport::ExportBinaryHorizontal(const SmNode* pNode, int nLevel)
+{
+ TG nGroup = pNode->GetToken().nGroup;
+
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ // Unfold the binary tree structure as long as the nodes are SmBinHorNode
+ // with the same nGroup. This will reduce the number of nested <mrow>
+ // elements e.g. we only need three <mrow> levels to export
+
+ // "a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l =
+ // a*b*c*d+e*f*g*h+i*j*k*l = a*b*c*d+e*f*g*h+i*j*k*l"
+
+ // See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=66081
+ ::std::stack<const SmNode*> s;
+ s.push(pNode);
+ while (!s.empty())
+ {
+ const SmNode* node = s.top();
+ s.pop();
+ if (node->GetType() != SmNodeType::BinHor || node->GetToken().nGroup != nGroup)
+ {
+ ExportNodes(node, nLevel + 1);
+ continue;
+ }
+ const SmBinHorNode* binNode = static_cast<const SmBinHorNode*>(node);
+ s.push(binNode->RightOperand());
+ s.push(binNode->Symbol());
+ s.push(binNode->LeftOperand());
+ }
+}
+
+void SmXMLExport::ExportUnaryHorizontal(const SmNode* pNode, int nLevel)
+{
+ ExportExpression(pNode, nLevel);
+}
+
+void SmXMLExport::ExportExpression(const SmNode* pNode, int nLevel,
+ bool bNoMrowContainer /*=false*/)
+{
+ std::unique_ptr<SvXMLElementExport> pRow;
+ size_t nSize = pNode->GetNumSubNodes();
+
+ // #i115443: nodes of type expression always need to be grouped with mrow statement
+ if (!bNoMrowContainer && (nSize > 1 || pNode->GetType() == SmNodeType::Expression))
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode* pTemp = pNode->GetSubNode(i))
+ ExportNodes(pTemp, nLevel + 1);
+ }
+}
+
+void SmXMLExport::ExportBinaryVertical(const SmNode* pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+ const SmNode* pNum = pNode->GetSubNode(0);
+ const SmNode* pDenom = pNode->GetSubNode(2);
+ if (pNum->GetType() == SmNodeType::Align && pNum->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the numerator:
+ // attach the corresponding numalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_NUMALIGN,
+ pNum->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ if (pDenom->GetType() == SmNodeType::Align && pDenom->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on the denominator:
+ // attach the corresponding denomalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_DENOMALIGN,
+ pDenom->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
+ ExportNodes(pNum, nLevel);
+ ExportNodes(pDenom, nLevel);
+}
+
+void SmXMLExport::ExportBinaryDiagonal(const SmNode* pNode, int nLevel)
+{
+ assert(pNode->GetNumSubNodes() == 3);
+
+ if (pNode->GetToken().eType == TWIDESLASH)
+ {
+ // wideslash
+ // export the node as <mfrac bevelled="true">
+ AddAttribute(XML_NAMESPACE_MATH, XML_BEVELLED, XML_TRUE);
+ SvXMLElementExport aFraction(*this, XML_NAMESPACE_MATH, XML_MFRAC, true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+ else
+ {
+ // widebslash
+ // We can not use <mfrac> to a backslash, so just use <mo>\</mo>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ ExportNodes(pNode->GetSubNode(0), nLevel);
+
+ { // Scoping for <mo> creation
+ SvXMLElementExport aMo(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
+ GetDocHandler()->characters(OUStringChar(MS_BACKSLASH));
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel);
+ }
+}
+
+void SmXMLExport::ExportTable(const SmNode* pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pTable;
+
+ size_t nSize = pNode->GetNumSubNodes();
+
+ //If the list ends in newline then the last entry has
+ //no subnodes, the newline is superfluous so we just drop
+ //the last node, inclusion would create a bad MathML
+ //table
+ if (nSize >= 1)
+ {
+ const SmNode* pLine = pNode->GetSubNode(nSize - 1);
+ if (pLine->GetType() == SmNodeType::Line && pLine->GetNumSubNodes() == 1
+ && pLine->GetSubNode(0) != nullptr
+ && pLine->GetSubNode(0)->GetToken().eType == TNEWLINE)
+ --nSize;
+ }
+
+ // try to avoid creating a mtable element when the formula consists only
+ // of a single output line
+ if (nLevel || (nSize > 1))
+ pTable.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true));
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (const SmNode* pTemp = pNode->GetSubNode(i))
+ {
+ std::unique_ptr<SvXMLElementExport> pRow;
+ std::unique_ptr<SvXMLElementExport> pCell;
+ if (pTable)
+ {
+ pRow.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTR, true, true));
+ SmTokenType eAlign = TALIGNC;
+ if (pTemp->GetType() == SmNodeType::Align)
+ {
+ // For Binom() and Stack() constructions, the SmNodeType::Align nodes
+ // are direct children.
+ // binom{alignl ...}{alignr ...} and
+ // stack{alignl ... ## alignr ... ## ...}
+ eAlign = pTemp->GetToken().eType;
+ }
+ else if (pTemp->GetType() == SmNodeType::Line && pTemp->GetNumSubNodes() == 1
+ && pTemp->GetSubNode(0)
+ && pTemp->GetSubNode(0)->GetType() == SmNodeType::Align)
+ {
+ // For the Table() construction, the SmNodeType::Align node is a child
+ // of an SmNodeType::Line node.
+ // alignl ... newline alignr ... newline ...
+ eAlign = pTemp->GetSubNode(0)->GetToken().eType;
+ }
+ if (eAlign != TALIGNC)
+ {
+ // If a left or right alignment is specified on this line,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ eAlign == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ pCell.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTD, true, true));
+ }
+ ExportNodes(pTemp, nLevel + 1);
+ }
+ }
+}
+
+void SmXMLExport::ExportMath(const SmNode* pNode)
+{
+ const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
+ std::unique_ptr<SvXMLElementExport> pMath;
+
+ if (pNode->GetType() == SmNodeType::Math || pNode->GetType() == SmNodeType::GlyphSpecial)
+ {
+ // Export SmNodeType::Math and SmNodeType::GlyphSpecial symbols as <mo> elements
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MO, true, false));
+ }
+ else if (pNode->GetType() == SmNodeType::Special)
+ {
+ bool bIsItalic = IsItalic(pNode->GetFont());
+ if (!bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ else
+ {
+ // Export SmNodeType::MathIdent and SmNodeType::Place symbols as <mi> elements:
+ // - These math symbols should not be drawn slanted. Hence we should
+ // attach a mathvariant="normal" attribute to single-char <mi> elements
+ // that are not mathematical alphanumeric symbol. For simplicity and to
+ // work around browser limitations, we always attach such an attribute.
+ // - The MathML specification suggests to use empty <mi> elements as
+ // placeholders but they won't be visible in most MathML rendering
+ // engines so let's use an empty square for SmNodeType::Place instead.
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pMath.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ }
+ sal_Unicode nArse = pTemp->GetText()[0];
+ sal_Unicode cTmp = ConvertMathToMathML(nArse);
+ if (cTmp != 0)
+ nArse = cTmp;
+ OSL_ENSURE(nArse != 0xffff, "Non existent symbol");
+ GetDocHandler()->characters(OUString(nArse));
+}
+
+void SmXMLExport::ExportText(const SmNode* pNode)
+{
+ std::unique_ptr<SvXMLElementExport> pText;
+ const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
+ switch (pNode->GetToken().eType)
+ {
+ default:
+ case TIDENT:
+ {
+ //Note that we change the fontstyle to italic for strings that
+ //are italic and longer than a single character.
+ bool bIsItalic = IsItalic(pTemp->GetFont());
+ if ((pTemp->GetText().getLength() > 1) && bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_ITALIC);
+ else if ((pTemp->GetText().getLength() == 1) && !bIsItalic)
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, XML_NORMAL);
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MI, true, false));
+ break;
+ }
+ case TNUMBER:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MN, true, false));
+ break;
+ case TTEXT:
+ pText.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MTEXT, true, false));
+ break;
+ }
+ GetDocHandler()->characters(pTemp->GetText());
+}
+
+void SmXMLExport::ExportBlank(const SmNode* pNode)
+{
+ const SmBlankNode* pTemp = static_cast<const SmBlankNode*>(pNode);
+ //!! exports an <mspace> element. Note that for example "~_~" is allowed in
+ //!! Math (so it has no sense at all) but must not result in an empty
+ //!! <msub> tag in MathML !!
+
+ if (pTemp->GetBlankNum() != 0)
+ {
+ // Attach a width attribute. We choose the (somewhat arbitrary) values
+ // ".5em" for a small gap '`' and "2em" for a large gap '~'.
+ // (see SmBlankNode::IncreaseBy for how pTemp->mnNum is set).
+ OUStringBuffer sStrBuf;
+ ::sax::Converter::convertDouble(sStrBuf, pTemp->GetBlankNum() * .5);
+ sStrBuf.append("em");
+ AddAttribute(XML_NAMESPACE_MATH, XML_WIDTH, sStrBuf.makeStringAndClear());
+ }
+
+ SvXMLElementExport aTextExport(*this, XML_NAMESPACE_MATH, XML_MSPACE, true, false);
+
+ GetDocHandler()->characters(OUString());
+}
+
+void SmXMLExport::ExportSubSupScript(const SmNode* pNode, int nLevel)
+{
+ const SmNode* pSub = nullptr;
+ const SmNode* pSup = nullptr;
+ const SmNode* pCSub = nullptr;
+ const SmNode* pCSup = nullptr;
+ const SmNode* pLSub = nullptr;
+ const SmNode* pLSup = nullptr;
+ std::unique_ptr<SvXMLElementExport> pThing2;
+
+ //if we have prescripts at all then we must use the tensor notation
+
+ //This is one of those excellent locations where scope is vital to
+ //arrange the construction and destruction of the element helper
+ //classes correctly
+ pLSub = pNode->GetSubNode(LSUB + 1);
+ pLSup = pNode->GetSubNode(LSUP + 1);
+ if (pLSub || pLSup)
+ {
+ SvXMLElementExport aMultiScripts(*this, XML_NAMESPACE_MATH, XML_MMULTISCRIPTS, true, true);
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
+ && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
+ {
+ pThing2.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
+ {
+ pThing2.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel + 1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel + 1);
+ pThing2.reset();
+
+ pSub = pNode->GetSubNode(RSUB + 1);
+ pSup = pNode->GetSubNode(RSUP + 1);
+ if (pSub || pSup)
+ {
+ if (pSub)
+ ExportNodes(pSub, nLevel + 1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ if (pSup)
+ ExportNodes(pSup, nLevel + 1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ }
+
+ //Separator element between suffix and prefix sub/sup pairs
+ {
+ SvXMLElementExport aPrescripts(*this, XML_NAMESPACE_MATH, XML_MPRESCRIPTS, true, true);
+ }
+
+ if (pLSub)
+ ExportNodes(pLSub, nLevel + 1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ if (pLSup)
+ ExportNodes(pLSup, nLevel + 1);
+ else
+ {
+ SvXMLElementExport aNone(*this, XML_NAMESPACE_MATH, XML_NONE, true, true);
+ }
+ }
+ else
+ {
+ std::unique_ptr<SvXMLElementExport> pThing;
+ if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1))
+ && nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
+ {
+ pThing.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUBSUP, true, true));
+ }
+ else if (nullptr != (pSub = pNode->GetSubNode(RSUB + 1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUB, true, true));
+ }
+ else if (nullptr != (pSup = pNode->GetSubNode(RSUP + 1)))
+ {
+ pThing.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MSUP, true, true));
+ }
+
+ if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1))
+ && nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
+ {
+ pThing2.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDEROVER, true, true));
+ }
+ else if (nullptr != (pCSub = pNode->GetSubNode(CSUB + 1)))
+ {
+ pThing2.reset(
+ new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
+ }
+ else if (nullptr != (pCSup = pNode->GetSubNode(CSUP + 1)))
+ {
+ pThing2.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
+ }
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1); //Main Term
+
+ if (pCSub)
+ ExportNodes(pCSub, nLevel + 1);
+ if (pCSup)
+ ExportNodes(pCSup, nLevel + 1);
+ pThing2.reset();
+
+ if (pSub)
+ ExportNodes(pSub, nLevel + 1);
+ if (pSup)
+ ExportNodes(pSup, nLevel + 1);
+ pThing.reset();
+ }
+}
+
+void SmXMLExport::ExportBrace(const SmNode* pNode, int nLevel)
+{
+ const SmNode* pTemp;
+ const SmNode* pLeft = pNode->GetSubNode(0);
+ const SmNode* pRight = pNode->GetSubNode(2);
+
+ // This used to generate <mfenced> or <mrow>+<mo> elements according to
+ // the stretchiness of fences. The MathML recommendation defines an
+ // <mrow>+<mo> construction that is equivalent to the <mfenced> element:
+ // http://www.w3.org/TR/MathML3/chapter3.html#presm.mfenced
+ // To simplify our code and avoid issues with mfenced implementations in
+ // MathML rendering engines, we now always generate <mrow>+<mo> elements.
+ // See #fdo 66282.
+
+ // <mrow>
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+
+ // <mo fence="true"> opening-fence </mo>
+ if (pLeft && (pLeft->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_PREFIX);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pLeft, nLevel + 1);
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(1)))
+ {
+ // <mrow>
+ SvXMLElementExport aRowExport(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+ ExportNodes(pTemp, nLevel + 1);
+ // </mrow>
+ }
+
+ // <mo fence="true"> closing-fence </mo>
+ if (pRight && (pRight->GetToken().eType != TNONE))
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_FENCE, XML_TRUE);
+ AddAttribute(XML_NAMESPACE_MATH, XML_FORM, XML_POSTFIX);
+ if (pNode->GetScaleMode() == SmScaleMode::Height)
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ else
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ ExportNodes(pRight, nLevel + 1);
+ }
+
+ // </mrow>
+}
+
+void SmXMLExport::ExportRoot(const SmNode* pNode, int nLevel)
+{
+ if (pNode->GetSubNode(0))
+ {
+ SvXMLElementExport aRoot(*this, XML_NAMESPACE_MATH, XML_MROOT, true, true);
+ ExportNodes(pNode->GetSubNode(2), nLevel + 1);
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1);
+ }
+ else
+ {
+ SvXMLElementExport aSqrt(*this, XML_NAMESPACE_MATH, XML_MSQRT, true, true);
+ ExportNodes(pNode->GetSubNode(2), nLevel + 1);
+ }
+}
+
+void SmXMLExport::ExportOperator(const SmNode* pNode, int nLevel)
+{
+ /*we need to either use content or font and size attributes
+ *here*/
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MROW, true, true);
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1);
+ ExportNodes(pNode->GetSubNode(1), nLevel + 1);
+}
+
+void SmXMLExport::ExportAttributes(const SmNode* pNode, int nLevel)
+{
+ std::unique_ptr<SvXMLElementExport> pElement;
+
+ if (pNode->GetToken().eType == TUNDERLINE)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENTUNDER, XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MUNDER, true, true));
+ }
+ else if (pNode->GetToken().eType == TOVERSTRIKE)
+ {
+ // export as <menclose notation="horizontalstrike">
+ AddAttribute(XML_NAMESPACE_MATH, XML_NOTATION, XML_HORIZONTALSTRIKE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MENCLOSE, true, true));
+ }
+ else
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_ACCENT, XML_TRUE);
+ pElement.reset(new SvXMLElementExport(*this, XML_NAMESPACE_MATH, XML_MOVER, true, true));
+ }
+
+ ExportNodes(pNode->GetSubNode(1), nLevel + 1);
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
+ static constexpr OUStringLiteral nArse = u"\u00AF";
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TUNDERLINE:
+ {
+ //proper entity support required
+ SvXMLElementExport aMath(*this, XML_NAMESPACE_MATH, XML_MO, true, true);
+ static constexpr OUStringLiteral nArse = u"\u0332";
+ GetDocHandler()->characters(nArse);
+ }
+ break;
+ case TOVERSTRIKE:
+ break;
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ {
+ // make these wide accents stretchy
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1);
+ }
+ break;
+ default:
+ ExportNodes(pNode->GetSubNode(0), nLevel + 1);
+ break;
+ }
+}
+
+static bool lcl_HasEffectOnMathvariant(const SmTokenType eType)
+{
+ return eType == TBOLD || eType == TNBOLD || eType == TITALIC || eType == TNITALIC
+ || eType == TSANS || eType == TSERIF || eType == TFIXED;
+}
+
+void SmXMLExport::ExportFont(const SmNode* pNode, int nLevel)
+{
+ // gather the mathvariant attribute relevant data from all
+ // successively following SmFontNodes...
+
+ int nBold = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nItalic = -1; // for the following variables: -1 = yet undefined; 0 = false; 1 = true;
+ int nSansSerifFixed = -1;
+ SmTokenType eNodeType = TUNKNOWN;
+
+ for (;;)
+ {
+ eNodeType = pNode->GetToken().eType;
+ if (!lcl_HasEffectOnMathvariant(eNodeType))
+ break;
+ switch (eNodeType)
+ {
+ case TBOLD:
+ nBold = 1;
+ break;
+ case TNBOLD:
+ nBold = 0;
+ break;
+ case TITALIC:
+ nItalic = 1;
+ break;
+ case TNITALIC:
+ nItalic = 0;
+ break;
+ case TSANS:
+ nSansSerifFixed = 0;
+ break;
+ case TSERIF:
+ nSansSerifFixed = 1;
+ break;
+ case TFIXED:
+ nSansSerifFixed = 2;
+ break;
+ default:
+ SAL_WARN("starmath", "unexpected case");
+ }
+ // According to the parser every node that is to be evaluated here
+ // has a single non-zero subnode at index 1!! Thus we only need to check
+ // that single node for follow-up nodes that have an effect on the attribute.
+ if (pNode->GetNumSubNodes() > 1 && pNode->GetSubNode(1)
+ && lcl_HasEffectOnMathvariant(pNode->GetSubNode(1)->GetToken().eType))
+ {
+ pNode = pNode->GetSubNode(1);
+ }
+ else
+ break;
+ }
+
+ sal_uInt32 nc;
+ switch (pNode->GetToken().eType)
+ {
+ case TPHANTOM:
+ // No attribute needed. An <mphantom> element will be used below.
+ break;
+ case TMATHMLCOL:
+ {
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ const OUString& sssStr = starmathdatabase::Identify_Color_MATHML(nc).aIdent;
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, sssStr);
+ }
+ break;
+ case TRGB:
+ case TRGBA:
+ case THEX:
+ case THTMLCOL:
+ case TDVIPSNAMESCOL:
+ case TICONICCOL:
+ {
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ OUString ssStr("#" + Color(ColorTransparency, nc).AsRGBHEXString());
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHCOLOR, ssStr);
+ }
+ break;
+ case TSIZE:
+ {
+ const SmFontNode* pFontNode = static_cast<const SmFontNode*>(pNode);
+ const Fraction& aFrac = pFontNode->GetSizeParameter();
+
+ OUStringBuffer sStrBuf;
+ switch (pFontNode->GetSizeType())
+ {
+ case FontSizeType::MULTIPLY:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(aFrac * Fraction(100.00)));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::DIVIDE:
+ ::sax::Converter::convertDouble(sStrBuf,
+ static_cast<double>(Fraction(100.00) / aFrac));
+ sStrBuf.append('%');
+ break;
+ case FontSizeType::ABSOLUT:
+ ::sax::Converter::convertDouble(sStrBuf, static_cast<double>(aFrac));
+ sStrBuf.append(GetXMLToken(XML_UNIT_PT));
+ break;
+ default:
+ {
+ //The problem here is that the wheels fall off because
+ //font size is stored in 100th's of a mm not pts, and
+ //rounding errors take their toll on the original
+ //value specified in points.
+
+ //Must fix StarMath to retain the original pt values
+ Fraction aTemp = Sm100th_mmToPts(pFontNode->GetFont().GetFontSize().Height());
+
+ if (pFontNode->GetSizeType() == FontSizeType::MINUS)
+ aTemp -= aFrac;
+ else
+ aTemp += aFrac;
+
+ double mytest = static_cast<double>(aTemp);
+
+ mytest = ::rtl::math::round(mytest, 1);
+ ::sax::Converter::convertDouble(sStrBuf, mytest);
+ sStrBuf.append(GetXMLToken(XML_UNIT_PT));
+ }
+ break;
+ }
+
+ OUString sStr(sStrBuf.makeStringAndClear());
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHSIZE, sStr);
+ }
+ break;
+ case TBOLD:
+ case TITALIC:
+ case TNBOLD:
+ case TNITALIC:
+ case TFIXED:
+ case TSANS:
+ case TSERIF:
+ {
+ // nBold: -1 = yet undefined; 0 = false; 1 = true;
+ // nItalic: -1 = yet undefined; 0 = false; 1 = true;
+ // nSansSerifFixed: -1 = undefined; 0 = sans; 1 = serif; 2 = fixed;
+ const char* pText = "normal";
+ if (nSansSerifFixed == -1 || nSansSerifFixed == 1)
+ {
+ pText = "normal";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "bold-italic";
+ }
+ else if (nSansSerifFixed == 0)
+ {
+ pText = "sans-serif";
+ if (nBold == 1 && nItalic != 1)
+ pText = "bold-sans-serif";
+ else if (nBold != 1 && nItalic == 1)
+ pText = "sans-serif-italic";
+ else if (nBold == 1 && nItalic == 1)
+ pText = "sans-serif-bold-italic";
+ }
+ else if (nSansSerifFixed == 2)
+ pText = "monospace"; // no modifiers allowed for monospace ...
+ else
+ {
+ SAL_WARN("starmath", "unexpected case");
+ }
+ AddAttribute(XML_NAMESPACE_MATH, XML_MATHVARIANT, OUString::createFromAscii(pText));
+ }
+ break;
+ default:
+ break;
+ }
+ {
+ // Wrap everything in an <mphantom> or <mstyle> element. These elements
+ // are mrow-like, so ExportExpression doesn't need to add an explicit
+ // <mrow> element. See #fdo 66283.
+ SvXMLElementExport aElement(*this, XML_NAMESPACE_MATH,
+ pNode->GetToken().eType == TPHANTOM ? XML_MPHANTOM : XML_MSTYLE,
+ true, true);
+ ExportExpression(pNode, nLevel, true);
+ }
+}
+
+void SmXMLExport::ExportVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel)
+{
+ // "[body] overbrace [script]"
+
+ // Position body, overbrace and script vertically. First place the overbrace
+ // OVER the body and then the script OVER this expression.
+
+ // [script]
+ // --[overbrace]--
+ // XXXXXX[body]XXXXXXX
+
+ // Similarly for the underbrace construction.
+
+ XMLTokenEnum which;
+
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERBRACE:
+ default:
+ which = XML_MOVER;
+ break;
+ case TUNDERBRACE:
+ which = XML_MUNDER;
+ break;
+ }
+
+ SvXMLElementExport aOver1(*this, XML_NAMESPACE_MATH, which, true, true);
+ { //Scoping
+ // using accents will draw the over-/underbraces too close to the base
+ // see http://www.w3.org/TR/MathML2/chapter3.html#id.3.4.5.2
+ // also XML_ACCENT is illegal with XML_MUNDER. Thus no XML_ACCENT attribute here!
+ SvXMLElementExport aOver2(*this, XML_NAMESPACE_MATH, which, true, true);
+ ExportNodes(pNode->Body(), nLevel);
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ ExportNodes(pNode->Brace(), nLevel);
+ }
+ ExportNodes(pNode->Script(), nLevel);
+}
+
+void SmXMLExport::ExportMatrix(const SmNode* pNode, int nLevel)
+{
+ SvXMLElementExport aTable(*this, XML_NAMESPACE_MATH, XML_MTABLE, true, true);
+ const SmMatrixNode* pMatrix = static_cast<const SmMatrixNode*>(pNode);
+ size_t i = 0;
+ for (sal_uInt16 y = 0; y < pMatrix->GetNumRows(); y++)
+ {
+ SvXMLElementExport aRow(*this, XML_NAMESPACE_MATH, XML_MTR, true, true);
+ for (sal_uInt16 x = 0; x < pMatrix->GetNumCols(); x++)
+ {
+ if (const SmNode* pTemp = pNode->GetSubNode(i++))
+ {
+ if (pTemp->GetType() == SmNodeType::Align && pTemp->GetToken().eType != TALIGNC)
+ {
+ // A left or right alignment is specified on this cell,
+ // attach the corresponding columnalign attribute.
+ AddAttribute(XML_NAMESPACE_MATH, XML_COLUMNALIGN,
+ pTemp->GetToken().eType == TALIGNL ? XML_LEFT : XML_RIGHT);
+ }
+ SvXMLElementExport aCell(*this, XML_NAMESPACE_MATH, XML_MTD, true, true);
+ ExportNodes(pTemp, nLevel + 1);
+ }
+ }
+ }
+}
+
+void SmXMLExport::ExportNodes(const SmNode* pNode, int nLevel)
+{
+ if (!pNode)
+ return;
+ switch (pNode->GetType())
+ {
+ case SmNodeType::Table:
+ ExportTable(pNode, nLevel);
+ break;
+ case SmNodeType::Align:
+ case SmNodeType::Bracebody:
+ case SmNodeType::Expression:
+ ExportExpression(pNode, nLevel);
+ break;
+ case SmNodeType::Line:
+ ExportLine(pNode, nLevel);
+ break;
+ case SmNodeType::Text:
+ ExportText(pNode);
+ break;
+ case SmNodeType::GlyphSpecial:
+ case SmNodeType::Math:
+ {
+ sal_Unicode cTmp = 0;
+ const SmTextNode* pTemp = static_cast<const SmTextNode*>(pNode);
+ if (!pTemp->GetText().isEmpty())
+ cTmp = ConvertMathToMathML(pTemp->GetText()[0]);
+ if (cTmp == 0)
+ {
+ // no conversion to MathML implemented -> export it as text
+ // thus at least it will not vanish into nothing
+ ExportText(pNode);
+ }
+ else
+ {
+ switch (pNode->GetToken().eType)
+ {
+ case TINTD:
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_TRUE);
+ break;
+ default:
+ break;
+ }
+ //To fully handle generic MathML we need to implement the full
+ //operator dictionary, we will generate MathML with explicit
+ //stretchiness for now.
+ sal_Int16 nLength = GetAttrList().getLength();
+ bool bAddStretch = true;
+ for (sal_Int16 i = 0; i < nLength; i++)
+ {
+ OUString sLocalName;
+ sal_uInt16 nPrefix = GetNamespaceMap().GetKeyByAttrName(
+ GetAttrList().getNameByIndex(i), &sLocalName);
+
+ if ((XML_NAMESPACE_MATH == nPrefix) && IsXMLToken(sLocalName, XML_STRETCHY))
+ {
+ bAddStretch = false;
+ break;
+ }
+ }
+ if (bAddStretch)
+ {
+ AddAttribute(XML_NAMESPACE_MATH, XML_STRETCHY, XML_FALSE);
+ }
+ ExportMath(pNode);
+ }
+ }
+ break;
+ case SmNodeType::
+ Special: //SmNodeType::Special requires some sort of Entity preservation in the XML engine.
+ case SmNodeType::MathIdent:
+ case SmNodeType::Place:
+ ExportMath(pNode);
+ break;
+ case SmNodeType::BinHor:
+ ExportBinaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::UnHor:
+ ExportUnaryHorizontal(pNode, nLevel);
+ break;
+ case SmNodeType::Brace:
+ ExportBrace(pNode, nLevel);
+ break;
+ case SmNodeType::BinVer:
+ ExportBinaryVertical(pNode, nLevel);
+ break;
+ case SmNodeType::BinDiagonal:
+ ExportBinaryDiagonal(pNode, nLevel);
+ break;
+ case SmNodeType::SubSup:
+ ExportSubSupScript(pNode, nLevel);
+ break;
+ case SmNodeType::Root:
+ ExportRoot(pNode, nLevel);
+ break;
+ case SmNodeType::Oper:
+ ExportOperator(pNode, nLevel);
+ break;
+ case SmNodeType::Attribute:
+ ExportAttributes(pNode, nLevel);
+ break;
+ case SmNodeType::Font:
+ ExportFont(pNode, nLevel);
+ break;
+ case SmNodeType::VerticalBrace:
+ ExportVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Matrix:
+ ExportMatrix(pNode, nLevel);
+ break;
+ case SmNodeType::Blank:
+ ExportBlank(pNode);
+ break;
+ default:
+ SAL_WARN("starmath", "Warning: failed to export a node?");
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathml/mathmlimport.cxx b/starmath/source/mathml/mathmlimport.cxx
new file mode 100644
index 000000000..44d8cc1e3
--- /dev/null
+++ b/starmath/source/mathml/mathmlimport.cxx
@@ -0,0 +1,2696 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/*todo: Change characters and tcharacters to accumulate the characters together
+into one string, xml parser hands them to us line by line rather than all in
+one go*/
+
+#include <com/sun/star/xml/sax/InputSource.hpp>
+#include <com/sun/star/xml/sax/FastParser.hpp>
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/SAXParseException.hpp>
+#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
+#include <com/sun/star/packages/WrongPasswordException.hpp>
+#include <com/sun/star/packages/zip/ZipIOException.hpp>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/uno/Any.h>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+#include <comphelper/fileformat.h>
+#include <comphelper/genericpropertyset.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/propertysetinfo.hxx>
+#include <rtl/character.hxx>
+#include <sal/log.hxx>
+#include <sfx2/frame.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/sfxsids.hrc>
+#include <sfx2/sfxmodelfactory.hxx>
+#include <osl/diagnose.h>
+#include <sot/storage.hxx>
+#include <svtools/sfxecode.hxx>
+#include <svl/itemset.hxx>
+#include <svl/stritem.hxx>
+#include <unotools/streamwrap.hxx>
+#include <sax/tools/converter.hxx>
+#include <xmloff/DocumentSettingsContext.hxx>
+#include <xmloff/xmlnamespace.hxx>
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <xmloff/xmlmetai.hxx>
+#include <svx/dialmgr.hxx>
+#include <svx/strings.hrc>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.hxx>
+
+#include <mathmlattr.hxx>
+#include <xparsmlbase.hxx>
+#include <mathmlimport.hxx>
+#include <document.hxx>
+#include <smdll.hxx>
+#include <unomodel.hxx>
+#include <utility.hxx>
+#include <visitors.hxx>
+#include <starmathdatabase.hxx>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::document;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+namespace
+{
+std::unique_ptr<SmNode> popOrZero(SmNodeStack& rStack)
+{
+ if (rStack.empty())
+ return nullptr;
+ auto pTmp = std::move(rStack.front());
+ rStack.pop_front();
+ return pTmp;
+}
+}
+
+ErrCode SmXMLImportWrapper::Import(SfxMedium& rMedium)
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+
+ //Make a model component from our SmModel
+ uno::Reference<lang::XComponent> xModelComp = xModel;
+ OSL_ENSURE(xModelComp.is(), "XMLReader::Read: got no model");
+
+ // try to get an XStatusIndicator from the Medium
+ uno::Reference<task::XStatusIndicator> xStatusIndicator;
+
+ bool bEmbedded = false;
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ SmDocShell* pDocShell = pModel ? static_cast<SmDocShell*>(pModel->GetObjectShell()) : nullptr;
+ if (pDocShell)
+ {
+ OSL_ENSURE(pDocShell->GetMedium() == &rMedium, "different SfxMedium found");
+
+ SfxItemSet* pSet = rMedium.GetItemSet();
+ if (pSet)
+ {
+ const SfxUnoAnyItem* pItem = pSet->GetItem(SID_PROGRESS_STATUSBAR_CONTROL);
+ if (pItem)
+ pItem->GetValue() >>= xStatusIndicator;
+ }
+
+ if (SfxObjectCreateMode::EMBEDDED == pDocShell->GetCreateMode())
+ bEmbedded = true;
+ }
+
+ static const comphelper::PropertyMapEntry aInfoMap[]
+ = { { OUString("PrivateData"), 0, cppu::UnoType<XInterface>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("BaseURI"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamRelPath"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 },
+ { OUString("StreamName"), 0, ::cppu::UnoType<OUString>::get(),
+ beans::PropertyAttribute::MAYBEVOID, 0 } };
+ uno::Reference<beans::XPropertySet> xInfoSet(
+ comphelper::GenericPropertySet_CreateInstance(new comphelper::PropertySetInfo(aInfoMap)));
+
+ // Set base URI
+ OUString const baseURI(rMedium.GetBaseURL());
+ // needed for relative URLs; but it's OK to import e.g. MathML from the
+ // clipboard without one
+ SAL_INFO_IF(baseURI.isEmpty(), "starmath", "SmXMLImportWrapper: no base URL");
+ xInfoSet->setPropertyValue("BaseURI", Any(baseURI));
+
+ sal_Int32 nSteps = 3;
+ if (!(rMedium.IsStorage()))
+ nSteps = 1;
+
+ sal_Int32 nProgressRange(nSteps);
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start(SvxResId(RID_SVXSTR_DOC_LOAD), nProgressRange);
+ }
+
+ nSteps = 0;
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ if (rMedium.IsStorage())
+ {
+ // TODO/LATER: handle the case of embedded links gracefully
+ if (bEmbedded) // && !rMedium.GetStorage()->IsRoot() )
+ {
+ OUString aName("dummyObjName");
+ if (rMedium.GetItemSet())
+ {
+ const SfxStringItem* pDocHierarchItem
+ = rMedium.GetItemSet()->GetItem(SID_DOC_HIERARCHICALNAME);
+ if (pDocHierarchItem)
+ aName = pDocHierarchItem->GetValue();
+ }
+
+ if (!aName.isEmpty())
+ {
+ xInfoSet->setPropertyValue("StreamRelPath", Any(aName));
+ }
+ }
+
+ bool bOASIS = (SotStorage::GetVersion(rMedium.GetStorage()) > SOFFICE_FILEFORMAT_60);
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ auto nWarn
+ = ReadThroughComponent(rMedium.GetStorage(), xModelComp, "meta.xml", xContext, xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisMetaImporter"
+ : "com.sun.star.comp.Math.XMLMetaImporter"),
+ m_bUseHTMLMLEntities);
+
+ if (nWarn != ERRCODE_IO_BROKENPACKAGE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nWarn = ReadThroughComponent(rMedium.GetStorage(), xModelComp, "settings.xml", xContext,
+ xInfoSet,
+ (bOASIS ? "com.sun.star.comp.Math.XMLOasisSettingsImporter"
+ : "com.sun.star.comp.Math.XMLSettingsImporter"),
+ m_bUseHTMLMLEntities);
+
+ if (nWarn != ERRCODE_IO_BROKENPACKAGE)
+ {
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent(
+ rMedium.GetStorage(), xModelComp, "content.xml", xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", m_bUseHTMLMLEntities);
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ else
+ {
+ Reference<io::XInputStream> xInputStream
+ = new utl::OInputStreamWrapper(rMedium.GetInStream());
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nSteps++);
+
+ nError = ReadThroughComponent(xInputStream, xModelComp, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", false,
+ m_bUseHTMLMLEntities);
+ }
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->end();
+ return nError;
+}
+
+/// read a component (file + filter version)
+ErrCode SmXMLImportWrapper::ReadThroughComponent(const Reference<io::XInputStream>& xInputStream,
+ const Reference<XComponent>& xModelComponent,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pFilterName, bool bEncrypted,
+ bool bUseHTMLMLEntities)
+{
+ ErrCode nError = ERRCODE_SFX_DOLOADFAILED;
+ OSL_ENSURE(xInputStream.is(), "input stream missing");
+ OSL_ENSURE(xModelComponent.is(), "document missing");
+ OSL_ENSURE(rxContext.is(), "factory missing");
+ OSL_ENSURE(nullptr != pFilterName, "I need a service name for the component!");
+
+ // prepare ParserInputSource
+ xml::sax::InputSource aParserInput;
+ aParserInput.aInputStream = xInputStream;
+
+ Sequence<Any> aArgs{ Any(rPropSet) };
+
+ // get filter
+ Reference<XInterface> xFilter
+ = rxContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ OUString::createFromAscii(pFilterName), aArgs, rxContext);
+ SAL_WARN_IF(!xFilter, "starmath", "Can't instantiate filter component " << pFilterName);
+ if (!xFilter.is())
+ return nError;
+
+ // connect model and filter
+ Reference<XImporter> xImporter(xFilter, UNO_QUERY);
+ xImporter->setTargetDocument(xModelComponent);
+
+ // finally, parser the stream
+ try
+ {
+ Reference<css::xml::sax::XFastParser> xFastParser(xFilter, UNO_QUERY);
+ Reference<css::xml::sax::XFastDocumentHandler> xFastDocHandler(xFilter, UNO_QUERY);
+ if (xFastParser)
+ {
+ if (bUseHTMLMLEntities)
+ xFastParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xFastParser->parseStream(aParserInput);
+ }
+ else if (xFastDocHandler)
+ {
+ Reference<css::xml::sax::XFastParser> xParser
+ = css::xml::sax::FastParser::create(rxContext);
+ if (bUseHTMLMLEntities)
+ xParser->setCustomEntityNames(starmathdatabase::icustomMathmlHtmlEntities);
+ xParser->setFastDocumentHandler(xFastDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+ else
+ {
+ Reference<css::xml::sax::XDocumentHandler> xDocHandler(xFilter, UNO_QUERY);
+ assert(xDocHandler);
+ Reference<css::xml::sax::XParser> xParser = css::xml::sax::Parser::create(rxContext);
+ xParser->setDocumentHandler(xDocHandler);
+ xParser->parseStream(aParserInput);
+ }
+
+ auto pFilter = comphelper::getFromUnoTunnel<SmXMLImport>(xFilter);
+ if (pFilter && pFilter->GetSuccess())
+ nError = ERRCODE_NONE;
+ }
+ catch (const xml::sax::SAXParseException& r)
+ {
+ // sax parser sends wrapped exceptions,
+ // try to find the original one
+ xml::sax::SAXException aSaxEx = *static_cast<const xml::sax::SAXException*>(&r);
+ bool bTryChild = true;
+
+ while (bTryChild)
+ {
+ xml::sax::SAXException aTmp;
+ if (aSaxEx.WrappedException >>= aTmp)
+ aSaxEx = aTmp;
+ else
+ bTryChild = false;
+ }
+
+ packages::zip::ZipIOException aBrokenPackage;
+ if (aSaxEx.WrappedException >>= aBrokenPackage)
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if (bEncrypted)
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const xml::sax::SAXException& r)
+ {
+ packages::zip::ZipIOException aBrokenPackage;
+ if (r.WrappedException >>= aBrokenPackage)
+ return ERRCODE_IO_BROKENPACKAGE;
+
+ if (bEncrypted)
+ nError = ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (const packages::zip::ZipIOException&)
+ {
+ nError = ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (const io::IOException&)
+ {
+ }
+ catch (const std::range_error&)
+ {
+ }
+
+ return nError;
+}
+
+ErrCode SmXMLImportWrapper::ReadThroughComponent(const uno::Reference<embed::XStorage>& xStorage,
+ const Reference<XComponent>& xModelComponent,
+ const char* pStreamName,
+ Reference<uno::XComponentContext> const& rxContext,
+ Reference<beans::XPropertySet> const& rPropSet,
+ const char* pFilterName, bool bUseHTMLMLEntities)
+{
+ OSL_ENSURE(xStorage.is(), "Need storage!");
+ OSL_ENSURE(nullptr != pStreamName, "Please, please, give me a name!");
+
+ // open stream (and set parser input)
+ OUString sStreamName = OUString::createFromAscii(pStreamName);
+
+ // get input stream
+ try
+ {
+ uno::Reference<io::XStream> xEventsStream
+ = xStorage->openStreamElement(sStreamName, embed::ElementModes::READ);
+
+ // determine if stream is encrypted or not
+ uno::Reference<beans::XPropertySet> xProps(xEventsStream, uno::UNO_QUERY);
+ Any aAny = xProps->getPropertyValue("Encrypted");
+ bool bEncrypted = false;
+ if (aAny.getValueType() == cppu::UnoType<bool>::get())
+ aAny >>= bEncrypted;
+
+ // set Base URL
+ if (rPropSet.is())
+ {
+ rPropSet->setPropertyValue("StreamName", Any(sStreamName));
+ }
+
+ Reference<io::XInputStream> xStream = xEventsStream->getInputStream();
+ return ReadThroughComponent(xStream, xModelComponent, rxContext, rPropSet, pFilterName,
+ bEncrypted, bUseHTMLMLEntities);
+ }
+ catch (packages::WrongPasswordException&)
+ {
+ return ERRCODE_SFX_WRONGPASSWORD;
+ }
+ catch (packages::zip::ZipIOException&)
+ {
+ return ERRCODE_IO_BROKENPACKAGE;
+ }
+ catch (uno::Exception&)
+ {
+ }
+
+ return ERRCODE_SFX_DOLOADFAILED;
+}
+
+SmXMLImport::SmXMLImport(const css::uno::Reference<css::uno::XComponentContext>& rContext,
+ OUString const& implementationName, SvXMLImportFlags nImportFlags)
+ : SvXMLImport(rContext, implementationName, nImportFlags)
+ , bSuccess(false)
+ , nParseDepth(0)
+ , mnSmSyntaxVersion(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+{
+}
+
+const uno::Sequence<sal_Int8>& SmXMLImport::getUnoTunnelId() noexcept
+{
+ static const comphelper::UnoIdInit theSmXMLImportUnoTunnelId;
+ return theSmXMLImportUnoTunnelId.getSeq();
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(
+ new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLImporter", SvXMLImportFlags::ALL));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisMetaImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisMetaImporter",
+ SvXMLImportFlags::META));
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+Math_XMLOasisSettingsImporter_get_implementation(uno::XComponentContext* pCtx,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmXMLImport(pCtx, "com.sun.star.comp.Math.XMLOasisSettingsImporter",
+ SvXMLImportFlags::SETTINGS));
+}
+
+sal_Int64 SAL_CALL SmXMLImport::getSomething(const uno::Sequence<sal_Int8>& rId)
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SvXMLImport>{});
+}
+
+void SmXMLImport::endDocument()
+{
+ //Set the resulted tree into the SmDocShell where it belongs
+ std::unique_ptr<SmNode> pTree = popOrZero(aNodeStack);
+ if (pTree && pTree->GetType() == SmNodeType::Table)
+ {
+ uno::Reference<frame::XModel> xModel = GetModel();
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (pModel)
+ {
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ auto pTreeTmp = pTree.get();
+ pDocShell->SetFormulaTree(static_cast<SmTableNode*>(pTree.release()));
+ if (aText.isEmpty()) //If we picked up no annotation text
+ {
+ // Get text from imported formula
+ SmNodeToTextVisitor tmpvisitor(pTreeTmp, aText);
+ }
+
+ // Convert symbol names
+ AbstractSmParser* rParser = pDocShell->GetParser();
+ bool bVal = rParser->IsImportSymbolNames();
+ rParser->SetImportSymbolNames(true);
+ auto pTmpTree = rParser->Parse(aText);
+ aText = rParser->GetText();
+ pTmpTree.reset();
+ rParser->SetImportSymbolNames(bVal);
+
+ pDocShell->SetText(aText);
+ pDocShell->SetSmSyntaxVersion(mnSmSyntaxVersion);
+ }
+ OSL_ENSURE(pModel, "So there *was* a UNO problem after all");
+
+ bSuccess = true;
+ }
+
+ SvXMLImport::endDocument();
+}
+
+namespace
+{
+class SmXMLImportContext : public SvXMLImportContext
+{
+public:
+ SmXMLImportContext(SmXMLImport& rImport)
+ : SvXMLImportContext(rImport)
+ {
+ GetSmImport().IncParseDepth();
+ }
+
+ virtual ~SmXMLImportContext() override { GetSmImport().DecParseDepth(); }
+
+ SmXMLImport& GetSmImport() { return static_cast<SmXMLImport&>(GetImport()); }
+
+ virtual void TCharacters(const OUString& /*rChars*/);
+ virtual void SAL_CALL characters(const OUString& rChars) override;
+ virtual void SAL_CALL startFastElement(
+ sal_Int32 /*nElement*/,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& /*rAttrList*/) override
+ {
+ if (GetSmImport().TooDeep())
+ throw std::range_error("too deep");
+ }
+};
+}
+
+void SmXMLImportContext::TCharacters(const OUString& /*rChars*/) {}
+
+void SmXMLImportContext::characters(const OUString& rChars)
+{
+ /*
+ Whitespace occurring within the content of token elements is "trimmed"
+ from the ends (i.e. all whitespace at the beginning and end of the
+ content is removed), and "collapsed" internally (i.e. each sequence of
+ 1 or more whitespace characters is replaced with one blank character).
+ */
+ //collapsing not done yet!
+ const OUString& rChars2 = rChars.trim();
+ if (!rChars2.isEmpty())
+ TCharacters(rChars2 /*.collapse()*/);
+}
+
+namespace
+{
+struct SmXMLContext_Helper
+{
+ sal_Int8 nIsBold;
+ sal_Int8 nIsItalic;
+ double nFontSize;
+ OUString sFontFamily;
+ OUString sColor;
+
+ SmXMLImportContext& rContext;
+
+ explicit SmXMLContext_Helper(SmXMLImportContext& rImport)
+ : nIsBold(-1)
+ , nIsItalic(-1)
+ , nFontSize(0.0)
+ , rContext(rImport)
+ {
+ }
+
+ bool IsFontNodeNeeded() const;
+ void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
+ void ApplyAttrs();
+};
+}
+
+bool SmXMLContext_Helper::IsFontNodeNeeded() const
+{
+ return nIsBold != -1 || nIsItalic != -1 || nFontSize != 0.0 || !sFontFamily.isEmpty()
+ || !sColor.isEmpty();
+}
+
+void SmXMLContext_Helper::RetrieveAttrs(
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ bool bMvFound = false;
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ // sometimes they have namespace, sometimes not?
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_FONTWEIGHT:
+ nIsBold = sal_Int8(IsXMLToken(aIter, XML_BOLD));
+ break;
+ case XML_FONTSTYLE:
+ nIsItalic = sal_Int8(IsXMLToken(aIter, XML_ITALIC));
+ break;
+ case XML_FONTSIZE:
+ case XML_MATHSIZE:
+ {
+ OUString sValue = aIter.toString();
+ ::sax::Converter::convertDouble(nFontSize, sValue);
+ rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
+ util::MeasureUnit::POINT);
+ if (-1 == sValue.indexOf(GetXMLToken(XML_UNIT_PT)))
+ {
+ if (-1 == sValue.indexOf('%'))
+ nFontSize = 0.0;
+ else
+ {
+ rContext.GetSmImport().GetMM100UnitConverter().SetXMLMeasureUnit(
+ util::MeasureUnit::PERCENT);
+ }
+ }
+ break;
+ }
+ case XML_FONTFAMILY:
+ sFontFamily = aIter.toString();
+ break;
+ case XML_COLOR:
+ sColor = aIter.toString();
+ break;
+ case XML_MATHCOLOR:
+ sColor = aIter.toString();
+ break;
+ case XML_MATHVARIANT:
+ bMvFound = true;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+
+ if (bMvFound)
+ {
+ // Ignore deprecated attributes fontfamily, fontweight, and fontstyle
+ // in favor of mathvariant, as specified in
+ // <https://www.w3.org/TR/MathML3/chapter3.html#presm.deprecatt>.
+ sFontFamily.clear();
+ nIsBold = -1;
+ nIsItalic = -1;
+ }
+}
+
+void SmXMLContext_Helper::ApplyAttrs()
+{
+ SmNodeStack& rNodeStack = rContext.GetSmImport().GetNodeStack();
+
+ if (!IsFontNodeNeeded())
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+
+ if (nIsBold != -1)
+ {
+ if (nIsBold)
+ aToken.eType = TBOLD;
+ else
+ aToken.eType = TNBOLD;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nIsItalic != -1)
+ {
+ if (nIsItalic)
+ aToken.eType = TITALIC;
+ else
+ aToken.eType = TNITALIC;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (nFontSize != 0.0)
+ {
+ aToken.eType = TSIZE;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+
+ if (util::MeasureUnit::PERCENT
+ == rContext.GetSmImport().GetMM100UnitConverter().GetXMLMeasureUnit())
+ {
+ if (nFontSize < 100.00)
+ pFontNode->SetSizeParameter(Fraction(100.00 / nFontSize), FontSizeType::DIVIDE);
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize / 100.00), FontSizeType::MULTIPLY);
+ }
+ else
+ pFontNode->SetSizeParameter(Fraction(nFontSize), FontSizeType::ABSOLUT);
+
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ if (!sColor.isEmpty())
+ {
+ SmColorTokenTableEntry aSmColorTokenTableEntry;
+ aSmColorTokenTableEntry = starmathdatabase::Identify_ColorName_HTML(sColor);
+ if (aSmColorTokenTableEntry.eType == TRGB)
+ aSmColorTokenTableEntry = starmathdatabase::Identify_Color_Parser(
+ sal_uInt32(aSmColorTokenTableEntry.cColor));
+ if (aSmColorTokenTableEntry.eType != TERROR)
+ {
+ aToken = aSmColorTokenTableEntry;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+ // If not known, not implemented yet. Giving up.
+ }
+ if (sFontFamily.isEmpty())
+ return;
+
+ if (sFontFamily.equalsIgnoreAsciiCase(GetXMLToken(XML_FIXED)))
+ aToken.eType = TFIXED;
+ else if (sFontFamily.equalsIgnoreAsciiCase("sans"))
+ aToken.eType = TSANS;
+ else if (sFontFamily.equalsIgnoreAsciiCase("serif"))
+ aToken.eType = TSERIF;
+ else //Just give up, we need to extend our font mechanism to be
+ //more general
+ return;
+
+ aToken.aText = sFontFamily;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+}
+
+namespace
+{
+class SmXMLTokenAttrHelper
+{
+ SmXMLImportContext& mrContext;
+ MathMLMathvariantValue meMv;
+ bool mbMvFound;
+
+public:
+ SmXMLTokenAttrHelper(SmXMLImportContext& rContext)
+ : mrContext(rContext)
+ , meMv(MathMLMathvariantValue::Normal)
+ , mbMvFound(false)
+ {
+ }
+
+ void RetrieveAttrs(const uno::Reference<xml::sax::XFastAttributeList>& xAttrList);
+ void ApplyAttrs(MathMLMathvariantValue eDefaultMv);
+};
+}
+
+void SmXMLTokenAttrHelper::RetrieveAttrs(
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_MATHVARIANT:
+ if (!GetMathMLMathvariantValue(sValue, meMv))
+ SAL_WARN("starmath", "failed to recognize mathvariant: " << sValue);
+ mbMvFound = true;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLTokenAttrHelper::ApplyAttrs(MathMLMathvariantValue eDefaultMv)
+{
+ assert(eDefaultMv == MathMLMathvariantValue::Normal
+ || eDefaultMv == MathMLMathvariantValue::Italic);
+
+ std::vector<SmTokenType> vVariant;
+ MathMLMathvariantValue eMv = mbMvFound ? meMv : eDefaultMv;
+ switch (eMv)
+ {
+ case MathMLMathvariantValue::Normal:
+ vVariant.push_back(TNITALIC);
+ break;
+ case MathMLMathvariantValue::Bold:
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Italic:
+ // nothing to do
+ break;
+ case MathMLMathvariantValue::BoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::DoubleStruck:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldFraktur:
+ // TODO: Fraktur
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Script:
+ // TODO
+ break;
+ case MathMLMathvariantValue::BoldScript:
+ // TODO: Script
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::Fraktur:
+ // TODO
+ break;
+ case MathMLMathvariantValue::SansSerif:
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::BoldSansSerif:
+ vVariant.push_back(TSANS);
+ vVariant.push_back(TBOLD);
+ break;
+ case MathMLMathvariantValue::SansSerifItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::SansSerifBoldItalic:
+ vVariant.push_back(TITALIC);
+ vVariant.push_back(TBOLD);
+ vVariant.push_back(TSANS);
+ break;
+ case MathMLMathvariantValue::Monospace:
+ vVariant.push_back(TFIXED);
+ break;
+ case MathMLMathvariantValue::Initial:
+ case MathMLMathvariantValue::Tailed:
+ case MathMLMathvariantValue::Looped:
+ case MathMLMathvariantValue::Stretched:
+ // TODO
+ break;
+ }
+ if (vVariant.empty())
+ return;
+ SmNodeStack& rNodeStack = mrContext.GetSmImport().GetNodeStack();
+ for (auto eType : vVariant)
+ {
+ SmToken aToken;
+ aToken.eType = eType;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(aToken));
+ pFontNode->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pFontNode));
+ }
+}
+
+namespace
+{
+class SmXMLDocContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLDocContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+/*avert the gaze from the originator*/
+class SmXMLRowContext_Impl : public SmXMLDocContext_Impl
+{
+protected:
+ size_t nElementCount;
+
+public:
+ SmXMLRowContext_Impl(SmXMLImport& rImport)
+ : SmXMLDocContext_Impl(rImport)
+ , nElementCount(GetSmImport().GetNodeStack().size())
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+
+ uno::Reference<xml::sax::XFastContextHandler> StrictCreateChildContext(sal_Int32 nElement);
+
+ virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLEncloseContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <menclose notation="horizontalstrike"> into
+ // "overstrike{}" and extend the Math syntax to support more notations
+ SmXMLEncloseContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLEncloseContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <menclose> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+}
+
+namespace
+{
+class SmXMLFracContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ // TODO/LATER: convert <mfrac bevelled="true"> into "wideslash{}{}"
+ SmXMLFracContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLSqrtContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLSqrtContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLRootContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLRootContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+class SmXMLStyleContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ SmXMLContext_Helper aStyleHelper;
+
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLStyleContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , aStyleHelper(*this)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+void SmXMLStyleContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ aStyleHelper.RetrieveAttrs(xAttrList);
+}
+
+void SmXMLStyleContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mstyle> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ if (rNodeStack.size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+ aStyleHelper.ApplyAttrs();
+}
+
+namespace
+{
+class SmXMLPaddedContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPaddedContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLPaddedContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mpadded> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+}
+
+namespace
+{
+class SmXMLPhantomContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ /*Right now the style tag is completely ignored*/
+ SmXMLPhantomContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLPhantomContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <mphantom> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TPHANTOM;
+
+ std::unique_ptr<SmFontNode> pPhantom(new SmFontNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ pPhantom->SetSubNodes(nullptr, popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pPhantom));
+}
+
+namespace
+{
+class SmXMLFencedContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ sal_Unicode cBegin;
+ sal_Unicode cEnd;
+ bool bIsStretchy;
+
+public:
+ SmXMLFencedContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , cBegin('(')
+ , cEnd(')')
+ , bIsStretchy(false)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLFencedContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ //temp, starmath cannot handle multichar brackets (I think)
+ case XML_OPEN:
+ cBegin = aIter.toString()[0];
+ break;
+ case XML_CLOSE:
+ cEnd = aIter.toString()[0];
+ break;
+ case XML_STRETCHY:
+ bIsStretchy = IsXMLToken(aIter, XML_TRUE);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ /*Go to superclass*/
+ break;
+ }
+ }
+}
+
+void SmXMLFencedContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.aText = ",";
+ aToken.nLevel = 5;
+
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ if (bIsStretchy)
+ aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cBegin);
+ else
+ aToken = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(cBegin);
+ if (aToken.eType == TERROR)
+ aToken = SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+ if (bIsStretchy)
+ aToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(cEnd);
+ else
+ aToken = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(cEnd);
+ if (aToken.eType == TERROR)
+ aToken = SmToken(TRPARENT, MS_RPARENT, ")", TG::LBrace, 5);
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ aToken.cMathChar = u"";
+ aToken.eType = TIDENT;
+
+ auto i = rNodeStack.size() - nElementCount;
+ if (rNodeStack.size() - nElementCount > 1)
+ i += rNodeStack.size() - 1 - nElementCount;
+ aRelationArray.resize(i);
+ while (rNodeStack.size() > nElementCount)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[--i] = pNode.release();
+ if (i > 1 && rNodeStack.size() > 1)
+ aRelationArray[--i] = new SmGlyphSpecialNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray));
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ // mfenced is always scalable. Stretchy keyword is not official, but in case of been in there
+ // can be used as a hint.
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pSNode));
+}
+
+namespace
+{
+class SmXMLErrorContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLErrorContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLErrorContext_Impl::endFastElement(sal_Int32 /*nElement*/)
+{
+ /*Right now the error tag is completely ignored, what
+ can I do with it in starmath, ?, maybe we need a
+ report window ourselves, do a test for validity of
+ the xml input, use mirrors, and then generate
+ the markup inside the merror with a big red colour
+ of something. For now just throw them all away.
+ */
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ while (rNodeStack.size() > nElementCount)
+ {
+ rNodeStack.pop_front();
+ }
+}
+
+namespace
+{
+class SmXMLNumberContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLNumberContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TNUMBER;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLNumberContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+void SmXMLNumberContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_NUMBER));
+}
+
+namespace
+{
+class SmXMLAnnotationContext_Impl : public SmXMLImportContext
+{
+ sal_uInt8 mnStarMathVersion;
+
+public:
+ SmXMLAnnotationContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , mnStarMathVersion(0)
+ {
+ }
+
+ void SAL_CALL characters(const OUString& rChars) override;
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+void SmXMLAnnotationContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ // sometimes they have namespace, sometimes not?
+ switch (aIter.getToken() & TOKEN_MASK)
+ {
+ case XML_ENCODING:
+ mnStarMathVersion
+ = aIter.toView() == "StarMath 5.0" ? 5 : aIter.toView() == "StarMath 6" ? 6 : 0;
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLAnnotationContext_Impl::characters(const OUString& rChars)
+{
+ if (mnStarMathVersion)
+ {
+ GetSmImport().SetText(GetSmImport().GetText() + rChars);
+ GetSmImport().SetSmSyntaxVersion(mnStarMathVersion);
+ }
+}
+
+namespace
+{
+class SmXMLTextContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLTextContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLTextContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+void SmXMLTextContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_TEXT));
+}
+
+namespace
+{
+class SmXMLStringContext_Impl : public SmXMLImportContext
+{
+protected:
+ SmToken aToken;
+
+public:
+ SmXMLStringContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TTEXT;
+ }
+
+ virtual void TCharacters(const OUString& rChars) override;
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLStringContext_Impl::TCharacters(const OUString& rChars)
+{
+ /*
+ The content of <ms> elements should be rendered with visible "escaping" of
+ certain characters in the content, including at least "double quote"
+ itself, and preferably whitespace other than individual blanks. The intent
+ is for the viewer to see that the expression is a string literal, and to
+ see exactly which characters form its content. For example, <ms>double
+ quote is "</ms> might be rendered as "double quote is \"".
+
+ Obviously this isn't fully done here.
+ */
+ aToken.aText = "\"" + rChars + "\"";
+}
+
+void SmXMLStringContext_Impl::endFastElement(sal_Int32)
+{
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_FIXED));
+}
+
+namespace
+{
+class SmXMLIdentifierContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ SmXMLContext_Helper aStyleHelper;
+ SmToken aToken;
+
+public:
+ SmXMLIdentifierContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , aStyleHelper(*this)
+ {
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ }
+
+ void TCharacters(const OUString& rChars) override;
+ void SAL_CALL
+ startFastElement(sal_Int32 /*nElement*/,
+ const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override
+ {
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+ aStyleHelper.RetrieveAttrs(xAttrList);
+ };
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLIdentifierContext_Impl::endFastElement(sal_Int32)
+{
+ std::unique_ptr<SmTextNode> pNode;
+ //we will handle identifier italic/normal here instead of with a standalone
+ //font node
+ if (((aStyleHelper.nIsItalic == -1) && (aToken.aText.getLength() > 1))
+ || ((aStyleHelper.nIsItalic == 0) && (aToken.aText.getLength() == 1)))
+ {
+ pNode.reset(new SmTextNode(aToken, FNT_FUNCTION));
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ else
+ pNode.reset(new SmTextNode(aToken, FNT_VARIABLE));
+ if (aStyleHelper.nIsItalic != -1)
+ {
+ if (aStyleHelper.nIsItalic)
+ pNode->GetFont().SetItalic(ITALIC_NORMAL);
+ else
+ pNode->GetFont().SetItalic(ITALIC_NONE);
+ aStyleHelper.nIsItalic = -1;
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+ aStyleHelper.ApplyAttrs();
+
+ maTokenAttrHelper.ApplyAttrs((aToken.aText.getLength() == 1) ? MathMLMathvariantValue::Italic
+ : MathMLMathvariantValue::Normal);
+}
+
+void SmXMLIdentifierContext_Impl::TCharacters(const OUString& rChars) { aToken.aText = rChars; }
+
+namespace
+{
+class SmXMLOperatorContext_Impl : public SmXMLImportContext
+{
+ SmXMLTokenAttrHelper maTokenAttrHelper;
+ bool bIsStretchy;
+ bool bIsFenced;
+ bool isPrefix;
+ bool isInfix;
+ bool isPostfix;
+ SmToken aToken;
+
+public:
+ SmXMLOperatorContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ , maTokenAttrHelper(*this)
+ , bIsStretchy(false)
+ , bIsFenced(false)
+ , isPrefix(false)
+ , isInfix(false)
+ , isPostfix(false)
+ {
+ aToken.eType = TSPECIAL;
+ aToken.nLevel = 5;
+ }
+
+ void TCharacters(const OUString& rChars) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLOperatorContext_Impl::TCharacters(const OUString& rChars)
+{
+ aToken.setChar(rChars[0]);
+ SmToken bToken;
+ if (bIsFenced)
+ {
+ if (isPrefix)
+ bToken
+ = starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]);
+ else if (isInfix)
+ bToken = SmToken(TMLINE, MS_VERTLINE, "mline", TG::NONE, 0);
+ else if (isPostfix)
+ bToken
+ = starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(aToken.cMathChar[0]);
+ else
+ bToken = starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(
+ aToken.cMathChar[0]);
+ }
+ else
+ bToken = starmathdatabase::Identify_SmXMLOperatorContext_Impl(aToken.cMathChar[0],
+ bIsStretchy);
+ if (bToken.eType != TERROR)
+ aToken = bToken;
+}
+
+void SmXMLOperatorContext_Impl::endFastElement(sal_Int32)
+{
+ std::unique_ptr<SmMathSymbolNode> pNode(new SmMathSymbolNode(aToken));
+ //For stretchy scaling the scaling must be retrieved from this node
+ //and applied to the expression itself so as to get the expression
+ //to scale the operator to the height of the expression itself
+ if (bIsStretchy)
+ pNode->SetScaleMode(SmScaleMode::Height);
+ GetSmImport().GetNodeStack().push_front(std::move(pNode));
+
+ // TODO: apply to non-alphabetic characters too
+ if (rtl::isAsciiAlpha(aToken.cMathChar[0]))
+ maTokenAttrHelper.ApplyAttrs(MathMLMathvariantValue::Normal);
+}
+
+void SmXMLOperatorContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ maTokenAttrHelper.RetrieveAttrs(xAttrList);
+
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_STRETCHY:
+ bIsStretchy = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_FENCE:
+ bIsFenced = IsXMLToken(aIter, XML_TRUE);
+ break;
+ case XML_FORM:
+ isPrefix = IsXMLToken(aIter, XML_PREFIX); // <
+ isInfix = IsXMLToken(aIter, XML_INFIX); // |
+ isPostfix = IsXMLToken(aIter, XML_POSTFIX); // >
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+namespace
+{
+class SmXMLSpaceContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLSpaceContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+bool lcl_CountBlanks(const MathMLAttributeLengthValue& rLV, sal_Int32* pWide, sal_Int32* pNarrow)
+{
+ assert(pWide);
+ assert(pNarrow);
+ if (rLV.aNumber.GetNumerator() == 0)
+ {
+ *pWide = *pNarrow = 0;
+ return true;
+ }
+ // TODO: honor other units than em
+ if (rLV.eUnit != MathMLLengthUnit::Em)
+ return false;
+ if (rLV.aNumber.GetNumerator() < 0)
+ return false;
+ const Fraction aTwo(2, 1);
+ auto aWide = rLV.aNumber / aTwo;
+ auto nWide = static_cast<sal_Int32>(static_cast<tools::Long>(aWide));
+ if (nWide < 0)
+ return false;
+ const Fraction aPointFive(1, 2);
+ auto aNarrow = (rLV.aNumber - Fraction(nWide, 1) * aTwo) / aPointFive;
+ auto nNarrow = static_cast<sal_Int32>(static_cast<tools::Long>(aNarrow));
+ if (nNarrow < 0)
+ return false;
+ *pWide = nWide;
+ *pNarrow = nNarrow;
+ return true;
+}
+}
+
+void SmXMLSpaceContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ // There is no syntax in Math to specify blank nodes of arbitrary size yet.
+ MathMLAttributeLengthValue aLV;
+ sal_Int32 nWide = 0, nNarrow = 0;
+
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ OUString sValue = aIter.toString();
+ switch (aIter.getToken())
+ {
+ case XML_WIDTH:
+ if (!ParseMathMLAttributeLengthValue(o3tl::trim(sValue), aLV)
+ || !lcl_CountBlanks(aLV, &nWide, &nNarrow))
+ SAL_WARN("starmath", "ignore mspace's width: " << sValue);
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+ SmToken aToken;
+ aToken.eType = TBLANK;
+ aToken.cMathChar = u"";
+ aToken.nGroup = TG::Blank;
+ aToken.nLevel = 5;
+ std::unique_ptr<SmBlankNode> pBlank(new SmBlankNode(aToken));
+ if (nWide > 0)
+ pBlank->IncreaseBy(aToken, nWide);
+ if (nNarrow > 0)
+ {
+ aToken.eType = TSBLANK;
+ pBlank->IncreaseBy(aToken, nNarrow);
+ }
+ GetSmImport().GetNodeStack().push_front(std::move(pBlank));
+}
+
+namespace
+{
+class SmXMLSubContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType, SmSubSup aSubSup);
+
+public:
+ SmXMLSubContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB); }
+};
+}
+
+void SmXMLSubContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup eSubSup)
+{
+ /*The <msub> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[eSubSup + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLSupContext_Impl : public SmXMLSubContext_Impl
+{
+public:
+ SmXMLSupContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUP, RSUP); }
+};
+
+class SmXMLSubSupContext_Impl : public SmXMLRowContext_Impl
+{
+protected:
+ void GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup);
+
+public:
+ SmXMLSubSupContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TRSUB, RSUB, RSUP); }
+};
+}
+
+void SmXMLSubSupContext_Impl::GenericEndElement(SmTokenType eType, SmSubSup aSub, SmSubSup aSup)
+{
+ /*The <msub> element requires exactly 3 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 3;
+ OSL_ENSURE(bNodeCheck, "SubSup has not three arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = eType;
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes;
+ aSubNodes.resize(1 + SUBSUP_NUM_ENTRIES);
+ for (size_t i = 1; i < aSubNodes.size(); i++)
+ aSubNodes[i] = nullptr;
+
+ aSubNodes[aSup + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[aSub + 1] = popOrZero(rNodeStack).release();
+ aSubNodes[0] = popOrZero(rNodeStack).release();
+ pNode->SetSubNodes(std::move(aSubNodes));
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLUnderContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLUnderContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ , nAttrCount(0)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void HandleAccent();
+};
+}
+
+void SmXMLUnderContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList& rAttribList
+ = sax_fastparser::castToFastAttributeList(xAttrList);
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+void SmXMLUnderContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ /*Just one special case for the underline thing*/
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pTest = popOrZero(rNodeStack);
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TUNDERLINE;
+
+ std::unique_ptr<SmNode> pFirst;
+ std::unique_ptr<SmStructureNode> pNode(new SmAttributeNode(aToken));
+ if ((pTest->GetToken().cMathChar[0] & 0x0FFF) == 0x0332)
+ {
+ pFirst.reset(new SmRectangleNode(aToken));
+ }
+ else
+ pFirst = std::move(pTest);
+
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+}
+
+void SmXMLUnderContext_Impl::endFastElement(sal_Int32)
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUB, CSUB);
+ else
+ HandleAccent();
+}
+
+namespace
+{
+class SmXMLOverContext_Impl : public SmXMLSubContext_Impl
+{
+protected:
+ sal_Int16 nAttrCount;
+
+public:
+ SmXMLOverContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubContext_Impl(rImport)
+ , nAttrCount(0)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void HandleAccent();
+};
+}
+
+void SmXMLOverContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ sax_fastparser::FastAttributeList& rAttribList
+ = sax_fastparser::castToFastAttributeList(xAttrList);
+ nAttrCount = rAttribList.getFastAttributeTokens().size();
+}
+
+void SmXMLOverContext_Impl::endFastElement(sal_Int32)
+{
+ if (!nAttrCount)
+ GenericEndElement(TCSUP, CSUP);
+ else
+ HandleAccent();
+}
+
+void SmXMLOverContext_Impl::HandleAccent()
+{
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Sub has not two arguments");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TACUTE;
+
+ std::unique_ptr<SmAttributeNode> pNode(new SmAttributeNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ pNode->SetSubNodes(std::move(pFirst), std::move(pSecond));
+ pNode->SetScaleMode(SmScaleMode::Width);
+ rNodeStack.push_front(std::move(pNode));
+}
+
+namespace
+{
+class SmXMLUnderOverContext_Impl : public SmXMLSubSupContext_Impl
+{
+public:
+ SmXMLUnderOverContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubSupContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32) override { GenericEndElement(TCSUB, CSUB, CSUP); }
+};
+
+class SmXMLMultiScriptsContext_Impl : public SmXMLSubSupContext_Impl
+{
+ bool bHasPrescripts;
+
+ void ProcessSubSupPairs(bool bIsPrescript);
+
+public:
+ SmXMLMultiScriptsContext_Impl(SmXMLImport& rImport)
+ : SmXMLSubSupContext_Impl(rImport)
+ , bHasPrescripts(false)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLNoneContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLNoneContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+}
+
+void SmXMLNoneContext_Impl::endFastElement(sal_Int32)
+{
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.aText.clear();
+ aToken.nLevel = 5;
+ aToken.eType = TIDENT;
+ GetSmImport().GetNodeStack().push_front(std::make_unique<SmTextNode>(aToken, FNT_VARIABLE));
+}
+
+namespace
+{
+class SmXMLPrescriptsContext_Impl : public SmXMLImportContext
+{
+public:
+ SmXMLPrescriptsContext_Impl(SmXMLImport& rImport)
+ : SmXMLImportContext(rImport)
+ {
+ }
+};
+
+class SmXMLTableRowContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableRowContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLTableContext_Impl : public SmXMLTableRowContext_Impl
+{
+public:
+ SmXMLTableContext_Impl(SmXMLImport& rImport)
+ : SmXMLTableRowContext_Impl(rImport)
+ {
+ }
+
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+ virtual uno::Reference<xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+};
+
+class SmXMLTableCellContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLTableCellContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+};
+
+class SmXMLAlignGroupContext_Impl : public SmXMLRowContext_Impl
+{
+public:
+ SmXMLAlignGroupContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ {
+ }
+
+ /*Don't do anything with alignment for now*/
+};
+
+class SmXMLActionContext_Impl : public SmXMLRowContext_Impl
+{
+ size_t mnSelection; // 1-based
+
+public:
+ SmXMLActionContext_Impl(SmXMLImport& rImport)
+ : SmXMLRowContext_Impl(rImport)
+ , mnSelection(1)
+ {
+ }
+
+ void SAL_CALL startFastElement(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList) override;
+ void SAL_CALL endFastElement(sal_Int32 nElement) override;
+};
+
+// NB: virtually inherit so we can multiply inherit properly
+// in SmXMLFlatDocContext_Impl
+class SmXMLOfficeContext_Impl : public virtual SvXMLImportContext
+{
+public:
+ SmXMLOfficeContext_Impl(SmXMLImport& rImport)
+ : SvXMLImportContext(rImport)
+ {
+ }
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLOfficeContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ if (nElement == XML_ELEMENT(OFFICE, XML_META))
+ {
+ SAL_WARN("starmath",
+ "XML_TOK_DOC_META: should not have come here, maybe document is invalid?");
+ }
+ else if (nElement == XML_ELEMENT(OFFICE, XML_SETTINGS))
+ {
+ return new XMLDocumentSettingsContext(GetImport());
+ }
+ return nullptr;
+}
+
+namespace
+{
+// context for flat file xml format
+class SmXMLFlatDocContext_Impl : public SmXMLOfficeContext_Impl, public SvXMLMetaDocumentContext
+{
+public:
+ SmXMLFlatDocContext_Impl(SmXMLImport& i_rImport,
+ const uno::Reference<document::XDocumentProperties>& i_xDocProps);
+
+ virtual css::uno::Reference<css::xml::sax::XFastContextHandler> SAL_CALL createFastChildContext(
+ sal_Int32 nElement,
+ const css::uno::Reference<css::xml::sax::XFastAttributeList>& xAttrList) override;
+};
+}
+
+SmXMLFlatDocContext_Impl::SmXMLFlatDocContext_Impl(
+ SmXMLImport& i_rImport, const uno::Reference<document::XDocumentProperties>& i_xDocProps)
+ : SvXMLImportContext(i_rImport)
+ , SmXMLOfficeContext_Impl(i_rImport)
+ , SvXMLMetaDocumentContext(i_rImport, i_xDocProps)
+{
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+ SAL_CALL SmXMLFlatDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ // behave like meta base class iff we encounter office:meta
+ if (nElement == XML_ELEMENT(OFFICE, XML_META))
+ {
+ return SvXMLMetaDocumentContext::createFastChildContext(nElement, xAttrList);
+ }
+ else
+ {
+ return SmXMLOfficeContext_Impl::createFastChildContext(nElement, xAttrList);
+ }
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLDocContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ //Consider semantics a dummy except for any starmath annotations
+ case XML_ELEMENT(MATH, XML_SEMANTICS):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ /*General Layout Schemata*/
+ case XML_ELEMENT(MATH, XML_MROW):
+ xContext = new SmXMLRowContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MENCLOSE):
+ xContext = new SmXMLEncloseContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFRAC):
+ xContext = new SmXMLFracContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSQRT):
+ xContext = new SmXMLSqrtContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MROOT):
+ xContext = new SmXMLRootContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSTYLE):
+ xContext = new SmXMLStyleContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MERROR):
+ xContext = new SmXMLErrorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPADDED):
+ xContext = new SmXMLPaddedContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MPHANTOM):
+ xContext = new SmXMLPhantomContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MFENCED):
+ xContext = new SmXMLFencedContext_Impl(GetSmImport());
+ break;
+ /*Script and Limit Schemata*/
+ case XML_ELEMENT(MATH, XML_MSUB):
+ xContext = new SmXMLSubContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUP):
+ xContext = new SmXMLSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSUBSUP):
+ xContext = new SmXMLSubSupContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDER):
+ xContext = new SmXMLUnderContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MOVER):
+ xContext = new SmXMLOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MUNDEROVER):
+ xContext = new SmXMLUnderOverContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MMULTISCRIPTS):
+ xContext = new SmXMLMultiScriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTABLE):
+ xContext = new SmXMLTableContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MACTION):
+ xContext = new SmXMLActionContext_Impl(GetSmImport());
+ break;
+ default:
+ /*Basically there's an implicit mrow around certain bare
+ *elements, use a RowContext to see if this is one of
+ *those ones*/
+ rtl::Reference<SmXMLRowContext_Impl> aTempContext(
+ new SmXMLRowContext_Impl(GetSmImport()));
+
+ xContext = aTempContext->StrictCreateChildContext(nElement);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLDocContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ std::unique_ptr<SmNode> pContextNode = popOrZero(rNodeStack);
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmLineNode(aDummy));
+ pSNode->SetSubNodes(std::move(pContextNode), nullptr);
+ rNodeStack.push_front(std::move(pSNode));
+
+ SmNodeArray LineArray;
+ auto n = rNodeStack.size();
+ LineArray.resize(n);
+ for (size_t j = 0; j < n; j++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ LineArray[n - (j + 1)] = pNode.release();
+ }
+ std::unique_ptr<SmStructureNode> pSNode2(new SmTableNode(aDummy));
+ pSNode2->SetSubNodes(std::move(LineArray));
+ rNodeStack.push_front(std::move(pSNode2));
+}
+
+void SmXMLFracContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ const bool bNodeCheck = rNodeStack.size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Fraction (mfrac) tag is missing component");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TFRAC;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBinVerNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRectangleNode(aToken));
+ std::unique_ptr<SmNode> pSecond = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pFirst = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pFirst), std::move(pOper), std::move(pSecond));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRootContext_Impl::endFastElement(sal_Int32)
+{
+ /*The <mroot> element requires exactly 2 arguments.*/
+ const bool bNodeCheck = GetSmImport().GetNodeStack().size() - nElementCount == 2;
+ OSL_ENSURE(bNodeCheck, "Root tag is missing component");
+ if (!bNodeCheck)
+ return;
+
+ SmToken aToken;
+ aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
+ aToken.eType = TNROOT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ std::unique_ptr<SmNode> pIndex = popOrZero(rNodeStack);
+ std::unique_ptr<SmNode> pBase = popOrZero(rNodeStack);
+ pSNode->SetSubNodes(std::move(pIndex), std::move(pOper), std::move(pBase));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLSqrtContext_Impl::endFastElement(sal_Int32 nElement)
+{
+ /*
+ <msqrt> accepts any number of arguments; if this number is not 1, its
+ contents are treated as a single "inferred <mrow>" containing its
+ arguments
+ */
+ if (GetSmImport().GetNodeStack().size() - nElementCount != 1)
+ SmXMLRowContext_Impl::endFastElement(nElement);
+
+ SmToken aToken;
+ aToken.setChar(MS_SQRT); //Temporary: alert, based on StarSymbol font
+ aToken.eType = TSQRT;
+ std::unique_ptr<SmStructureNode> pSNode(new SmRootNode(aToken));
+ std::unique_ptr<SmNode> pOper(new SmRootSymbolNode(aToken));
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ pSNode->SetSubNodes(nullptr, std::move(pOper), popOrZero(rNodeStack));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+void SmXMLRowContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeArray aRelationArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() > nElementCount)
+ {
+ auto nSize = rNodeStack.size() - nElementCount;
+
+ aRelationArray.resize(nSize);
+ for (auto j = nSize; j > 0; j--)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aRelationArray[j - 1] = pNode.release();
+ }
+
+ //If the first or last element is an operator with stretchyness
+ //set then we must create a brace node here from those elements,
+ //removing the stretchness from the operators and applying it to
+ //ourselves, and creating the appropriate dummy StarMath none bracket
+ //to balance the arrangement
+ if (((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ || ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math)))
+ {
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.nLevel = 5;
+
+ int nLeft = 0, nRight = 0;
+ if ((aRelationArray[0]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[0]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[0]->GetToken();
+ nLeft = 1;
+ }
+ else
+ aToken.cMathChar = u"";
+
+ aToken.eType = TLPARENT;
+ std::unique_ptr<SmNode> pLeft(new SmMathSymbolNode(aToken));
+
+ if ((aRelationArray[nSize - 1]->GetScaleMode() == SmScaleMode::Height)
+ && (aRelationArray[nSize - 1]->GetType() == SmNodeType::Math))
+ {
+ aToken = aRelationArray[nSize - 1]->GetToken();
+ nRight = 1;
+ }
+ else
+ aToken.cMathChar = u"";
+
+ aToken.eType = TRPARENT;
+ std::unique_ptr<SmNode> pRight(new SmMathSymbolNode(aToken));
+
+ SmNodeArray aRelationArray2;
+
+ //!! nSize-nLeft-nRight may be < 0 !!
+ int nRelArrSize = nSize - nLeft - nRight;
+ if (nRelArrSize > 0)
+ {
+ aRelationArray2.resize(nRelArrSize);
+ for (int i = 0; i < nRelArrSize; i++)
+ {
+ aRelationArray2[i] = aRelationArray[i + nLeft];
+ aRelationArray[i + nLeft] = nullptr;
+ }
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmBraceNode(aToken));
+ std::unique_ptr<SmStructureNode> pBody(new SmExpressionNode(aDummy));
+ pBody->SetSubNodes(std::move(aRelationArray2));
+
+ pSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ pSNode->SetScaleMode(SmScaleMode::Height);
+ rNodeStack.push_front(std::move(pSNode));
+
+ for (auto a : aRelationArray)
+ delete a;
+
+ return;
+ }
+ }
+ else
+ {
+ // The elements msqrt, mstyle, merror, menclose, mpadded, mphantom, mtd, and math
+ // treat their content as a single inferred mrow in case their content is empty.
+ // Here an empty group {} is used to catch those cases and transform them without error
+ // to StarMath.
+ aRelationArray.resize(2);
+ SmToken aToken;
+ aToken.setChar(MS_LBRACE);
+ aToken.nLevel = 5;
+ aToken.eType = TLGROUP;
+ aToken.nGroup = TG::NONE;
+ aToken.aText = "{";
+ aRelationArray[0] = new SmLineNode(aToken);
+
+ aToken.setChar(MS_RBRACE);
+ aToken.nLevel = 0;
+ aToken.eType = TRGROUP;
+ aToken.nGroup = TG::NONE;
+ aToken.aText = "}";
+ aRelationArray[1] = new SmLineNode(aToken);
+ }
+
+ SmToken aDummy;
+ std::unique_ptr<SmStructureNode> pSNode(new SmExpressionNode(aDummy));
+ pSNode->SetSubNodes(std::move(aRelationArray));
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference<xml::sax::XFastContextHandler>
+SmXMLRowContext_Impl::StrictCreateChildContext(sal_Int32 nElement)
+{
+ uno::Reference<xml::sax::XFastContextHandler> pContext;
+
+ switch (nElement)
+ {
+ /*Note that these should accept malignmark subelements, but do not*/
+ case XML_ELEMENT(MATH, XML_MN):
+ pContext = new SmXMLNumberContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MI):
+ pContext = new SmXMLIdentifierContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MO):
+ pContext = new SmXMLOperatorContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MTEXT):
+ pContext = new SmXMLTextContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MSPACE):
+ pContext = new SmXMLSpaceContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_MS):
+ pContext = new SmXMLStringContext_Impl(GetSmImport());
+ break;
+
+ /*Note: The maligngroup should only be seen when the row
+ * (or descendants) are in a table*/
+ case XML_ELEMENT(MATH, XML_MALIGNGROUP):
+ pContext = new SmXMLAlignGroupContext_Impl(GetSmImport());
+ break;
+
+ case XML_ELEMENT(MATH, XML_ANNOTATION):
+ pContext = new SmXMLAnnotationContext_Impl(GetSmImport());
+ break;
+
+ default:
+ break;
+ }
+ return pContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext = StrictCreateChildContext(nElement);
+
+ if (!xContext)
+ {
+ //Hmm, unrecognized for this level, check to see if it's
+ //an element that can have an implicit schema around it
+ xContext = SmXMLDocContext_Impl::createFastChildContext(nElement, xAttrList);
+ }
+ return xContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLMultiScriptsContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MPRESCRIPTS):
+ bHasPrescripts = true;
+ ProcessSubSupPairs(false);
+ xContext = new SmXMLPrescriptsContext_Impl(GetSmImport());
+ break;
+ case XML_ELEMENT(MATH, XML_NONE):
+ xContext = new SmXMLNoneContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::ProcessSubSupPairs(bool bIsPrescript)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+
+ if (rNodeStack.size() <= nElementCount)
+ return;
+
+ auto nCount = rNodeStack.size() - nElementCount - 1;
+ if (nCount == 0)
+ return;
+
+ if (nCount % 2 == 0)
+ {
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = bIsPrescript ? TLSUB : TRSUB;
+
+ SmNodeStack aReverseStack;
+ for (size_t i = 0; i < nCount + 1; i++)
+ {
+ auto pNode = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ aReverseStack.push_front(std::move(pNode));
+ }
+
+ SmSubSup eSub = bIsPrescript ? LSUB : RSUB;
+ SmSubSup eSup = bIsPrescript ? LSUP : RSUP;
+
+ for (size_t i = 0; i < nCount; i += 2)
+ {
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(aToken));
+
+ // initialize subnodes array
+ SmNodeArray aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+
+ /*On each loop the base and its sub sup pair becomes the
+ base for the next loop to which the next sub sup pair is
+ attached, i.e. wheels within wheels*/
+ aSubNodes[0] = popOrZero(aReverseStack).release();
+
+ std::unique_ptr<SmNode> pScriptNode = popOrZero(aReverseStack);
+
+ if (pScriptNode
+ && ((pScriptNode->GetToken().eType != TIDENT)
+ || (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSub + 1] = pScriptNode.release();
+ pScriptNode = popOrZero(aReverseStack);
+ if (pScriptNode
+ && ((pScriptNode->GetToken().eType != TIDENT)
+ || (!pScriptNode->GetToken().aText.isEmpty())))
+ aSubNodes[eSup + 1] = pScriptNode.release();
+
+ pNode->SetSubNodes(std::move(aSubNodes));
+ aReverseStack.push_front(std::move(pNode));
+ }
+ assert(!aReverseStack.empty());
+ auto pNode = std::move(aReverseStack.front());
+ aReverseStack.pop_front();
+ rNodeStack.push_front(std::move(pNode));
+ }
+ else
+ {
+ // Ignore odd number of elements.
+ for (size_t i = 0; i < nCount; i++)
+ {
+ rNodeStack.pop_front();
+ }
+ }
+}
+
+void SmXMLTableContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeArray aExpressionArray;
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ SmNodeStack aReverseStack;
+ aExpressionArray.resize(rNodeStack.size() - nElementCount);
+
+ size_t nRows = rNodeStack.size() - nElementCount;
+ size_t nCols = 0;
+
+ for (size_t i = nRows; i > 0; --i)
+ {
+ SmNode* pArray = rNodeStack.front().release();
+ rNodeStack.pop_front();
+ if (pArray->GetNumSubNodes() == 0)
+ {
+ //This is a little tricky, it is possible that there was
+ //be elements that were not inside a <mtd> pair, in which
+ //case they will not be in a row, i.e. they will not have
+ //SubNodes, so we have to wait until here before we can
+ //resolve the situation. Implicit surrounding tags are
+ //surprisingly difficult to get right within this
+ //architecture
+
+ SmNodeArray aRelationArray;
+ aRelationArray.resize(1);
+ aRelationArray[0] = pArray;
+ SmToken aDummy;
+ SmExpressionNode* pExprNode = new SmExpressionNode(aDummy);
+ pExprNode->SetSubNodes(std::move(aRelationArray));
+ pArray = pExprNode;
+ }
+
+ nCols = std::max(nCols, pArray->GetNumSubNodes());
+ aReverseStack.push_front(std::unique_ptr<SmNode>(pArray));
+ }
+ if (nCols > SAL_MAX_UINT16)
+ throw std::range_error("column limit");
+ if (nRows > SAL_MAX_UINT16)
+ throw std::range_error("row limit");
+ aExpressionArray.resize(nCols * nRows);
+ size_t j = 0;
+ for (auto& elem : aReverseStack)
+ {
+ std::unique_ptr<SmStructureNode> xArray(static_cast<SmStructureNode*>(elem.release()));
+ for (size_t i = 0; i < xArray->GetNumSubNodes(); ++i)
+ aExpressionArray[j++] = xArray->GetSubNode(i);
+ xArray->ClearSubNodes();
+ }
+ aReverseStack.clear();
+
+ SmToken aToken;
+ aToken.cMathChar = u"";
+ aToken.eType = TMATRIX;
+ std::unique_ptr<SmMatrixNode> pSNode(new SmMatrixNode(aToken));
+ pSNode->SetSubNodes(std::move(aExpressionArray));
+ pSNode->SetRowCol(nRows, nCols);
+ rNodeStack.push_front(std::move(pSNode));
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLTableRowContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTD):
+ xContext = new SmXMLTableCellContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+uno::Reference<xml::sax::XFastContextHandler> SmXMLTableContext_Impl::createFastChildContext(
+ sal_Int32 nElement, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ uno::Reference<xml::sax::XFastContextHandler> xContext;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(MATH, XML_MTR):
+ xContext = new SmXMLTableRowContext_Impl(GetSmImport());
+ break;
+ default:
+ xContext = SmXMLTableRowContext_Impl::createFastChildContext(nElement, xAttrList);
+ break;
+ }
+ return xContext;
+}
+
+void SmXMLMultiScriptsContext_Impl::endFastElement(sal_Int32)
+{
+ ProcessSubSupPairs(bHasPrescripts);
+}
+
+void SmXMLActionContext_Impl::startFastElement(
+ sal_Int32 /*nElement*/, const uno::Reference<xml::sax::XFastAttributeList>& xAttrList)
+{
+ for (auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList))
+ {
+ switch (aIter.getToken())
+ {
+ case XML_SELECTION:
+ {
+ sal_Int32 n = aIter.toInt32();
+ if (n > 0)
+ mnSelection = static_cast<size_t>(n);
+ }
+ break;
+ default:
+ XMLOFF_WARN_UNKNOWN("starmath", aIter);
+ break;
+ }
+ }
+}
+
+void SmXMLActionContext_Impl::endFastElement(sal_Int32)
+{
+ SmNodeStack& rNodeStack = GetSmImport().GetNodeStack();
+ auto nSize = rNodeStack.size();
+ if (nSize <= nElementCount)
+ {
+ // not compliant to maction's specification, e.g., no subexpressions
+ return;
+ }
+ assert(mnSelection > 0);
+ if (nSize < nElementCount + mnSelection)
+ {
+ // No selected subexpression exists, which is a MathML error;
+ // fallback to selecting the first
+ mnSelection = 1;
+ }
+ assert(nSize >= nElementCount + mnSelection);
+ for (auto i = nSize - (nElementCount + mnSelection); i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ auto pSelected = std::move(rNodeStack.front());
+ rNodeStack.pop_front();
+ for (auto i = rNodeStack.size() - nElementCount; i > 0; i--)
+ {
+ rNodeStack.pop_front();
+ }
+ rNodeStack.push_front(std::move(pSelected));
+}
+
+SvXMLImportContext*
+SmXMLImport::CreateFastContext(sal_Int32 nElement,
+ const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/)
+{
+ SvXMLImportContext* pContext = nullptr;
+
+ switch (nElement)
+ {
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT):
+ case XML_ELEMENT(OFFICE, XML_DOCUMENT_META):
+ {
+ uno::Reference<document::XDocumentPropertiesSupplier> xDPS(GetModel(),
+ uno::UNO_QUERY_THROW);
+ pContext = ((nElement & TOKEN_MASK) == XML_DOCUMENT_META)
+ ? new SvXMLMetaDocumentContext(*this, xDPS->getDocumentProperties())
+ // flat OpenDocument file format -- this has not been tested...
+ : new SmXMLFlatDocContext_Impl(*this, xDPS->getDocumentProperties());
+ }
+ break;
+ default:
+ if (IsTokenInNamespace(nElement, XML_NAMESPACE_OFFICE))
+ pContext = new SmXMLOfficeContext_Impl(*this);
+ else
+ pContext = new SmXMLDocContext_Impl(*this);
+ }
+ return pContext;
+}
+
+SmXMLImport::~SmXMLImport() noexcept { cleanup(); }
+
+void SmXMLImport::SetViewSettings(const Sequence<PropertyValue>& aViewProps)
+{
+ uno::Reference<frame::XModel> xModel = GetModel();
+ if (!xModel.is())
+ return;
+
+ SmModel* pModel = comphelper::getFromUnoTunnel<SmModel>(xModel);
+
+ if (!pModel)
+ return;
+
+ SmDocShell* pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ if (!pDocShell)
+ return;
+
+ tools::Rectangle aRect(pDocShell->GetVisArea());
+
+ tools::Long nTmp = 0;
+
+ for (const PropertyValue& rValue : aViewProps)
+ {
+ if (rValue.Name == "ViewAreaTop")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosY(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaLeft")
+ {
+ rValue.Value >>= nTmp;
+ aRect.SaturatingSetPosX(nTmp);
+ }
+ else if (rValue.Name == "ViewAreaWidth")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setWidth(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ else if (rValue.Name == "ViewAreaHeight")
+ {
+ rValue.Value >>= nTmp;
+ Size aSize(aRect.GetSize());
+ aSize.setHeight(nTmp);
+ aRect.SaturatingSetSize(aSize);
+ }
+ }
+
+ pDocShell->SetVisArea(aRect);
+}
+
+void SmXMLImport::SetConfigurationSettings(const Sequence<PropertyValue>& aConfProps)
+{
+ uno::Reference<XPropertySet> xProps(GetModel(), UNO_QUERY);
+ if (!xProps.is())
+ return;
+
+ Reference<XPropertySetInfo> xInfo(xProps->getPropertySetInfo());
+ if (!xInfo.is())
+ return;
+
+ static const OUStringLiteral sFormula(u"Formula");
+ static const OUStringLiteral sBasicLibraries(u"BasicLibraries");
+ static const OUStringLiteral sDialogLibraries(u"DialogLibraries");
+ for (const PropertyValue& rValue : aConfProps)
+ {
+ if (rValue.Name != sFormula && rValue.Name != sBasicLibraries
+ && rValue.Name != sDialogLibraries)
+ {
+ try
+ {
+ if (xInfo->hasPropertyByName(rValue.Name))
+ xProps->setPropertyValue(rValue.Name, rValue.Value);
+ }
+ catch (const beans::PropertyVetoException&)
+ {
+ // dealing with read-only properties here. Nothing to do...
+ }
+ catch (const Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMML(SvStream& rStream)
+{
+ SmGlobals::ensure();
+
+ SfxObjectShellLock xDocSh(new SmDocShell(SfxModelFlags::EMBEDDED_OBJECT));
+ xDocSh->DoInitNew();
+ uno::Reference<frame::XModel> xModel(xDocSh->GetModel());
+
+ uno::Reference<beans::XPropertySet> xInfoSet;
+ uno::Reference<uno::XComponentContext> xContext(comphelper::getProcessComponentContext());
+ uno::Reference<io::XInputStream> xStream(new utl::OSeekableInputStreamWrapper(rStream));
+
+ //SetLoading hack because the document properties will be re-initted
+ //by the xml filter and during the init, while it's considered uninitialized,
+ //setting a property will inform the document it's modified, which attempts
+ //to update the properties, which throws cause the properties are uninitialized
+ xDocSh->SetLoading(SfxLoadedFlags::NONE);
+
+ ErrCode nRet = ERRCODE_SFX_DOLOADFAILED;
+
+ try
+ {
+ nRet = SmXMLImportWrapper::ReadThroughComponent(xStream, xModel, xContext, xInfoSet,
+ "com.sun.star.comp.Math.XMLImporter", false,
+ false);
+ }
+ catch (...)
+ {
+ }
+
+ xDocSh->SetLoading(SfxLoadedFlags::ALL);
+
+ xDocSh->DoClose();
+
+ return nRet != ERRCODE_NONE;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathml/starmathdatabase.cxx b/starmath/source/mathml/starmathdatabase.cxx
new file mode 100644
index 000000000..66bdf3f34
--- /dev/null
+++ b/starmath/source/mathml/starmathdatabase.cxx
@@ -0,0 +1,794 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <starmathdatabase.hxx>
+#include <types.hxx>
+
+SmToken starmathdatabase::Identify_SmXMLOperatorContext_Impl(sal_Unicode cChar, bool bIsStretchy)
+{
+ switch (cChar)
+ {
+ case MS_COPROD:
+ return SmToken(TCOPROD, MS_COPROD, "coprod", TG::Oper, 5);
+ case MS_IIINT:
+ return SmToken(TIIINT, MS_IIINT, "iiint", TG::Oper, 5);
+ case MS_IINT:
+ return SmToken(TIINT, MS_IINT, "iint", TG::Oper, 5);
+ case MS_INT:
+ if (bIsStretchy)
+ return SmToken(TINTD, MS_INT, "intd", TG::Oper, 5);
+ else
+ return SmToken(TINT, MS_INT, "int", TG::Oper, 5);
+ case MS_LINT:
+ return SmToken(TLINT, MS_LINT, "lint", TG::Oper, 5);
+ case MS_LLINT:
+ return SmToken(TLLINT, MS_LLINT, "llint", TG::Oper, 5);
+ case MS_LLLINT:
+ return SmToken(TLLLINT, MS_LLLINT, "lllint", TG::Oper, 5);
+ case MS_PROD:
+ return SmToken(TPROD, MS_PROD, "prod", TG::Oper, 5);
+ case MS_SUM:
+ return SmToken(TSUM, MS_SUM, "sum", TG::Oper, 5);
+ case MS_FACT:
+ return SmToken(TFACT, MS_FACT, "!", TG::UnOper, 5);
+ case MS_NEG:
+ return SmToken(TNEG, MS_NEG, "neg", TG::UnOper, 5);
+ case MS_OMINUS:
+ return SmToken(TOMINUS, MS_OMINUS, "ominus", TG::Sum, 0);
+ case MS_OPLUS:
+ return SmToken(TOPLUS, MS_OPLUS, "oplus", TG::Sum, 0);
+ case MS_UNION:
+ return SmToken(TUNION, MS_UNION, "union", TG::Sum, 0);
+ case MS_OR:
+ return SmToken(TOR, MS_OR, "|", TG::Sum, 5);
+ case MS_PLUSMINUS:
+ return SmToken(TPLUSMINUS, MS_PLUSMINUS, "+-", TG::Sum | TG::UnOper, 5);
+ case MS_MINUSPLUS:
+ return SmToken(TMINUSPLUS, MS_MINUSPLUS, "-+", TG::Sum | TG::UnOper, 5);
+ case 0xe083:
+ case MS_PLUS:
+ return SmToken(TPLUS, MS_PLUS, "+", TG::Sum | TG::UnOper, 5);
+ case MS_MINUS:
+ return SmToken(TMINUS, MS_MINUS, "-", TG::Sum | TG::UnOper, 5);
+ case 0x2022:
+ case MS_CDOT:
+ return SmToken(TCDOT, MS_CDOT, "cdot", TG::Product, 0);
+ case MS_DIV:
+ return SmToken(TDIV, MS_DIV, "div", TG::Product, 0);
+ case MS_TIMES:
+ return SmToken(TTIMES, MS_TIMES, "times", TG::Product, 0);
+ case MS_INTERSECT:
+ return SmToken(TINTERSECT, MS_INTERSECT, "intersection", TG::Product, 0);
+ case MS_ODIVIDE:
+ return SmToken(TODIVIDE, MS_ODIVIDE, "odivide", TG::Product, 0);
+ case MS_ODOT:
+ return SmToken(TODOT, MS_ODOT, "odot", TG::Product, 0);
+ case MS_OTIMES:
+ return SmToken(TOTIMES, MS_OTIMES, "otimes", TG::Product, 0);
+ case MS_AND:
+ return SmToken(TAND, MS_AND, "&", TG::Product, 0);
+ case MS_MULTIPLY:
+ return SmToken(TMULTIPLY, MS_MULTIPLY, "*", TG::Product, 0);
+ case MS_SLASH:
+ if (bIsStretchy)
+ return SmToken(TWIDESLASH, MS_SLASH, "wideslash", TG::Product, 0);
+ else
+ return SmToken(TSLASH, MS_SLASH, "slash", TG::Product, 0);
+ case MS_BACKSLASH:
+ if (bIsStretchy)
+ return SmToken(TWIDEBACKSLASH, MS_BACKSLASH, "widebslash", TG::Product, 0);
+ else
+ return SmToken(TBACKSLASH, MS_BACKSLASH, "bslash", TG::Product, 0);
+ case MS_DEF:
+ return SmToken(TDEF, MS_DEF, "def", TG::Relation, 0);
+ case MS_LINE:
+ return SmToken(TDIVIDES, MS_LINE, "divides", TG::Relation, 0);
+ case MS_EQUIV:
+ return SmToken(TEQUIV, MS_EQUIV, "equiv", TG::Relation, 0);
+ case MS_GE:
+ return SmToken(TGE, MS_GE, ">=", TG::Relation, 0);
+ case MS_GESLANT:
+ return SmToken(TGESLANT, MS_GESLANT, "geslant", TG::Relation, 0);
+ case MS_GG:
+ return SmToken(TGG, MS_GG, ">>", TG::Relation, 0);
+ case MS_GT:
+ return SmToken(TGT, MS_GT, ">", TG::Relation, 0);
+ case MS_IN:
+ return SmToken(TIN, MS_IN, "in", TG::Relation, 0);
+ case MS_LE:
+ return SmToken(TLE, MS_LE, "<=", TG::Relation, 0);
+ case MS_LESLANT:
+ return SmToken(TLESLANT, MS_LESLANT, "leslant", TG::Relation, 0);
+ case MS_LL:
+ return SmToken(TLL, MS_LL, "<<", TG::Relation, 0);
+ case MS_LT:
+ return SmToken(TLT, MS_LT, "<", TG::Relation, 0);
+ case MS_NDIVIDES:
+ return SmToken(TNDIVIDES, MS_NDIVIDES, "ndivides", TG::Relation, 0);
+ case MS_NEQ:
+ return SmToken(TNEQ, MS_NEQ, "<>", TG::Relation, 0);
+ case MS_NOTIN:
+ return SmToken(TNOTIN, MS_NOTIN, "notin", TG::Relation, 0);
+ case MS_NOTPRECEDES:
+ return SmToken(TNOTPRECEDES, MS_NOTPRECEDES, "nprec", TG::Relation, 0);
+ case MS_NSUBSET:
+ return SmToken(TNSUBSET, MS_NSUBSET, "nsubset", TG::Relation, 0);
+ case MS_NSUBSETEQ:
+ return SmToken(TNSUBSETEQ, MS_NSUBSETEQ, "nsubseteq", TG::Relation, 0);
+ case MS_NOTSUCCEEDS:
+ return SmToken(TNOTSUCCEEDS, MS_NOTSUCCEEDS, "nsucc", TG::Relation, 0);
+ case MS_NSUPSET:
+ return SmToken(TNSUPSET, MS_NSUPSET, "nsupset", TG::Relation, 0);
+ case MS_NSUPSETEQ:
+ return SmToken(TNSUPSETEQ, MS_NSUPSETEQ, "nsupseteq", TG::Relation, 0);
+ case MS_ORTHO:
+ return SmToken(TORTHO, MS_ORTHO, "ortho", TG::Relation, 0);
+ case MS_NI:
+ return SmToken(TNI, MS_NI, "owns", TG::Relation, 0);
+ case MS_DLINE:
+ return SmToken(TPARALLEL, MS_DLINE, "parallel", TG::Relation, 0);
+ case MS_PRECEDES:
+ return SmToken(TPRECEDES, MS_PRECEDES, "prec", TG::Relation, 0);
+ case MS_PRECEDESEQUAL:
+ return SmToken(TPRECEDESEQUAL, MS_PRECEDESEQUAL, "preccurlyeq", TG::Relation, 0);
+ case MS_PRECEDESEQUIV:
+ return SmToken(TPRECEDESEQUIV, MS_PRECEDESEQUIV, "precsim", TG::Relation, 0);
+ case MS_PROP:
+ return SmToken(TPROP, MS_PROP, "prop", TG::Relation, 0);
+ case MS_SIM:
+ return SmToken(TSIM, MS_SIM, "sim", TG::Relation, 0);
+ case 0x2245:
+ case MS_SIMEQ:
+ return SmToken(TSIMEQ, MS_SIMEQ, "simeq", TG::Relation, 0);
+ case MS_SUBSET:
+ return SmToken(TSUBSET, MS_SUBSET, "subset", TG::Relation, 0);
+ case MS_SUBSETEQ:
+ return SmToken(TSUBSETEQ, MS_SUBSETEQ, "subseteq", TG::Relation, 0);
+ case MS_SUCCEEDS:
+ return SmToken(TSUCCEEDS, MS_SUCCEEDS, "succ", TG::Relation, 0);
+ case MS_SUCCEEDSEQUAL:
+ return SmToken(TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, "succcurlyeq", TG::Relation, 0);
+ case MS_SUCCEEDSEQUIV:
+ return SmToken(TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, "succsim", TG::Relation, 0);
+ case MS_SUPSET:
+ return SmToken(TSUPSET, MS_SUPSET, "supset", TG::Relation, 0);
+ case MS_SUPSETEQ:
+ return SmToken(TSUPSETEQ, MS_SUPSETEQ, "supseteq", TG::Relation, 0);
+ case MS_RIGHTARROW:
+ return SmToken(TTOWARD, MS_RIGHTARROW, "toward", TG::Relation, 0);
+ case MS_TRANSL:
+ return SmToken(TTRANSL, MS_TRANSL, "transl", TG::Relation, 0);
+ case MS_TRANSR:
+ return SmToken(TTRANSR, MS_TRANSR, "transr", TG::Relation, 0);
+ case MS_ASSIGN:
+ return SmToken(TASSIGN, MS_ASSIGN, "=", TG::Relation, 0);
+ case MS_LANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LMATHANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LBRACE:
+ return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ case MS_LCEIL:
+ return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5);
+ case MS_LFLOOR:
+ return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5);
+ case MS_LDBRACKET:
+ return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5);
+ case MS_LBRACKET:
+ return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ case MS_LPARENT:
+ return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ case MS_RANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RMATHANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RBRACE:
+ return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ case MS_RCEIL:
+ return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5);
+ case MS_RFLOOR:
+ return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5);
+ case MS_RDBRACKET:
+ return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5);
+ case MS_RBRACKET:
+ return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ case MS_RPARENT:
+ return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ case MS_NONE:
+ return SmToken(TNONE, MS_NONE, "none", TG::RBrace | TG::LBrace, 5);
+ default:
+ return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16);
+ }
+}
+
+SmToken starmathdatabase::Identify_Prefix_SmXMLOperatorContext_Impl(sal_Unicode cChar)
+{
+ switch (cChar)
+ {
+ case MS_VERTLINE:
+ return SmToken(TLLINE, MS_VERTLINE, "lline", TG::LBrace, 5);
+ case MS_DVERTLINE:
+ return SmToken(TLDLINE, MS_DVERTLINE, "ldline", TG::LBrace, 5);
+ case MS_LANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LMATHANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LBRACE:
+ return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ case MS_LCEIL:
+ return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5);
+ case MS_LFLOOR:
+ return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5);
+ case MS_LDBRACKET:
+ return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5);
+ case MS_LBRACKET:
+ return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ case MS_LPARENT:
+ return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ case MS_RANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RMATHANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RBRACE:
+ return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ case MS_RCEIL:
+ return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5);
+ case MS_RFLOOR:
+ return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5);
+ case MS_RDBRACKET:
+ return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5);
+ case MS_RBRACKET:
+ return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ case MS_RPARENT:
+ return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ case MS_NONE:
+ return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5);
+ default:
+ return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16);
+ }
+}
+
+SmToken starmathdatabase::Identify_Postfix_SmXMLOperatorContext_Impl(sal_Unicode cChar)
+{
+ switch (cChar)
+ {
+ case MS_VERTLINE:
+ return SmToken(TRLINE, MS_VERTLINE, "rline", TG::RBrace, 5);
+ case MS_DVERTLINE:
+ return SmToken(TRDLINE, MS_DVERTLINE, "rdline", TG::RBrace, 5);
+ case MS_LANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LMATHANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LBRACE:
+ return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ case MS_LCEIL:
+ return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5);
+ case MS_LFLOOR:
+ return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5);
+ case MS_LDBRACKET:
+ return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5);
+ case MS_LBRACKET:
+ return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ case MS_LPARENT:
+ return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ case MS_RANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RMATHANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RBRACE:
+ return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ case MS_RCEIL:
+ return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5);
+ case MS_RFLOOR:
+ return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5);
+ case MS_RDBRACKET:
+ return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5);
+ case MS_RBRACKET:
+ return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ case MS_RPARENT:
+ return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ case MS_NONE:
+ return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5);
+ default:
+ return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16);
+ }
+}
+
+SmToken starmathdatabase::Identify_PrefixPostfix_SmXMLOperatorContext_Impl(sal_Unicode cChar)
+{
+ switch (cChar)
+ {
+ case MS_VERTLINE:
+ return SmToken(TLRLINE, MS_VERTLINE, "lrline", TG::LBrace | TG::RBrace, 5);
+ case MS_DVERTLINE:
+ return SmToken(TLRDLINE, MS_DVERTLINE, "lrdline", TG::LBrace | TG::RBrace, 5);
+ case MS_LANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LMATHANGLE:
+ return SmToken(TLANGLE, MS_LMATHANGLE, "langle", TG::LBrace, 5);
+ case MS_LBRACE:
+ return SmToken(TLBRACE, MS_LBRACE, "lbrace", TG::LBrace, 5);
+ case MS_LCEIL:
+ return SmToken(TLCEIL, MS_LCEIL, "lceil", TG::LBrace, 5);
+ case MS_LFLOOR:
+ return SmToken(TLFLOOR, MS_LFLOOR, "lfloor", TG::LBrace, 5);
+ case MS_LDBRACKET:
+ return SmToken(TLDBRACKET, MS_LDBRACKET, "ldbracket", TG::LBrace, 5);
+ case MS_LBRACKET:
+ return SmToken(TLBRACKET, MS_LBRACKET, "[", TG::LBrace, 5);
+ case MS_LPARENT:
+ return SmToken(TLPARENT, MS_LPARENT, "(", TG::LBrace, 5);
+ case MS_RANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RMATHANGLE:
+ return SmToken(TRANGLE, MS_RMATHANGLE, "rangle", TG::RBrace, 5);
+ case MS_RBRACE:
+ return SmToken(TRBRACE, MS_RBRACE, "rbrace", TG::RBrace, 5);
+ case MS_RCEIL:
+ return SmToken(TRCEIL, MS_RCEIL, "rceil", TG::RBrace, 5);
+ case MS_RFLOOR:
+ return SmToken(TRFLOOR, MS_RFLOOR, "rfloor", TG::RBrace, 5);
+ case MS_RDBRACKET:
+ return SmToken(TRDBRACKET, MS_RDBRACKET, "rdbracket", TG::RBrace, 5);
+ case MS_RBRACKET:
+ return SmToken(TRBRACKET, MS_RBRACKET, "]", TG::RBrace, 5);
+ case MS_RPARENT:
+ return SmToken(TRPARENT, MS_RPARENT, ")", TG::RBrace, 5);
+ case MS_NONE:
+ return SmToken(TNONE, MS_NONE, "none", TG::LBrace | TG::RBrace, 5);
+ default:
+ return SmToken(TERROR, MS_NONE, "", TG::NONE, SAL_MAX_UINT16);
+ }
+}
+
+const SmColorTokenTableEntry starmathdatabase::aColorTokenTableParse[]
+ = { { "aliceblue", THTMLCOL, COL_SM_ALICEBLUE },
+ { "antiquewhite", THTMLCOL, COL_SM_ANTIQUEWHITE },
+ { "aqua", TMATHMLCOL, COL_SM_AQUA },
+ { "aquamarine", THTMLCOL, COL_SM_AQUAMARINE },
+ { "azure", THTMLCOL, COL_SM_AZURE },
+ { "beige", THTMLCOL, COL_SM_BEIGE },
+ { "bisque", THTMLCOL, COL_SM_BISQUE },
+ { "black", TMATHMLCOL, COL_SM_BLACK },
+ { "blanchedalmond", THTMLCOL, COL_SM_BLANCHEDALMOND },
+ { "blue", TMATHMLCOL, COL_SM_BLUE },
+ { "blueviolet", THTMLCOL, COL_SM_BLUEVIOLET },
+ { "brown", THTMLCOL, COL_SM_BROWN },
+ { "burlywood", THTMLCOL, COL_SM_BURLYWOOD },
+ { "cadetblue", THTMLCOL, COL_SM_CADETBLUE },
+ { "chartreuse", THTMLCOL, COL_SM_CHARTREUSE },
+ { "chocolate", THTMLCOL, COL_SM_CHOCOLATE },
+ { "coral", THTMLCOL, COL_SM_CORAL },
+ { "cornflowerblue", THTMLCOL, COL_SM_CORNFLOWERBLUE },
+ { "cornsilk", THTMLCOL, COL_SM_CORNSILK },
+ { "crimson", THTMLCOL, COL_SM_CRIMSON },
+ { "cyan", TMATHMLCOL, COL_SM_CYAN },
+ { "darkblue", THTMLCOL, COL_SM_DARKBLUE },
+ { "darkcyan", THTMLCOL, COL_SM_DARKCYAN },
+ { "darkgoldenrod", THTMLCOL, COL_SM_DARKGOLDENROD },
+ { "darkgray", THTMLCOL, COL_SM_DARKGRAY },
+ { "darkgreen", THTMLCOL, COL_SM_DARKGREEN },
+ { "darkgrey", THTMLCOL, COL_SM_DARKGREY },
+ { "darkkhaki", THTMLCOL, COL_SM_DARKKHAKI },
+ { "darkmagenta", THTMLCOL, COL_SM_DARKMAGENTA },
+ { "darkolivegreen", THTMLCOL, COL_SM_DARKOLIVEGREEN },
+ { "darkorange", THTMLCOL, COL_SM_DARKORANGE },
+ { "darkorchid", THTMLCOL, COL_SM_DARKORCHID },
+ { "darkred", THTMLCOL, COL_SM_DARKRED },
+ { "darksalmon", THTMLCOL, COL_SM_DARKSALMON },
+ { "darkseagreen", THTMLCOL, COL_SM_DARKSEAGREEN },
+ { "darkslateblue", THTMLCOL, COL_SM_DARKSLATEBLUE },
+ { "darkslategray", THTMLCOL, COL_SM_DARKSLATEGRAY },
+ { "darkslategrey", THTMLCOL, COL_SM_DARKSLATEGREY },
+ { "darkturquoise", THTMLCOL, COL_SM_DARKTURQUOISE },
+ { "darkviolet", THTMLCOL, COL_SM_DARKVIOLET },
+ { "debian", TICONICCOL, COL_SM_DEBIAN_MAGENTA },
+ { "deeppink", THTMLCOL, COL_SM_DEEPPINK },
+ { "deepskyblue", THTMLCOL, COL_SM_DEEPSKYBLUE },
+ { "dimgray", THTMLCOL, COL_SM_DIMGRAY },
+ { "dimgrey", THTMLCOL, COL_SM_DIMGREY },
+ { "dodgerblue", THTMLCOL, COL_SM_DODGERBLUE },
+ { "dvip", TDVIPSNAMESCOL, COL_SM_BLACK },
+ { "firebrick", THTMLCOL, COL_SM_FIREBRICK },
+ { "floralwhite", THTMLCOL, COL_SM_FLORALWHITE },
+ { "forestgreen", THTMLCOL, COL_SM_FORESTGREEN },
+ { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA },
+ { "gainsboro", THTMLCOL, COL_SM_GAINSBORO },
+ { "ghostwhite", THTMLCOL, COL_SM_GHOSTWHITE },
+ { "gold", THTMLCOL, COL_SM_GOLD },
+ { "goldenrod", THTMLCOL, COL_SM_GOLDENROD },
+ { "gray", TMATHMLCOL, COL_SM_GRAY },
+ { "green", TMATHMLCOL, COL_SM_GREEN },
+ { "greenyellow", THTMLCOL, COL_SM_GREENYELLOW },
+ { "grey", THTMLCOL, COL_SM_GREY },
+ { "hex", THEX, COL_SM_BLACK },
+ { "honeydew", THTMLCOL, COL_SM_HONEYDEW },
+ { "hotpink", THTMLCOL, COL_SM_HOTPINK },
+ { "indianred", THTMLCOL, COL_SM_INDIANRED },
+ { "indigo", THTMLCOL, COL_SM_INDIGO },
+ { "ivory", THTMLCOL, COL_SM_IVORY },
+ { "khaki", THTMLCOL, COL_SM_KHAKI },
+ { "lavender", THTMLCOL, COL_SM_LAVENDER },
+ { "lavenderblush", THTMLCOL, COL_SM_LAVENDERBLUSH },
+ { "lawngreen", THTMLCOL, COL_SM_LAWNGREEN },
+ { "lemonchiffon", THTMLCOL, COL_SM_LEMONCHIFFON },
+ { "lightblue", THTMLCOL, COL_SM_LIGHTBLUE },
+ { "lightcoral", THTMLCOL, COL_SM_LIGHTCORAL },
+ { "lightcyan", THTMLCOL, COL_SM_LIGHTCYAN },
+ { "lightgoldenrodyellow", THTMLCOL, COL_SM_LIGHTGOLDENRODYELLOW },
+ { "lightgray", THTMLCOL, COL_SM_LIGHTGRAY },
+ { "lightgreen", THTMLCOL, COL_SM_LIGHTGREEN },
+ { "lightgrey", THTMLCOL, COL_SM_LIGHTGREY },
+ { "lightpink", THTMLCOL, COL_SM_LIGHTPINK },
+ { "lightsalmon", THTMLCOL, COL_SM_LIGHTSALMON },
+ { "lightseagreen", THTMLCOL, COL_SM_LIGHTSEAGREEN },
+ { "lightskyblue", THTMLCOL, COL_SM_LIGHTSKYBLUE },
+ { "lightslategray", THTMLCOL, COL_SM_LIGHTSLATEGRAY },
+ { "lightslategrey", THTMLCOL, COL_SM_LIGHTSLATEGREY },
+ { "lightsteelblue", THTMLCOL, COL_SM_LIGHTSTEELBLUE },
+ { "lightyellow", THTMLCOL, COL_SM_LIGHTYELLOW },
+ { "lime", TMATHMLCOL, COL_SM_LIME },
+ { "limegreen", THTMLCOL, COL_SM_LIMEGREEN },
+ { "linen", THTMLCOL, COL_SM_LINEN },
+ { "lo", TICONICCOL, COL_SM_LO_GREEN },
+ { "magenta", TMATHMLCOL, COL_SM_MAGENTA },
+ { "maroon", TMATHMLCOL, COL_SM_MAROON },
+ { "mediumaquamarine", THTMLCOL, COL_SM_MEDIUMAQUAMARINE },
+ { "mediumblue", THTMLCOL, COL_SM_MEDIUMBLUE },
+ { "mediumorchid", THTMLCOL, COL_SM_MEDIUMORCHID },
+ { "mediumpurple", THTMLCOL, COL_SM_MEDIUMPURPLE },
+ { "mediumseagreen", THTMLCOL, COL_SM_MEDIUMSEAGREEN },
+ { "mediumslateblue", THTMLCOL, COL_SM_MEDIUMSLATEBLUE },
+ { "mediumspringgreen", THTMLCOL, COL_SM_MEDIUMSPRINGGREEN },
+ { "mediumturquoise", THTMLCOL, COL_SM_MEDIUMTURQUOISE },
+ { "mediumvioletred", THTMLCOL, COL_SM_MEDIUMVIOLETRED },
+ { "midnightblue", THTMLCOL, COL_SM_MIDNIGHTBLUE },
+ { "mintcream", THTMLCOL, COL_SM_MINTCREAM },
+ { "mistyrose", THTMLCOL, COL_SM_MISTYROSE },
+ { "moccasin", THTMLCOL, COL_SM_MOCCASIN },
+ { "navajowhite", THTMLCOL, COL_SM_NAVAJOWHITE },
+ { "navy", TMATHMLCOL, COL_SM_NAVY },
+ { "oldlace", THTMLCOL, COL_SM_OLDLACE },
+ { "olive", TMATHMLCOL, COL_SM_OLIVE },
+ { "olivedrab", THTMLCOL, COL_SM_OLIVEDRAB },
+ { "orange", THTMLCOL, COL_SM_ORANGE },
+ { "orangered", THTMLCOL, COL_SM_ORANGERED },
+ { "orchid", THTMLCOL, COL_SM_ORCHID },
+ { "palegoldenrod", THTMLCOL, COL_SM_PALEGOLDENROD },
+ { "palegreen", THTMLCOL, COL_SM_PALEGREEN },
+ { "paleturquoise", THTMLCOL, COL_SM_PALETURQUOISE },
+ { "palevioletred", THTMLCOL, COL_SM_PALEVIOLETRED },
+ { "papayawhip", THTMLCOL, COL_SM_PAPAYAWHIP },
+ { "peachpuff", THTMLCOL, COL_SM_PEACHPUFF },
+ { "peru", THTMLCOL, COL_SM_PERU },
+ { "pink", THTMLCOL, COL_SM_PINK },
+ { "plum", THTMLCOL, COL_SM_PLUM },
+ { "powderblue", THTMLCOL, COL_SM_POWDERBLUE },
+ { "purple", TMATHMLCOL, COL_SM_PURPLE },
+ { "rebeccapurple", THTMLCOL, COL_SM_REBECCAPURPLE },
+ { "red", TMATHMLCOL, COL_SM_RED },
+ { "rgb", TRGB, COL_AUTO },
+ { "rgba", TRGBA, COL_AUTO },
+ { "rosybrown", THTMLCOL, COL_SM_ROSYBROWN },
+ { "royalblue", THTMLCOL, COL_SM_ROYALBLUE },
+ { "saddlebrown", THTMLCOL, COL_SM_SADDLEBROWN },
+ { "salmon", THTMLCOL, COL_SM_SALMON },
+ { "sandybrown", THTMLCOL, COL_SM_SANDYBROWN },
+ { "seagreen", THTMLCOL, COL_SM_SEAGREEN },
+ { "seashell", THTMLCOL, COL_SM_SEASHELL },
+ { "sienna", THTMLCOL, COL_SM_SIENNA },
+ { "silver", TMATHMLCOL, COL_SM_SILVER },
+ { "skyblue", THTMLCOL, COL_SM_SKYBLUE },
+ { "slateblue", THTMLCOL, COL_SM_SLATEBLUE },
+ { "slategray", THTMLCOL, COL_SM_SLATEGRAY },
+ { "slategrey", THTMLCOL, COL_SM_SLATEGREY },
+ { "snow", THTMLCOL, COL_SM_SNOW },
+ { "springgreen", THTMLCOL, COL_SM_SPRINGGREEN },
+ { "steelblue", THTMLCOL, COL_SM_STEELBLUE },
+ { "tan", THTMLCOL, COL_SM_TAN },
+ { "teal", TMATHMLCOL, COL_SM_TEAL },
+ { "thistle", THTMLCOL, COL_SM_THISTLE },
+ { "tomato", THTMLCOL, COL_SM_TOMATO },
+ { "turquoise", THTMLCOL, COL_SM_TURQUOISE },
+ { "ubuntu", TICONICCOL, COL_SM_UBUNTU_ORANGE },
+ { "violet", THTMLCOL, COL_SM_VIOLET },
+ { "wheat", THTMLCOL, COL_SM_WHEAT },
+ { "white", TMATHMLCOL, COL_SM_WHITE },
+ { "whitesmoke", THTMLCOL, COL_SM_WHITESMOKE },
+ { "yellow", TMATHMLCOL, COL_SM_YELLOW },
+ { "yellowgreen", THTMLCOL, COL_SM_YELLOWGREEN } };
+
+const SmColorTokenTableEntry starmathdatabase::aColorTokenTableHTML[]
+ = { { "aliceblue", THTMLCOL, COL_SM_ALICEBLUE },
+ { "antiquewhite", THTMLCOL, COL_SM_ANTIQUEWHITE },
+ { "aqua", TMATHMLCOL, COL_SM_AQUA },
+ { "aquamarine", THTMLCOL, COL_SM_AQUAMARINE },
+ { "azure", THTMLCOL, COL_SM_AZURE },
+ { "beige", THTMLCOL, COL_SM_BEIGE },
+ { "bisque", THTMLCOL, COL_SM_BISQUE },
+ { "black", TMATHMLCOL, COL_SM_BLACK },
+ { "blanchedalmond", THTMLCOL, COL_SM_BLANCHEDALMOND },
+ { "blue", TMATHMLCOL, COL_SM_BLUE },
+ { "blueviolet", THTMLCOL, COL_SM_BLUEVIOLET },
+ { "brown", THTMLCOL, COL_SM_BROWN },
+ { "burlywood", THTMLCOL, COL_SM_BURLYWOOD },
+ { "cadetblue", THTMLCOL, COL_SM_CADETBLUE },
+ { "chartreuse", THTMLCOL, COL_SM_CHARTREUSE },
+ { "chocolate", THTMLCOL, COL_SM_CHOCOLATE },
+ { "coral", THTMLCOL, COL_SM_CORAL },
+ { "cornflowerblue", THTMLCOL, COL_SM_CORNFLOWERBLUE },
+ { "cornsilk", THTMLCOL, COL_SM_CORNSILK },
+ { "crimson", THTMLCOL, COL_SM_CRIMSON },
+ { "cyan", TMATHMLCOL, COL_SM_CYAN },
+ { "darkblue", THTMLCOL, COL_SM_DARKBLUE },
+ { "darkcyan", THTMLCOL, COL_SM_DARKCYAN },
+ { "darkgoldenrod", THTMLCOL, COL_SM_DARKGOLDENROD },
+ { "darkgray", THTMLCOL, COL_SM_DARKGRAY },
+ { "darkgreen", THTMLCOL, COL_SM_DARKGREEN },
+ { "darkgrey", THTMLCOL, COL_SM_DARKGREY },
+ { "darkkhaki", THTMLCOL, COL_SM_DARKKHAKI },
+ { "darkmagenta", THTMLCOL, COL_SM_DARKMAGENTA },
+ { "darkolivegreen", THTMLCOL, COL_SM_DARKOLIVEGREEN },
+ { "darkorange", THTMLCOL, COL_SM_DARKORANGE },
+ { "darkorchid", THTMLCOL, COL_SM_DARKORCHID },
+ { "darkred", THTMLCOL, COL_SM_DARKRED },
+ { "darksalmon", THTMLCOL, COL_SM_DARKSALMON },
+ { "darkseagreen", THTMLCOL, COL_SM_DARKSEAGREEN },
+ { "darkslateblue", THTMLCOL, COL_SM_DARKSLATEBLUE },
+ { "darkslategray", THTMLCOL, COL_SM_DARKSLATEGRAY },
+ { "darkslategrey", THTMLCOL, COL_SM_DARKSLATEGREY },
+ { "darkturquoise", THTMLCOL, COL_SM_DARKTURQUOISE },
+ { "darkviolet", THTMLCOL, COL_SM_DARKVIOLET },
+ { "deeppink", THTMLCOL, COL_SM_DEEPPINK },
+ { "deepskyblue", THTMLCOL, COL_SM_DEEPSKYBLUE },
+ { "dimgray", THTMLCOL, COL_SM_DIMGRAY },
+ { "dimgrey", THTMLCOL, COL_SM_DIMGREY },
+ { "dodgerblue", THTMLCOL, COL_SM_DODGERBLUE },
+ { "firebrick", THTMLCOL, COL_SM_FIREBRICK },
+ { "floralwhite", THTMLCOL, COL_SM_FLORALWHITE },
+ { "forestgreen", THTMLCOL, COL_SM_FORESTGREEN },
+ { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA },
+ { "gainsboro", THTMLCOL, COL_SM_GAINSBORO },
+ { "ghostwhite", THTMLCOL, COL_SM_GHOSTWHITE },
+ { "gold", THTMLCOL, COL_SM_GOLD },
+ { "goldenrod", THTMLCOL, COL_SM_GOLDENROD },
+ { "gray", TMATHMLCOL, COL_SM_GRAY },
+ { "green", TMATHMLCOL, COL_SM_GREEN },
+ { "greenyellow", THTMLCOL, COL_SM_GREENYELLOW },
+ { "grey", THTMLCOL, COL_SM_GREY },
+ { "honeydew", THTMLCOL, COL_SM_HONEYDEW },
+ { "hotpink", THTMLCOL, COL_SM_HOTPINK },
+ { "indianred", THTMLCOL, COL_SM_INDIANRED },
+ { "indigo", THTMLCOL, COL_SM_INDIGO },
+ { "ivory", THTMLCOL, COL_SM_IVORY },
+ { "khaki", THTMLCOL, COL_SM_KHAKI },
+ { "lavender", THTMLCOL, COL_SM_LAVENDER },
+ { "lavenderblush", THTMLCOL, COL_SM_LAVENDERBLUSH },
+ { "lawngreen", THTMLCOL, COL_SM_LAWNGREEN },
+ { "lemonchiffon", THTMLCOL, COL_SM_LEMONCHIFFON },
+ { "lightblue", THTMLCOL, COL_SM_LIGHTBLUE },
+ { "lightcoral", THTMLCOL, COL_SM_LIGHTCORAL },
+ { "lightcyan", THTMLCOL, COL_SM_LIGHTCYAN },
+ { "lightgoldenrodyellow", THTMLCOL, COL_SM_LIGHTGOLDENRODYELLOW },
+ { "lightgray", THTMLCOL, COL_SM_LIGHTGRAY },
+ { "lightgreen", THTMLCOL, COL_SM_LIGHTGREEN },
+ { "lightgrey", THTMLCOL, COL_SM_LIGHTGREY },
+ { "lightpink", THTMLCOL, COL_SM_LIGHTPINK },
+ { "lightsalmon", THTMLCOL, COL_SM_LIGHTSALMON },
+ { "lightseagreen", THTMLCOL, COL_SM_LIGHTSEAGREEN },
+ { "lightskyblue", THTMLCOL, COL_SM_LIGHTSKYBLUE },
+ { "lightslategray", THTMLCOL, COL_SM_LIGHTSLATEGRAY },
+ { "lightslategrey", THTMLCOL, COL_SM_LIGHTSLATEGREY },
+ { "lightsteelblue", THTMLCOL, COL_SM_LIGHTSTEELBLUE },
+ { "lightyellow", THTMLCOL, COL_SM_LIGHTYELLOW },
+ { "lime", TMATHMLCOL, COL_SM_LIME },
+ { "limegreen", THTMLCOL, COL_SM_LIMEGREEN },
+ { "linen", THTMLCOL, COL_SM_LINEN },
+ { "magenta", TMATHMLCOL, COL_SM_MAGENTA },
+ { "maroon", TMATHMLCOL, COL_SM_MAROON },
+ { "mediumaquamarine", THTMLCOL, COL_SM_MEDIUMAQUAMARINE },
+ { "mediumblue", THTMLCOL, COL_SM_MEDIUMBLUE },
+ { "mediumorchid", THTMLCOL, COL_SM_MEDIUMORCHID },
+ { "mediumpurple", THTMLCOL, COL_SM_MEDIUMPURPLE },
+ { "mediumseagreen", THTMLCOL, COL_SM_MEDIUMSEAGREEN },
+ { "mediumslateblue", THTMLCOL, COL_SM_MEDIUMSLATEBLUE },
+ { "mediumspringgreen", THTMLCOL, COL_SM_MEDIUMSPRINGGREEN },
+ { "mediumturquoise", THTMLCOL, COL_SM_MEDIUMTURQUOISE },
+ { "mediumvioletred", THTMLCOL, COL_SM_MEDIUMVIOLETRED },
+ { "midnightblue", THTMLCOL, COL_SM_MIDNIGHTBLUE },
+ { "mintcream", THTMLCOL, COL_SM_MINTCREAM },
+ { "mistyrose", THTMLCOL, COL_SM_MISTYROSE },
+ { "moccasin", THTMLCOL, COL_SM_MOCCASIN },
+ { "navajowhite", THTMLCOL, COL_SM_NAVAJOWHITE },
+ { "navy", TMATHMLCOL, COL_SM_NAVY },
+ { "oldlace", THTMLCOL, COL_SM_OLDLACE },
+ { "olive", TMATHMLCOL, COL_SM_OLIVE },
+ { "olivedrab", THTMLCOL, COL_SM_OLIVEDRAB },
+ { "orange", THTMLCOL, COL_SM_ORANGE },
+ { "orangered", THTMLCOL, COL_SM_ORANGERED },
+ { "orchid", THTMLCOL, COL_SM_ORCHID },
+ { "palegoldenrod", THTMLCOL, COL_SM_PALEGOLDENROD },
+ { "palegreen", THTMLCOL, COL_SM_PALEGREEN },
+ { "paleturquoise", THTMLCOL, COL_SM_PALETURQUOISE },
+ { "palevioletred", THTMLCOL, COL_SM_PALEVIOLETRED },
+ { "papayawhip", THTMLCOL, COL_SM_PAPAYAWHIP },
+ { "peachpuff", THTMLCOL, COL_SM_PEACHPUFF },
+ { "peru", THTMLCOL, COL_SM_PERU },
+ { "pink", THTMLCOL, COL_SM_PINK },
+ { "plum", THTMLCOL, COL_SM_PLUM },
+ { "powderblue", THTMLCOL, COL_SM_POWDERBLUE },
+ { "purple", TMATHMLCOL, COL_SM_PURPLE },
+ { "rebeccapurple", THTMLCOL, COL_SM_REBECCAPURPLE },
+ { "red", TMATHMLCOL, COL_SM_RED },
+ { "rosybrown", THTMLCOL, COL_SM_ROSYBROWN },
+ { "royalblue", THTMLCOL, COL_SM_ROYALBLUE },
+ { "saddlebrown", THTMLCOL, COL_SM_SADDLEBROWN },
+ { "salmon", THTMLCOL, COL_SM_SALMON },
+ { "sandybrown", THTMLCOL, COL_SM_SANDYBROWN },
+ { "seagreen", THTMLCOL, COL_SM_SEAGREEN },
+ { "seashell", THTMLCOL, COL_SM_SEASHELL },
+ { "sienna", THTMLCOL, COL_SM_SIENNA },
+ { "silver", TMATHMLCOL, COL_SM_SILVER },
+ { "skyblue", THTMLCOL, COL_SM_SKYBLUE },
+ { "slateblue", THTMLCOL, COL_SM_SLATEBLUE },
+ { "slategray", THTMLCOL, COL_SM_SLATEGRAY },
+ { "slategrey", THTMLCOL, COL_SM_SLATEGREY },
+ { "snow", THTMLCOL, COL_SM_SNOW },
+ { "springgreen", THTMLCOL, COL_SM_SPRINGGREEN },
+ { "steelblue", THTMLCOL, COL_SM_STEELBLUE },
+ { "tan", THTMLCOL, COL_SM_TAN },
+ { "teal", TMATHMLCOL, COL_SM_TEAL },
+ { "thistle", THTMLCOL, COL_SM_THISTLE },
+ { "tomato", THTMLCOL, COL_SM_TOMATO },
+ { "turquoise", THTMLCOL, COL_SM_TURQUOISE },
+ { "violet", THTMLCOL, COL_SM_VIOLET },
+ { "wheat", THTMLCOL, COL_SM_WHEAT },
+ { "white", TMATHMLCOL, COL_SM_WHITE },
+ { "whitesmoke", THTMLCOL, COL_SM_WHITESMOKE },
+ { "yellow", TMATHMLCOL, COL_SM_YELLOW },
+ { "yellowgreen", THTMLCOL, COL_SM_YELLOWGREEN } };
+
+const SmColorTokenTableEntry starmathdatabase::aColorTokenTableDVIPS[]
+ = { { "apricot", TDVIPSNAMESCOL, COL_SM_DIV_APRICOT },
+ { "aquamarine", TDVIPSNAMESCOL, COL_SM_DIV_AQUAMARINE },
+ { "bittersweet", TDVIPSNAMESCOL, COL_SM_DIV_BITTERSWEET },
+ { "black", TDVIPSNAMESCOL, COL_SM_BLACK },
+ { "blue", TDVIPSNAMESCOL, COL_SM_BLACK } };
+
+const SmColorTokenTableEntry starmathdatabase::aColorTokenTableMATHML[] = {
+ // clang-format off
+ { "aqua", TMATHMLCOL, COL_SM_AQUA },
+ { "black", TMATHMLCOL, COL_SM_BLACK },
+ { "blue", TMATHMLCOL, COL_SM_BLUE },
+ { "fuchsia", TMATHMLCOL, COL_SM_FUCHSIA },
+ { "gray", TMATHMLCOL, COL_SM_GRAY },
+ { "green", TMATHMLCOL, COL_SM_GREEN },
+ { "lime", TMATHMLCOL, COL_SM_LIME },
+ { "maroon", TMATHMLCOL, COL_SM_MAROON },
+ { "navy", TMATHMLCOL, COL_SM_NAVY },
+ { "olive", TMATHMLCOL, COL_SM_OLIVE },
+ { "purple", TMATHMLCOL, COL_SM_PURPLE },
+ { "red", TMATHMLCOL, COL_SM_RED },
+ { "silver", TMATHMLCOL, COL_SM_SILVER },
+ { "teal", TMATHMLCOL, COL_SM_TEAL },
+ { "white", TMATHMLCOL, COL_SM_WHITE },
+ { "yellow", TMATHMLCOL, COL_SM_YELLOW }
+ // clang-format on
+};
+
+const SmColorTokenTableEntry starmathdatabase::aColorTokenTableERROR[]
+ = { { "", TERROR, COL_SM_BLACK } };
+
+SmColorTokenTableEntry starmathdatabase::Identify_Color_Parser(sal_uInt32 cColor)
+{
+ for (auto i = std::begin(aColorTokenTableParse); i < std::end(aColorTokenTableParse); ++i)
+ if (i->equals(cColor))
+ return i;
+ for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i)
+ if (i->equals(cColor))
+ return i;
+ if ((cColor & 0x00FFFFFF) == cColor)
+ return SmColorTokenTableEntry("", TRGB, cColor);
+ else
+ return SmColorTokenTableEntry("", TRGBA, cColor);
+}
+
+SmColorTokenTableEntry starmathdatabase::Identify_Color_HTML(sal_uInt32 cColor)
+{
+ for (auto i = std::begin(aColorTokenTableHTML); i < std::end(aColorTokenTableHTML); ++i)
+ if (i->equals(cColor))
+ return i;
+ if ((cColor & 0x00FFFFFF) == cColor)
+ return SmColorTokenTableEntry("", TRGB, cColor);
+ else
+ return SmColorTokenTableEntry("", TRGBA, cColor);
+}
+
+SmColorTokenTableEntry starmathdatabase::Identify_Color_MATHML(sal_uInt32 cColor)
+{
+ for (auto i = std::begin(aColorTokenTableMATHML); i < std::end(aColorTokenTableMATHML); ++i)
+ if (i->equals(cColor))
+ return i;
+ if ((cColor & 0x00FFFFFF) == cColor)
+ return SmColorTokenTableEntry("", TRGB, cColor);
+ else
+ return SmColorTokenTableEntry("", TRGBA, cColor);
+}
+
+SmColorTokenTableEntry starmathdatabase::Identify_Color_DVIPSNAMES(sal_uInt32 cColor)
+{
+ for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i)
+ if (i->equals(cColor))
+ return i;
+ if ((cColor & 0x00FFFFFF) == cColor)
+ return SmColorTokenTableEntry("", TRGB, cColor);
+ else
+ return SmColorTokenTableEntry("", TRGBA, cColor);
+}
+
+const SmColorTokenTableEntry*
+starmathdatabase::Identify_ColorName_Parser(std::u16string_view colorname)
+{
+ if (colorname.empty())
+ return &aColorTokenTableERROR[0];
+ for (auto i = std::begin(aColorTokenTableParse); i < std::end(aColorTokenTableParse); ++i)
+ {
+ sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent);
+ if (matches == 0)
+ return i;
+ if (matches < 0)
+ break;
+ }
+ return &aColorTokenTableERROR[0];
+}
+SmColorTokenTableEntry starmathdatabase::Identify_ColorName_HTML(std::u16string_view colorname)
+{
+ if (colorname.empty())
+ return SmColorTokenTableEntry("", TERROR, COL_SM_BLACK);
+ if (colorname[0] == '#')
+ {
+ Color col = Color::STRtoRGB(colorname);
+ return SmColorTokenTableEntry("", TRGB, col);
+ }
+ for (auto i = std::begin(aColorTokenTableHTML); i < std::end(aColorTokenTableHTML); ++i)
+ {
+ sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent);
+ if (matches == 0)
+ return i;
+ if (matches < 0)
+ break;
+ }
+ return SmColorTokenTableEntry("", TERROR, COL_SM_BLACK);
+}
+const SmColorTokenTableEntry*
+starmathdatabase::Identify_ColorName_DVIPSNAMES(std::u16string_view colorname)
+{
+ if (colorname.empty())
+ return &aColorTokenTableERROR[0];
+ for (auto i = std::begin(aColorTokenTableDVIPS); i < std::end(aColorTokenTableDVIPS); ++i)
+ {
+ sal_Int32 matches = o3tl::compareToIgnoreAsciiCase(colorname, i->aIdent);
+ if (matches == 0)
+ return i;
+ if (matches < 0)
+ break;
+ }
+ return &aColorTokenTableERROR[0];
+}
diff --git a/starmath/source/mathml/xparsmlbase.cxx b/starmath/source/mathml/xparsmlbase.cxx
new file mode 100644
index 000000000..7e0e93ab7
--- /dev/null
+++ b/starmath/source/mathml/xparsmlbase.cxx
@@ -0,0 +1,2166 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#include <xparsmlbase.hxx>
+
+static ::css::beans::Pair<::rtl::OUString, ::rtl::OUString>
+ icustomMathmlHtmlEntitiesData[starmathdatabase::STARMATH_MATHMLHTML_ENTITY_NUMBER] = {
+ // clang-format off
+ { u"AElig", u"\u00C6" },
+ { u"AMP", u"\u0026" },
+ { u"Aacute", u"\u00C1" },
+ { u"Abreve", u"\u0102" },
+ { u"Acirc", u"\u00C2" },
+ { u"Acy", u"\u0410" },
+ { u"Afr", u"\U0001D504" },
+ { u"Agrave", u"\u00C0" },
+ { u"Alpha", u"\u0391" },
+ { u"Amacr", u"\u0100" },
+ { u"And", u"\u2A53" },
+ { u"Aogon", u"\u0104" },
+ { u"Aopf", u"\U0001D538" },
+ { u"ApplyFunction", u"\u2061" },
+ { u"Aring", u"\u00C5" },
+ { u"Ascr", u"\U0001D49C" },
+ { u"Assign", u"\u2254" },
+ { u"Atilde", u"\u00C3" },
+ { u"Auml", u"\u00C4" },
+ { u"Backslash", u"\u2216" },
+ { u"Barv", u"\u2AE7" },
+ { u"Barwed", u"\u2306" },
+ { u"Bcy", u"\u0411" },
+ { u"Because", u"\u2235" },
+ { u"Bernoullis", u"\u212C" },
+ { u"Beta", u"\u0392" },
+ { u"Bfr", u"\U0001D505" },
+ { u"Bopf", u"\U0001D539" },
+ { u"Breve", u"\u02D8" },
+ { u"Bscr", u"\u212C" },
+ { u"Bumpeq", u"\u224E" },
+ { u"CHcy", u"\u0427" },
+ { u"COPY", u"\u00A9" },
+ { u"Cacute", u"\u0106" },
+ { u"Cap", u"\u22D2" },
+ { u"CapitalDifferentialD", u"\u2145" },
+ { u"Cayleys", u"\u212D" },
+ { u"Ccaron", u"\u010C" },
+ { u"Ccedil", u"\u00C7" },
+ { u"Ccirc", u"\u0108" },
+ { u"Cconint", u"\u2230" },
+ { u"Cdot", u"\u010A" },
+ { u"Cedilla", u"\u00B8" },
+ { u"CenterDot", u"\u00B7" },
+ { u"Cfr", u"\u212D" },
+ { u"Chi", u"\u03A7" },
+ { u"CircleDot", u"\u2299" },
+ { u"CircleMinus", u"\u2296" },
+ { u"CirclePlus", u"\u2295" },
+ { u"CircleTimes", u"\u2297" },
+ { u"ClockwiseContourIntegral", u"\u2232" },
+ { u"CloseCurlyDoubleQuote", u"\u201D" },
+ { u"CloseCurlyQuote", u"\u2019" },
+ { u"Colon", u"\u2237" },
+ { u"Colone", u"\u2A74" },
+ { u"Congruent", u"\u2261" },
+ { u"Conint", u"\u222F" },
+ { u"ContourIntegral", u"\u222E" },
+ { u"Copf", u"\u2102" },
+ { u"Coproduct", u"\u2210" },
+ { u"CounterClockwiseContourIntegral", u"\u2233" },
+ { u"Cross", u"\u2A2F" },
+ { u"Cscr", u"\U0001D49E" },
+ { u"Cup", u"\u22D3" },
+ { u"CupCap", u"\u224D" },
+ { u"DD", u"\u2145" },
+ { u"DDotrahd", u"\u2911" },
+ { u"DJcy", u"\u0402" },
+ { u"DScy", u"\u0405" },
+ { u"DZcy", u"\u040F" },
+ { u"Dagger", u"\u2021" },
+ { u"Darr", u"\u21A1" },
+ { u"Dashv", u"\u2AE4" },
+ { u"Dcaron", u"\u010E" },
+ { u"Dcy", u"\u0414" },
+ { u"Del", u"\u2207" },
+ { u"Delta", u"\u0394" },
+ { u"Dfr", u"\U0001D507" },
+ { u"DiacriticalAcute", u"\u00B4" },
+ { u"DiacriticalDot", u"\u02D9" },
+ { u"DiacriticalDoubleAcute", u"\u02DD" },
+ { u"DiacriticalGrave", u"\u0060" },
+ { u"DiacriticalTilde", u"\u02DC" },
+ { u"Diamond", u"\u22C4" },
+ { u"DifferentialD", u"\u2146" },
+ { u"Dopf", u"\U0001D53B" },
+ { u"Dot", u"\u00A8" },
+ { u"DotDot", u"\u20DC" },
+ { u"DotEqual", u"\u2250" },
+ { u"DoubleContourIntegral", u"\u222F" },
+ { u"DoubleDot", u"\u00A8" },
+ { u"DoubleDownArrow", u"\u21D3" },
+ { u"DoubleLeftArrow", u"\u21D0" },
+ { u"DoubleLeftRightArrow", u"\u21D4" },
+ { u"DoubleLeftTee", u"\u2AE4" },
+ { u"DoubleLongLeftArrow", u"\u27F8" },
+ { u"DoubleLongLeftRightArrow", u"\u27FA" },
+ { u"DoubleLongRightArrow", u"\u27F9" },
+ { u"DoubleRightArrow", u"\u21D2" },
+ { u"DoubleRightTee", u"\u22A8" },
+ { u"DoubleUpArrow", u"\u21D1" },
+ { u"DoubleUpDownArrow", u"\u21D5" },
+ { u"DoubleVerticalBar", u"\u2225" },
+ { u"DownArrow", u"\u2193" },
+ { u"DownArrowBar", u"\u2913" },
+ { u"DownArrowUpArrow", u"\u21F5" },
+ { u"DownBreve", u"\u0311" },
+ { u"DownLeftRightVector", u"\u2950" },
+ { u"DownLeftTeeVector", u"\u295E" },
+ { u"DownLeftVector", u"\u21BD" },
+ { u"DownLeftVectorBar", u"\u2956" },
+ { u"DownRightTeeVector", u"\u295F" },
+ { u"DownRightVector", u"\u21C1" },
+ { u"DownRightVectorBar", u"\u2957" },
+ { u"DownTee", u"\u22A4" },
+ { u"DownTeeArrow", u"\u21A7" },
+ { u"Downarrow", u"\u21D3" },
+ { u"Dscr", u"\U0001D49F" },
+ { u"Dstrok", u"\u0110" },
+ { u"ENG", u"\u014A" },
+ { u"ETH", u"\u00D0" },
+ { u"Eacute", u"\u00C9" },
+ { u"Ecaron", u"\u011A" },
+ { u"Ecirc", u"\u00CA" },
+ { u"Ecy", u"\u042D" },
+ { u"Edot", u"\u0116" },
+ { u"Efr", u"\U0001D508" },
+ { u"Egrave", u"\u00C8" },
+ { u"Element", u"\u2208" },
+ { u"Emacr", u"\u0112" },
+ { u"EmptySmallSquare", u"\u25FB" },
+ { u"EmptyVerySmallSquare", u"\u25AB" },
+ { u"Eogon", u"\u0118" },
+ { u"Eopf", u"\U0001D53C" },
+ { u"Epsilon", u"\u0395" },
+ { u"Equal", u"\u2A75" },
+ { u"EqualTilde", u"\u2242" },
+ { u"Equilibrium", u"\u21CC" },
+ { u"Escr", u"\u2130" },
+ { u"Esim", u"\u2A73" },
+ { u"Eta", u"\u0397" },
+ { u"Euml", u"\u00CB" },
+ { u"Exists", u"\u2203" },
+ { u"ExponentialE", u"\u2147" },
+ { u"Fcy", u"\u0424" },
+ { u"Ffr", u"\U0001D509" },
+ { u"FilledSmallSquare", u"\u25FC" },
+ { u"FilledVerySmallSquare", u"\u25AA" },
+ { u"Fopf", u"\U0001D53D" },
+ { u"ForAll", u"\u2200" },
+ { u"Fouriertrf", u"\u2131" },
+ { u"Fscr", u"\u2131" },
+ { u"GJcy", u"\u0403" },
+ { u"GT", u"\u003E" },
+ { u"Gamma", u"\u0393" },
+ { u"Gammad", u"\u03DC" },
+ { u"Gbreve", u"\u011E" },
+ { u"Gcedil", u"\u0122" },
+ { u"Gcirc", u"\u011C" },
+ { u"Gcy", u"\u0413" },
+ { u"Gdot", u"\u0120" },
+ { u"Gfr", u"\U0001D50A" },
+ { u"Gg", u"\u22D9" },
+ { u"Gopf", u"\U0001D53E" },
+ { u"GreaterEqual", u"\u2265" },
+ { u"GreaterEqualLess", u"\u22DB" },
+ { u"GreaterFullEqual", u"\u2267" },
+ { u"GreaterGreater", u"\u2AA2" },
+ { u"GreaterLess", u"\u2277" },
+ { u"GreaterSlantEqual", u"\u2A7E" },
+ { u"GreaterTilde", u"\u2273" },
+ { u"Gscr", u"\U0001D4A2" },
+ { u"Gt", u"\u226B" },
+ { u"HARDcy", u"\u042A" },
+ { u"Hacek", u"\u02C7" },
+ { u"Hat", u"\u005E" },
+ { u"Hcirc", u"\u0124" },
+ { u"Hfr", u"\u210C" },
+ { u"HilbertSpace", u"\u210B" },
+ { u"Hopf", u"\u210D" },
+ { u"HorizontalLine", u"\u2500" },
+ { u"Hscr", u"\u210B" },
+ { u"Hstrok", u"\u0126" },
+ { u"HumpDownHump", u"\u224E" },
+ { u"HumpEqual", u"\u224F" },
+ { u"IEcy", u"\u0415" },
+ { u"IJlig", u"\u0132" },
+ { u"IOcy", u"\u0401" },
+ { u"Iacute", u"\u00CD" },
+ { u"Icirc", u"\u00CE" },
+ { u"Icy", u"\u0418" },
+ { u"Idot", u"\u0130" },
+ { u"Ifr", u"\u2111" },
+ { u"Igrave", u"\u00CC" },
+ { u"Im", u"\u2111" },
+ { u"Imacr", u"\u012A" },
+ { u"ImaginaryI", u"\u2148" },
+ { u"Implies", u"\u21D2" },
+ { u"Int", u"\u222C" },
+ { u"Integral", u"\u222B" },
+ { u"Intersection", u"\u22C2" },
+ { u"InvisibleComma", u"\u2063" },
+ { u"InvisibleTimes", u"\u2062" },
+ { u"Iogon", u"\u012E" },
+ { u"Iopf", u"\U0001D540" },
+ { u"Iota", u"\u0399" },
+ { u"Iscr", u"\u2110" },
+ { u"Itilde", u"\u0128" },
+ { u"Iukcy", u"\u0406" },
+ { u"Iuml", u"\u00CF" },
+ { u"Jcirc", u"\u0134" },
+ { u"Jcy", u"\u0419" },
+ { u"Jfr", u"\U0001D50D" },
+ { u"Jopf", u"\U0001D541" },
+ { u"Jscr", u"\U0001D4A5" },
+ { u"Jsercy", u"\u0408" },
+ { u"Jukcy", u"\u0404" },
+ { u"KHcy", u"\u0425" },
+ { u"KJcy", u"\u040C" },
+ { u"Kappa", u"\u039A" },
+ { u"Kcedil", u"\u0136" },
+ { u"Kcy", u"\u041A" },
+ { u"Kfr", u"\U0001D50E" },
+ { u"Kopf", u"\U0001D542" },
+ { u"Kscr", u"\U0001D4A6" },
+ { u"LJcy", u"\u0409" },
+ { u"LT", u"\u003C" },
+ { u"Lacute", u"\u0139" },
+ { u"Lambda", u"\u039B" },
+ { u"Lang", u"\u27EA" },
+ { u"Laplacetrf", u"\u2112" },
+ { u"Larr", u"\u219E" },
+ { u"Lcaron", u"\u013D" },
+ { u"Lcedil", u"\u013B" },
+ { u"Lcy", u"\u041B" },
+ { u"LeftAngleBracket", u"\u27E8" },
+ { u"LeftArrow", u"\u2190" },
+ { u"LeftArrowBar", u"\u21E4" },
+ { u"LeftArrowRightArrow", u"\u21C6" },
+ { u"LeftCeiling", u"\u2308" },
+ { u"LeftDoubleBracket", u"\u27E6" },
+ { u"LeftDownTeeVector", u"\u2961" },
+ { u"LeftDownVector", u"\u21C3" },
+ { u"LeftDownVectorBar", u"\u2959" },
+ { u"LeftFloor", u"\u230A" },
+ { u"LeftRightArrow", u"\u2194" },
+ { u"LeftRightVector", u"\u294E" },
+ { u"LeftTee", u"\u22A3" },
+ { u"LeftTeeArrow", u"\u21A4" },
+ { u"LeftTeeVector", u"\u295A" },
+ { u"LeftTriangle", u"\u22B2" },
+ { u"LeftTriangleBar", u"\u29CF" },
+ { u"LeftTriangleEqual", u"\u22B4" },
+ { u"LeftUpDownVector", u"\u2951" },
+ { u"LeftUpTeeVector", u"\u2960" },
+ { u"LeftUpVector", u"\u21BF" },
+ { u"LeftUpVectorBar", u"\u2958" },
+ { u"LeftVector", u"\u21BC" },
+ { u"LeftVectorBar", u"\u2952" },
+ { u"Leftarrow", u"\u21D0" },
+ { u"Leftrightarrow", u"\u21D4" },
+ { u"LessEqualGreater", u"\u22DA" },
+ { u"LessFullEqual", u"\u2266" },
+ { u"LessGreater", u"\u2276" },
+ { u"LessLess", u"\u2AA1" },
+ { u"LessSlantEqual", u"\u2A7D" },
+ { u"LessTilde", u"\u2272" },
+ { u"Lfr", u"\U0001D50F" },
+ { u"Ll", u"\u22D8" },
+ { u"Lleftarrow", u"\u21DA" },
+ { u"Lmidot", u"\u013F" },
+ { u"LongLeftArrow", u"\u27F5" },
+ { u"LongLeftRightArrow", u"\u27F7" },
+ { u"LongRightArrow", u"\u27F6" },
+ { u"Longleftarrow", u"\u27F8" },
+ { u"Longleftrightarrow", u"\u27FA" },
+ { u"Longrightarrow", u"\u27F9" },
+ { u"Lopf", u"\U0001D543" },
+ { u"LowerLeftArrow", u"\u2199" },
+ { u"LowerRightArrow", u"\u2198" },
+ { u"Lscr", u"\u2112" },
+ { u"Lsh", u"\u21B0" },
+ { u"Lstrok", u"\u0141" },
+ { u"Lt", u"\u226A" },
+ { u"Map", u"\u2905" },
+ { u"Mcy", u"\u041C" },
+ { u"MediumSpace", u"\u205F" },
+ { u"Mellintrf", u"\u2133" },
+ { u"Mfr", u"\U0001D510" },
+ { u"MinusPlus", u"\u2213" },
+ { u"Mopf", u"\U0001D544" },
+ { u"Mscr", u"\u2133" },
+ { u"Mu", u"\u039C" },
+ { u"NJcy", u"\u040A" },
+ { u"Nacute", u"\u0143" },
+ { u"Ncaron", u"\u0147" },
+ { u"Ncedil", u"\u0145" },
+ { u"Ncy", u"\u041D" },
+ { u"NegativeMediumSpace", u"\u200B" },
+ { u"NegativeThickSpace", u"\u200B" },
+ { u"NegativeThinSpace", u"\u200B" },
+ { u"NegativeVeryThinSpace", u"\u200B" },
+ { u"NestedGreaterGreater", u"\u226B" },
+ { u"NestedLessLess", u"\u226A" },
+ { u"NewLine", u"\u000A" },
+ { u"Nfr", u"\U0001D511" },
+ { u"NoBreak", u"\u2060" },
+ { u"NonBreakingSpace", u"\u00A0" },
+ { u"Nopf", u"\u2115" },
+ { u"Not", u"\u2AEC" },
+ { u"NotCongruent", u"\u2262" },
+ { u"NotCupCap", u"\u226D" },
+ { u"NotDoubleVerticalBar", u"\u2226" },
+ { u"NotElement", u"\u2209" },
+ { u"NotEqual", u"\u2260" },
+ { u"NotEqualTilde", u"\u2242\u0338" },
+ { u"NotExists", u"\u2204" },
+ { u"NotGreater", u"\u226F" },
+ { u"NotGreaterEqual", u"\u2271" },
+ { u"NotGreaterFullEqual", u"\u2267\u0338" },
+ { u"NotGreaterGreater", u"\u226B\u0338" },
+ { u"NotGreaterLess", u"\u2279" },
+ { u"NotGreaterSlantEqual", u"\u2A7E\u0338" },
+ { u"NotGreaterTilde", u"\u2275" },
+ { u"NotHumpDownHump", u"\u224E\u0338" },
+ { u"NotHumpEqual", u"\u224F\u0338" },
+ { u"NotLeftTriangle", u"\u22EA" },
+ { u"NotLeftTriangleBar", u"\u29CF\u0338" },
+ { u"NotLeftTriangleEqual", u"\u22EC" },
+ { u"NotLess", u"\u226E" },
+ { u"NotLessEqual", u"\u2270" },
+ { u"NotLessGreater", u"\u2278" },
+ { u"NotLessLess", u"\u226A\u0338" },
+ { u"NotLessSlantEqual", u"\u2A7D\u0338" },
+ { u"NotLessTilde", u"\u2274" },
+ { u"NotNestedGreaterGreater", u"\u2AA2\u0338" },
+ { u"NotNestedLessLess", u"\u2AA1\u0338" },
+ { u"NotPrecedes", u"\u2280" },
+ { u"NotPrecedesEqual", u"\u2AAF\u0338" },
+ { u"NotPrecedesSlantEqual", u"\u22E0" },
+ { u"NotReverseElement", u"\u220C" },
+ { u"NotRightTriangle", u"\u22EB" },
+ { u"NotRightTriangleBar", u"\u29D0\u0338" },
+ { u"NotRightTriangleEqual", u"\u22ED" },
+ { u"NotSquareSubset", u"\u228F\u0338" },
+ { u"NotSquareSubsetEqual", u"\u22E2" },
+ { u"NotSquareSuperset", u"\u2290\u0338" },
+ { u"NotSquareSupersetEqual", u"\u22E3" },
+ { u"NotSubset", u"\u2282\u20D2" },
+ { u"NotSubsetEqual", u"\u2288" },
+ { u"NotSucceeds", u"\u2281" },
+ { u"NotSucceedsEqual", u"\u2AB0\u0338" },
+ { u"NotSucceedsSlantEqual", u"\u22E1" },
+ { u"NotSucceedsTilde", u"\u227F\u0338" },
+ { u"NotSuperset", u"\u2283\u20D2" },
+ { u"NotSupersetEqual", u"\u2289" },
+ { u"NotTilde", u"\u2241" },
+ { u"NotTildeEqual", u"\u2244" },
+ { u"NotTildeFullEqual", u"\u2247" },
+ { u"NotTildeTilde", u"\u2249" },
+ { u"NotVerticalBar", u"\u2224" },
+ { u"Nscr", u"\U0001D4A9" },
+ { u"Ntilde", u"\u00D1" },
+ { u"Nu", u"\u039D" },
+ { u"OElig", u"\u0152" },
+ { u"Oacute", u"\u00D3" },
+ { u"Ocirc", u"\u00D4" },
+ { u"Ocy", u"\u041E" },
+ { u"Odblac", u"\u0150" },
+ { u"Ofr", u"\U0001D512" },
+ { u"Ograve", u"\u00D2" },
+ { u"Omacr", u"\u014C" },
+ { u"Omega", u"\u03A9" },
+ { u"Omicron", u"\u039F" },
+ { u"Oopf", u"\U0001D546" },
+ { u"OpenCurlyDoubleQuote", u"\u201C" },
+ { u"OpenCurlyQuote", u"\u2018" },
+ { u"Or", u"\u2A54" },
+ { u"Oscr", u"\U0001D4AA" },
+ { u"Oslash", u"\u00D8" },
+ { u"Otilde", u"\u00D5" },
+ { u"Otimes", u"\u2A37" },
+ { u"Ouml", u"\u00D6" },
+ { u"OverBar", u"\u203E" },
+ { u"OverBrace", u"\u23DE" },
+ { u"OverBracket", u"\u23B4" },
+ { u"OverParenthesis", u"\u23DC" },
+ { u"PartialD", u"\u2202" },
+ { u"Pcy", u"\u041F" },
+ { u"Pfr", u"\U0001D513" },
+ { u"Phi", u"\u03A6" },
+ { u"Pi", u"\u03A0" },
+ { u"PlusMinus", u"\u00B1" },
+ { u"Poincareplane", u"\u210C" },
+ { u"Popf", u"\u2119" },
+ { u"Pr", u"\u2ABB" },
+ { u"Precedes", u"\u227A" },
+ { u"PrecedesEqual", u"\u2AAF" },
+ { u"PrecedesSlantEqual", u"\u227C" },
+ { u"PrecedesTilde", u"\u227E" },
+ { u"Prime", u"\u2033" },
+ { u"Product", u"\u220F" },
+ { u"Proportion", u"\u2237" },
+ { u"Proportional", u"\u221D" },
+ { u"Pscr", u"\U0001D4AB" },
+ { u"Psi", u"\u03A8" },
+ { u"QUOT", u"\u0022" },
+ { u"Qfr", u"\U0001D514" },
+ { u"Qopf", u"\u211A" },
+ { u"Qscr", u"\U0001D4AC" },
+ { u"RBarr", u"\u2910" },
+ { u"REG", u"\u00AE" },
+ { u"Racute", u"\u0154" },
+ { u"Rang", u"\u27EB" },
+ { u"Rarr", u"\u21A0" },
+ { u"Rarrtl", u"\u2916" },
+ { u"Rcaron", u"\u0158" },
+ { u"Rcedil", u"\u0156" },
+ { u"Rcy", u"\u0420" },
+ { u"Re", u"\u211C" },
+ { u"ReverseElement", u"\u220B" },
+ { u"ReverseEquilibrium", u"\u21CB" },
+ { u"ReverseUpEquilibrium", u"\u296F" },
+ { u"Rfr", u"\u211C" },
+ { u"Rho", u"\u03A1" },
+ { u"RightAngleBracket", u"\u27E9" },
+ { u"RightArrow", u"\u2192" },
+ { u"RightArrowBar", u"\u21E5" },
+ { u"RightArrowLeftArrow", u"\u21C4" },
+ { u"RightCeiling", u"\u2309" },
+ { u"RightDoubleBracket", u"\u27E7" },
+ { u"RightDownTeeVector", u"\u295D" },
+ { u"RightDownVector", u"\u21C2" },
+ { u"RightDownVectorBar", u"\u2955" },
+ { u"RightFloor", u"\u230B" },
+ { u"RightTee", u"\u22A2" },
+ { u"RightTeeArrow", u"\u21A6" },
+ { u"RightTeeVector", u"\u295B" },
+ { u"RightTriangle", u"\u22B3" },
+ { u"RightTriangleBar", u"\u29D0" },
+ { u"RightTriangleEqual", u"\u22B5" },
+ { u"RightUpDownVector", u"\u294F" },
+ { u"RightUpTeeVector", u"\u295C" },
+ { u"RightUpVector", u"\u21BE" },
+ { u"RightUpVectorBar", u"\u2954" },
+ { u"RightVector", u"\u21C0" },
+ { u"RightVectorBar", u"\u2953" },
+ { u"Rightarrow", u"\u21D2" },
+ { u"Ropf", u"\u211D" },
+ { u"RoundImplies", u"\u2970" },
+ { u"Rrightarrow", u"\u21DB" },
+ { u"Rscr", u"\u211B" },
+ { u"Rsh", u"\u21B1" },
+ { u"RuleDelayed", u"\u29F4" },
+ { u"SHCHcy", u"\u0429" },
+ { u"SHcy", u"\u0428" },
+ { u"SOFTcy", u"\u042C" },
+ { u"Sacute", u"\u015A" },
+ { u"Sc", u"\u2ABC" },
+ { u"Scaron", u"\u0160" },
+ { u"Scedil", u"\u015E" },
+ { u"Scirc", u"\u015C" },
+ { u"Scy", u"\u0421" },
+ { u"Sfr", u"\U0001D516" },
+ { u"ShortDownArrow", u"\u2193" },
+ { u"ShortLeftArrow", u"\u2190" },
+ { u"ShortRightArrow", u"\u2192" },
+ { u"ShortUpArrow", u"\u2191" },
+ { u"Sigma", u"\u03A3" },
+ { u"SmallCircle", u"\u2218" },
+ { u"Sopf", u"\U0001D54A" },
+ { u"Sqrt", u"\u221A" },
+ { u"Square", u"\u25A1" },
+ { u"SquareIntersection", u"\u2293" },
+ { u"SquareSubset", u"\u228F" },
+ { u"SquareSubsetEqual", u"\u2291" },
+ { u"SquareSuperset", u"\u2290" },
+ { u"SquareSupersetEqual", u"\u2292" },
+ { u"SquareUnion", u"\u2294" },
+ { u"Sscr", u"\U0001D4AE" },
+ { u"Star", u"\u22C6" },
+ { u"Sub", u"\u22D0" },
+ { u"Subset", u"\u22D0" },
+ { u"SubsetEqual", u"\u2286" },
+ { u"Succeeds", u"\u227B" },
+ { u"SucceedsEqual", u"\u2AB0" },
+ { u"SucceedsSlantEqual", u"\u227D" },
+ { u"SucceedsTilde", u"\u227F" },
+ { u"SuchThat", u"\u220B" },
+ { u"Sum", u"\u2211" },
+ { u"Sup", u"\u22D1" },
+ { u"Superset", u"\u2283" },
+ { u"SupersetEqual", u"\u2287" },
+ { u"Supset", u"\u22D1" },
+ { u"THORN", u"\u00DE" },
+ { u"TRADE", u"\u2122" },
+ { u"TSHcy", u"\u040B" },
+ { u"TScy", u"\u0426" },
+ { u"Tab", u"\u0009" },
+ { u"Tau", u"\u03A4" },
+ { u"Tcaron", u"\u0164" },
+ { u"Tcedil", u"\u0162" },
+ { u"Tcy", u"\u0422" },
+ { u"Tfr", u"\U0001D517" },
+ { u"Therefore", u"\u2234" },
+ { u"Theta", u"\u0398" },
+ { u"ThickSpace", u"\u205F\u200A" },
+ { u"ThinSpace", u"\u2009" },
+ { u"Tilde", u"\u223C" },
+ { u"TildeEqual", u"\u2243" },
+ { u"TildeFullEqual", u"\u2245" },
+ { u"TildeTilde", u"\u2248" },
+ { u"Topf", u"\U0001D54B" },
+ { u"TripleDot", u"\u20DB" },
+ { u"Tscr", u"\U0001D4AF" },
+ { u"Tstrok", u"\u0166" },
+ { u"Uacute", u"\u00DA" },
+ { u"Uarr", u"\u219F" },
+ { u"Uarrocir", u"\u2949" },
+ { u"Ubrcy", u"\u040E" },
+ { u"Ubreve", u"\u016C" },
+ { u"Ucirc", u"\u00DB" },
+ { u"Ucy", u"\u0423" },
+ { u"Udblac", u"\u0170" },
+ { u"Ufr", u"\U0001D518" },
+ { u"Ugrave", u"\u00D9" },
+ { u"Umacr", u"\u016A" },
+ { u"UnderBar", u"\u005F" },
+ { u"UnderBrace", u"\u23DF" },
+ { u"UnderBracket", u"\u23B5" },
+ { u"UnderParenthesis", u"\u23DD" },
+ { u"Union", u"\u22C3" },
+ { u"UnionPlus", u"\u228E" },
+ { u"Uogon", u"\u0172" },
+ { u"Uopf", u"\U0001D54C" },
+ { u"UpArrow", u"\u2191" },
+ { u"UpArrowBar", u"\u2912" },
+ { u"UpArrowDownArrow", u"\u21C5" },
+ { u"UpDownArrow", u"\u2195" },
+ { u"UpEquilibrium", u"\u296E" },
+ { u"UpTee", u"\u22A5" },
+ { u"UpTeeArrow", u"\u21A5" },
+ { u"Uparrow", u"\u21D1" },
+ { u"Updownarrow", u"\u21D5" },
+ { u"UpperLeftArrow", u"\u2196" },
+ { u"UpperRightArrow", u"\u2197" },
+ { u"Upsi", u"\u03D2" },
+ { u"Upsilon", u"\u03A5" },
+ { u"Uring", u"\u016E" },
+ { u"Uscr", u"\U0001D4B0" },
+ { u"Utilde", u"\u0168" },
+ { u"Uuml", u"\u00DC" },
+ { u"VDash", u"\u22AB" },
+ { u"Vbar", u"\u2AEB" },
+ { u"Vcy", u"\u0412" },
+ { u"Vdash", u"\u22A9" },
+ { u"Vdashl", u"\u2AE6" },
+ { u"Vee", u"\u22C1" },
+ { u"Verbar", u"\u2016" },
+ { u"Vert", u"\u2016" },
+ { u"VerticalBar", u"\u2223" },
+ { u"VerticalLine", u"\u007C" },
+ { u"VerticalSeparator", u"\u2758" },
+ { u"VerticalTilde", u"\u2240" },
+ { u"VeryThinSpace", u"\u200A" },
+ { u"Vfr", u"\U0001D519" },
+ { u"Vopf", u"\U0001D54D" },
+ { u"Vscr", u"\U0001D4B1" },
+ { u"Vvdash", u"\u22AA" },
+ { u"Wcirc", u"\u0174" },
+ { u"Wedge", u"\u22C0" },
+ { u"Wfr", u"\U0001D51A" },
+ { u"Wopf", u"\U0001D54E" },
+ { u"Wscr", u"\U0001D4B2" },
+ { u"Xfr", u"\U0001D51B" },
+ { u"Xi", u"\u039E" },
+ { u"Xopf", u"\U0001D54F" },
+ { u"Xscr", u"\U0001D4B3" },
+ { u"YAcy", u"\u042F" },
+ { u"YIcy", u"\u0407" },
+ { u"YUcy", u"\u042E" },
+ { u"Yacute", u"\u00DD" },
+ { u"Ycirc", u"\u0176" },
+ { u"Ycy", u"\u042B" },
+ { u"Yfr", u"\U0001D51C" },
+ { u"Yopf", u"\U0001D550" },
+ { u"Yscr", u"\U0001D4B4" },
+ { u"Yuml", u"\u0178" },
+ { u"ZHcy", u"\u0416" },
+ { u"Zacute", u"\u0179" },
+ { u"Zcaron", u"\u017D" },
+ { u"Zcy", u"\u0417" },
+ { u"Zdot", u"\u017B" },
+ { u"ZeroWidthSpace", u"\u200B" },
+ { u"Zeta", u"\u0396" },
+ { u"Zfr", u"\u2128" },
+ { u"Zopf", u"\u2124" },
+ { u"Zscr", u"\U0001D4B5" },
+ { u"aacute", u"\u00E1" },
+ { u"abreve", u"\u0103" },
+ { u"ac", u"\u223E" },
+ { u"acE", u"\u223E\u0333" },
+ { u"acd", u"\u223F" },
+ { u"acirc", u"\u00E2" },
+ { u"acute", u"\u00B4" },
+ { u"acy", u"\u0430" },
+ { u"aelig", u"\u00E6" },
+ { u"af", u"\u2061" },
+ { u"afr", u"\U0001D51E" },
+ { u"agrave", u"\u00E0" },
+ { u"alefsym", u"\u2135" },
+ { u"aleph", u"\u2135" },
+ { u"alpha", u"\u03B1" },
+ { u"amacr", u"\u0101" },
+ { u"amalg", u"\u2A3F" },
+ { u"amp", u"\u0026" },
+ { u"and", u"\u2227" },
+ { u"andand", u"\u2A55" },
+ { u"andd", u"\u2A5C" },
+ { u"andslope", u"\u2A58" },
+ { u"andv", u"\u2A5A" },
+ { u"ang", u"\u2220" },
+ { u"ange", u"\u29A4" },
+ { u"angle", u"\u2220" },
+ { u"angmsd", u"\u2221" },
+ { u"angmsdaa", u"\u29A8" },
+ { u"angmsdab", u"\u29A9" },
+ { u"angmsdac", u"\u29AA" },
+ { u"angmsdad", u"\u29AB" },
+ { u"angmsdae", u"\u29AC" },
+ { u"angmsdaf", u"\u29AD" },
+ { u"angmsdag", u"\u29AE" },
+ { u"angmsdah", u"\u29AF" },
+ { u"angrt", u"\u221F" },
+ { u"angrtvb", u"\u22BE" },
+ { u"angrtvbd", u"\u299D" },
+ { u"angsph", u"\u2222" },
+ { u"angst", u"\u00C5" },
+ { u"angzarr", u"\u237C" },
+ { u"aogon", u"\u0105" },
+ { u"aopf", u"\U0001D552" },
+ { u"ap", u"\u2248" },
+ { u"apE", u"\u2A70" },
+ { u"apacir", u"\u2A6F" },
+ { u"ape", u"\u224A" },
+ { u"apid", u"\u224B" },
+ { u"apos", u"\u0027" },
+ { u"approx", u"\u2248" },
+ { u"approxeq", u"\u224A" },
+ { u"aring", u"\u00E5" },
+ { u"ascr", u"\U0001D4B6" },
+ { u"ast", u"\u002A" },
+ { u"asymp", u"\u2248" },
+ { u"asympeq", u"\u224D" },
+ { u"atilde", u"\u00E3" },
+ { u"auml", u"\u00E4" },
+ { u"awconint", u"\u2233" },
+ { u"awint", u"\u2A11" },
+ { u"bNot", u"\u2AED" },
+ { u"backcong", u"\u224C" },
+ { u"backepsilon", u"\u03F6" },
+ { u"backprime", u"\u2035" },
+ { u"backsim", u"\u223D" },
+ { u"backsimeq", u"\u22CD" },
+ { u"barvee", u"\u22BD" },
+ { u"barwed", u"\u2305" },
+ { u"barwedge", u"\u2305" },
+ { u"bbrk", u"\u23B5" },
+ { u"bbrktbrk", u"\u23B6" },
+ { u"bcong", u"\u224C" },
+ { u"bcy", u"\u0431" },
+ { u"bdquo", u"\u201E" },
+ { u"becaus", u"\u2235" },
+ { u"because", u"\u2235" },
+ { u"bemptyv", u"\u29B0" },
+ { u"bepsi", u"\u03F6" },
+ { u"bernou", u"\u212C" },
+ { u"beta", u"\u03B2" },
+ { u"beth", u"\u2136" },
+ { u"between", u"\u226C" },
+ { u"bfr", u"\U0001D51F" },
+ { u"bigcap", u"\u22C2" },
+ { u"bigcirc", u"\u25EF" },
+ { u"bigcup", u"\u22C3" },
+ { u"bigodot", u"\u2A00" },
+ { u"bigoplus", u"\u2A01" },
+ { u"bigotimes", u"\u2A02" },
+ { u"bigsqcup", u"\u2A06" },
+ { u"bigstar", u"\u2605" },
+ { u"bigtriangledown", u"\u25BD" },
+ { u"bigtriangleup", u"\u25B3" },
+ { u"biguplus", u"\u2A04" },
+ { u"bigvee", u"\u22C1" },
+ { u"bigwedge", u"\u22C0" },
+ { u"bkarow", u"\u290D" },
+ { u"blacklozenge", u"\u29EB" },
+ { u"blacksquare", u"\u25AA" },
+ { u"blacktriangle", u"\u25B4" },
+ { u"blacktriangledown", u"\u25BE" },
+ { u"blacktriangleleft", u"\u25C2" },
+ { u"blacktriangleright", u"\u25B8" },
+ { u"blank", u"\u2423" },
+ { u"blk12", u"\u2592" },
+ { u"blk14", u"\u2591" },
+ { u"blk34", u"\u2593" },
+ { u"block", u"\u2588" },
+ { u"bne", u"\u003D\u20E5" },
+ { u"bnequiv", u"\u2261\u20E5" },
+ { u"bnot", u"\u2310" },
+ { u"bopf", u"\U0001D553" },
+ { u"bot", u"\u22A5" },
+ { u"bottom", u"\u22A5" },
+ { u"bowtie", u"\u22C8" },
+ { u"boxDL", u"\u2557" },
+ { u"boxDR", u"\u2554" },
+ { u"boxDl", u"\u2556" },
+ { u"boxDr", u"\u2553" },
+ { u"boxH", u"\u2550" },
+ { u"boxHD", u"\u2566" },
+ { u"boxHU", u"\u2569" },
+ { u"boxHd", u"\u2564" },
+ { u"boxHu", u"\u2567" },
+ { u"boxUL", u"\u255D" },
+ { u"boxUR", u"\u255A" },
+ { u"boxUl", u"\u255C" },
+ { u"boxUr", u"\u2559" },
+ { u"boxV", u"\u2551" },
+ { u"boxVH", u"\u256C" },
+ { u"boxVL", u"\u2563" },
+ { u"boxVR", u"\u2560" },
+ { u"boxVh", u"\u256B" },
+ { u"boxVl", u"\u2562" },
+ { u"boxVr", u"\u255F" },
+ { u"boxbox", u"\u29C9" },
+ { u"boxdL", u"\u2555" },
+ { u"boxdR", u"\u2552" },
+ { u"boxdl", u"\u2510" },
+ { u"boxdr", u"\u250C" },
+ { u"boxh", u"\u2500" },
+ { u"boxhD", u"\u2565" },
+ { u"boxhU", u"\u2568" },
+ { u"boxhd", u"\u252C" },
+ { u"boxhu", u"\u2534" },
+ { u"boxminus", u"\u229F" },
+ { u"boxplus", u"\u229E" },
+ { u"boxtimes", u"\u22A0" },
+ { u"boxuL", u"\u255B" },
+ { u"boxuR", u"\u2558" },
+ { u"boxul", u"\u2518" },
+ { u"boxur", u"\u2514" },
+ { u"boxv", u"\u2502" },
+ { u"boxvH", u"\u256A" },
+ { u"boxvL", u"\u2561" },
+ { u"boxvR", u"\u255E" },
+ { u"boxvh", u"\u253C" },
+ { u"boxvl", u"\u2524" },
+ { u"boxvr", u"\u251C" },
+ { u"bprime", u"\u2035" },
+ { u"breve", u"\u02D8" },
+ { u"brvbar", u"\u00A6" },
+ { u"bscr", u"\U0001D4B7" },
+ { u"bsemi", u"\u204F" },
+ { u"bsim", u"\u223D" },
+ { u"bsime", u"\u22CD" },
+ { u"bsol", u"\u005C" },
+ { u"bsolb", u"\u29C5" },
+ { u"bsolhsub", u"\u27C8" },
+ { u"bull", u"\u2022" },
+ { u"bullet", u"\u2022" },
+ { u"bump", u"\u224E" },
+ { u"bumpE", u"\u2AAE" },
+ { u"bumpe", u"\u224F" },
+ { u"bumpeq", u"\u224F" },
+ { u"cacute", u"\u0107" },
+ { u"cap", u"\u2229" },
+ { u"capand", u"\u2A44" },
+ { u"capbrcup", u"\u2A49" },
+ { u"capcap", u"\u2A4B" },
+ { u"capcup", u"\u2A47" },
+ { u"capdot", u"\u2A40" },
+ { u"caps", u"\u2229\uFE00" },
+ { u"caret", u"\u2041" },
+ { u"caron", u"\u02C7" },
+ { u"ccaps", u"\u2A4D" },
+ { u"ccaron", u"\u010D" },
+ { u"ccedil", u"\u00E7" },
+ { u"ccirc", u"\u0109" },
+ { u"ccups", u"\u2A4C" },
+ { u"ccupssm", u"\u2A50" },
+ { u"cdot", u"\u010B" },
+ { u"cedil", u"\u00B8" },
+ { u"cemptyv", u"\u29B2" },
+ { u"cent", u"\u00A2" },
+ { u"centerdot", u"\u00B7" },
+ { u"cfr", u"\U0001D520" },
+ { u"chcy", u"\u0447" },
+ { u"check", u"\u2713" },
+ { u"checkmark", u"\u2713" },
+ { u"chi", u"\u03C7" },
+ { u"cir", u"\u25CB" },
+ { u"cirE", u"\u29C3" },
+ { u"circ", u"\u02C6" },
+ { u"circeq", u"\u2257" },
+ { u"circlearrowleft", u"\u21BA" },
+ { u"circlearrowright", u"\u21BB" },
+ { u"circledR", u"\u00AE" },
+ { u"circledS", u"\u24C8" },
+ { u"circledast", u"\u229B" },
+ { u"circledcirc", u"\u229A" },
+ { u"circleddash", u"\u229D" },
+ { u"cire", u"\u2257" },
+ { u"cirfnint", u"\u2A10" },
+ { u"cirmid", u"\u2AEF" },
+ { u"cirscir", u"\u29C2" },
+ { u"clubs", u"\u2663" },
+ { u"clubsuit", u"\u2663" },
+ { u"colon", u"\u003A" },
+ { u"colone", u"\u2254" },
+ { u"coloneq", u"\u2254" },
+ { u"comma", u"\u002C" },
+ { u"commat", u"\u0040" },
+ { u"comp", u"\u2201" },
+ { u"compfn", u"\u2218" },
+ { u"complement", u"\u2201" },
+ { u"complexes", u"\u2102" },
+ { u"cong", u"\u2245" },
+ { u"congdot", u"\u2A6D" },
+ { u"conint", u"\u222E" },
+ { u"copf", u"\U0001D554" },
+ { u"coprod", u"\u2210" },
+ { u"copy", u"\u00A9" },
+ { u"copysr", u"\u2117" },
+ { u"crarr", u"\u21B5" },
+ { u"cross", u"\u2717" },
+ { u"cscr", u"\U0001D4B8" },
+ { u"csub", u"\u2ACF" },
+ { u"csube", u"\u2AD1" },
+ { u"csup", u"\u2AD0" },
+ { u"csupe", u"\u2AD2" },
+ { u"ctdot", u"\u22EF" },
+ { u"cudarrl", u"\u2938" },
+ { u"cudarrr", u"\u2935" },
+ { u"cuepr", u"\u22DE" },
+ { u"cuesc", u"\u22DF" },
+ { u"cularr", u"\u21B6" },
+ { u"cularrp", u"\u293D" },
+ { u"cup", u"\u222A" },
+ { u"cupbrcap", u"\u2A48" },
+ { u"cupcap", u"\u2A46" },
+ { u"cupcup", u"\u2A4A" },
+ { u"cupdot", u"\u228D" },
+ { u"cupor", u"\u2A45" },
+ { u"cups", u"\u222A\uFE00" },
+ { u"curarr", u"\u21B7" },
+ { u"curarrm", u"\u293C" },
+ { u"curlyeqprec", u"\u22DE" },
+ { u"curlyeqsucc", u"\u22DF" },
+ { u"curlyvee", u"\u22CE" },
+ { u"curlywedge", u"\u22CF" },
+ { u"curren", u"\u00A4" },
+ { u"curvearrowleft", u"\u21B6" },
+ { u"curvearrowright", u"\u21B7" },
+ { u"cuvee", u"\u22CE" },
+ { u"cuwed", u"\u22CF" },
+ { u"cwconint", u"\u2232" },
+ { u"cwint", u"\u2231" },
+ { u"cylcty", u"\u232D" },
+ { u"dArr", u"\u21D3" },
+ { u"dHar", u"\u2965" },
+ { u"dagger", u"\u2020" },
+ { u"daleth", u"\u2138" },
+ { u"darr", u"\u2193" },
+ { u"dash", u"\u2010" },
+ { u"dashv", u"\u22A3" },
+ { u"dbkarow", u"\u290F" },
+ { u"dblac", u"\u02DD" },
+ { u"dcaron", u"\u010F" },
+ { u"dcy", u"\u0434" },
+ { u"dd", u"\u2146" },
+ { u"ddagger", u"\u2021" },
+ { u"ddarr", u"\u21CA" },
+ { u"ddotseq", u"\u2A77" },
+ { u"deg", u"\u00B0" },
+ { u"delta", u"\u03B4" },
+ { u"demptyv", u"\u29B1" },
+ { u"dfisht", u"\u297F" },
+ { u"dfr", u"\U0001D521" },
+ { u"dharl", u"\u21C3" },
+ { u"dharr", u"\u21C2" },
+ { u"diam", u"\u22C4" },
+ { u"diamond", u"\u22C4" },
+ { u"diamondsuit", u"\u2666" },
+ { u"diams", u"\u2666" },
+ { u"die", u"\u00A8" },
+ { u"digamma", u"\u03DD" },
+ { u"disin", u"\u22F2" },
+ { u"div", u"\u00F7" },
+ { u"divide", u"\u00F7" },
+ { u"divideontimes", u"\u22C7" },
+ { u"divonx", u"\u22C7" },
+ { u"djcy", u"\u0452" },
+ { u"dlcorn", u"\u231E" },
+ { u"dlcrop", u"\u230D" },
+ { u"dollar", u"\u0024" },
+ { u"dopf", u"\U0001D555" },
+ { u"dot", u"\u02D9" },
+ { u"doteq", u"\u2250" },
+ { u"doteqdot", u"\u2251" },
+ { u"dotminus", u"\u2238" },
+ { u"dotplus", u"\u2214" },
+ { u"dotsquare", u"\u22A1" },
+ { u"doublebarwedge", u"\u2306" },
+ { u"downarrow", u"\u2193" },
+ { u"downdownarrows", u"\u21CA" },
+ { u"downharpoonleft", u"\u21C3" },
+ { u"downharpoonright", u"\u21C2" },
+ { u"drbkarow", u"\u2910" },
+ { u"drcorn", u"\u231F" },
+ { u"drcrop", u"\u230C" },
+ { u"dscr", u"\U0001D4B9" },
+ { u"dscy", u"\u0455" },
+ { u"dsol", u"\u29F6" },
+ { u"dstrok", u"\u0111" },
+ { u"dtdot", u"\u22F1" },
+ { u"dtri", u"\u25BF" },
+ { u"dtrif", u"\u25BE" },
+ { u"duarr", u"\u21F5" },
+ { u"duhar", u"\u296F" },
+ { u"dwangle", u"\u29A6" },
+ { u"dzcy", u"\u045F" },
+ { u"dzigrarr", u"\u27FF" },
+ { u"eDDot", u"\u2A77" },
+ { u"eDot", u"\u2251" },
+ { u"eacute", u"\u00E9" },
+ { u"easter", u"\u2A6E" },
+ { u"ecaron", u"\u011B" },
+ { u"ecir", u"\u2256" },
+ { u"ecirc", u"\u00EA" },
+ { u"ecolon", u"\u2255" },
+ { u"ecy", u"\u044D" },
+ { u"edot", u"\u0117" },
+ { u"ee", u"\u2147" },
+ { u"efDot", u"\u2252" },
+ { u"efr", u"\U0001D522" },
+ { u"eg", u"\u2A9A" },
+ { u"egrave", u"\u00E8" },
+ { u"egs", u"\u2A96" },
+ { u"egsdot", u"\u2A98" },
+ { u"el", u"\u2A99" },
+ { u"elinters", u"\u23E7" },
+ { u"ell", u"\u2113" },
+ { u"els", u"\u2A95" },
+ { u"elsdot", u"\u2A97" },
+ { u"emacr", u"\u0113" },
+ { u"empty", u"\u2205" },
+ { u"emptyset", u"\u2205" },
+ { u"emptyv", u"\u2205" },
+ { u"emsp", u"\u2003" },
+ { u"emsp13", u"\u2004" },
+ { u"emsp14", u"\u2005" },
+ { u"eng", u"\u014B" },
+ { u"ensp", u"\u2002" },
+ { u"eogon", u"\u0119" },
+ { u"eopf", u"\U0001D556" },
+ { u"epar", u"\u22D5" },
+ { u"eparsl", u"\u29E3" },
+ { u"eplus", u"\u2A71" },
+ { u"epsi", u"\u03B5" },
+ { u"epsilon", u"\u03B5" },
+ { u"epsiv", u"\u03F5" },
+ { u"eqcirc", u"\u2256" },
+ { u"eqcolon", u"\u2255" },
+ { u"eqsim", u"\u2242" },
+ { u"eqslantgtr", u"\u2A96" },
+ { u"eqslantless", u"\u2A95" },
+ { u"equals", u"\u003D" },
+ { u"equest", u"\u225F" },
+ { u"equiv", u"\u2261" },
+ { u"equivDD", u"\u2A78" },
+ { u"eqvparsl", u"\u29E5" },
+ { u"erDot", u"\u2253" },
+ { u"erarr", u"\u2971" },
+ { u"escr", u"\u212F" },
+ { u"esdot", u"\u2250" },
+ { u"esim", u"\u2242" },
+ { u"eta", u"\u03B7" },
+ { u"eth", u"\u00F0" },
+ { u"euml", u"\u00EB" },
+ { u"euro", u"\u20AC" },
+ { u"excl", u"\u0021" },
+ { u"exist", u"\u2203" },
+ { u"expectation", u"\u2130" },
+ { u"exponentiale", u"\u2147" },
+ { u"fallingdotseq", u"\u2252" },
+ { u"fcy", u"\u0444" },
+ { u"female", u"\u2640" },
+ { u"ffilig", u"\uFB03" },
+ { u"fflig", u"\uFB00" },
+ { u"ffllig", u"\uFB04" },
+ { u"ffr", u"\U0001D523" },
+ { u"filig", u"\uFB01" },
+ { u"fjlig", u"\u0066\u006A" },
+ { u"flat", u"\u266D" },
+ { u"fllig", u"\uFB02" },
+ { u"fltns", u"\u25B1" },
+ { u"fnof", u"\u0192" },
+ { u"fopf", u"\U0001D557" },
+ { u"forall", u"\u2200" },
+ { u"fork", u"\u22D4" },
+ { u"forkv", u"\u2AD9" },
+ { u"fpartint", u"\u2A0D" },
+ { u"frac12", u"\u00BD" },
+ { u"frac13", u"\u2153" },
+ { u"frac14", u"\u00BC" },
+ { u"frac15", u"\u2155" },
+ { u"frac16", u"\u2159" },
+ { u"frac18", u"\u215B" },
+ { u"frac23", u"\u2154" },
+ { u"frac25", u"\u2156" },
+ { u"frac34", u"\u00BE" },
+ { u"frac35", u"\u2157" },
+ { u"frac38", u"\u215C" },
+ { u"frac45", u"\u2158" },
+ { u"frac56", u"\u215A" },
+ { u"frac58", u"\u215D" },
+ { u"frac78", u"\u215E" },
+ { u"frasl", u"\u2044" },
+ { u"frown", u"\u2322" },
+ { u"fscr", u"\U0001D4BB" },
+ { u"gE", u"\u2267" },
+ { u"gEl", u"\u2A8C" },
+ { u"gacute", u"\u01F5" },
+ { u"gamma", u"\u03B3" },
+ { u"gammad", u"\u03DD" },
+ { u"gap", u"\u2A86" },
+ { u"gbreve", u"\u011F" },
+ { u"gcirc", u"\u011D" },
+ { u"gcy", u"\u0433" },
+ { u"gdot", u"\u0121" },
+ { u"ge", u"\u2265" },
+ { u"gel", u"\u22DB" },
+ { u"geq", u"\u2265" },
+ { u"geqq", u"\u2267" },
+ { u"geqslant", u"\u2A7E" },
+ { u"ges", u"\u2A7E" },
+ { u"gescc", u"\u2AA9" },
+ { u"gesdot", u"\u2A80" },
+ { u"gesdoto", u"\u2A82" },
+ { u"gesdotol", u"\u2A84" },
+ { u"gesl", u"\u22DB\uFE00" },
+ { u"gesles", u"\u2A94" },
+ { u"gfr", u"\U0001D524" },
+ { u"gg", u"\u226B" },
+ { u"ggg", u"\u22D9" },
+ { u"gimel", u"\u2137" },
+ { u"gjcy", u"\u0453" },
+ { u"gl", u"\u2277" },
+ { u"glE", u"\u2A92" },
+ { u"gla", u"\u2AA5" },
+ { u"glj", u"\u2AA4" },
+ { u"gnE", u"\u2269" },
+ { u"gnap", u"\u2A8A" },
+ { u"gnapprox", u"\u2A8A" },
+ { u"gne", u"\u2A88" },
+ { u"gneq", u"\u2A88" },
+ { u"gneqq", u"\u2269" },
+ { u"gnsim", u"\u22E7" },
+ { u"gopf", u"\U0001D558" },
+ { u"grave", u"\u0060" },
+ { u"gscr", u"\u210A" },
+ { u"gsim", u"\u2273" },
+ { u"gsime", u"\u2A8E" },
+ { u"gsiml", u"\u2A90" },
+ { u"gt", u"\u003E" },
+ { u"gtcc", u"\u2AA7" },
+ { u"gtcir", u"\u2A7A" },
+ { u"gtdot", u"\u22D7" },
+ { u"gtlPar", u"\u2995" },
+ { u"gtquest", u"\u2A7C" },
+ { u"gtrapprox", u"\u2A86" },
+ { u"gtrarr", u"\u2978" },
+ { u"gtrdot", u"\u22D7" },
+ { u"gtreqless", u"\u22DB" },
+ { u"gtreqqless", u"\u2A8C" },
+ { u"gtrless", u"\u2277" },
+ { u"gtrsim", u"\u2273" },
+ { u"gvertneqq", u"\u2269\uFE00" },
+ { u"gvnE", u"\u2269\uFE00" },
+ { u"hArr", u"\u21D4" },
+ { u"hairsp", u"\u200A" },
+ { u"half", u"\u00BD" },
+ { u"hamilt", u"\u210B" },
+ { u"hardcy", u"\u044A" },
+ { u"harr", u"\u2194" },
+ { u"harrcir", u"\u2948" },
+ { u"harrw", u"\u21AD" },
+ { u"hbar", u"\u210F" },
+ { u"hcirc", u"\u0125" },
+ { u"hearts", u"\u2665" },
+ { u"heartsuit", u"\u2665" },
+ { u"hellip", u"\u2026" },
+ { u"hercon", u"\u22B9" },
+ { u"hfr", u"\U0001D525" },
+ { u"hksearow", u"\u2925" },
+ { u"hkswarow", u"\u2926" },
+ { u"hoarr", u"\u21FF" },
+ { u"homtht", u"\u223B" },
+ { u"hookleftarrow", u"\u21A9" },
+ { u"hookrightarrow", u"\u21AA" },
+ { u"hopf", u"\U0001D559" },
+ { u"horbar", u"\u2015" },
+ { u"hscr", u"\U0001D4BD" },
+ { u"hslash", u"\u210F" },
+ { u"hstrok", u"\u0127" },
+ { u"hybull", u"\u2043" },
+ { u"hyphen", u"\u2010" },
+ { u"iacute", u"\u00ED" },
+ { u"ic", u"\u2063" },
+ { u"icirc", u"\u00EE" },
+ { u"icy", u"\u0438" },
+ { u"iecy", u"\u0435" },
+ { u"iexcl", u"\u00A1" },
+ { u"iff", u"\u21D4" },
+ { u"ifr", u"\U0001D526" },
+ { u"igrave", u"\u00EC" },
+ { u"ii", u"\u2148" },
+ { u"iiiint", u"\u2A0C" },
+ { u"iiint", u"\u222D" },
+ { u"iinfin", u"\u29DC" },
+ { u"iiota", u"\u2129" },
+ { u"ijlig", u"\u0133" },
+ { u"imacr", u"\u012B" },
+ { u"image", u"\u2111" },
+ { u"imagline", u"\u2110" },
+ { u"imagpart", u"\u2111" },
+ { u"imath", u"\u0131" },
+ { u"imof", u"\u22B7" },
+ { u"imped", u"\u01B5" },
+ { u"in", u"\u2208" },
+ { u"incare", u"\u2105" },
+ { u"infin", u"\u221E" },
+ { u"infintie", u"\u29DD" },
+ { u"inodot", u"\u0131" },
+ { u"int", u"\u222B" },
+ { u"intcal", u"\u22BA" },
+ { u"integers", u"\u2124" },
+ { u"intercal", u"\u22BA" },
+ { u"intlarhk", u"\u2A17" },
+ { u"intprod", u"\u2A3C" },
+ { u"iocy", u"\u0451" },
+ { u"iogon", u"\u012F" },
+ { u"iopf", u"\U0001D55A" },
+ { u"iota", u"\u03B9" },
+ { u"iprod", u"\u2A3C" },
+ { u"iquest", u"\u00BF" },
+ { u"iscr", u"\U0001D4BE" },
+ { u"isin", u"\u2208" },
+ { u"isinE", u"\u22F9" },
+ { u"isindot", u"\u22F5" },
+ { u"isins", u"\u22F4" },
+ { u"isinsv", u"\u22F3" },
+ { u"isinv", u"\u2208" },
+ { u"it", u"\u2062" },
+ { u"itilde", u"\u0129" },
+ { u"iukcy", u"\u0456" },
+ { u"iuml", u"\u00EF" },
+ { u"jcirc", u"\u0135" },
+ { u"jcy", u"\u0439" },
+ { u"jfr", u"\U0001D527" },
+ { u"jmath", u"\u0237" },
+ { u"jopf", u"\U0001D55B" },
+ { u"jscr", u"\U0001D4BF" },
+ { u"jsercy", u"\u0458" },
+ { u"jukcy", u"\u0454" },
+ { u"kappa", u"\u03BA" },
+ { u"kappav", u"\u03F0" },
+ { u"kcedil", u"\u0137" },
+ { u"kcy", u"\u043A" },
+ { u"kfr", u"\U0001D528" },
+ { u"kgreen", u"\u0138" },
+ { u"khcy", u"\u0445" },
+ { u"kjcy", u"\u045C" },
+ { u"kopf", u"\U0001D55C" },
+ { u"kscr", u"\U0001D4C0" },
+ { u"lAarr", u"\u21DA" },
+ { u"lArr", u"\u21D0" },
+ { u"lAtail", u"\u291B" },
+ { u"lBarr", u"\u290E" },
+ { u"lE", u"\u2266" },
+ { u"lEg", u"\u2A8B" },
+ { u"lHar", u"\u2962" },
+ { u"lacute", u"\u013A" },
+ { u"laemptyv", u"\u29B4" },
+ { u"lagran", u"\u2112" },
+ { u"lambda", u"\u03BB" },
+ { u"lang", u"\u27E8" },
+ { u"langd", u"\u2991" },
+ { u"langle", u"\u27E8" },
+ { u"lap", u"\u2A85" },
+ { u"laquo", u"\u00AB" },
+ { u"larr", u"\u2190" },
+ { u"larrb", u"\u21E4" },
+ { u"larrbfs", u"\u291F" },
+ { u"larrfs", u"\u291D" },
+ { u"larrhk", u"\u21A9" },
+ { u"larrlp", u"\u21AB" },
+ { u"larrpl", u"\u2939" },
+ { u"larrsim", u"\u2973" },
+ { u"larrtl", u"\u21A2" },
+ { u"lat", u"\u2AAB" },
+ { u"latail", u"\u2919" },
+ { u"late", u"\u2AAD" },
+ { u"lates", u"\u2AAD\uFE00" },
+ { u"lbarr", u"\u290C" },
+ { u"lbbrk", u"\u2772" },
+ { u"lbrace", u"\u007B" },
+ { u"lbrack", u"\u005B" },
+ { u"lbrke", u"\u298B" },
+ { u"lbrksld", u"\u298F" },
+ { u"lbrkslu", u"\u298D" },
+ { u"lcaron", u"\u013E" },
+ { u"lcedil", u"\u013C" },
+ { u"lceil", u"\u2308" },
+ { u"lcub", u"\u007B" },
+ { u"lcy", u"\u043B" },
+ { u"ldca", u"\u2936" },
+ { u"ldquo", u"\u201C" },
+ { u"ldquor", u"\u201E" },
+ { u"ldrdhar", u"\u2967" },
+ { u"ldrushar", u"\u294B" },
+ { u"ldsh", u"\u21B2" },
+ { u"le", u"\u2264" },
+ { u"leftarrow", u"\u2190" },
+ { u"leftarrowtail", u"\u21A2" },
+ { u"leftharpoondown", u"\u21BD" },
+ { u"leftharpoonup", u"\u21BC" },
+ { u"leftleftarrows", u"\u21C7" },
+ { u"leftrightarrow", u"\u2194" },
+ { u"leftrightarrows", u"\u21C6" },
+ { u"leftrightharpoons", u"\u21CB" },
+ { u"leftrightsquigarrow", u"\u21AD" },
+ { u"leftthreetimes", u"\u22CB" },
+ { u"leg", u"\u22DA" },
+ { u"leq", u"\u2264" },
+ { u"leqq", u"\u2266" },
+ { u"leqslant", u"\u2A7D" },
+ { u"les", u"\u2A7D" },
+ { u"lescc", u"\u2AA8" },
+ { u"lesdot", u"\u2A7F" },
+ { u"lesdoto", u"\u2A81" },
+ { u"lesdotor", u"\u2A83" },
+ { u"lesg", u"\u22DA\uFE00" },
+ { u"lesges", u"\u2A93" },
+ { u"lessapprox", u"\u2A85" },
+ { u"lessdot", u"\u22D6" },
+ { u"lesseqgtr", u"\u22DA" },
+ { u"lesseqqgtr", u"\u2A8B" },
+ { u"lessgtr", u"\u2276" },
+ { u"lesssim", u"\u2272" },
+ { u"lfisht", u"\u297C" },
+ { u"lfloor", u"\u230A" },
+ { u"lfr", u"\U0001D529" },
+ { u"lg", u"\u2276" },
+ { u"lgE", u"\u2A91" },
+ { u"lhard", u"\u21BD" },
+ { u"lharu", u"\u21BC" },
+ { u"lharul", u"\u296A" },
+ { u"lhblk", u"\u2584" },
+ { u"ljcy", u"\u0459" },
+ { u"ll", u"\u226A" },
+ { u"llarr", u"\u21C7" },
+ { u"llcorner", u"\u231E" },
+ { u"llhard", u"\u296B" },
+ { u"lltri", u"\u25FA" },
+ { u"lmidot", u"\u0140" },
+ { u"lmoust", u"\u23B0" },
+ { u"lmoustache", u"\u23B0" },
+ { u"lnE", u"\u2268" },
+ { u"lnap", u"\u2A89" },
+ { u"lnapprox", u"\u2A89" },
+ { u"lne", u"\u2A87" },
+ { u"lneq", u"\u2A87" },
+ { u"lneqq", u"\u2268" },
+ { u"lnsim", u"\u22E6" },
+ { u"loang", u"\u27EC" },
+ { u"loarr", u"\u21FD" },
+ { u"lobrk", u"\u27E6" },
+ { u"longleftarrow", u"\u27F5" },
+ { u"longleftrightarrow", u"\u27F7" },
+ { u"longmapsto", u"\u27FC" },
+ { u"longrightarrow", u"\u27F6" },
+ { u"looparrowleft", u"\u21AB" },
+ { u"looparrowright", u"\u21AC" },
+ { u"lopar", u"\u2985" },
+ { u"lopf", u"\U0001D55D" },
+ { u"loplus", u"\u2A2D" },
+ { u"lotimes", u"\u2A34" },
+ { u"lowast", u"\u2217" },
+ { u"lowbar", u"\u005F" },
+ { u"loz", u"\u25CA" },
+ { u"lozenge", u"\u25CA" },
+ { u"lozf", u"\u29EB" },
+ { u"lpar", u"\u0028" },
+ { u"lparlt", u"\u2993" },
+ { u"lrarr", u"\u21C6" },
+ { u"lrcorner", u"\u231F" },
+ { u"lrhar", u"\u21CB" },
+ { u"lrhard", u"\u296D" },
+ { u"lrm", u"\u200E" },
+ { u"lrtri", u"\u22BF" },
+ { u"lsaquo", u"\u2039" },
+ { u"lscr", u"\U0001D4C1" },
+ { u"lsh", u"\u21B0" },
+ { u"lsim", u"\u2272" },
+ { u"lsime", u"\u2A8D" },
+ { u"lsimg", u"\u2A8F" },
+ { u"lsqb", u"\u005B" },
+ { u"lsquo", u"\u2018" },
+ { u"lsquor", u"\u201A" },
+ { u"lstrok", u"\u0142" },
+ { u"lt", u"\u003C" },
+ { u"ltcc", u"\u2AA6" },
+ { u"ltcir", u"\u2A79" },
+ { u"ltdot", u"\u22D6" },
+ { u"lthree", u"\u22CB" },
+ { u"ltimes", u"\u22C9" },
+ { u"ltlarr", u"\u2976" },
+ { u"ltquest", u"\u2A7B" },
+ { u"ltrPar", u"\u2996" },
+ { u"ltri", u"\u25C3" },
+ { u"ltrie", u"\u22B4" },
+ { u"ltrif", u"\u25C2" },
+ { u"lurdshar", u"\u294A" },
+ { u"luruhar", u"\u2966" },
+ { u"lvertneqq", u"\u2268\uFE00" },
+ { u"lvnE", u"\u2268\uFE00" },
+ { u"mDDot", u"\u223A" },
+ { u"macr", u"\u00AF" },
+ { u"male", u"\u2642" },
+ { u"malt", u"\u2720" },
+ { u"maltese", u"\u2720" },
+ { u"map", u"\u21A6" },
+ { u"mapsto", u"\u21A6" },
+ { u"mapstodown", u"\u21A7" },
+ { u"mapstoleft", u"\u21A4" },
+ { u"mapstoup", u"\u21A5" },
+ { u"marker", u"\u25AE" },
+ { u"mcomma", u"\u2A29" },
+ { u"mcy", u"\u043C" },
+ { u"mdash", u"\u2014" },
+ { u"measuredangle", u"\u2221" },
+ { u"mfr", u"\U0001D52A" },
+ { u"mho", u"\u2127" },
+ { u"micro", u"\u00B5" },
+ { u"mid", u"\u2223" },
+ { u"midast", u"\u002A" },
+ { u"midcir", u"\u2AF0" },
+ { u"middot", u"\u00B7" },
+ { u"minus", u"\u2212" },
+ { u"minusb", u"\u229F" },
+ { u"minusd", u"\u2238" },
+ { u"minusdu", u"\u2A2A" },
+ { u"mlcp", u"\u2ADB" },
+ { u"mldr", u"\u2026" },
+ { u"mnplus", u"\u2213" },
+ { u"models", u"\u22A7" },
+ { u"mopf", u"\U0001D55E" },
+ { u"mp", u"\u2213" },
+ { u"mscr", u"\U0001D4C2" },
+ { u"mstpos", u"\u223E" },
+ { u"mu", u"\u03BC" },
+ { u"multimap", u"\u22B8" },
+ { u"mumap", u"\u22B8" },
+ { u"nGg", u"\u22D9\u0338" },
+ { u"nGt", u"\u226B\u20D2" },
+ { u"nGtv", u"\u226B\u0338" },
+ { u"nLeftarrow", u"\u21CD" },
+ { u"nLeftrightarrow", u"\u21CE" },
+ { u"nLl", u"\u22D8\u0338" },
+ { u"nLt", u"\u226A\u20D2" },
+ { u"nLtv", u"\u226A\u0338" },
+ { u"nRightarrow", u"\u21CF" },
+ { u"nVDash", u"\u22AF" },
+ { u"nVdash", u"\u22AE" },
+ { u"nabla", u"\u2207" },
+ { u"nacute", u"\u0144" },
+ { u"nang", u"\u2220\u20D2" },
+ { u"nap", u"\u2249" },
+ { u"napE", u"\u2A70\u0338" },
+ { u"napid", u"\u224B\u0338" },
+ { u"napos", u"\u0149" },
+ { u"napprox", u"\u2249" },
+ { u"natur", u"\u266E" },
+ { u"natural", u"\u266E" },
+ { u"naturals", u"\u2115" },
+ { u"nbsp", u"\u00A0" },
+ { u"nbump", u"\u224E\u0338" },
+ { u"nbumpe", u"\u224F\u0338" },
+ { u"ncap", u"\u2A43" },
+ { u"ncaron", u"\u0148" },
+ { u"ncedil", u"\u0146" },
+ { u"ncong", u"\u2247" },
+ { u"ncongdot", u"\u2A6D\u0338" },
+ { u"ncup", u"\u2A42" },
+ { u"ncy", u"\u043D" },
+ { u"ndash", u"\u2013" },
+ { u"ne", u"\u2260" },
+ { u"neArr", u"\u21D7" },
+ { u"nearhk", u"\u2924" },
+ { u"nearr", u"\u2197" },
+ { u"nearrow", u"\u2197" },
+ { u"nedot", u"\u2250\u0338" },
+ { u"nequiv", u"\u2262" },
+ { u"nesear", u"\u2928" },
+ { u"nesim", u"\u2242\u0338" },
+ { u"nexist", u"\u2204" },
+ { u"nexists", u"\u2204" },
+ { u"nfr", u"\U0001D52B" },
+ { u"ngE", u"\u2267\u0338" },
+ { u"nge", u"\u2271" },
+ { u"ngeq", u"\u2271" },
+ { u"ngeqq", u"\u2267\u0338" },
+ { u"ngeqslant", u"\u2A7E\u0338" },
+ { u"nges", u"\u2A7E\u0338" },
+ { u"ngsim", u"\u2275" },
+ { u"ngt", u"\u226F" },
+ { u"ngtr", u"\u226F" },
+ { u"nhArr", u"\u21CE" },
+ { u"nharr", u"\u21AE" },
+ { u"nhpar", u"\u2AF2" },
+ { u"ni", u"\u220B" },
+ { u"nis", u"\u22FC" },
+ { u"nisd", u"\u22FA" },
+ { u"niv", u"\u220B" },
+ { u"njcy", u"\u045A" },
+ { u"nlArr", u"\u21CD" },
+ { u"nlE", u"\u2266\u0338" },
+ { u"nlarr", u"\u219A" },
+ { u"nldr", u"\u2025" },
+ { u"nle", u"\u2270" },
+ { u"nleftarrow", u"\u219A" },
+ { u"nleftrightarrow", u"\u21AE" },
+ { u"nleq", u"\u2270" },
+ { u"nleqq", u"\u2266\u0338" },
+ { u"nleqslant", u"\u2A7D\u0338" },
+ { u"nles", u"\u2A7D\u0338" },
+ { u"nless", u"\u226E" },
+ { u"nlsim", u"\u2274" },
+ { u"nlt", u"\u226E" },
+ { u"nltri", u"\u22EA" },
+ { u"nltrie", u"\u22EC" },
+ { u"nmid", u"\u2224" },
+ { u"nopf", u"\U0001D55F" },
+ { u"not", u"\u00AC" },
+ { u"notin", u"\u2209" },
+ { u"notinE", u"\u22F9\u0338" },
+ { u"notindot", u"\u22F5\u0338" },
+ { u"notinva", u"\u2209" },
+ { u"notinvb", u"\u22F7" },
+ { u"notinvc", u"\u22F6" },
+ { u"notni", u"\u220C" },
+ { u"notniva", u"\u220C" },
+ { u"notnivb", u"\u22FE" },
+ { u"notnivc", u"\u22FD" },
+ { u"npar", u"\u2226" },
+ { u"nparallel", u"\u2226" },
+ { u"nparsl", u"\u2AFD\u20E5" },
+ { u"npart", u"\u2202\u0338" },
+ { u"npolint", u"\u2A14" },
+ { u"npr", u"\u2280" },
+ { u"nprcue", u"\u22E0" },
+ { u"npre", u"\u2AAF\u0338" },
+ { u"nprec", u"\u2280" },
+ { u"npreceq", u"\u2AAF\u0338" },
+ { u"nrArr", u"\u21CF" },
+ { u"nrarr", u"\u219B" },
+ { u"nrarrc", u"\u2933\u0338" },
+ { u"nrarrw", u"\u219D\u0338" },
+ { u"nrightarrow", u"\u219B" },
+ { u"nrtri", u"\u22EB" },
+ { u"nrtrie", u"\u22ED" },
+ { u"nsc", u"\u2281" },
+ { u"nsccue", u"\u22E1" },
+ { u"nsce", u"\u2AB0\u0338" },
+ { u"nscr", u"\U0001D4C3" },
+ { u"nshortmid", u"\u2224" },
+ { u"nshortparallel", u"\u2226" },
+ { u"nsim", u"\u2241" },
+ { u"nsime", u"\u2244" },
+ { u"nsimeq", u"\u2244" },
+ { u"nsmid", u"\u2224" },
+ { u"nspar", u"\u2226" },
+ { u"nsqsube", u"\u22E2" },
+ { u"nsqsupe", u"\u22E3" },
+ { u"nsub", u"\u2284" },
+ { u"nsubE", u"\u2AC5\u0338" },
+ { u"nsube", u"\u2288" },
+ { u"nsubset", u"\u2282\u20D2" },
+ { u"nsubseteq", u"\u2288" },
+ { u"nsubseteqq", u"\u2AC5\u0338" },
+ { u"nsucc", u"\u2281" },
+ { u"nsucceq", u"\u2AB0\u0338" },
+ { u"nsup", u"\u2285" },
+ { u"nsupE", u"\u2AC6\u0338" },
+ { u"nsupe", u"\u2289" },
+ { u"nsupset", u"\u2283\u20D2" },
+ { u"nsupseteq", u"\u2289" },
+ { u"nsupseteqq", u"\u2AC6\u0338" },
+ { u"ntgl", u"\u2279" },
+ { u"ntilde", u"\u00F1" },
+ { u"ntlg", u"\u2278" },
+ { u"ntriangleleft", u"\u22EA" },
+ { u"ntrianglelefteq", u"\u22EC" },
+ { u"ntriangleright", u"\u22EB" },
+ { u"ntrianglerighteq", u"\u22ED" },
+ { u"nu", u"\u03BD" },
+ { u"num", u"\u0023" },
+ { u"numero", u"\u2116" },
+ { u"numsp", u"\u2007" },
+ { u"nvDash", u"\u22AD" },
+ { u"nvHarr", u"\u2904" },
+ { u"nvap", u"\u224D\u20D2" },
+ { u"nvdash", u"\u22AC" },
+ { u"nvge", u"\u2265\u20D2" },
+ { u"nvgt", u"\u003E\u20D2" },
+ { u"nvinfin", u"\u29DE" },
+ { u"nvlArr", u"\u2902" },
+ { u"nvle", u"\u2264\u20D2" },
+ { u"nvlt", u"\u003C\u20D2" },
+ { u"nvltrie", u"\u22B4\u20D2" },
+ { u"nvrArr", u"\u2903" },
+ { u"nvrtrie", u"\u22B5\u20D2" },
+ { u"nvsim", u"\u223C\u20D2" },
+ { u"nwArr", u"\u21D6" },
+ { u"nwarhk", u"\u2923" },
+ { u"nwarr", u"\u2196" },
+ { u"nwarrow", u"\u2196" },
+ { u"nwnear", u"\u2927" },
+ { u"oS", u"\u24C8" },
+ { u"oacute", u"\u00F3" },
+ { u"oast", u"\u229B" },
+ { u"ocir", u"\u229A" },
+ { u"ocirc", u"\u00F4" },
+ { u"ocy", u"\u043E" },
+ { u"odash", u"\u229D" },
+ { u"odblac", u"\u0151" },
+ { u"odiv", u"\u2A38" },
+ { u"odot", u"\u2299" },
+ { u"odsold", u"\u29BC" },
+ { u"oelig", u"\u0153" },
+ { u"ofcir", u"\u29BF" },
+ { u"ofr", u"\U0001D52C" },
+ { u"ogon", u"\u02DB" },
+ { u"ograve", u"\u00F2" },
+ { u"ogt", u"\u29C1" },
+ { u"ohbar", u"\u29B5" },
+ { u"ohm", u"\u03A9" },
+ { u"oint", u"\u222E" },
+ { u"olarr", u"\u21BA" },
+ { u"olcir", u"\u29BE" },
+ { u"olcross", u"\u29BB" },
+ { u"oline", u"\u203E" },
+ { u"olt", u"\u29C0" },
+ { u"omacr", u"\u014D" },
+ { u"omega", u"\u03C9" },
+ { u"omicron", u"\u03BF" },
+ { u"omid", u"\u29B6" },
+ { u"ominus", u"\u2296" },
+ { u"oopf", u"\U0001D560" },
+ { u"opar", u"\u29B7" },
+ { u"operp", u"\u29B9" },
+ { u"oplus", u"\u2295" },
+ { u"or", u"\u2228" },
+ { u"orarr", u"\u21BB" },
+ { u"ord", u"\u2A5D" },
+ { u"order", u"\u2134" },
+ { u"orderof", u"\u2134" },
+ { u"ordf", u"\u00AA" },
+ { u"ordm", u"\u00BA" },
+ { u"origof", u"\u22B6" },
+ { u"oror", u"\u2A56" },
+ { u"orslope", u"\u2A57" },
+ { u"orv", u"\u2A5B" },
+ { u"oscr", u"\u2134" },
+ { u"oslash", u"\u00F8" },
+ { u"osol", u"\u2298" },
+ { u"otilde", u"\u00F5" },
+ { u"otimes", u"\u2297" },
+ { u"otimesas", u"\u2A36" },
+ { u"ouml", u"\u00F6" },
+ { u"ovbar", u"\u233D" },
+ { u"par", u"\u2225" },
+ { u"para", u"\u00B6" },
+ { u"parallel", u"\u2225" },
+ { u"parsim", u"\u2AF3" },
+ { u"parsl", u"\u2AFD" },
+ { u"part", u"\u2202" },
+ { u"pcy", u"\u043F" },
+ { u"percnt", u"\u0025" },
+ { u"period", u"\u002E" },
+ { u"permil", u"\u2030" },
+ { u"perp", u"\u22A5" },
+ { u"pertenk", u"\u2031" },
+ { u"pfr", u"\U0001D52D" },
+ { u"phi", u"\u03C6" },
+ { u"phiv", u"\u03D5" },
+ { u"phmmat", u"\u2133" },
+ { u"phone", u"\u260E" },
+ { u"pi", u"\u03C0" },
+ { u"pitchfork", u"\u22D4" },
+ { u"piv", u"\u03D6" },
+ { u"planck", u"\u210F" },
+ { u"planckh", u"\u210E" },
+ { u"plankv", u"\u210F" },
+ { u"plus", u"\u002B" },
+ { u"plusacir", u"\u2A23" },
+ { u"plusb", u"\u229E" },
+ { u"pluscir", u"\u2A22" },
+ { u"plusdo", u"\u2214" },
+ { u"plusdu", u"\u2A25" },
+ { u"pluse", u"\u2A72" },
+ { u"plusmn", u"\u00B1" },
+ { u"plussim", u"\u2A26" },
+ { u"plustwo", u"\u2A27" },
+ { u"pm", u"\u00B1" },
+ { u"pointint", u"\u2A15" },
+ { u"popf", u"\U0001D561" },
+ { u"pound", u"\u00A3" },
+ { u"pr", u"\u227A" },
+ { u"prE", u"\u2AB3" },
+ { u"prap", u"\u2AB7" },
+ { u"prcue", u"\u227C" },
+ { u"pre", u"\u2AAF" },
+ { u"prec", u"\u227A" },
+ { u"precapprox", u"\u2AB7" },
+ { u"preccurlyeq", u"\u227C" },
+ { u"preceq", u"\u2AAF" },
+ { u"precnapprox", u"\u2AB9" },
+ { u"precneqq", u"\u2AB5" },
+ { u"precnsim", u"\u22E8" },
+ { u"precsim", u"\u227E" },
+ { u"prime", u"\u2032" },
+ { u"primes", u"\u2119" },
+ { u"prnE", u"\u2AB5" },
+ { u"prnap", u"\u2AB9" },
+ { u"prnsim", u"\u22E8" },
+ { u"prod", u"\u220F" },
+ { u"profalar", u"\u232E" },
+ { u"profline", u"\u2312" },
+ { u"profsurf", u"\u2313" },
+ { u"prop", u"\u221D" },
+ { u"propto", u"\u221D" },
+ { u"prsim", u"\u227E" },
+ { u"prurel", u"\u22B0" },
+ { u"pscr", u"\U0001D4C5" },
+ { u"psi", u"\u03C8" },
+ { u"puncsp", u"\u2008" },
+ { u"qfr", u"\U0001D52E" },
+ { u"qint", u"\u2A0C" },
+ { u"qopf", u"\U0001D562" },
+ { u"qprime", u"\u2057" },
+ { u"qscr", u"\U0001D4C6" },
+ { u"quaternions", u"\u210D" },
+ { u"quatint", u"\u2A16" },
+ { u"quest", u"\u003F" },
+ { u"questeq", u"\u225F" },
+ { u"quot", u"\u0022" },
+ { u"rAarr", u"\u21DB" },
+ { u"rArr", u"\u21D2" },
+ { u"rAtail", u"\u291C" },
+ { u"rBarr", u"\u290F" },
+ { u"rHar", u"\u2964" },
+ { u"race", u"\u223D\u0331" },
+ { u"racute", u"\u0155" },
+ { u"radic", u"\u221A" },
+ { u"raemptyv", u"\u29B3" },
+ { u"rang", u"\u27E9" },
+ { u"rangd", u"\u2992" },
+ { u"range", u"\u29A5" },
+ { u"rangle", u"\u27E9" },
+ { u"raquo", u"\u00BB" },
+ { u"rarr", u"\u2192" },
+ { u"rarrap", u"\u2975" },
+ { u"rarrb", u"\u21E5" },
+ { u"rarrbfs", u"\u2920" },
+ { u"rarrc", u"\u2933" },
+ { u"rarrfs", u"\u291E" },
+ { u"rarrhk", u"\u21AA" },
+ { u"rarrlp", u"\u21AC" },
+ { u"rarrpl", u"\u2945" },
+ { u"rarrsim", u"\u2974" },
+ { u"rarrtl", u"\u21A3" },
+ { u"rarrw", u"\u219D" },
+ { u"ratail", u"\u291A" },
+ { u"ratio", u"\u2236" },
+ { u"rationals", u"\u211A" },
+ { u"rbarr", u"\u290D" },
+ { u"rbbrk", u"\u2773" },
+ { u"rbrace", u"\u007D" },
+ { u"rbrack", u"\u005D" },
+ { u"rbrke", u"\u298C" },
+ { u"rbrksld", u"\u298E" },
+ { u"rbrkslu", u"\u2990" },
+ { u"rcaron", u"\u0159" },
+ { u"rcedil", u"\u0157" },
+ { u"rceil", u"\u2309" },
+ { u"rcub", u"\u007D" },
+ { u"rcy", u"\u0440" },
+ { u"rdca", u"\u2937" },
+ { u"rdldhar", u"\u2969" },
+ { u"rdquo", u"\u201D" },
+ { u"rdquor", u"\u201D" },
+ { u"rdsh", u"\u21B3" },
+ { u"real", u"\u211C" },
+ { u"realine", u"\u211B" },
+ { u"realpart", u"\u211C" },
+ { u"reals", u"\u211D" },
+ { u"rect", u"\u25AD" },
+ { u"reg", u"\u00AE" },
+ { u"rfisht", u"\u297D" },
+ { u"rfloor", u"\u230B" },
+ { u"rfr", u"\U0001D52F" },
+ { u"rhard", u"\u21C1" },
+ { u"rharu", u"\u21C0" },
+ { u"rharul", u"\u296C" },
+ { u"rho", u"\u03C1" },
+ { u"rhov", u"\u03F1" },
+ { u"rightarrow", u"\u2192" },
+ { u"rightarrowtail", u"\u21A3" },
+ { u"rightharpoondown", u"\u21C1" },
+ { u"rightharpoonup", u"\u21C0" },
+ { u"rightleftarrows", u"\u21C4" },
+ { u"rightleftharpoons", u"\u21CC" },
+ { u"rightrightarrows", u"\u21C9" },
+ { u"rightsquigarrow", u"\u219D" },
+ { u"rightthreetimes", u"\u22CC" },
+ { u"ring", u"\u02DA" },
+ { u"risingdotseq", u"\u2253" },
+ { u"rlarr", u"\u21C4" },
+ { u"rlhar", u"\u21CC" },
+ { u"rlm", u"\u200F" },
+ { u"rmoust", u"\u23B1" },
+ { u"rmoustache", u"\u23B1" },
+ { u"rnmid", u"\u2AEE" },
+ { u"roang", u"\u27ED" },
+ { u"roarr", u"\u21FE" },
+ { u"robrk", u"\u27E7" },
+ { u"ropar", u"\u2986" },
+ { u"ropf", u"\U0001D563" },
+ { u"roplus", u"\u2A2E" },
+ { u"rotimes", u"\u2A35" },
+ { u"rpar", u"\u0029" },
+ { u"rpargt", u"\u2994" },
+ { u"rppolint", u"\u2A12" },
+ { u"rrarr", u"\u21C9" },
+ { u"rsaquo", u"\u203A" },
+ { u"rscr", u"\U0001D4C7" },
+ { u"rsh", u"\u21B1" },
+ { u"rsqb", u"\u005D" },
+ { u"rsquo", u"\u2019" },
+ { u"rsquor", u"\u2019" },
+ { u"rthree", u"\u22CC" },
+ { u"rtimes", u"\u22CA" },
+ { u"rtri", u"\u25B9" },
+ { u"rtrie", u"\u22B5" },
+ { u"rtrif", u"\u25B8" },
+ { u"rtriltri", u"\u29CE" },
+ { u"ruluhar", u"\u2968" },
+ { u"rx", u"\u211E" },
+ { u"sacute", u"\u015B" },
+ { u"sbquo", u"\u201A" },
+ { u"sc", u"\u227B" },
+ { u"scE", u"\u2AB4" },
+ { u"scap", u"\u2AB8" },
+ { u"scaron", u"\u0161" },
+ { u"sccue", u"\u227D" },
+ { u"sce", u"\u2AB0" },
+ { u"scedil", u"\u015F" },
+ { u"scirc", u"\u015D" },
+ { u"scnE", u"\u2AB6" },
+ { u"scnap", u"\u2ABA" },
+ { u"scnsim", u"\u22E9" },
+ { u"scpolint", u"\u2A13" },
+ { u"scsim", u"\u227F" },
+ { u"scy", u"\u0441" },
+ { u"sdot", u"\u22C5" },
+ { u"sdotb", u"\u22A1" },
+ { u"sdote", u"\u2A66" },
+ { u"seArr", u"\u21D8" },
+ { u"searhk", u"\u2925" },
+ { u"searr", u"\u2198" },
+ { u"searrow", u"\u2198" },
+ { u"sect", u"\u00A7" },
+ { u"semi", u"\u003B" },
+ { u"seswar", u"\u2929" },
+ { u"setminus", u"\u2216" },
+ { u"setmn", u"\u2216" },
+ { u"sext", u"\u2736" },
+ { u"sfr", u"\U0001D530" },
+ { u"sfrown", u"\u2322" },
+ { u"sharp", u"\u266F" },
+ { u"shchcy", u"\u0449" },
+ { u"shcy", u"\u0448" },
+ { u"shortmid", u"\u2223" },
+ { u"shortparallel", u"\u2225" },
+ { u"shy", u"\u00AD" },
+ { u"sigma", u"\u03C3" },
+ { u"sigmaf", u"\u03C2" },
+ { u"sigmav", u"\u03C2" },
+ { u"sim", u"\u223C" },
+ { u"simdot", u"\u2A6A" },
+ { u"sime", u"\u2243" },
+ { u"simeq", u"\u2243" },
+ { u"simg", u"\u2A9E" },
+ { u"simgE", u"\u2AA0" },
+ { u"siml", u"\u2A9D" },
+ { u"simlE", u"\u2A9F" },
+ { u"simne", u"\u2246" },
+ { u"simplus", u"\u2A24" },
+ { u"simrarr", u"\u2972" },
+ { u"slarr", u"\u2190" },
+ { u"smallsetminus", u"\u2216" },
+ { u"smashp", u"\u2A33" },
+ { u"smeparsl", u"\u29E4" },
+ { u"smid", u"\u2223" },
+ { u"smile", u"\u2323" },
+ { u"smt", u"\u2AAA" },
+ { u"smte", u"\u2AAC" },
+ { u"smtes", u"\u2AAC\uFE00" },
+ { u"softcy", u"\u044C" },
+ { u"sol", u"\u002F" },
+ { u"solb", u"\u29C4" },
+ { u"solbar", u"\u233F" },
+ { u"sopf", u"\U0001D564" },
+ { u"spades", u"\u2660" },
+ { u"spadesuit", u"\u2660" },
+ { u"spar", u"\u2225" },
+ { u"sqcap", u"\u2293" },
+ { u"sqcaps", u"\u2293\uFE00" },
+ { u"sqcup", u"\u2294" },
+ { u"sqcups", u"\u2294\uFE00" },
+ { u"sqsub", u"\u228F" },
+ { u"sqsube", u"\u2291" },
+ { u"sqsubset", u"\u228F" },
+ { u"sqsubseteq", u"\u2291" },
+ { u"sqsup", u"\u2290" },
+ { u"sqsupe", u"\u2292" },
+ { u"sqsupset", u"\u2290" },
+ { u"sqsupseteq", u"\u2292" },
+ { u"squ", u"\u25A1" },
+ { u"square", u"\u25A1" },
+ { u"squarf", u"\u25AA" },
+ { u"squf", u"\u25AA" },
+ { u"srarr", u"\u2192" },
+ { u"sscr", u"\U0001D4C8" },
+ { u"ssetmn", u"\u2216" },
+ { u"ssmile", u"\u2323" },
+ { u"sstarf", u"\u22C6" },
+ { u"star", u"\u2606" },
+ { u"starf", u"\u2605" },
+ { u"straightepsilon", u"\u03F5" },
+ { u"straightphi", u"\u03D5" },
+ { u"strns", u"\u00AF" },
+ { u"sub", u"\u2282" },
+ { u"subE", u"\u2AC5" },
+ { u"subdot", u"\u2ABD" },
+ { u"sube", u"\u2286" },
+ { u"subedot", u"\u2AC3" },
+ { u"submult", u"\u2AC1" },
+ { u"subnE", u"\u2ACB" },
+ { u"subne", u"\u228A" },
+ { u"subplus", u"\u2ABF" },
+ { u"subrarr", u"\u2979" },
+ { u"subset", u"\u2282" },
+ { u"subseteq", u"\u2286" },
+ { u"subseteqq", u"\u2AC5" },
+ { u"subsetneq", u"\u228A" },
+ { u"subsetneqq", u"\u2ACB" },
+ { u"subsim", u"\u2AC7" },
+ { u"subsub", u"\u2AD5" },
+ { u"subsup", u"\u2AD3" },
+ { u"succ", u"\u227B" },
+ { u"succapprox", u"\u2AB8" },
+ { u"succcurlyeq", u"\u227D" },
+ { u"succeq", u"\u2AB0" },
+ { u"succnapprox", u"\u2ABA" },
+ { u"succneqq", u"\u2AB6" },
+ { u"succnsim", u"\u22E9" },
+ { u"succsim", u"\u227F" },
+ { u"sum", u"\u2211" },
+ { u"sung", u"\u266A" },
+ { u"sup", u"\u2283" },
+ { u"sup1", u"\u00B9" },
+ { u"sup2", u"\u00B2" },
+ { u"sup3", u"\u00B3" },
+ { u"supE", u"\u2AC6" },
+ { u"supdot", u"\u2ABE" },
+ { u"supdsub", u"\u2AD8" },
+ { u"supe", u"\u2287" },
+ { u"supedot", u"\u2AC4" },
+ { u"suphsol", u"\u27C9" },
+ { u"suphsub", u"\u2AD7" },
+ { u"suplarr", u"\u297B" },
+ { u"supmult", u"\u2AC2" },
+ { u"supnE", u"\u2ACC" },
+ { u"supne", u"\u228B" },
+ { u"supplus", u"\u2AC0" },
+ { u"supset", u"\u2283" },
+ { u"supseteq", u"\u2287" },
+ { u"supseteqq", u"\u2AC6" },
+ { u"supsetneq", u"\u228B" },
+ { u"supsetneqq", u"\u2ACC" },
+ { u"supsim", u"\u2AC8" },
+ { u"supsub", u"\u2AD4" },
+ { u"supsup", u"\u2AD6" },
+ { u"swArr", u"\u21D9" },
+ { u"swarhk", u"\u2926" },
+ { u"swarr", u"\u2199" },
+ { u"swarrow", u"\u2199" },
+ { u"swnwar", u"\u292A" },
+ { u"szlig", u"\u00DF" },
+ { u"target", u"\u2316" },
+ { u"tau", u"\u03C4" },
+ { u"tbrk", u"\u23B4" },
+ { u"tcaron", u"\u0165" },
+ { u"tcedil", u"\u0163" },
+ { u"tcy", u"\u0442" },
+ { u"tdot", u"\u20DB" },
+ { u"telrec", u"\u2315" },
+ { u"tfr", u"\U0001D531" },
+ { u"there4", u"\u2234" },
+ { u"therefore", u"\u2234" },
+ { u"theta", u"\u03B8" },
+ { u"thetasym", u"\u03D1" },
+ { u"thetav", u"\u03D1" },
+ { u"thickapprox", u"\u2248" },
+ { u"thicksim", u"\u223C" },
+ { u"thinsp", u"\u2009" },
+ { u"thkap", u"\u2248" },
+ { u"thksim", u"\u223C" },
+ { u"thorn", u"\u00FE" },
+ { u"tilde", u"\u02DC" },
+ { u"times", u"\u00D7" },
+ { u"timesb", u"\u22A0" },
+ { u"timesbar", u"\u2A31" },
+ { u"timesd", u"\u2A30" },
+ { u"tint", u"\u222D" },
+ { u"toea", u"\u2928" },
+ { u"top", u"\u22A4" },
+ { u"topbot", u"\u2336" },
+ { u"topcir", u"\u2AF1" },
+ { u"topf", u"\U0001D565" },
+ { u"topfork", u"\u2ADA" },
+ { u"tosa", u"\u2929" },
+ { u"tprime", u"\u2034" },
+ { u"trade", u"\u2122" },
+ { u"triangle", u"\u25B5" },
+ { u"triangledown", u"\u25BF" },
+ { u"triangleleft", u"\u25C3" },
+ { u"trianglelefteq", u"\u22B4" },
+ { u"triangleq", u"\u225C" },
+ { u"triangleright", u"\u25B9" },
+ { u"trianglerighteq", u"\u22B5" },
+ { u"tridot", u"\u25EC" },
+ { u"trie", u"\u225C" },
+ { u"triminus", u"\u2A3A" },
+ { u"triplus", u"\u2A39" },
+ { u"trisb", u"\u29CD" },
+ { u"tritime", u"\u2A3B" },
+ { u"trpezium", u"\u23E2" },
+ { u"tscr", u"\U0001D4C9" },
+ { u"tscy", u"\u0446" },
+ { u"tshcy", u"\u045B" },
+ { u"tstrok", u"\u0167" },
+ { u"twixt", u"\u226C" },
+ { u"twoheadleftarrow", u"\u219E" },
+ { u"twoheadrightarrow", u"\u21A0" },
+ { u"uArr", u"\u21D1" },
+ { u"uHar", u"\u2963" },
+ { u"uacute", u"\u00FA" },
+ { u"uarr", u"\u2191" },
+ { u"ubrcy", u"\u045E" },
+ { u"ubreve", u"\u016D" },
+ { u"ucirc", u"\u00FB" },
+ { u"ucy", u"\u0443" },
+ { u"udarr", u"\u21C5" },
+ { u"udblac", u"\u0171" },
+ { u"udhar", u"\u296E" },
+ { u"ufisht", u"\u297E" },
+ { u"ufr", u"\U0001D532" },
+ { u"ugrave", u"\u00F9" },
+ { u"uharl", u"\u21BF" },
+ { u"uharr", u"\u21BE" },
+ { u"uhblk", u"\u2580" },
+ { u"ulcorn", u"\u231C" },
+ { u"ulcorner", u"\u231C" },
+ { u"ulcrop", u"\u230F" },
+ { u"ultri", u"\u25F8" },
+ { u"umacr", u"\u016B" },
+ { u"uml", u"\u00A8" },
+ { u"uogon", u"\u0173" },
+ { u"uopf", u"\U0001D566" },
+ { u"uparrow", u"\u2191" },
+ { u"updownarrow", u"\u2195" },
+ { u"upharpoonleft", u"\u21BF" },
+ { u"upharpoonright", u"\u21BE" },
+ { u"uplus", u"\u228E" },
+ { u"upsi", u"\u03C5" },
+ { u"upsih", u"\u03D2" },
+ { u"upsilon", u"\u03C5" },
+ { u"upuparrows", u"\u21C8" },
+ { u"urcorn", u"\u231D" },
+ { u"urcorner", u"\u231D" },
+ { u"urcrop", u"\u230E" },
+ { u"uring", u"\u016F" },
+ { u"urtri", u"\u25F9" },
+ { u"uscr", u"\U0001D4CA" },
+ { u"utdot", u"\u22F0" },
+ { u"utilde", u"\u0169" },
+ { u"utri", u"\u25B5" },
+ { u"utrif", u"\u25B4" },
+ { u"uuarr", u"\u21C8" },
+ { u"uuml", u"\u00FC" },
+ { u"uwangle", u"\u29A7" },
+ { u"vArr", u"\u21D5" },
+ { u"vBar", u"\u2AE8" },
+ { u"vBarv", u"\u2AE9" },
+ { u"vDash", u"\u22A8" },
+ { u"vangrt", u"\u299C" },
+ { u"varepsilon", u"\u03F5" },
+ { u"varkappa", u"\u03F0" },
+ { u"varnothing", u"\u2205" },
+ { u"varphi", u"\u03D5" },
+ { u"varpi", u"\u03D6" },
+ { u"varpropto", u"\u221D" },
+ { u"varr", u"\u2195" },
+ { u"varrho", u"\u03F1" },
+ { u"varsigma", u"\u03C2" },
+ { u"varsubsetneq", u"\u228A\uFE00" },
+ { u"varsubsetneqq", u"\u2ACB\uFE00" },
+ { u"varsupsetneq", u"\u228B\uFE00" },
+ { u"varsupsetneqq", u"\u2ACC\uFE00" },
+ { u"vartheta", u"\u03D1" },
+ { u"vartriangleleft", u"\u22B2" },
+ { u"vartriangleright", u"\u22B3" },
+ { u"vcy", u"\u0432" },
+ { u"vdash", u"\u22A2" },
+ { u"vee", u"\u2228" },
+ { u"veebar", u"\u22BB" },
+ { u"veeeq", u"\u225A" },
+ { u"vellip", u"\u22EE" },
+ { u"verbar", u"\u007C" },
+ { u"vert", u"\u007C" },
+ { u"vfr", u"\U0001D533" },
+ { u"vltri", u"\u22B2" },
+ { u"vnsub", u"\u2282\u20D2" },
+ { u"vnsup", u"\u2283\u20D2" },
+ { u"vopf", u"\U0001D567" },
+ { u"vprop", u"\u221D" },
+ { u"vrtri", u"\u22B3" },
+ { u"vscr", u"\U0001D4CB" },
+ { u"vsubnE", u"\u2ACB\uFE00" },
+ { u"vsubne", u"\u228A\uFE00" },
+ { u"vsupnE", u"\u2ACC\uFE00" },
+ { u"vsupne", u"\u228B\uFE00" },
+ { u"vzigzag", u"\u299A" },
+ { u"wcirc", u"\u0175" },
+ { u"wedbar", u"\u2A5F" },
+ { u"wedge", u"\u2227" },
+ { u"wedgeq", u"\u2259" },
+ { u"weierp", u"\u2118" },
+ { u"wfr", u"\U0001D534" },
+ { u"wopf", u"\U0001D568" },
+ { u"wp", u"\u2118" },
+ { u"wr", u"\u2240" },
+ { u"wreath", u"\u2240" },
+ { u"wscr", u"\U0001D4CC" },
+ { u"xcap", u"\u22C2" },
+ { u"xcirc", u"\u25EF" },
+ { u"xcup", u"\u22C3" },
+ { u"xdtri", u"\u25BD" },
+ { u"xfr", u"\U0001D535" },
+ { u"xhArr", u"\u27FA" },
+ { u"xharr", u"\u27F7" },
+ { u"xi", u"\u03BE" },
+ { u"xlArr", u"\u27F8" },
+ { u"xlarr", u"\u27F5" },
+ { u"xmap", u"\u27FC" },
+ { u"xnis", u"\u22FB" },
+ { u"xodot", u"\u2A00" },
+ { u"xopf", u"\U0001D569" },
+ { u"xoplus", u"\u2A01" },
+ { u"xotime", u"\u2A02" },
+ { u"xrArr", u"\u27F9" },
+ { u"xrarr", u"\u27F6" },
+ { u"xscr", u"\U0001D4CD" },
+ { u"xsqcup", u"\u2A06" },
+ { u"xuplus", u"\u2A04" },
+ { u"xutri", u"\u25B3" },
+ { u"xvee", u"\u22C1" },
+ { u"xwedge", u"\u22C0" },
+ { u"yacute", u"\u00FD" },
+ { u"yacy", u"\u044F" },
+ { u"ycirc", u"\u0177" },
+ { u"ycy", u"\u044B" },
+ { u"yen", u"\u00A5" },
+ { u"yfr", u"\U0001D536" },
+ { u"yicy", u"\u0457" },
+ { u"yopf", u"\U0001D56A" },
+ { u"yscr", u"\U0001D4CE" },
+ { u"yucy", u"\u044E" },
+ { u"yuml", u"\u00FF" },
+ { u"zacute", u"\u017A" },
+ { u"zcaron", u"\u017E" },
+ { u"zcy", u"\u0437" },
+ { u"zdot", u"\u017C" },
+ { u"zeetrf", u"\u2128" },
+ { u"zeta", u"\u03B6" },
+ { u"zfr", u"\U0001D537" },
+ { u"zhcy", u"\u0436" },
+ { u"zigrarr", u"\u21DD" },
+ { u"zopf", u"\U0001D56B" },
+ { u"zscr", u"\U0001D4CF" },
+ { u"zwj", u"\u200D" },
+ { u"zwnj", u"\u200C" }
+ // clang-format on
+ };
+
+const ::css::uno::Sequence<::css::beans::Pair<OUString, OUString>>
+ starmathdatabase::icustomMathmlHtmlEntities(
+ icustomMathmlHtmlEntitiesData, starmathdatabase::STARMATH_MATHMLHTML_ENTITY_NUMBER);
+
+static ::css::beans::Pair<::rtl::OUString, ::rtl::OUString>
+ icustomMathmlHtmlEntitiesNamesExportData[2] = {
+ // clang-format off
+ { u"&sigma;", u"\u03C3"},
+ { u"&infin;", u"\u221E"}
+ // clang-format on
+ };
+const ::css::uno::Sequence<::css::beans::Pair<::rtl::OUString, ::rtl::OUString>>
+ starmathdatabase::icustomMathmlHtmlEntitiesExport(icustomMathmlHtmlEntitiesNamesExportData, 2);
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathtype.cxx b/starmath/source/mathtype.cxx
new file mode 100644
index 000000000..518f29fc5
--- /dev/null
+++ b/starmath/source/mathtype.cxx
@@ -0,0 +1,3354 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "mathtype.hxx"
+#include "eqnolefilehdr.hxx"
+
+#include <filter/msfilter/classids.hxx>
+#include <osl/diagnose.h>
+#include <sfx2/docfile.hxx>
+#include <sot/storage.hxx>
+#include <array>
+
+//These are the default MathType sizes
+constexpr std::array<sal_Int16, 7> aSizeTable {
+ 12,
+ 8,
+ 6,
+ 24,
+ 10,
+ 12,
+ 12,
+};
+
+void MathType::Init()
+{
+ /*
+ These are the default MathType italic/bold settings If mathtype is changed
+ from its defaults, there is nothing we can do, as this information is not
+ stored in the document
+ */
+ MathTypeFont aFont;
+ aUserStyles.reserve(11);
+ for(sal_uInt8 i=1;i<=11;i++)
+ {
+ aFont.nTface = i+128;
+ switch (i)
+ {
+ default:
+ aFont.nStyle=0;
+ break;
+ case 3:
+ case 4:
+ aFont.nStyle=1;
+ break;
+ case 7:
+ aFont.nStyle=2;
+ break;
+ }
+ aUserStyles.insert(aFont);
+ }
+}
+
+
+/*ToDo replace with table rather than switch, returns
+ sal_True in the case that the char is just a char, and
+ sal_False if the character is an operator which must not be
+ placed inside the quote sequence designed to protect
+ against being parsed as a keyword
+
+ General solution required to force starmath to handle
+ unicode math chars the way it handles its own math
+ chars rather than handle them as text as it will do
+ for the default case below, i.e. incorrect spacing
+ between math symbols and ordinary text e.g. 1=2 rather
+ than 1 = 2
+ */
+bool MathType::LookupChar(sal_Unicode nChar,OUStringBuffer &rRet,sal_uInt8 nVersion,
+ sal_uInt8 nTypeFace)
+{
+ bool bRet=false;
+ const char *pC = nullptr;
+ switch(nChar)
+ {
+ case 0x0000:
+ pC = " none ";
+ break;
+ case 0x00ac:
+ pC = " neg ";
+ break;
+ case 0x00b1:
+ pC = " +- ";
+ break;
+ case '(':
+ pC = " \\( ";
+ break;
+ case ')':
+ pC = " \\) ";
+ break;
+ case '[':
+ pC = " \\[ ";
+ break;
+ case ']':
+ pC = " \\] ";
+ break;
+ case '.':
+ pC = " \".\" ";
+ break;
+ case 0xae:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " rightarrow ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00fb:
+ if ((nVersion < 3) && (nTypeFace == 0x81))
+ nChar = 0xDF;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'a':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3b1;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'b':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3b2;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'l':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3bb;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'n':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3bd;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'r':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x3c1;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 'D':
+ if ((nVersion < 3) && (nTypeFace == 0x84))
+ nChar = 0x394;
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 0xa9:
+ if ((nVersion < 3) && (nTypeFace == 0x82))
+ nChar = '\'';
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ case 0x00f1:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " \\rangle ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00a3:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " <= ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x00de:
+ if ((nVersion < 3) && (nTypeFace == 0x86))
+ pC = " drarrow ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x0057:
+ if ((nVersion < 3) && (nTypeFace == 0x85))
+ pC = " %OMEGA ";
+ else
+ {
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ }
+ break;
+ case 0x007b:
+ pC = " lbrace ";
+ break;
+ case 0x007c:
+ pC = " \\lline ";
+ break;
+ case 0x007d:
+ pC = " rbrace ";
+ break;
+ case 0x007e:
+ pC = " \"~\" ";
+ break;
+ case 0x2224:
+ pC = " ndivides ";
+ break;
+ case 0x2225:
+ pC = " parallel ";
+ break;
+ case 0x00d7:
+ if (nVersion < 3)
+ pC = " cdot ";
+ else
+ pC = " times ";
+ break;
+ case 0x00f7:
+ pC = " div ";
+ break;
+ case 0x019b:
+ pC = " lambdabar ";
+ break;
+ case 0x2026:
+ pC = " dotslow ";
+ break;
+ case 0x2022:
+ pC = " cdot ";
+ break;
+ case 0x2102:
+ pC = " setC ";
+ break;
+ case 0x210f:
+ pC = " hbar ";
+ break;
+ case 0x2111:
+ pC = " Im ";
+ break;
+ case 0x2115:
+ pC = " setN ";
+ break;
+ case 0x2118:
+ pC = " wp ";
+ break;
+ case 0x211a:
+ pC = " setQ ";
+ break;
+ case 0x211c:
+ pC = " Re ";
+ break;
+ case 0x211d:
+ pC = " setR ";
+ break;
+ case 0x2124:
+ pC = " setZ ";
+ break;
+ case 0x2135:
+ pC = " aleph ";
+ break;
+ case 0x2190:
+ pC = " leftarrow ";
+ break;
+ case 0x2191:
+ pC = " uparrow ";
+ break;
+ case 0x2192:
+ pC = " rightarrow ";
+ break;
+ case 0x0362:
+ pC = " widevec ";
+ break;
+ case 0x2193:
+ pC = " downarrow ";
+ break;
+ case 0x21d0:
+ pC = " dlarrow ";
+ break;
+ case 0x21d2:
+ pC = " drarrow ";
+ break;
+ case 0x21d4:
+ pC = " dlrarrow ";
+ break;
+ case 0x2200:
+ pC = " forall ";
+ break;
+ case 0x2202:
+ pC = " partial ";
+ break;
+ case 0x2203:
+ pC = " exists ";
+ break;
+ case 0x2204:
+ pC = " notexists ";
+ break;
+ case 0x2205:
+ pC = " emptyset ";
+ break;
+ case 0x2207:
+ pC = " nabla ";
+ break;
+ case 0x2112:
+ pC = " laplace ";
+ break;
+ case 0x03F6:
+ pC = " backepsilon ";
+ break;
+ case 0x2208: // in
+ case 0x2209: // notin
+ rRet.append(" func " + OUStringChar(nChar) + " ");
+ break;
+ case 0x220d: // owns
+ rRet.append(u" func \u220b ");
+ break;
+ case 0x220f:
+ pC = " prod ";
+ break;
+ case 0x2210:
+ pC = " coprod ";
+ break;
+ case 0x2211:
+ pC = " sum ";
+ break;
+ case 0x2212:
+ pC = " - ";
+ break;
+ case 0x2213:
+ pC = " -+ ";
+ break;
+ case 0x2217:
+ pC = " * ";
+ break;
+ case 0x2218:
+ pC = " circ ";
+ break;
+ case 0x221d:
+ pC = " prop ";
+ break;
+ case 0x221e:
+ pC = " infinity ";
+ break;
+ case 0x2227:
+ pC = " and ";
+ break;
+ case 0x2228:
+ pC = " or ";
+ break;
+ case 0x2229:
+ pC = " intersection ";
+ break;
+ case 0x222a:
+ pC = " union ";
+ break;
+ case 0x222b:
+ pC = " int ";
+ break;
+ case 0x222c:
+ pC = " iint ";
+ break;
+ case 0x222d:
+ pC = " iiint ";
+ break;
+ case 0x222e:
+ pC = " lint ";
+ break;
+ case 0x222f:
+ pC = " llint ";
+ break;
+ case 0x2230:
+ pC = " lllint ";
+ break;
+ case 0x2245:
+ pC = " simeq ";
+ break;
+ case 0x2248:
+ pC = " approx ";
+ break;
+ case 0x2260:
+ pC = " <> ";
+ break;
+ case 0x2261:
+ pC = " equiv ";
+ break;
+ case 0x2264:
+ pC = " <= ";
+ break;
+ case 0x2265:
+ pC = " >= ";
+ break;
+
+ case 0x227A:
+ pC = " prec ";
+ break;
+ case 0x227B:
+ pC = " succ ";
+ break;
+ case 0x227C:
+ pC = " preccurlyeq ";
+ break;
+ case 0x227D:
+ pC = " succcurlyeq ";
+ break;
+ case 0x227E:
+ pC = " precsim ";
+ break;
+ case 0x227F:
+ pC = " succsim ";
+ break;
+ case 0x2280:
+ pC = " nprec ";
+ break;
+ case 0x2281:
+ pC = " nsucc ";
+ break;
+
+ case 0x2282: // subset
+ case 0x2283: // supset
+ case 0x2284: // nsubset
+ case 0x2285: // nsupset
+ case 0x2286: // subseteq
+ case 0x2287: // supseteq
+ case 0x2288: // nsubseteq
+ case 0x2289: // nsupseteq
+ case 0x22b2: // NORMAL SUBGROUP OF
+ case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
+ rRet.append(" func " + OUStringChar(nChar) + " ");
+ break;
+ case 0x22a5:
+ pC = " ortho ";
+ break;
+ case 0x22c5:
+ pC = " cdot ";
+ break;
+ case 0x22ee:
+ pC = " dotsvert ";
+ break;
+ case 0x22ef:
+ pC = " dotsaxis ";
+ break;
+ case 0x22f0:
+ pC = " dotsup ";
+ break;
+ case 0x22f1:
+ pC = " dotsdown ";
+ break;
+ case MS_LANGLE:
+ case MS_LMATHANGLE:
+ pC = " langle ";
+ break;
+ case MS_RANGLE:
+ case MS_RMATHANGLE:
+ pC = " rangle ";
+ break;
+ case 0x301a:
+ pC = " ldbracket ";
+ break;
+ case 0x301b:
+ pC = " rdbracket ";
+ break;
+ case 0xe083:
+ rRet.append("+");
+ bRet=true;
+ break;
+ case '^':
+ case 0xe091:
+ pC = " widehat ";
+ break;
+ case 0xe096:
+ pC = " widetilde ";
+ break;
+ case 0xe098:
+ pC = " widevec ";
+ break;
+ case 0xE421:
+ pC = " geslant ";
+ break;
+ case 0xE425:
+ pC = " leslant ";
+ break;
+ case 0xeb01: //no space
+ case 0xeb08: //normal space
+ bRet=true;
+ break;
+ case 0xef04: //tiny space
+ case 0xef05: //tiny space
+ case 0xeb02: //small space
+ case 0xeb04: //medium space
+ rRet.append("`");
+ break;
+ case 0xeb05: //large space
+ rRet.append("~");
+ break;
+ case 0x3a9:
+ pC = " %OMEGA ";
+ break;
+ default:
+ rRet.append(OUStringChar(nChar));
+ bRet=true;
+ break;
+ }
+ if (pC)
+ rRet.appendAscii(pC);
+ return bRet;
+}
+
+void MathTypeFont::AppendStyleToText(OUString &rRet)
+{
+ const char *pC = nullptr;
+ switch (nStyle)
+ {
+ default:
+ case 0:
+ break;
+ case 1:
+ pC = " ital ";
+ break;
+ case 2:
+ pC = " bold ";
+ break;
+ case 3:
+ pC = " bold italic";
+ break;
+ }
+ if (pC)
+ rRet += OUString::createFromAscii( pC );
+}
+
+void MathType::TypeFaceToString(OUString &rTxt,sal_uInt8 nFace)
+{
+ MathTypeFont aFont(nFace);
+ auto aItr = aUserStyles.find(aFont);
+ if (aItr != aUserStyles.end())
+ aFont.nStyle = aItr->nStyle;
+ aFont.AppendStyleToText(rTxt);
+}
+
+bool MathType::Parse(SotStorage *pStor)
+{
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream(
+ "Equation Native",
+ StreamMode::STD_READ);
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return false;
+ return Parse(xSrc.get());
+}
+
+bool MathType::Parse(SvStream* pStream)
+{
+ pS = pStream;
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ EQNOLEFILEHDR aHdr;
+ aHdr.Read(pS);
+ sal_uInt8 nProdVersion;
+ sal_uInt8 nProdSubVersion;
+ sal_uInt8 nPlatform;
+ sal_uInt8 nProduct;
+ pS->ReadUChar( nVersion );
+ pS->ReadUChar( nPlatform );
+ pS->ReadUChar( nProduct );
+ pS->ReadUChar( nProdVersion );
+ pS->ReadUChar( nProdSubVersion );
+
+ if (!pS->good() || nVersion > 3) // allow only supported versions of MathType to be parsed
+ return false;
+
+ bool bRet = HandleRecords(0);
+ //little crude hack to close occasionally open expressions
+ //a sophisticated system to determine what expressions are
+ //opened is required, but this is as much work as rewriting
+ //starmaths internals.
+ rRet.append("{}");
+
+ return bRet;
+}
+
+static void lcl_PrependDummyTerm(OUStringBuffer &rRet, sal_Int32 &rTextStart)
+{
+ if ((rTextStart < rRet.getLength()) &&
+ (rRet[rTextStart] == '=') &&
+ ((rTextStart == 0) || (rRet[ rTextStart-1 ] == '{'))
+ )
+ {
+ rRet.insert(rTextStart, " {}");
+ rTextStart+=3;
+ }
+}
+
+static void lcl_AppendDummyTerm(OUStringBuffer &rRet)
+{
+ bool bOk=false;
+ for(int nI=rRet.getLength()-1;nI >= 0; nI--)
+ {
+ sal_Int32 nIdx = sal::static_int_cast< sal_Int32 >(nI);
+ sal_Unicode nChar = rRet[nIdx];
+ if (nChar == ' ')
+ continue;
+ if (rRet[nIdx] != '{')
+ bOk=true;
+ break;
+ }
+ if (!bOk) //No term, use dummy
+ rRet.append(" {}");
+}
+
+void MathType::HandleNudge()
+{
+ sal_uInt8 nXNudge(0);
+ pS->ReadUChar(nXNudge);
+ sal_uInt8 nYNudge(0);
+ pS->ReadUChar(nYNudge);
+ if (nXNudge == 128 && nYNudge == 128)
+ {
+ sal_uInt16 nXLongNudge(0);
+ sal_uInt16 nYLongNudge(0);
+ pS->ReadUInt16(nXLongNudge);
+ pS->ReadUInt16(nYLongNudge);
+ }
+}
+
+/* Fabulously complicated as many tokens have to be reordered and generally
+ * moved around from mathtypes paradigm to starmaths. */
+bool MathType::HandleRecords(int nLevel, sal_uInt8 nSelector,
+ sal_uInt8 nVariation, int nMatrixRows, int nMatrixCols)
+{
+ //depth-protect
+ if (nLevel > 1024)
+ return false;
+
+ sal_uInt8 nTag,nRecord;
+ sal_uInt8 nTabType;
+ sal_uInt16 nTabOffset;
+ int i, newline=0;
+ bool bSilent=false;
+ int nPart=0;
+ OUString sPush,sMainTerm;
+ int nSetSize=0,nSetAlign=0;
+ int nCurRow=0,nCurCol=0;
+ bool bOpenString=false;
+ sal_Int32 nTextStart = 0;
+ sal_Int32 nSubSupStartPos = 0;
+ sal_Int32 nLastTemplateBracket=-1;
+ bool bRet = true;
+
+ do
+ {
+ nTag = 0;
+ pS->ReadUChar( nTag );
+ nRecord = nTag&0x0F;
+
+ /*MathType strings can of course include words which
+ *are StarMath keywords, the simplest solution is
+ to escape strings of greater than len 1 with double
+ quotes to avoid scanning the TokenTable for matches
+
+ Unfortunately it may turn out that the string gets
+ split during the handling of a character emblishment
+ so this special case must be handled in the
+ character handler case 2:
+ */
+ if ((nRecord == CHAR) && (!bOpenString))
+ {
+ bOpenString=true;
+ nTextStart = rRet.getLength();
+ }
+ else if ((nRecord != CHAR) && bOpenString)
+ {
+ bOpenString=false;
+ if ((rRet.getLength() - nTextStart) > 1)
+ {
+ OUString aStr;
+ TypeFaceToString(aStr,nTypeFace);
+ aStr += "\"";
+ rRet.insert(nTextStart,aStr);
+ rRet.append("\"");
+ }
+ else if (nRecord == END && !rRet.isEmpty())
+ {
+ sal_Unicode cChar = 0;
+ sal_Int32 nI = rRet.getLength()-1;
+ while (nI)
+ {
+ cChar = rRet[nI];
+ if (cChar != ' ')
+ break;
+ --nI;
+ }
+ if ((cChar == '=') || (cChar == '+') || (cChar == '-'))
+ rRet.append("{}");
+ }
+ }
+
+ switch(nRecord)
+ {
+ case LINE:
+ {
+ if (xfLMOVE(nTag))
+ HandleNudge();
+
+ if (newline>0)
+ rRet.append("\nnewline\n");
+ if (!(xfNULL(nTag)))
+ {
+ switch (nSelector)
+ {
+ case tmANGLE:
+ if (nVariation==0)
+ rRet.append(" langle ");
+ else if (nVariation==1)
+ rRet.append(" \\langle ");
+ break;
+ case tmPAREN:
+ if (nVariation==0)
+ rRet.append(" left (");
+ else if (nVariation==1)
+ rRet.append("\\(");
+ break;
+ case tmBRACE:
+ if ((nVariation==0) || (nVariation==1))
+ rRet.append(" left lbrace ");
+ else
+ rRet.append(" left none ");
+ break;
+ case tmBRACK:
+ if (nVariation==0)
+ rRet.append(" left [");
+ else if (nVariation==1)
+ rRet.append("\\[");
+ break;
+ case tmLBLB:
+ case tmLBRP:
+ rRet.append(" \\[");
+ break;
+ case tmBAR:
+ if (nVariation==0)
+ rRet.append(" lline ");
+ else if (nVariation==1)
+ rRet.append(" \\lline ");
+ break;
+ case tmDBAR:
+ if (nVariation==0)
+ rRet.append(" ldline ");
+ else if (nVariation==1)
+ rRet.append(" \\ldline ");
+ break;
+ case tmFLOOR:
+ if (nVariation == 0 || nVariation & 0x01) // tvFENCE_L
+ rRet.append(" left lfloor ");
+ else
+ rRet.append(" left none ");
+ break;
+ case tmCEILING:
+ if (nVariation==0)
+ rRet.append(" lceil ");
+ else if (nVariation==1)
+ rRet.append(" \\lceil ");
+ break;
+ case tmRBRB:
+ case tmRBLB:
+ rRet.append(" \\]");
+ break;
+ case tmLPRB:
+ rRet.append(" \\(");
+ break;
+ case tmROOT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" sqrt");
+ else
+ {
+ rRet.append(" nroot");
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ rRet.append(" {");
+ break;
+ case tmFRACT:
+ if (nPart == 0)
+ rRet.append(" { ");
+
+
+ if (nPart == 1)
+ rRet.append(" over ");
+ rRet.append(" {");
+ break;
+ case tmSCRIPT:
+ nSubSupStartPos = rRet.getLength();
+ if ((nVariation == 0) ||
+ ((nVariation == 2) && (nPart==1)))
+ {
+ lcl_AppendDummyTerm(rRet);
+ rRet.append(" rSup");
+ }
+ else if ((nVariation == 1) ||
+ ((nVariation == 2) && (nPart==0)))
+ {
+ lcl_AppendDummyTerm(rRet);
+ rRet.append(" rSub");
+ }
+ rRet.append(" {");
+ break;
+ case tmUBAR:
+ if (nVariation == 0)
+ rRet.append(" {underline ");
+ else if (nVariation == 1)
+ rRet.append(" {underline underline ");
+ rRet.append(" {");
+ break;
+ case tmOBAR:
+ if (nVariation == 0)
+ rRet.append(" {overline ");
+ else if (nVariation == 1)
+ rRet.append(" {overline overline ");
+ rRet.append(" {");
+ break;
+ case tmLARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//left arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmRARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//right arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmBARROW:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" widevec ");//double arrow above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//double arrow below
+ rRet.append(" {");
+ }
+ break;
+ case tmSINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 3) || (nVariation == 4))
+ rRet.append(" lInt");
+ else
+ rRet.append(" Int");
+ if ( (nVariation != 0) && (nVariation != 3))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 4)) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 2) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmDINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 2) || (nVariation == 3))
+ rRet.append(" llInt");
+ else
+ rRet.append(" iInt");
+ if ( (nVariation != 0) && (nVariation != 2))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 3)) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmTINT:
+ if (nPart == 0)
+ {
+ if ((nVariation == 2) || (nVariation == 3))
+ rRet.append(" lllInt");
+ else
+ rRet.append(" iiInt");
+ if ( (nVariation != 0) && (nVariation != 2))
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 3)) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmSSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 2)
+ rRet.append(" lInt");
+ else
+ rRet.append(" Int");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (((nVariation == 1) ||
+ (nVariation == 2)) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 0) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmDSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" llInt");
+ else
+ rRet.append(" iInt");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (nPart==1)
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmTSINT:
+ if (nPart == 0)
+ {
+ if (nVariation == 0)
+ rRet.append(" lllInt");
+ else
+ rRet.append(" iiInt");
+ sPush = rRet.makeStringAndClear();
+ }
+ if (nPart==1)
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmUHBRACE:
+ case tmLHBRACE:
+ rRet.append(" {");
+ break;
+ case tmSUM:
+ if (nPart == 0)
+ {
+ rRet.append(" Sum");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmISUM:
+ if (nPart == 0)
+ {
+ rRet.append(" Sum");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" Prod");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" Prod");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmCOPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" coProd");
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmICOPROD:
+ if (nPart == 0)
+ {
+ rRet.append(" coProd");
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmUNION:
+ if (nPart == 0)
+ {
+ rRet.append(" union"); //union
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIUNION:
+ if (nPart == 0)
+ {
+ rRet.append(" union"); //union
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmINTER:
+ if (nPart == 0)
+ {
+ rRet.append(" intersect"); //intersect
+ if (nVariation != 2)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmIINTER:
+ if (nPart == 0)
+ {
+ rRet.append(" intersect"); //intersect
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" rSub");
+ else if ((nVariation == 1) && (nPart==2))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmLIM:
+ if ((nVariation == 0) && (nPart==1))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==2))
+ rRet.append(" cSup");
+ rRet.append(" {");
+ break;
+ case tmLDIV:
+ if (nVariation == 0)
+ {
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ }
+ rRet.append(" {");
+ if (nVariation == 0)
+ {
+ if (nPart == 1)
+ rRet.append("alignr ");
+ }
+ if (nPart == 0)
+ rRet.append("\\lline ");
+ if (nVariation == 1)
+ rRet.append("overline ");
+ break;
+ case tmSLFRACT:
+ rRet.append(" {");
+ break;
+ case tmINTOP:
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==0))
+ rRet.append(" rSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" rSup");
+ else if ((nVariation == 1) && (nPart==0))
+ rRet.append(" rSub");
+ else if ((nVariation == 2) && (nPart==0))
+ rRet.append(" rSub");
+ rRet.append(" {");
+ break;
+ case tmSUMOP:
+ if (nPart == 0)
+ {
+ sPush = rRet.makeStringAndClear();
+ }
+ if ((nVariation == 0) && (nPart==0))
+ rRet.append(" cSup");
+ else if ((nVariation == 2) && (nPart==1))
+ rRet.append(" cSup");
+ else if ((nVariation == 1) && (nPart==0))
+ rRet.append(" cSub");
+ else if ((nVariation == 2) && (nPart==0))
+ rRet.append(" cSub");
+ rRet.append(" {");
+ break;
+ case tmLSCRIPT:
+ if (nPart == 0)
+ rRet.append("\"\"");
+ if ((nVariation == 0)
+ || ((nVariation == 2) && (nPart==1)))
+ rRet.append(" lSup");
+ else if ((nVariation == 1)
+ || ((nVariation == 2) && (nPart==0)))
+ rRet.append(" lSub");
+ rRet.append(" {");
+ break;
+ case tmDIRAC:
+ if (nVariation==0)
+ {
+ if (nPart == 0)
+ rRet.append(" langle ");
+ }
+ else if (nVariation==1)
+ {
+ rRet.append(" \\langle ");
+ newline--;
+ }
+ else if (nVariation==2)
+ {
+ rRet.append(" \\lline ");
+ newline--;
+ }
+ break;
+ case tmUARROW:
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left below
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right below
+ else if (nVariation == 2)
+ rRet.append(" widevec ");//double headed below
+ rRet.append(" {");
+ break;
+ case tmOARROW:
+ if (nVariation == 0)
+ rRet.append(" widevec ");//left above
+ else if (nVariation == 1)
+ rRet.append(" widevec ");//right above
+ else if (nVariation == 2)
+ rRet.append(" widevec ");//double headed above
+ rRet.append(" {");
+ break;
+ default:
+ break;
+ }
+ sal_Int16 nOldCurSize=nCurSize;
+ sal_Int32 nSizeStartPos = rRet.getLength();
+ HandleSize( nLSize, nDSize, nSetSize );
+ bRet = HandleRecords( nLevel+1 );
+ while (nSetSize)
+ {
+ bool bOk=false;
+ sal_Int32 nI = rRet.lastIndexOf('{');
+ if (nI != -1)
+ {
+ for(nI=nI+1;nI<rRet.getLength();nI++)
+ if (rRet[nI] != ' ')
+ {
+ bOk=true;
+ break;
+ }
+ }
+ else
+ bOk=true;
+
+ if (bOk)
+ rRet.append("} ");
+ else if (rRet.getLength() > nSizeStartPos)
+ rRet = rRet.truncate(nSizeStartPos);
+ nSetSize--;
+ nCurSize=nOldCurSize;
+ }
+
+
+ HandleMatrixSeparator(nMatrixRows,nMatrixCols,
+ nCurCol,nCurRow);
+
+ switch (nSelector)
+ {
+ case tmANGLE:
+ if (nVariation==0)
+ rRet.append(" rangle ");
+ else if (nVariation==2)
+ rRet.append(" \\rangle ");
+ break;
+ case tmPAREN:
+ if (nVariation==0)
+ rRet.append(" right )");
+ else if (nVariation==2)
+ rRet.append("\\)");
+ break;
+ case tmBRACE:
+ if ((nVariation==0) || (nVariation==2))
+ rRet.append(" right rbrace ");
+ else
+ rRet.append(" right none ");
+ break;
+ case tmBRACK:
+ if (nVariation==0)
+ rRet.append(" right ]");
+ else if (nVariation==2)
+ rRet.append("\\]");
+ break;
+ case tmBAR:
+ if (nVariation==0)
+ rRet.append(" rline ");
+ else if (nVariation==2)
+ rRet.append(" \\rline ");
+ break;
+ case tmDBAR:
+ if (nVariation==0)
+ rRet.append(" rdline ");
+ else if (nVariation==2)
+ rRet.append(" \\rdline ");
+ break;
+ case tmFLOOR:
+ if (nVariation == 0 || nVariation & 0x02) // tvFENCE_R
+ rRet.append(" right rfloor ");
+ else
+ rRet.append(" right none ");
+ break;
+ case tmCEILING:
+ if (nVariation==0)
+ rRet.append(" rceil ");
+ else if (nVariation==2)
+ rRet.append(" \\rceil ");
+ break;
+ case tmLBLB:
+ case tmRBLB:
+ rRet.append("\\[");
+ break;
+ case tmRBRB:
+ case tmLPRB:
+ rRet.append("\\]");
+ break;
+ case tmROOT:
+ rRet.append("} ");
+ if (nVariation == 1)
+ {
+ if (nPart == 0)
+ {
+ newline--;
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ }
+ else
+ {
+ if (nPart == 0)
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmLBRP:
+ rRet.append("\\)");
+ break;
+ case tmFRACT:
+ rRet.append("} ");
+ if (nPart == 0)
+ newline--;
+ else
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmSCRIPT:
+ {
+ if ((nPart == 0) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+
+ bool bOk=false;
+ sal_Int32 nI = rRet.lastIndexOf('{');
+ if (nI != -1)
+ {
+ for(nI=nI+1;nI<rRet.getLength();nI++)
+ if (rRet[nI] != ' ')
+ {
+ bOk=true;
+ break;
+ }
+ }
+ else
+ bOk=true;
+
+ if (bOk)
+ rRet.append("} ");
+ else if (rRet.getLength() > nSubSupStartPos)
+ rRet = rRet.truncate(nSubSupStartPos);
+ nPart++;
+ }
+ break;
+ case tmLSCRIPT:
+ if ((nPart == 0) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmUARROW:
+ case tmOARROW:
+ rRet.append("} ");
+ break;
+ case tmUBAR:
+ case tmOBAR:
+ rRet.append("}} ");
+ break;
+ case tmLARROW:
+ case tmRARROW:
+ case tmBARROW:
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("} ");
+ }
+ nPart++;
+ break;
+ case tmUHBRACE:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("overbrace");
+ }
+ nPart++;
+ break;
+ case tmLHBRACE:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ rRet.append("underbrace");
+ }
+ nPart++;
+ break;
+ case tmLIM:
+ if (nPart==0)
+ newline--;
+ else if ((nPart==1) &&
+ ((nVariation == 2) || (nVariation == 1)))
+ newline--;
+ rRet.append("} ");
+ nPart++;
+ break;
+ case tmLDIV:
+ rRet.append("} ");
+ if (nVariation == 0)
+ {
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(" over " + sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ }
+ if (nPart == 0)
+ newline--;
+ nPart++;
+ break;
+ case tmSLFRACT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ newline--;
+ switch (nVariation)
+ {
+ case 1:
+ rRet.append("slash");
+ break;
+ default:
+ rRet.append("wideslash");
+ break;
+ }
+ }
+ nPart++;
+ break;
+ case tmSUM:
+ case tmISUM:
+ case tmPROD:
+ case tmIPROD:
+ case tmCOPROD:
+ case tmICOPROD:
+ case tmUNION:
+ case tmIUNION:
+ case tmINTER:
+ case tmIINTER:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if (nVariation != 2)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 0))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 1))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 1))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if ((nVariation != 0) && (nVariation != 3))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==4)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 2))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 2))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmDINT:
+ case tmTINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ if ((nVariation != 0) && (nVariation != 2))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ }
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==3)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmSSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 1) &&
+ ((nVariation == 1) || (nVariation==2)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ else if ((nPart == 1) && (nVariation == 0))
+ newline--;
+ else if ((nPart == 2) && (nVariation == 0))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmDSINT:
+ case tmTSINT:
+ rRet.append("} ");
+ if (nPart == 0)
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if (nPart == 1)
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ newline--;
+ }
+ nPart++;
+ break;
+ case tmINTOP:
+ case tmSUMOP:
+ rRet.append("} ");
+
+ if ((nPart == 0) &&
+ ((nVariation == 0) || (nVariation == 1)))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 0) && (nVariation == 2))
+ newline--;
+ else if ((nPart == 1) && (nVariation == 2))
+ {
+ sMainTerm = rRet.makeStringAndClear();
+ newline--;
+ }
+ else if ((nPart == 2) || ((nPart == 1) &&
+ (nVariation == 0 || nVariation == 1)))
+ {
+ rRet.insert(0, sPush);
+ rRet.append(sMainTerm);
+ sPush.clear();
+ sMainTerm.clear();
+ }
+ nPart++;
+ break;
+ case tmDIRAC:
+ if (nVariation==0)
+ {
+ if (nPart == 0)
+ {
+ newline--; //there is another term to arrive
+ rRet.append(" mline ");
+ }
+ else
+ rRet.append(" rangle ");
+ }
+ else if (nVariation==1)
+ rRet.append(" \\lline ");
+ else if (nVariation==2)
+ rRet.append(" \\rangle ");
+ nPart++;
+ break;
+ default:
+ break;
+ }
+ bSilent = true; //Skip the optional brackets and/or
+ //symbols that follow some of these
+ //records. Foo Data.
+
+ /*In matrices and piles we cannot separate equation
+ *lines with the newline keyword*/
+ if (nMatrixCols==0)
+ newline++;
+ }
+ }
+ break;
+ case CHAR:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleChar( nTextStart, nSetSize, nLevel, nTag, nSelector, nVariation, bSilent );
+ break;
+ case TMPL:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleTemplate( nLevel, nSelector, nVariation, nLastTemplateBracket );
+ break;
+ case PILE:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandlePile( nSetAlign, nLevel, nSelector, nVariation );
+ HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
+ break;
+ case MATRIX:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ bRet = HandleMatrix( nLevel, nSelector, nVariation );
+ HandleMatrixSeparator( nMatrixRows, nMatrixCols, nCurCol, nCurRow );
+ break;
+ case EMBEL:
+ if (xfLMOVE(nTag))
+ HandleNudge();
+ HandleEmblishments();
+ break;
+ case RULER:
+ {
+ sal_uInt8 nTabStops(0);
+ pS->ReadUChar( nTabStops );
+ for (i=0;i<nTabStops;i++)
+ {
+ pS->ReadUChar( nTabType );
+ pS->ReadUInt16( nTabOffset );
+ }
+ SAL_WARN("starmath", "Not seen in the wild Equation Ruler Field");
+ break;
+ }
+ case FONT:
+ {
+ MathTypeFont aFont;
+ pS->ReadUChar( aFont.nTface );
+ /*
+ The typeface number is the negative (which makes it
+ positive) of the typeface value (unbiased) that appears in
+ CHAR records that might follow a given FONT record
+ */
+ aFont.nTface = 128-aFont.nTface;
+ pS->ReadUChar( aFont.nStyle );
+ aUserStyles.insert(aFont);
+ // read font name
+ while(true)
+ {
+ char nChar8(0);
+ pS->ReadChar( nChar8 );
+ if (nChar8 == 0)
+ break;
+ }
+ }
+ break;
+ case SIZE:
+ HandleSetSize();
+ break;
+ case 10:
+ case 11:
+ case 12:
+ case 13:
+ case 14:
+ nLSize=nRecord-10;
+ break;
+ case END:
+ default:
+ break;
+ }
+ }
+ while (nRecord != END && !pS->eof());
+ while (nSetSize)
+ {
+ rRet.append("}");
+ nSetSize--;
+ }
+ return bRet;
+}
+
+/*Simply determine if we are at the end of a record or the end of a line,
+ *with fiddly logic to see if we are in a matrix or a pile or neither
+
+ Note we cannot tell until after the event that this is the last entry
+ of a pile, so we must strip the last separator of a pile after this
+ is detected in the PILE handler
+ */
+void MathType::HandleMatrixSeparator(int nMatrixRows,int nMatrixCols,
+ int &rCurCol,int &rCurRow)
+{
+ if (nMatrixRows==0)
+ return;
+
+ if (rCurCol == nMatrixCols-1)
+ {
+ if (rCurRow != nMatrixRows-1)
+ rRet.append(" {} ##\n");
+ if (nMatrixRows!=-1)
+ {
+ rCurCol=0;
+ rCurRow++;
+ }
+ }
+ else
+ {
+ rRet.append(" {} # ");
+ if (nMatrixRows!=-1)
+ rCurCol++;
+ else
+ rRet.append("\n");
+ }
+}
+
+/* set the alignment of the following term, but starmath currently
+ * cannot handle vertical alignment */
+void MathType::HandleAlign(sal_uInt8 nHorAlign, int &rSetAlign)
+{
+ switch(nHorAlign)
+ {
+ case 1:
+ default:
+ rRet.append("alignl {");
+ break;
+ case 2:
+ rRet.append("alignc {");
+ break;
+ case 3:
+ rRet.append("alignr {");
+ break;
+ }
+ rSetAlign++;
+}
+
+/* set size of text, complexity due to overuse of signedness as a flag
+ * indicator by mathtype file format*/
+bool MathType::HandleSize(sal_Int16 nLstSize,sal_Int16 nDefSize, int &rSetSize)
+{
+ bool bRet=false;
+ if (nLstSize < 0)
+ {
+ const sal_Int16 nDefaultSize = 12;
+ if ((-nLstSize/32 != nDefaultSize) && (-nLstSize/32 != nCurSize))
+ {
+ if (rSetSize)
+ {
+ rSetSize--;
+ rRet.append("}");
+ bRet=true;
+ }
+ if (-nLstSize/32 != nLastSize)
+ {
+ nLastSize = nCurSize;
+ rRet.append(" size ");
+ rRet.append(static_cast<sal_Int32>(-nLstSize/32));
+ rRet.append("{");
+ bRet=true;
+ rSetSize++;
+ }
+ nCurSize = -nLstSize/32;
+ }
+ }
+ else
+ {
+ /*sizetable should theoretically be filled with the default sizes
+ *of the various font groupings matching starmaths equivalents
+ in aTypeFaces, and a test would be done to see if the new font
+ size would be the same as what starmath would have chosen for
+ itself anyway in which case the size setting could be ignored*/
+ nLstSize = aSizeTable.at(nLstSize);
+ nLstSize = nLstSize + nDefSize;
+ if (nLstSize != nCurSize)
+ {
+ if (rSetSize)
+ {
+ rSetSize--;
+ rRet.append("}");
+ bRet=true;
+ }
+ if (nLstSize != nLastSize)
+ {
+ nLastSize = nCurSize;
+ rRet.append(" size ");
+ rRet.append(static_cast<sal_Int32>(nLstSize));
+ rRet.append("{");
+ bRet=true;
+ rSetSize++;
+ }
+ nCurSize = nLstSize;
+ }
+ }
+ return bRet;
+}
+
+bool MathType::ConvertFromStarMath( SfxMedium& rMedium )
+{
+ if (!pTree)
+ return false;
+
+ SvStream *pStream = rMedium.GetOutStream();
+ if ( pStream )
+ {
+ tools::SvRef<SotStorage> pStor = new SotStorage( pStream, false );
+
+ SvGlobalName aGName(MSO_EQUATION3_CLASSID);
+ pStor->SetClass( aGName, SotClipboardFormatId::NONE, "Microsoft Equation 3.0");
+
+ static sal_uInt8 const aCompObj[] = {
+ 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0xFF, 0x02, 0xCE, 0x02, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x46, 0x17, 0x00, 0x00, 0x00,
+ 0x4D, 0x69, 0x63, 0x72, 0x6F, 0x73, 0x6F, 0x66,
+ 0x74, 0x20, 0x45, 0x71, 0x75, 0x61, 0x74, 0x69,
+ 0x6F, 0x6E, 0x20, 0x33, 0x2E, 0x30, 0x00, 0x0C,
+ 0x00, 0x00, 0x00, 0x44, 0x53, 0x20, 0x45, 0x71,
+ 0x75, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x00, 0x0B,
+ 0x00, 0x00, 0x00, 0x45, 0x71, 0x75, 0x61, 0x74,
+ 0x69, 0x6F, 0x6E, 0x2E, 0x33, 0x00, 0xF4, 0x39,
+ 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+ tools::SvRef<SotStorageStream> xStor( pStor->OpenSotStream("\1CompObj"));
+ xStor->WriteBytes(aCompObj, sizeof(aCompObj));
+
+ static sal_uInt8 const aOle[] = {
+ 0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00
+ };
+ tools::SvRef<SotStorageStream> xStor2( pStor->OpenSotStream("\1Ole"));
+ xStor2->WriteBytes(aOle, sizeof(aOle));
+ xStor.clear();
+ xStor2.clear();
+
+ tools::SvRef<SotStorageStream> xSrc = pStor->OpenSotStream("Equation Native");
+ if ( (!xSrc.is()) || (ERRCODE_NONE != xSrc->GetError()))
+ return false;
+
+ pS = xSrc.get();
+ pS->SetEndian( SvStreamEndian::LITTLE );
+
+ pS->SeekRel(EQNOLEFILEHDR_SIZE); //Skip 28byte Header and fill it in later
+ pS->WriteUChar( 0x03 );
+ pS->WriteUChar( 0x01 );
+ pS->WriteUChar( 0x01 );
+ pS->WriteUChar( 0x03 );
+ pS->WriteUChar( 0x00 );
+ sal_uInt32 nSize = pS->Tell();
+ nPendingAttributes=0;
+
+ HandleNodes(pTree, 0);
+ pS->WriteUChar( END );
+
+ nSize = pS->Tell()-nSize;
+ pS->Seek(0);
+ EQNOLEFILEHDR aHdr(nSize+4+1);
+ aHdr.Write(pS);
+
+ pStor->Commit();
+ }
+
+ return true;
+}
+
+
+void MathType::HandleNodes(SmNode *pNode,int nLevel)
+{
+ switch(pNode->GetType())
+ {
+ case SmNodeType::Attribute:
+ HandleAttributes(pNode,nLevel);
+ break;
+ case SmNodeType::Text:
+ HandleText(pNode);
+ break;
+ case SmNodeType::VerticalBrace:
+ HandleVerticalBrace(pNode,nLevel);
+ break;
+ case SmNodeType::Brace:
+ HandleBrace(pNode,nLevel);
+ break;
+ case SmNodeType::Oper:
+ HandleOperator(pNode,nLevel);
+ break;
+ case SmNodeType::BinVer:
+ HandleFractions(pNode,nLevel);
+ break;
+ case SmNodeType::Root:
+ HandleRoot(pNode,nLevel);
+ break;
+ case SmNodeType::Special:
+ {
+ SmTextNode *pText = static_cast<SmTextNode *>(pNode);
+ //if the token str and the result text are the same then this
+ //is to be seen as text, else assume it's a mathchar
+ if (pText->GetText() == pText->GetToken().aText)
+ HandleText(pText);
+ else
+ HandleMath(pText);
+ }
+ break;
+ case SmNodeType::Math:
+ case SmNodeType::MathIdent:
+ HandleMath(pNode);
+ break;
+ case SmNodeType::SubSup:
+ HandleSubSupScript(pNode,nLevel);
+ break;
+ case SmNodeType::Expression:
+ {
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ break;
+ }
+ case SmNodeType::Table:
+ //Root Node, PILE equivalent, i.e. vertical stack
+ HandleTable(pNode,nLevel);
+ break;
+ case SmNodeType::Matrix:
+ HandleSmMatrix(static_cast<SmMatrixNode *>(pNode),nLevel);
+ break;
+ case SmNodeType::Line:
+ {
+ pS->WriteUChar( 0x0a );
+ pS->WriteUChar( LINE );
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ pS->WriteUChar( END );
+ break;
+ }
+ case SmNodeType::Align:
+ HandleMAlign(pNode,nLevel);
+ break;
+ case SmNodeType::Blank:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x98 );
+ if (pNode->GetToken().eType == TSBLANK)
+ pS->WriteUInt16( 0xEB04 );
+ else
+ pS->WriteUInt16( 0xEB05 );
+ break;
+ default:
+ {
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ break;
+ }
+ }
+}
+
+
+int MathType::StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation)
+{
+ int nOldPending=nPendingAttributes;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( nSelector ); //selector
+ pS->WriteUChar( nVariation ); //variation
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( LINE );
+ //there's just no way we can now handle any character
+ //attributes (from mathtypes perspective) centered
+ //over an expression but above template attribute
+ //such as widevec and similar constructs
+ //we have to drop them
+ nPendingAttributes=0;
+ return nOldPending;
+}
+
+void MathType::EndTemplate(int nOldPendingAttributes)
+{
+ pS->WriteUChar( END ); //end line
+ pS->WriteUChar( END ); //end template
+ nPendingAttributes=nOldPendingAttributes;
+}
+
+
+void MathType::HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel)
+{
+ pS->WriteUChar( MATRIX );
+ pS->WriteUChar( 0x00 ); //vAlign ?
+ pS->WriteUChar( 0x00 ); //h_just
+ pS->WriteUChar( 0x00 ); //v_just
+ pS->WriteUChar( pMatrix->GetNumRows() ); //v_just
+ pS->WriteUChar( pMatrix->GetNumCols() ); //v_just
+ int nBytes=(pMatrix->GetNumRows()+1)*2/8;
+ if (((pMatrix->GetNumRows()+1)*2)%8)
+ nBytes++;
+ for (int j = 0; j < nBytes; j++)
+ pS->WriteUChar( 0x00 ); //row_parts
+ nBytes=(pMatrix->GetNumCols()+1)*2/8;
+ if (((pMatrix->GetNumCols()+1)*2)%8)
+ nBytes++;
+ for (int k = 0; k < nBytes; k++)
+ pS->WriteUChar( 0x00 ); //col_parts
+ size_t nSize = pMatrix->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pMatrix->GetSubNode(i))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //end line
+ }
+ }
+ pS->WriteUChar( END );
+}
+
+
+//Root Node, PILE equivalent, i.e. vertical stack
+void MathType::HandleTable(SmNode *pNode,int nLevel)
+{
+ size_t nSize = pNode->GetNumSubNodes();
+ //The root of the starmath is a table, if
+ //we convert this them each iteration of
+ //conversion from starmath to mathtype will
+ //add an extra unnecessary level to the
+ //mathtype output stack which would grow
+ //without bound in a multi step conversion
+
+ if (nLevel == 0)
+ pS->WriteUChar( 0x0A ); //initial size
+
+ if ( nLevel || (nSize >1))
+ {
+ pS->WriteUChar( PILE );
+ pS->WriteUChar( nHAlign ); //vAlign ?
+ pS->WriteUChar( 0x01 ); //hAlign
+ }
+
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ {
+ pS->WriteUChar( LINE );
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+ }
+ if (nLevel || (nSize>1))
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleRoot(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0D ); //selector
+ if (pNode->GetSubNode(0))
+ pS->WriteUChar( 0x01 ); //variation
+ else
+ pS->WriteUChar( 0x00 ); //variation
+ pS->WriteUChar( 0x00 ); //options
+
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+ }
+ else
+ pS->WriteUChar( LINE|0x10 ); //dummy line
+
+
+ pS->WriteUChar( END );
+}
+
+sal_uInt8 MathType::HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel,
+ sal_uInt64 *pPos,bool bTest)
+{
+ sal_uInt8 nVariation2=0xff;
+
+ if (bTest && pNode->GetSubNode(CSUP+1))
+ {
+ nVariation2=0;
+ if (pNode->GetSubNode(CSUB+1))
+ nVariation2=2;
+ }
+ else if (pNode->GetSubNode(CSUB+1))
+ nVariation2=1;
+
+ if (nVariation2!=0xff)
+ {
+ if (pPos)
+ *pPos = pS->Tell();
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x2B ); //selector
+ pS->WriteUChar( nVariation2 );
+ pS->WriteUChar( 0x00 ); //options
+
+ if (pContent)
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pContent,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+
+ pS->WriteUChar( 0x0B );
+
+ SmNode *pTemp;
+ if (nullptr != (pTemp = pNode->GetSubNode(CSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (bTest && nullptr != (pTemp = pNode->GetSubNode(CSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ }
+ return nVariation2;
+}
+
+
+/*
+ Sub and Sup scripts and another problem area, StarMath
+ can have all possible options used at the same time, whereas
+ Mathtype cannot. The ordering of the nodes for each system
+ is quite different as well leading to some complexity
+ */
+void MathType::HandleSubSupScript(SmNode *pNode,int nLevel)
+{
+ sal_uInt8 nVariation=0xff;
+ if (pNode->GetSubNode(LSUP+1))
+ {
+ nVariation=0;
+ if (pNode->GetSubNode(LSUB+1))
+ nVariation=2;
+ }
+ else if ( nullptr != pNode->GetSubNode(LSUB+1) )
+ nVariation=1;
+
+ SmNode *pTemp;
+ if (nVariation!=0xff)
+ {
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x2c ); //selector
+ pS->WriteUChar( nVariation );
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( 0x0B );
+
+ if (nullptr != (pTemp = pNode->GetSubNode(LSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (nullptr != (pTemp = pNode->GetSubNode(LSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( END );
+ nVariation=0xff;
+ }
+
+
+ sal_uInt8 nVariation2=HandleCScript(pNode,nullptr,nLevel);
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ HandleNodes(pTemp,nLevel+1);
+ }
+
+ if (nVariation2 != 0xff)
+ pS->WriteUChar( END );
+
+ if (nullptr != (pNode->GetSubNode(RSUP+1)))
+ {
+ nVariation=0;
+ if (pNode->GetSubNode(RSUB+1))
+ nVariation=2;
+ }
+ else if (nullptr != pNode->GetSubNode(RSUB+1))
+ nVariation=1;
+
+ if (nVariation!=0xff)
+ {
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0F ); //selector
+ pS->WriteUChar( nVariation );
+ pS->WriteUChar( 0x00 ); //options
+ pS->WriteUChar( 0x0B );
+
+ if (nullptr != (pTemp = pNode->GetSubNode(RSUB+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ if (nullptr != (pTemp = pNode->GetSubNode(RSUP+1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //line
+ }
+ else
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( END ); //line
+ }
+
+ //After subscript mathtype will keep the size of
+ //normal text at the subscript size, sigh.
+ pS->WriteUChar( 0x0A );
+}
+
+
+void MathType::HandleFractions(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ pS->WriteUChar( 0x0E ); //selector
+ pS->WriteUChar( 0x00 ); //variation
+ pS->WriteUChar( 0x00 ); //options
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END );
+
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleBrace(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ SmNode *pLeft=pNode->GetSubNode(0);
+ SmNode *pRight=pNode->GetSubNode(2);
+
+ pS->WriteUChar( TMPL ); //Template
+ bIsReInterpBrace=false;
+ sal_uInt8 nBSpec=0x10;
+ auto nLoc = pS->Tell();
+ if (pLeft)
+ {
+ switch (pLeft->GetToken().eType)
+ {
+ case TLANGLE:
+ pS->WriteUChar( tmANGLE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ case TLBRACE:
+ pS->WriteUChar( tmBRACE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLBRACKET:
+ pS->WriteUChar( tmBRACK ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLFLOOR:
+ pS->WriteUChar( tmFLOOR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ case TLLINE:
+ pS->WriteUChar( tmBAR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ case TLDLINE:
+ pS->WriteUChar( tmDBAR ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ break;
+ default:
+ pS->WriteUChar( tmPAREN ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+ nBSpec+=3;
+ break;
+ }
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(1)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+ nSpec=nBSpec;
+ if (pLeft)
+ HandleNodes(pLeft,nLevel+1);
+ if (bIsReInterpBrace)
+ {
+ auto nLoc2 = pS->Tell();
+ pS->Seek(nLoc);
+ pS->WriteUChar( 0x2D );
+ pS->Seek(nLoc2);
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x96 );
+ pS->WriteUInt16( 0xEC07 );
+ bIsReInterpBrace=false;
+ }
+ if (pRight)
+ HandleNodes(pRight,nLevel+1);
+ nSpec=0x0;
+ pS->WriteUChar( END );
+}
+
+
+void MathType::HandleVerticalBrace(SmNode *pNode,int nLevel)
+{
+ SmNode *pTemp;
+ pS->WriteUChar( TMPL ); //Template
+ if (pNode->GetToken().eType == TUNDERBRACE)
+ pS->WriteUChar( tmLHBRACE ); //selector
+ else
+ pS->WriteUChar( tmUHBRACE ); //selector
+ pS->WriteUChar( 0 ); //variation
+ pS->WriteUChar( 0 ); //options
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+
+ if (nullptr != (pTemp = pNode->GetSubNode(2)))
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pTemp,nLevel+1);
+ pS->WriteUChar( END ); //options
+ }
+ pS->WriteUChar( END );
+}
+
+void MathType::HandleOperator(SmNode *pNode,int nLevel)
+{
+ if (HandleLim(pNode,nLevel))
+ return;
+
+ sal_uInt64 nPos;
+ sal_uInt8 nVariation;
+
+ switch (pNode->GetToken().eType)
+ {
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ nVariation=HandleCScript(pNode->GetSubNode(0),
+ pNode->GetSubNode(1),nLevel,&nPos,false);
+ break;
+ default:
+ nVariation=HandleCScript(pNode->GetSubNode(0),
+ pNode->GetSubNode(1),nLevel,&nPos);
+ break;
+ }
+
+ sal_uInt8 nOldVariation=nVariation;
+ sal_uInt8 nIntVariation=nVariation;
+
+ sal_uInt64 nPos2=0;
+ if (nVariation != 0xff)
+ {
+ nPos2 = pS->Tell();
+ pS->Seek(nPos);
+ if (nVariation == 2)
+ {
+ nIntVariation=0;
+ nVariation = 1;
+ }
+ else if (nVariation == 0)
+ nVariation = 1;
+ else if (nVariation == 1)
+ nVariation = 0;
+ }
+ else
+ {
+ nVariation = 2;
+ nIntVariation=0;
+ }
+ pS->WriteUChar( TMPL );
+ switch(pNode->GetToken().eType)
+ {
+ case TINT:
+ case TINTD:
+ if (nOldVariation != 0xff)
+ pS->WriteUChar( 0x18 ); //selector
+ else
+ pS->WriteUChar( 0x15 ); //selector
+ pS->WriteUChar( nIntVariation ); //variation
+ break;
+ case TIINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x19 );
+ pS->WriteUChar( 0x01 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x16 );
+ pS->WriteUChar( 0x00 );
+ }
+ break;
+ case TIIINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x1a );
+ pS->WriteUChar( 0x01 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x17 );
+ pS->WriteUChar( 0x00 );
+ }
+ break;
+ case TLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x18 );
+ pS->WriteUChar( 0x02 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x15 );
+ pS->WriteUChar( 0x03 );
+ }
+ break;
+ case TLLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x19 );
+ pS->WriteUChar( 0x00 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x16 );
+ pS->WriteUChar( 0x02 );
+ }
+ break;
+ case TLLLINT:
+ if (nOldVariation != 0xff)
+ {
+ pS->WriteUChar( 0x1a );
+ pS->WriteUChar( 0x00 );
+ }
+ else
+ {
+ pS->WriteUChar( 0x17 );
+ pS->WriteUChar( 0x02 );
+ }
+ break;
+ case TSUM:
+ default:
+ pS->WriteUChar( 0x1d );
+ pS->WriteUChar( nVariation );
+ break;
+ case TPROD:
+ pS->WriteUChar( 0x1f );
+ pS->WriteUChar( nVariation );
+ break;
+ case TCOPROD:
+ pS->WriteUChar( 0x21 );
+ pS->WriteUChar( nVariation );
+ break;
+ }
+ pS->WriteUChar( 0 ); //options
+
+ if (nPos2)
+ pS->Seek(nPos2);
+ else
+ {
+ pS->WriteUChar( LINE ); //line
+ HandleNodes(pNode->GetSubNode(1),nLevel+1);
+ pS->WriteUChar( END ); //line
+ pS->WriteUChar( LINE|0x10 );
+ pS->WriteUChar( LINE|0x10 );
+ }
+
+ pS->WriteUChar( 0x0D );
+ switch(pNode->GetToken().eType)
+ {
+ case TSUM:
+ default:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x2211 );
+ break;
+ case TPROD:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x220F );
+ break;
+ case TCOPROD:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x8B );
+ pS->WriteUInt16( 0x2210 );
+ break;
+ case TIIINT:
+ case TLLLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ [[fallthrough]];
+ case TIINT:
+ case TLLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ [[fallthrough]];
+ case TINT:
+ case TINTD:
+ case TLINT:
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x222B );
+ break;
+ }
+ pS->WriteUChar( END );
+ pS->WriteUChar( 0x0A );
+}
+
+
+bool MathType::HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
+{
+ sal_uInt8 nVAlign;
+ pS->ReadUChar( nHAlign );
+ pS->ReadUChar( nVAlign );
+
+ HandleAlign(nHAlign, rSetAlign);
+
+ rRet.append(" stack {\n");
+ bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, -1, -1 );
+ int nRemoveFrom = rRet.getLength() >= 3 ? rRet.getLength() - 3 : 0;
+ rRet.remove(nRemoveFrom, 2);
+ rRet.append("} ");
+
+ while (rSetAlign)
+ {
+ rRet.append("} ");
+ rSetAlign--;
+ }
+ return bRet;
+}
+
+bool MathType::HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation)
+{
+ sal_uInt8 nH_just,nV_just,nRows,nCols,nVAlign;
+ pS->ReadUChar( nVAlign );
+ pS->ReadUChar( nH_just );
+ pS->ReadUChar( nV_just );
+ pS->ReadUChar( nRows );
+ pS->ReadUChar( nCols );
+ if (!pS->good())
+ return false;
+ int nBytes = ((nRows+1)*2)/8;
+ if (((nRows+1)*2)%8)
+ nBytes++;
+ pS->SeekRel(nBytes);
+ nBytes = ((nCols+1)*2)/8;
+ if (((nCols+1)*2)%8)
+ nBytes++;
+ pS->SeekRel(nBytes);
+ rRet.append(" matrix {\n");
+ bool bRet = HandleRecords( nLevel+1, nSelector, nVariation, nRows, nCols );
+
+ sal_Int32 nI = rRet.lastIndexOf('#');
+ if (nI > 0)
+ if (rRet[nI-1] != '#') //missing column
+ rRet.append("{}");
+
+ rRet.append("\n} ");
+ return bRet;
+}
+
+bool MathType::HandleTemplate(int nLevel, sal_uInt8 &rSelector,
+ sal_uInt8 &rVariation, sal_Int32 &rLastTemplateBracket)
+{
+ sal_uInt8 nOption; //This appears utterly unused
+ pS->ReadUChar( rSelector );
+ pS->ReadUChar( rVariation );
+ pS->ReadUChar( nOption );
+ OSL_ENSURE(rSelector < 48,"Selector out of range");
+ if ((rSelector >= 21) && (rSelector <=26))
+ {
+ OSL_ENSURE(nOption < 2,"Option out of range");
+ }
+ else if (rSelector <= 12)
+ {
+ OSL_ENSURE(nOption < 3,"Option out of range");
+ }
+
+ //For the (broken) case where one subscript template ends, and there is
+ //another one after it, mathtype handles it as if the second one was
+ //inside the first one and renders it as sub of sub
+ bool bRemove=false;
+ if ( (rSelector == 0xf) && (rLastTemplateBracket != -1) )
+ {
+ bRemove=true;
+ for (sal_Int32 nI = rLastTemplateBracket+1; nI < rRet.getLength(); nI++ )
+ if (rRet[nI] != ' ')
+ {
+ bRemove=false;
+ break;
+ }
+ }
+
+ //suborderlist
+ bool bRet = HandleRecords( nLevel+1, rSelector, rVariation );
+
+ if (bRemove)
+ {
+ if (rLastTemplateBracket < rRet.getLength())
+ rRet.remove(rLastTemplateBracket, 1);
+ rRet.append("} ");
+ rLastTemplateBracket = -1;
+ }
+ if (rSelector == 0xf)
+ rLastTemplateBracket = rRet.lastIndexOf('}');
+ else
+ rLastTemplateBracket = -1;
+
+ rSelector = sal::static_int_cast< sal_uInt8 >(-1);
+ return bRet;
+}
+
+void MathType::HandleEmblishments()
+{
+ sal_uInt8 nEmbel;
+ do
+ {
+ pS->ReadUChar( nEmbel );
+ if (!pS->good())
+ break;
+ switch (nEmbel)
+ {
+ case 0x02:
+ rRet.append(" dot ");
+ break;
+ case 0x03:
+ rRet.append(" ddot ");
+ break;
+ case 0x04:
+ rRet.append(" dddot ");
+ break;
+ case 0x05:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," ' ");
+ nPostSup += 3;
+ break;
+ case 0x06:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," '' ");
+ nPostSup += 4;
+ break;
+ case 0x07:
+ if (!nPostlSup)
+ {
+ sPost.append(" lsup {}");
+ nPostlSup = sPost.getLength();
+ }
+ sPost.insert(nPostlSup-1," ' ");
+ nPostlSup += 3;
+ break;
+ case 0x08:
+ rRet.append(" tilde ");
+ break;
+ case 0x09:
+ rRet.append(" hat ");
+ break;
+ case 0x0b:
+ rRet.append(" vec ");
+ break;
+ case 0x10:
+ rRet.append(" overstrike ");
+ break;
+ case 0x11:
+ rRet.append(" bar ");
+ break;
+ case 0x12:
+ if (!nPostSup)
+ {
+ sPost.append(" sup {}");
+ nPostSup = sPost.getLength();
+ }
+ sPost.insert(nPostSup-1," ''' ");
+ nPostSup += 5;
+ break;
+ case 0x14:
+ rRet.append(" breve ");
+ break;
+ default:
+ OSL_ENSURE(nEmbel < 21,"Embel out of range");
+ break;
+ }
+ if (nVersion < 3)
+ break;
+ }while (nEmbel);
+}
+
+void MathType::HandleSetSize()
+{
+ sal_uInt8 nTemp(0);
+ pS->ReadUChar(nTemp);
+ switch (nTemp)
+ {
+ case 101:
+ pS->ReadInt16( nLSize );
+ nLSize = -nLSize;
+ break;
+ case 100:
+ pS->ReadUChar( nTemp );
+ nLSize = nTemp;
+ pS->ReadInt16( nDSize );
+ break;
+ default:
+ nLSize = nTemp;
+ pS->ReadUChar( nTemp );
+ nDSize = nTemp-128;
+ break;
+ }
+}
+
+bool MathType::HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel,
+ sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent)
+{
+ sal_Unicode nChar(0);
+ bool bRet = true;
+
+ if (xfAUTO(nTag))
+ {
+ //This is a candidate for function recognition, whatever
+ //that is!
+ }
+
+ sal_uInt8 nOldTypeFace = nTypeFace;
+ pS->ReadUChar( nTypeFace );
+ if (nVersion < 3)
+ {
+ sal_uInt8 nChar8(0);
+ pS->ReadUChar( nChar8 );
+ nChar = nChar8;
+ }
+ else
+ pS->ReadUtf16( nChar );
+
+ /*
+ bad character, old mathtype < 3 has these
+ */
+ if (nChar < 0x20)
+ return bRet;
+
+ if (xfEMBELL(nTag))
+ {
+ //A bit tricky, the character emblishments for
+ //mathtype can all be listed after each other, in
+ //starmath some must go before the character and some
+ //must go after. In addition some of the emblishments
+ //may repeated and in starmath some of these groups
+ //must be gathered together. sPost is the portion that
+ //follows the char and nPostSup and nPostlSup are the
+ //indexes at which this class of emblishment is
+ //collated together
+ sPost = "";
+ nPostSup = nPostlSup = 0;
+ int nOriglen=rRet.getLength()-rTextStart;
+ rRet.append(" {"); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
+ if ((!bSilent) && (nOriglen > 1))
+ rRet.append("\"");
+ bRet = HandleRecords( nLevel+1, nSelector, nVariation );
+ if (!bSilent)
+ {
+ if (nOriglen > 1)
+ {
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(std::min(rTextStart, rRet.getLength()), aStr);
+
+ aStr.clear();
+ TypeFaceToString(aStr,nTypeFace);
+ rRet.append(aStr + "{");
+ }
+ else
+ rRet.append(" {");
+ rTextStart = rRet.getLength();
+ }
+ }
+
+ if (!bSilent)
+ {
+ sal_Int32 nOldLen = rRet.getLength();
+ if (
+ HandleSize(nLSize,nDSize,rSetSize) ||
+ (nOldTypeFace != nTypeFace)
+ )
+ {
+ if ((nOldLen - rTextStart) > 1)
+ {
+ rRet.insert(nOldLen, "\"");
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(rTextStart,aStr);
+ }
+ rTextStart = rRet.getLength();
+ }
+ nOldLen = rRet.getLength();
+ if (!LookupChar(nChar,rRet,nVersion,nTypeFace))
+ {
+ if (nOldLen - rTextStart > 1)
+ {
+ rRet.insert(nOldLen, "\"");
+ OUString aStr;
+ TypeFaceToString(aStr,nOldTypeFace);
+ aStr += "\"";
+ rRet.insert(rTextStart, aStr);
+ }
+ rTextStart = rRet.getLength();
+ }
+ lcl_PrependDummyTerm(rRet, rTextStart);
+ }
+
+ if ((xfEMBELL(nTag)) && (!bSilent))
+ {
+ rRet.append("}}" + sPost); // #i24340# make what would be "vec {A}_n" become "{vec {A}}_n"
+ rTextStart = rRet.getLength();
+ }
+ return bRet;
+}
+
+bool MathType::HandleLim(SmNode *pNode,int nLevel)
+{
+ bool bRet=false;
+ //Special case for the "lim" option in StarMath
+ if ((pNode->GetToken().eType == TLIM)
+ || (pNode->GetToken().eType == TLIMSUP)
+ || (pNode->GetToken().eType == TLIMINF)
+ )
+ {
+ if (pNode->GetSubNode(1))
+ {
+ sal_uInt8 nVariation2=HandleCScript(pNode->GetSubNode(0),nullptr,
+ nLevel);
+
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( LINE ); //line
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'l' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'i' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'm' );
+
+ if (pNode->GetToken().eType == TLIMSUP)
+ {
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 's' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'u' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'p' );
+ }
+ else if (pNode->GetToken().eType == TLIMINF)
+ {
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'i' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'n' );
+ pS->WriteUChar( CHAR|0x10 );
+ pS->WriteUChar( 0x82 );
+ pS->WriteUInt16( 'f' );
+ }
+
+
+ pS->WriteUChar( CHAR ); //some space
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB04 );
+
+ if (nVariation2 != 0xff)
+ {
+ pS->WriteUChar( END );
+ pS->WriteUChar( END );
+ }
+ HandleNodes(pNode->GetSubNode(1),nLevel+1);
+ bRet = true;
+ }
+ }
+ return bRet;
+}
+
+void MathType::HandleMAlign(SmNode *pNode,int nLevel)
+{
+ sal_uInt8 nPushedHAlign=nHAlign;
+ switch(pNode->GetToken().eType)
+ {
+ case TALIGNC:
+ nHAlign=2;
+ break;
+ case TALIGNR:
+ nHAlign=3;
+ break;
+ default:
+ nHAlign=1;
+ break;
+ }
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (SmNode *pTemp = pNode->GetSubNode(i))
+ HandleNodes(pTemp,nLevel+1);
+ }
+ nHAlign=nPushedHAlign;
+}
+
+void MathType::HandleMath(SmNode *pNode)
+{
+ if (pNode->GetToken().eType == TMLINE)
+ {
+ pS->WriteUChar( END );
+ pS->WriteUChar( LINE );
+ bIsReInterpBrace=true;
+ return;
+ }
+ SmMathSymbolNode *pTemp = static_cast<SmMathSymbolNode *>(pNode);
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+ sal_Unicode nArse = SmTextNode::ConvertSymbolToUnicode(pTemp->GetText()[i]);
+ if ((nArse == 0x2224) || (nArse == 0x2288) || (nArse == 0x2285) ||
+ (nArse == 0x2289))
+ {
+ pS->WriteUChar( CHAR|0x20 );
+ }
+ else if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( 0x22 );
+ }
+ else
+ pS->WriteUChar( CHAR ); //char without formula recognition
+ //The typeface seems to be MTEXTRA for unicode characters,
+ //though how to determine when mathtype chooses one over
+ //the other is unknown. This should do the trick
+ //nevertheless.
+ sal_uInt8 nBias;
+ if ( (nArse == 0x2213) || (nArse == 0x2218) ||
+ (nArse == 0x210F) || (
+ (nArse >= 0x22EE) && (nArse <= 0x22FF)
+ ))
+ {
+ nBias = 0xB; //typeface
+ }
+ else if ((nArse == 0x2F) || (nArse == 0x2225))
+ nBias = 0x2; //typeface
+ else if ((nArse > 0x2000) || (nArse == 0x00D7))
+ nBias = 0x6; //typeface
+ else if (nArse == 0x3d1)
+ nBias = 0x4;
+ else if ((nArse > 0xFF) && ((nArse < 0x393) || (nArse > 0x3c9)))
+ nBias = 0xB; //typeface
+ else
+ nBias = 0x3; //typeface
+
+ pS->WriteUChar( nSpec+nBias+128 ); //typeface
+
+ if (nArse == 0x2224)
+ {
+ pS->WriteUInt16( 0x7C );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2225)
+ pS->WriteUInt16( 0xEC09 );
+ else if (nArse == 0xE421)
+ pS->WriteUInt16( 0x2265 );
+ else if (nArse == 0x230A)
+ pS->WriteUInt16( 0xF8F0 );
+ else if (nArse == 0x230B)
+ pS->WriteUInt16( 0xF8FB );
+ else if (nArse == 0xE425)
+ pS->WriteUInt16( 0x2264 );
+ else if (nArse == 0x226A)
+ {
+ pS->WriteUInt16( 0x3C );
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x98 );
+ pS->WriteUInt16( 0xEB01 );
+ pS->WriteUChar( CHAR );
+ pS->WriteUChar( 0x86 );
+ pS->WriteUInt16( 0x3c );
+ }
+ else if (nArse == 0x2288)
+ {
+ pS->WriteUInt16( 0x2286 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2289)
+ {
+ pS->WriteUInt16( 0x2287 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else if (nArse == 0x2285)
+ {
+ pS->WriteUInt16( 0x2283 );
+ pS->WriteUChar( EMBEL );
+ pS->WriteUChar( 0x0A );
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ else
+ pS->WriteUInt16( nArse );
+ }
+ nPendingAttributes = 0;
+}
+
+void MathType::HandleAttributes(SmNode *pNode,int nLevel)
+{
+ int nOldPending = 0;
+ SmNode *pTemp = nullptr;
+ SmTextNode *pIsText = nullptr;
+
+ if (nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ pIsText = static_cast<SmTextNode *>(pNode->GetSubNode(1));
+
+ switch (pTemp->GetToken().eType)
+ {
+ case TWIDEVEC:
+ //there's just no way we can now handle any character
+ //attributes (from mathtypes perspective) centered
+ //over an expression but above template attributes
+ //such as widevec and similar constructs
+ //we have to drop them
+ nOldPending = StartTemplate(0x2f,0x01);
+ break;
+ case TCHECK: //Not Exportable
+ case TACUTE: //Not Exportable
+ case TGRAVE: //Not Exportable
+ case TCIRCLE: //Not Exportable
+ case TWIDEHARPOON: //Not Exportable
+ case TWIDETILDE: //Not Exportable
+ case TWIDEHAT: //Not Exportable
+ break;
+ case TUNDERLINE:
+ nOldPending = StartTemplate(0x10);
+ break;
+ case TOVERLINE: //If the next node is not text
+ //or text with more than one char
+ if ((pIsText->GetToken().eType != TTEXT) ||
+ (pIsText->GetText().getLength() > 1))
+ nOldPending = StartTemplate(0x11);
+ break;
+ default:
+ nPendingAttributes++;
+ break;
+ }
+ }
+
+ if (pIsText)
+ HandleNodes(pIsText,nLevel+1);
+
+ switch (pTemp->GetToken().eType)
+ {
+ case TWIDEVEC:
+ case TUNDERLINE:
+ EndTemplate(nOldPending);
+ break;
+ case TOVERLINE:
+ if ((pIsText->GetToken().eType != TTEXT) ||
+ (pIsText->GetText().getLength() > 1))
+ EndTemplate(nOldPending);
+ break;
+ default:
+ break;
+ }
+
+ //if there was no suitable place to put the attribute,
+ //then we have to just give up on it
+ if (nPendingAttributes)
+ nPendingAttributes--;
+ else
+ {
+ if ((nInsertion != 0) && nullptr != (pTemp = pNode->GetSubNode(0)))
+ {
+ auto nPos = pS->Tell();
+ nInsertion--;
+ pS->Seek(nInsertion);
+ switch(pTemp->GetToken().eType)
+ {
+ case TACUTE: //Not Exportable
+ case TGRAVE: //Not Exportable
+ case TCIRCLE: //Not Exportable
+ break;
+ case TCDOT:
+ pS->WriteUChar( 2 );
+ break;
+ case TDDOT:
+ pS->WriteUChar( 3 );
+ break;
+ case TDDDOT:
+ pS->WriteUChar( 4 );
+ break;
+ case TTILDE:
+ pS->WriteUChar( 8 );
+ break;
+ case THAT:
+ pS->WriteUChar( 9 );
+ break;
+ case TVEC:
+ pS->WriteUChar( 11 );
+ break;
+ case TOVERSTRIKE:
+ pS->WriteUChar( 16 );
+ break;
+ case TOVERLINE:
+ if ((pIsText->GetToken().eType == TTEXT) &&
+ (pIsText->GetText().getLength() == 1))
+ pS->WriteUChar( 17 );
+ break;
+ case TBREVE:
+ pS->WriteUChar( 20 );
+ break;
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ case TUNDERLINE:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ break;
+ case TBAR:
+ pS->WriteUChar( 17 );
+ break;
+ default:
+ pS->WriteUChar( 2 );
+ break;
+ }
+ pS->Seek(nPos);
+ }
+ }
+}
+
+void MathType::HandleText(SmNode *pNode)
+{
+ SmTextNode *pTemp = static_cast<SmTextNode *>(pNode);
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+ if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( 0x22 ); //char, with attributes right
+ //after the character
+ }
+ else
+ pS->WriteUChar( CHAR );
+
+ sal_uInt8 nFace = 0x1;
+ if (pNode->GetFont().GetItalic() == ITALIC_NORMAL)
+ nFace = 0x3;
+ else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD)
+ nFace = 0x7;
+ pS->WriteUChar( nFace+128 ); //typeface
+ sal_uInt16 nChar = pTemp->GetText()[i];
+ pS->WriteUInt16( SmTextNode::ConvertSymbolToUnicode(nChar) );
+
+ //Mathtype can only have these sort of character
+ //attributes on a single character, starmath can put them
+ //anywhere, when the entity involved is a text run this is
+ //a large effort to place the character attribute on the
+ //central mathtype character so that it does pretty much
+ //what the user probably has in mind. The attributes
+ //filled in here are dummy ones which are replaced in the
+ //ATTRIBUTE handler if a suitable location for the
+ //attributes was found here. Unfortunately it is
+ //possible for starmath to place character attributes on
+ //entities which cannot occur in mathtype e.g. a Summation
+ //symbol so these attributes may be lost
+ if (nPendingAttributes &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ pS->WriteUChar( EMBEL );
+ while (nPendingAttributes)
+ {
+ pS->WriteUChar( 2 );
+ //wedge the attributes in here and clear
+ //the pending stack
+ nPendingAttributes--;
+ }
+ nInsertion=pS->Tell();
+ pS->WriteUChar( END ); //end embel
+ pS->WriteUChar( END ); //end embel
+ }
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportMathType(SvStream &rStream)
+{
+ OUStringBuffer sText;
+ MathType aEquation(sText);
+ bool bRet = false;
+ try
+ {
+ bRet = aEquation.Parse(&rStream);
+ }
+ catch (const std::out_of_range&)
+ {
+ }
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/mathtype.hxx b/starmath/source/mathtype.hxx
new file mode 100644
index 000000000..cd06cfcdd
--- /dev/null
+++ b/starmath/source/mathtype.hxx
@@ -0,0 +1,184 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <node.hxx>
+
+#include <o3tl/sorted_vector.hxx>
+
+class SfxMedium;
+class SotStorage;
+class SvStream;
+
+class MathTypeFont
+{
+public:
+ sal_uInt8 nTface;
+ sal_uInt8 nStyle;
+ MathTypeFont() : nTface(0),nStyle(0) {}
+ explicit MathTypeFont(sal_uInt8 nFace) : nTface(nFace),nStyle(0) {}
+ void AppendStyleToText(OUString &rS);
+};
+
+struct LessMathTypeFont
+{
+ bool operator() (const MathTypeFont &rValue1,
+ const MathTypeFont &rValue2) const
+ {
+ return rValue1.nTface < rValue2.nTface;
+ }
+};
+
+typedef o3tl::sorted_vector< MathTypeFont, LessMathTypeFont > MathTypeFontSet;
+
+class MathType
+{
+public:
+ explicit MathType(OUStringBuffer &rIn)
+ : nVersion(0)
+ , pS(nullptr)
+ , rRet(rIn)
+ , pTree(nullptr)
+ , nHAlign(0)
+ , nPendingAttributes(0)
+ , nInsertion(0)
+ , nLSize(0)
+ , nDSize(0)
+ , nCurSize(0)
+ , nLastSize(0)
+ , nSpec(0)
+ , bIsReInterpBrace(false)
+ , nPostSup(0)
+ , nPostlSup(0)
+ , nTypeFace(0)
+ {
+ Init();
+ }
+
+ MathType(OUStringBuffer &rIn,SmNode *pIn)
+ : nVersion(0)
+ , pS(nullptr)
+ , rRet(rIn)
+ , pTree(pIn)
+ , nHAlign(2)
+ , nPendingAttributes(0)
+ , nInsertion(0)
+ , nLSize(0)
+ , nDSize(0)
+ , nCurSize(0)
+ , nLastSize(0)
+ , nSpec(0)
+ , bIsReInterpBrace(false)
+ , nPostSup(0)
+ , nPostlSup(0)
+ , nTypeFace(0)
+ {
+ Init();
+ }
+
+ bool Parse(SotStorage* pStor);
+ bool Parse(SvStream* pStream);
+ bool ConvertFromStarMath( SfxMedium& rMedium );
+
+private:
+/*Ver 2 Header*/
+ sal_uInt8 nVersion;
+
+ SvStream* pS;
+
+ void Init();
+
+ bool HandleRecords(int nLevel, sal_uInt8 nSelector =0xFF,
+ sal_uInt8 nVariation =0xFF, int nRows =0, int nCols =0);
+ bool HandleSize(sal_Int16 nLSize, sal_Int16 nDSize, int &rSetSize);
+ void HandleAlign(sal_uInt8 nHAlign, int &rSetAlign);
+ bool HandlePile(int &rSetAlign, int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariation);
+ bool HandleMatrix(int nLevel, sal_uInt8 nSelector, sal_uInt8 nVariarion);
+ void HandleMatrixSeparator(int nMatrixRows, int nMatrixCols, int &rCurCol, int &rCurRow);
+ bool HandleTemplate(int nLevel, sal_uInt8 &rSelector, sal_uInt8 &rVariation,
+ sal_Int32 &rLastTemplateBracket);
+ void HandleEmblishments();
+ void HandleSetSize();
+ bool HandleChar(sal_Int32 &rTextStart, int &rSetSize, int nLevel,
+ sal_uInt8 nTag, sal_uInt8 nSelector, sal_uInt8 nVariation, bool bSilent);
+ void HandleNudge();
+
+ static int xfLMOVE(sal_uInt8 nTest) {return nTest&0x80;}
+ static int xfAUTO(sal_uInt8 nTest) {return nTest&0x10;}
+ static int xfEMBELL(sal_uInt8 nTest) {return nTest&0x20;}
+ static int xfNULL(sal_uInt8 nTest) {return nTest&0x10;}
+
+ void HandleNodes(SmNode *pNode,int nLevel);
+ int StartTemplate(sal_uInt16 nSelector,sal_uInt16 nVariation=0);
+ void EndTemplate(int nOldPendingAttributes);
+ void HandleSmMatrix(SmMatrixNode *pMatrix,int nLevel);
+ void HandleTable(SmNode *pNode,int nLevel);
+ void HandleRoot(SmNode *pNode,int nLevel);
+ void HandleSubSupScript(SmNode *pNode,int nLevel);
+ sal_uInt8 HandleCScript(SmNode *pNode,SmNode *pContent,int nLevel,
+ sal_uInt64 *pPos=nullptr,bool bTest=true);
+ void HandleFractions(SmNode *pNode,int nLevel);
+ void HandleBrace(SmNode *pNode,int nLevel);
+ void HandleVerticalBrace(SmNode *pNode,int nLevel);
+ void HandleOperator(SmNode *pNode,int nLevel);
+ bool HandleLim(SmNode *pNode,int nLevel);
+ void HandleMAlign(SmNode *pNode,int nLevel);
+ void HandleMath(SmNode *pNode);
+ void HandleText(SmNode *pNode);
+ void HandleAttributes(SmNode *pNode,int nLevel);
+ void TypeFaceToString(OUString &rRet,sal_uInt8 nFace);
+
+ OUStringBuffer &rRet;
+ SmNode *pTree;
+
+ sal_uInt8 nHAlign;
+
+ int nPendingAttributes;
+ sal_uInt64 nInsertion;
+
+ sal_Int16 nLSize;
+ sal_Int16 nDSize;
+ sal_Int16 nCurSize;
+ sal_Int16 nLastSize;
+ sal_uInt8 nSpec;
+ bool bIsReInterpBrace;
+ OUStringBuffer sPost;
+ sal_Int32 nPostSup;
+ sal_Int32 nPostlSup;
+ sal_uInt8 nTypeFace;
+ MathTypeFontSet aUserStyles;
+
+ enum MTOKENS {END,LINE,CHAR,TMPL,PILE,MATRIX,EMBEL,RULER,FONT,SIZE};
+ enum MTEMPLATES
+ {
+ tmANGLE,tmPAREN,tmBRACE,tmBRACK,tmBAR,tmDBAR,tmFLOOR,tmCEILING,
+ tmLBLB,tmRBRB,tmRBLB,tmLBRP,tmLPRB,tmROOT,tmFRACT,tmSCRIPT,tmUBAR,
+ tmOBAR,tmLARROW,tmRARROW,tmBARROW,tmSINT,tmDINT,tmTINT,tmSSINT,
+ tmDSINT,tmTSINT,tmUHBRACE,tmLHBRACE,tmSUM,tmISUM,tmPROD,tmIPROD,
+ tmCOPROD,tmICOPROD,tmUNION,tmIUNION,tmINTER,tmIINTER,tmLIM,tmLDIV,
+ tmSLFRACT,tmINTOP,tmSUMOP,tmLSCRIPT,tmDIRAC,tmUARROW,tmOARROW,
+ tmOARC
+ };
+public:
+ static bool LookupChar(sal_Unicode nChar,OUStringBuffer &rRet,
+ sal_uInt8 nVersion,sal_uInt8 nTypeFace=0);
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/node.cxx b/starmath/source/node.cxx
new file mode 100644
index 000000000..c4c013b15
--- /dev/null
+++ b/starmath/source/node.cxx
@@ -0,0 +1,2486 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <symbol.hxx>
+#include <smmod.hxx>
+#include "tmpdevice.hxx"
+#include <visitors.hxx>
+#include <vcl/metric.hxx>
+#include <osl/diagnose.h>
+#include <basegfx/numeric/ftools.hxx>
+
+namespace {
+
+template<typename F>
+void ForEachNonNull(SmNode *pNode, F && f)
+{
+ size_t nSize = pNode->GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode *pSubNode = pNode->GetSubNode(i);
+ if (pSubNode != nullptr)
+ f(pSubNode);
+ }
+}
+
+}
+
+SmNode::SmNode(SmNodeType eNodeType, const SmToken &rNodeToken)
+ : maNodeToken( rNodeToken )
+ , meType( eNodeType )
+ , meScaleMode( SmScaleMode::None )
+ , meRectHorAlign( RectHorAlign::Left )
+ , mnFlags( FontChangeMask::None )
+ , mnAttributes( FontAttribute::None )
+ , mbIsPhantom( false )
+ , mbIsSelected( false )
+ , mnAccIndex( -1 )
+ , mpParentNode( nullptr )
+{
+}
+
+SmNode::~SmNode()
+{
+}
+
+const SmNode * SmNode::GetLeftMost() const
+ // returns leftmost node of current subtree.
+ //! (this assumes the one with index 0 is always the leftmost subnode
+ //! for the current node).
+{
+ const SmNode *pNode = GetNumSubNodes() > 0 ?
+ GetSubNode(0) : nullptr;
+
+ return pNode ? pNode->GetLeftMost() : this;
+}
+
+
+void SmNode::SetPhantom(bool bIsPhantomP)
+{
+ if (! (Flags() & FontChangeMask::Phantom))
+ mbIsPhantom = bIsPhantomP;
+
+ bool b = mbIsPhantom;
+ ForEachNonNull(this, [b](SmNode *pNode){pNode->SetPhantom(b);});
+}
+
+
+void SmNode::SetColor(const Color& rColor)
+{
+ if (! (Flags() & FontChangeMask::Color))
+ GetFont().SetColor(rColor);
+
+ ForEachNonNull(this, [&rColor](SmNode *pNode){pNode->SetColor(rColor);});
+}
+
+
+void SmNode::SetAttribute(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes |= nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->SetAttribute(nAttrib);});
+}
+
+
+void SmNode::ClearAttribute(FontAttribute nAttrib)
+{
+ if (
+ (nAttrib == FontAttribute::Bold && !(Flags() & FontChangeMask::Bold)) ||
+ (nAttrib == FontAttribute::Italic && !(Flags() & FontChangeMask::Italic))
+ )
+ {
+ mnAttributes &= ~nAttrib;
+ }
+
+ ForEachNonNull(this, [nAttrib](SmNode *pNode){pNode->ClearAttribute(nAttrib);});
+}
+
+
+void SmNode::SetFont(const SmFace &rFace)
+{
+ if (!(Flags() & FontChangeMask::Face))
+ GetFont() = rFace;
+ ForEachNonNull(this, [&rFace](SmNode *pNode){pNode->SetFont(rFace);});
+}
+
+
+void SmNode::SetFontSize(const Fraction &rSize, FontSizeType nType)
+ //! 'rSize' is in units of pts
+{
+ Size aFntSize;
+
+ if (!(Flags() & FontChangeMask::Size))
+ {
+ Fraction aVal (SmPtsTo100th_mm(rSize.GetNumerator()),
+ rSize.GetDenominator());
+ tools::Long nHeight = static_cast<tools::Long>(aVal);
+
+ aFntSize = GetFont().GetFontSize();
+ aFntSize.setWidth( 0 );
+ switch(nType)
+ {
+ case FontSizeType::ABSOLUT:
+ aFntSize.setHeight( nHeight );
+ break;
+
+ case FontSizeType::PLUS:
+ aFntSize.AdjustHeight(nHeight );
+ break;
+
+ case FontSizeType::MINUS:
+ aFntSize.AdjustHeight( -nHeight );
+ break;
+
+ case FontSizeType::MULTIPLY:
+ aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) * rSize) );
+ break;
+
+ case FontSizeType::DIVIDE:
+ if (rSize != Fraction(0))
+ aFntSize.setHeight( static_cast<tools::Long>(Fraction(aFntSize.Height()) / rSize) );
+ break;
+ default:
+ break;
+ }
+
+ // check the requested size against maximum value
+ static int const nMaxVal = SmPtsTo100th_mm(128);
+ if (aFntSize.Height() > nMaxVal)
+ aFntSize.setHeight( nMaxVal );
+
+ GetFont().SetSize(aFntSize);
+ }
+
+ ForEachNonNull(this, [&rSize, &nType](SmNode *pNode){pNode->SetFontSize(rSize, nType);});
+}
+
+
+void SmNode::SetSize(const Fraction &rSize)
+{
+ GetFont() *= rSize;
+
+ ForEachNonNull(this, [&rSize](SmNode *pNode){pNode->SetSize(rSize);});
+}
+
+
+void SmNode::SetRectHorAlign(RectHorAlign eHorAlign, bool bApplyToSubTree )
+{
+ meRectHorAlign = eHorAlign;
+
+ if (bApplyToSubTree)
+ ForEachNonNull(this, [eHorAlign](SmNode *pNode){pNode->SetRectHorAlign(eHorAlign);});
+}
+
+
+void SmNode::PrepareAttributes()
+{
+ GetFont().SetWeight((Attributes() & FontAttribute::Bold) ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ GetFont().SetItalic((Attributes() & FontAttribute::Italic) ? ITALIC_NORMAL : ITALIC_NONE);
+}
+
+
+void SmNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ if (nDepth > 1024)
+ throw std::range_error("parser depth limit");
+
+ mbIsPhantom = false;
+ mnFlags = FontChangeMask::None;
+ mnAttributes = FontAttribute::None;
+
+ switch (rFormat.GetHorAlign())
+ { case SmHorAlign::Left: meRectHorAlign = RectHorAlign::Left; break;
+ case SmHorAlign::Center: meRectHorAlign = RectHorAlign::Center; break;
+ case SmHorAlign::Right: meRectHorAlign = RectHorAlign::Right; break;
+ }
+
+ GetFont() = rFormat.GetFont(FNT_MATH);
+ OSL_ENSURE( GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "unexpected CharSet" );
+ GetFont().SetWeight(WEIGHT_NORMAL);
+ GetFont().SetItalic(ITALIC_NONE);
+
+ ForEachNonNull(this, [&rFormat, &rDocShell, nDepth](SmNode *pNode){pNode->Prepare(rFormat, rDocShell, nDepth + 1);});
+}
+
+void SmNode::Move(const Point& rVector)
+{
+ if (rVector.X() == 0 && rVector.Y() == 0)
+ return;
+
+ SmRect::Move(rVector);
+
+ ForEachNonNull(this, [&rVector](SmNode *pNode){pNode->Move(rVector);});
+}
+
+void SmNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong /*nWidth*/)
+{
+}
+
+
+void SmNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong /*nHeight*/)
+{
+}
+
+
+const SmNode * SmNode::FindTokenAt(sal_uInt16 nRow, sal_uInt16 nCol) const
+ // returns (first) ** visible ** (sub)node with the tokens text at
+ // position 'nRow', 'nCol'.
+ //! (there should be exactly one such node if any)
+{
+ if ( IsVisible()
+ && nRow == GetSelection().nStartPara
+ && nCol >= GetSelection().nStartPos && nCol <= GetSelection().nEndPos )
+ return this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pResult = pNode->FindTokenAt(nRow, nCol);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return nullptr;
+}
+
+
+const SmNode * SmNode::FindRectClosestTo(const Point &rPoint) const
+{
+ tools::Long nDist = LONG_MAX;
+ const SmNode *pResult = nullptr;
+
+ if (IsVisible())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+
+ if (!pNode)
+ continue;
+
+ const SmNode *pFound = pNode->FindRectClosestTo(rPoint);
+ if (pFound)
+ {
+ tools::Long nTmp = pFound->OrientedDist(rPoint);
+ if (nTmp < nDist)
+ {
+ nDist = nTmp;
+ pResult = pFound;
+
+ // quit immediately if 'rPoint' is inside the *should not
+ // overlap with other rectangles* part.
+ // This (partly) serves for getting the attributes in eg
+ // "bar overstrike a".
+ // ('nDist < 0' is used as *quick shot* to avoid evaluation of
+ // the following expression, where the result is already determined)
+ if (nDist < 0 && pFound->IsInsideRect(rPoint))
+ break;
+ }
+ }
+ }
+ }
+
+ return pResult;
+}
+
+const SmNode * SmNode::FindNodeWithAccessibleIndex(sal_Int32 nAccIdx) const
+{
+ const SmNode *pResult = nullptr;
+
+ sal_Int32 nIdx = GetAccessibleIndex();
+ OUStringBuffer aTxt;
+ if (nIdx >= 0)
+ GetAccessibleText( aTxt ); // get text if used in following 'if' statement
+
+ if (nIdx >= 0
+ && nIdx <= nAccIdx && nAccIdx < nIdx + aTxt.getLength())
+ pResult = this;
+ else
+ {
+ size_t nNumSubNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNumSubNodes; ++i)
+ {
+ const SmNode *pNode = GetSubNode(i);
+ if (!pNode)
+ continue;
+
+ pResult = pNode->FindNodeWithAccessibleIndex(nAccIdx);
+ if (pResult)
+ return pResult;
+ }
+ }
+
+ return pResult;
+}
+
+
+SmStructureNode::~SmStructureNode()
+{
+ ForEachNonNull(this, std::default_delete<SmNode>());
+}
+
+
+void SmStructureNode::ClearSubNodes()
+{
+ maSubNodes.clear();
+}
+
+void SmStructureNode::SetSubNodes(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
+{
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[1] = pSecond.release();
+ if (pThird)
+ maSubNodes[2] = pThird.release();
+
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodes(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
+{
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst;
+ if (pSecond)
+ maSubNodes[1] = pSecond;
+ if (pThird)
+ maSubNodes[2] = pThird;
+
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodesBinMo(std::unique_ptr<SmNode> pFirst, std::unique_ptr<SmNode> pSecond, std::unique_ptr<SmNode> pThird)
+{
+ if(GetType()==SmNodeType::BinDiagonal)
+ {
+ size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[2] = pSecond.release();
+ if (pThird)
+ maSubNodes[1] = pThird.release();
+ }
+ else
+ {
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst.release();
+ if (pSecond)
+ maSubNodes[1] = pSecond.release();
+ if (pThird)
+ maSubNodes[2] = pThird.release();
+ }
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodesBinMo(SmNode* pFirst, SmNode* pSecond, SmNode* pThird)
+{
+ if(GetType()==SmNodeType::BinDiagonal)
+ {
+ size_t nSize = pSecond ? 3 : (pThird ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst;
+ if (pSecond)
+ maSubNodes[2] = pSecond;
+ if (pThird)
+ maSubNodes[1] = pThird;
+ }
+ else
+ {
+ size_t nSize = pThird ? 3 : (pSecond ? 2 : (pFirst ? 1 : 0));
+ maSubNodes.resize( nSize );
+ if (pFirst)
+ maSubNodes[0] = pFirst;
+ if (pSecond)
+ maSubNodes[1] = pSecond;
+ if (pThird)
+ maSubNodes[2] = pThird;
+ }
+ ClaimPaternity();
+}
+
+void SmStructureNode::SetSubNodes(SmNodeArray&& rNodeArray)
+{
+ maSubNodes = std::move(rNodeArray);
+ ClaimPaternity();
+}
+
+bool SmStructureNode::IsVisible() const
+{
+ return false;
+}
+
+size_t SmStructureNode::GetNumSubNodes() const
+{
+ return maSubNodes.size();
+}
+
+SmNode* SmStructureNode::GetSubNode(size_t nIndex)
+{
+ return maSubNodes[nIndex];
+}
+
+SmNode* SmStructureNode::GetSubNodeBinMo(size_t nIndex) const
+{
+ if(GetType()==SmNodeType::BinDiagonal)
+ {
+ if (nIndex==1)
+ nIndex = 2;
+ else if (nIndex==2)
+ nIndex = 1;
+ }
+ return maSubNodes[nIndex];
+}
+
+void SmStructureNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ ForEachNonNull(const_cast<SmStructureNode *>(this),
+ [&rText](SmNode *pNode)
+ {
+ if (pNode->IsVisible())
+ pNode->SetAccessibleIndex(rText.getLength());
+ pNode->GetAccessibleText( rText );
+ });
+}
+
+void SmStructureNode::ClaimPaternity()
+{
+ ForEachNonNull(this, [this](SmNode *pNode){pNode->SetParent(this);});
+}
+
+int SmStructureNode::IndexOfSubNode(SmNode const * pSubNode)
+{
+ size_t nSize = GetNumSubNodes();
+ for (size_t i = 0; i < nSize; i++)
+ if (pSubNode == GetSubNode(i))
+ return i;
+ return -1;
+}
+
+void SmStructureNode::SetSubNode(size_t nIndex, SmNode* pNode)
+{
+ size_t size = maSubNodes.size();
+ if (size <= nIndex)
+ {
+ //Resize subnodes array
+ maSubNodes.resize(nIndex + 1);
+ //Set new slots to NULL except at nIndex
+ for (size_t i = size; i < nIndex; i++)
+ maSubNodes[i] = nullptr;
+ }
+ maSubNodes[nIndex] = pNode;
+ if (pNode)
+ pNode->SetParent(this);
+}
+
+bool SmVisibleNode::IsVisible() const
+{
+ return true;
+}
+
+size_t SmVisibleNode::GetNumSubNodes() const
+{
+ return 0;
+}
+
+SmNode * SmVisibleNode::GetSubNode(size_t /*nIndex*/)
+{
+ return nullptr;
+}
+
+void SmGraphicNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(GetToken().aText);
+}
+
+void SmTableNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one column
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+
+ // make distance depend on font size
+ tools::Long nDist = +(rFormat.GetDistance(DIS_VERTICAL)
+ * GetFont().GetFontSize().Height()) / 100;
+
+ if (nSize < 1)
+ return;
+
+ // arrange subnodes and get maximum width of them
+ tools::Long nMaxWidth = 0,
+ nTmp;
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { pNode->Arrange(rDev, rFormat);
+ if ((nTmp = pNode->GetItalicWidth()) > nMaxWidth)
+ nMaxWidth = nTmp;
+ }
+ }
+
+ Point aPos;
+ SmRect::operator = (SmRect(nMaxWidth, 1));
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ { const SmRect &rNodeRect = pNode->GetRect();
+ const SmNode *pCoNode = pNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ aPos = rNodeRect.AlignTo(*this, RectPos::Bottom,
+ eHorAlign, RectVerAlign::Baseline);
+ if (i)
+ aPos.AdjustY(nDist );
+ pNode->MoveTo(aPos);
+ ExtendBy(rNodeRect, nSize > 1 ? RectCopyMBL::None : RectCopyMBL::Arg);
+ }
+ }
+ // #i972#
+ if (HasBaseline())
+ mnFormulaBaseline = GetBaseline();
+ else
+ {
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect aRect(aTmpDev, &rFormat, "a", GetFont().GetBorderWidth());
+ mnFormulaBaseline = GetAlignM();
+ // move from middle position by constant - distance
+ // between middle and baseline for single letter
+ mnFormulaBaseline += aRect.GetBaseline() - aRect.GetAlignM();
+ }
+}
+
+const SmNode * SmTableNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+tools::Long SmTableNode::GetFormulaBaseline() const
+{
+ return mnFormulaBaseline;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here we use the 'FNT_VARIABLE' font since it's ascent and descent in general fit better
+ // to the rest of the formula compared to the 'FNT_MATH' font.
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ Flags() |= FontChangeMask::Face;
+}
+
+
+/**************************************************************************/
+
+
+void SmLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // arranges all subnodes in one row with some extra space between
+{
+ SmNode *pNode;
+ size_t nSize = GetNumSubNodes();
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ if (nSize < 1)
+ {
+ // provide an empty rectangle with alignment parameters for the "current"
+ // font (in order to make "a^1 {}_2^3 a_4" work correct, that is, have the
+ // same sub-/supscript positions.)
+ //! be sure to use a character that has explicitly defined HiAttribut
+ //! line in rect.cxx such as 'a' in order to make 'vec a' look same to
+ //! 'vec {a}'.
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, "a",
+ GetFont().GetBorderWidth()));
+ // make sure that the rectangle occupies (almost) no space
+ SetWidth(1);
+ SetItalicSpaces(0, 0);
+ return;
+ }
+
+ // make distance depend on font size
+ tools::Long nDist = (rFormat.GetDistance(DIS_HORIZONTAL) * GetFont().GetFontSize().Height()) / 100;
+ if (!IsUseExtraSpaces())
+ nDist = 0;
+
+ Point aPos;
+ // copy the first node into LineNode and extend by the others
+ if (nullptr != (pNode = GetSubNode(0)))
+ SmRect::operator = (pNode->GetRect());
+
+ for (size_t i = 1; i < nSize; ++i)
+ {
+ if (nullptr != (pNode = GetSubNode(i)))
+ {
+ aPos = pNode->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // add horizontal space to the left for each but the first sub node
+ aPos.AdjustX(nDist );
+
+ pNode->MoveTo(aPos);
+ ExtendBy( *pNode, RectCopyMBL::Xor );
+ }
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmExpressionNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // as 'SmLineNode::Arrange' but keeps alignment of leftmost subnode
+{
+ SmLineNode::Arrange(rDev, rFormat);
+
+ // copy alignment of leftmost subnode if any
+ const SmNode *pNode = GetLeftMost();
+ if (pNode)
+ SetRectHorAlign(pNode->GetRectHorAlign(), false);
+}
+
+
+/**************************************************************************/
+
+
+void SmUnHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ bool bIsPostfix = GetToken().eType == TFACT;
+
+ SmNode *pNode0 = GetSubNode(0),
+ *pNode1 = GetSubNode(1);
+ SmNode *pOper = bIsPostfix ? pNode1 : pNode0,
+ *pBody = bIsPostfix ? pNode0 : pNode1;
+ assert(pOper);
+ assert(pBody);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+ pOper->Arrange(rDev, rFormat);
+ pBody->Arrange(rDev, rFormat);
+
+ tools::Long nDist = (pOper->GetRect().GetWidth() * rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
+
+ SmRect::operator = (*pNode0);
+
+ Point aPos = pNode1->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pNode1->MoveTo(aPos);
+ ExtendBy(*pNode1, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+namespace {
+
+void lcl_GetHeightVerOffset(const SmRect &rRect,
+ tools::Long &rHeight, tools::Long &rVerOffset)
+ // calculate height and vertical offset of root sign suitable for 'rRect'
+{
+ rVerOffset = (rRect.GetBottom() - rRect.GetAlignB()) / 2;
+ rHeight = rRect.GetHeight() - rVerOffset;
+
+ OSL_ENSURE(rHeight >= 0, "Sm : Ooops...");
+ OSL_ENSURE(rVerOffset >= 0, "Sm : Ooops...");
+}
+
+
+Point lcl_GetExtraPos(const SmRect &rRootSymbol,
+ const SmRect &rExtra)
+{
+ const Size &rSymSize = rRootSymbol.GetSize();
+
+ Point aPos = rRootSymbol.GetTopLeft()
+ + Point((rSymSize.Width() * 70) / 100,
+ (rSymSize.Height() * 52) / 100);
+
+ // from this calculate topleft edge of 'rExtra'
+ aPos.AdjustX( -(rExtra.GetWidth() + rExtra.GetItalicRightSpace()) );
+ aPos.AdjustY( -(rExtra.GetHeight()) );
+ // if there's enough space move a bit less to the right
+ // examples: "nroot i a", "nroot j a"
+ // (it looks better if we don't use italic-spaces here)
+ tools::Long nX = rRootSymbol.GetLeft() + (rSymSize.Width() * 30) / 100;
+ if (aPos.X() > nX)
+ aPos.setX( nX );
+
+ return aPos;
+}
+
+}
+
+void SmRootNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! pExtra needs to have the smaller index than pRootSym in order to
+ //! not to get the root symbol but the pExtra when clicking on it in the
+ //! GraphicWindow. (That is because of the simplicity of the algorithm
+ //! that finds the node corresponding to a mouseclick in the window.)
+ SmNode *pExtra = GetSubNode(0),
+ *pRootSym = GetSubNode(1),
+ *pBody = GetSubNode(2);
+ assert(pRootSym);
+ assert(pBody);
+
+ pBody->Arrange(rDev, rFormat);
+
+ tools::Long nHeight,
+ nVerOffset;
+ lcl_GetHeightVerOffset(*pBody, nHeight, nVerOffset);
+ nHeight += rFormat.GetDistance(DIS_ROOT)
+ * GetFont().GetFontSize().Height() / 100;
+
+ if (nHeight < 0)
+ {
+ SAL_WARN("starmath", "negative height");
+ nHeight = 0;
+ }
+
+ // font specialist advised to change the width first
+ pRootSym->AdaptToY(rDev, nHeight);
+ pRootSym->AdaptToX(rDev, pBody->GetItalicWidth());
+
+ pRootSym->Arrange(rDev, rFormat);
+
+ Point aPos = pRootSym->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, RectVerAlign::Baseline);
+ //! override calculated vertical position
+ aPos.setY( pRootSym->GetTop() + pBody->GetBottom() - pRootSym->GetBottom() );
+ aPos.AdjustY( -nVerOffset );
+ pRootSym->MoveTo(aPos);
+
+ if (pExtra)
+ { pExtra->SetSize(Fraction(rFormat.GetRelSize(SIZ_INDEX), 100));
+ pExtra->Arrange(rDev, rFormat);
+
+ aPos = lcl_GetExtraPos(*pRootSym, *pExtra);
+ pExtra->MoveTo(aPos);
+ }
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pRootSym, RectCopyMBL::This);
+ if (pExtra)
+ ExtendBy(*pExtra, RectCopyMBL::This, true);
+}
+
+/**************************************************************************/
+
+
+void SmBinHorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = LeftOperand(),
+ *pOper = Symbol(),
+ *pRight = RightOperand();
+ assert(pLeft);
+ assert(pOper);
+ assert(pRight);
+
+ pOper->SetSize(Fraction (rFormat.GetRelSize(SIZ_OPERATOR), 100));
+
+ pLeft ->Arrange(rDev, rFormat);
+ pOper ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ const SmRect &rOpRect = pOper->GetRect();
+
+ tools::Long nDist = (rOpRect.GetWidth() *
+ rFormat.GetDistance(DIS_HORIZONTAL)) / 100;
+
+ SmRect::operator = (*pLeft);
+
+ Point aPos;
+ aPos = pOper->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+ pOper->MoveTo(aPos);
+ ExtendBy(*pOper, RectCopyMBL::Xor);
+
+ aPos = pRight->AlignTo(*this, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustX(nDist );
+
+ pRight->MoveTo(aPos);
+ ExtendBy(*pRight, RectCopyMBL::Xor);
+}
+
+
+/**************************************************************************/
+
+
+void SmBinVerNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNum = GetSubNode(0),
+ *pLine = GetSubNode(1),
+ *pDenom = GetSubNode(2);
+ assert(pNum);
+ assert(pLine);
+ assert(pDenom);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ if (bIsTextmode)
+ {
+ Fraction aFraction(rFormat.GetRelSize(SIZ_INDEX), 100);
+ pNum ->SetSize(aFraction);
+ pLine ->SetSize(aFraction);
+ pDenom->SetSize(aFraction);
+ }
+
+ pNum ->Arrange(rDev, rFormat);
+ pDenom->Arrange(rDev, rFormat);
+
+ tools::Long nFontHeight = GetFont().GetFontSize().Height(),
+ nExtLen = nFontHeight * rFormat.GetDistance(DIS_FRACTION) / 100,
+ nThick = nFontHeight * rFormat.GetDistance(DIS_STROKEWIDTH) / 100,
+ nWidth = std::max(pNum->GetItalicWidth(), pDenom->GetItalicWidth()),
+ nNumDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_NUMERATOR) / 100,
+ nDenomDist = bIsTextmode ? 0 :
+ nFontHeight * rFormat.GetDistance(DIS_DENOMINATOR) / 100;
+
+ // font specialist advised to change the width first
+ pLine->AdaptToY(rDev, nThick);
+ pLine->AdaptToX(rDev, nWidth + 2 * nExtLen);
+ pLine->Arrange(rDev, rFormat);
+
+ // get horizontal alignment for numerator
+ const SmNode *pLM = pNum->GetLeftMost();
+ RectHorAlign eHorAlign = pLM->GetRectHorAlign();
+
+ // move numerator to its position
+ Point aPos = pNum->AlignTo(*pLine, RectPos::Top, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY( -nNumDist );
+ pNum->MoveTo(aPos);
+
+ // get horizontal alignment for denominator
+ pLM = pDenom->GetLeftMost();
+ eHorAlign = pLM->GetRectHorAlign();
+
+ // move denominator to its position
+ aPos = pDenom->AlignTo(*pLine, RectPos::Bottom, eHorAlign, RectVerAlign::Baseline);
+ aPos.AdjustY(nDenomDist );
+ pDenom->MoveTo(aPos);
+
+ SmRect::operator = (*pNum);
+ ExtendBy(*pDenom, RectCopyMBL::None).ExtendBy(*pLine, RectCopyMBL::None, pLine->GetCenterY());
+}
+
+const SmNode * SmBinVerNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+namespace {
+
+/// @return value of the determinant formed by the two points
+double Det(const Point &rHeading1, const Point &rHeading2)
+{
+ return rHeading1.X() * rHeading2.Y() - rHeading1.Y() * rHeading2.X();
+}
+
+
+/// Is true iff the point 'rPoint1' belongs to the straight line through 'rPoint2'
+/// and has the direction vector 'rHeading2'
+bool IsPointInLine(const Point &rPoint1,
+ const Point &rPoint2, const Point &rHeading2)
+{
+ assert(rHeading2 != Point());
+
+ bool bRes = false;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ double fLambda;
+ if (std::abs(rHeading2.X()) > std::abs(rHeading2.Y()))
+ {
+ fLambda = (rPoint1.X() - rPoint2.X()) / static_cast<double>(rHeading2.X());
+ bRes = fabs(rPoint1.Y() - (rPoint2.Y() + fLambda * rHeading2.Y())) < eps;
+ }
+ else
+ {
+ fLambda = (rPoint1.Y() - rPoint2.Y()) / static_cast<double>(rHeading2.Y());
+ bRes = fabs(rPoint1.X() - (rPoint2.X() + fLambda * rHeading2.X())) < eps;
+ }
+
+ return bRes;
+}
+
+
+sal_uInt16 GetLineIntersectionPoint(Point &rResult,
+ const Point& rPoint1, const Point &rHeading1,
+ const Point& rPoint2, const Point &rHeading2)
+{
+ assert(rHeading1 != Point());
+ assert(rHeading2 != Point());
+
+ sal_uInt16 nRes = 1;
+ static const double eps = 5.0 * DBL_EPSILON;
+
+ // are the direction vectors linearly dependent?
+ double fDet = Det(rHeading1, rHeading2);
+ if (fabs(fDet) < eps)
+ {
+ nRes = IsPointInLine(rPoint1, rPoint2, rHeading2) ? USHRT_MAX : 0;
+ rResult = nRes ? rPoint1 : Point();
+ }
+ else
+ {
+ // here we do not pay attention to the computational accuracy
+ // (that would be more complicated and is not really worth it in this case)
+ double fLambda = ( (rPoint1.Y() - rPoint2.Y()) * rHeading2.X()
+ - (rPoint1.X() - rPoint2.X()) * rHeading2.Y())
+ / fDet;
+ rResult = Point(rPoint1.X() + static_cast<tools::Long>(fLambda * rHeading1.X()),
+ rPoint1.Y() + static_cast<tools::Long>(fLambda * rHeading1.Y()));
+ }
+
+ return nRes;
+}
+
+}
+
+
+/// @return position and size of the diagonal line
+/// premise: SmRect of the node defines the limitation(!) consequently it has to be known upfront
+void SmBinDiagonalNode::GetOperPosSize(Point &rPos, Size &rSize,
+ const Point &rDiagPoint, double fAngleDeg) const
+
+{
+ double fAngleRad = basegfx::deg2rad(fAngleDeg);
+ tools::Long nRectLeft = GetItalicLeft(),
+ nRectRight = GetItalicRight(),
+ nRectTop = GetTop(),
+ nRectBottom = GetBottom();
+ Point aRightHdg (100, 0),
+ aDownHdg (0, 100),
+ aDiagHdg ( static_cast<tools::Long>(100.0 * cos(fAngleRad)),
+ static_cast<tools::Long>(-100.0 * sin(fAngleRad)) );
+
+ tools::Long nLeft, nRight, nTop, nBottom; // margins of the rectangle for the diagonal
+ Point aPoint;
+ if (IsAscending())
+ {
+ // determine top right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nBottom = aPoint.Y();
+ }
+ }
+ else
+ {
+ // determine top left corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the top border?
+ if (aPoint.X() >= nRectLeft)
+ {
+ nLeft = aPoint.X();
+ nTop = nRectTop;
+ }
+ else
+ {
+ // there has to be a point of intersection with the left border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nLeft = nRectLeft;
+ nTop = aPoint.Y();
+ }
+
+ // determine bottom right corner
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectLeft, nRectBottom), aRightHdg,
+ rDiagPoint, aDiagHdg);
+ // is there a point of intersection with the bottom border?
+ if (aPoint.X() <= nRectRight)
+ {
+ nRight = aPoint.X();
+ nBottom = nRectBottom;
+ }
+ else
+ {
+ // there has to be a point of intersection with the right border!
+ GetLineIntersectionPoint(aPoint,
+ Point(nRectRight, nRectTop), aDownHdg,
+ rDiagPoint, aDiagHdg);
+
+ nRight = nRectRight;
+ nBottom = aPoint.Y();
+ }
+ }
+
+ rSize = Size(nRight - nLeft + 1, nBottom - nTop + 1);
+ rPos.setX( nLeft );
+ rPos.setY( nTop );
+}
+
+
+void SmBinDiagonalNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ // Both arguments have to get into the SubNodes before the Operator so that clicking
+ // within the GraphicWindow sets the FormulaCursor correctly (cf. SmRootNode)
+ SmNode *pLeft = GetSubNode(0),
+ *pRight = GetSubNode(1),
+ *pLine = GetSubNode(2);
+ assert(pLeft);
+ assert(pRight);
+ assert(pLine && pLine->GetType() == SmNodeType::PolyLine);
+
+ SmPolyLineNode *pOper = static_cast<SmPolyLineNode *>(pLine);
+ assert(pOper);
+
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pLeft->Arrange(aTmpDev, rFormat);
+ pRight->Arrange(aTmpDev, rFormat);
+
+ // determine implicitly the values (incl. the margin) of the diagonal line
+ pOper->Arrange(aTmpDev, rFormat);
+
+ tools::Long nDelta = pOper->GetWidth() * 8 / 10;
+
+ // determine TopLeft position from the right argument
+ Point aPos;
+ aPos.setX( pLeft->GetItalicRight() + nDelta + pRight->GetItalicLeftSpace() );
+ if (IsAscending())
+ aPos.setY( pLeft->GetBottom() + nDelta );
+ else
+ aPos.setY( pLeft->GetTop() - nDelta - pRight->GetHeight() );
+
+ pRight->MoveTo(aPos);
+
+ // determine new baseline
+ tools::Long nTmpBaseline = IsAscending() ? (pLeft->GetBottom() + pRight->GetTop()) / 2
+ : (pLeft->GetTop() + pRight->GetBottom()) / 2;
+ Point aLogCenter ((pLeft->GetItalicRight() + pRight->GetItalicLeft()) / 2,
+ nTmpBaseline);
+
+ SmRect::operator = (*pLeft);
+ ExtendBy(*pRight, RectCopyMBL::None);
+
+
+ // determine position and size of diagonal line
+ Size aTmpSize;
+ GetOperPosSize(aPos, aTmpSize, aLogCenter, IsAscending() ? 60.0 : -60.0);
+
+ // font specialist advised to change the width first
+ pOper->AdaptToY(aTmpDev, aTmpSize.Height());
+ pOper->AdaptToX(aTmpDev, aTmpSize.Width());
+ // and make it active
+ pOper->Arrange(aTmpDev, rFormat);
+
+ pOper->MoveTo(aPos);
+
+ ExtendBy(*pOper, RectCopyMBL::None, nTmpBaseline);
+}
+
+
+/**************************************************************************/
+
+
+void SmSubSupNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ OSL_ENSURE(GetNumSubNodes() == 1 + SUBSUP_NUM_ENTRIES,
+ "Sm: wrong number of subnodes");
+
+ SmNode *pBody = GetBody();
+ assert(pBody);
+
+ tools::Long nOrigHeight = pBody->GetFont().GetFontSize().Height();
+
+ pBody->Arrange(rDev, rFormat);
+
+ const SmRect &rBodyRect = pBody->GetRect();
+ SmRect::operator = (rBodyRect);
+
+ // line that separates sub- and supscript rectangles
+ tools::Long nDelimLine = SmFromTo(GetAlignB(), GetAlignT(), 0.4);
+
+ Point aPos;
+ tools::Long nDelta, nDist;
+
+ // iterate over all possible sub-/supscripts
+ SmRect aTmpRect (rBodyRect);
+ for (int i = 0; i < SUBSUP_NUM_ENTRIES; i++)
+ {
+ SmSubSup eSubSup = static_cast<SmSubSup>(i);
+ SmNode *pSubSup = GetSubSup(eSubSup);
+
+ if (!pSubSup)
+ continue;
+
+ // switch position of limits if we are in textmode
+ if (rFormat.IsTextmode() && (GetToken().nGroup & TG::Limit))
+ switch (eSubSup)
+ { case CSUB: eSubSup = RSUB; break;
+ case CSUP: eSubSup = RSUP; break;
+ default:
+ break;
+ }
+
+ // prevent sub-/supscripts from diminishing in size
+ // (as would be in "a_{1_{2_{3_4}}}")
+ if (GetFont().GetFontSize().Height() > rFormat.GetBaseSize().Height() / 3)
+ {
+ sal_uInt16 nIndex = (eSubSup == CSUB || eSubSup == CSUP) ?
+ SIZ_LIMITS : SIZ_INDEX;
+ Fraction aFraction ( rFormat.GetRelSize(nIndex), 100 );
+ pSubSup->SetSize(aFraction);
+ }
+
+ pSubSup->Arrange(rDev, rFormat);
+
+ bool bIsTextmode = rFormat.IsTextmode();
+ nDist = 0;
+
+ //! be sure that CSUB, CSUP are handled before the other cases!
+ switch (eSubSup)
+ { case RSUB :
+ case LSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUBSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUB ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Bottom);
+ aPos.AdjustY(nDist );
+ nDelta = nDelimLine - aPos.Y();
+ if (nDelta > 0)
+ aPos.AdjustY(nDelta );
+ break;
+ case RSUP :
+ case LSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_SUPERSCRIPT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(aTmpRect,
+ eSubSup == LSUP ? RectPos::Left : RectPos::Right,
+ RectHorAlign::Center, RectVerAlign::Top);
+ aPos.AdjustY( -nDist );
+ nDelta = aPos.Y() + pSubSup->GetHeight() - nDelimLine;
+ if (nDelta > 0)
+ aPos.AdjustY( -nDelta );
+ break;
+ case CSUB :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_LOWERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Bottom,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDist );
+ break;
+ case CSUP :
+ if (!bIsTextmode)
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_UPPERLIMIT) / 100;
+ aPos = pSubSup->GetRect().AlignTo(rBodyRect, RectPos::Top,
+ RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY( -nDist );
+ break;
+ }
+
+ pSubSup->MoveTo(aPos);
+ ExtendBy(*pSubSup, RectCopyMBL::This, true);
+
+ // update rectangle to which RSUB, RSUP, LSUB, LSUP
+ // will be aligned to
+ if (eSubSup == CSUB || eSubSup == CSUP)
+ aTmpRect = *this;
+ }
+}
+
+/**************************************************************************/
+
+void SmBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pLeft = OpeningBrace(),
+ *pBody = Body(),
+ *pRight = ClosingBrace();
+ assert(pLeft);
+ assert(pBody);
+ assert(pRight);
+
+ pBody->Arrange(rDev, rFormat);
+
+ bool bIsScaleNormal = rFormat.IsScaleNormalBrackets(),
+ bScale = pBody->GetHeight() > 0 &&
+ (GetScaleMode() == SmScaleMode::Height || bIsScaleNormal),
+ bIsABS = GetToken().eType == TABS;
+
+ tools::Long nFaceHeight = GetFont().GetFontSize().Height();
+
+ // determine oversize in %
+ sal_uInt16 nPerc = 0;
+ if (!bIsABS && bScale)
+ { // in case of oversize braces...
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ nPerc = rFormat.GetDistance(nIndex);
+ }
+
+ // determine the height for the braces
+ tools::Long nBraceHeight;
+ if (bScale)
+ {
+ nBraceHeight = pBody->GetType() == SmNodeType::Bracebody ?
+ static_cast<SmBracebodyNode *>(pBody)->GetBodyHeight()
+ : pBody->GetHeight();
+ nBraceHeight += 2 * (nBraceHeight * nPerc / 100);
+ }
+ else
+ nBraceHeight = nFaceHeight;
+
+ // distance to the argument
+ nPerc = bIsABS ? 0 : rFormat.GetDistance(DIS_BRACKETSPACE);
+ tools::Long nDist = nFaceHeight * nPerc / 100;
+
+ // if wanted, scale the braces to the wanted size
+ if (bScale)
+ {
+ Size aTmpSize (pLeft->GetFont().GetFontSize());
+ OSL_ENSURE(pRight->GetFont().GetFontSize() == aTmpSize,
+ "Sm : different font sizes");
+ aTmpSize.setWidth( std::min(nBraceHeight * 60 / 100,
+ rFormat.GetBaseSize().Height() * 3 / 2) );
+ // correction factor since change from StarMath to OpenSymbol font
+ // because of the different font width in the FontMetric
+ aTmpSize.setWidth( aTmpSize.Width() * 182 );
+ aTmpSize.setWidth( aTmpSize.Width() / 267 );
+
+ sal_Unicode cChar = pLeft->GetToken().cMathChar[0];
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pLeft ->GetFont().SetSize(aTmpSize);
+
+ cChar = pRight->GetToken().cMathChar[0];
+ if (cChar != MS_LINE && cChar != MS_DLINE &&
+ cChar != MS_VERTLINE && cChar != MS_DVERTLINE)
+ pRight->GetFont().SetSize(aTmpSize);
+
+ pLeft ->AdaptToY(rDev, nBraceHeight);
+ pRight->AdaptToY(rDev, nBraceHeight);
+ }
+
+ pLeft ->Arrange(rDev, rFormat);
+ pRight->Arrange(rDev, rFormat);
+
+ // required in order to make "\(a\) - (a) - left ( a right )" look alright
+ RectVerAlign eVerAlign = bScale ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ Point aPos;
+ aPos = pLeft->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX( -nDist );
+ pLeft->MoveTo(aPos);
+
+ aPos = pRight->AlignTo(*pBody, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustX(nDist );
+ pRight->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pLeft, RectCopyMBL::This).ExtendBy(*pRight, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmBracebodyNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ size_t nNumSubNodes = GetNumSubNodes();
+ if (nNumSubNodes == 0)
+ return;
+
+ // arrange arguments
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ GetSubNode(i)->Arrange(rDev, rFormat);
+
+ // build reference rectangle with necessary info for vertical alignment
+ SmRect aRefRect (*GetSubNode(0));
+ for (size_t i = 0; i < nNumSubNodes; i += 2)
+ {
+ SmRect aTmpRect (*GetSubNode(i));
+ Point aPos = aTmpRect.AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+ aTmpRect.MoveTo(aPos);
+ aRefRect.ExtendBy(aTmpRect, RectCopyMBL::Xor);
+ }
+
+ mnBodyHeight = aRefRect.GetHeight();
+
+ // scale separators to required height and arrange them
+ bool bScale = GetScaleMode() == SmScaleMode::Height || rFormat.IsScaleNormalBrackets();
+ tools::Long nHeight = bScale ? aRefRect.GetHeight() : GetFont().GetFontSize().Height();
+ sal_uInt16 nIndex = GetScaleMode() == SmScaleMode::Height ?
+ DIS_BRACKETSIZE : DIS_NORMALBRACKETSIZE;
+ sal_uInt16 nPerc = rFormat.GetDistance(nIndex);
+ if (bScale)
+ nHeight += 2 * (nHeight * nPerc / 100);
+ for (size_t i = 1; i < nNumSubNodes; i += 2)
+ {
+ SmNode *pNode = GetSubNode(i);
+ pNode->AdaptToY(rDev, nHeight);
+ pNode->Arrange(rDev, rFormat);
+ }
+
+ // horizontal distance between argument and brackets or separators
+ tools::Long nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_BRACKETSPACE) / 100;
+
+ SmNode *pLeft = GetSubNode(0);
+ SmRect::operator = (*pLeft);
+ for (size_t i = 1; i < nNumSubNodes; ++i)
+ {
+ bool bIsSeparator = i % 2 != 0;
+ RectVerAlign eVerAlign = bIsSeparator ? RectVerAlign::CenterY : RectVerAlign::Baseline;
+
+ SmNode *pRight = GetSubNode(i);
+ Point aPosX = pRight->AlignTo(*pLeft, RectPos::Right, RectHorAlign::Center, eVerAlign),
+ aPosY = pRight->AlignTo(aRefRect, RectPos::Right, RectHorAlign::Center, eVerAlign);
+ aPosX.AdjustX(nDist );
+
+ pRight->MoveTo(Point(aPosX.X(), aPosY.Y()));
+ ExtendBy(*pRight, bIsSeparator ? RectCopyMBL::This : RectCopyMBL::Xor);
+
+ pLeft = pRight;
+ }
+}
+
+
+/**************************************************************************/
+
+
+void SmVerticalBraceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pBody = Body(),
+ *pBrace = Brace(),
+ *pScript = Script();
+ assert(pBody);
+ assert(pBrace);
+ assert(pScript);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ pBody->Arrange(aTmpDev, rFormat);
+
+ // size is the same as for limits for this part
+ pScript->SetSize( Fraction( rFormat.GetRelSize(SIZ_LIMITS), 100 ) );
+ // braces are a bit taller than usually
+ pBrace ->SetSize( Fraction(3, 2) );
+
+ tools::Long nItalicWidth = pBody->GetItalicWidth();
+ if (nItalicWidth > 0)
+ pBrace->AdaptToX(aTmpDev, nItalicWidth);
+
+ pBrace ->Arrange(aTmpDev, rFormat);
+ pScript->Arrange(aTmpDev, rFormat);
+
+ // determine the relative position and the distances between each other
+ RectPos eRectPos;
+ tools::Long nFontHeight = pBody->GetFont().GetFontSize().Height();
+ tools::Long nDistBody = nFontHeight * rFormat.GetDistance(DIS_ORNAMENTSIZE),
+ nDistScript = nFontHeight;
+ if (GetToken().eType == TOVERBRACE)
+ {
+ eRectPos = RectPos::Top;
+ nDistBody = - nDistBody;
+ nDistScript *= - rFormat.GetDistance(DIS_UPPERLIMIT);
+ }
+ else // TUNDERBRACE
+ {
+ eRectPos = RectPos::Bottom;
+ nDistScript *= + rFormat.GetDistance(DIS_LOWERLIMIT);
+ }
+ nDistBody /= 100;
+ nDistScript /= 100;
+
+ Point aPos = pBrace->AlignTo(*pBody, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistBody );
+ pBrace->MoveTo(aPos);
+
+ aPos = pScript->AlignTo(*pBrace, eRectPos, RectHorAlign::Center, RectVerAlign::Baseline);
+ aPos.AdjustY(nDistScript );
+ pScript->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pBrace, RectCopyMBL::This).ExtendBy(*pScript, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+SmNode * SmOperNode::GetSymbol()
+{
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ if (pNode->GetType() == SmNodeType::SubSup)
+ pNode = static_cast<SmSubSupNode *>(pNode)->GetBody();
+
+ OSL_ENSURE(pNode, "Sm: NULL pointer!");
+ return pNode;
+}
+
+
+tools::Long SmOperNode::CalcSymbolHeight(const SmNode &rSymbol,
+ const SmFormat &rFormat) const
+ // returns the font height to be used for operator-symbol
+{
+ tools::Long nHeight = GetFont().GetFontSize().Height();
+
+ SmTokenType eTmpType = GetToken().eType;
+ if (eTmpType == TLIM || eTmpType == TLIMINF || eTmpType == TLIMSUP)
+ return nHeight;
+
+ if (!rFormat.IsTextmode())
+ {
+ // set minimum size ()
+ nHeight += (nHeight * 20) / 100;
+
+ nHeight += nHeight
+ * rFormat.GetDistance(DIS_OPERATORSIZE) / 100;
+ nHeight = nHeight * 686 / 845;
+ }
+
+ // correct user-defined symbols to match height of sum from used font
+ if (rSymbol.GetToken().eType == TSPECIAL)
+ nHeight = nHeight * 845 / 686;
+
+ return nHeight;
+}
+
+
+void SmOperNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pOper = GetSubNode(0);
+ SmNode *pBody = GetSubNode(1);
+
+ assert(pOper);
+ assert(pBody);
+
+ SmNode *pSymbol = GetSymbol();
+ pSymbol->SetSize(Fraction(CalcSymbolHeight(*pSymbol, rFormat),
+ pSymbol->GetFont().GetFontSize().Height()));
+
+ pBody->Arrange(rDev, rFormat);
+ bool bDynamicallySized = false;
+ if (pSymbol->GetToken().eType == TINTD)
+ {
+ tools::Long nBodyHeight = pBody->GetHeight();
+ tools::Long nFontHeight = pSymbol->GetFont().GetFontSize().Height();
+ if (nFontHeight < nBodyHeight)
+ {
+ pSymbol->SetSize(Fraction(nBodyHeight, nFontHeight));
+ bDynamicallySized = true;
+ }
+ }
+ pOper->Arrange(rDev, rFormat);
+
+ tools::Long nOrigHeight = GetFont().GetFontSize().Height(),
+ nDist = nOrigHeight
+ * rFormat.GetDistance(DIS_OPERATORSPACE) / 100;
+
+ Point aPos = pOper->AlignTo(*pBody, RectPos::Left, RectHorAlign::Center, bDynamicallySized ? RectVerAlign::CenterY : RectVerAlign::Mid);
+ aPos.AdjustX( -nDist );
+ pOper->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pOper, RectCopyMBL::This);
+}
+
+
+/**************************************************************************/
+
+
+void SmAlignNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+ // set alignment within the entire subtree (including current node)
+{
+ assert(GetNumSubNodes() == 1);
+
+ SmNode *pNode = GetSubNode(0);
+ assert(pNode);
+
+ RectHorAlign eHorAlign = RectHorAlign::Center;
+ switch (GetToken().eType)
+ {
+ case TALIGNL: eHorAlign = RectHorAlign::Left; break;
+ case TALIGNC: eHorAlign = RectHorAlign::Center; break;
+ case TALIGNR: eHorAlign = RectHorAlign::Right; break;
+ default:
+ break;
+ }
+ SetRectHorAlign(eHorAlign);
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmAttributeNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pAttr = Attribute(),
+ *pBody = Body();
+ assert(pBody);
+ assert(pAttr);
+
+ pBody->Arrange(rDev, rFormat);
+
+ if (GetScaleMode() == SmScaleMode::Width)
+ pAttr->AdaptToX(rDev, pBody->GetItalicWidth());
+ pAttr->Arrange(rDev, rFormat);
+
+ // get relative position of attribute
+ RectVerAlign eVerAlign;
+ tools::Long nDist = 0;
+ switch (GetToken().eType)
+ { case TUNDERLINE :
+ eVerAlign = RectVerAlign::AttributeLo;
+ break;
+ case TOVERSTRIKE :
+ eVerAlign = RectVerAlign::AttributeMid;
+ break;
+ default :
+ eVerAlign = RectVerAlign::AttributeHi;
+ if (pBody->GetType() == SmNodeType::Attribute)
+ nDist = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_ORNAMENTSPACE) / 100;
+ }
+ Point aPos = pAttr->AlignTo(*pBody, RectPos::Attribute, RectHorAlign::Center, eVerAlign);
+ aPos.AdjustY( -nDist );
+ pAttr->MoveTo(aPos);
+
+ SmRect::operator = (*pBody);
+ ExtendBy(*pAttr, RectCopyMBL::This, true);
+}
+
+void SmFontNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ //! prepare subnodes first
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ int nFnt = -1;
+ switch (GetToken().eType)
+ {
+ case TFIXED: nFnt = FNT_FIXED; break;
+ case TSANS: nFnt = FNT_SANS; break;
+ case TSERIF: nFnt = FNT_SERIF; break;
+ default:
+ break;
+ }
+ if (nFnt != -1)
+ { GetFont() = rFormat.GetFont( sal::static_int_cast< sal_uInt16 >(nFnt) );
+ SetFont(GetFont());
+ }
+
+ //! prevent overwrites of this font by 'Arrange' or 'SetFont' calls of
+ //! other font nodes (those with lower depth in the tree)
+ Flags() |= FontChangeMask::Face;
+}
+
+void SmFontNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode = GetSubNode(1);
+ assert(pNode);
+ sal_uInt32 nc;
+
+ switch (GetToken().eType)
+ { case TSIZE :
+ pNode->SetFontSize(maFontSize, meSizeType);
+ break;
+ case TSANS :
+ case TSERIF :
+ case TFIXED :
+ pNode->SetFont(GetFont());
+ break;
+ case TUNKNOWN : break; // no assertion on "font <?> <?>"
+
+ case TPHANTOM : SetPhantom(true); break;
+ case TBOLD : SetAttribute(FontAttribute::Bold); break;
+ case TITALIC : SetAttribute(FontAttribute::Italic); break;
+ case TNBOLD : ClearAttribute(FontAttribute::Bold); break;
+ case TNITALIC : ClearAttribute(FontAttribute::Italic); break;
+
+ // Using HTML CSS Level 1 standard
+ case TRGB :
+ case TRGBA :
+ case THTMLCOL :
+ case TMATHMLCOL :
+ case TDVIPSNAMESCOL:
+ case TICONICCOL :
+ case THEX :
+ nc = GetToken().cMathChar.toUInt32(16);
+ SetColor(Color(ColorTransparency, nc));
+ break;
+
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+
+ pNode->Arrange(rDev, rFormat);
+
+ SmRect::operator = (pNode->GetRect());
+}
+
+/**************************************************************************/
+
+
+SmPolyLineNode::SmPolyLineNode(const SmToken &rNodeToken)
+ : SmGraphicNode(SmNodeType::PolyLine, rNodeToken)
+ , maPoly(2)
+ , mnWidth(0)
+{
+}
+
+
+void SmPolyLineNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nNewWidth)
+{
+ maToSize.setWidth( nNewWidth );
+}
+
+
+void SmPolyLineNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nNewHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nNewHeight );
+}
+
+
+void SmPolyLineNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ //! some routines being called extract some info from the OutputDevice's
+ //! font (eg the space to be used for borders OR the font name(!!)).
+ //! Thus the font should reflect the needs and has to be set!
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ tools::Long nBorderwidth = GetFont().GetBorderWidth();
+
+ // create polygon using both endpoints
+ assert(maPoly.GetSize() == 2);
+ Point aPointA, aPointB;
+ if (GetToken().eType == TWIDESLASH)
+ {
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( maToSize.Height() - nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( nBorderwidth );
+ }
+ else
+ {
+ OSL_ENSURE(GetToken().eType == TWIDEBACKSLASH, "Sm : unexpected token");
+ aPointA.setX( nBorderwidth );
+ aPointA.setY( nBorderwidth );
+ aPointB.setX( maToSize.Width() - nBorderwidth );
+ aPointB.setY( maToSize.Height() - nBorderwidth );
+ }
+ maPoly.SetPoint(aPointA, 0);
+ maPoly.SetPoint(aPointB, 1);
+
+ tools::Long nThick = GetFont().GetFontSize().Height()
+ * rFormat.GetDistance(DIS_STROKEWIDTH) / 100;
+ mnWidth = nThick + 2 * nBorderwidth;
+
+ SmRect::operator = (SmRect(maToSize.Width(), maToSize.Height()));
+}
+
+
+/**************************************************************************/
+
+void SmRootSymbolNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ mnBodyWidth = nWidth;
+}
+
+
+void SmRootSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ // some additional length so that the horizontal
+ // bar will be positioned above the argument
+ SmMathSymbolNode::AdaptToY(rDev, nHeight + nHeight / 10);
+}
+
+
+/**************************************************************************/
+
+
+void SmRectangleNode::AdaptToX(OutputDevice &/*rDev*/, sal_uLong nWidth)
+{
+ maToSize.setWidth( nWidth );
+}
+
+
+void SmRectangleNode::AdaptToY(OutputDevice &/*rDev*/, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ maToSize.setHeight( nHeight );
+}
+
+
+void SmRectangleNode::Arrange(OutputDevice &rDev, const SmFormat &/*rFormat*/)
+{
+ tools::Long nFontHeight = GetFont().GetFontSize().Height();
+ tools::Long nWidth = maToSize.Width(),
+ nHeight = maToSize.Height();
+ if (nHeight == 0)
+ nHeight = nFontHeight / 30;
+ if (nWidth == 0)
+ nWidth = nFontHeight / 3;
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // add some borderspace
+ sal_uLong nTmpBorderWidth = GetFont().GetBorderWidth();
+ nHeight += 2 * nTmpBorderWidth;
+
+ //! use this method in order to have 'SmRect::HasAlignInfo() == true'
+ //! and thus having the attribute-fences updated in 'SmRect::ExtendBy'
+ SmRect::operator = (SmRect(nWidth, nHeight));
+}
+
+
+/**************************************************************************/
+
+
+SmTextNode::SmTextNode( SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(eNodeType, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+SmTextNode::SmTextNode( const SmToken &rNodeToken, sal_uInt16 nFontDescP )
+ : SmVisibleNode(SmNodeType::Text, rNodeToken)
+ , mnFontDesc(nFontDescP)
+ , mnSelectionStart(0)
+ , mnSelectionEnd(0)
+{
+}
+
+void SmTextNode::ChangeText(const OUString &rText) {
+ maText = rText;
+ GetToken().aText = rText;
+ AdjustFontDesc();
+}
+
+void SmTextNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // default setting for horizontal alignment of nodes with TTEXT
+ // content is as alignl (cannot be done in Arrange since it would
+ // override the settings made by an SmAlignNode before)
+ if (TTEXT == GetToken().eType)
+ SetRectHorAlign( RectHorAlign::Left );
+
+ maText = GetToken().aText;
+ GetFont() = rFormat.GetFont(GetFontDesc());
+
+ if (IsItalic( GetFont() ))
+ Attributes() |= FontAttribute::Italic;
+ if (IsBold( GetFont() ))
+ Attributes() |= FontAttribute::Bold;
+
+ // special handling for ':' where it is a token on its own and is likely
+ // to be used for mathematical notations. (E.g. a:b = 2:3)
+ // In that case it should not be displayed in italic.
+ if (GetToken().aText.getLength() == 1 && GetToken().aText[0] == ':')
+ Attributes() &= ~FontAttribute::Italic;
+};
+
+
+void SmTextNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ sal_uInt16 nSizeDesc = GetFontDesc() == FNT_FUNCTION ?
+ SIZ_FUNCTION : SIZ_TEXT;
+ GetFont() *= Fraction (rFormat.GetRelSize(nSizeDesc), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, maText, GetFont().GetBorderWidth()));
+}
+
+void SmTextNode::GetAccessibleText( OUStringBuffer &rText ) const
+{
+ rText.append(maText);
+}
+
+void SmTextNode::AdjustFontDesc()
+{
+ if (GetToken().nGroup == TG::Function) mnFontDesc = FNT_FUNCTION;
+ else if (GetToken().eType == TTEXT) mnFontDesc = FNT_TEXT;
+ else {
+ sal_Unicode firstChar = maText[0];
+ if( ('0' <= firstChar && firstChar <= '9') || firstChar == '.' || firstChar == ',')
+ mnFontDesc = FNT_NUMBER;
+ else mnFontDesc = FNT_VARIABLE;
+ }
+}
+
+sal_Unicode SmTextNode::ConvertSymbolToUnicode(sal_Unicode nIn)
+{
+ //Find the best match in accepted unicode for our private area symbols
+ static const sal_Unicode aStarMathPrivateToUnicode[] =
+ {
+ 0x2030, 0xF613, 0xF612, 0x002B, 0x003C, 0x003E, 0xE425, 0xE421, 0xE088, 0x2208,
+ 0x0192, 0x2026, 0x2192, 0x221A, 0x221A, 0x221A, 0xE090, 0x005E, 0x02C7, 0x02D8,
+ 0x00B4, 0x0060, 0x02DC, 0x00AF, 0x0362, 0xE099, 0xE09A, 0x20DB, 0xE09C, 0xE09D,
+ 0x0028, 0x0029, 0x2220, 0x22AF, 0xE0A2, 0xE0A3, 0xE0A4, 0xE0A5, 0xE0A6, 0xE0A7,
+ 0x002F, 0x005C, 0x274F, 0xE0AB, 0x0393, 0x0394, 0x0398, 0x039b, 0x039e, 0x03A0,
+ 0x03a3, 0x03a5, 0x03a6, 0x03a8, 0x03A9, 0x03B1, 0x03B2, 0x03b3, 0x03b4, 0x03b5,
+ 0x03b6, 0x03b7, 0x03b8, 0x03b9, 0x03ba, 0x03bb, 0x03bc, 0x03bd, 0x03be, 0x03bf,
+ 0x03c0, 0x03c1, 0x03c3, 0x03c4, 0x03c5, 0x03c6, 0x03c7, 0x03c8, 0x03c9, 0x03b5,
+ 0x03d1, 0x03d6, 0xE0D2, 0x03db, 0x2118, 0x2202, 0x2129, 0xE0D7, 0xE0D8, 0x22A4,
+ 0xE0DA, 0x2190, 0x2191, 0x2193
+ };
+ if ((nIn >= 0xE080) && (nIn <= 0xE0DD))
+ nIn = aStarMathPrivateToUnicode[nIn-0xE080];
+
+ //For whatever unicode glyph that equation editor doesn't ship with that
+ //we have a possible match we can munge it to.
+ switch (nIn)
+ {
+ case 0x2223:
+ nIn = '|';
+ break;
+ default:
+ break;
+ }
+
+ return nIn;
+}
+
+/**************************************************************************/
+
+void SmMatrixNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmNode *pNode;
+
+ // initialize array that is to hold the maximum widths of all
+ // elements (subnodes) in that column.
+ std::vector<tools::Long> aColWidth(mnNumCols);
+
+ // arrange subnodes and calculate the above arrays contents
+ size_t nNodes = GetNumSubNodes();
+ for (size_t i = 0; i < nNodes; ++i)
+ {
+ size_t nIdx = nNodes - 1 - i;
+ if (nullptr != (pNode = GetSubNode(nIdx)))
+ {
+ pNode->Arrange(rDev, rFormat);
+ int nCol = nIdx % mnNumCols;
+ aColWidth[nCol] = std::max(aColWidth[nCol], pNode->GetItalicWidth());
+ }
+ }
+
+ // norm distance from which the following two are calculated
+ const tools::Long nNormDist = 3 * GetFont().GetFontSize().Height();
+
+ // define horizontal and vertical minimal distances that separate
+ // the elements
+ tools::Long nHorDist = nNormDist * rFormat.GetDistance(DIS_MATRIXCOL) / 100,
+ nVerDist = nNormDist * rFormat.GetDistance(DIS_MATRIXROW) / 100;
+
+ // build array that holds the leftmost position for each column
+ std::vector<tools::Long> aColLeft(mnNumCols);
+ tools::Long nX = 0;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ aColLeft[j] = nX;
+ nX += aColWidth[j] + nHorDist;
+ }
+
+ SmRect::operator = (SmRect());
+ for (size_t i = 0; i < mnNumRows; ++i)
+ {
+ Point aPos;
+ SmRect aLineRect;
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ SmNode *pTmpNode = GetSubNode(i * mnNumCols + j);
+ assert(pTmpNode);
+
+ const SmRect &rNodeRect = pTmpNode->GetRect();
+
+ // align all baselines in that row if possible
+ aPos = rNodeRect.AlignTo(aLineRect, RectPos::Right, RectHorAlign::Center, RectVerAlign::Baseline);
+
+ // get horizontal alignment
+ const SmNode *pCoNode = pTmpNode->GetLeftMost();
+ RectHorAlign eHorAlign = pCoNode->GetRectHorAlign();
+
+ // calculate horizontal position of element depending on column
+ // and horizontal alignment
+ switch (eHorAlign)
+ { case RectHorAlign::Left:
+ aPos.setX( aColLeft[j] );
+ break;
+ case RectHorAlign::Center:
+ aPos.setX( rNodeRect.GetLeft() + aColLeft[j]
+ + aColWidth[j] / 2
+ - rNodeRect.GetItalicCenterX() );
+ break;
+ case RectHorAlign::Right:
+ aPos.setX( aColLeft[j]
+ + aColWidth[j] - rNodeRect.GetItalicWidth() );
+ break;
+ default:
+ assert(false);
+ }
+
+ pTmpNode->MoveTo(aPos);
+ aLineRect.ExtendBy(rNodeRect, RectCopyMBL::Xor);
+ }
+
+ aPos = aLineRect.AlignTo(*this, RectPos::Bottom, RectHorAlign::Center, RectVerAlign::Baseline);
+ if (i > 0)
+ aPos.AdjustY(nVerDist );
+
+ // move 'aLineRect' and rectangles in that line to final position
+ Point aDelta(0, // since horizontal alignment is already done
+ aPos.Y() - aLineRect.GetTop());
+ aLineRect.Move(aDelta);
+ for (size_t j = 0; j < mnNumCols; ++j)
+ {
+ if (nullptr != (pNode = GetSubNode(i * mnNumCols + j)))
+ pNode->Move(aDelta);
+ }
+
+ ExtendBy(aLineRect, RectCopyMBL::None);
+ }
+}
+
+const SmNode * SmMatrixNode::GetLeftMost() const
+{
+ return this;
+}
+
+
+/**************************************************************************/
+
+
+SmMathSymbolNode::SmMathSymbolNode(const SmToken &rNodeToken)
+: SmSpecialNode(SmNodeType::Math, rNodeToken, FNT_MATH)
+{
+ SetText(GetToken().cMathChar);
+}
+
+void SmMathSymbolNode::AdaptToX(OutputDevice &rDev, sal_uLong nWidth)
+{
+ // Since there is no function to do this, we try to approximate it:
+ Size aFntSize (GetFont().GetFontSize());
+
+ //! however the result is a bit better with 'nWidth' as initial font width
+ aFntSize.setWidth( nWidth );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for width
+ tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
+ tools::Long nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetItalicWidth();
+
+ // scale fontwidth with this error factor
+ aFntSize.setWidth( aFntSize.Width() * nWidth );
+ aFntSize.setWidth( aFntSize.Width() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+void SmMathSymbolNode::AdaptToY(OutputDevice &rDev, sal_uLong nHeight)
+{
+ GetFont().FreezeBorderWidth();
+ Size aFntSize (GetFont().GetFontSize());
+
+ // Since we only want to scale the height, we might have
+ // to determine the font width in order to keep it
+ if (aFntSize.Width() == 0)
+ {
+ rDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
+ rDev.SetFont(GetFont());
+ aFntSize.setWidth( rDev.GetFontMetric().GetFontSize().Width() );
+ rDev.Pop();
+ }
+ OSL_ENSURE(aFntSize.Width() != 0, "Sm: ");
+
+ //! however the result is a bit better with 'nHeight' as initial
+ //! font height
+ aFntSize.setHeight( nHeight );
+ GetFont().SetSize(aFntSize);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // get denominator of error factor for height
+ tools::Long nTmpBorderWidth = GetFont().GetBorderWidth();
+ tools::Long nDenom = 0;
+ if (!GetText().isEmpty())
+ nDenom = SmRect(aTmpDev, nullptr, GetText(), nTmpBorderWidth).GetHeight();
+
+ // scale fontwidth with this error factor
+ aFntSize.setHeight( aFntSize.Height() * nHeight );
+ aFntSize.setHeight( aFntSize.Height() / ( nDenom ? nDenom : 1) );
+
+ GetFont().SetSize(aFntSize);
+}
+
+
+void SmMathSymbolNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont() = rFormat.GetFont(GetFontDesc());
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ OSL_ENSURE(GetFont().GetCharSet() == RTL_TEXTENCODING_SYMBOL ||
+ GetFont().GetCharSet() == RTL_TEXTENCODING_UNICODE,
+ "wrong charset for character from StarMath/OpenSymbol font");
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmMathSymbolNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ const OUString &rText = GetText();
+
+ if (rText.isEmpty() || rText[0] == '\0')
+ { SmRect::operator = (SmRect());
+ return;
+ }
+
+ PrepareAttributes();
+
+ GetFont() *= Fraction (rFormat.GetRelSize(SIZ_TEXT), 100);
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+static bool lcl_IsFromGreekSymbolSet( const OUString &rTokenText )
+{
+ bool bRes = false;
+
+ // valid symbol name needs to have a '%' at pos 0 and at least an additional char
+ if (rTokenText.getLength() > 2 && rTokenText[0] == u'%')
+ {
+ OUString aName( rTokenText.copy(1) );
+ SmSym *pSymbol = SM_MOD()->GetSymbolManager().GetSymbolByName( aName );
+ if (pSymbol && SmLocalizedSymbolData::GetExportSymbolSetName(pSymbol->GetSymbolSetName()) == "Greek")
+ bRes = true;
+ }
+
+ return bRes;
+}
+
+
+SmSpecialNode::SmSpecialNode(SmNodeType eNodeType, const SmToken &rNodeToken, sal_uInt16 _nFontDesc)
+ : SmTextNode(eNodeType, rNodeToken, _nFontDesc)
+ , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
+{
+}
+
+
+SmSpecialNode::SmSpecialNode(const SmToken &rNodeToken)
+ : SmTextNode(SmNodeType::Special, rNodeToken, FNT_MATH) // default Font isn't always correct!
+ , mbIsFromGreekSymbolSet(lcl_IsFromGreekSymbolSet( rNodeToken.aText ))
+{
+}
+
+
+void SmSpecialNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ const SmSym *pSym;
+ SmModule *pp = SM_MOD();
+
+ OUString aName(GetToken().aText.copy(1));
+ if (nullptr != (pSym = pp->GetSymbolManager().GetSymbolByName( aName )))
+ {
+ sal_UCS4 cChar = pSym->GetCharacter();
+ OUString aTmp( &cChar, 1 );
+ SetText( aTmp );
+ GetFont() = pSym->GetFace();
+ }
+ else
+ {
+ SetText( GetToken().aText );
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+ }
+ // use same font size as is used for variables
+ GetFont().SetSize( rFormat.GetFont( FNT_VARIABLE ).GetFontSize() );
+
+ // Actually only WEIGHT_NORMAL and WEIGHT_BOLD should occur... However, the sms-file also
+ // contains e.g. 'WEIGHT_ULTRALIGHT'. Consequently, compare here with '>' instead of '!='.
+ // (In the long term the necessity for 'PrepareAttribut' and thus also for this here should be dropped)
+
+ //! see also SmFontStyles::GetStyleName
+ if (IsItalic( GetFont() ))
+ SetAttribute(FontAttribute::Italic);
+ if (IsBold( GetFont() ))
+ SetAttribute(FontAttribute::Bold);
+
+ Flags() |= FontChangeMask::Face;
+
+ if (!mbIsFromGreekSymbolSet)
+ return;
+
+ OSL_ENSURE( GetText().getLength() == 1, "a symbol should only consist of 1 char!" );
+ bool bItalic = false;
+ sal_Int16 nStyle = rFormat.GetGreekCharStyle();
+ OSL_ENSURE( nStyle >= 0 && nStyle <= 2, "unexpected value for GreekCharStyle" );
+ if (nStyle == 1)
+ bItalic = true;
+ else if (nStyle == 2)
+ {
+ const OUString& rTmp(GetText());
+ if (!rTmp.isEmpty())
+ {
+ static const sal_Unicode cUppercaseAlpha = 0x0391;
+ static const sal_Unicode cUppercaseOmega = 0x03A9;
+ sal_Unicode cChar = rTmp[0];
+ // uppercase letters should be straight and lowercase letters italic
+ bItalic = cUppercaseAlpha > cChar || cChar > cUppercaseOmega;
+ }
+ }
+
+ if (bItalic)
+ Attributes() |= FontAttribute::Italic;
+ else
+ Attributes() &= ~FontAttribute::Italic;
+};
+
+
+void SmSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+
+void SmGlyphSpecialNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(),
+ GetFont().GetBorderWidth()).AsGlyphRect());
+}
+
+
+/**************************************************************************/
+
+
+void SmPlaceNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_GRAY);
+ Flags() |= FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Italic;
+};
+
+
+void SmPlaceNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, GetText(), GetFont().GetBorderWidth()));
+}
+
+
+/**************************************************************************/
+
+
+void SmErrorNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ GetFont().SetColor(COL_RED);
+ Flags() |= FontChangeMask::Phantom | FontChangeMask::Bold | FontChangeMask::Italic
+ | FontChangeMask::Color | FontChangeMask::Face | FontChangeMask::Size;
+}
+
+
+void SmErrorNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ PrepareAttributes();
+
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ const OUString &rText = GetText();
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, rText, GetFont().GetBorderWidth()));
+}
+
+/**************************************************************************/
+
+void SmBlankNode::IncreaseBy(const SmToken &rToken, sal_uInt32 nMultiplyBy)
+{
+ switch(rToken.eType)
+ {
+ case TBLANK: mnNum += (4 * nMultiplyBy); break;
+ case TSBLANK: mnNum += (1 * nMultiplyBy); break;
+ default:
+ break;
+ }
+}
+
+void SmBlankNode::Prepare(const SmFormat &rFormat, const SmDocShell &rDocShell, int nDepth)
+{
+ SmNode::Prepare(rFormat, rDocShell, nDepth);
+
+ // Here it need/should not be the StarMath font, so that for the character
+ // used in Arrange a normal (non-clipped) rectangle is generated
+ GetFont() = rFormat.GetFont(FNT_VARIABLE);
+
+ Flags() |= FontChangeMask::Face | FontChangeMask::Bold | FontChangeMask::Italic;
+}
+
+
+void SmBlankNode::Arrange(OutputDevice &rDev, const SmFormat &rFormat)
+{
+ SmTmpDevice aTmpDev (rDev, true);
+ aTmpDev.SetFont(GetFont());
+
+ // make distance depend on the font height
+ // (so that it increases when scaling (e.g. size *2 {a ~ b})
+ tools::Long nDist = GetFont().GetFontSize().Height() / 10,
+ nSpace = mnNum * nDist;
+
+ // get a SmRect with Baseline and all the bells and whistles
+ SmRect::operator = (SmRect(aTmpDev, &rFormat, OUString(' '),
+ GetFont().GetBorderWidth()));
+
+ // and resize it to the requested size
+ SetItalicSpaces(0, 0);
+ SetWidth(nSpace);
+}
+
+/**************************************************************************/
+//Implementation of all accept methods for SmVisitor
+
+void SmTableNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBracebodyNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmOperNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAlignNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmAttributeNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmFontNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmUnHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinHorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinVerNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBinDiagonalNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSubSupNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMatrixNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPlaceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmTextNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmGlyphSpecialNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmMathSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmBlankNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmErrorNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmExpressionNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmPolyLineNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRootSymbolNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmRectangleNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+void SmVerticalBraceNode::Accept(SmVisitor* pVisitor) {
+ pVisitor->Visit(this);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlexport.cxx b/starmath/source/ooxmlexport.cxx
new file mode 100644
index 000000000..8a94b8908
--- /dev/null
+++ b/starmath/source/ooxmlexport.cxx
@@ -0,0 +1,598 @@
+/* -*- 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 "ooxmlexport.hxx"
+
+#include <oox/token/tokens.hxx>
+#include <rtl/ustring.hxx>
+#include <sal/log.hxx>
+#include <oox/mathml/export.hxx>
+
+using namespace oox;
+using namespace oox::core;
+
+SmOoxmlExport::SmOoxmlExport(const SmNode *const pIn, OoxmlVersion const v,
+ drawingml::DocumentType const documentType)
+: SmWordExportBase( pIn )
+, version( v )
+, m_DocumentType(documentType)
+{
+}
+
+void SmOoxmlExport::ConvertFromStarMath( const ::sax_fastparser::FSHelperPtr& serializer, const sal_Int8 nAlign )
+{
+ if( GetTree() == nullptr )
+ return;
+ m_pSerializer = serializer;
+
+ //Formula alignment situations:
+ //
+ // 1)Inline(as before):
+ //
+ // <m:oMath>
+ // <m:r> ... </m:r>
+ // </m:oMath>
+ //
+ // 2)Aligned:
+ //
+ // <m:oMathPara>
+ // <m:oMathParaPr>
+ // <m:jc m:val="left|right|center">
+ // </m:oMathParaPr>
+ // <m:oMath>
+ // <m:r> ... </m:r>
+ // </m:oMath>
+ // </m:oMathPara>
+
+ if (nAlign != FormulaExportBase::eFormulaAlign::INLINE)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_oMathPara,
+ FSNS(XML_xmlns, XML_m), "http://schemas.openxmlformats.org/officeDocument/2006/math");
+ m_pSerializer->startElementNS(XML_m, XML_oMathParaPr);
+ if (nAlign == FormulaExportBase::eFormulaAlign::CENTER)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "center");
+ if (nAlign == FormulaExportBase::eFormulaAlign::GROUPEDCENTER)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "center");
+ if (nAlign == FormulaExportBase::eFormulaAlign::LEFT)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "left");
+ if (nAlign == FormulaExportBase::eFormulaAlign::RIGHT)
+ m_pSerializer->singleElementNS(XML_m, XML_jc, FSNS(XML_m, XML_val), "right");
+ m_pSerializer->endElementNS(XML_m, XML_oMathParaPr);
+ m_pSerializer->startElementNS(XML_m, XML_oMath);
+ HandleNode(GetTree(), 0);
+ m_pSerializer->endElementNS(XML_m, XML_oMath);
+ m_pSerializer->endElementNS(XML_m, XML_oMathPara);
+ }
+ else //else, inline as was before
+ {
+ m_pSerializer->startElementNS(XML_m, XML_oMath,
+ FSNS(XML_xmlns, XML_m), "http://schemas.openxmlformats.org/officeDocument/2006/math");
+ HandleNode( GetTree(), 0 );
+ m_pSerializer->endElementNS( XML_m, XML_oMath );
+ }
+}
+
+// NOTE: This is still work in progress and unfinished, but it already covers a good
+// part of the ooxml math stuff.
+
+void SmOoxmlExport::HandleVerticalStack( const SmNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_eqArr);
+ int size = pNode->GetNumSubNodes();
+ for( int i = 0;
+ i < size;
+ ++i )
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( i ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_eqArr );
+}
+
+void SmOoxmlExport::HandleText( const SmNode* pNode, int /*nLevel*/)
+{
+ m_pSerializer->startElementNS(XML_m, XML_r);
+
+ if( pNode->GetToken().eType == TTEXT ) // literal text (in quotes)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_rPr);
+ m_pSerializer->singleElementNS(XML_m, XML_lit);
+ m_pSerializer->singleElementNS(XML_m, XML_nor);
+ m_pSerializer->endElementNS( XML_m, XML_rPr );
+ }
+ if (drawingml::DOCUMENT_DOCX == m_DocumentType && ECMA_DIALECT == version)
+ { // HACK: MSOffice2007 does not import characters properly unless this font is explicitly given
+ m_pSerializer->startElementNS(XML_w, XML_rPr);
+ m_pSerializer->singleElementNS( XML_w, XML_rFonts, FSNS( XML_w, XML_ascii ), "Cambria Math",
+ FSNS( XML_w, XML_hAnsi ), "Cambria Math" );
+ m_pSerializer->endElementNS( XML_w, XML_rPr );
+ }
+ m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
+ const SmTextNode* pTemp = static_cast<const SmTextNode* >(pNode);
+ SAL_INFO( "starmath.ooxml", "Text:" << pTemp->GetText());
+ OUStringBuffer buf(pTemp->GetText());
+ for(sal_Int32 i=0;i<pTemp->GetText().getLength();i++)
+ {
+#if 0
+ if ((nPendingAttributes) &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ *pS << sal_uInt8(0x22); //char, with attributes right
+ //after the character
+ }
+ else
+ *pS << sal_uInt8(CHAR);
+
+ sal_uInt8 nFace = 0x1;
+ if (pNode->GetFont().GetItalic() == ITALIC_NORMAL)
+ nFace = 0x3;
+ else if (pNode->GetFont().GetWeight() == WEIGHT_BOLD)
+ nFace = 0x7;
+ *pS << sal_uInt8(nFace+128); //typeface
+#endif
+ buf[i] = SmTextNode::ConvertSymbolToUnicode(buf[i]);
+#if 0
+ //Mathtype can only have these sort of character
+ //attributes on a single character, starmath can put them
+ //anywhere, when the entity involved is a text run this is
+ //a large effort to place the character attribute on the
+ //central mathtype character so that it does pretty much
+ //what the user probably has in mind. The attributes
+ //filled in here are dummy ones which are replaced in the
+ //ATTRIBUTE handler if a suitable location for the
+ //attributes was found here. Unfortunately it is
+ //possible for starmath to place character attributes on
+ //entities which cannot occur in mathtype e.g. a Summation
+ //symbol so these attributes may be lost
+ if ((nPendingAttributes) &&
+ (i == ((pTemp->GetText().getLength()+1)/2)-1))
+ {
+ *pS << sal_uInt8(EMBEL);
+ while (nPendingAttributes)
+ {
+ *pS << sal_uInt8(2);
+ //wedge the attributes in here and clear
+ //the pending stack
+ nPendingAttributes--;
+ }
+ nInsertion=pS->Tell();
+ *pS << sal_uInt8(END); //end embel
+ *pS << sal_uInt8(END); //end embel
+ }
+#endif
+ }
+ m_pSerializer->writeEscaped(buf.makeStringAndClear());
+ m_pSerializer->endElementNS( XML_m, XML_t );
+ m_pSerializer->endElementNS( XML_m, XML_r );
+}
+
+void SmOoxmlExport::HandleFractions( const SmNode* pNode, int nLevel, const char* type )
+{
+ m_pSerializer->startElementNS(XML_m, XML_f);
+ if( type != nullptr )
+ {
+ m_pSerializer->startElementNS(XML_m, XML_fPr);
+ m_pSerializer->singleElementNS(XML_m, XML_type, FSNS(XML_m, XML_val), type);
+ m_pSerializer->endElementNS( XML_m, XML_fPr );
+ }
+ assert( pNode->GetNumSubNodes() == 3 );
+ m_pSerializer->startElementNS(XML_m, XML_num);
+ HandleNode( pNode->GetSubNode( 0 ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_num );
+ m_pSerializer->startElementNS(XML_m, XML_den);
+ HandleNode( pNode->GetSubNode( 2 ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_den );
+ m_pSerializer->endElementNS( XML_m, XML_f );
+}
+
+void SmOoxmlExport::HandleAttribute( const SmAttributeNode* pNode, int nLevel )
+{
+ switch( pNode->Attribute()->GetToken().eType )
+ {
+ case TCHECK:
+ case TACUTE:
+ case TGRAVE:
+ case TBREVE:
+ case TCIRCLE:
+ case TVEC:
+ case TTILDE:
+ case THAT:
+ case TDOT:
+ case TDDOT:
+ case TDDDOT:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEHARPOON:
+ case TWIDEVEC:
+ case TBAR:
+ {
+ m_pSerializer->startElementNS(XML_m, XML_acc);
+ m_pSerializer->startElementNS(XML_m, XML_accPr);
+ OString value = OUStringToOString(pNode->Attribute()->GetToken().cMathChar, RTL_TEXTENCODING_UTF8 );
+ m_pSerializer->singleElementNS(XML_m, XML_chr, FSNS(XML_m, XML_val), value);
+ m_pSerializer->endElementNS( XML_m, XML_accPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_acc );
+ break;
+ }
+ case TOVERLINE:
+ case TUNDERLINE:
+ m_pSerializer->startElementNS(XML_m, XML_bar);
+ m_pSerializer->startElementNS(XML_m, XML_barPr);
+ m_pSerializer->singleElementNS( XML_m, XML_pos, FSNS( XML_m, XML_val ),
+ ( pNode->Attribute()->GetToken().eType == TUNDERLINE ) ? "bot" : "top" );
+ m_pSerializer->endElementNS( XML_m, XML_barPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_bar );
+ break;
+ case TOVERSTRIKE:
+ m_pSerializer->startElementNS(XML_m, XML_borderBox);
+ m_pSerializer->startElementNS(XML_m, XML_borderBoxPr);
+ m_pSerializer->singleElementNS(XML_m, XML_hideTop, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideBot, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideLeft, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_hideRight, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->singleElementNS(XML_m, XML_strikeH, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_borderBoxPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_borderBox );
+ break;
+ default:
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleRoot( const SmRootNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_rad);
+ if( const SmNode* argument = pNode->Argument())
+ {
+ m_pSerializer->startElementNS(XML_m, XML_deg);
+ HandleNode( argument, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_deg );
+ }
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_radPr);
+ m_pSerializer->singleElementNS(XML_m, XML_degHide, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_radPr );
+ m_pSerializer->singleElementNS(XML_m, XML_deg); // empty but present
+ }
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_rad );
+}
+
+static OString mathSymbolToString( const SmNode* node )
+{
+ assert( node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent );
+ const SmTextNode* txtnode = static_cast< const SmTextNode* >( node );
+ assert( txtnode->GetText().getLength() == 1 );
+ sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode( txtnode->GetText()[0] );
+ return OUStringToOString( OUStringChar( chr ), RTL_TEXTENCODING_UTF8 );
+}
+
+void SmOoxmlExport::HandleOperator( const SmOperNode* pNode, int nLevel )
+{
+ SAL_INFO( "starmath.ooxml", "Operator: " << int( pNode->GetToken().eType ));
+ switch( pNode->GetToken().eType )
+ {
+ case TINT:
+ case TINTD:
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ case TPROD:
+ case TCOPROD:
+ case TSUM:
+ {
+ const SmSubSupNode* subsup = pNode->GetSubNode( 0 )->GetType() == SmNodeType::SubSup
+ ? static_cast< const SmSubSupNode* >( pNode->GetSubNode( 0 )) : nullptr;
+ const SmNode* operation = subsup != nullptr ? subsup->GetBody() : pNode->GetSubNode( 0 );
+ m_pSerializer->startElementNS(XML_m, XML_nary);
+ m_pSerializer->startElementNS(XML_m, XML_naryPr);
+ m_pSerializer->singleElementNS( XML_m, XML_chr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(operation) );
+ if( subsup == nullptr || subsup->GetSubSup( CSUB ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_subHide, FSNS(XML_m, XML_val), "1");
+ if( subsup == nullptr || subsup->GetSubSup( CSUP ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_supHide, FSNS(XML_m, XML_val), "1");
+ m_pSerializer->endElementNS( XML_m, XML_naryPr );
+ if( subsup == nullptr || subsup->GetSubSup( CSUB ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_sub);
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( subsup->GetSubSup( CSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ }
+ if( subsup == nullptr || subsup->GetSubSup( CSUP ) == nullptr )
+ m_pSerializer->singleElementNS(XML_m, XML_sup);
+ else
+ {
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( subsup->GetSubSup( CSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ }
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( 1 ), nLevel + 1 ); // body
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_nary );
+ break;
+ }
+ case TLIM:
+ m_pSerializer->startElementNS(XML_m, XML_func);
+ m_pSerializer->startElementNS(XML_m, XML_fName);
+ m_pSerializer->startElementNS(XML_m, XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSymbol(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ if( const SmSubSupNode* subsup = pNode->GetSubNode( 0 )->GetType() == SmNodeType::SubSup
+ ? static_cast< const SmSubSupNode* >( pNode->GetSubNode( 0 )) : nullptr )
+ {
+ if( subsup->GetSubSup( CSUB ) != nullptr )
+ HandleNode( subsup->GetSubSup( CSUB ), nLevel + 1 );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limLow );
+ m_pSerializer->endElementNS( XML_m, XML_fName );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->GetSubNode( 1 ), nLevel + 1 ); // body
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_func );
+ break;
+ default:
+ SAL_WARN("starmath.ooxml", "Unhandled operation");
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleSubSupScriptInternal( const SmSubSupNode* pNode, int nLevel, int flags )
+{
+// docx supports only a certain combination of sub/super scripts, but LO can have any,
+// so try to merge it using several tags if necessary
+ if( flags == 0 ) // none
+ return;
+ if(( flags & ( 1 << RSUP | 1 << RSUB )) == ( 1 << RSUP | 1 << RSUB ))
+ { // m:sSubSup
+ m_pSerializer->startElementNS(XML_m, XML_sSubSup);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUP | 1 << RSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( RSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( RSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->endElementNS( XML_m, XML_sSubSup );
+ }
+ else if(( flags & ( 1 << RSUB )) == 1 << RSUB )
+ { // m:sSub
+ m_pSerializer->startElementNS(XML_m, XML_sSub);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( RSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->endElementNS( XML_m, XML_sSub );
+ }
+ else if(( flags & ( 1 << RSUP )) == 1 << RSUP )
+ { // m:sSup
+ m_pSerializer->startElementNS(XML_m, XML_sSup);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << RSUP );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( RSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->endElementNS( XML_m, XML_sSup );
+ }
+ else if(( flags & ( 1 << LSUP | 1 << LSUB )) == ( 1 << LSUP | 1 << LSUB ))
+ { // m:sPre
+ m_pSerializer->startElementNS(XML_m, XML_sPre);
+ m_pSerializer->startElementNS(XML_m, XML_sub);
+ HandleNode( pNode->GetSubSup( LSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sub );
+ m_pSerializer->startElementNS(XML_m, XML_sup);
+ HandleNode( pNode->GetSubSup( LSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_sup );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << LSUP | 1 << LSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_sPre );
+ }
+ else if(( flags & ( 1 << CSUB )) == ( 1 << CSUB ))
+ { // m:limLow looks like a good element for central superscript
+ m_pSerializer->startElementNS(XML_m, XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << CSUB );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->GetSubSup( CSUB ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limLow );
+ }
+ else if(( flags & ( 1 << CSUP )) == ( 1 << CSUP ))
+ { // m:limUpp looks like a good element for central superscript
+ m_pSerializer->startElementNS(XML_m, XML_limUpp);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ flags &= ~( 1 << CSUP );
+ if( flags == 0 )
+ HandleNode( pNode->GetBody(), nLevel + 1 );
+ else
+ HandleSubSupScriptInternal( pNode, nLevel, flags );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->GetSubSup( CSUP ), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, XML_limUpp );
+ }
+ else
+ {
+ SAL_WARN("starmath.ooxml", "Unhandled sub/sup combination");
+ // TODO do not do anything, this should be probably an assert()
+ // HandleAllSubNodes( pNode, nLevel );
+ }
+}
+
+void SmOoxmlExport::HandleMatrix( const SmMatrixNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_m);
+ for (size_t row = 0; row < pNode->GetNumRows(); ++row)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_mr);
+ for (size_t col = 0; col < pNode->GetNumCols(); ++col)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ if( const SmNode* node = pNode->GetSubNode( row * pNode->GetNumCols() + col ))
+ HandleNode( node, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_mr );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_m );
+}
+
+void SmOoxmlExport::HandleBrace( const SmBraceNode* pNode, int nLevel )
+{
+ m_pSerializer->startElementNS(XML_m, XML_d);
+ m_pSerializer->startElementNS(XML_m, XML_dPr);
+
+ //check if the node has an opening brace
+ if( TNONE == pNode->OpeningBrace()->GetToken().eType )
+ m_pSerializer->singleElementNS(XML_m, XML_begChr, FSNS(XML_m, XML_val), "");
+ else
+ m_pSerializer->singleElementNS( XML_m, XML_begChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString( pNode->OpeningBrace()) );
+
+ std::vector< const SmNode* > subnodes;
+ if( pNode->Body()->GetType() == SmNodeType::Bracebody )
+ {
+ const SmBracebodyNode* body = static_cast< const SmBracebodyNode* >( pNode->Body());
+ bool separatorWritten = false; // assume all separators are the same
+ for (size_t i = 0; i < body->GetNumSubNodes(); ++i)
+ {
+ const SmNode* subnode = body->GetSubNode( i );
+ if (subnode->GetType() == SmNodeType::Math || subnode->GetType() == SmNodeType::MathIdent)
+ { // do not write, but write what separator it is
+ const SmMathSymbolNode* math = static_cast< const SmMathSymbolNode* >( subnode );
+ if( !separatorWritten )
+ {
+ m_pSerializer->singleElementNS( XML_m, XML_sepChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(math) );
+ separatorWritten = true;
+ }
+ }
+ else
+ subnodes.push_back( subnode );
+ }
+ }
+ else
+ subnodes.push_back( pNode->Body());
+
+ if( TNONE == pNode->ClosingBrace()->GetToken().eType )
+ m_pSerializer->singleElementNS(XML_m, XML_endChr, FSNS(XML_m, XML_val), "");
+ else
+ m_pSerializer->singleElementNS( XML_m, XML_endChr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(pNode->ClosingBrace()) );
+
+ m_pSerializer->endElementNS( XML_m, XML_dPr );
+ for(const SmNode* subnode : subnodes)
+ {
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( subnode, nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ }
+ m_pSerializer->endElementNS( XML_m, XML_d );
+}
+
+void SmOoxmlExport::HandleVerticalBrace( const SmVerticalBraceNode* pNode, int nLevel )
+{
+ SAL_INFO( "starmath.ooxml", "Vertical: " << int( pNode->GetToken().eType ));
+ switch( pNode->GetToken().eType )
+ {
+ case TOVERBRACE:
+ case TUNDERBRACE:
+ {
+ bool top = ( pNode->GetToken().eType == TOVERBRACE );
+ m_pSerializer->startElementNS(XML_m, top ? XML_limUpp : XML_limLow);
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ m_pSerializer->startElementNS(XML_m, XML_groupChr);
+ m_pSerializer->startElementNS(XML_m, XML_groupChrPr);
+ m_pSerializer->singleElementNS( XML_m, XML_chr,
+ FSNS( XML_m, XML_val ), mathSymbolToString(pNode->Brace()) );
+ // TODO not sure if pos and vertJc are correct
+ m_pSerializer->singleElementNS( XML_m, XML_pos,
+ FSNS( XML_m, XML_val ), top ? "top" : "bot" );
+ m_pSerializer->singleElementNS(XML_m, XML_vertJc, FSNS(XML_m, XML_val),
+ top ? "bot" : "top");
+ m_pSerializer->endElementNS( XML_m, XML_groupChrPr );
+ m_pSerializer->startElementNS(XML_m, XML_e);
+ HandleNode( pNode->Body(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->endElementNS( XML_m, XML_groupChr );
+ m_pSerializer->endElementNS( XML_m, XML_e );
+ m_pSerializer->startElementNS(XML_m, XML_lim);
+ HandleNode( pNode->Script(), nLevel + 1 );
+ m_pSerializer->endElementNS( XML_m, XML_lim );
+ m_pSerializer->endElementNS( XML_m, top ? XML_limUpp : XML_limLow );
+ break;
+ }
+ default:
+ SAL_WARN("starmath.ooxml", "Unhandled vertical brace");
+ HandleAllSubNodes( pNode, nLevel );
+ break;
+ }
+}
+
+void SmOoxmlExport::HandleBlank()
+{
+ m_pSerializer->startElementNS(XML_m, XML_r);
+ m_pSerializer->startElementNS(XML_m, XML_t, FSNS(XML_xml, XML_space), "preserve");
+ m_pSerializer->write( " " );
+ m_pSerializer->endElementNS( XML_m, XML_t );
+ m_pSerializer->endElementNS( XML_m, XML_r );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlexport.hxx b/starmath/source/ooxmlexport.hxx
new file mode 100644
index 000000000..cd2e9d446
--- /dev/null
+++ b/starmath/source/ooxmlexport.hxx
@@ -0,0 +1,46 @@
+/* -*- 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 "wordexportbase.hxx"
+
+#include <sax/fshelper.hxx>
+#include <oox/core/filterbase.hxx>
+#include <oox/export/utils.hxx>
+
+/**
+ Class implementing writing of formulas to OOXML.
+ */
+class SmOoxmlExport : public SmWordExportBase
+{
+public:
+ SmOoxmlExport(const SmNode* pIn, oox::core::OoxmlVersion version,
+ oox::drawingml::DocumentType documentType);
+ void ConvertFromStarMath(const ::sax_fastparser::FSHelperPtr& m_pSerializer, const sal_Int8);
+
+private:
+ void HandleVerticalStack(const SmNode* pNode, int nLevel) override;
+ void HandleText(const SmNode* pNode, int nLevel) override;
+ void HandleFractions(const SmNode* pNode, int nLevel, const char* type) override;
+ void HandleRoot(const SmRootNode* pNode, int nLevel) override;
+ void HandleAttribute(const SmAttributeNode* pNode, int nLevel) override;
+ void HandleOperator(const SmOperNode* pNode, int nLevel) override;
+ void HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) override;
+ void HandleMatrix(const SmMatrixNode* pNode, int nLevel) override;
+ void HandleBrace(const SmBraceNode* pNode, int nLevel) override;
+ void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) override;
+ void HandleBlank() override;
+ ::sax_fastparser::FSHelperPtr m_pSerializer;
+ oox::core::OoxmlVersion version;
+ /// needed to determine markup for nested run properties
+ oox::drawingml::DocumentType const m_DocumentType;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlimport.cxx b/starmath/source/ooxmlimport.cxx
new file mode 100644
index 000000000..b096d80ba
--- /dev/null
+++ b/starmath/source/ooxmlimport.cxx
@@ -0,0 +1,686 @@
+/* -*- 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 "ooxmlimport.hxx"
+#include <types.hxx>
+
+#include <oox/mathml/importutils.hxx>
+#include <oox/token/namespaces.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace oox::formulaimport;
+
+/*
+The primary internal data structure for the formula is the text representation
+(the SmNode tree is built from it), so read data must be converted into this format.
+*/
+
+#define OPENING( token ) XML_STREAM_OPENING( token )
+#define CLOSING( token ) XML_STREAM_CLOSING( token )
+
+// TODO create IS_OPENING(), IS_CLOSING() instead of doing 'next == OPENING( next )' ?
+
+SmOoxmlImport::SmOoxmlImport( oox::formulaimport::XmlStream& s )
+ : m_rStream( s )
+{
+}
+
+OUString SmOoxmlImport::ConvertToStarMath()
+{
+ return handleStream();
+}
+
+// "toplevel" of reading, there will be oMath (if there was oMathPara, that was
+// up to the parent component to handle)
+
+// NOT complete
+OUString SmOoxmlImport::handleStream()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( oMath ));
+ OUStringBuffer ret;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( M_TOKEN( oMath )))
+ {
+ // strictly speaking, it is not OMathArg here, but currently supported
+ // functionality is the same like OMathArg, in the future this may need improving
+ OUString item = readOMathArg( M_TOKEN( oMath ));
+ if( item.isEmpty())
+ continue;
+ if( !ret.isEmpty())
+ ret.append(" ");
+ ret.append(item);
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( oMath ));
+ // Placeholders are written out as nothing (i.e. nothing inside e.g. the <e> element),
+ // which will result in "{}" in the formula text. Fix this up.
+ OUString ret2 = ret.makeStringAndClear().replaceAll( "{}", "<?>" );
+ // And as a result, empty parts of the formula that are not placeholders are written out
+ // as a single space, so fix that up too.
+ ret2 = ret2.replaceAll( "{ }", "{}" );
+ SAL_INFO( "starmath.ooxml", "Formula: " << ret2 );
+ return ret2;
+}
+
+OUString SmOoxmlImport::readOMathArg( int stoptoken )
+{
+ OUStringBuffer ret;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( stoptoken ))
+ {
+ if( !ret.isEmpty())
+ ret.append(" ");
+ switch( m_rStream.currentToken())
+ {
+ case OPENING( M_TOKEN( acc )):
+ ret.append(handleAcc());
+ break;
+ case OPENING( M_TOKEN( bar )):
+ ret.append(handleBar());
+ break;
+ case OPENING( M_TOKEN( box )):
+ ret.append(handleBox());
+ break;
+ case OPENING( M_TOKEN( borderBox )):
+ ret.append(handleBorderBox());
+ break;
+ case OPENING( M_TOKEN( d )):
+ ret.append(handleD());
+ break;
+ case OPENING( M_TOKEN( eqArr )):
+ ret.append(handleEqArr());
+ break;
+ case OPENING( M_TOKEN( f )):
+ ret.append(handleF());
+ break;
+ case OPENING( M_TOKEN( func )):
+ ret.append(handleFunc());
+ break;
+ case OPENING( M_TOKEN( limLow )):
+ ret.append(handleLimLowUpp( LimLow ));
+ break;
+ case OPENING( M_TOKEN( limUpp )):
+ ret.append(handleLimLowUpp( LimUpp ));
+ break;
+ case OPENING( M_TOKEN( groupChr )):
+ ret.append(handleGroupChr());
+ break;
+ case OPENING( M_TOKEN( m )):
+ ret.append(handleM());
+ break;
+ case OPENING( M_TOKEN( nary )):
+ ret.append(handleNary());
+ break;
+ case OPENING( M_TOKEN( r )):
+ ret.append(handleR());
+ break;
+ case OPENING( M_TOKEN( rad )):
+ ret.append(handleRad());
+ break;
+ case OPENING( M_TOKEN( sPre )):
+ ret.append(handleSpre());
+ break;
+ case OPENING( M_TOKEN( sSub )):
+ ret.append(handleSsub());
+ break;
+ case OPENING( M_TOKEN( sSubSup )):
+ ret.append(handleSsubsup());
+ break;
+ case OPENING( M_TOKEN( sSup )):
+ ret.append(handleSsup());
+ break;
+ default:
+ m_rStream.handleUnexpectedTag();
+ break;
+ }
+ }
+ return ret.makeStringAndClear();
+}
+
+OUString SmOoxmlImport::readOMathArgInElement( int token )
+{
+ m_rStream.ensureOpeningTag( token );
+ OUString ret = readOMathArg( token );
+ m_rStream.ensureClosingTag( token );
+ return ret;
+}
+
+OUString SmOoxmlImport::handleAcc()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( acc ));
+ sal_Unicode accChr = 0x302;
+ if( XmlStream::Tag accPr = m_rStream.checkOpeningTag( M_TOKEN( accPr )))
+ {
+ if( XmlStream::Tag chr = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ accChr = chr.attribute( M_TOKEN( val ), accChr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( accPr ));
+ }
+ // see aTokenTable in parse.cxx
+ OUString acc;
+ switch( accChr )
+ {
+ case MS_BAR:
+ case MS_COMBBAR:
+ acc = "bar";
+ break;
+ case MS_CHECK:
+ case MS_COMBCHECK:
+ acc = "check";
+ break;
+ case MS_ACUTE:
+ case MS_COMBACUTE:
+ acc = "acute";
+ break;
+ case MS_COMBOVERLINE:
+ acc = "overline";
+ break;
+ case MS_GRAVE:
+ case MS_COMBGRAVE:
+ acc = "grave";
+ break;
+ case MS_BREVE:
+ case MS_COMBBREVE:
+ acc = "breve";
+ break;
+ case MS_CIRCLE:
+ case MS_COMBCIRCLE:
+ acc = "circle";
+ break;
+ case MS_RIGHTARROW:
+ case MS_VEC:
+ // prefer wide variants for these 3, .docx can't seem to differentiate
+ // between e.g. 'vec' and 'widevec', if whatever the accent is above is short, this
+ // shouldn't matter, but short above a longer expression doesn't look right
+ acc = "widevec";
+ break;
+ case MS_HARPOON:
+ acc = "wideharpoon";
+ break;
+ case MS_TILDE:
+ case MS_COMBTILDE:
+ acc = "widetilde";
+ break;
+ case MS_HAT:
+ case MS_COMBHAT:
+ acc = "widehat";
+ break;
+ case MS_DOT:
+ case MS_COMBDOT:
+ acc = "dot";
+ break;
+ case MS_DDOT:
+ case MS_COMBDDOT:
+ acc = "ddot";
+ break;
+ case MS_DDDOT:
+ acc = "dddot";
+ break;
+ default:
+ acc = "acute";
+ SAL_WARN( "starmath.ooxml", "Unknown m:chr in m:acc \'" << OUString(accChr) << "\'" );
+ break;
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( acc ));
+ return acc + " {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleBar()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( bar ));
+ enum pos_t { top, bot } topbot = bot;
+ if( m_rStream.checkOpeningTag( M_TOKEN( barPr )))
+ {
+ if( XmlStream::Tag pos = m_rStream.checkOpeningTag( M_TOKEN( pos )))
+ {
+ if( pos.attribute( M_TOKEN( val )) == "top" )
+ topbot = top;
+ else if( pos.attribute( M_TOKEN( val )) == "bot" )
+ topbot = bot;
+ m_rStream.ensureClosingTag( M_TOKEN( pos ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( barPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( bar ));
+ if( topbot == top )
+ return "overline {" + e + "}";
+ else
+ return "underline {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleBox()
+{
+ // there does not seem to be functionality in LO to actually implement this
+ // (or is there), but at least read in the contents instead of ignoring them
+ m_rStream.ensureOpeningTag( M_TOKEN( box ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( box ));
+ return e;
+}
+
+
+OUString SmOoxmlImport::handleBorderBox()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( borderBox ));
+ bool isStrikeH = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( borderBoxPr )))
+ {
+ if( XmlStream::Tag strikeH = m_rStream.checkOpeningTag( M_TOKEN( strikeH )))
+ {
+ if( strikeH.attribute( M_TOKEN( val ), false ))
+ isStrikeH = true;
+ m_rStream.ensureClosingTag( M_TOKEN( strikeH ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( borderBoxPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( borderBox ));
+ if( isStrikeH )
+ return "overstrike {" + e + "}";
+ // LO does not seem to implement anything for handling the other cases
+ return e;
+}
+
+OUString SmOoxmlImport::handleD()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( d ));
+ OUString opening = "(";
+ OUString closing = ")";
+ OUString separator = "|";
+ if( XmlStream::Tag dPr = m_rStream.checkOpeningTag( M_TOKEN( dPr )))
+ {
+ if( XmlStream::Tag begChr = m_rStream.checkOpeningTag( M_TOKEN( begChr )))
+ {
+ opening = begChr.attribute( M_TOKEN( val ), opening );
+ m_rStream.ensureClosingTag( M_TOKEN( begChr ));
+ }
+ if( XmlStream::Tag sepChr = m_rStream.checkOpeningTag( M_TOKEN( sepChr )))
+ {
+ separator = sepChr.attribute( M_TOKEN( val ), separator );
+ m_rStream.ensureClosingTag( M_TOKEN( sepChr ));
+ }
+ if( XmlStream::Tag endChr = m_rStream.checkOpeningTag( M_TOKEN( endChr )))
+ {
+ closing = endChr.attribute( M_TOKEN( val ), closing );
+ m_rStream.ensureClosingTag( M_TOKEN( endChr ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( dPr ));
+ }
+ if( opening == "{" )
+ opening = "left lbrace ";
+ if( closing == "}" )
+ closing = " right rbrace";
+ if( opening == u"\u27e6" )
+ opening = "left ldbracket ";
+ if( closing == u"\u27e7" )
+ closing = " right rdbracket";
+ if( opening == "|" )
+ opening = "left lline ";
+ if( closing == "|" )
+ closing = " right rline";
+ if (opening == OUStringChar(MS_DLINE)
+ || opening == OUStringChar(MS_DVERTLINE))
+ opening = "left ldline ";
+ if (closing == OUStringChar(MS_DLINE)
+ || closing == OUStringChar(MS_DVERTLINE))
+ closing = " right rdline";
+ if (opening == OUStringChar(MS_LANGLE)
+ || opening == OUStringChar(MS_LMATHANGLE))
+ opening = "left langle ";
+ if (closing == OUStringChar(MS_RANGLE)
+ || closing == OUStringChar(MS_RMATHANGLE))
+ closing = " right rangle";
+ // use scalable brackets (the explicit "left" or "right")
+ if( opening == "(" || opening == "[" )
+ opening = "left " + opening;
+ if( closing == ")" || closing == "]" )
+ closing = " right " + closing;
+ if( separator == "|" ) // plain "|" would be actually "V" (logical or)
+ separator = " mline ";
+ if( opening.isEmpty())
+ opening = "left none ";
+ if( closing.isEmpty())
+ closing = " right none";
+ OUStringBuffer ret;
+ ret.append( opening );
+ bool first = true;
+ while( m_rStream.findTag( OPENING( M_TOKEN( e ))))
+ {
+ if( !first )
+ ret.append( separator );
+ first = false;
+ ret.append( readOMathArgInElement( M_TOKEN( e )));
+ }
+ ret.append( closing );
+ m_rStream.ensureClosingTag( M_TOKEN( d ));
+ return ret.makeStringAndClear();
+}
+
+OUString SmOoxmlImport::handleEqArr()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( eqArr ));
+ OUStringBuffer ret;
+ do
+ { // there must be at least one m:e
+ if( !ret.isEmpty())
+ ret.append("#");
+ ret.append(" ");
+ ret.append(readOMathArgInElement( M_TOKEN( e )));
+ ret.append(" ");
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
+ m_rStream.ensureClosingTag( M_TOKEN( eqArr ));
+ return "stack {" + ret.makeStringAndClear() + "}";
+}
+
+OUString SmOoxmlImport::handleF()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( f ));
+ enum operation_t { bar, lin, noBar } operation = bar;
+ if( m_rStream.checkOpeningTag( M_TOKEN( fPr )))
+ {
+ if( XmlStream::Tag type = m_rStream.checkOpeningTag( M_TOKEN( type )))
+ {
+ if( type.attribute( M_TOKEN( val )) == "bar" )
+ operation = bar;
+ else if( type.attribute( M_TOKEN( val )) == "lin" )
+ operation = lin;
+ else if( type.attribute( M_TOKEN( val )) == "noBar" )
+ operation = noBar;
+ m_rStream.ensureClosingTag( M_TOKEN( type ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( fPr ));
+ }
+ OUString num = readOMathArgInElement( M_TOKEN( num ));
+ OUString den = readOMathArgInElement( M_TOKEN( den ));
+ m_rStream.ensureClosingTag( M_TOKEN( f ));
+ if( operation == bar )
+ return "{" + num + "} over {" + den + "}";
+ else if( operation == lin )
+ return "{" + num + "} / {" + den + "}";
+ else // noBar
+ {
+ return "binom {" + num + "} {" + den + "}";
+ }
+}
+
+OUString SmOoxmlImport::handleFunc()
+{
+//lim from{x rightarrow 1} x
+ m_rStream.ensureOpeningTag( M_TOKEN( func ));
+ OUString fname = readOMathArgInElement( M_TOKEN( fName ));
+ // fix the various functions
+ if( fname.startsWith( "lim csub {" ))
+ fname = OUString::Concat("lim from {") + fname.subView( 10 );
+ OUString ret = fname + " {" + readOMathArgInElement( M_TOKEN( e )) + "}";
+ m_rStream.ensureClosingTag( M_TOKEN( func ));
+ return ret;
+}
+
+OUString SmOoxmlImport::handleLimLowUpp( LimLowUpp_t limlowupp )
+{
+ int token = limlowupp == LimLow ? M_TOKEN( limLow ) : M_TOKEN( limUpp );
+ m_rStream.ensureOpeningTag( token );
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString lim = readOMathArgInElement( M_TOKEN( lim ));
+ m_rStream.ensureClosingTag( token );
+ // fix up overbrace/underbrace (use { }, as {} will be converted to a placeholder)
+ if( limlowupp == LimUpp && e.endsWith( " overbrace { }" ))
+ return e.subView( 0, e.getLength() - 2 ) + lim + "}";
+ if( limlowupp == LimLow && e.endsWith( " underbrace { }" ))
+ return e.subView( 0, e.getLength() - 2 ) + lim + "}";
+ return e
+ + ( limlowupp == LimLow
+ ? std::u16string_view( u" csub {" ) : std::u16string_view( u" csup {" ))
+ + lim + "}";
+}
+
+OUString SmOoxmlImport::handleGroupChr()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( groupChr ));
+ sal_Unicode chr = 0x23df;
+ enum pos_t { top, bot } pos = bot;
+ if( m_rStream.checkOpeningTag( M_TOKEN( groupChrPr )))
+ {
+ if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ chr = chrTag.attribute( M_TOKEN( val ), chr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ if( XmlStream::Tag posTag = m_rStream.checkOpeningTag( M_TOKEN( pos )))
+ {
+ if( posTag.attribute( M_TOKEN( val ), OUString( "bot" )) == "top" )
+ pos = top;
+ m_rStream.ensureClosingTag( M_TOKEN( pos ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( groupChrPr ));
+ }
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( groupChr ));
+ if( pos == top && chr == u'\x23de')
+ return "{" + e + "} overbrace { }";
+ if( pos == bot && chr == u'\x23df')
+ return "{" + e + "} underbrace { }";
+ if( pos == top )
+ return "{" + e + "} csup {" + OUStringChar( chr ) + "}";
+ else
+ return "{" + e + "} csub {" + OUStringChar( chr ) + "}";
+}
+
+OUString SmOoxmlImport::handleM()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( m ));
+ OUStringBuffer allrows;
+ do // there must be at least one m:mr
+ {
+ m_rStream.ensureOpeningTag( M_TOKEN( mr ));
+ OUStringBuffer row;
+ do // there must be at least one m:e
+ {
+ if( !row.isEmpty())
+ row.append(" # ");
+ row.append(readOMathArgInElement( M_TOKEN( e )));
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( e ))));
+ if( !allrows.isEmpty())
+ allrows.append(" ## ");
+ allrows.append(row);
+ m_rStream.ensureClosingTag( M_TOKEN( mr ));
+ } while( !m_rStream.atEnd() && m_rStream.findTag( OPENING( M_TOKEN( mr ))));
+ m_rStream.ensureClosingTag( M_TOKEN( m ));
+ return "matrix {" + allrows.makeStringAndClear() + "}";
+}
+
+OUString SmOoxmlImport::handleNary()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( nary ));
+ sal_Unicode chr = 0x222b;
+ bool subHide = false;
+ bool supHide = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( naryPr )))
+ {
+ if( XmlStream::Tag chrTag = m_rStream.checkOpeningTag( M_TOKEN( chr )))
+ {
+ chr = chrTag.attribute( M_TOKEN( val ), chr );
+ m_rStream.ensureClosingTag( M_TOKEN( chr ));
+ }
+ if( XmlStream::Tag subHideTag = m_rStream.checkOpeningTag( M_TOKEN( subHide )))
+ {
+ subHide = subHideTag.attribute( M_TOKEN( val ), subHide );
+ m_rStream.ensureClosingTag( M_TOKEN( subHide ));
+ }
+ if( XmlStream::Tag supHideTag = m_rStream.checkOpeningTag( M_TOKEN( supHide )))
+ {
+ supHide = supHideTag.attribute( M_TOKEN( val ), supHide );
+ m_rStream.ensureClosingTag( M_TOKEN( supHide ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( naryPr ));
+ }
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString ret;
+ switch( chr )
+ {
+ case MS_INT:
+ ret = "int";
+ break;
+ case MS_IINT:
+ ret = "iint";
+ break;
+ case MS_IIINT:
+ ret = "iiint";
+ break;
+ case MS_LINT:
+ ret = "lint";
+ break;
+ case MS_LLINT:
+ ret = "llint";
+ break;
+ case MS_LLLINT:
+ ret = "lllint";
+ break;
+ case MS_PROD:
+ ret = "prod";
+ break;
+ case MS_COPROD:
+ ret = "coprod";
+ break;
+ case MS_SUM:
+ ret = "sum";
+ break;
+ default:
+ SAL_WARN( "starmath.ooxml", "Unknown m:nary chr \'" << OUString(chr) << "\'" );
+ break;
+ }
+ if( !subHide )
+ ret += " from {" + sub + "}";
+ if( !supHide )
+ ret += " to {" + sup + "}";
+ ret += " {" + e + "}";
+ m_rStream.ensureClosingTag( M_TOKEN( nary ));
+ return ret;
+}
+
+// NOT complete
+OUString SmOoxmlImport::handleR()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( r ));
+ bool normal = false;
+ bool literal = false;
+ if( XmlStream::Tag rPr = m_rStream.checkOpeningTag( M_TOKEN( rPr )))
+ {
+ if( XmlStream::Tag litTag = m_rStream.checkOpeningTag( M_TOKEN( lit )))
+ {
+ literal = litTag.attribute( M_TOKEN( val ), true );
+ m_rStream.ensureClosingTag( M_TOKEN( lit ));
+ }
+ if( XmlStream::Tag norTag = m_rStream.checkOpeningTag( M_TOKEN( nor )))
+ {
+ normal = norTag.attribute( M_TOKEN( val ), true );
+ m_rStream.ensureClosingTag( M_TOKEN( nor ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( rPr ));
+ }
+ OUStringBuffer text;
+ while( !m_rStream.atEnd() && m_rStream.currentToken() != CLOSING( m_rStream.currentToken()))
+ {
+ switch( m_rStream.currentToken())
+ {
+ case OPENING( M_TOKEN( t )):
+ {
+ XmlStream::Tag rtag = m_rStream.ensureOpeningTag( M_TOKEN( t ));
+ if( rtag.attribute( OOX_TOKEN( xml, space )) != "preserve" )
+ text.append(o3tl::trim(rtag.text));
+ else
+ text.append(rtag.text);
+ m_rStream.ensureClosingTag( M_TOKEN( t ));
+ break;
+ }
+ default:
+ m_rStream.handleUnexpectedTag();
+ break;
+ }
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( r ));
+ if( normal || literal )
+ {
+ text.insert(0, "\"");
+ text.append("\"");
+ }
+ return text.makeStringAndClear().replaceAll("{", "\\{").replaceAll("}", "\\}");
+}
+
+OUString SmOoxmlImport::handleRad()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( rad ));
+ bool degHide = false;
+ if( m_rStream.checkOpeningTag( M_TOKEN( radPr )))
+ {
+ if( XmlStream::Tag degHideTag = m_rStream.checkOpeningTag( M_TOKEN( degHide )))
+ {
+ degHide = degHideTag.attribute( M_TOKEN( val ), degHide );
+ m_rStream.ensureClosingTag( M_TOKEN( degHide ));
+ }
+ m_rStream.ensureClosingTag( M_TOKEN( radPr ));
+ }
+ OUString deg = readOMathArgInElement( M_TOKEN( deg ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( rad ));
+ if( degHide )
+ return "sqrt {" + e + "}";
+ else
+ return "nroot {" + deg + "} {" + e + "}";
+}
+
+OUString SmOoxmlImport::handleSpre()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sPre ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ m_rStream.ensureClosingTag( M_TOKEN( sPre ));
+ return "{" + e + "} lsub {" + sub + "} lsup {" + sup + "}";
+}
+
+OUString SmOoxmlImport::handleSsub()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSub ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSub ));
+ return "{" + e + "} rsub {" + sub + "}";
+}
+
+OUString SmOoxmlImport::handleSsubsup()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSubSup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sub = readOMathArgInElement( M_TOKEN( sub ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSubSup ));
+ return "{" + e + "} rsub {" + sub + "} rsup {" + sup + "}";
+}
+
+OUString SmOoxmlImport::handleSsup()
+{
+ m_rStream.ensureOpeningTag( M_TOKEN( sSup ));
+ OUString e = readOMathArgInElement( M_TOKEN( e ));
+ OUString sup = readOMathArgInElement( M_TOKEN( sup ));
+ m_rStream.ensureClosingTag( M_TOKEN( sSup ));
+ return "{" + e + "} ^ {" + sup + "}";
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/ooxmlimport.hxx b/starmath/source/ooxmlimport.hxx
new file mode 100644
index 000000000..4f8df14bf
--- /dev/null
+++ b/starmath/source/ooxmlimport.hxx
@@ -0,0 +1,51 @@
+/* -*- 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 <rtl/ustring.hxx>
+
+namespace oox::formulaimport { class XmlStream; }
+/**
+ Class implementing reading of formulas from OOXML. The toplevel element is expected
+ to be oMath (handle oMathPara outside of this code).
+ */
+class SmOoxmlImport
+{
+public:
+ explicit SmOoxmlImport( oox::formulaimport::XmlStream& stream );
+ OUString ConvertToStarMath();
+private:
+ OUString handleStream();
+ OUString handleAcc();
+ OUString handleBar();
+ OUString handleBox();
+ OUString handleBorderBox();
+ OUString handleD();
+ OUString handleEqArr();
+ OUString handleF();
+ OUString handleFunc();
+ enum LimLowUpp_t { LimLow, LimUpp };
+ OUString handleLimLowUpp( LimLowUpp_t limlowupp );
+ OUString handleGroupChr();
+ OUString handleM();
+ OUString handleNary();
+ OUString handleR();
+ OUString handleRad();
+ OUString handleSpre();
+ OUString handleSsub();
+ OUString handleSsubsup();
+ OUString handleSsup();
+ OUString readOMathArg( int stoptoken );
+ OUString readOMathArgInElement( int token );
+
+ oox::formulaimport::XmlStream& m_rStream;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/parse.cxx b/starmath/source/parse.cxx
new file mode 100644
index 000000000..66daec9d2
--- /dev/null
+++ b/starmath/source/parse.cxx
@@ -0,0 +1,52 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <parse.hxx>
+#include <parse5.hxx>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+
+AbstractSmParser* starmathdatabase::GetDefaultSmParser()
+{
+ switch(SM_MOD()->GetConfig()->GetDefaultSmSyntaxVersion())
+ {
+ case 5:
+ {
+ AbstractSmParser* aParser = new SmParser5();
+ return aParser;
+ }
+ default:
+ throw std::range_error("parser version limit");
+ }
+}
+
+AbstractSmParser* starmathdatabase::GetVersionSmParser(sal_uInt16 nVersion)
+{
+ switch(nVersion)
+ {
+ case 5:
+ {
+ AbstractSmParser* aParser = new SmParser5();
+ return aParser;
+ }
+ default:
+ throw std::range_error("parser version limit");
+ }
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/parse5.cxx b/starmath/source/parse5.cxx
new file mode 100644
index 000000000..06aa373aa
--- /dev/null
+++ b/starmath/source/parse5.cxx
@@ -0,0 +1,2773 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/i18n/UnicodeType.hpp>
+#include <com/sun/star/i18n/KParseTokens.hpp>
+#include <com/sun/star/i18n/KParseType.hpp>
+#include <i18nlangtag/lang.h>
+#include <tools/lineend.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/syslocale.hxx>
+#include <osl/diagnose.h>
+#include <rtl/character.hxx>
+#include <parse5.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+#include <starmathdatabase.hxx>
+
+#include <stack>
+
+using namespace ::com::sun::star::i18n;
+
+//Definition of math keywords
+const SmTokenTableEntry aTokenTable[]
+ = { { u"abs", TABS, '\0', TG::UnOper, 13 },
+ { u"acute", TACUTE, MS_ACUTE, TG::Attribute, 5 },
+ { u"aleph", TALEPH, MS_ALEPH, TG::Standalone, 5 },
+ { u"alignb", TALIGNC, '\0', TG::Align, 0 },
+ { u"alignc", TALIGNC, '\0', TG::Align, 0 },
+ { u"alignl", TALIGNL, '\0', TG::Align, 0 },
+ { u"alignm", TALIGNC, '\0', TG::Align, 0 },
+ { u"alignr", TALIGNR, '\0', TG::Align, 0 },
+ { u"alignt", TALIGNC, '\0', TG::Align, 0 },
+ { u"and", TAND, MS_AND, TG::Product, 0 },
+ { u"approx", TAPPROX, MS_APPROX, TG::Relation, 0 },
+ { u"arccos", TACOS, '\0', TG::Function, 5 },
+ { u"arccot", TACOT, '\0', TG::Function, 5 },
+ { u"arcosh", TACOSH, '\0', TG::Function, 5 },
+ { u"arcoth", TACOTH, '\0', TG::Function, 5 },
+ { u"arcsin", TASIN, '\0', TG::Function, 5 },
+ { u"arctan", TATAN, '\0', TG::Function, 5 },
+ { u"arsinh", TASINH, '\0', TG::Function, 5 },
+ { u"artanh", TATANH, '\0', TG::Function, 5 },
+ { u"backepsilon", TBACKEPSILON, MS_BACKEPSILON, TG::Standalone, 5 },
+ { u"bar", TBAR, MS_BAR, TG::Attribute, 5 },
+ { u"binom", TBINOM, '\0', TG::NONE, 5 },
+ { u"bold", TBOLD, '\0', TG::FontAttr, 5 },
+ { u"boper", TBOPER, '\0', TG::Product, 0 },
+ { u"breve", TBREVE, MS_BREVE, TG::Attribute, 5 },
+ { u"bslash", TBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { u"cdot", TCDOT, MS_CDOT, TG::Product, 0 },
+ { u"check", TCHECK, MS_CHECK, TG::Attribute, 5 },
+ { u"circ", TCIRC, MS_CIRC, TG::Standalone, 5 },
+ { u"circle", TCIRCLE, MS_CIRCLE, TG::Attribute, 5 },
+ { u"color", TCOLOR, '\0', TG::FontAttr, 5 },
+ { u"coprod", TCOPROD, MS_COPROD, TG::Oper, 5 },
+ { u"cos", TCOS, '\0', TG::Function, 5 },
+ { u"cosh", TCOSH, '\0', TG::Function, 5 },
+ { u"cot", TCOT, '\0', TG::Function, 5 },
+ { u"coth", TCOTH, '\0', TG::Function, 5 },
+ { u"csub", TCSUB, '\0', TG::Power, 0 },
+ { u"csup", TCSUP, '\0', TG::Power, 0 },
+ { u"dddot", TDDDOT, MS_DDDOT, TG::Attribute, 5 },
+ { u"ddot", TDDOT, MS_DDOT, TG::Attribute, 5 },
+ { u"def", TDEF, MS_DEF, TG::Relation, 0 },
+ { u"div", TDIV, MS_DIV, TG::Product, 0 },
+ { u"divides", TDIVIDES, MS_LINE, TG::Relation, 0 },
+ { u"dlarrow", TDLARROW, MS_DLARROW, TG::Standalone, 5 },
+ { u"dlrarrow", TDLRARROW, MS_DLRARROW, TG::Standalone, 5 },
+ { u"dot", TDOT, MS_DOT, TG::Attribute, 5 },
+ { u"dotsaxis", TDOTSAXIS, MS_DOTSAXIS, TG::Standalone, 5 }, // 5 to continue expression
+ { u"dotsdiag", TDOTSDIAG, MS_DOTSUP, TG::Standalone, 5 },
+ { u"dotsdown", TDOTSDOWN, MS_DOTSDOWN, TG::Standalone, 5 },
+ { u"dotslow", TDOTSLOW, MS_DOTSLOW, TG::Standalone, 5 },
+ { u"dotsup", TDOTSUP, MS_DOTSUP, TG::Standalone, 5 },
+ { u"dotsvert", TDOTSVERT, MS_DOTSVERT, TG::Standalone, 5 },
+ { u"downarrow", TDOWNARROW, MS_DOWNARROW, TG::Standalone, 5 },
+ { u"drarrow", TDRARROW, MS_DRARROW, TG::Standalone, 5 },
+ { u"emptyset", TEMPTYSET, MS_EMPTYSET, TG::Standalone, 5 },
+ { u"equiv", TEQUIV, MS_EQUIV, TG::Relation, 0 },
+ { u"evaluate", TEVALUATE, '\0', TG::NONE, 0 },
+ { u"exists", TEXISTS, MS_EXISTS, TG::Standalone, 5 },
+ { u"exp", TEXP, '\0', TG::Function, 5 },
+ { u"fact", TFACT, MS_FACT, TG::UnOper, 5 },
+ { u"fixed", TFIXED, '\0', TG::Font, 0 },
+ { u"font", TFONT, '\0', TG::FontAttr, 5 },
+ { u"forall", TFORALL, MS_FORALL, TG::Standalone, 5 },
+ { u"fourier", TFOURIER, MS_FOURIER, TG::Standalone, 5 },
+ { u"frac", TFRAC, '\0', TG::NONE, 5 },
+ { u"from", TFROM, '\0', TG::Limit, 0 },
+ { u"func", TFUNC, '\0', TG::Function, 5 },
+ { u"ge", TGE, MS_GE, TG::Relation, 0 },
+ { u"geslant", TGESLANT, MS_GESLANT, TG::Relation, 0 },
+ { u"gg", TGG, MS_GG, TG::Relation, 0 },
+ { u"grave", TGRAVE, MS_GRAVE, TG::Attribute, 5 },
+ { u"gt", TGT, MS_GT, TG::Relation, 0 },
+ { u"harpoon", THARPOON, MS_HARPOON, TG::Attribute, 5 },
+ { u"hat", THAT, MS_HAT, TG::Attribute, 5 },
+ { u"hbar", THBAR, MS_HBAR, TG::Standalone, 5 },
+ { u"hex", THEX, '\0', TG::NONE, 5 },
+ { u"iiint", TIIINT, MS_IIINT, TG::Oper, 5 },
+ { u"iint", TIINT, MS_IINT, TG::Oper, 5 },
+ { u"im", TIM, MS_IM, TG::Standalone, 5 },
+ { u"in", TIN, MS_IN, TG::Relation, 0 },
+ { u"infinity", TINFINITY, MS_INFINITY, TG::Standalone, 5 },
+ { u"infty", TINFINITY, MS_INFINITY, TG::Standalone, 5 },
+ { u"int", TINT, MS_INT, TG::Oper, 5 },
+ { u"intd", TINTD, MS_INT, TG::Oper, 5 },
+ { u"intersection", TINTERSECT, MS_INTERSECT, TG::Product, 0 },
+ { u"it", TIT, '\0', TG::Product, 0 },
+ { u"ital", TITALIC, '\0', TG::FontAttr, 5 },
+ { u"italic", TITALIC, '\0', TG::FontAttr, 5 },
+ { u"lambdabar", TLAMBDABAR, MS_LAMBDABAR, TG::Standalone, 5 },
+ { u"langle", TLANGLE, MS_LMATHANGLE, TG::LBrace, 5 },
+ { u"laplace", TLAPLACE, MS_LAPLACE, TG::Standalone, 5 },
+ { u"lbrace", TLBRACE, MS_LBRACE, TG::LBrace, 5 },
+ { u"lceil", TLCEIL, MS_LCEIL, TG::LBrace, 5 },
+ { u"ldbracket", TLDBRACKET, MS_LDBRACKET, TG::LBrace, 5 },
+ { u"ldline", TLDLINE, MS_DVERTLINE, TG::LBrace, 5 },
+ { u"le", TLE, MS_LE, TG::Relation, 0 },
+ { u"left", TLEFT, '\0', TG::NONE, 5 },
+ { u"leftarrow", TLEFTARROW, MS_LEFTARROW, TG::Standalone, 5 },
+ { u"leslant", TLESLANT, MS_LESLANT, TG::Relation, 0 },
+ { u"lfloor", TLFLOOR, MS_LFLOOR, TG::LBrace, 5 },
+ { u"lim", TLIM, '\0', TG::Oper, 5 },
+ { u"liminf", TLIMINF, '\0', TG::Oper, 5 },
+ { u"limsup", TLIMSUP, '\0', TG::Oper, 5 },
+ { u"lint", TLINT, MS_LINT, TG::Oper, 5 },
+ { u"ll", TLL, MS_LL, TG::Relation, 0 },
+ { u"lline", TLLINE, MS_VERTLINE, TG::LBrace, 5 },
+ { u"llint", TLLINT, MS_LLINT, TG::Oper, 5 },
+ { u"lllint", TLLLINT, MS_LLLINT, TG::Oper, 5 },
+ { u"ln", TLN, '\0', TG::Function, 5 },
+ { u"log", TLOG, '\0', TG::Function, 5 },
+ { u"lrline", TLRLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
+ { u"lrdline", TLRDLINE, MS_VERTLINE, TG::LBrace | TG::RBrace, 5 },
+ { u"lsub", TLSUB, '\0', TG::Power, 0 },
+ { u"lsup", TLSUP, '\0', TG::Power, 0 },
+ { u"lt", TLT, MS_LT, TG::Relation, 0 },
+ { u"matrix", TMATRIX, '\0', TG::NONE, 5 },
+ { u"minusplus", TMINUSPLUS, MS_MINUSPLUS, TG::UnOper | TG::Sum, 5 },
+ { u"mline", TMLINE, MS_VERTLINE, TG::NONE, 0 }, //! not in TG::RBrace, Level 0
+ { u"nabla", TNABLA, MS_NABLA, TG::Standalone, 5 },
+ { u"nbold", TNBOLD, '\0', TG::FontAttr, 5 },
+ { u"ndivides", TNDIVIDES, MS_NDIVIDES, TG::Relation, 0 },
+ { u"neg", TNEG, MS_NEG, TG::UnOper, 5 },
+ { u"neq", TNEQ, MS_NEQ, TG::Relation, 0 },
+ { u"newline", TNEWLINE, '\0', TG::NONE, 0 },
+ { u"ni", TNI, MS_NI, TG::Relation, 0 },
+ { u"nitalic", TNITALIC, '\0', TG::FontAttr, 5 },
+ { u"none", TNONE, '\0', TG::LBrace | TG::RBrace, 0 },
+ { u"nospace", TNOSPACE, '\0', TG::Standalone, 5 },
+ { u"notexists", TNOTEXISTS, MS_NOTEXISTS, TG::Standalone, 5 },
+ { u"notin", TNOTIN, MS_NOTIN, TG::Relation, 0 },
+ { u"nprec", TNOTPRECEDES, MS_NOTPRECEDES, TG::Relation, 0 },
+ { u"nroot", TNROOT, MS_SQRT, TG::UnOper, 5 },
+ { u"nsubset", TNSUBSET, MS_NSUBSET, TG::Relation, 0 },
+ { u"nsubseteq", TNSUBSETEQ, MS_NSUBSETEQ, TG::Relation, 0 },
+ { u"nsucc", TNOTSUCCEEDS, MS_NOTSUCCEEDS, TG::Relation, 0 },
+ { u"nsupset", TNSUPSET, MS_NSUPSET, TG::Relation, 0 },
+ { u"nsupseteq", TNSUPSETEQ, MS_NSUPSETEQ, TG::Relation, 0 },
+ { u"odivide", TODIVIDE, MS_ODIVIDE, TG::Product, 0 },
+ { u"odot", TODOT, MS_ODOT, TG::Product, 0 },
+ { u"ominus", TOMINUS, MS_OMINUS, TG::Sum, 0 },
+ { u"oper", TOPER, '\0', TG::Oper, 5 },
+ { u"oplus", TOPLUS, MS_OPLUS, TG::Sum, 0 },
+ { u"or", TOR, MS_OR, TG::Sum, 0 },
+ { u"ortho", TORTHO, MS_ORTHO, TG::Relation, 0 },
+ { u"otimes", TOTIMES, MS_OTIMES, TG::Product, 0 },
+ { u"over", TOVER, '\0', TG::Product, 0 },
+ { u"overbrace", TOVERBRACE, MS_OVERBRACE, TG::Product, 5 },
+ { u"overline", TOVERLINE, '\0', TG::Attribute, 5 },
+ { u"overstrike", TOVERSTRIKE, '\0', TG::Attribute, 5 },
+ { u"owns", TNI, MS_NI, TG::Relation, 0 },
+ { u"parallel", TPARALLEL, MS_DLINE, TG::Relation, 0 },
+ { u"partial", TPARTIAL, MS_PARTIAL, TG::Standalone, 5 },
+ { u"phantom", TPHANTOM, '\0', TG::FontAttr, 5 },
+ { u"plusminus", TPLUSMINUS, MS_PLUSMINUS, TG::UnOper | TG::Sum, 5 },
+ { u"prec", TPRECEDES, MS_PRECEDES, TG::Relation, 0 },
+ { u"preccurlyeq", TPRECEDESEQUAL, MS_PRECEDESEQUAL, TG::Relation, 0 },
+ { u"precsim", TPRECEDESEQUIV, MS_PRECEDESEQUIV, TG::Relation, 0 },
+ { u"prod", TPROD, MS_PROD, TG::Oper, 5 },
+ { u"prop", TPROP, MS_PROP, TG::Relation, 0 },
+ { u"rangle", TRANGLE, MS_RMATHANGLE, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"rbrace", TRBRACE, MS_RBRACE, TG::RBrace, 0 },
+ { u"rceil", TRCEIL, MS_RCEIL, TG::RBrace, 0 },
+ { u"rdbracket", TRDBRACKET, MS_RDBRACKET, TG::RBrace, 0 },
+ { u"rdline", TRDLINE, MS_DVERTLINE, TG::RBrace, 0 },
+ { u"re", TRE, MS_RE, TG::Standalone, 5 },
+ { u"rfloor", TRFLOOR, MS_RFLOOR, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"right", TRIGHT, '\0', TG::NONE, 0 },
+ { u"rightarrow", TRIGHTARROW, MS_RIGHTARROW, TG::Standalone, 5 },
+ { u"rline", TRLINE, MS_VERTLINE, TG::RBrace, 0 }, //! 0 to terminate expression
+ { u"rsub", TRSUB, '\0', TG::Power, 0 },
+ { u"rsup", TRSUP, '\0', TG::Power, 0 },
+ { u"sans", TSANS, '\0', TG::Font, 0 },
+ { u"serif", TSERIF, '\0', TG::Font, 0 },
+ { u"setC", TSETC, MS_SETC, TG::Standalone, 5 },
+ { u"setminus", TSETMINUS, MS_BACKSLASH, TG::Product, 0 },
+ { u"setN", TSETN, MS_SETN, TG::Standalone, 5 },
+ { u"setQ", TSETQ, MS_SETQ, TG::Standalone, 5 },
+ { u"setquotient", TSETQUOTIENT, MS_SLASH, TG::Product, 0 },
+ { u"setR", TSETR, MS_SETR, TG::Standalone, 5 },
+ { u"setZ", TSETZ, MS_SETZ, TG::Standalone, 5 },
+ { u"sim", TSIM, MS_SIM, TG::Relation, 0 },
+ { u"simeq", TSIMEQ, MS_SIMEQ, TG::Relation, 0 },
+ { u"sin", TSIN, '\0', TG::Function, 5 },
+ { u"sinh", TSINH, '\0', TG::Function, 5 },
+ { u"size", TSIZE, '\0', TG::FontAttr, 5 },
+ { u"slash", TSLASH, MS_SLASH, TG::Product, 0 },
+ { u"sqrt", TSQRT, MS_SQRT, TG::UnOper, 5 },
+ { u"stack", TSTACK, '\0', TG::NONE, 5 },
+ { u"sub", TRSUB, '\0', TG::Power, 0 },
+ { u"subset", TSUBSET, MS_SUBSET, TG::Relation, 0 },
+ { u"subseteq", TSUBSETEQ, MS_SUBSETEQ, TG::Relation, 0 },
+ { u"succ", TSUCCEEDS, MS_SUCCEEDS, TG::Relation, 0 },
+ { u"succcurlyeq", TSUCCEEDSEQUAL, MS_SUCCEEDSEQUAL, TG::Relation, 0 },
+ { u"succsim", TSUCCEEDSEQUIV, MS_SUCCEEDSEQUIV, TG::Relation, 0 },
+ { u"sum", TSUM, MS_SUM, TG::Oper, 5 },
+ { u"sup", TRSUP, '\0', TG::Power, 0 },
+ { u"supset", TSUPSET, MS_SUPSET, TG::Relation, 0 },
+ { u"supseteq", TSUPSETEQ, MS_SUPSETEQ, TG::Relation, 0 },
+ { u"tan", TTAN, '\0', TG::Function, 5 },
+ { u"tanh", TTANH, '\0', TG::Function, 5 },
+ { u"tilde", TTILDE, MS_TILDE, TG::Attribute, 5 },
+ { u"times", TTIMES, MS_TIMES, TG::Product, 0 },
+ { u"to", TTO, '\0', TG::Limit, 0 },
+ { u"toward", TTOWARD, MS_RIGHTARROW, TG::Relation, 0 },
+ { u"transl", TTRANSL, MS_TRANSL, TG::Relation, 0 },
+ { u"transr", TTRANSR, MS_TRANSR, TG::Relation, 0 },
+ { u"underbrace", TUNDERBRACE, MS_UNDERBRACE, TG::Product, 5 },
+ { u"underline", TUNDERLINE, '\0', TG::Attribute, 5 },
+ { u"union", TUNION, MS_UNION, TG::Sum, 0 },
+ { u"uoper", TUOPER, '\0', TG::UnOper, 5 },
+ { u"uparrow", TUPARROW, MS_UPARROW, TG::Standalone, 5 },
+ { u"vec", TVEC, MS_VEC, TG::Attribute, 5 },
+ { u"widebslash", TWIDEBACKSLASH, MS_BACKSLASH, TG::Product, 0 },
+ { u"wideharpoon", TWIDEHARPOON, MS_HARPOON, TG::Attribute, 5 },
+ { u"widehat", TWIDEHAT, MS_HAT, TG::Attribute, 5 },
+ { u"wideslash", TWIDESLASH, MS_SLASH, TG::Product, 0 },
+ { u"widetilde", TWIDETILDE, MS_TILDE, TG::Attribute, 5 },
+ { u"widevec", TWIDEVEC, MS_VEC, TG::Attribute, 5 },
+ { u"wp", TWP, MS_WP, TG::Standalone, 5 } };
+
+// First character may be any alphabetic
+const sal_Int32 coStartFlags = KParseTokens::ANY_LETTER | KParseTokens::IGNORE_LEADING_WS;
+
+// Continuing characters may be any alphabetic
+const sal_Int32 coContFlags = (coStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
+ | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
+// First character for numbers, may be any numeric or dot
+const sal_Int32 coNumStartFlags
+ = KParseTokens::ASC_DIGIT | KParseTokens::ASC_DOT | KParseTokens::IGNORE_LEADING_WS;
+// Continuing characters for numbers, may be any numeric or dot or comma.
+// tdf#127873: additionally accept ',' comma group separator as too many
+// existing documents unwittingly may have used that as decimal separator
+// in such locales (though it never was as this is always the en-US locale
+// and the group separator is only parsed away).
+const sal_Int32 coNumContFlags = (coNumStartFlags & ~KParseTokens::IGNORE_LEADING_WS)
+ | KParseTokens::GROUP_SEPARATOR_IN_NUMBER;
+// First character for numbers hexadecimal
+const sal_Int32 coNum16StartFlags
+ = KParseTokens::ASC_DIGIT | KParseTokens::ASC_UPALPHA | KParseTokens::IGNORE_LEADING_WS;
+
+// Continuing characters for numbers hexadecimal
+const sal_Int32 coNum16ContFlags = (coNum16StartFlags & ~KParseTokens::IGNORE_LEADING_WS);
+// user-defined char continuing characters may be any alphanumeric or dot.
+const sal_Int32 coUserDefinedCharContFlags = KParseTokens::ANY_LETTER_OR_NUMBER
+ | KParseTokens::ASC_DOT
+ | KParseTokens::TWO_DOUBLE_QUOTES_BREAK_STRING;
+
+//Checks if keyword is in the list.
+static inline bool findCompare(const SmTokenTableEntry& lhs, const OUString& s)
+{
+ return s.compareToIgnoreAsciiCase(lhs.aIdent) > 0;
+}
+
+//Returns the SmTokenTableEntry for a keyword
+static const SmTokenTableEntry* GetTokenTableEntry(const OUString& rName)
+{
+ if (rName.isEmpty())
+ return nullptr; //avoid null pointer exceptions
+ //Looks for the first keyword after or equal to rName in alphabetical order.
+ auto findIter
+ = std::lower_bound(std::begin(aTokenTable), std::end(aTokenTable), rName, findCompare);
+ if (findIter != std::end(aTokenTable) && rName.equalsIgnoreAsciiCase(findIter->aIdent))
+ return &*findIter; //check is equal
+ return nullptr; //not found
+}
+
+static bool IsDelimiter(const OUString& rTxt, sal_Int32 nPos)
+{ // returns 'true' iff cChar is '\0' or a delimiter
+
+ assert(nPos <= rTxt.getLength()); //index out of range
+ if (nPos == rTxt.getLength())
+ return true; //This is EOF
+ sal_Unicode cChar = rTxt[nPos];
+
+ // check if 'cChar' is in the delimiter table
+ static const sal_Unicode aDelimiterTable[] = {
+ ' ', '{', '}', '(', ')', '\t', '\n', '\r', '+', '-', '*', '/', '=', '[',
+ ']', '^', '_', '#', '%', '>', '<', '&', '|', '\\', '"', '~', '`'
+ }; //reordered by usage (by eye) for nanoseconds saving.
+
+ //checks the array
+ for (auto const& cDelimiter : aDelimiterTable)
+ {
+ if (cDelimiter == cChar)
+ return true;
+ }
+
+ //special chars support
+ sal_Int16 nTypJp = SM_MOD()->GetSysLocale().GetCharClass().getType(rTxt, nPos);
+ return (nTypJp == css::i18n::UnicodeType::SPACE_SEPARATOR
+ || nTypJp == css::i18n::UnicodeType::CONTROL);
+}
+
+// checks number used as arguments in Math formulas (e.g. 'size' command)
+// Format: no negative numbers, must start with a digit, no exponent notation, ...
+static bool lcl_IsNumber(const OUString& rText)
+{
+ bool bPoint = false;
+ const sal_Unicode* pBuffer = rText.getStr();
+ for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
+ {
+ const sal_Unicode cChar = *pBuffer;
+ if (cChar == '.')
+ {
+ if (bPoint)
+ return false;
+ else
+ bPoint = true;
+ }
+ else if (!rtl::isAsciiDigit(cChar))
+ return false;
+ }
+ return true;
+}
+// checks number used as arguments in Math formulas (e.g. 'size' command)
+// Format: no negative numbers, must start with a digit, no exponent notation, ...
+static bool lcl_IsNotWholeNumber(const OUString& rText)
+{
+ const sal_Unicode* pBuffer = rText.getStr();
+ for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
+ if (!rtl::isAsciiDigit(*pBuffer))
+ return true;
+ return false;
+}
+// checks hex number used as arguments in Math formulas (e.g. 'hex' command)
+// Format: no negative numbers, must start with a digit, no exponent notation, ...
+static bool lcl_IsNotWholeNumber16(const OUString& rText)
+{
+ const sal_Unicode* pBuffer = rText.getStr();
+ for (sal_Int32 nPos = 0; nPos < rText.getLength(); nPos++, pBuffer++)
+ if (!rtl::isAsciiCanonicHexDigit(*pBuffer))
+ return true;
+ return false;
+}
+
+//Text replace onto m_aBufferString
+void SmParser5::Replace(sal_Int32 nPos, sal_Int32 nLen, const OUString& rText)
+{
+ assert(nPos + nLen <= m_aBufferString.getLength()); //checks if length allows text replace
+
+ m_aBufferString = m_aBufferString.replaceAt(nPos, nLen, rText); //replace and reindex
+ sal_Int32 nChg = rText.getLength() - nLen;
+ m_nBufferIndex = m_nBufferIndex + nChg;
+ m_nTokenIndex = m_nTokenIndex + nChg;
+}
+
+void SmParser5::NextToken() //Central part of the parser
+{
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+ ParseResult aRes;
+ sal_Int32 nRealStart;
+ bool bCont;
+ do
+ {
+ // skip white spaces
+ while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
+ ++m_nBufferIndex;
+
+ // Try to parse a number in a locale-independent manner using
+ // '.' as decimal separator.
+ // See https://bz.apache.org/ooo/show_bug.cgi?id=45779
+ aRes
+ = m_aNumCC.parsePredefinedToken(KParseType::ASC_NUMBER, m_aBufferString, m_nBufferIndex,
+ coNumStartFlags, "", coNumContFlags, "");
+
+ if (aRes.TokenType == 0)
+ {
+ // Try again with the default token parsing.
+ aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
+ coContFlags, "");
+ }
+
+ nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
+ m_nBufferIndex = nRealStart;
+
+ bCont = false;
+ if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
+ {
+ // keep data needed for tokens row and col entry up to date
+ ++m_nRow;
+ m_nBufferIndex = m_nColOff = nRealStart + 1;
+ bCont = true;
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
+ {
+ //SkipComment
+ m_nBufferIndex = nRealStart + 2;
+ while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
+ ++m_nBufferIndex;
+ bCont = true;
+ }
+ }
+
+ } while (bCont);
+
+ // set index of current token
+ m_nTokenIndex = m_nBufferIndex;
+ sal_uInt32 nCol = nRealStart - m_nColOff;
+
+ bool bHandled = true;
+ if (nRealStart >= nBufLen)
+ {
+ m_aCurToken.eType = TEND;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText.clear();
+ }
+ else if (aRes.TokenType & KParseType::ANY_NUMBER)
+ {
+ assert(aRes.EndPos > 0);
+ if (m_aBufferString[aRes.EndPos - 1] == ',' && aRes.EndPos < nBufLen
+ && m_pSysCC->getType(m_aBufferString, aRes.EndPos) != UnicodeType::SPACE_SEPARATOR)
+ {
+ // Comma followed by a non-space char is unlikely for decimal/thousands separator.
+ --aRes.EndPos;
+ }
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ m_aCurToken.eType = TNUMBER;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = m_aBufferString.copy(nRealStart, n);
+
+ SAL_WARN_IF(!IsDelimiter(m_aBufferString, aRes.EndPos), "starmath",
+ "identifier really finished? (compatibility!)");
+ }
+ else if (aRes.TokenType & KParseType::DOUBLE_QUOTE_STRING)
+ {
+ m_aCurToken.eType = TTEXT;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = aRes.DequotedNameOrString;
+ nCol++;
+ }
+ else if (aRes.TokenType & KParseType::IDENTNAME)
+ {
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ OUString aName(m_aBufferString.copy(nRealStart, n));
+ const SmTokenTableEntry* pEntry = GetTokenTableEntry(aName);
+
+ if (pEntry)
+ {
+ m_aCurToken.eType = pEntry->eType;
+ m_aCurToken.setChar(pEntry->cMathChar);
+ m_aCurToken.nGroup = pEntry->nGroup;
+ m_aCurToken.nLevel = pEntry->nLevel;
+ m_aCurToken.aText = pEntry->aIdent;
+ }
+ else
+ {
+ m_aCurToken.eType = TIDENT;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = aName;
+
+ SAL_WARN_IF(!IsDelimiter(m_aBufferString, aRes.EndPos), "starmath",
+ "identifier really finished? (compatibility!)");
+ }
+ }
+ else if (aRes.TokenType == 0 && '_' == m_aBufferString[nRealStart])
+ {
+ m_aCurToken.eType = TRSUB;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::Power;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "_";
+
+ aRes.EndPos = nRealStart + 1;
+ }
+ else if (aRes.TokenType & KParseType::BOOLEAN)
+ {
+ sal_Int32& rnEndPos = aRes.EndPos;
+ if (rnEndPos - nRealStart <= 2)
+ {
+ sal_Unicode ch = m_aBufferString[nRealStart];
+ switch (ch)
+ {
+ case '<':
+ {
+ if (m_aBufferString.match("<<", nRealStart))
+ {
+ m_aCurToken.eType = TLL;
+ m_aCurToken.setChar(MS_LL);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<<";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<=", nRealStart))
+ {
+ m_aCurToken.eType = TLE;
+ m_aCurToken.setChar(MS_LE);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<=";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<-", nRealStart))
+ {
+ m_aCurToken.eType = TLEFTARROW;
+ m_aCurToken.setChar(MS_LEFTARROW);
+ m_aCurToken.nGroup = TG::Standalone;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "<-";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<>", nRealStart))
+ {
+ m_aCurToken.eType = TNEQ;
+ m_aCurToken.setChar(MS_NEQ);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<>";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("<?>", nRealStart))
+ {
+ m_aCurToken.eType = TPLACE;
+ m_aCurToken.setChar(MS_PLACE);
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "<?>";
+
+ rnEndPos = nRealStart + 3;
+ }
+ else
+ {
+ m_aCurToken.eType = TLT;
+ m_aCurToken.setChar(MS_LT);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "<";
+ }
+ }
+ break;
+ case '>':
+ {
+ if (m_aBufferString.match(">=", nRealStart))
+ {
+ m_aCurToken.eType = TGE;
+ m_aCurToken.setChar(MS_GE);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">=";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match(">>", nRealStart))
+ {
+ m_aCurToken.eType = TGG;
+ m_aCurToken.setChar(MS_GG);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">>";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TGT;
+ m_aCurToken.setChar(MS_GT);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = ">";
+ }
+ }
+ break;
+ default:
+ bHandled = false;
+ }
+ }
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ sal_Int32& rnEndPos = aRes.EndPos;
+ if (rnEndPos - nRealStart == 1)
+ {
+ sal_Unicode ch = m_aBufferString[nRealStart];
+ switch (ch)
+ {
+ case '%':
+ {
+ //! modifies aRes.EndPos
+
+ OSL_ENSURE(rnEndPos >= nBufLen || '%' != m_aBufferString[rnEndPos],
+ "unexpected comment start");
+
+ // get identifier of user-defined character
+ ParseResult aTmpRes = m_pSysCC->parseAnyToken(m_aBufferString, rnEndPos,
+ KParseTokens::ANY_LETTER, "",
+ coUserDefinedCharContFlags, "");
+
+ sal_Int32 nTmpStart = rnEndPos + aTmpRes.LeadingWhiteSpace;
+
+ // default setting for the case that no identifier
+ // i.e. a valid symbol-name is following the '%'
+ // character
+ m_aCurToken.eType = TTEXT;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "%";
+
+ if (aTmpRes.TokenType & KParseType::IDENTNAME)
+ {
+ sal_Int32 n = aTmpRes.EndPos - nTmpStart;
+ m_aCurToken.eType = TSPECIAL;
+ m_aCurToken.aText = m_aBufferString.copy(nTmpStart - 1, n + 1);
+
+ OSL_ENSURE(aTmpRes.EndPos > rnEndPos, "empty identifier");
+ if (aTmpRes.EndPos > rnEndPos)
+ rnEndPos = aTmpRes.EndPos;
+ else
+ ++rnEndPos;
+ }
+
+ // if no symbol-name was found we start-over with
+ // finding the next token right after the '%' sign.
+ // I.e. we leave rnEndPos unmodified.
+ }
+ break;
+ case '[':
+ {
+ m_aCurToken.eType = TLBRACKET;
+ m_aCurToken.setChar(MS_LBRACKET);
+ m_aCurToken.nGroup = TG::LBrace;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "[";
+ }
+ break;
+ case '\\':
+ {
+ m_aCurToken.eType = TESCAPE;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "\\";
+ }
+ break;
+ case ']':
+ {
+ m_aCurToken.eType = TRBRACKET;
+ m_aCurToken.setChar(MS_RBRACKET);
+ m_aCurToken.nGroup = TG::RBrace;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "]";
+ }
+ break;
+ case '^':
+ {
+ m_aCurToken.eType = TRSUP;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::Power;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "^";
+ }
+ break;
+ case '`':
+ {
+ m_aCurToken.eType = TSBLANK;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::Blank;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "`";
+ }
+ break;
+ case '{':
+ {
+ m_aCurToken.eType = TLGROUP;
+ m_aCurToken.setChar(MS_LBRACE);
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "{";
+ }
+ break;
+ case '|':
+ {
+ m_aCurToken.eType = TOR;
+ m_aCurToken.setChar(MS_OR);
+ m_aCurToken.nGroup = TG::Sum;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "|";
+ }
+ break;
+ case '}':
+ {
+ m_aCurToken.eType = TRGROUP;
+ m_aCurToken.setChar(MS_RBRACE);
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "}";
+ }
+ break;
+ case '~':
+ {
+ m_aCurToken.eType = TBLANK;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::Blank;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "~";
+ }
+ break;
+ case '#':
+ {
+ if (m_aBufferString.match("##", nRealStart))
+ {
+ m_aCurToken.eType = TDPOUND;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "##";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TPOUND;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "#";
+ }
+ }
+ break;
+ case '&':
+ {
+ m_aCurToken.eType = TAND;
+ m_aCurToken.setChar(MS_AND);
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "&";
+ }
+ break;
+ case '(':
+ {
+ m_aCurToken.eType = TLPARENT;
+ m_aCurToken.setChar(MS_LPARENT);
+ m_aCurToken.nGroup = TG::LBrace;
+ m_aCurToken.nLevel = 5; //! 0 to continue expression
+ m_aCurToken.aText = "(";
+ }
+ break;
+ case ')':
+ {
+ m_aCurToken.eType = TRPARENT;
+ m_aCurToken.setChar(MS_RPARENT);
+ m_aCurToken.nGroup = TG::RBrace;
+ m_aCurToken.nLevel = 0; //! 0 to terminate expression
+ m_aCurToken.aText = ")";
+ }
+ break;
+ case '*':
+ {
+ m_aCurToken.eType = TMULTIPLY;
+ m_aCurToken.setChar(MS_MULTIPLY);
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "*";
+ }
+ break;
+ case '+':
+ {
+ if (m_aBufferString.match("+-", nRealStart))
+ {
+ m_aCurToken.eType = TPLUSMINUS;
+ m_aCurToken.setChar(MS_PLUSMINUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "+-";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TPLUS;
+ m_aCurToken.setChar(MS_PLUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "+";
+ }
+ }
+ break;
+ case '-':
+ {
+ if (m_aBufferString.match("-+", nRealStart))
+ {
+ m_aCurToken.eType = TMINUSPLUS;
+ m_aCurToken.setChar(MS_MINUSPLUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "-+";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else if (m_aBufferString.match("->", nRealStart))
+ {
+ m_aCurToken.eType = TRIGHTARROW;
+ m_aCurToken.setChar(MS_RIGHTARROW);
+ m_aCurToken.nGroup = TG::Standalone;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "->";
+
+ rnEndPos = nRealStart + 2;
+ }
+ else
+ {
+ m_aCurToken.eType = TMINUS;
+ m_aCurToken.setChar(MS_MINUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "-";
+ }
+ }
+ break;
+ case '.':
+ {
+ // Only one character? Then it can't be a number.
+ if (m_nBufferIndex < m_aBufferString.getLength() - 1)
+ {
+ // for compatibility with SO5.2
+ // texts like .34 ...56 ... h ...78..90
+ // will be treated as numbers
+ m_aCurToken.eType = TNUMBER;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+
+ sal_Int32 nTxtStart = m_nBufferIndex;
+ sal_Unicode cChar;
+ // if the equation ends with dot(.) then increment m_nBufferIndex till end of string only
+ do
+ {
+ cChar = m_aBufferString[++m_nBufferIndex];
+ } while ((cChar == '.' || rtl::isAsciiDigit(cChar))
+ && (m_nBufferIndex < m_aBufferString.getLength() - 1));
+
+ m_aCurToken.aText
+ = m_aBufferString.copy(nTxtStart, m_nBufferIndex - nTxtStart);
+ aRes.EndPos = m_nBufferIndex;
+ }
+ else
+ bHandled = false;
+ }
+ break;
+ case '/':
+ {
+ m_aCurToken.eType = TDIVIDEBY;
+ m_aCurToken.setChar(MS_SLASH);
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "/";
+ }
+ break;
+ case '=':
+ {
+ m_aCurToken.eType = TASSIGN;
+ m_aCurToken.setChar(MS_ASSIGN);
+ m_aCurToken.nGroup = TG::Relation;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "=";
+ }
+ break;
+ default:
+ bHandled = false;
+ }
+ }
+ }
+ else
+ bHandled = false;
+
+ if (!bHandled)
+ {
+ m_aCurToken.eType = TCHARACTER;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+
+ // tdf#129372: we may have to deal with surrogate pairs
+ // (see https://en.wikipedia.org/wiki/Universal_Character_Set_characters#Surrogates)
+ // in this case, we must read 2 sal_Unicode instead of 1
+ int nOffset(rtl::isSurrogate(m_aBufferString[nRealStart]) ? 2 : 1);
+ m_aCurToken.aText = m_aBufferString.copy(nRealStart, nOffset);
+
+ aRes.EndPos = nRealStart + nOffset;
+ }
+ m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
+
+ if (TEND != m_aCurToken.eType)
+ m_nBufferIndex = aRes.EndPos;
+}
+
+void SmParser5::NextTokenColor(SmTokenType dvipload)
+{
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+ ParseResult aRes;
+ sal_Int32 nRealStart;
+ bool bCont;
+
+ do
+ {
+ // skip white spaces
+ while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
+ ++m_nBufferIndex;
+ //parse, there are few options, so less strict.
+ aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
+ coContFlags, "");
+ nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
+ m_nBufferIndex = nRealStart;
+ bCont = false;
+ if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
+ {
+ // keep data needed for tokens row and col entry up to date
+ ++m_nRow;
+ m_nBufferIndex = m_nColOff = nRealStart + 1;
+ bCont = true;
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
+ {
+ //SkipComment
+ m_nBufferIndex = nRealStart + 2;
+ while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
+ ++m_nBufferIndex;
+ bCont = true;
+ }
+ }
+ } while (bCont);
+
+ // set index of current token
+ m_nTokenIndex = m_nBufferIndex;
+ sal_uInt32 nCol = nRealStart - m_nColOff;
+
+ if (nRealStart >= nBufLen)
+ m_aCurToken.eType = TEND;
+ else if (aRes.TokenType & KParseType::IDENTNAME)
+ {
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ OUString aName(m_aBufferString.copy(nRealStart, n));
+ switch (dvipload)
+ {
+ case TCOLOR:
+ m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName);
+ break;
+ case TDVIPSNAMESCOL:
+ m_aCurToken = starmathdatabase::Identify_ColorName_DVIPSNAMES(aName);
+ break;
+ default:
+ m_aCurToken = starmathdatabase::Identify_ColorName_Parser(aName);
+ break;
+ }
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (m_aBufferString[nRealStart] == '#' && !m_aBufferString.match("##", nRealStart))
+ {
+ m_aCurToken.eType = THEX;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::Color;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "hex";
+ }
+ }
+ else
+ m_aCurToken.eType = TNONE;
+
+ m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
+ if (TEND != m_aCurToken.eType)
+ m_nBufferIndex = aRes.EndPos;
+}
+
+void SmParser5::NextTokenFontSize()
+{
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+ ParseResult aRes;
+ sal_Int32 nRealStart;
+ bool bCont;
+ bool hex = false;
+
+ do
+ {
+ // skip white spaces
+ while (UnicodeType::SPACE_SEPARATOR == m_pSysCC->getType(m_aBufferString, m_nBufferIndex))
+ ++m_nBufferIndex;
+ //hexadecimal parser
+ aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coNum16StartFlags, ".",
+ coNum16ContFlags, ".,");
+ if (aRes.TokenType == 0)
+ {
+ // Try again with the default token parsing.
+ aRes = m_pSysCC->parseAnyToken(m_aBufferString, m_nBufferIndex, coStartFlags, "",
+ coContFlags, "");
+ }
+ else
+ hex = true;
+ nRealStart = m_nBufferIndex + aRes.LeadingWhiteSpace;
+ m_nBufferIndex = nRealStart;
+ bCont = false;
+ if (aRes.TokenType == 0 && nRealStart < nBufLen && '\n' == m_aBufferString[nRealStart])
+ {
+ // keep data needed for tokens row and col entry up to date
+ ++m_nRow;
+ m_nBufferIndex = m_nColOff = nRealStart + 1;
+ bCont = true;
+ }
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (nRealStart + 2 <= nBufLen && m_aBufferString.match("%%", nRealStart))
+ {
+ //SkipComment
+ m_nBufferIndex = nRealStart + 2;
+ while (m_nBufferIndex < nBufLen && '\n' != m_aBufferString[m_nBufferIndex])
+ ++m_nBufferIndex;
+ bCont = true;
+ }
+ }
+ } while (bCont);
+
+ // set index of current token
+ m_nTokenIndex = m_nBufferIndex;
+ sal_uInt32 nCol = nRealStart - m_nColOff;
+
+ if (nRealStart >= nBufLen)
+ m_aCurToken.eType = TEND;
+ else if (aRes.TokenType & KParseType::ONE_SINGLE_CHAR)
+ {
+ if (aRes.EndPos - nRealStart == 1)
+ {
+ switch (m_aBufferString[nRealStart])
+ {
+ case '*':
+ m_aCurToken.eType = TMULTIPLY;
+ m_aCurToken.setChar(MS_MULTIPLY);
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "*";
+ break;
+ case '+':
+ m_aCurToken.eType = TPLUS;
+ m_aCurToken.setChar(MS_PLUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "+";
+ break;
+ case '-':
+ m_aCurToken.eType = TMINUS;
+ m_aCurToken.setChar(MS_MINUS);
+ m_aCurToken.nGroup = TG::UnOper | TG::Sum;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = "-";
+ break;
+ case '/':
+ m_aCurToken.eType = TDIVIDEBY;
+ m_aCurToken.setChar(MS_SLASH);
+ m_aCurToken.nGroup = TG::Product;
+ m_aCurToken.nLevel = 0;
+ m_aCurToken.aText = "/";
+ break;
+ default:
+ m_aCurToken.eType = TNONE;
+ break;
+ }
+ }
+ else
+ m_aCurToken.eType = TNONE;
+ }
+ else if (hex)
+ {
+ assert(aRes.EndPos > 0);
+ sal_Int32 n = aRes.EndPos - nRealStart;
+ assert(n >= 0);
+ m_aCurToken.eType = THEX;
+ m_aCurToken.cMathChar = u"";
+ m_aCurToken.nGroup = TG::NONE;
+ m_aCurToken.nLevel = 5;
+ m_aCurToken.aText = m_aBufferString.copy(nRealStart, n);
+ }
+ else
+ m_aCurToken.eType = TNONE;
+
+ m_aCurESelection = ESelection(m_nRow, nCol, m_nRow, nCol + m_aCurToken.aText.getLength());
+ if (TEND != m_aCurToken.eType)
+ m_nBufferIndex = aRes.EndPos;
+}
+
+namespace
+{
+SmNodeArray buildNodeArray(std::vector<std::unique_ptr<SmNode>>& rSubNodes)
+{
+ SmNodeArray aSubArray(rSubNodes.size());
+ for (size_t i = 0; i < rSubNodes.size(); ++i)
+ aSubArray[i] = rSubNodes[i].release();
+ return aSubArray;
+}
+} //end namespace
+
+// grammar
+/*************************************************************************************************/
+
+std::unique_ptr<SmTableNode> SmParser5::DoTable()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::vector<std::unique_ptr<SmNode>> aLineArray;
+ aLineArray.push_back(DoLine());
+ while (m_aCurToken.eType == TNEWLINE)
+ {
+ NextToken();
+ aLineArray.push_back(DoLine());
+ }
+ assert(m_aCurToken.eType == TEND);
+ std::unique_ptr<SmTableNode> xSNode(new SmTableNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ xSNode->SetSubNodes(buildNodeArray(aLineArray));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoAlign(bool bUseExtraSpaces)
+// parse alignment info (if any), then go on with rest of expression
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::unique_ptr<SmStructureNode> xSNode;
+
+ if (TokenInGroup(TG::Align))
+ {
+ xSNode.reset(new SmAlignNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+
+ NextToken();
+
+ // allow for just one align statement in 5.0
+ if (TokenInGroup(TG::Align))
+ return DoError(SmParseError::DoubleAlign);
+ }
+
+ auto pNode = DoExpression(bUseExtraSpaces);
+
+ if (xSNode)
+ {
+ xSNode->SetSubNode(0, pNode.release());
+ return xSNode;
+ }
+ return pNode;
+}
+
+// Postcondition: m_aCurToken.eType == TEND || m_aCurToken.eType == TNEWLINE
+std::unique_ptr<SmNode> SmParser5::DoLine()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::vector<std::unique_ptr<SmNode>> ExpressionArray;
+
+ // start with single expression that may have an alignment statement
+ // (and go on with expressions that must not have alignment
+ // statements in 'while' loop below. See also 'Expression()'.)
+ if (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
+ ExpressionArray.push_back(DoAlign());
+
+ while (m_aCurToken.eType != TEND && m_aCurToken.eType != TNEWLINE)
+ ExpressionArray.push_back(DoExpression());
+
+ //If there's no expression, add an empty one.
+ //this is to avoid a formula tree without any caret
+ //positions, in visual formula editor.
+ if (ExpressionArray.empty())
+ {
+ SmToken aTok;
+ aTok.eType = TNEWLINE;
+ ExpressionArray.emplace_back(std::unique_ptr<SmNode>(new SmExpressionNode(aTok)));
+ }
+
+ auto xSNode = std::make_unique<SmLineNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+ xSNode->SetSubNodes(buildNodeArray(ExpressionArray));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoExpression(bool bUseExtraSpaces)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::vector<std::unique_ptr<SmNode>> RelationArray;
+ RelationArray.push_back(DoRelation());
+ while (m_aCurToken.nLevel >= 4)
+ RelationArray.push_back(DoRelation());
+
+ if (RelationArray.size() > 1)
+ {
+ std::unique_ptr<SmExpressionNode> xSNode(new SmExpressionNode(m_aCurToken));
+ xSNode->SetSubNodes(buildNodeArray(RelationArray));
+ xSNode->SetUseExtraSpaces(bUseExtraSpaces);
+ return xSNode;
+ }
+ else
+ {
+ // This expression has only one node so just push this node.
+ return std::move(RelationArray[0]);
+ }
+}
+
+std::unique_ptr<SmNode> SmParser5::DoRelation()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ int nDepthLimit = m_nParseDepth;
+
+ auto xFirst = DoSum();
+ while (TokenInGroup(TG::Relation))
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ auto xSecond = DoOpSubSup();
+ auto xThird = DoSum();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
+ xFirst = std::move(xSNode);
+
+ ++m_nParseDepth;
+ DepthProtect bDepthGuard(m_nParseDepth);
+ }
+
+ m_nParseDepth = nDepthLimit;
+
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoSum()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ int nDepthLimit = m_nParseDepth;
+
+ auto xFirst = DoProduct();
+ while (TokenInGroup(TG::Sum))
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmBinHorNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ auto xSecond = DoOpSubSup();
+ auto xThird = DoProduct();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond), std::move(xThird));
+ xFirst = std::move(xSNode);
+
+ ++m_nParseDepth;
+ DepthProtect bDepthGuard(m_nParseDepth);
+ }
+
+ m_nParseDepth = nDepthLimit;
+
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoProduct()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ auto xFirst = DoPower();
+
+ int nDepthLimit = 0;
+
+ while (TokenInGroup(TG::Product))
+ {
+ //this linear loop builds a recursive structure, if it gets
+ //too deep then later processing, e.g. releasing the tree,
+ //can exhaust stack
+ if (m_nParseDepth + nDepthLimit > DEPTH_LIMIT)
+ throw std::range_error("parser depth limit");
+
+ std::unique_ptr<SmStructureNode> xSNode;
+ std::unique_ptr<SmNode> xOper;
+
+ SmTokenType eType = m_aCurToken.eType;
+ switch (eType)
+ {
+ case TOVER:
+ xSNode.reset(new SmBinVerNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ xOper.reset(new SmRectangleNode(m_aCurToken));
+ xOper->SetSelection(m_aCurESelection);
+ NextToken();
+ break;
+
+ case TBOPER:
+ xSNode.reset(new SmBinHorNode(m_aCurToken));
+
+ NextToken();
+
+ //Let the glyph node know it's a binary operation
+ m_aCurToken.eType = TBOPER;
+ m_aCurToken.nGroup = TG::Product;
+ xOper = DoGlyphSpecial();
+ break;
+
+ case TOVERBRACE:
+ case TUNDERBRACE:
+ xSNode.reset(new SmVerticalBraceNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ xOper.reset(new SmMathSymbolNode(m_aCurToken));
+ xOper->SetSelection(m_aCurESelection);
+
+ NextToken();
+ break;
+
+ case TWIDEBACKSLASH:
+ case TWIDESLASH:
+ {
+ SmBinDiagonalNode* pSTmp = new SmBinDiagonalNode(m_aCurToken);
+ pSTmp->SetAscending(eType == TWIDESLASH);
+ xSNode.reset(pSTmp);
+
+ xOper.reset(new SmPolyLineNode(m_aCurToken));
+ xOper->SetSelection(m_aCurESelection);
+ NextToken();
+
+ break;
+ }
+
+ default:
+ xSNode.reset(new SmBinHorNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+
+ xOper = DoOpSubSup();
+ }
+
+ auto xArg = DoPower();
+ xSNode->SetSubNodesBinMo(std::move(xFirst), std::move(xOper), std::move(xArg));
+ xFirst = std::move(xSNode);
+ ++nDepthLimit;
+ }
+ return xFirst;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoSubSup(TG nActiveGroup, std::unique_ptr<SmNode> xGivenNode)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(nActiveGroup == TG::Power || nActiveGroup == TG::Limit);
+ assert(m_aCurToken.nGroup == nActiveGroup);
+
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken));
+ pNode->SetSelection(m_aCurESelection);
+ //! Of course 'm_aCurToken' is just the first sub-/supscript token.
+ //! It should be of no further interest. The positions of the
+ //! sub-/supscripts will be identified by the corresponding subnodes
+ //! index in the 'aSubNodes' array (enum value from 'SmSubSup').
+
+ pNode->SetUseLimits(nActiveGroup == TG::Limit);
+
+ // initialize subnodes array
+ std::vector<std::unique_ptr<SmNode>> aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+ aSubNodes[0] = std::move(xGivenNode);
+
+ // process all sub-/supscripts
+ int nIndex = 0;
+ while (TokenInGroup(nActiveGroup))
+ {
+ SmTokenType eType(m_aCurToken.eType);
+
+ switch (eType)
+ {
+ case TRSUB:
+ nIndex = static_cast<int>(RSUB);
+ break;
+ case TRSUP:
+ nIndex = static_cast<int>(RSUP);
+ break;
+ case TFROM:
+ case TCSUB:
+ nIndex = static_cast<int>(CSUB);
+ break;
+ case TTO:
+ case TCSUP:
+ nIndex = static_cast<int>(CSUP);
+ break;
+ case TLSUB:
+ nIndex = static_cast<int>(LSUB);
+ break;
+ case TLSUP:
+ nIndex = static_cast<int>(LSUP);
+ break;
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+ nIndex++;
+ assert(1 <= nIndex && nIndex <= SUBSUP_NUM_ENTRIES);
+
+ std::unique_ptr<SmNode> xENode;
+ if (aSubNodes[nIndex]) // if already occupied at earlier iteration
+ {
+ // forget the earlier one, remember an error instead
+ aSubNodes[nIndex].reset();
+ xENode = DoError(SmParseError::DoubleSubsupscript); // this also skips current token.
+ }
+ else
+ {
+ // skip sub-/supscript token
+ NextToken();
+ }
+
+ // get sub-/supscript node
+ // (even when we saw a double-sub/supscript error in the above
+ // in order to minimize mess and continue parsing.)
+ std::unique_ptr<SmNode> xSNode;
+ if (eType == TFROM || eType == TTO)
+ {
+ // parse limits in old 4.0 and 5.0 style
+ xSNode = DoRelation();
+ }
+ else
+ xSNode = DoTerm(true);
+
+ aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode);
+ }
+
+ pNode->SetSubNodes(buildNodeArray(aSubNodes));
+ return pNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoSubSupEvaluate(std::unique_ptr<SmNode> xGivenNode)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::unique_ptr<SmSubSupNode> pNode(new SmSubSupNode(m_aCurToken));
+ pNode->SetSelection(m_aCurESelection);
+ pNode->SetUseLimits(true);
+
+ // initialize subnodes array
+ std::vector<std::unique_ptr<SmNode>> aSubNodes(1 + SUBSUP_NUM_ENTRIES);
+ aSubNodes[0] = std::move(xGivenNode);
+
+ // process all sub-/supscripts
+ int nIndex = 0;
+ while (TokenInGroup(TG::Limit))
+ {
+ SmTokenType eType(m_aCurToken.eType);
+
+ switch (eType)
+ {
+ case TFROM:
+ nIndex = static_cast<int>(RSUB);
+ break;
+ case TTO:
+ nIndex = static_cast<int>(RSUP);
+ break;
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+ nIndex++;
+ assert(1 <= nIndex && nIndex <= SUBSUP_NUM_ENTRIES);
+
+ std::unique_ptr<SmNode> xENode;
+ if (aSubNodes[nIndex]) // if already occupied at earlier iteration
+ {
+ // forget the earlier one, remember an error instead
+ aSubNodes[nIndex].reset();
+ xENode = DoError(SmParseError::DoubleSubsupscript); // this also skips current token.
+ }
+ else
+ NextToken(); // skip sub-/supscript token
+
+ // get sub-/supscript node
+ std::unique_ptr<SmNode> xSNode;
+ xSNode = DoTerm(true);
+
+ aSubNodes[nIndex] = std::move(xENode ? xENode : xSNode);
+ }
+
+ pNode->SetSubNodes(buildNodeArray(aSubNodes));
+ return pNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoOpSubSup()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ // get operator symbol
+ auto xNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ xNode->SetSelection(m_aCurESelection);
+ // skip operator token
+ NextToken();
+ // get sub- supscripts if any
+ if (m_aCurToken.nGroup == TG::Power)
+ return DoSubSup(TG::Power, std::move(xNode));
+ return xNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoPower()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ // get body for sub- supscripts on top of stack
+ std::unique_ptr<SmNode> xNode(DoTerm(false));
+
+ if (m_aCurToken.nGroup == TG::Power)
+ return DoSubSup(TG::Power, std::move(xNode));
+ return xNode;
+}
+
+std::unique_ptr<SmBlankNode> SmParser5::DoBlank()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(TokenInGroup(TG::Blank));
+ std::unique_ptr<SmBlankNode> pBlankNode(new SmBlankNode(m_aCurToken));
+ pBlankNode->SetSelection(m_aCurESelection);
+
+ do
+ {
+ pBlankNode->IncreaseBy(m_aCurToken);
+ NextToken();
+ } while (TokenInGroup(TG::Blank));
+
+ // Ignore trailing spaces, if corresponding option is set
+ if (m_aCurToken.eType == TNEWLINE
+ || (m_aCurToken.eType == TEND && !utl::ConfigManager::IsFuzzing()
+ && SM_MOD()->GetConfig()->IsIgnoreSpacesRight()))
+ {
+ pBlankNode->Clear();
+ }
+ return pBlankNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoTerm(bool bGroupNumberIdent)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ switch (m_aCurToken.eType)
+ {
+ case TESCAPE:
+ return DoEscape();
+
+ case TNOSPACE:
+ case TLGROUP:
+ {
+ bool bNoSpace = m_aCurToken.eType == TNOSPACE;
+ if (bNoSpace)
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoTerm(false); // nospace is no longer concerned
+
+ NextToken();
+
+ // allow for empty group
+ if (m_aCurToken.eType == TRGROUP)
+ {
+ std::unique_ptr<SmStructureNode> xSNode(new SmExpressionNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ xSNode->SetSubNodes(nullptr, nullptr);
+
+ NextToken();
+ return std::unique_ptr<SmNode>(xSNode.release());
+ }
+
+ auto pNode = DoAlign(!bNoSpace);
+ if (m_aCurToken.eType == TRGROUP)
+ {
+ NextToken();
+ return pNode;
+ }
+ auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+ std::unique_ptr<SmNode> xError(DoError(SmParseError::RgroupExpected));
+ xSNode->SetSubNodes(std::move(pNode), std::move(xError));
+ return std::unique_ptr<SmNode>(xSNode.release());
+ }
+
+ case TLEFT:
+ return DoBrace();
+ case TEVALUATE:
+ return DoEvaluate();
+
+ case TBLANK:
+ case TSBLANK:
+ return DoBlank();
+
+ case TTEXT:
+ {
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_TEXT);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ case TCHARACTER:
+ {
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_VARIABLE);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ case TIDENT:
+ case TNUMBER:
+ {
+ auto pTextNode = std::make_unique<SmTextNode>(
+ m_aCurToken, m_aCurToken.eType == TNUMBER ? FNT_NUMBER : FNT_VARIABLE);
+ pTextNode->SetSelection(m_aCurESelection);
+ if (!bGroupNumberIdent)
+ {
+ NextToken();
+ return std::unique_ptr<SmNode>(pTextNode.release());
+ }
+ std::vector<std::unique_ptr<SmNode>> aNodes;
+ // Some people want to be able to write "x_2n" for "x_{2n}"
+ // although e.g. LaTeX or AsciiMath interpret that as "x_2 n".
+ // The tokenizer skips whitespaces so we need some additional
+ // work to distinguish from "x_2 n".
+ // See https://bz.apache.org/ooo/show_bug.cgi?id=11752 and
+ // https://bugs.libreoffice.org/show_bug.cgi?id=55853
+ sal_Int32 nBufLen = m_aBufferString.getLength();
+
+ // We need to be careful to call NextToken() only after having
+ // tested for a whitespace separator (otherwise it will be
+ // skipped!)
+ bool moveToNextToken = true;
+ while (m_nBufferIndex < nBufLen
+ && m_pSysCC->getType(m_aBufferString, m_nBufferIndex)
+ != UnicodeType::SPACE_SEPARATOR)
+ {
+ NextToken();
+ if (m_aCurToken.eType != TNUMBER && m_aCurToken.eType != TIDENT)
+ {
+ // Neither a number nor an identifier. We just moved to
+ // the next token, so no need to do that again.
+ moveToNextToken = false;
+ break;
+ }
+ aNodes.emplace_back(std::unique_ptr<SmNode>(new SmTextNode(
+ m_aCurToken, m_aCurToken.eType == TNUMBER ? FNT_NUMBER : FNT_VARIABLE)));
+ }
+ if (moveToNextToken)
+ NextToken();
+ if (aNodes.empty())
+ return std::unique_ptr<SmNode>(pTextNode.release());
+ // We have several concatenated identifiers and numbers.
+ // Let's group them into one SmExpressionNode.
+ aNodes.insert(aNodes.begin(), std::move(pTextNode));
+ std::unique_ptr<SmExpressionNode> xNode(new SmExpressionNode(SmToken()));
+ xNode->SetSubNodes(buildNodeArray(aNodes));
+ return std::unique_ptr<SmNode>(xNode.release());
+ }
+ case TLEFTARROW:
+ case TRIGHTARROW:
+ case TUPARROW:
+ case TDOWNARROW:
+ case TCIRC:
+ case TDRARROW:
+ case TDLARROW:
+ case TDLRARROW:
+ case TEXISTS:
+ case TNOTEXISTS:
+ case TFORALL:
+ case TPARTIAL:
+ case TNABLA:
+ case TLAPLACE:
+ case TFOURIER:
+ case TTOWARD:
+ case TDOTSAXIS:
+ case TDOTSDIAG:
+ case TDOTSDOWN:
+ case TDOTSLOW:
+ case TDOTSUP:
+ case TDOTSVERT:
+ {
+ auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TSETN:
+ case TSETZ:
+ case TSETQ:
+ case TSETR:
+ case TSETC:
+ case THBAR:
+ case TLAMBDABAR:
+ case TBACKEPSILON:
+ case TALEPH:
+ case TIM:
+ case TRE:
+ case TWP:
+ case TEMPTYSET:
+ case TINFINITY:
+ {
+ auto pNode = std::make_unique<SmMathIdentifierNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TPLACE:
+ {
+ auto pNode = std::make_unique<SmPlaceNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+
+ case TSPECIAL:
+ return DoSpecial();
+
+ case TBINOM:
+ return DoBinom();
+
+ case TFRAC:
+ return DoFrac();
+
+ case TSTACK:
+ return DoStack();
+
+ case TMATRIX:
+ return DoMatrix();
+
+ case THEX:
+ NextTokenFontSize();
+ if (m_aCurToken.eType == THEX)
+ {
+ auto pTextNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_NUMBER);
+ pTextNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return pTextNode;
+ }
+ else
+ return DoError(SmParseError::NumberExpected);
+ default:
+ if (TokenInGroup(TG::LBrace))
+ return DoBrace();
+ if (TokenInGroup(TG::Oper))
+ return DoOperator();
+ if (TokenInGroup(TG::UnOper))
+ return DoUnOper();
+ if (TokenInGroup(TG::Attribute) || TokenInGroup(TG::FontAttr))
+ {
+ std::stack<std::unique_ptr<SmStructureNode>,
+ std::vector<std::unique_ptr<SmStructureNode>>>
+ aStack;
+ bool bIsAttr;
+ for (;;)
+ {
+ bIsAttr = TokenInGroup(TG::Attribute);
+ if (!bIsAttr && !TokenInGroup(TG::FontAttr))
+ break;
+ aStack.push(bIsAttr ? DoAttribute() : DoFontAttribute());
+ }
+
+ auto xFirstNode = DoPower();
+ while (!aStack.empty())
+ {
+ std::unique_ptr<SmStructureNode> xNode = std::move(aStack.top());
+ aStack.pop();
+ xNode->SetSubNodes(nullptr, std::move(xFirstNode));
+ xFirstNode = std::move(xNode);
+ }
+ return xFirstNode;
+ }
+ if (TokenInGroup(TG::Function))
+ return DoFunction();
+ return DoError(SmParseError::UnexpectedChar);
+ }
+}
+
+std::unique_ptr<SmNode> SmParser5::DoEscape()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ NextToken();
+
+ switch (m_aCurToken.eType)
+ {
+ case TLPARENT:
+ case TRPARENT:
+ case TLBRACKET:
+ case TRBRACKET:
+ case TLDBRACKET:
+ case TRDBRACKET:
+ case TLBRACE:
+ case TLGROUP:
+ case TRBRACE:
+ case TRGROUP:
+ case TLANGLE:
+ case TRANGLE:
+ case TLCEIL:
+ case TRCEIL:
+ case TLFLOOR:
+ case TRFLOOR:
+ case TLLINE:
+ case TRLINE:
+ case TLDLINE:
+ case TRDLINE:
+ {
+ auto pNode = std::make_unique<SmMathSymbolNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return std::unique_ptr<SmNode>(pNode.release());
+ }
+ default:
+ return DoError(SmParseError::UnexpectedToken);
+ }
+}
+
+std::unique_ptr<SmOperNode> SmParser5::DoOperator()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(TokenInGroup(TG::Oper));
+
+ auto xSNode = std::make_unique<SmOperNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+
+ // get operator
+ auto xOperator = DoOper();
+
+ if (m_aCurToken.nGroup == TG::Limit || m_aCurToken.nGroup == TG::Power)
+ xOperator = DoSubSup(m_aCurToken.nGroup, std::move(xOperator));
+
+ // get argument
+ auto xArg = DoPower();
+
+ xSNode->SetSubNodes(std::move(xOperator), std::move(xArg));
+ return xSNode;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoOper()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ SmTokenType eType(m_aCurToken.eType);
+ std::unique_ptr<SmNode> pNode;
+
+ switch (eType)
+ {
+ case TSUM:
+ case TPROD:
+ case TCOPROD:
+ case TINT:
+ case TINTD:
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ pNode.reset(new SmMathSymbolNode(m_aCurToken));
+ pNode->SetSelection(m_aCurESelection);
+ break;
+
+ case TLIM:
+ case TLIMSUP:
+ case TLIMINF:
+ m_aCurToken.aText
+ = eType == TLIMSUP ? u"lim sup" : eType == TLIMINF ? u"lim inf" : u"lim";
+ pNode.reset(new SmTextNode(m_aCurToken, FNT_TEXT));
+ pNode->SetSelection(m_aCurESelection);
+ break;
+
+ case TOPER:
+ NextToken();
+ OSL_ENSURE(m_aCurToken.eType == TSPECIAL, "Sm: wrong token");
+ m_aCurToken.eType = TOPER;
+ pNode.reset(new SmGlyphSpecialNode(m_aCurToken));
+ pNode->SetSelection(m_aCurESelection);
+ break;
+
+ default:
+ assert(false && "unknown case");
+ }
+
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoUnOper()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(TokenInGroup(TG::UnOper));
+
+ SmToken aNodeToken = m_aCurToken;
+ ESelection aESelection = m_aCurESelection;
+ SmTokenType eType = m_aCurToken.eType;
+ bool bIsPostfix = eType == TFACT;
+
+ std::unique_ptr<SmStructureNode> xSNode;
+ std::unique_ptr<SmNode> xOper;
+ std::unique_ptr<SmNode> xExtra;
+ std::unique_ptr<SmNode> xArg;
+
+ switch (eType)
+ {
+ case TABS:
+ case TSQRT:
+ NextToken();
+ break;
+
+ case TNROOT:
+ NextToken();
+ xExtra = DoPower();
+ break;
+
+ case TUOPER:
+ NextToken();
+ //Let the glyph know what it is...
+ m_aCurToken.eType = TUOPER;
+ m_aCurToken.nGroup = TG::UnOper;
+ xOper = DoGlyphSpecial();
+ break;
+
+ case TPLUS:
+ case TMINUS:
+ case TPLUSMINUS:
+ case TMINUSPLUS:
+ case TNEG:
+ case TFACT:
+ xOper = DoOpSubSup();
+ break;
+
+ default:
+ assert(false);
+ }
+
+ // get argument
+ xArg = DoPower();
+
+ if (eType == TABS)
+ {
+ xSNode.reset(new SmBraceNode(aNodeToken));
+ xSNode->SetSelection(aESelection);
+ xSNode->SetScaleMode(SmScaleMode::Height);
+
+ // build nodes for left & right lines
+ // (text, group, level of the used token are of no interest here)
+ // we'll use row & column of the keyword for abs
+ aNodeToken.eType = TABS;
+
+ aNodeToken.setChar(MS_VERTLINE);
+ std::unique_ptr<SmNode> xLeft(new SmMathSymbolNode(aNodeToken));
+ xLeft->SetSelection(aESelection);
+ std::unique_ptr<SmNode> xRight(new SmMathSymbolNode(aNodeToken));
+ xRight->SetSelection(aESelection);
+
+ xSNode->SetSubNodes(std::move(xLeft), std::move(xArg), std::move(xRight));
+ }
+ else if (eType == TSQRT || eType == TNROOT)
+ {
+ xSNode.reset(new SmRootNode(aNodeToken));
+ xSNode->SetSelection(aESelection);
+ xOper.reset(new SmRootSymbolNode(aNodeToken));
+ xOper->SetSelection(aESelection);
+ xSNode->SetSubNodes(std::move(xExtra), std::move(xOper), std::move(xArg));
+ }
+ else
+ {
+ xSNode.reset(new SmUnHorNode(aNodeToken));
+ xSNode->SetSelection(aESelection);
+ if (bIsPostfix)
+ xSNode->SetSubNodes(std::move(xArg), std::move(xOper));
+ else
+ {
+ // prefix operator
+ xSNode->SetSubNodes(std::move(xOper), std::move(xArg));
+ }
+ }
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoAttribute()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(TokenInGroup(TG::Attribute));
+
+ auto xSNode = std::make_unique<SmAttributeNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+ std::unique_ptr<SmNode> xAttr;
+ SmScaleMode eScaleMode = SmScaleMode::None;
+
+ // get appropriate node for the attribute itself
+ switch (m_aCurToken.eType)
+ {
+ case TUNDERLINE:
+ case TOVERLINE:
+ case TOVERSTRIKE:
+ xAttr.reset(new SmRectangleNode(m_aCurToken));
+ xAttr->SetSelection(m_aCurESelection);
+ eScaleMode = SmScaleMode::Width;
+ break;
+
+ case TWIDEVEC:
+ case TWIDEHARPOON:
+ case TWIDEHAT:
+ case TWIDETILDE:
+ xAttr.reset(new SmMathSymbolNode(m_aCurToken));
+ xAttr->SetSelection(m_aCurESelection);
+ eScaleMode = SmScaleMode::Width;
+ break;
+
+ default:
+ xAttr.reset(new SmMathSymbolNode(m_aCurToken));
+ xAttr->SetSelection(m_aCurESelection);
+ }
+
+ NextToken();
+
+ xSNode->SetSubNodes(std::move(xAttr), nullptr); // the body will be filled later
+ xSNode->SetScaleMode(eScaleMode);
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoFontAttribute()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(TokenInGroup(TG::FontAttr));
+
+ switch (m_aCurToken.eType)
+ {
+ case TITALIC:
+ case TNITALIC:
+ case TBOLD:
+ case TNBOLD:
+ case TPHANTOM:
+ {
+ auto pNode = std::make_unique<SmFontNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return pNode;
+ }
+
+ case TSIZE:
+ return DoFontSize();
+
+ case TFONT:
+ return DoFont();
+
+ case TCOLOR:
+ return DoColor();
+
+ default:
+ assert(false);
+ return {};
+ }
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoColor()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(m_aCurToken.eType == TCOLOR);
+ sal_Int32 nBufferIndex = m_nBufferIndex;
+ NextTokenColor(TCOLOR);
+ SmToken aToken;
+ ESelection aESelection;
+
+ if (m_aCurToken.eType == TDVIPSNAMESCOL)
+ NextTokenColor(TDVIPSNAMESCOL);
+ if (m_aCurToken.eType == TERROR)
+ return DoError(SmParseError::ColorExpected);
+ if (TokenInGroup(TG::Color))
+ {
+ aToken = m_aCurToken;
+ aESelection = m_aCurESelection;
+ if (m_aCurToken.eType == TRGB) //loads r, g and b
+ {
+ sal_uInt32 nr, ng, nb, nc;
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ nr = m_aCurToken.aText.toUInt32();
+ if (nr > 255)
+ return DoError(SmParseError::ColorExpected);
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ ng = m_aCurToken.aText.toUInt32();
+ if (ng > 255)
+ return DoError(SmParseError::ColorExpected);
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ nb = m_aCurToken.aText.toUInt32();
+ if (nb > 255)
+ return DoError(SmParseError::ColorExpected);
+ nc = nb | ng << 8 | nr << 16 | sal_uInt32(0) << 24;
+ aToken.cMathChar = OUString::number(nc, 16);
+ }
+ else if (m_aCurToken.eType == TRGBA) //loads r, g and b
+ {
+ sal_uInt32 nr, na, ng, nb, nc;
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ nr = m_aCurToken.aText.toUInt32();
+ if (nr > 255)
+ return DoError(SmParseError::ColorExpected);
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ ng = m_aCurToken.aText.toUInt32();
+ if (ng > 255)
+ return DoError(SmParseError::ColorExpected);
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ nb = m_aCurToken.aText.toUInt32();
+ if (nb > 255)
+ return DoError(SmParseError::ColorExpected);
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ na = m_aCurToken.aText.toUInt32();
+ if (na > 255)
+ return DoError(SmParseError::ColorExpected);
+ nc = nb | ng << 8 | nr << 16 | na << 24;
+ aToken.cMathChar = OUString::number(nc, 16);
+ }
+ else if (m_aCurToken.eType == THEX) //loads hex code
+ {
+ sal_uInt32 nc;
+ NextTokenFontSize();
+ if (lcl_IsNotWholeNumber16(m_aCurToken.aText))
+ return DoError(SmParseError::ColorExpected);
+ nc = m_aCurToken.aText.toUInt32(16);
+ aToken.cMathChar = OUString::number(nc, 16);
+ }
+ aToken.aText = m_aBufferString.subView(nBufferIndex, m_nBufferIndex - nBufferIndex);
+ NextToken();
+ }
+ else
+ return DoError(SmParseError::ColorExpected);
+
+ std::unique_ptr<SmStructureNode> xNode;
+ xNode.reset(new SmFontNode(aToken));
+ xNode->SetSelection(aESelection);
+ return xNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoFont()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(m_aCurToken.eType == TFONT);
+
+ std::unique_ptr<SmStructureNode> xNode;
+ // last font rules, get that one
+ SmToken aToken;
+ ESelection aESelection = m_aCurESelection;
+ do
+ {
+ NextToken();
+
+ if (TokenInGroup(TG::Font))
+ {
+ aToken = m_aCurToken;
+ NextToken();
+ }
+ else
+ {
+ return DoError(SmParseError::FontExpected);
+ }
+ } while (m_aCurToken.eType == TFONT);
+
+ xNode.reset(new SmFontNode(aToken));
+ xNode->SetSelection(aESelection);
+ return xNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoFontSize()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+ std::unique_ptr<SmFontNode> pFontNode(new SmFontNode(m_aCurToken));
+ pFontNode->SetSelection(m_aCurESelection);
+ NextTokenFontSize();
+ FontSizeType Type;
+
+ switch (m_aCurToken.eType)
+ {
+ case THEX:
+ Type = FontSizeType::ABSOLUT;
+ break;
+ case TPLUS:
+ Type = FontSizeType::PLUS;
+ break;
+ case TMINUS:
+ Type = FontSizeType::MINUS;
+ break;
+ case TMULTIPLY:
+ Type = FontSizeType::MULTIPLY;
+ break;
+ case TDIVIDEBY:
+ Type = FontSizeType::DIVIDE;
+ break;
+
+ default:
+ return DoError(SmParseError::SizeExpected);
+ }
+
+ if (Type != FontSizeType::ABSOLUT)
+ {
+ NextTokenFontSize();
+ if (m_aCurToken.eType != THEX)
+ return DoError(SmParseError::SizeExpected);
+ }
+
+ // get number argument
+ Fraction aValue(1);
+ if (lcl_IsNumber(m_aCurToken.aText))
+ {
+ aValue = m_aCurToken.aText.toDouble();
+ //!! Reduce values in order to avoid numerical errors
+ if (aValue.GetDenominator() > 1000)
+ {
+ tools::Long nNum = aValue.GetNumerator();
+ tools::Long nDenom = aValue.GetDenominator();
+ while (nDenom > 1000) //remove big denominator
+ {
+ nNum /= 10;
+ nDenom /= 10;
+ }
+ aValue = Fraction(nNum, nDenom);
+ }
+ }
+ else
+ return DoError(SmParseError::SizeExpected);
+
+ pFontNode->SetSizeParameter(aValue, Type);
+ NextToken();
+ return pFontNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoBrace()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ assert(m_aCurToken.eType == TLEFT || TokenInGroup(TG::LBrace));
+
+ std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ std::unique_ptr<SmNode> pBody, pLeft, pRight;
+ SmScaleMode eScaleMode = SmScaleMode::None;
+ SmParseError eError = SmParseError::None;
+
+ if (m_aCurToken.eType == TLEFT)
+ {
+ NextToken();
+
+ eScaleMode = SmScaleMode::Height;
+
+ // check for left bracket
+ if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
+ {
+ pLeft.reset(new SmMathSymbolNode(m_aCurToken));
+ pLeft->SetSelection(m_aCurESelection);
+
+ NextToken();
+ pBody = DoBracebody(true);
+
+ if (m_aCurToken.eType == TRIGHT)
+ {
+ NextToken();
+
+ // check for right bracket
+ if (TokenInGroup(TG::LBrace) || TokenInGroup(TG::RBrace))
+ {
+ pRight.reset(new SmMathSymbolNode(m_aCurToken));
+ pRight->SetSelection(m_aCurESelection);
+ NextToken();
+ }
+ else
+ eError = SmParseError::RbraceExpected;
+ }
+ else
+ eError = SmParseError::RightExpected;
+ }
+ else
+ eError = SmParseError::LbraceExpected;
+ }
+ else
+ {
+ assert(TokenInGroup(TG::LBrace));
+
+ pLeft.reset(new SmMathSymbolNode(m_aCurToken));
+ pLeft->SetSelection(m_aCurESelection);
+
+ NextToken();
+ pBody = DoBracebody(false);
+
+ SmTokenType eExpectedType = TUNKNOWN;
+ switch (pLeft->GetToken().eType)
+ {
+ case TLPARENT:
+ eExpectedType = TRPARENT;
+ break;
+ case TLBRACKET:
+ eExpectedType = TRBRACKET;
+ break;
+ case TLBRACE:
+ eExpectedType = TRBRACE;
+ break;
+ case TLDBRACKET:
+ eExpectedType = TRDBRACKET;
+ break;
+ case TLLINE:
+ eExpectedType = TRLINE;
+ break;
+ case TLDLINE:
+ eExpectedType = TRDLINE;
+ break;
+ case TLANGLE:
+ eExpectedType = TRANGLE;
+ break;
+ case TLFLOOR:
+ eExpectedType = TRFLOOR;
+ break;
+ case TLCEIL:
+ eExpectedType = TRCEIL;
+ break;
+ case TLRLINE:
+ eExpectedType = TLRLINE;
+ break;
+ case TLRDLINE:
+ eExpectedType = TLRDLINE;
+ break;
+ default:
+ SAL_WARN("starmath", "unknown case");
+ }
+
+ if (m_aCurToken.eType == eExpectedType)
+ {
+ pRight.reset(new SmMathSymbolNode(m_aCurToken));
+ pRight->SetSelection(m_aCurESelection);
+ NextToken();
+ }
+ else
+ eError = SmParseError::ParentMismatch;
+ }
+
+ if (eError == SmParseError::None)
+ {
+ assert(pLeft);
+ assert(pRight);
+ xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ xSNode->SetScaleMode(eScaleMode);
+ return xSNode;
+ }
+ return DoError(eError);
+}
+
+std::unique_ptr<SmBracebodyNode> SmParser5::DoBracebody(bool bIsLeftRight)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ auto pBody = std::make_unique<SmBracebodyNode>(m_aCurToken);
+ pBody->SetSelection(m_aCurESelection);
+
+ std::vector<std::unique_ptr<SmNode>> aNodes;
+ // get body if any
+ if (bIsLeftRight)
+ {
+ do
+ {
+ if (m_aCurToken.eType == TMLINE)
+ {
+ SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken);
+ pTempNode->SetSelection(m_aCurESelection);
+ aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode));
+ NextToken();
+ }
+ else if (m_aCurToken.eType != TRIGHT)
+ {
+ aNodes.push_back(DoAlign());
+ if (m_aCurToken.eType != TMLINE && m_aCurToken.eType != TRIGHT)
+ aNodes.emplace_back(DoError(SmParseError::RightExpected));
+ }
+ } while (m_aCurToken.eType != TEND && m_aCurToken.eType != TRIGHT);
+ }
+ else
+ {
+ do
+ {
+ if (m_aCurToken.eType == TMLINE)
+ {
+ SmMathSymbolNode* pTempNode = new SmMathSymbolNode(m_aCurToken);
+ pTempNode->SetSelection(m_aCurESelection);
+ aNodes.emplace_back(std::unique_ptr<SmMathSymbolNode>(pTempNode));
+ NextToken();
+ }
+ else if (!TokenInGroup(TG::RBrace))
+ {
+ aNodes.push_back(DoAlign());
+ if (m_aCurToken.eType != TMLINE && !TokenInGroup(TG::RBrace))
+ aNodes.emplace_back(DoError(SmParseError::RbraceExpected));
+ }
+ } while (m_aCurToken.eType != TEND && !TokenInGroup(TG::RBrace));
+ }
+
+ pBody->SetSubNodes(buildNodeArray(aNodes));
+ pBody->SetScaleMode(bIsLeftRight ? SmScaleMode::Height : SmScaleMode::None);
+ return pBody;
+}
+
+std::unique_ptr<SmNode> SmParser5::DoEvaluate()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ // Create node
+ std::unique_ptr<SmStructureNode> xSNode(new SmBraceNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ SmToken aToken(TRLINE, MS_VERTLINE, "evaluate", TG::RBrace, 5);
+
+ // Parse body && left none
+ NextToken();
+ std::unique_ptr<SmNode> pBody = DoPower();
+ SmToken bToken(TNONE, '\0', "", TG::LBrace, 5);
+ std::unique_ptr<SmNode> pLeft;
+ pLeft.reset(new SmMathSymbolNode(bToken));
+
+ // Mount nodes
+ std::unique_ptr<SmNode> pRight;
+ pRight.reset(new SmMathSymbolNode(aToken));
+ xSNode->SetSubNodes(std::move(pLeft), std::move(pBody), std::move(pRight));
+ xSNode->SetScaleMode(SmScaleMode::Height); // scalable line
+
+ // Parse from to
+ if (m_aCurToken.nGroup == TG::Limit)
+ {
+ std::unique_ptr<SmNode> rSNode;
+ rSNode = DoSubSupEvaluate(std::move(xSNode));
+ rSNode->GetToken().eType = TEVALUATE;
+ return rSNode;
+ }
+
+ return xSNode;
+}
+
+std::unique_ptr<SmTextNode> SmParser5::DoFunction()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ if (m_aCurToken.eType == TFUNC)
+ {
+ NextToken(); // skip "FUNC"-statement
+ m_aCurToken.eType = TFUNC;
+ m_aCurToken.nGroup = TG::Function;
+ }
+ auto pNode = std::make_unique<SmTextNode>(m_aCurToken, FNT_FUNCTION);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmTableNode> SmParser5::DoBinom()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ auto xSNode = std::make_unique<SmTableNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+
+ NextToken();
+
+ auto xFirst = DoSum();
+ auto xSecond = DoSum();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xSecond));
+ return xSNode;
+}
+
+std::unique_ptr<SmBinVerNode> SmParser5::DoFrac()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::unique_ptr<SmBinVerNode> xSNode = std::make_unique<SmBinVerNode>(m_aCurToken);
+ xSNode->SetSelection(m_aCurESelection);
+ std::unique_ptr<SmNode> xOper = std::make_unique<SmRectangleNode>(m_aCurToken);
+ xOper->SetSelection(m_aCurESelection);
+
+ NextToken();
+
+ auto xFirst = DoSum();
+ auto xSecond = DoSum();
+ xSNode->SetSubNodes(std::move(xFirst), std::move(xOper), std::move(xSecond));
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoStack()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::unique_ptr<SmStructureNode> xSNode(new SmTableNode(m_aCurToken));
+ xSNode->SetSelection(m_aCurESelection);
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoError(SmParseError::LgroupExpected);
+ std::vector<std::unique_ptr<SmNode>> aExprArr;
+ do
+ {
+ NextToken();
+ aExprArr.push_back(DoAlign());
+ } while (m_aCurToken.eType == TPOUND);
+
+ if (m_aCurToken.eType == TRGROUP)
+ NextToken();
+ else
+ aExprArr.emplace_back(DoError(SmParseError::RgroupExpected));
+
+ xSNode->SetSubNodes(buildNodeArray(aExprArr));
+ return xSNode;
+}
+
+std::unique_ptr<SmStructureNode> SmParser5::DoMatrix()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ std::unique_ptr<SmMatrixNode> xMNode(new SmMatrixNode(m_aCurToken));
+ xMNode->SetSelection(m_aCurESelection);
+ NextToken();
+ if (m_aCurToken.eType != TLGROUP)
+ return DoError(SmParseError::LgroupExpected);
+
+ std::vector<std::unique_ptr<SmNode>> aExprArr;
+ do
+ {
+ NextToken();
+ aExprArr.push_back(DoAlign());
+ } while (m_aCurToken.eType == TPOUND);
+
+ size_t nCol = aExprArr.size();
+ size_t nRow = 1;
+ while (m_aCurToken.eType == TDPOUND)
+ {
+ NextToken();
+ for (size_t i = 0; i < nCol; i++)
+ {
+ auto xNode = DoAlign();
+ if (i < (nCol - 1))
+ {
+ if (m_aCurToken.eType == TPOUND)
+ NextToken();
+ else
+ xNode = DoError(SmParseError::PoundExpected);
+ }
+ aExprArr.emplace_back(std::move(xNode));
+ }
+ ++nRow;
+ }
+
+ if (m_aCurToken.eType == TRGROUP)
+ NextToken();
+ else
+ {
+ std::unique_ptr<SmNode> xENode(DoError(SmParseError::RgroupExpected));
+ if (aExprArr.empty())
+ nRow = nCol = 1;
+ else
+ aExprArr.pop_back();
+ aExprArr.emplace_back(std::move(xENode));
+ }
+
+ xMNode->SetSubNodes(buildNodeArray(aExprArr));
+ xMNode->SetRowCol(static_cast<sal_uInt16>(nRow), static_cast<sal_uInt16>(nCol));
+ return std::unique_ptr<SmStructureNode>(xMNode.release());
+}
+
+std::unique_ptr<SmSpecialNode> SmParser5::DoSpecial()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ bool bReplace = false;
+ OUString& rName = m_aCurToken.aText;
+ OUString aNewName;
+
+ // conversion of symbol names for 6.0 (XML) file format
+ // (name change on import / export.
+ // UI uses localized names XML file format does not.)
+ if (rName.startsWith("%"))
+ {
+ if (IsImportSymbolNames())
+ {
+ aNewName = SmLocalizedSymbolData::GetUiSymbolName(rName.subView(1));
+ bReplace = true;
+ }
+ else if (IsExportSymbolNames())
+ {
+ aNewName = SmLocalizedSymbolData::GetExportSymbolName(rName.subView(1));
+ bReplace = true;
+ }
+ }
+ if (!aNewName.isEmpty())
+ aNewName = "%" + aNewName;
+
+ if (bReplace && !aNewName.isEmpty() && rName != aNewName)
+ {
+ Replace(GetTokenIndex(), rName.getLength(), aNewName);
+ rName = aNewName;
+ }
+
+ // add symbol name to list of used symbols
+ const OUString aSymbolName(m_aCurToken.aText.copy(1));
+ if (!aSymbolName.isEmpty())
+ m_aUsedSymbols.insert(aSymbolName);
+
+ auto pNode = std::make_unique<SmSpecialNode>(m_aCurToken);
+ pNode->SetSelection(m_aCurESelection);
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmGlyphSpecialNode> SmParser5::DoGlyphSpecial()
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ auto pNode = std::make_unique<SmGlyphSpecialNode>(m_aCurToken);
+ NextToken();
+ return pNode;
+}
+
+std::unique_ptr<SmExpressionNode> SmParser5::DoError(SmParseError eError)
+{
+ DepthProtect aDepthGuard(m_nParseDepth);
+
+ // Identify error message
+ OUString sStrBuf(SmResId(RID_ERR_IDENT) + starmathdatabase::getParseErrorDesc(eError));
+
+ // Generate error node
+ m_aCurToken.eType = TERROR;
+ m_aCurToken.cMathChar = sStrBuf;
+ auto xSNode = std::make_unique<SmExpressionNode>(m_aCurToken);
+ SmErrorNode* pErr(new SmErrorNode(m_aCurToken));
+ pErr->SetSelection(m_aCurESelection);
+ xSNode->SetSubNode(0, pErr);
+
+ // Append error to the error list
+ SmErrorDesc aErrDesc(eError, xSNode.get(), m_aCurToken.cMathChar);
+ m_aErrDescList.push_back(aErrDesc);
+
+ NextToken();
+
+ return xSNode;
+}
+
+// end grammar
+
+SmParser5::SmParser5()
+ : m_nCurError(0)
+ , m_nBufferIndex(0)
+ , m_nTokenIndex(0)
+ , m_nRow(0)
+ , m_nColOff(0)
+ , m_bImportSymNames(false)
+ , m_bExportSymNames(false)
+ , m_nParseDepth(0)
+ , m_aNumCC(LanguageTag(LANGUAGE_ENGLISH_US))
+ , m_pSysCC(&SM_MOD()->GetSysLocale().GetCharClass())
+{
+}
+
+SmParser5::~SmParser5() {}
+
+std::unique_ptr<SmTableNode> SmParser5::Parse(const OUString& rBuffer)
+{
+ m_aUsedSymbols.clear();
+
+ m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
+ m_nBufferIndex = 0;
+ m_nTokenIndex = 0;
+ m_nRow = 0;
+ m_nColOff = 0;
+ m_nCurError = -1;
+
+ m_aErrDescList.clear();
+
+ NextToken();
+ return DoTable();
+}
+
+std::unique_ptr<SmNode> SmParser5::ParseExpression(const OUString& rBuffer)
+{
+ m_aBufferString = convertLineEnd(rBuffer, LINEEND_LF);
+ m_nBufferIndex = 0;
+ m_nTokenIndex = 0;
+ m_nRow = 0;
+ m_nColOff = 0;
+ m_nCurError = -1;
+
+ m_aErrDescList.clear();
+
+ NextToken();
+ return DoExpression();
+}
+
+const SmErrorDesc* SmParser5::NextError()
+{
+ if (!m_aErrDescList.empty())
+ if (m_nCurError > 0)
+ return &m_aErrDescList[--m_nCurError];
+ else
+ {
+ m_nCurError = 0;
+ return &m_aErrDescList[m_nCurError];
+ }
+ else
+ return nullptr;
+}
+
+const SmErrorDesc* SmParser5::PrevError()
+{
+ if (!m_aErrDescList.empty())
+ if (m_nCurError < static_cast<int>(m_aErrDescList.size() - 1))
+ return &m_aErrDescList[++m_nCurError];
+ else
+ {
+ m_nCurError = static_cast<int>(m_aErrDescList.size() - 1);
+ return &m_aErrDescList[m_nCurError];
+ }
+ else
+ return nullptr;
+}
+
+const SmErrorDesc* SmParser5::GetError() const
+{
+ if (m_aErrDescList.empty())
+ return nullptr;
+ return &m_aErrDescList.front();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/parsebase.cxx b/starmath/source/parsebase.cxx
new file mode 100644
index 000000000..31c1e40c9
--- /dev/null
+++ b/starmath/source/parsebase.cxx
@@ -0,0 +1,50 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <parsebase.hxx>
+#include <strings.hrc>
+#include <smmod.hxx>
+
+const TranslateId starmathdatabase::SmParseErrorDesc[] = {
+ // clang-format off
+ RID_ERR_NONE,
+ RID_ERR_UNEXPECTEDCHARACTER,
+ RID_ERR_UNEXPECTEDTOKEN,
+ RID_ERR_POUNDEXPECTED,
+ RID_ERR_COLOREXPECTED,
+ RID_ERR_LGROUPEXPECTED,
+ RID_ERR_RGROUPEXPECTED,
+ RID_ERR_LBRACEEXPECTED,
+ RID_ERR_RBRACEEXPECTED,
+ RID_ERR_PARENTMISMATCH,
+ RID_ERR_RIGHTEXPECTED,
+ RID_ERR_FONTEXPECTED,
+ RID_ERR_SIZEEXPECTED,
+ RID_ERR_DOUBLEALIGN,
+ RID_ERR_DOUBLESUBSUPSCRIPT,
+ RID_ERR_NUMBEREXPECTED
+ // clang-format on
+};
+
+OUString starmathdatabase::getParseErrorDesc(SmParseError err)
+{
+ return SmResId(starmathdatabase::SmParseErrorDesc[static_cast<uint_fast8_t>(err)]);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rect.cxx b/starmath/source/rect.cxx
new file mode 100644
index 000000000..e7b761a46
--- /dev/null
+++ b/starmath/source/rect.cxx
@@ -0,0 +1,621 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/diagnose.h>
+#include <o3tl/sorted_vector.hxx>
+#include <vcl/metric.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+
+#include <format.hxx>
+#include <rect.hxx>
+#include <types.hxx>
+#include <smmod.hxx>
+
+namespace {
+
+bool SmGetGlyphBoundRect(const vcl::RenderContext &rDev,
+ const OUString &rText, tools::Rectangle &rRect)
+ // basically the same as 'GetTextBoundRect' (in class 'OutputDevice')
+ // but with a string as argument.
+{
+ // handle special case first
+ if (rText.isEmpty())
+ {
+ rRect.SetEmpty();
+ return true;
+ }
+
+ // get a device where 'OutputDevice::GetTextBoundRect' will be successful
+ OutputDevice *pGlyphDev;
+ if (rDev.GetOutDevType() != OUTDEV_PRINTER)
+ pGlyphDev = const_cast<OutputDevice *>(&rDev);
+ else
+ {
+ // since we format for the printer (where GetTextBoundRect will fail)
+ // we need a virtual device here.
+ pGlyphDev = &SM_MOD()->GetDefaultVirtualDev();
+ }
+
+ const FontMetric aDevFM (rDev.GetFontMetric());
+
+ pGlyphDev->Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE);
+ vcl::Font aFnt(rDev.GetFont());
+ aFnt.SetAlignment(ALIGN_TOP);
+
+ // use scale factor when calling GetTextBoundRect to counter
+ // negative effects from antialiasing which may otherwise result
+ // in significant incorrect bounding rectangles for some characters.
+ Size aFntSize = aFnt.GetFontSize();
+
+ // Workaround to avoid HUGE font sizes and resulting problems
+ tools::Long nScaleFactor = 1;
+ while( aFntSize.Height() > 2000 * nScaleFactor )
+ nScaleFactor *= 2;
+
+ aFnt.SetFontSize( Size( aFntSize.Width() / nScaleFactor, aFntSize.Height() / nScaleFactor ) );
+ pGlyphDev->SetFont(aFnt);
+
+ tools::Long nTextWidth = rDev.GetTextWidth(rText);
+ tools::Rectangle aResult (Point(), Size(nTextWidth, rDev.GetTextHeight())),
+ aTmp;
+
+ bool bSuccess = pGlyphDev->GetTextBoundRect(aTmp, rText);
+ OSL_ENSURE( bSuccess, "GetTextBoundRect failed" );
+
+
+ if (!aTmp.IsEmpty())
+ {
+ aResult = tools::Rectangle(aTmp.Left() * nScaleFactor, aTmp.Top() * nScaleFactor,
+ aTmp.Right() * nScaleFactor, aTmp.Bottom() * nScaleFactor);
+ if (&rDev != pGlyphDev) /* only when rDev is a printer... */
+ {
+ tools::Long nGDTextWidth = pGlyphDev->GetTextWidth(rText);
+ if (nGDTextWidth != 0 &&
+ nTextWidth != nGDTextWidth)
+ {
+ aResult.SetRight( aResult.Right() * nTextWidth );
+ aResult.SetRight( aResult.Right() / ( nGDTextWidth * nScaleFactor) );
+ }
+ }
+ }
+
+ // move rectangle to match possibly different baselines
+ // (because of different devices)
+ tools::Long nDelta = aDevFM.GetAscent() - pGlyphDev->GetFontMetric().GetAscent() * nScaleFactor;
+ aResult.Move(0, nDelta);
+
+ pGlyphDev->Pop();
+
+ rRect = aResult;
+ return bSuccess;
+}
+
+bool SmIsMathAlpha(const OUString &rText)
+ // true iff symbol (from StarMath Font) should be treated as letter
+{
+ // Set of symbols, which should be treated as letters in StarMath Font
+ // (to get a normal (non-clipped) SmRect in contrast to the other operators
+ // and symbols).
+ static o3tl::sorted_vector<sal_Unicode> const aMathAlpha({
+ MS_ALEPH, MS_IM, MS_RE,
+ MS_WP, u'\xE070', MS_EMPTYSET,
+ u'\x2113', u'\xE0D6', u'\x2107',
+ u'\x2127', u'\x210A', MS_HBAR,
+ MS_LAMBDABAR, MS_SETN, MS_SETZ,
+ MS_SETQ, MS_SETR, MS_SETC,
+ u'\x2373', u'\xE0A5', u'\x2112',
+ u'\x2130', u'\x2131'
+ });
+
+ if (rText.isEmpty())
+ return false;
+
+ OSL_ENSURE(rText.getLength() == 1, "Sm : string must be exactly one character long");
+ sal_Unicode cChar = rText[0];
+
+ // is it a greek symbol?
+ if (u'\xE0AC' <= cChar && cChar <= u'\xE0D4')
+ return true;
+ // or, does it appear in 'aMathAlpha'?
+ return aMathAlpha.find(cChar) != aMathAlpha.end();
+}
+
+}
+
+
+SmRect::SmRect()
+ // constructs empty rectangle at (0, 0) with width and height 0.
+ : aTopLeft(0, 0)
+ , aSize(0, 0)
+ , nBaseline(0)
+ , nAlignT(0)
+ , nAlignM(0)
+ , nAlignB(0)
+ , nGlyphTop(0)
+ , nGlyphBottom(0)
+ , nItalicLeftSpace(0)
+ , nItalicRightSpace(0)
+ , nLoAttrFence(0)
+ , nHiAttrFence(0)
+ , nBorderWidth(0)
+ , bHasBaseline(false)
+ , bHasAlignInfo(false)
+{
+}
+
+
+void SmRect::CopyAlignInfo(const SmRect &rRect)
+{
+ nBaseline = rRect.nBaseline;
+ bHasBaseline = rRect.bHasBaseline;
+ nAlignT = rRect.nAlignT;
+ nAlignM = rRect.nAlignM;
+ nAlignB = rRect.nAlignB;
+ bHasAlignInfo = rRect.bHasAlignInfo;
+ nLoAttrFence = rRect.nLoAttrFence;
+ nHiAttrFence = rRect.nHiAttrFence;
+}
+
+
+SmRect::SmRect(const OutputDevice &rDev, const SmFormat *pFormat,
+ const OUString &rText, sal_uInt16 nBorder)
+ // get rectangle fitting for drawing 'rText' on OutputDevice 'rDev'
+ : aTopLeft(0, 0)
+ , aSize(rDev.GetTextWidth(rText), rDev.GetTextHeight())
+{
+ const FontMetric aFM (rDev.GetFontMetric());
+ bool bIsMath = aFM.GetFamilyName().equalsIgnoreAsciiCase( FONTNAME_MATH );
+ bool bAllowSmaller = bIsMath && !SmIsMathAlpha(rText);
+ const tools::Long nFontHeight = rDev.GetFont().GetFontSize().Height();
+
+ nBorderWidth = nBorder;
+ bHasAlignInfo = true;
+ bHasBaseline = true;
+ nBaseline = aFM.GetAscent();
+ nAlignT = nBaseline - nFontHeight * 750 / 1000;
+ nAlignM = nBaseline - nFontHeight * 121 / 422;
+ // that's where the horizontal bars of '+', '-', ... are
+ // (1/3 of ascent over baseline)
+ // (121 = 1/3 of 12pt ascent, 422 = 12pt fontheight)
+ nAlignB = nBaseline;
+
+ // workaround for printer fonts with very small (possible 0 or even
+ // negative(!)) leading
+ if (aFM.GetInternalLeading() < 5 && rDev.GetOutDevType() == OUTDEV_PRINTER)
+ {
+ OutputDevice *pWindow = Application::GetDefaultDevice();
+
+ pWindow->Push(vcl::PushFlags::MAPMODE | vcl::PushFlags::FONT);
+
+ pWindow->SetMapMode(rDev.GetMapMode());
+ pWindow->SetFont(rDev.GetFontMetric());
+
+ tools::Long nDelta = pWindow->GetFontMetric().GetInternalLeading();
+ if (nDelta == 0)
+ { // this value approx. fits a Leading of 80 at a
+ // Fontheight of 422 (12pt)
+ nDelta = nFontHeight * 8 / 43;
+ }
+ SetTop(GetTop() - nDelta);
+
+ pWindow->Pop();
+ }
+
+ // get GlyphBoundRect
+ tools::Rectangle aGlyphRect;
+ bool bSuccess = SmGetGlyphBoundRect(rDev, rText, aGlyphRect);
+ if (!bSuccess)
+ SAL_WARN("starmath", "Ooops... (Font missing?)");
+
+ nItalicLeftSpace = GetLeft() - aGlyphRect.Left() + nBorderWidth;
+ nItalicRightSpace = aGlyphRect.Right() - GetRight() + nBorderWidth;
+ if (nItalicLeftSpace < 0 && !bAllowSmaller)
+ nItalicLeftSpace = 0;
+ if (nItalicRightSpace < 0 && !bAllowSmaller)
+ nItalicRightSpace = 0;
+
+ tools::Long nDist = 0;
+ if (pFormat)
+ nDist = (rDev.GetFont().GetFontSize().Height()
+ * pFormat->GetDistance(DIS_ORNAMENTSIZE)) / 100;
+
+ nHiAttrFence = aGlyphRect.Top() - 1 - nBorderWidth - nDist;
+ nLoAttrFence = SmFromTo(GetAlignB(), GetBottom(), 0.0);
+
+ nGlyphTop = aGlyphRect.Top() - nBorderWidth;
+ nGlyphBottom = aGlyphRect.Bottom() + nBorderWidth;
+
+ if (bAllowSmaller)
+ {
+ // for symbols and operators from the StarMath Font
+ // we adjust upper and lower margin of the symbol
+ SetTop(nGlyphTop);
+ SetBottom(nGlyphBottom);
+ }
+
+ if (nHiAttrFence < GetTop())
+ nHiAttrFence = GetTop();
+
+ if (nLoAttrFence > GetBottom())
+ nLoAttrFence = GetBottom();
+
+ OSL_ENSURE(rText.isEmpty() || !IsEmpty(),
+ "Sm: empty rectangle created");
+}
+
+
+SmRect::SmRect(tools::Long nWidth, tools::Long nHeight)
+ // this constructor should never be used for anything textlike because
+ // it will not provide useful values for baseline, AlignT and AlignB!
+ // It's purpose is to get a 'SmRect' for the horizontal line in fractions
+ // as used in 'SmBinVerNode'.
+ : aTopLeft(0, 0)
+ , aSize(nWidth, nHeight)
+ , nBaseline(0)
+ , nItalicLeftSpace(0)
+ , nItalicRightSpace(0)
+ , nBorderWidth(0)
+ , bHasBaseline(false)
+ , bHasAlignInfo(true)
+{
+ nAlignT = nGlyphTop = nHiAttrFence = GetTop();
+ nAlignB = nGlyphBottom = nLoAttrFence = GetBottom();
+ nAlignM = (nAlignT + nAlignB) / 2; // this is the default
+}
+
+
+void SmRect::SetLeft(tools::Long nLeft)
+{
+ if (nLeft <= GetRight())
+ { aSize.setWidth( GetRight() - nLeft + 1 );
+ aTopLeft.setX( nLeft );
+ }
+}
+
+
+void SmRect::SetRight(tools::Long nRight)
+{
+ if (nRight >= GetLeft())
+ aSize.setWidth( nRight - GetLeft() + 1 );
+}
+
+
+void SmRect::SetBottom(tools::Long nBottom)
+{
+ if (nBottom >= GetTop())
+ aSize.setHeight( nBottom - GetTop() + 1 );
+}
+
+
+void SmRect::SetTop(tools::Long nTop)
+{
+ if (nTop <= GetBottom())
+ { aSize.setHeight( GetBottom() - nTop + 1 );
+ aTopLeft.setY( nTop );
+ }
+}
+
+
+void SmRect::Move(const Point &rPosition)
+ // move rectangle by position 'rPosition'.
+{
+ aTopLeft += rPosition;
+
+ tools::Long nDelta = rPosition.Y();
+ nBaseline += nDelta;
+ nAlignT += nDelta;
+ nAlignM += nDelta;
+ nAlignB += nDelta;
+ nGlyphTop += nDelta;
+ nGlyphBottom += nDelta;
+ nHiAttrFence += nDelta;
+ nLoAttrFence += nDelta;
+}
+
+
+Point SmRect::AlignTo(const SmRect &rRect, RectPos ePos,
+ RectHorAlign eHor, RectVerAlign eVer) const
+{ Point aPos (GetTopLeft());
+ // will become the topleft point of the new rectangle position
+
+ // set horizontal or vertical new rectangle position depending on ePos
+ switch (ePos)
+ { case RectPos::Left:
+ aPos.setX( rRect.GetItalicLeft() - GetItalicRightSpace()
+ - GetWidth() );
+ break;
+ case RectPos::Right:
+ aPos.setX( rRect.GetItalicRight() + 1 + GetItalicLeftSpace() );
+ break;
+ case RectPos::Top:
+ aPos.setY( rRect.GetTop() - GetHeight() );
+ break;
+ case RectPos::Bottom:
+ aPos.setY( rRect.GetBottom() + 1 );
+ break;
+ case RectPos::Attribute:
+ aPos.setX( rRect.GetItalicCenterX() - GetItalicWidth() / 2
+ + GetItalicLeftSpace() );
+ break;
+ default:
+ assert(false);
+ }
+
+ // check if horizontal position is already set
+ if (ePos == RectPos::Left || ePos == RectPos::Right || ePos == RectPos::Attribute)
+ // correct error in current vertical position
+ switch (eVer)
+ { case RectVerAlign::Top :
+ aPos.AdjustY(rRect.GetAlignT() - GetAlignT() );
+ break;
+ case RectVerAlign::Mid :
+ aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
+ break;
+ case RectVerAlign::Baseline :
+ // align baselines if possible else align mid's
+ if (HasBaseline() && rRect.HasBaseline())
+ aPos.AdjustY(rRect.GetBaseline() - GetBaseline() );
+ else
+ aPos.AdjustY(rRect.GetAlignM() - GetAlignM() );
+ break;
+ case RectVerAlign::Bottom :
+ aPos.AdjustY(rRect.GetAlignB() - GetAlignB() );
+ break;
+ case RectVerAlign::CenterY :
+ aPos.AdjustY(rRect.GetCenterY() - GetCenterY() );
+ break;
+ case RectVerAlign::AttributeHi:
+ aPos.AdjustY(rRect.GetHiAttrFence() - GetBottom() );
+ break;
+ case RectVerAlign::AttributeMid :
+ aPos.AdjustY(SmFromTo(rRect.GetAlignB(), rRect.GetAlignT(), 0.4)
+ - GetCenterY() );
+ break;
+ case RectVerAlign::AttributeLo :
+ aPos.AdjustY(rRect.GetLoAttrFence() - GetTop() );
+ break;
+ default :
+ assert(false);
+ }
+
+ // check if vertical position is already set
+ if (ePos == RectPos::Top || ePos == RectPos::Bottom)
+ // correct error in current horizontal position
+ switch (eHor)
+ { case RectHorAlign::Left:
+ aPos.AdjustX(rRect.GetItalicLeft() - GetItalicLeft() );
+ break;
+ case RectHorAlign::Center:
+ aPos.AdjustX(rRect.GetItalicCenterX() - GetItalicCenterX() );
+ break;
+ case RectHorAlign::Right:
+ aPos.AdjustX(rRect.GetItalicRight() - GetItalicRight() );
+ break;
+ default:
+ assert(false);
+ }
+
+ return aPos;
+}
+
+
+void SmRect::Union(const SmRect &rRect)
+ // rectangle union of current one with 'rRect'. The result is to be the
+ // smallest rectangles that covers the space of both rectangles.
+ // (empty rectangles cover no space)
+ //! Italic correction is NOT taken into account here!
+{
+ if (rRect.IsEmpty())
+ return;
+
+ tools::Long nL = rRect.GetLeft(),
+ nR = rRect.GetRight(),
+ nT = rRect.GetTop(),
+ nB = rRect.GetBottom(),
+ nGT = rRect.nGlyphTop,
+ nGB = rRect.nGlyphBottom;
+ if (!IsEmpty())
+ { tools::Long nTmp;
+
+ if ((nTmp = GetLeft()) < nL)
+ nL = nTmp;
+ if ((nTmp = GetRight()) > nR)
+ nR = nTmp;
+ if ((nTmp = GetTop()) < nT)
+ nT = nTmp;
+ if ((nTmp = GetBottom()) > nB)
+ nB = nTmp;
+ if ((nTmp = nGlyphTop) < nGT)
+ nGT = nTmp;
+ if ((nTmp = nGlyphBottom) > nGB)
+ nGB = nTmp;
+ }
+
+ SetLeft(nL);
+ SetRight(nR);
+ SetTop(nT);
+ SetBottom(nB);
+ nGlyphTop = nGT;
+ nGlyphBottom = nGB;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode)
+ // let current rectangle be the union of itself and 'rRect'
+ // (the smallest rectangle surrounding both). Also adapt values for
+ // 'AlignT', 'AlignM', 'AlignB', baseline and italic-spaces.
+ // The baseline is set according to 'eCopyMode'.
+ // If one of the rectangles has no relevant info the other one is copied.
+{
+ // get some values used for (italic) spaces adaptation
+ // ! (need to be done before changing current SmRect) !
+ tools::Long nL = std::min(GetItalicLeft(), rRect.GetItalicLeft()),
+ nR = std::max(GetItalicRight(), rRect.GetItalicRight());
+
+ Union(rRect);
+
+ SetItalicSpaces(GetLeft() - nL, nR - GetRight());
+
+ if (!HasAlignInfo())
+ CopyAlignInfo(rRect);
+ else if (rRect.HasAlignInfo())
+ {
+ assert(HasAlignInfo());
+ nAlignT = std::min(GetAlignT(), rRect.GetAlignT());
+ nAlignB = std::max(GetAlignB(), rRect.GetAlignB());
+ nHiAttrFence = std::min(GetHiAttrFence(), rRect.GetHiAttrFence());
+ nLoAttrFence = std::max(GetLoAttrFence(), rRect.GetLoAttrFence());
+
+ switch (eCopyMode)
+ { case RectCopyMBL::This:
+ // already done
+ break;
+ case RectCopyMBL::Arg:
+ CopyMBL(rRect);
+ break;
+ case RectCopyMBL::None:
+ bHasBaseline = false;
+ nAlignM = (nAlignT + nAlignB) / 2;
+ break;
+ case RectCopyMBL::Xor:
+ if (!HasBaseline())
+ CopyMBL(rRect);
+ break;
+ default :
+ assert(false);
+ }
+ }
+
+ return *this;
+}
+
+
+void SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ tools::Long nNewAlignM)
+ // as 'ExtendBy' but sets AlignM value to 'nNewAlignM'.
+ // (this version will be used in 'SmBinVerNode' to provide means to
+ // align eg "{a over b} over c" correctly where AlignM should not
+ // be (AlignT + AlignB) / 2)
+{
+ OSL_ENSURE(HasAlignInfo(), "Sm: no align info");
+
+ ExtendBy(rRect, eCopyMode);
+ nAlignM = nNewAlignM;
+}
+
+
+SmRect & SmRect::ExtendBy(const SmRect &rRect, RectCopyMBL eCopyMode,
+ bool bKeepVerAlignParams)
+ // as 'ExtendBy' but keeps original values for AlignT, -M and -B and
+ // baseline.
+ // (this is used in 'SmSupSubNode' where the sub-/supscripts shouldn't
+ // be allowed to modify these values.)
+{
+ tools::Long nOldAlignT = GetAlignT(),
+ nOldAlignM = GetAlignM(),
+ nOldAlignB = GetAlignB(),
+ nOldBaseline = nBaseline; //! depends not on 'HasBaseline'
+ bool bOldHasAlignInfo = HasAlignInfo();
+
+ ExtendBy(rRect, eCopyMode);
+
+ if (bKeepVerAlignParams)
+ { nAlignT = nOldAlignT;
+ nAlignM = nOldAlignM;
+ nAlignB = nOldAlignB;
+ nBaseline = nOldBaseline;
+ bHasAlignInfo = bOldHasAlignInfo;
+ }
+
+ return *this;
+}
+
+
+tools::Long SmRect::OrientedDist(const Point &rPoint) const
+ // return oriented distance of rPoint to the current rectangle,
+ // especially the return value is <= 0 iff the point is inside the
+ // rectangle.
+ // For simplicity the maximum-norm is used.
+{
+ bool bIsInside = IsInsideItalicRect(rPoint);
+
+ // build reference point to define the distance
+ Point aRef;
+ if (bIsInside)
+ { Point aIC (GetItalicCenterX(), GetCenterY());
+
+ aRef.setX( rPoint.X() >= aIC.X() ? GetItalicRight() : GetItalicLeft() );
+ aRef.setY( rPoint.Y() >= aIC.Y() ? GetBottom() : GetTop() );
+ }
+ else
+ {
+ // x-coordinate
+ if (rPoint.X() > GetItalicRight())
+ aRef.setX( GetItalicRight() );
+ else if (rPoint.X() < GetItalicLeft())
+ aRef.setX( GetItalicLeft() );
+ else
+ aRef.setX( rPoint.X() );
+ // y-coordinate
+ if (rPoint.Y() > GetBottom())
+ aRef.setY( GetBottom() );
+ else if (rPoint.Y() < GetTop())
+ aRef.setY( GetTop() );
+ else
+ aRef.setY( rPoint.Y() );
+ }
+
+ // build distance vector
+ Point aDist (aRef - rPoint);
+
+ tools::Long nAbsX = std::abs(aDist.X()),
+ nAbsY = std::abs(aDist.Y());
+
+ return bIsInside ? - std::min(nAbsX, nAbsY) : std::max (nAbsX, nAbsY);
+}
+
+
+bool SmRect::IsInsideRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetLeft()
+ && rPoint.X() <= GetRight();
+}
+
+
+bool SmRect::IsInsideItalicRect(const Point &rPoint) const
+{
+ return rPoint.Y() >= GetTop()
+ && rPoint.Y() <= GetBottom()
+ && rPoint.X() >= GetItalicLeft()
+ && rPoint.X() <= GetItalicRight();
+}
+
+SmRect SmRect::AsGlyphRect() const
+{
+ SmRect aRect (*this);
+ aRect.SetTop(nGlyphTop);
+ aRect.SetBottom(nGlyphBottom);
+ return aRect;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rtfexport.cxx b/starmath/source/rtfexport.cxx
new file mode 100644
index 000000000..ee5c486ea
--- /dev/null
+++ b/starmath/source/rtfexport.cxx
@@ -0,0 +1,502 @@
+/* -*- 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 "rtfexport.hxx"
+
+#include <svtools/rtfkeywd.hxx>
+#include <filter/msfilter/rtfutil.hxx>
+
+SmRtfExport::SmRtfExport(const SmNode* pIn)
+ : SmWordExportBase(pIn)
+ , m_pBuffer(nullptr)
+ , m_nEncoding(RTL_TEXTENCODING_DONTKNOW)
+{
+}
+
+void SmRtfExport::ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ if (!GetTree())
+ return;
+ m_pBuffer = &rBuffer;
+ m_nEncoding = nEncoding;
+ m_pBuffer->append("{" OOO_STRING_SVTOOLS_RTF_IGNORE LO_STRING_SVTOOLS_RTF_MOMATH " ");
+ HandleNode(GetTree(), 0);
+ m_pBuffer->append("}"); // moMath
+}
+
+// NOTE: This is still work in progress and unfinished, but it already covers a good
+// part of the rtf math stuff.
+
+void SmRtfExport::HandleVerticalStack(const SmNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MEQARR " ");
+ int size = pNode->GetNumSubNodes();
+ for (int i = 0; i < size; ++i)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(i), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // meqArr
+}
+
+void SmRtfExport::HandleText(const SmNode* pNode, int /*nLevel*/)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
+
+ if (pNode->GetToken().eType == TTEXT) // literal text
+ m_pBuffer->append(LO_STRING_SVTOOLS_RTF_MNOR " ");
+
+ auto pTemp = static_cast<const SmTextNode*>(pNode);
+ SAL_INFO("starmath.rtf", "Text: " << pTemp->GetText());
+ for (sal_Int32 i = 0; i < pTemp->GetText().getLength(); i++)
+ {
+ sal_uInt16 nChar = pTemp->GetText()[i];
+ OUString aValue(SmTextNode::ConvertSymbolToUnicode(nChar));
+ m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
+ }
+
+ m_pBuffer->append("}"); // mr
+}
+
+void SmRtfExport::HandleFractions(const SmNode* pNode, int nLevel, const char* type)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MF " ");
+ if (type)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MTYPE " ");
+ m_pBuffer->append(type);
+ m_pBuffer->append("}"); // mtype
+ m_pBuffer->append("}"); // mfPr
+ }
+ assert(pNode->GetNumSubNodes() == 3);
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNUM " ");
+ HandleNode(pNode->GetSubNode(0), nLevel + 1);
+ m_pBuffer->append("}"); // mnum
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEN " ");
+ HandleNode(pNode->GetSubNode(2), nLevel + 1);
+ m_pBuffer->append("}"); // mden
+ m_pBuffer->append("}"); // mf
+}
+
+void SmRtfExport::HandleAttribute(const SmAttributeNode* pNode, int nLevel)
+{
+ switch (pNode->Attribute()->GetToken().eType)
+ {
+ case TCHECK:
+ case TACUTE:
+ case TGRAVE:
+ case TBREVE:
+ case TCIRCLE:
+ case TVEC:
+ case TTILDE:
+ case THAT:
+ case TDOT:
+ case TDDOT:
+ case TDDDOT:
+ case TWIDETILDE:
+ case TWIDEHAT:
+ case TWIDEHARPOON:
+ case TWIDEVEC:
+ case TBAR:
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACC " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MACCPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ OUString aValue(pNode->Attribute()->GetToken().cMathChar);
+ m_pBuffer->append(msfilter::rtfutil::OutString(aValue, m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ m_pBuffer->append("}"); // maccPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // macc
+ break;
+ }
+ case TOVERLINE:
+ case TUNDERLINE:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBAR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBARPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ");
+ m_pBuffer->append((pNode->Attribute()->GetToken().eType == TUNDERLINE) ? "bot" : "top");
+ m_pBuffer->append("}"); // mpos
+ m_pBuffer->append("}"); // mbarPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mbar
+ break;
+ case TOVERSTRIKE:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOX " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBORDERBOXPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDETOP " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDEBOT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDELEFT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MHIDERIGHT " 1}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSTRIKEH " 1}");
+ m_pBuffer->append("}"); // mborderBoxPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mborderBox
+ break;
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+void SmRtfExport::HandleRoot(const SmRootNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRAD " ");
+ if (const SmNode* argument = pNode->Argument())
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " ");
+ HandleNode(argument, nLevel + 1);
+ m_pBuffer->append("}"); // mdeg
+ }
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MRADPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEGHIDE " 1}");
+ m_pBuffer->append("}"); // mradPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDEG " }"); // empty but present
+ }
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mrad
+}
+
+namespace
+{
+OString mathSymbolToString(const SmNode* node, rtl_TextEncoding nEncoding)
+{
+ assert(node->GetType() == SmNodeType::Math || node->GetType() == SmNodeType::MathIdent);
+ auto txtnode = static_cast<const SmTextNode*>(node);
+ if (txtnode->GetText().isEmpty())
+ return {};
+ assert(txtnode->GetText().getLength() == 1);
+ sal_Unicode chr = SmTextNode::ConvertSymbolToUnicode(txtnode->GetText()[0]);
+ OUString aValue(chr);
+ return msfilter::rtfutil::OutString(aValue, nEncoding);
+}
+}
+
+void SmRtfExport::HandleOperator(const SmOperNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.rtf", "Operator: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TINT:
+ case TINTD:
+ case TIINT:
+ case TIIINT:
+ case TLINT:
+ case TLLINT:
+ case TLLLINT:
+ case TPROD:
+ case TCOPROD:
+ case TSUM:
+ {
+ const SmSubSupNode* subsup
+ = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
+ ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
+ : nullptr;
+ const SmNode* operation = subsup ? subsup->GetBody() : pNode->GetSubNode(0);
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARY " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MNARYPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ m_pBuffer->append(mathSymbolToString(operation, m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ if (!subsup || !subsup->GetSubSup(CSUB))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUBHIDE " 1}");
+ if (!subsup || !subsup->GetSubSup(CSUP))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUPHIDE " 1}");
+ m_pBuffer->append("}"); // mnaryPr
+ if (!subsup || !subsup->GetSubSup(CSUB))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " }");
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ }
+ if (!subsup || !subsup->GetSubSup(CSUP))
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " }");
+ else
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(subsup->GetSubSup(CSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ }
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mnary
+ break;
+ }
+ case TLIM:
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFUNC " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MFNAME " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSymbol(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ if (const SmSubSupNode* subsup
+ = pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup
+ ? static_cast<const SmSubSupNode*>(pNode->GetSubNode(0))
+ : nullptr)
+ if (subsup->GetSubSup(CSUB))
+ HandleNode(subsup->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimLow
+ m_pBuffer->append("}"); // mfName
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->GetSubNode(1), nLevel + 1); // body
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mfunc
+ break;
+ default:
+ SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled oper type");
+ break;
+ }
+}
+
+void SmRtfExport::HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags)
+{
+ // rtf supports only a certain combination of sub/super scripts, but LO can have any,
+ // so try to merge it using several tags if necessary
+ if (flags == 0) // none
+ return;
+ if ((flags & (1 << RSUP | 1 << RSUB)) == (1 << RSUP | 1 << RSUB))
+ {
+ // m:sSubSup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUBSUP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUP | 1 << RSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("}"); // msubSup
+ }
+ else if ((flags & (1 << RSUB)) == 1 << RSUB)
+ {
+ // m:sSub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUB " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(RSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("}"); // msSub
+ }
+ else if ((flags & (1 << RSUP)) == 1 << RSUP)
+ {
+ // m:sSup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSSUP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << RSUP);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(RSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("}"); // msSup
+ }
+ else if ((flags & (1 << LSUP | 1 << LSUB)) == (1 << LSUP | 1 << LSUB))
+ {
+ // m:sPre
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSPRE " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUB " ");
+ HandleNode(pNode->GetSubSup(LSUB), nLevel + 1);
+ m_pBuffer->append("}"); // msub
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSUP " ");
+ HandleNode(pNode->GetSubSup(LSUP), nLevel + 1);
+ m_pBuffer->append("}"); // msup
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << LSUP | 1 << LSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // msPre
+ }
+ else if ((flags & (1 << CSUB)) == (1 << CSUB))
+ {
+ // m:limLow looks like a good element for central superscript
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << CSUB);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->GetSubSup(CSUB), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimLow
+ }
+ else if ((flags & (1 << CSUP)) == (1 << CSUP))
+ {
+ // m:limUpp looks like a good element for central superscript
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ flags &= ~(1 << CSUP);
+ if (flags == 0)
+ HandleNode(pNode->GetBody(), nLevel + 1);
+ else
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->GetSubSup(CSUP), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimUpp
+ }
+ else
+ SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled subsup type");
+}
+
+void SmRtfExport::HandleMatrix(const SmMatrixNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MM " ");
+ for (size_t row = 0; row < pNode->GetNumRows(); ++row)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MMR " ");
+ for (size_t col = 0; col < pNode->GetNumCols(); ++col)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ if (const SmNode* node = pNode->GetSubNode(row * pNode->GetNumCols() + col))
+ HandleNode(node, nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // mmr
+ }
+ m_pBuffer->append("}"); // mm
+}
+
+void SmRtfExport::HandleBrace(const SmBraceNode* pNode, int nLevel)
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MD " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MDPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MBEGCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->OpeningBrace(), m_nEncoding));
+ m_pBuffer->append("}"); // mbegChr
+ std::vector<const SmNode*> subnodes;
+ if (pNode->Body()->GetType() == SmNodeType::Bracebody)
+ {
+ auto body = static_cast<const SmBracebodyNode*>(pNode->Body());
+ bool separatorWritten = false; // assume all separators are the same
+ for (size_t i = 0; i < body->GetNumSubNodes(); ++i)
+ {
+ const SmNode* subnode = body->GetSubNode(i);
+ if (subnode->GetType() == SmNodeType::Math
+ || subnode->GetType() == SmNodeType::MathIdent)
+ {
+ // do not write, but write what separator it is
+ auto math = static_cast<const SmMathSymbolNode*>(subnode);
+ if (!separatorWritten)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MSEPCHR " ");
+ m_pBuffer->append(mathSymbolToString(math, m_nEncoding));
+ m_pBuffer->append("}"); // msepChr
+ separatorWritten = true;
+ }
+ }
+ else
+ subnodes.push_back(subnode);
+ }
+ }
+ else
+ subnodes.push_back(pNode->Body());
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MENDCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->ClosingBrace(), m_nEncoding));
+ m_pBuffer->append("}"); // mendChr
+ m_pBuffer->append("}"); // mdPr
+ for (const SmNode* subnode : subnodes)
+ {
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(subnode, nLevel + 1);
+ m_pBuffer->append("}"); // me
+ }
+ m_pBuffer->append("}"); // md
+}
+
+void SmRtfExport::HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.rtf", "Vertical: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TOVERBRACE:
+ case TUNDERBRACE:
+ {
+ bool top = (pNode->GetToken().eType == TOVERBRACE);
+ if (top)
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMUPP " ");
+ else
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIMLOW " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MGROUPCHRPR " ");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MCHR " ");
+ m_pBuffer->append(mathSymbolToString(pNode->Brace(), m_nEncoding));
+ m_pBuffer->append("}"); // mchr
+ // TODO not sure if pos and vertJc are correct
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MPOS " ")
+ .append(top ? "top" : "bot")
+ .append("}");
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MVERTJC " ")
+ .append(top ? "bot" : "top")
+ .append("}");
+ m_pBuffer->append("}"); // mgroupChrPr
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_ME " ");
+ HandleNode(pNode->Body(), nLevel + 1);
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("}"); // mgroupChr
+ m_pBuffer->append("}"); // me
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MLIM " ");
+ HandleNode(pNode->Script(), nLevel + 1);
+ m_pBuffer->append("}"); // mlim
+ m_pBuffer->append("}"); // mlimUpp or mlimLow
+ break;
+ }
+ default:
+ SAL_INFO("starmath.rtf", "TODO: " << __func__ << " unhandled vertical brace type");
+ break;
+ }
+}
+
+void SmRtfExport::HandleBlank()
+{
+ m_pBuffer->append("{" LO_STRING_SVTOOLS_RTF_MR " ");
+ m_pBuffer->append(" ");
+ m_pBuffer->append("}"); // mr
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/rtfexport.hxx b/starmath/source/rtfexport.hxx
new file mode 100644
index 000000000..e8c38aaa6
--- /dev/null
+++ b/starmath/source/rtfexport.hxx
@@ -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/.
+ */
+
+#pragma once
+
+#include "wordexportbase.hxx"
+
+#include <rtl/strbuf.hxx>
+
+/**
+ Class implementing writing of formulas to RTF.
+ */
+class SmRtfExport : public SmWordExportBase
+{
+public:
+ explicit SmRtfExport(const SmNode* pIn);
+ void ConvertFromStarMath(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding);
+
+private:
+ void HandleVerticalStack(const SmNode* pNode, int nLevel) override;
+ void HandleText(const SmNode* pNode, int nLevel) override;
+ void HandleFractions(const SmNode* pNode, int nLevel, const char* type) override;
+ void HandleRoot(const SmRootNode* pNode, int nLevel) override;
+ void HandleAttribute(const SmAttributeNode* pNode, int nLevel) override;
+ void HandleOperator(const SmOperNode* pNode, int nLevel) override;
+ void HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) override;
+ void HandleMatrix(const SmMatrixNode* pNode, int nLevel) override;
+ void HandleBrace(const SmBraceNode* pNode, int nLevel) override;
+ void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) override;
+ void HandleBlank() override;
+
+ OStringBuffer* m_pBuffer;
+ rtl_TextEncoding m_nEncoding;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdetect.cxx b/starmath/source/smdetect.cxx
new file mode 100644
index 000000000..f17ec7864
--- /dev/null
+++ b/starmath/source/smdetect.cxx
@@ -0,0 +1,148 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "smdetect.hxx"
+#include <cppuhelper/supportsservice.hxx>
+#include <com/sun/star/io/XInputStream.hpp>
+#include <com/sun/star/ucb/ContentCreationException.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <sfx2/docfile.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <sot/storage.hxx>
+#include <tools/diagnose_ex.h>
+
+#include "eqnolefilehdr.hxx"
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using utl::MediaDescriptor;
+
+SmFilterDetect::SmFilterDetect()
+{
+}
+
+SmFilterDetect::~SmFilterDetect()
+{
+}
+
+OUString SAL_CALL SmFilterDetect::detect( Sequence< PropertyValue >& lDescriptor )
+{
+ MediaDescriptor aMediaDesc( lDescriptor );
+ uno::Reference< io::XInputStream > xInStream ( aMediaDesc[MediaDescriptor::PROP_INPUTSTREAM], uno::UNO_QUERY );
+ if ( !xInStream.is() )
+ return OUString();
+
+ SfxMedium aMedium;
+ aMedium.UseInteractionHandler( false );
+ aMedium.setStreamToLoadFrom( xInStream, true );
+
+ SvStream *pInStrm = aMedium.GetInStream();
+ if ( !pInStrm || pInStrm->GetError() )
+ return OUString();
+
+ // Do not attempt to create an SotStorage on a
+ // 0-length stream as that would create the compound
+ // document header on the stream and effectively write to
+ // disk!
+ pInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ if ( pInStrm->remainingSize() == 0 )
+ return OUString();
+
+ bool bStorageOk = false;
+ try
+ {
+ tools::SvRef<SotStorage> aStorage = new SotStorage( pInStrm, false );
+ bStorageOk = !aStorage->GetError();
+ if (bStorageOk)
+ {
+ if ( aStorage->IsStream("Equation Native") )
+ {
+ sal_uInt8 nVersion;
+ if ( GetMathTypeVersion( aStorage.get(), nVersion ) && nVersion <=3 )
+ return "math_MathType_3x";
+ }
+ }
+ }
+ catch (const css::ucb::ContentCreationException &)
+ {
+ TOOLS_WARN_EXCEPTION("starmath", "SmFilterDetect::detect caught" );
+ }
+
+ if (!bStorageOk)
+ {
+ // 200 should be enough for the XML
+ // version, encoding and !DOCTYPE
+ // stuff I hope?
+ static const sal_uInt16 nBufferSize = 200;
+ char aBuffer[nBufferSize+1];
+ pInStrm->Seek( STREAM_SEEK_TO_BEGIN );
+ pInStrm->StartReadingUnicodeText( RTL_TEXTENCODING_DONTKNOW ); // avoid BOM marker
+ auto nBytesRead = pInStrm->ReadBytes( aBuffer, nBufferSize );
+ if (nBytesRead >= 6)
+ {
+ aBuffer[nBytesRead] = 0;
+ bool bIsMathType = false;
+ if (0 == strncmp( "<?xml", aBuffer, 5))
+ bIsMathType = (strstr( aBuffer, "<math>" ) ||
+ strstr( aBuffer, "<math " ) ||
+ strstr( aBuffer, "<math:math " ));
+ else
+ // this is the old <math tag to MathML in the beginning of the XML file
+ bIsMathType = (0 == strncmp( "<math ", aBuffer, 6) ||
+ 0 == strncmp( "<math> ", aBuffer, 7) ||
+ 0 == strncmp( "<math:math> ", aBuffer, 12));
+
+ if ( bIsMathType )
+ return "math_MathML_XML_Math";
+ }
+ }
+
+ return OUString();
+}
+
+/* XServiceInfo */
+OUString SAL_CALL SmFilterDetect::getImplementationName()
+{
+ return "com.sun.star.comp.math.FormatDetector";
+}
+
+/* XServiceInfo */
+sal_Bool SAL_CALL SmFilterDetect::supportsService( const OUString& sServiceName )
+{
+ return cppu::supportsService(this, sServiceName);
+}
+
+/* XServiceInfo */
+Sequence< OUString > SAL_CALL SmFilterDetect::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ExtendedTypeDetection" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+math_FormatDetector_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new SmFilterDetect);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdetect.hxx b/starmath/source/smdetect.hxx
new file mode 100644
index 000000000..81a68d909
--- /dev/null
+++ b/starmath/source/smdetect.hxx
@@ -0,0 +1,46 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <rtl/ustring.hxx>
+#include <com/sun/star/document/XExtendedFilterDetection.hpp>
+#include <cppuhelper/implbase.hxx>
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+
+
+namespace com::sun::star::beans { struct PropertyValue; }
+
+class SmFilterDetect : public ::cppu::WeakImplHelper< css::document::XExtendedFilterDetection, css::lang::XServiceInfo >
+{
+public:
+ explicit SmFilterDetect();
+ virtual ~SmFilterDetect() override;
+
+ /* XServiceInfo */
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& sServiceName ) override;
+ virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+ // XExtendedFilterDetect
+ virtual OUString SAL_CALL detect( css::uno::Sequence< css::beans::PropertyValue >& lDescriptor ) override;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smdll.cxx b/starmath/source/smdll.cxx
new file mode 100644
index 000000000..f10e9d9db
--- /dev/null
+++ b/starmath/source/smdll.cxx
@@ -0,0 +1,90 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <svx/svxids.hrc>
+#include <svx/modctrl.hxx>
+#include <svx/zoomctrl.hxx>
+#include <svx/zoomsliderctrl.hxx>
+#include <sfx2/docfac.hxx>
+#include <sfx2/app.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+
+#include <smdll.hxx>
+#include <smmod.hxx>
+#include <document.hxx>
+#include <view.hxx>
+
+#include <ElementsDockingWindow.hxx>
+
+#include <starmath.hrc>
+
+#include <svx/xmlsecctrl.hxx>
+
+namespace
+{
+ class SmDLL
+ {
+ public:
+ SmDLL();
+ };
+
+ SmDLL::SmDLL()
+ {
+ if ( SfxApplication::GetModule(SfxToolsModule::Math) ) // Module already active
+ return;
+
+ SfxObjectFactory& rFactory = SmDocShell::Factory();
+
+ auto pUniqueModule = std::make_unique<SmModule>(&rFactory);
+ SmModule* pModule = pUniqueModule.get();
+ SfxApplication::SetModule(SfxToolsModule::Math, std::move(pUniqueModule));
+
+ rFactory.SetDocumentServiceName( "com.sun.star.formula.FormulaProperties" );
+
+ SmModule::RegisterInterface(pModule);
+ SmDocShell::RegisterInterface(pModule);
+ SmViewShell::RegisterInterface(pModule);
+
+ SmViewShell::RegisterFactory(SFX_INTERFACE_SFXAPP);
+
+ SvxZoomStatusBarControl::RegisterControl(SID_ATTR_ZOOM, pModule);
+ SvxZoomSliderControl::RegisterControl(SID_ATTR_ZOOMSLIDER, pModule);
+ SvxModifyControl::RegisterControl(SID_TEXTSTATUS, pModule);
+ XmlSecStatusBarControl::RegisterControl(SID_SIGNATURE, pModule);
+
+ sfx2::sidebar::SidebarChildWindow::RegisterChildWindow(false, pModule);
+
+ SmCmdBoxWrapper::RegisterChildWindow(true);
+ SmElementsDockingWindowWrapper::RegisterChildWindow(true);
+ }
+
+ struct theSmDLLInstance : public rtl::Static<SmDLL, theSmDLLInstance> {};
+}
+
+namespace SmGlobals
+{
+ void ensure()
+ {
+ theSmDLLInstance::get();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/smediteng.cxx b/starmath/source/smediteng.cxx
new file mode 100644
index 000000000..5560d501e
--- /dev/null
+++ b/starmath/source/smediteng.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
+/*
+ * 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 <smediteng.hxx>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+
+#include <vcl/settings.hxx>
+#include <editeng/editview.hxx>
+#include <editeng/eeitem.hxx>
+#include <editeng/fhgtitem.hxx>
+#include <editeng/fontitem.hxx>
+#include <svl/itempool.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/svapp.hxx>
+
+SmEditEngine::SmEditEngine(SfxItemPool* pItemPool)
+ : EditEngine(pItemPool)
+ , m_nOldZoom(100)
+ , m_nNewZoom(100)
+ , m_nDefaultFontSize(0)
+ , m_aAllSelection(0, 0, 0, 0)
+{
+ SetText(u"");
+
+ // Add external text leading
+ SetAddExtLeading(true);
+
+ // Allow to undo changes ( Ctrl + z )
+ EnableUndo(true);
+
+ // Length in pixel of a tabulation
+ SetDefTab(sal_uInt16(Application::GetDefaultDevice()->GetTextWidth("XXXX")));
+
+ // Set default background color by theme
+ SetBackgroundColor(
+ Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetFieldColor());
+
+ // Control words
+ SetControlWord((GetControlWord() | EEControlBits::AUTOINDENTING)
+ & EEControlBits(~EEControlBits::UNDOATTRIBS)
+ & EEControlBits(~EEControlBits::PASTESPECIAL));
+
+ // Word delimiters for auto word selection by double click
+ SetWordDelimiters(" .=+-*/(){}[];\"");
+
+ // Default mapping mode
+ SetRefMapMode(MapMode(MapUnit::MapPixel));
+
+ // Default size of the box
+ SetPaperSize(Size(1000, 0));
+}
+
+bool SmEditEngine::checkZoom()
+{
+ return m_nOldZoom != (m_nNewZoom = SM_MOD()->GetConfig()->GetSmEditWindowZoomFactor());
+}
+
+void SmEditEngine::executeZoom(EditView* pEditView)
+{
+ if (checkZoom())
+ {
+ updateZoom();
+ if (pEditView)
+ {
+ FormatAndLayout(pEditView);
+ pEditView->SetSelection(pEditView->GetSelection());
+ }
+ }
+}
+
+void SmEditEngine::updateZoom()
+{
+ // In first run get font size as base to scale
+ if (m_nDefaultFontSize == 0)
+ {
+ SfxItemSet sfxatts = GetAttribs(0, 0, 0, GetAttribsFlags::CHARATTRIBS);
+ const SvxFontHeightItem* sfxattsh = sfxatts.GetItem(EE_CHAR_FONTHEIGHT);
+ m_nDefaultFontSize = sfxattsh->GetHeight();
+ }
+
+ // Now we calculate the new font size
+ sal_Int32 nNewFontSize = m_nDefaultFontSize * m_nNewZoom / 100;
+
+ // We apply the new font size to all the text
+ updateAllESelection(); // Update length of the text
+ SfxItemSet aSet = GetEmptyItemSet();
+ aSet.Put(SvxFontHeightItem(nNewFontSize, 100, EE_CHAR_FONTHEIGHT));
+ QuickSetAttribs(aSet, m_aAllSelection);
+
+ // We don't forget to equalize the zoomvalues
+ m_nOldZoom = m_nNewZoom;
+}
+
+void SmEditEngine::updateAllESelection()
+{
+ sal_Int32 paracount = GetParagraphCount();
+ m_aAllSelection.nEndPara = paracount > 0 ? paracount - 1 : 0;
+ sal_Int32 textlength = GetTextLen(m_aAllSelection.nEndPara);
+ m_aAllSelection.nEndPos = textlength > 0 ? textlength : 0;
+}
+
+void SmEditEngine::setSmItemPool(SfxItemPool* mpItemPool, const SvtLinguOptions& maLangOptions)
+{
+ // Set fonts to be used
+ struct FontData
+ {
+ LanguageType nFallbackLang;
+ LanguageType nLang;
+ DefaultFontType nFontType;
+ sal_uInt16 nFontInfoId;
+ };
+
+ FontData aFontDataTable[3]
+ = { // info to get western font to be used
+ { LANGUAGE_ENGLISH_US, maLangOptions.nDefaultLanguage, DefaultFontType::FIXED,
+ EE_CHAR_FONTINFO },
+ // info to get CJK font to be used
+ { LANGUAGE_JAPANESE, maLangOptions.nDefaultLanguage_CJK, DefaultFontType::CJK_TEXT,
+ EE_CHAR_FONTINFO_CJK },
+ // info to get CTL font to be used
+ { LANGUAGE_ARABIC_SAUDI_ARABIA, maLangOptions.nDefaultLanguage_CTL,
+ DefaultFontType::CTL_TEXT, EE_CHAR_FONTINFO_CTL }
+ };
+
+ // Text color
+ auto aDefaultDevice = Application::GetDefaultDevice();
+ Color aTextColor = aDefaultDevice->GetSettings().GetStyleSettings().GetFieldTextColor();
+ for (const FontData& aFontData : aFontDataTable)
+ {
+ LanguageType nLang
+ = (LANGUAGE_NONE == aFontData.nLang) ? aFontData.nFallbackLang : aFontData.nLang;
+ vcl::Font aFont = OutputDevice::GetDefaultFont(aFontData.nFontType, nLang,
+ GetDefaultFontFlags::OnlyOne);
+ aFont.SetColor(aTextColor);
+ mpItemPool->SetPoolDefaultItem(SvxFontItem(aFont.GetFamilyType(), aFont.GetFamilyName(),
+ aFont.GetStyleName(), aFont.GetPitch(),
+ aFont.GetCharSet(), aFontData.nFontInfoId));
+ }
+
+ // Set font heights
+ SvxFontHeightItem aFontHeight(
+ aDefaultDevice->LogicToPixel(Size(0, 11), MapMode(MapUnit::MapPoint)).Height(), 100,
+ EE_CHAR_FONTHEIGHT);
+ mpItemPool->SetPoolDefaultItem(aFontHeight);
+ aFontHeight.SetWhich(EE_CHAR_FONTHEIGHT_CJK);
+ mpItemPool->SetPoolDefaultItem(aFontHeight);
+ aFontHeight.SetWhich(EE_CHAR_FONTHEIGHT_CTL);
+ mpItemPool->SetPoolDefaultItem(aFontHeight);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/starmath/source/smmod.cxx b/starmath/source/smmod.cxx
new file mode 100644
index 000000000..783965844
--- /dev/null
+++ b/starmath/source/smmod.cxx
@@ -0,0 +1,237 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/string_view.hxx>
+#include <sfx2/objface.hxx>
+#include <svl/whiter.hxx>
+#include <sfx2/viewsh.hxx>
+#include <svx/svxids.hrc>
+#include <vcl/virdev.hxx>
+#include <unotools/syslocale.hxx>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+#include <dialog.hxx>
+#include <view.hxx>
+#include <smmod.hrc>
+#include <starmath.hrc>
+#include <svx/modctrl.hxx>
+#include <svtools/colorcfg.hxx>
+
+
+#define ShellClass_SmModule
+#include <smslots.hxx>
+
+OUString SmResId(TranslateId aId)
+{
+ return Translate::get(aId, SM_MOD()->GetResLocale());
+}
+
+OUString SmLocalizedSymbolData::GetUiSymbolName( std::u16string_view rExportName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOL_NAMES); ++i)
+ {
+ if (o3tl::equalsAscii(rExportName, RID_UI_SYMBOL_NAMES[i].mpId))
+ {
+ aRes = SmResId(RID_UI_SYMBOL_NAMES[i]);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetExportSymbolName( std::u16string_view rUiName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOL_NAMES); ++i)
+ {
+ if (rUiName == SmResId(RID_UI_SYMBOL_NAMES[i]))
+ {
+ const char *pKey = RID_UI_SYMBOL_NAMES[i].mpId;
+ aRes = OUString(pKey, strlen(pKey), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetUiSymbolSetName( std::u16string_view rExportName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOLSET_NAMES); ++i)
+ {
+ if (o3tl::equalsAscii(rExportName, RID_UI_SYMBOLSET_NAMES[i].mpId))
+ {
+ aRes = SmResId(RID_UI_SYMBOLSET_NAMES[i]);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+OUString SmLocalizedSymbolData::GetExportSymbolSetName( std::u16string_view rUiName )
+{
+ OUString aRes;
+
+ for (size_t i = 0; i < SAL_N_ELEMENTS(RID_UI_SYMBOLSET_NAMES); ++i)
+ {
+ if (rUiName == SmResId(RID_UI_SYMBOLSET_NAMES[i]))
+ {
+ const char *pKey = RID_UI_SYMBOLSET_NAMES[i].mpId;
+ aRes = OUString(pKey, strlen(pKey), RTL_TEXTENCODING_UTF8);
+ break;
+ }
+ }
+
+ return aRes;
+}
+
+SFX_IMPL_INTERFACE(SmModule, SfxModule)
+
+void SmModule::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterStatusBar(StatusBarId::MathStatusBar);
+}
+
+SmModule::SmModule(SfxObjectFactory* pObjFact)
+ : SfxModule("sm", {pObjFact})
+{
+ SetName("StarMath");
+
+ SvxModifyControl::RegisterControl(SID_DOC_MODIFIED, this);
+}
+
+SmModule::~SmModule()
+{
+ if (mpColorConfig)
+ mpColorConfig->RemoveListener(this);
+ mpVirtualDev.disposeAndClear();
+}
+
+svtools::ColorConfig & SmModule::GetColorConfig()
+{
+ if(!mpColorConfig)
+ {
+ mpColorConfig.reset(new svtools::ColorConfig);
+ mpColorConfig->AddListener(this);
+ }
+ return *mpColorConfig;
+}
+
+void SmModule::ConfigurationChanged(utl::ConfigurationBroadcaster* pBrdCst, ConfigurationHints)
+{
+ if (pBrdCst != mpColorConfig.get())
+ return;
+
+ SfxViewShell* pViewShell = SfxViewShell::GetFirst();
+ while (pViewShell)
+ {
+ // FIXME: What if pViewShell is for a different document,
+ // but OTOH Math is presumably never used through
+ // LibreOfficeKit, so maybe an irrelevant concern?
+ if (dynamic_cast<const SmViewShell *>(pViewShell) != nullptr)
+ pViewShell->GetWindow()->Invalidate();
+ pViewShell = SfxViewShell::GetNext(*pViewShell);
+ }
+}
+
+SmMathConfig * SmModule::GetConfig()
+{
+ if(!mpConfig)
+ mpConfig.reset(new SmMathConfig);
+ return mpConfig.get();
+}
+
+SmSymbolManager & SmModule::GetSymbolManager()
+{
+ return GetConfig()->GetSymbolManager();
+}
+
+const SvtSysLocale& SmModule::GetSysLocale()
+{
+ if( !mpSysLocale )
+ mpSysLocale.reset(new SvtSysLocale);
+ return *mpSysLocale;
+}
+
+VirtualDevice &SmModule::GetDefaultVirtualDev()
+{
+ if (!mpVirtualDev)
+ {
+ mpVirtualDev.reset( VclPtr<VirtualDevice>::Create() );
+ mpVirtualDev->SetReferenceDevice( VirtualDevice::RefDevMode::MSO1 );
+ }
+ return *mpVirtualDev;
+}
+
+void SmModule::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ for (sal_uInt16 nWh = aIter.FirstWhich(); 0 != nWh; nWh = aIter.NextWhich())
+ switch (nWh)
+ {
+ case SID_CONFIGEVENT :
+ rSet.DisableItem(SID_CONFIGEVENT);
+ break;
+ }
+}
+
+std::optional<SfxItemSet> SmModule::CreateItemSet( sal_uInt16 nId )
+{
+ std::optional<SfxItemSet> pRet;
+ if(nId == SID_SM_EDITOPTIONS)
+ {
+ pRet.emplace(
+ GetPool(),
+ svl::Items< //TP_SMPRINT
+ SID_PRINTTITLE, SID_PRINTZOOM,
+ SID_NO_RIGHT_SPACES, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>);
+
+ GetConfig()->ConfigToItemSet(*pRet);
+ }
+ return pRet;
+}
+
+void SmModule::ApplyItemSet( sal_uInt16 nId, const SfxItemSet& rSet )
+{
+ if(nId == SID_SM_EDITOPTIONS)
+ {
+ GetConfig()->ItemSetToConfig(rSet);
+ }
+}
+
+std::unique_ptr<SfxTabPage> SmModule::CreateTabPage( sal_uInt16 nId, weld::Container* pPage, weld::DialogController* pController, const SfxItemSet& rSet )
+{
+ std::unique_ptr<SfxTabPage> xRet;
+ if (nId == SID_SM_TP_PRINTOPTIONS)
+ xRet = SmPrintOptionsTabPage::Create(pPage, pController, rSet);
+ return xRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/symbol.cxx b/starmath/source/symbol.cxx
new file mode 100644
index 000000000..5dcf17309
--- /dev/null
+++ b/starmath/source/symbol.cxx
@@ -0,0 +1,273 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <symbol.hxx>
+#include <utility.hxx>
+#include <cfgitem.hxx>
+#include <smmod.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+
+SmSym::SmSym() :
+ m_aName(OUString("unknown")),
+ m_aSetName(OUString("unknown")),
+ m_cChar('\0'),
+ m_bPredefined(false)
+{
+ m_aExportName = m_aName;
+ m_aFace.SetTransparent(true);
+ m_aFace.SetAlignment(ALIGN_BASELINE);
+}
+
+
+SmSym::SmSym(const SmSym& rSymbol)
+{
+ *this = rSymbol;
+}
+
+
+SmSym::SmSym(const OUString& rName, const vcl::Font& rFont, sal_UCS4 cChar,
+ const OUString& rSet, bool bIsPredefined)
+{
+ m_aName = m_aExportName = rName;
+
+ m_aFace = rFont;
+ m_aFace.SetTransparent(true);
+ m_aFace.SetAlignment(ALIGN_BASELINE);
+
+ m_cChar = cChar;
+ m_aSetName = rSet;
+ m_bPredefined = bIsPredefined;
+}
+
+
+SmSym& SmSym::operator = (const SmSym& rSymbol)
+{
+ m_aName = rSymbol.m_aName;
+ m_aExportName = rSymbol.m_aExportName;
+ m_cChar = rSymbol.m_cChar;
+ m_aFace = rSymbol.m_aFace;
+ m_aSetName = rSymbol.m_aSetName;
+ m_bPredefined = rSymbol.m_bPredefined;
+
+ SM_MOD()->GetSymbolManager().SetModified(true);
+
+ return *this;
+}
+
+
+bool SmSym::IsEqualInUI( const SmSym& rSymbol ) const
+{
+ return m_aName == rSymbol.m_aName &&
+ m_aFace == rSymbol.m_aFace &&
+ m_cChar == rSymbol.m_cChar;
+}
+
+/**************************************************************************/
+
+
+SmSymbolManager::SmSymbolManager()
+{
+ m_bModified = false;
+}
+
+
+SmSymbolManager::SmSymbolManager(const SmSymbolManager& rSymbolSetManager)
+{
+ m_aSymbols = rSymbolSetManager.m_aSymbols;
+ m_bModified = true;
+}
+
+
+SmSymbolManager::~SmSymbolManager()
+{
+}
+
+
+SmSymbolManager& SmSymbolManager::operator = (const SmSymbolManager& rSymbolSetManager)
+{
+ m_aSymbols = rSymbolSetManager.m_aSymbols;
+ m_bModified = true;
+ return *this;
+}
+
+
+SmSym *SmSymbolManager::GetSymbolByName(const OUString& rSymbolName)
+{
+ SmSym *pRes = nullptr;
+ SymbolMap_t::iterator aIt( m_aSymbols.find( rSymbolName ) );
+ if (aIt != m_aSymbols.end())
+ pRes = &aIt->second;
+ return pRes;
+}
+
+
+SymbolPtrVec_t SmSymbolManager::GetSymbols() const
+{
+ SymbolPtrVec_t aRes;
+ aRes.reserve(m_aSymbols.size());
+ for (const auto& rEntry : m_aSymbols)
+ aRes.push_back( &rEntry.second );
+// OSL_ENSURE( sSymbols.size() == m_aSymbols.size(), "number of symbols mismatch " );
+ return aRes;
+}
+
+
+bool SmSymbolManager::AddOrReplaceSymbol( const SmSym &rSymbol, bool bForceChange )
+{
+ bool bAdded = false;
+
+ const OUString& aSymbolName( rSymbol.GetName() );
+ if (!aSymbolName.isEmpty() && !rSymbol.GetSymbolSetName().isEmpty())
+ {
+ const SmSym *pFound = GetSymbolByName( aSymbolName );
+ const bool bSymbolConflict = pFound && !pFound->IsEqualInUI( rSymbol );
+
+ // avoid having the same symbol name twice but with different symbols in use
+ if (!pFound || bForceChange)
+ {
+ m_aSymbols[ aSymbolName ] = rSymbol;
+ bAdded = true;
+ }
+ else if (bSymbolConflict)
+ {
+ // TODO: to solve this a document owned symbol manager would be required ...
+ SAL_WARN("starmath", "symbol conflict, different symbol with same name found!");
+ // symbols in all formulas. A copy of the global one would be needed here
+ // and then the new symbol has to be forcefully applied. This would keep
+ // the current formula intact but will leave the set of symbols in the
+ // global symbol manager somewhat to chance.
+ }
+
+ OSL_ENSURE( bAdded, "failed to add symbol" );
+ if (bAdded)
+ m_bModified = true;
+ OSL_ENSURE( bAdded || (pFound && !bSymbolConflict), "AddOrReplaceSymbol: unresolved symbol conflict" );
+ }
+
+ return bAdded;
+}
+
+
+void SmSymbolManager::RemoveSymbol( const OUString & rSymbolName )
+{
+ if (!rSymbolName.isEmpty())
+ {
+ size_t nOldSize = m_aSymbols.size();
+ m_aSymbols.erase( rSymbolName );
+ m_bModified = nOldSize != m_aSymbols.size();
+ }
+}
+
+
+std::set< OUString > SmSymbolManager::GetSymbolSetNames() const
+{
+ std::set< OUString > aRes;
+ for (const auto& rEntry : m_aSymbols)
+ aRes.insert( rEntry.second.GetSymbolSetName() );
+ return aRes;
+}
+
+
+SymbolPtrVec_t SmSymbolManager::GetSymbolSet( std::u16string_view rSymbolSetName )
+{
+ SymbolPtrVec_t aRes;
+ if (!rSymbolSetName.empty())
+ {
+ for (const auto& rEntry : m_aSymbols)
+ {
+ if (rEntry.second.GetSymbolSetName() == rSymbolSetName)
+ aRes.push_back( &rEntry.second );
+ }
+ }
+ return aRes;
+}
+
+
+void SmSymbolManager::Load()
+{
+ std::vector< SmSym > aSymbols;
+ SmMathConfig &rCfg = *SM_MOD()->GetConfig();
+ rCfg.GetSymbols( aSymbols );
+ size_t nSymbolCount = aSymbols.size();
+
+ m_aSymbols.clear();
+ for (size_t i = 0; i < nSymbolCount; ++i)
+ {
+ const SmSym &rSym = aSymbols[i];
+ OSL_ENSURE( !rSym.GetName().isEmpty(), "symbol without name!" );
+ if (!rSym.GetName().isEmpty())
+ AddOrReplaceSymbol( rSym );
+ }
+ m_bModified = true;
+
+ if (0 == nSymbolCount)
+ {
+ SAL_WARN("starmath", "no symbol set found");
+ m_bModified = false;
+ }
+
+ // now add a %i... symbol to the 'iGreek' set for every symbol found in the 'Greek' set.
+ const OUString aGreekSymbolSetName(SmLocalizedSymbolData::GetUiSymbolSetName(u"Greek"));
+ const SymbolPtrVec_t aGreekSymbols( GetSymbolSet( aGreekSymbolSetName ) );
+ OUString aSymbolSetName = "i" + aGreekSymbolSetName;
+ size_t nSymbols = aGreekSymbols.size();
+ for (size_t i = 0; i < nSymbols; ++i)
+ {
+ // make the new symbol a copy but with ITALIC_NORMAL, and add it to iGreek
+ const SmSym &rSym = *aGreekSymbols[i];
+ vcl::Font aFont( rSym.GetFace() );
+ OSL_ENSURE( aFont.GetItalic() == ITALIC_NONE, "expected Font with ITALIC_NONE, failed." );
+ aFont.SetItalic( ITALIC_NORMAL );
+ OUString aSymbolName = "i" + rSym.GetName();
+ SmSym aSymbol( aSymbolName, aFont, rSym.GetCharacter(),
+ aSymbolSetName, true /*bIsPredefined*/ );
+
+ AddOrReplaceSymbol( aSymbol );
+ }
+}
+
+void SmSymbolManager::Save()
+{
+ if (!m_bModified)
+ return;
+
+ SmMathConfig &rCfg = *SM_MOD()->GetConfig();
+
+ // prepare to skip symbols from iGreek on saving
+ OUString aSymbolSetName = "i" +
+ SmLocalizedSymbolData::GetUiSymbolSetName(u"Greek");
+
+ SymbolPtrVec_t aTmp( GetSymbols() );
+ std::vector< SmSym > aSymbols;
+ for (const SmSym* i : aTmp)
+ {
+ // skip symbols from iGreek set since those symbols always get added
+ // by computational means in SmSymbolManager::Load
+ if (i->GetSymbolSetName() != aSymbolSetName)
+ aSymbols.push_back( *i );
+ }
+ rCfg.SetSymbols( aSymbols );
+
+ m_bModified = false;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/tmpdevice.cxx b/starmath/source/tmpdevice.cxx
new file mode 100644
index 000000000..fccb20f72
--- /dev/null
+++ b/starmath/source/tmpdevice.cxx
@@ -0,0 +1,66 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <smmod.hxx>
+#include "tmpdevice.hxx"
+
+#include <svtools/colorcfg.hxx>
+#include <sal/log.hxx>
+
+// SmTmpDevice
+// Allows for font and color changes. The original settings will be restored
+// in the destructor.
+// It's main purpose is to allow for the "const" in the 'OutputDevice'
+// argument in the 'Arrange' functions and restore changes made in the 'Draw'
+// functions.
+// Usually a MapMode of 1/100th mm will be used.
+
+SmTmpDevice::SmTmpDevice(OutputDevice &rTheDev, bool bUseMap100th_mm) :
+ rOutDev(rTheDev)
+{
+ rOutDev.Push(vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE |
+ vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR);
+ if (bUseMap100th_mm && MapUnit::Map100thMM != rOutDev.GetMapMode().GetMapUnit())
+ {
+ SAL_WARN("starmath", "incorrect MapMode?");
+ rOutDev.SetMapMode(MapMode(MapUnit::Map100thMM)); // format for 100% always
+ }
+}
+
+
+Color SmTmpDevice::GetTextColor(const Color& rTextColor)
+{
+ if (rTextColor == COL_AUTO)
+ {
+ Color aConfigFontColor = SM_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor;
+ Color aConfigDocColor = SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor;
+ return rOutDev.GetReadableFontColor(aConfigFontColor, aConfigDocColor);
+ }
+
+ return rTextColor;
+}
+
+
+void SmTmpDevice::SetFont(const vcl::Font &rNewFont)
+{
+ rOutDev.SetFont(rNewFont);
+ rOutDev.SetTextColor(GetTextColor(rNewFont.GetColor()));
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/tmpdevice.hxx b/starmath/source/tmpdevice.hxx
new file mode 100644
index 000000000..9c15fe035
--- /dev/null
+++ b/starmath/source/tmpdevice.hxx
@@ -0,0 +1,46 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/color.hxx>
+#include <vcl/outdev.hxx>
+
+class SmTmpDevice
+{
+ OutputDevice& rOutDev;
+
+ SmTmpDevice(const SmTmpDevice&) = delete;
+ SmTmpDevice& operator=(const SmTmpDevice&) = delete;
+
+ Color GetTextColor(const Color& rTextColor);
+
+public:
+ SmTmpDevice(OutputDevice& rTheDev, bool bUseMap100th_mm);
+ ~SmTmpDevice() COVERITY_NOEXCEPT_FALSE { rOutDev.Pop(); }
+
+ void SetFont(const vcl::Font& rNewFont);
+
+ void SetLineColor(const Color& rColor) { rOutDev.SetLineColor(GetTextColor(rColor)); }
+ void SetFillColor(const Color& rColor) { rOutDev.SetFillColor(GetTextColor(rColor)); }
+
+ operator OutputDevice&() { return rOutDev; }
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/typemap.cxx b/starmath/source/typemap.cxx
new file mode 100644
index 000000000..ba7910747
--- /dev/null
+++ b/starmath/source/typemap.cxx
@@ -0,0 +1,39 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_options.h>
+
+#include <sfx2/msg.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <svl/slstitm.hxx>
+
+#ifdef DISABLE_DYNLOADING
+/* Avoid clash with the ones from svx/source/form/typemap.cxx */
+#define aSfxInt16Item_Impl starmath_source_appl_typemap_aSfxInt16Item_Impl
+#endif
+
+#define SFX_TYPEMAP
+#include <smslots.hxx>
+
+#ifdef DISABLE_DYNLOADING
+#undef aSfxInt16Item_Impl
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unodoc.cxx b/starmath/source/unodoc.cxx
new file mode 100644
index 000000000..3c125077c
--- /dev/null
+++ b/starmath/source/unodoc.cxx
@@ -0,0 +1,46 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sfx2/sfxmodelfactory.hxx>
+
+#include <smdll.hxx>
+#include <document.hxx>
+#include <com/sun/star/frame/XModel.hpp>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+Math_FormulaDocument_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const& args)
+{
+ SolarMutexGuard aGuard;
+ SmGlobals::ensure();
+ css::uno::Reference<css::uno::XInterface> xInterface = sfx2::createSfxModelInstance(args,
+ [](SfxModelFlags _nCreationFlags)
+ {
+ SfxObjectShell* pShell = new SmDocShell( _nCreationFlags );
+ return pShell->GetModel();
+ });
+ xInterface->acquire();
+ return xInterface.get();
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unofilter.cxx b/starmath/source/unofilter.cxx
new file mode 100644
index 000000000..fd150005d
--- /dev/null
+++ b/starmath/source/unofilter.cxx
@@ -0,0 +1,116 @@
+/* -*- 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 <unotools/mediadescriptor.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <sot/storage.hxx>
+#include <cppuhelper/supportsservice.hxx>
+
+#include <document.hxx>
+#include "mathtype.hxx"
+#include <unomodel.hxx>
+#include <tools/diagnose_ex.h>
+
+using namespace ::com::sun::star;
+
+namespace
+{
+/// Invokes the MathType importer via UNO.
+class MathTypeFilter
+ : public cppu::WeakImplHelper<document::XFilter, document::XImporter, lang::XServiceInfo>
+{
+ uno::Reference<lang::XComponent> m_xDstDoc;
+
+public:
+ MathTypeFilter();
+
+ // XFilter
+ sal_Bool SAL_CALL filter(const uno::Sequence<beans::PropertyValue>& rDescriptor) override;
+ void SAL_CALL cancel() override;
+
+ // XImporter
+ void SAL_CALL setTargetDocument(const uno::Reference<lang::XComponent>& xDoc) override;
+
+ // XServiceInfo
+ OUString SAL_CALL getImplementationName() override;
+ sal_Bool SAL_CALL supportsService(const OUString& rServiceName) override;
+ uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+MathTypeFilter::MathTypeFilter() = default;
+
+sal_Bool MathTypeFilter::filter(const uno::Sequence<beans::PropertyValue>& rDescriptor)
+{
+ bool bSuccess = false;
+ try
+ {
+ utl::MediaDescriptor aMediaDesc(rDescriptor);
+ aMediaDesc.addInputStream();
+ uno::Reference<io::XInputStream> xInputStream;
+ aMediaDesc[utl::MediaDescriptor::PROP_INPUTSTREAM] >>= xInputStream;
+ std::unique_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream));
+ if (pStream)
+ {
+ if (SotStorage::IsStorageFile(pStream.get()))
+ {
+ tools::SvRef<SotStorage> aStorage(new SotStorage(pStream.get(), false));
+ // Is this a MathType Storage?
+ if (aStorage->IsStream("Equation Native"))
+ {
+ if (auto pModel = dynamic_cast<SmModel*>(m_xDstDoc.get()))
+ {
+ auto pDocShell = static_cast<SmDocShell*>(pModel->GetObjectShell());
+ OUStringBuffer aText(pDocShell->GetText());
+ MathType aEquation(aText);
+ bSuccess = aEquation.Parse(aStorage.get());
+ if (bSuccess)
+ {
+ pDocShell->SetText(aText.makeStringAndClear());
+ pDocShell->Parse();
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (const uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("starmath");
+ }
+ return bSuccess;
+}
+
+void MathTypeFilter::cancel() {}
+
+void MathTypeFilter::setTargetDocument(const uno::Reference<lang::XComponent>& xDoc)
+{
+ m_xDstDoc = xDoc;
+}
+
+OUString MathTypeFilter::getImplementationName() { return "com.sun.star.comp.Math.MathTypeFilter"; }
+
+sal_Bool MathTypeFilter::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence<OUString> MathTypeFilter::getSupportedServiceNames()
+{
+ return { "com.sun.star.document.ImportFilter" };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT uno::XInterface*
+com_sun_star_comp_Math_MathTypeFilter_get_implementation(uno::XComponentContext* /*pCtx*/,
+ uno::Sequence<uno::Any> const& /*rSeq*/)
+{
+ return cppu::acquire(new MathTypeFilter);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/unomodel.cxx b/starmath/source/unomodel.cxx
new file mode 100644
index 000000000..42f377f27
--- /dev/null
+++ b/starmath/source/unomodel.cxx
@@ -0,0 +1,1087 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <utility>
+
+#include <o3tl/any.hxx>
+#include <sfx2/printer.hxx>
+#include <svl/itemprop.hxx>
+#include <svl/itemset.hxx>
+#include <vcl/svapp.hxx>
+#include <unotools/localedatawrapper.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/print.hxx>
+#include <toolkit/awt/vclxdevice.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <com/sun/star/formula/SymbolDescriptor.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <comphelper/propertysetinfo.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/sequence.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <unotools/moduleoptions.hxx>
+#include <tools/mapunit.hxx>
+#include <tools/stream.hxx>
+
+#include <unomodel.hxx>
+#include <document.hxx>
+#include <view.hxx>
+#include <symbol.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <cfgitem.hxx>
+
+using namespace ::cppu;
+using namespace ::std;
+using namespace ::comphelper;
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::formula;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::script;
+
+SmPrintUIOptions::SmPrintUIOptions()
+{
+ SmModule *pp = SM_MOD();
+ SmMathConfig *pConfig = pp->GetConfig();
+ SAL_WARN_IF( !pConfig, "starmath", "SmConfig not found" );
+ if (!pConfig)
+ return;
+
+ sal_Int32 nNumProps = 10, nIdx=0;
+
+ // create sequence of print UI options
+ // (Actually IsIgnoreSpacesRight is a parser option. Without it we need only 8 properties here.)
+ m_aUIProperties.resize( nNumProps );
+
+ // load the math PrinterOptions into the custom tab
+ m_aUIProperties[nIdx].Name = "OptionsUIFile";
+ m_aUIProperties[nIdx++].Value <<= OUString("modules/smath/ui/printeroptions.ui");
+
+ // create Section for formula (results in an extra tab page in dialog)
+ SvtModuleOptions aOpt;
+ OUString aAppGroupname(
+ SmResId( RID_PRINTUIOPT_PRODNAME ).
+ replaceFirst( "%s", aOpt.GetModuleName( SvtModuleOptions::EModule::MATH ) ) );
+ m_aUIProperties[nIdx++].Value = setGroupControlOpt("tabcontrol-page2", aAppGroupname, ".HelpID:vcl:PrintDialog:TabPage:AppPage");
+
+ // create subgroup for print options
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("contents", SmResId( RID_PRINTUIOPT_CONTENTS ), OUString());
+
+ // create a bool option for title row (matches to SID_PRINTTITLE)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("title", SmResId( RID_PRINTUIOPT_TITLE ),
+ ".HelpID:vcl:PrintDialog:TitleRow:CheckBox",
+ PRTUIOPT_TITLE_ROW,
+ pConfig->IsPrintTitle());
+ // create a bool option for formula text (matches to SID_PRINTTEXT)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("formulatext", SmResId( RID_PRINTUIOPT_FRMLTXT ),
+ ".HelpID:vcl:PrintDialog:FormulaText:CheckBox",
+ PRTUIOPT_FORMULA_TEXT,
+ pConfig->IsPrintFormulaText());
+ // create a bool option for border (matches to SID_PRINTFRAME)
+ m_aUIProperties[nIdx++].Value = setBoolControlOpt("borders", SmResId( RID_PRINTUIOPT_BORDERS ),
+ ".HelpID:vcl:PrintDialog:Border:CheckBox",
+ PRTUIOPT_BORDER,
+ pConfig->IsPrintFrame());
+
+ // create subgroup for print format
+ m_aUIProperties[nIdx++].Value = setSubgroupControlOpt("size", SmResId( RID_PRINTUIOPT_SIZE ), OUString());
+
+ // create a radio button group for print format (matches to SID_PRINTSIZE)
+ Sequence< OUString > aChoices{
+ SmResId( RID_PRINTUIOPT_ORIGSIZE ),
+ SmResId( RID_PRINTUIOPT_FITTOPAGE ),
+ SmResId( RID_PRINTUIOPT_SCALING )
+ };
+ Sequence< OUString > aHelpIds{
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:0",
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:1",
+ ".HelpID:vcl:PrintDialog:PrintFormat:RadioButton:2"
+ };
+ Sequence< OUString > aWidgetIds{
+ "originalsize",
+ "fittopage",
+ "scaling"
+ };
+ OUString aPrintFormatProp( PRTUIOPT_PRINT_FORMAT );
+ m_aUIProperties[nIdx++].Value = setChoiceRadiosControlOpt(aWidgetIds, OUString(),
+ aHelpIds,
+ aPrintFormatProp,
+ aChoices, static_cast< sal_Int32 >(pConfig->GetPrintSize())
+ );
+
+ // create a numeric box for scale dependent on PrintFormat = "Scaling" (matches to SID_PRINTZOOM)
+ vcl::PrinterOptionsHelper::UIControlOptions aRangeOpt( aPrintFormatProp, 2, true );
+ m_aUIProperties[nIdx++].Value = setRangeControlOpt("scalingspin", OUString(),
+ ".HelpID:vcl:PrintDialog:PrintScale:NumericField",
+ PRTUIOPT_PRINT_SCALE,
+ pConfig->GetPrintZoomFactor(), // initial value
+ 10, // min value
+ 1000, // max value
+ aRangeOpt);
+
+ Sequence aHintNoLayoutPage{ comphelper::makePropertyValue("HintNoLayoutPage", true) };
+ m_aUIProperties[nIdx++].Value <<= aHintNoLayoutPage;
+
+ assert(nIdx == nNumProps);
+}
+
+
+
+namespace {
+
+enum SmModelPropertyHandles
+{
+ HANDLE_FORMULA,
+ HANDLE_FONT_NAME_VARIABLES,
+ HANDLE_FONT_NAME_FUNCTIONS,
+ HANDLE_FONT_NAME_NUMBERS,
+ HANDLE_FONT_NAME_TEXT,
+ HANDLE_CUSTOM_FONT_NAME_SERIF,
+ HANDLE_CUSTOM_FONT_NAME_SANS,
+ HANDLE_CUSTOM_FONT_NAME_FIXED,
+ HANDLE_CUSTOM_FONT_FIXED_POSTURE,
+ HANDLE_CUSTOM_FONT_FIXED_WEIGHT,
+ HANDLE_CUSTOM_FONT_SANS_POSTURE,
+ HANDLE_CUSTOM_FONT_SANS_WEIGHT,
+ HANDLE_CUSTOM_FONT_SERIF_POSTURE,
+ HANDLE_CUSTOM_FONT_SERIF_WEIGHT,
+ HANDLE_FONT_VARIABLES_POSTURE,
+ HANDLE_FONT_VARIABLES_WEIGHT,
+ HANDLE_FONT_FUNCTIONS_POSTURE,
+ HANDLE_FONT_FUNCTIONS_WEIGHT,
+ HANDLE_FONT_NUMBERS_POSTURE,
+ HANDLE_FONT_NUMBERS_WEIGHT,
+ HANDLE_FONT_TEXT_POSTURE,
+ HANDLE_FONT_TEXT_WEIGHT,
+ HANDLE_BASE_FONT_HEIGHT,
+ HANDLE_RELATIVE_FONT_HEIGHT_TEXT,
+ HANDLE_RELATIVE_FONT_HEIGHT_INDICES,
+ HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS,
+ HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS,
+ HANDLE_RELATIVE_FONT_HEIGHT_LIMITS,
+ HANDLE_IS_TEXT_MODE,
+ HANDLE_GREEK_CHAR_STYLE,
+ HANDLE_ALIGNMENT,
+ HANDLE_RELATIVE_SPACING,
+ HANDLE_RELATIVE_LINE_SPACING,
+ HANDLE_RELATIVE_ROOT_SPACING,
+ HANDLE_RELATIVE_INDEX_SUPERSCRIPT,
+ HANDLE_RELATIVE_INDEX_SUBSCRIPT,
+ HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT,
+ HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH,
+ HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH,
+ HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT,
+ HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE,
+ HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE,
+ HANDLE_RELATIVE_BRACKET_EXCESS_SIZE,
+ HANDLE_RELATIVE_BRACKET_DISTANCE,
+ HANDLE_IS_SCALE_ALL_BRACKETS,
+ HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE,
+ HANDLE_RELATIVE_MATRIX_LINE_SPACING,
+ HANDLE_RELATIVE_MATRIX_COLUMN_SPACING,
+ HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT,
+ HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT,
+ HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE,
+ HANDLE_RELATIVE_OPERATOR_SPACING,
+ HANDLE_LEFT_MARGIN,
+ HANDLE_RIGHT_MARGIN,
+ HANDLE_TOP_MARGIN,
+ HANDLE_BOTTOM_MARGIN,
+ HANDLE_PRINTER_NAME,
+ HANDLE_PRINTER_SETUP,
+ HANDLE_SYMBOLS,
+ HANDLE_SAVE_THUMBNAIL,
+ HANDLE_USED_SYMBOLS,
+ HANDLE_BASIC_LIBRARIES,
+ HANDLE_RUNTIME_UID,
+ HANDLE_LOAD_READONLY, // Security Options
+ HANDLE_DIALOG_LIBRARIES, // #i73329#
+ HANDLE_BASELINE,
+ HANDLE_INTEROP_GRAB_BAG,
+ HANDLE_STARMATH_VERSION
+};
+
+}
+
+static const rtl::Reference<PropertySetInfo> & lcl_createModelPropertyInfo ()
+{
+ static const PropertyMapEntry aModelPropertyInfoMap[] =
+ {
+ { OUString("Alignment") , HANDLE_ALIGNMENT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("BaseFontHeight") , HANDLE_BASE_FONT_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("BasicLibraries") , HANDLE_BASIC_LIBRARIES , cppu::UnoType<script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("BottomMargin") , HANDLE_BOTTOM_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BOTTOMSPACE },
+ { OUString("CustomFontNameFixed") , HANDLE_CUSTOM_FONT_NAME_FIXED , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("CustomFontNameSans") , HANDLE_CUSTOM_FONT_NAME_SANS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("CustomFontNameSerif") , HANDLE_CUSTOM_FONT_NAME_SERIF , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("DialogLibraries") , HANDLE_DIALOG_LIBRARIES , cppu::UnoType<script::XLibraryContainer>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("FontFixedIsBold") , HANDLE_CUSTOM_FONT_FIXED_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("FontFixedIsItalic") , HANDLE_CUSTOM_FONT_FIXED_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FIXED },
+ { OUString("FontFunctionsIsBold") , HANDLE_FONT_FUNCTIONS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontFunctionsIsItalic") , HANDLE_FONT_FUNCTIONS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontNameFunctions") , HANDLE_FONT_NAME_FUNCTIONS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_FUNCTION },
+ { OUString("FontNameNumbers") , HANDLE_FONT_NAME_NUMBERS , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontNameText") , HANDLE_FONT_NAME_TEXT , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontNameVariables") , HANDLE_FONT_NAME_VARIABLES , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("FontNumbersIsBold") , HANDLE_FONT_NUMBERS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontNumbersIsItalic") , HANDLE_FONT_NUMBERS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_NUMBER },
+ { OUString("FontSansIsBold") , HANDLE_CUSTOM_FONT_SANS_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("FontSansIsItalic") , HANDLE_CUSTOM_FONT_SANS_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SANS },
+ { OUString("FontSerifIsBold") , HANDLE_CUSTOM_FONT_SERIF_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("FontSerifIsItalic") , HANDLE_CUSTOM_FONT_SERIF_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_SERIF },
+ { OUString("FontTextIsBold") , HANDLE_FONT_TEXT_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontTextIsItalic") , HANDLE_FONT_TEXT_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_TEXT },
+ { OUString("FontVariablesIsBold") , HANDLE_FONT_VARIABLES_WEIGHT , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("FontVariablesIsItalic") , HANDLE_FONT_VARIABLES_POSTURE , cppu::UnoType<bool>::get(), PROPERTY_NONE, FNT_VARIABLE },
+ { OUString("Formula") , HANDLE_FORMULA , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { OUString("IsScaleAllBrackets") , HANDLE_IS_SCALE_ALL_BRACKETS , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("IsTextMode") , HANDLE_IS_TEXT_MODE , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("GreekCharStyle") , HANDLE_GREEK_CHAR_STYLE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("LeftMargin") , HANDLE_LEFT_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_LEFTSPACE },
+ { OUString("PrinterName") , HANDLE_PRINTER_NAME , ::cppu::UnoType<OUString>::get(), PROPERTY_NONE, 0 },
+ { OUString("PrinterSetup") , HANDLE_PRINTER_SETUP , cppu::UnoType<const Sequence < sal_Int8 >>::get(), PROPERTY_NONE, 0 },
+ { OUString("RelativeBracketDistance") , HANDLE_RELATIVE_BRACKET_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BRACKETSPACE },
+ { OUString("RelativeBracketExcessSize") , HANDLE_RELATIVE_BRACKET_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_BRACKETSIZE },
+ { OUString("RelativeFontHeightFunctions") , HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_FUNCTION },
+ { OUString("RelativeFontHeightIndices") , HANDLE_RELATIVE_FONT_HEIGHT_INDICES , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_INDEX },
+ { OUString("RelativeFontHeightLimits") , HANDLE_RELATIVE_FONT_HEIGHT_LIMITS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_LIMITS },
+ { OUString("RelativeFontHeightOperators") , HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_OPERATOR },
+ { OUString("RelativeFontHeightText") , HANDLE_RELATIVE_FONT_HEIGHT_TEXT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, SIZ_TEXT },
+ { OUString("RelativeFractionBarExcessLength") , HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_FRACTION },
+ { OUString("RelativeFractionBarLineWeight") , HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_STROKEWIDTH },
+ { OUString("RelativeFractionDenominatorDepth") , HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH, ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_DENOMINATOR },
+ { OUString("RelativeFractionNumeratorHeight") , HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_NUMERATOR },
+ { OUString("RelativeIndexSubscript") , HANDLE_RELATIVE_INDEX_SUBSCRIPT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_SUBSCRIPT },
+ { OUString("RelativeIndexSuperscript") , HANDLE_RELATIVE_INDEX_SUPERSCRIPT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_SUPERSCRIPT },
+ { OUString("RelativeLineSpacing") , HANDLE_RELATIVE_LINE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_VERTICAL },
+ { OUString("RelativeLowerLimitDistance") , HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_LOWERLIMIT },
+ { OUString("RelativeMatrixColumnSpacing") , HANDLE_RELATIVE_MATRIX_COLUMN_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_MATRIXCOL },
+ { OUString("RelativeMatrixLineSpacing") , HANDLE_RELATIVE_MATRIX_LINE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_MATRIXROW },
+ { OUString("RelativeOperatorExcessSize") , HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_OPERATORSIZE },
+ { OUString("RelativeOperatorSpacing") , HANDLE_RELATIVE_OPERATOR_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_OPERATORSPACE },
+ { OUString("RelativeRootSpacing") , HANDLE_RELATIVE_ROOT_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ROOT },
+ { OUString("RelativeScaleBracketExcessSize") , HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_NORMALBRACKETSIZE },
+ { OUString("RelativeSpacing") , HANDLE_RELATIVE_SPACING , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_HORIZONTAL },
+ { OUString("RelativeSymbolMinimumHeight") , HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ORNAMENTSPACE },
+ { OUString("RelativeSymbolPrimaryHeight") , HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_ORNAMENTSIZE },
+ { OUString("RelativeUpperLimitDistance") , HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_UPPERLIMIT },
+ { OUString("RightMargin") , HANDLE_RIGHT_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_RIGHTSPACE },
+ { OUString("RuntimeUID") , HANDLE_RUNTIME_UID , cppu::UnoType<OUString>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("SaveThumbnail") , HANDLE_SAVE_THUMBNAIL , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ { OUString("Symbols") , HANDLE_SYMBOLS , cppu::UnoType<Sequence < SymbolDescriptor >>::get(), PROPERTY_NONE, 0 },
+ { OUString("UserDefinedSymbolsInUse") , HANDLE_USED_SYMBOLS , cppu::UnoType<Sequence < SymbolDescriptor >>::get(), PropertyAttribute::READONLY, 0 },
+ { OUString("TopMargin") , HANDLE_TOP_MARGIN , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, DIS_TOPSPACE },
+ // #i33095# Security Options
+ { OUString("LoadReadonly") , HANDLE_LOAD_READONLY , cppu::UnoType<bool>::get(), PROPERTY_NONE, 0 },
+ // #i972#
+ { OUString("BaseLine") , HANDLE_BASELINE , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ { OUString("InteropGrabBag") , HANDLE_INTEROP_GRAB_BAG , cppu::UnoType<uno::Sequence< beans::PropertyValue >>::get(), PROPERTY_NONE, 0 },
+ { OUString("SyntaxVersion") , HANDLE_STARMATH_VERSION , ::cppu::UnoType<sal_Int16>::get(), PROPERTY_NONE, 0 },
+ };
+ static const rtl::Reference<PropertySetInfo> PROPS_INFO = new PropertySetInfo ( aModelPropertyInfoMap );
+ return PROPS_INFO;
+}
+
+SmModel::SmModel( SfxObjectShell *pObjSh )
+: SfxBaseModel(pObjSh)
+, PropertySetHelper ( lcl_createModelPropertyInfo () )
+{
+}
+
+SmModel::~SmModel() noexcept
+{
+}
+
+uno::Any SAL_CALL SmModel::queryInterface( const uno::Type& rType )
+{
+ uno::Any aRet = ::cppu::queryInterface ( rType,
+ // OWeakObject interfaces
+ &dynamic_cast<XInterface&>(static_cast<XUnoTunnel&>(*this)),
+ static_cast< XWeak* > ( this ),
+ // PropertySetHelper interfaces
+ static_cast< XPropertySet* > ( this ),
+ static_cast< XMultiPropertySet* > ( this ),
+ // my own interfaces
+ static_cast< XServiceInfo* > ( this ),
+ static_cast< XRenderable* > ( this ) );
+ if (!aRet.hasValue())
+ aRet = SfxBaseModel::queryInterface ( rType );
+ return aRet;
+}
+
+void SAL_CALL SmModel::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL SmModel::release() noexcept
+{
+ OWeakObject::release();
+}
+
+uno::Sequence< uno::Type > SAL_CALL SmModel::getTypes( )
+{
+ return comphelper::concatSequences(SfxBaseModel::getTypes(),
+ uno::Sequence {
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XRenderable>::get() });
+}
+
+const uno::Sequence< sal_Int8 > & SmModel::getUnoTunnelId()
+{
+ static const comphelper::UnoIdInit theSmModelUnoTunnelId;
+ return theSmModelUnoTunnelId.getSeq();
+}
+
+sal_Int64 SAL_CALL SmModel::getSomething( const uno::Sequence< sal_Int8 >& rId )
+{
+ return comphelper::getSomethingImpl(rId, this,
+ comphelper::FallbackToGetSomethingOf<SfxBaseModel>{});
+}
+
+static sal_Int16 lcl_AnyToINT16(const uno::Any& rAny)
+{
+ sal_Int16 nRet = 0;
+ if( auto x = o3tl::tryAccess<double>(rAny) )
+ nRet = static_cast<sal_Int16>(*x);
+ else
+ rAny >>= nRet;
+ return nRet;
+}
+
+OUString SmModel::getImplementationName()
+{
+ return "com.sun.star.comp.Math.FormulaDocument";
+}
+
+sal_Bool SmModel::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+uno::Sequence< OUString > SmModel::getSupportedServiceNames()
+{
+ static constexpr OUStringLiteral service1 = u"com.sun.star.document.OfficeDocument";
+ static constexpr OUStringLiteral service2 = u"com.sun.star.formula.FormulaProperties";
+ return uno::Sequence<OUString>{ service1, service2 };
+}
+
+void SmModel::_setPropertyValues(const PropertyMapEntry** ppEntries, const Any* pValues)
+{
+ SolarMutexGuard aGuard;
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * > (GetObjectShell());
+
+ if ( nullptr == pDocSh )
+ throw UnknownPropertyException();
+
+ SmFormat aFormat = pDocSh->GetFormat();
+
+ for (; *ppEntries; ppEntries++, pValues++ )
+ {
+ if ((*ppEntries)->mnAttributes & PropertyAttribute::READONLY)
+ throw PropertyVetoException();
+
+ switch ( (*ppEntries)->mnHandle )
+ {
+ case HANDLE_FORMULA:
+ {
+ OUString aText;
+ *pValues >>= aText;
+ pDocSh->SetText(aText);
+ }
+ break;
+ case HANDLE_FONT_NAME_VARIABLES :
+ case HANDLE_FONT_NAME_FUNCTIONS :
+ case HANDLE_FONT_NAME_NUMBERS :
+ case HANDLE_FONT_NAME_TEXT :
+ case HANDLE_CUSTOM_FONT_NAME_SERIF :
+ case HANDLE_CUSTOM_FONT_NAME_SANS :
+ case HANDLE_CUSTOM_FONT_NAME_FIXED :
+ {
+ OUString sFontName;
+ *pValues >>= sFontName;
+ if(sFontName.isEmpty())
+ throw IllegalArgumentException();
+
+ if(aFormat.GetFont((*ppEntries)->mnMemberId).GetFamilyName() != sFontName)
+ {
+ const SmFace rOld = aFormat.GetFont((*ppEntries)->mnMemberId);
+
+ SmFace aSet( sFontName, rOld.GetFontSize() );
+ aSet.SetBorderWidth( rOld.GetBorderWidth() );
+ aSet.SetAlignment( ALIGN_BASELINE );
+ aFormat.SetFont( (*ppEntries)->mnMemberId, aSet );
+ }
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_POSTURE:
+ case HANDLE_CUSTOM_FONT_SANS_POSTURE :
+ case HANDLE_CUSTOM_FONT_SERIF_POSTURE:
+ case HANDLE_FONT_VARIABLES_POSTURE :
+ case HANDLE_FONT_FUNCTIONS_POSTURE :
+ case HANDLE_FONT_NUMBERS_POSTURE :
+ case HANDLE_FONT_TEXT_POSTURE :
+ {
+ auto bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal)
+ throw IllegalArgumentException();
+ vcl::Font aNewFont(aFormat.GetFont((*ppEntries)->mnMemberId));
+ aNewFont.SetItalic(*bVal ? ITALIC_NORMAL : ITALIC_NONE);
+ aFormat.SetFont((*ppEntries)->mnMemberId, aNewFont);
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SANS_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SERIF_WEIGHT :
+ case HANDLE_FONT_VARIABLES_WEIGHT :
+ case HANDLE_FONT_FUNCTIONS_WEIGHT :
+ case HANDLE_FONT_NUMBERS_WEIGHT :
+ case HANDLE_FONT_TEXT_WEIGHT :
+ {
+ auto bVal = o3tl::tryAccess<bool>(*pValues);
+ if(!bVal)
+ throw IllegalArgumentException();
+ vcl::Font aNewFont(aFormat.GetFont((*ppEntries)->mnMemberId));
+ aNewFont.SetWeight(*bVal ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ aFormat.SetFont((*ppEntries)->mnMemberId, aNewFont);
+ }
+ break;
+ case HANDLE_BASE_FONT_HEIGHT :
+ {
+ // Point!
+ sal_Int16 nVal = lcl_AnyToINT16(*pValues);
+ if(nVal < 1)
+ throw IllegalArgumentException();
+ Size aSize = aFormat.GetBaseSize();
+ aSize.setHeight( SmPtsTo100th_mm(nVal) );
+ aFormat.SetBaseSize(aSize);
+
+ // apply base size to fonts
+ const Size aTmp( aFormat.GetBaseSize() );
+ for (sal_uInt16 i = FNT_BEGIN; i <= FNT_END; i++)
+ aFormat.SetFontSize(i, aTmp);
+ }
+ break;
+ case HANDLE_RELATIVE_FONT_HEIGHT_TEXT :
+ case HANDLE_RELATIVE_FONT_HEIGHT_INDICES :
+ case HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_LIMITS :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 1)
+ throw IllegalArgumentException();
+ aFormat.SetRelSize((*ppEntries)->mnMemberId, nVal);
+ }
+ break;
+
+ case HANDLE_IS_TEXT_MODE :
+ {
+ aFormat.SetTextmode(*o3tl::doAccess<bool>(*pValues));
+ }
+ break;
+
+ case HANDLE_GREEK_CHAR_STYLE :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if (nVal < 0 || nVal > 2)
+ throw IllegalArgumentException();
+ aFormat.SetGreekCharStyle( nVal );
+ }
+ break;
+
+ case HANDLE_ALIGNMENT :
+ {
+ // SmHorAlign uses the same values as HorizontalAlignment
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 0 || nVal > 2)
+ throw IllegalArgumentException();
+ aFormat.SetHorAlign(static_cast<SmHorAlign>(nVal));
+ }
+ break;
+
+ case HANDLE_RELATIVE_SPACING :
+ case HANDLE_RELATIVE_LINE_SPACING :
+ case HANDLE_RELATIVE_ROOT_SPACING :
+ case HANDLE_RELATIVE_INDEX_SUPERSCRIPT :
+ case HANDLE_RELATIVE_INDEX_SUBSCRIPT :
+ case HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT :
+ case HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT :
+ case HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_BRACKET_DISTANCE :
+ case HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_MATRIX_LINE_SPACING :
+ case HANDLE_RELATIVE_MATRIX_COLUMN_SPACING :
+ case HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT :
+ case HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT :
+ case HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE :
+ case HANDLE_RELATIVE_OPERATOR_SPACING :
+ case HANDLE_LEFT_MARGIN :
+ case HANDLE_RIGHT_MARGIN :
+ case HANDLE_TOP_MARGIN :
+ case HANDLE_BOTTOM_MARGIN :
+ {
+ sal_Int16 nVal = 0;
+ *pValues >>= nVal;
+ if(nVal < 0)
+ throw IllegalArgumentException();
+ aFormat.SetDistance((*ppEntries)->mnMemberId, nVal);
+ }
+ break;
+ case HANDLE_IS_SCALE_ALL_BRACKETS :
+ aFormat.SetScaleNormalBrackets(*o3tl::doAccess<bool>(*pValues));
+ break;
+ case HANDLE_PRINTER_NAME:
+ {
+ // embedded documents just ignore this property for now
+ if ( pDocSh->GetCreateMode() != SfxObjectCreateMode::EMBEDDED )
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ( );
+ if (pPrinter)
+ {
+ OUString sPrinterName;
+ if ( !(*pValues >>= sPrinterName) )
+ throw IllegalArgumentException();
+
+ if ( !sPrinterName.isEmpty() )
+ {
+ VclPtrInstance<SfxPrinter> pNewPrinter( pPrinter->GetOptions().Clone(), sPrinterName );
+ if (pNewPrinter->IsKnown())
+ pDocSh->SetPrinter ( pNewPrinter );
+ else
+ pNewPrinter.disposeAndClear();
+ }
+ }
+ }
+ }
+ break;
+ case HANDLE_PRINTER_SETUP:
+ {
+ Sequence < sal_Int8 > aSequence;
+ if ( !(*pValues >>= aSequence) )
+ throw IllegalArgumentException();
+
+ sal_uInt32 nSize = aSequence.getLength();
+ SvMemoryStream aStream ( aSequence.getArray(), nSize, StreamMode::READ );
+ aStream.Seek ( STREAM_SEEK_TO_BEGIN );
+ auto pItemSet = std::make_unique<SfxItemSetFixed<
+ SID_PRINTTITLE, SID_PRINTTITLE,
+ SID_PRINTTEXT, SID_PRINTTEXT,
+ SID_PRINTFRAME, SID_PRINTFRAME,
+ SID_PRINTSIZE, SID_PRINTSIZE,
+ SID_PRINTZOOM, SID_PRINTZOOM,
+ SID_NO_RIGHT_SPACES, SID_NO_RIGHT_SPACES,
+ SID_SAVE_ONLY_USED_SYMBOLS, SID_SAVE_ONLY_USED_SYMBOLS,
+ SID_AUTO_CLOSE_BRACKETS, SID_SMEDITWINDOWZOOM>> ( SmDocShell::GetPool() );
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ConfigToItemSet(*pItemSet);
+ VclPtr<SfxPrinter> pPrinter = SfxPrinter::Create ( aStream, std::move(pItemSet) );
+
+ pDocSh->SetPrinter( pPrinter );
+ }
+ break;
+ case HANDLE_SYMBOLS:
+ {
+ // this is set
+ Sequence < SymbolDescriptor > aSequence;
+ if ( !(*pValues >>= aSequence) )
+ throw IllegalArgumentException();
+
+ SmModule *pp = SM_MOD();
+ SmSymbolManager &rManager = pp->GetSymbolManager();
+ for (const SymbolDescriptor& rDescriptor : std::as_const(aSequence))
+ {
+ vcl::Font aFont;
+ aFont.SetFamilyName ( rDescriptor.sFontName );
+ aFont.SetCharSet ( static_cast < rtl_TextEncoding > (rDescriptor.nCharSet) );
+ aFont.SetFamily ( static_cast < FontFamily > (rDescriptor.nFamily ) );
+ aFont.SetPitch ( static_cast < FontPitch > (rDescriptor.nPitch ) );
+ aFont.SetWeight ( static_cast < FontWeight > (rDescriptor.nWeight ) );
+ aFont.SetItalic ( static_cast < FontItalic > (rDescriptor.nItalic ) );
+ SmSym aSymbol ( rDescriptor.sName, aFont, static_cast < sal_Unicode > (rDescriptor.nCharacter),
+ rDescriptor.sSymbolSet );
+ aSymbol.SetExportName ( rDescriptor.sExportName );
+ rManager.AddOrReplaceSymbol ( aSymbol );
+ }
+ }
+ break;
+ // #i33095# Security Options
+ case HANDLE_LOAD_READONLY :
+ {
+ if ( (*pValues).getValueType() != cppu::UnoType<bool>::get() )
+ throw IllegalArgumentException();
+ bool bReadonly = false;
+ if ( *pValues >>= bReadonly )
+ pDocSh->SetLoadReadonly( bReadonly );
+ break;
+ }
+ case HANDLE_INTEROP_GRAB_BAG:
+ setGrabBagItem(*pValues);
+ break;
+ case HANDLE_SAVE_THUMBNAIL:
+ {
+ if ((*pValues).getValueType() != cppu::UnoType<bool>::get())
+ throw IllegalArgumentException();
+ bool bThumbnail = false;
+ if (*pValues >>= bThumbnail)
+ pDocSh->SetUseThumbnailSave(bThumbnail);
+ }
+ break;
+ case HANDLE_STARMATH_VERSION:
+ pDocSh->SetSmSyntaxVersion(pValues->get<sal_uInt16>());
+ break;
+ }
+ }
+
+ pDocSh->SetFormat( aFormat );
+
+ // #i67283# since about all of the above changes are likely to change
+ // the formula size we have to recalculate the vis-area now
+ pDocSh->SetVisArea( tools::Rectangle( Point(0, 0), pDocSh->GetSize() ) );
+}
+
+void SmModel::_getPropertyValues( const PropertyMapEntry **ppEntries, Any *pValue )
+{
+ SmDocShell *pDocSh = static_cast < SmDocShell * > (GetObjectShell());
+
+ if ( nullptr == pDocSh )
+ throw UnknownPropertyException();
+
+ const SmFormat & aFormat = pDocSh->GetFormat();
+
+ for (; *ppEntries; ppEntries++, pValue++ )
+ {
+ switch ( (*ppEntries)->mnHandle )
+ {
+ case HANDLE_FORMULA:
+ *pValue <<= pDocSh->GetText();
+ break;
+ case HANDLE_FONT_NAME_VARIABLES :
+ case HANDLE_FONT_NAME_FUNCTIONS :
+ case HANDLE_FONT_NAME_NUMBERS :
+ case HANDLE_FONT_NAME_TEXT :
+ case HANDLE_CUSTOM_FONT_NAME_SERIF :
+ case HANDLE_CUSTOM_FONT_NAME_SANS :
+ case HANDLE_CUSTOM_FONT_NAME_FIXED :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= rFace.GetFamilyName();
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_POSTURE:
+ case HANDLE_CUSTOM_FONT_SANS_POSTURE :
+ case HANDLE_CUSTOM_FONT_SERIF_POSTURE:
+ case HANDLE_FONT_VARIABLES_POSTURE :
+ case HANDLE_FONT_FUNCTIONS_POSTURE :
+ case HANDLE_FONT_NUMBERS_POSTURE :
+ case HANDLE_FONT_TEXT_POSTURE :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= IsItalic( rFace );
+ }
+ break;
+ case HANDLE_CUSTOM_FONT_FIXED_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SANS_WEIGHT :
+ case HANDLE_CUSTOM_FONT_SERIF_WEIGHT :
+ case HANDLE_FONT_VARIABLES_WEIGHT :
+ case HANDLE_FONT_FUNCTIONS_WEIGHT :
+ case HANDLE_FONT_NUMBERS_WEIGHT :
+ case HANDLE_FONT_TEXT_WEIGHT :
+ {
+ const SmFace & rFace = aFormat.GetFont((*ppEntries)->mnMemberId);
+ *pValue <<= IsBold( rFace );
+ }
+ break;
+ case HANDLE_BASE_FONT_HEIGHT :
+ {
+ // Point!
+ *pValue <<= sal_Int16(
+ SmRoundFraction(
+ Sm100th_mmToPts(aFormat.GetBaseSize().Height())));
+ }
+ break;
+ case HANDLE_RELATIVE_FONT_HEIGHT_TEXT :
+ case HANDLE_RELATIVE_FONT_HEIGHT_INDICES :
+ case HANDLE_RELATIVE_FONT_HEIGHT_FUNCTIONS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_OPERATORS :
+ case HANDLE_RELATIVE_FONT_HEIGHT_LIMITS :
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetRelSize((*ppEntries)->mnMemberId));
+ break;
+
+ case HANDLE_IS_TEXT_MODE :
+ *pValue <<= aFormat.IsTextmode();
+ break;
+
+ case HANDLE_GREEK_CHAR_STYLE :
+ *pValue <<= aFormat.GetGreekCharStyle();
+ break;
+
+ case HANDLE_ALIGNMENT :
+ // SmHorAlign uses the same values as HorizontalAlignment
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetHorAlign());
+ break;
+
+ case HANDLE_RELATIVE_SPACING :
+ case HANDLE_RELATIVE_LINE_SPACING :
+ case HANDLE_RELATIVE_ROOT_SPACING :
+ case HANDLE_RELATIVE_INDEX_SUPERSCRIPT :
+ case HANDLE_RELATIVE_INDEX_SUBSCRIPT :
+ case HANDLE_RELATIVE_FRACTION_NUMERATOR_HEIGHT :
+ case HANDLE_RELATIVE_FRACTION_DENOMINATOR_DEPTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_EXCESS_LENGTH:
+ case HANDLE_RELATIVE_FRACTION_BAR_LINE_WEIGHT :
+ case HANDLE_RELATIVE_UPPER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_LOWER_LIMIT_DISTANCE :
+ case HANDLE_RELATIVE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_BRACKET_DISTANCE :
+ case HANDLE_RELATIVE_SCALE_BRACKET_EXCESS_SIZE :
+ case HANDLE_RELATIVE_MATRIX_LINE_SPACING :
+ case HANDLE_RELATIVE_MATRIX_COLUMN_SPACING :
+ case HANDLE_RELATIVE_SYMBOL_PRIMARY_HEIGHT :
+ case HANDLE_RELATIVE_SYMBOL_MINIMUM_HEIGHT :
+ case HANDLE_RELATIVE_OPERATOR_EXCESS_SIZE :
+ case HANDLE_RELATIVE_OPERATOR_SPACING :
+ case HANDLE_LEFT_MARGIN :
+ case HANDLE_RIGHT_MARGIN :
+ case HANDLE_TOP_MARGIN :
+ case HANDLE_BOTTOM_MARGIN :
+ *pValue <<= static_cast<sal_Int16>(aFormat.GetDistance((*ppEntries)->mnMemberId));
+ break;
+ case HANDLE_IS_SCALE_ALL_BRACKETS :
+ *pValue <<= aFormat.IsScaleNormalBrackets();
+ break;
+ case HANDLE_PRINTER_NAME:
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ( );
+ *pValue <<= pPrinter ? pPrinter->GetName() : OUString();
+ }
+ break;
+ case HANDLE_PRINTER_SETUP:
+ {
+ SfxPrinter *pPrinter = pDocSh->GetPrinter ();
+ if (pPrinter)
+ {
+ SvMemoryStream aStream;
+ pPrinter->Store( aStream );
+ sal_uInt32 nSize = aStream.TellEnd();
+ aStream.Seek ( STREAM_SEEK_TO_BEGIN );
+ Sequence < sal_Int8 > aSequence ( nSize );
+ aStream.ReadBytes(aSequence.getArray(), nSize);
+ *pValue <<= aSequence;
+ }
+ }
+ break;
+ case HANDLE_SYMBOLS:
+ case HANDLE_USED_SYMBOLS:
+ {
+ const bool bUsedSymbolsOnly = (*ppEntries)->mnHandle == HANDLE_USED_SYMBOLS;
+ const std::set< OUString > &rUsedSymbols = pDocSh->GetUsedSymbols();
+
+ // this is get
+ SmModule *pp = SM_MOD();
+ const SmSymbolManager &rManager = pp->GetSymbolManager();
+ vector < const SmSym * > aVector;
+
+ const SymbolPtrVec_t aSymbols( rManager.GetSymbols() );
+ for (const SmSym* pSymbol : aSymbols)
+ {
+ if (pSymbol && !pSymbol->IsPredefined() &&
+ (!bUsedSymbolsOnly ||
+ rUsedSymbols.find( pSymbol->GetName() ) != rUsedSymbols.end()))
+ aVector.push_back ( pSymbol );
+ }
+ Sequence < SymbolDescriptor > aSequence ( aVector.size() );
+ SymbolDescriptor * pDescriptor = aSequence.getArray();
+
+ for (const SmSym* pSymbol : aVector)
+ {
+ pDescriptor->sName = pSymbol->GetName();
+ pDescriptor->sExportName = pSymbol->GetExportName();
+ pDescriptor->sSymbolSet = pSymbol->GetSymbolSetName();
+ pDescriptor->nCharacter = static_cast < sal_Int32 > (pSymbol->GetCharacter());
+
+ vcl::Font rFont = pSymbol->GetFace();
+ pDescriptor->sFontName = rFont.GetFamilyName();
+ pDescriptor->nCharSet = sal::static_int_cast< sal_Int16 >(rFont.GetCharSet());
+ pDescriptor->nFamily = sal::static_int_cast< sal_Int16 >(rFont.GetFamilyType());
+ pDescriptor->nPitch = sal::static_int_cast< sal_Int16 >(rFont.GetPitch());
+ pDescriptor->nWeight = sal::static_int_cast< sal_Int16 >(rFont.GetWeight());
+ pDescriptor->nItalic = sal::static_int_cast< sal_Int16 >(rFont.GetItalic());
+ pDescriptor++;
+ }
+ *pValue <<= aSequence;
+ }
+ break;
+ case HANDLE_BASIC_LIBRARIES:
+ *pValue <<= pDocSh->GetBasicContainer();
+ break;
+ case HANDLE_DIALOG_LIBRARIES:
+ *pValue <<= pDocSh->GetDialogContainer();
+ break;
+ case HANDLE_RUNTIME_UID:
+ *pValue <<= getRuntimeUID();
+ break;
+ // #i33095# Security Options
+ case HANDLE_LOAD_READONLY :
+ {
+ *pValue <<= pDocSh->IsLoadReadonly();
+ break;
+ }
+ // #i972#
+ case HANDLE_BASELINE:
+ {
+ if ( !pDocSh->GetFormulaTree() )
+ pDocSh->Parse();
+ if ( pDocSh->GetFormulaTree() )
+ {
+ pDocSh->ArrangeFormula();
+
+ *pValue <<= static_cast<sal_Int32>( pDocSh->GetFormulaTree()->GetFormulaBaseline() );
+ }
+ break;
+ }
+ case HANDLE_INTEROP_GRAB_BAG:
+ getGrabBagItem(*pValue);
+ break;
+ case HANDLE_SAVE_THUMBNAIL:
+ {
+ *pValue <<= pDocSh->IsUseThumbnailSave();
+ }
+ break;
+ case HANDLE_STARMATH_VERSION:
+ *pValue <<= pDocSh->GetSmSyntaxVersion();
+ break;
+ }
+ }
+}
+
+
+sal_Int32 SAL_CALL SmModel::getRendererCount(
+ const uno::Any& /*rSelection*/,
+ const uno::Sequence< beans::PropertyValue >& /*xOptions*/ )
+{
+ return 1;
+}
+
+
+static Size lcl_GuessPaperSize()
+{
+ Size aRes;
+ const LocaleDataWrapper& rLocWrp( AllSettings().GetLocaleDataWrapper() );
+ if( MeasurementSystem::Metric == rLocWrp.getMeasurementSystemEnum() )
+ {
+ // in 100th mm
+ PaperInfo aInfo( PAPER_A4 );
+ aRes.setWidth( aInfo.getWidth() );
+ aRes.setHeight( aInfo.getHeight() );
+ }
+ else
+ {
+ // in 100th mm
+ PaperInfo aInfo( PAPER_LETTER );
+ aRes.setWidth( aInfo.getWidth() );
+ aRes.setHeight( aInfo.getHeight() );
+ }
+ return aRes;
+}
+
+uno::Sequence< beans::PropertyValue > SAL_CALL SmModel::getRenderer(
+ sal_Int32 nRenderer,
+ const uno::Any& /*rSelection*/,
+ const uno::Sequence< beans::PropertyValue >& /*rxOptions*/ )
+{
+ SolarMutexGuard aGuard;
+
+ if (0 != nRenderer)
+ throw IllegalArgumentException();
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * >( GetObjectShell() );
+ if (!pDocSh)
+ throw RuntimeException();
+
+ SmPrinterAccess aPrinterAccess( *pDocSh );
+ Printer *pPrinter = aPrinterAccess.GetPrinter();
+ Size aPrtPaperSize ( pPrinter->GetPaperSize() );
+
+ // if paper size is 0 (usually if no 'real' printer is found),
+ // guess the paper size
+ if (aPrtPaperSize.IsEmpty())
+ aPrtPaperSize = lcl_GuessPaperSize();
+ awt::Size aPageSize( aPrtPaperSize.Width(), aPrtPaperSize.Height() );
+
+ uno::Sequence< beans::PropertyValue > aRenderer(1);
+ PropertyValue &rValue = aRenderer.getArray()[0];
+ rValue.Name = "PageSize";
+ rValue.Value <<= aPageSize;
+
+ if (!m_pPrintUIOptions)
+ m_pPrintUIOptions.reset(new SmPrintUIOptions);
+ m_pPrintUIOptions->appendPrintUIOptions( aRenderer );
+
+ return aRenderer;
+}
+
+void SAL_CALL SmModel::render(
+ sal_Int32 nRenderer,
+ const uno::Any& rSelection,
+ const uno::Sequence< beans::PropertyValue >& rxOptions )
+{
+ SolarMutexGuard aGuard;
+
+ if (0 != nRenderer)
+ throw IllegalArgumentException();
+
+ SmDocShell *pDocSh = static_cast < SmDocShell * >( GetObjectShell() );
+ if (!pDocSh)
+ throw RuntimeException();
+
+ // get device to be rendered in
+ uno::Reference< awt::XDevice > xRenderDevice;
+ for (const auto& rxOption : rxOptions)
+ {
+ if( rxOption.Name == "RenderDevice" )
+ rxOption.Value >>= xRenderDevice;
+ }
+
+ if (!xRenderDevice.is())
+ return;
+
+ VCLXDevice* pDevice = comphelper::getFromUnoTunnel<VCLXDevice>( xRenderDevice );
+ VclPtr< OutputDevice> pOut = pDevice ? pDevice->GetOutputDevice()
+ : VclPtr< OutputDevice >();
+ if (!pOut)
+ throw RuntimeException();
+
+ pOut->SetMapMode(MapMode(MapUnit::Map100thMM));
+
+ uno::Reference< frame::XModel > xModel;
+ rSelection >>= xModel;
+ if (xModel != pDocSh->GetModel())
+ return;
+
+ //!! when called via API we may not have an active view
+ //!! thus we go and look for a view that can be used.
+ SfxViewShell* pViewSh = SfxViewShell::GetFirst( false /* search non-visible views as well*/, checkSfxViewShell<SmViewShell> );
+ while (pViewSh && pViewSh->GetObjectShell() != pDocSh)
+ pViewSh = SfxViewShell::GetNext( *pViewSh, false /* search non-visible views as well*/, checkSfxViewShell<SmViewShell> );
+ SmViewShell *pView = dynamic_cast< SmViewShell *>( pViewSh );
+ SAL_WARN_IF( !pView, "starmath", "SmModel::render : no SmViewShell found" );
+
+ if (!pView)
+ return;
+
+ SmPrinterAccess aPrinterAccess( *pDocSh );
+ Printer *pPrinter = aPrinterAccess.GetPrinter();
+
+ Size aPrtPaperSize ( pPrinter->GetPaperSize() );
+ Size aOutputSize ( pPrinter->GetOutputSize() );
+ Point aPrtPageOffset( pPrinter->GetPageOffset() );
+
+ // no real printer ??
+ if (aPrtPaperSize.IsEmpty())
+ {
+ aPrtPaperSize = lcl_GuessPaperSize();
+ // factors from Windows DIN A4
+ aOutputSize = Size( static_cast<tools::Long>(aPrtPaperSize.Width() * 0.941),
+ static_cast<tools::Long>(aPrtPaperSize.Height() * 0.961));
+ aPrtPageOffset = Point( static_cast<tools::Long>(aPrtPaperSize.Width() * 0.0250),
+ static_cast<tools::Long>(aPrtPaperSize.Height() * 0.0214));
+ }
+ tools::Rectangle OutputRect( Point(), aOutputSize );
+
+
+ // set minimum top and bottom border
+ if (aPrtPageOffset.Y() < 2000)
+ OutputRect.AdjustTop(2000 - aPrtPageOffset.Y() );
+ if ((aPrtPaperSize.Height() - (aPrtPageOffset.Y() + OutputRect.Bottom())) < 2000)
+ OutputRect.AdjustBottom( -(2000 - (aPrtPaperSize.Height() -
+ (aPrtPageOffset.Y() + OutputRect.Bottom()))) );
+
+ // set minimum left and right border
+ if (aPrtPageOffset.X() < 2500)
+ OutputRect.AdjustLeft(2500 - aPrtPageOffset.X() );
+ if ((aPrtPaperSize.Width() - (aPrtPageOffset.X() + OutputRect.Right())) < 1500)
+ OutputRect.AdjustRight( -(1500 - (aPrtPaperSize.Width() -
+ (aPrtPageOffset.X() + OutputRect.Right()))) );
+
+ if (!m_pPrintUIOptions)
+ m_pPrintUIOptions.reset(new SmPrintUIOptions);
+ m_pPrintUIOptions->processProperties( rxOptions );
+
+ pView->Impl_Print( *pOut, *m_pPrintUIOptions, OutputRect );
+
+ // release SmPrintUIOptions when everything is done.
+ // That way, when SmPrintUIOptions is needed again it will read the latest configuration settings in its c-tor.
+ if (m_pPrintUIOptions->getBoolValue( "IsLastPage" ))
+ {
+ m_pPrintUIOptions.reset();
+ }
+}
+
+void SAL_CALL SmModel::setParent( const uno::Reference< uno::XInterface >& xParent)
+{
+ SolarMutexGuard aGuard;
+ SfxBaseModel::setParent( xParent );
+ if (SfxObjectShell* pDoc = SfxObjectShell::GetShellFromComponent(xParent))
+ GetObjectShell()->OnDocumentPrinterChanged(pDoc->GetDocumentPrinter());
+}
+
+void SmModel::writeFormulaOoxml(
+ ::sax_fastparser::FSHelperPtr const pSerializer,
+ oox::core::OoxmlVersion const version,
+ oox::drawingml::DocumentType const documentType, sal_Int8 nAlign)
+{
+ static_cast<SmDocShell*>(GetObjectShell())->writeFormulaOoxml(pSerializer, version, documentType, nAlign);
+}
+
+void SmModel::writeFormulaRtf(OStringBuffer& rBuffer, rtl_TextEncoding nEncoding)
+{
+ static_cast<SmDocShell*>(GetObjectShell())->writeFormulaRtf(rBuffer, nEncoding);
+}
+
+void SmModel::readFormulaOoxml( oox::formulaimport::XmlStream& stream )
+{
+ static_cast< SmDocShell* >( GetObjectShell())->readFormulaOoxml( stream );
+}
+
+Size SmModel::getFormulaSize() const
+{
+ return static_cast< SmDocShell* >( GetObjectShell())->GetSize();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/utility.cxx b/starmath/source/utility.cxx
new file mode 100644
index 000000000..e8406c103
--- /dev/null
+++ b/starmath/source/utility.cxx
@@ -0,0 +1,247 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <utility.hxx>
+#include <dialog.hxx>
+#include <view.hxx>
+
+#include <comphelper/lok.hxx>
+#include <sfx2/lokcomponenthelpers.hxx>
+
+// return pointer to active SmViewShell, if this is not possible
+// return 0 instead.
+//!! Since this method is based on the current focus it is somewhat
+//!! unreliable and may return unexpected 0 pointers!
+SmViewShell * SmGetActiveView()
+{
+ SfxViewShell *pView = SfxViewShell::Current();
+ SmViewShell* pSmView = dynamic_cast<SmViewShell*>(pView);
+ if (!pSmView && comphelper::LibreOfficeKit::isActive())
+ {
+ auto* pWindow = static_cast<SmGraphicWindow*>(LokStarMathHelper(pView).GetGraphicWindow());
+ if (pWindow)
+ pSmView = &pWindow->GetGraphicWidget().GetView();
+ }
+ return pSmView;
+}
+
+
+/**************************************************************************/
+
+void SmFontPickList::Clear()
+{
+ aFontVec.clear();
+}
+
+SmFontPickList& SmFontPickList::operator = (const SmFontPickList& rList)
+{
+ Clear();
+ nMaxItems = rList.nMaxItems;
+ aFontVec = rList.aFontVec;
+
+ return *this;
+}
+
+vcl::Font SmFontPickList::Get(sal_uInt16 nPos) const
+{
+ return nPos < aFontVec.size() ? aFontVec[nPos] : vcl::Font();
+}
+
+namespace {
+
+bool lcl_CompareItem(const vcl::Font & rFirstFont, const vcl::Font & rSecondFont)
+{
+ return rFirstFont.GetFamilyName() == rSecondFont.GetFamilyName() &&
+ rFirstFont.GetFamilyType() == rSecondFont.GetFamilyType() &&
+ rFirstFont.GetCharSet() == rSecondFont.GetCharSet() &&
+ rFirstFont.GetWeight() == rSecondFont.GetWeight() &&
+ rFirstFont.GetItalic() == rSecondFont.GetItalic();
+}
+
+OUString lcl_GetStringItem(const vcl::Font &rFont)
+{
+ OUStringBuffer aString(rFont.GetFamilyName());
+
+ if (IsItalic( rFont ))
+ {
+ aString.append(", ");
+ aString.append(SmResId(RID_FONTITALIC));
+ }
+ if (IsBold( rFont ))
+ {
+ aString.append(", ");
+ aString.append(SmResId(RID_FONTBOLD));
+ }
+
+ return aString.makeStringAndClear();
+}
+
+}
+
+void SmFontPickList::Insert(const vcl::Font &rFont)
+{
+ for (size_t nPos = 0; nPos < aFontVec.size(); nPos++)
+ if (lcl_CompareItem( aFontVec[nPos], rFont))
+ {
+ aFontVec.erase( aFontVec.begin() + nPos );
+ break;
+ }
+
+ aFontVec.push_front( rFont );
+
+ if (aFontVec.size() > nMaxItems)
+ {
+ aFontVec.pop_back();
+ }
+}
+
+void SmFontPickList::ReadFrom(const SmFontDialog& rDialog)
+{
+ Insert(rDialog.GetFont());
+}
+
+void SmFontPickList::WriteTo(SmFontDialog& rDialog) const
+{
+ rDialog.SetFont(Get());
+}
+
+
+/**************************************************************************/
+
+SmFontPickListBox::SmFontPickListBox(std::unique_ptr<weld::ComboBox> pWidget)
+ : SmFontPickList(4)
+ , m_xWidget(std::move(pWidget))
+{
+ m_xWidget->connect_changed(LINK(this, SmFontPickListBox, SelectHdl));
+}
+
+IMPL_LINK_NOARG(SmFontPickListBox, SelectHdl, weld::ComboBox&, void)
+{
+ const int nPos = m_xWidget->get_active();
+ if (nPos != 0)
+ {
+ SmFontPickList::Insert(Get(nPos));
+ OUString aString = m_xWidget->get_text(nPos);
+ m_xWidget->remove(nPos);
+ m_xWidget->insert_text(0, aString);
+ }
+
+ m_xWidget->set_active(0);
+}
+
+SmFontPickListBox& SmFontPickListBox::operator=(const SmFontPickList& rList)
+{
+ *static_cast<SmFontPickList *>(this) = rList;
+
+ for (decltype(aFontVec)::size_type nPos = 0; nPos < aFontVec.size(); nPos++)
+ m_xWidget->insert_text(nPos, lcl_GetStringItem(aFontVec[nPos]));
+
+ if (!aFontVec.empty())
+ m_xWidget->set_active_text(lcl_GetStringItem(aFontVec.front()));
+
+ return *this;
+}
+
+void SmFontPickListBox::Insert(const vcl::Font &rFont)
+{
+ SmFontPickList::Insert(rFont);
+
+ OUString aEntry(lcl_GetStringItem(aFontVec.front()));
+ int nPos = m_xWidget->find_text(aEntry);
+ if (nPos != -1)
+ m_xWidget->remove(nPos);
+ m_xWidget->insert_text(0, aEntry);
+ m_xWidget->set_active(0);
+
+ while (m_xWidget->get_count() > nMaxItems)
+ m_xWidget->remove(m_xWidget->get_count() - 1);
+}
+
+bool IsItalic( const vcl::Font &rFont )
+{
+ FontItalic eItalic = rFont.GetItalic();
+ // the code below leaves only _NONE and _DONTKNOW as not italic
+ return eItalic == ITALIC_OBLIQUE || eItalic == ITALIC_NORMAL;
+}
+
+
+bool IsBold( const vcl::Font &rFont )
+{
+ FontWeight eWeight = rFont.GetWeight();
+ return eWeight > WEIGHT_NORMAL;
+}
+
+
+void SmFace::Impl_Init()
+{
+ SetSize( GetFontSize() );
+ SetTransparent( true );
+ SetAlignment( ALIGN_BASELINE );
+ SetColor( COL_AUTO );
+}
+
+void SmFace::SetSize(const Size& rSize)
+{
+ Size aSize (rSize);
+
+ // check the requested size against minimum value
+ static int const nMinVal = SmPtsTo100th_mm(2);
+
+ if (aSize.Height() < nMinVal)
+ aSize.setHeight( nMinVal );
+
+ //! we don't force a maximum value here because this may prevent eg the
+ //! parentheses in "left ( ... right )" from matching up with large
+ //! bodies (eg stack{...} with many entries).
+ //! Of course this is holds only if characters are used and not polygons.
+
+ Font::SetFontSize(aSize);
+}
+
+
+tools::Long SmFace::GetBorderWidth() const
+{
+ if (nBorderWidth < 0)
+ return GetDefaultBorderWidth();
+ else
+ return nBorderWidth;
+}
+
+SmFace & SmFace::operator = (const SmFace &rFace)
+{
+ Font::operator = (rFace);
+ nBorderWidth = -1;
+ return *this;
+}
+
+
+SmFace & operator *= (SmFace &rFace, const Fraction &rFrac)
+ // scales the width and height of 'rFace' by 'rFrac' and returns a
+ // reference to 'rFace'.
+ // It's main use is to make scaling fonts look easier.
+{ const Size &rFaceSize = rFace.GetFontSize();
+
+ rFace.SetSize(Size(tools::Long(rFaceSize.Width() * rFrac),
+ tools::Long(rFaceSize.Height() * rFrac)));
+ return rFace;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/view.cxx b/starmath/source/view.cxx
new file mode 100644
index 000000000..2ddb7a230
--- /dev/null
+++ b/starmath/source/view.cxx
@@ -0,0 +1,2269 @@
+/* -*- 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/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <com/sun/star/accessibility/AccessibleEventId.hpp>
+#include <com/sun/star/accessibility/AccessibleStateType.hpp>
+#include <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XFramesSupplier.hpp>
+#include <com/sun/star/container/XChild.hpp>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <comphelper/storagehelper.hxx>
+#include <comphelper/string.hxx>
+#include <i18nutil/unicode.hxx>
+#include <officecfg/Office/Common.hxx>
+#include <sfx2/dispatch.hxx>
+#include <sfx2/docfile.hxx>
+#include <sfx2/docfilt.hxx>
+#include <sfx2/docinsert.hxx>
+#include <sfx2/filedlghelper.hxx>
+#include <sfx2/infobar.hxx>
+#include <sfx2/msg.hxx>
+#include <sfx2/objface.hxx>
+#include <sfx2/printer.hxx>
+#include <sfx2/request.hxx>
+#include <sfx2/sfxbasecontroller.hxx>
+#include <sfx2/sidebar/SidebarChildWindow.hxx>
+#include <sfx2/viewfac.hxx>
+#include <svl/eitem.hxx>
+#include <svl/itemset.hxx>
+#include <svl/poolitem.hxx>
+#include <svl/stritem.hxx>
+#include <vcl/transfer.hxx>
+#include <svtools/colorcfg.hxx>
+#include <svl/whiter.hxx>
+#include <svx/sidebar/SelectionChangeHandler.hxx>
+#include <svx/zoomslideritem.hxx>
+#include <editeng/editeng.hxx>
+#include <editeng/editview.hxx>
+#include <svx/svxdlg.hxx>
+#include <sfx2/zoomitem.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/settings.hxx>
+#include <vcl/virdev.hxx>
+#include <sal/log.hxx>
+#include <tools/svborder.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <unotools/streamwrap.hxx>
+
+#include <unomodel.hxx>
+#include <view.hxx>
+#include <cfgitem.hxx>
+#include <dialog.hxx>
+#include <document.hxx>
+#include <starmath.hrc>
+#include <strings.hrc>
+#include <smmod.hxx>
+#include <mathmlimport.hxx>
+#include <cursor.hxx>
+#include "accessibility.hxx"
+#include <ElementsDockingWindow.hxx>
+#include <helpids.h>
+
+#define MINZOOM sal_uInt16(25)
+#define MAXZOOM sal_uInt16(800)
+
+// space around the edit window, in pixels
+// fdo#69111: Increased border on the top so that the window is
+// easier to tear off.
+#define CMD_BOX_PADDING 3
+#define CMD_BOX_PADDING_TOP 11
+
+#define ShellClass_SmViewShell
+#include <smslots.hxx>
+
+using namespace css;
+using namespace css::accessibility;
+using namespace css::uno;
+
+SmGraphicWindow::SmGraphicWindow(SmViewShell& rShell)
+ : InterimItemWindow(&rShell.GetViewFrame()->GetWindow(), "modules/smath/ui/mathwindow.ui", "MathWindow")
+ , nLinePixH(GetSettings().GetStyleSettings().GetScrollBarSize())
+ , nColumnPixW(nLinePixH)
+ , nZoom(100)
+ // continue to use user-scrolling to make this work equivalent to how it 'always' worked
+ , mxScrolledWindow(m_xBuilder->weld_scrolled_window("scrolledwindow", true))
+ , mxGraphic(new SmGraphicWidget(rShell, *this))
+ , mxGraphicWin(new weld::CustomWeld(*m_xBuilder, "mathview", *mxGraphic))
+{
+ InitControlBase(mxGraphic->GetDrawingArea());
+
+ mxScrolledWindow->connect_hadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
+ mxScrolledWindow->connect_vadjustment_changed(LINK(this, SmGraphicWindow, ScrollHdl));
+
+ // docking windows are usually hidden (often already done in the
+ // resource) and will be shown by the sfx framework.
+ Hide();
+}
+
+void SmGraphicWindow::dispose()
+{
+ InitControlBase(nullptr);
+ mxGraphicWin.reset();
+ mxGraphic.reset();
+ mxScrolledWindow.reset();
+ InterimItemWindow::dispose();
+}
+
+SmGraphicWindow::~SmGraphicWindow()
+{
+ disposeOnce();
+}
+
+void SmGraphicWindow::Resize()
+{
+ InterimItemWindow::Resize();
+
+ // get the new output-size in pixel
+ Size aOutPixSz = GetOutputSizePixel();
+
+ // determine the size of the output-area and if we need scrollbars
+ const auto nScrSize = mxScrolledWindow->get_scroll_thickness();
+ bool bVVisible = false; // by default no vertical-ScrollBar
+ bool bHVisible = false; // by default no horizontal-ScrollBar
+ bool bChanged; // determines if a visiblility was changed
+ do
+ {
+ bChanged = false;
+
+ // does we need a vertical ScrollBar
+ if ( aOutPixSz.Width() < aTotPixSz.Width() && !bHVisible )
+ {
+ bHVisible = true;
+ aOutPixSz.AdjustHeight( -nScrSize );
+ bChanged = true;
+ }
+
+ // does we need a horizontal ScrollBar
+ if ( aOutPixSz.Height() < aTotPixSz.Height() && !bVVisible )
+ {
+ bVVisible = true;
+ aOutPixSz.AdjustWidth( -nScrSize );
+ bChanged = true;
+ }
+
+ }
+ while ( bChanged ); // until no visibility has changed
+
+ // store the old offset and map-mode
+ MapMode aMap(GetGraphicMapMode());
+ Point aOldPixOffset(aPixOffset);
+
+ // justify (right/bottom borders should never exceed the virtual window)
+ Size aPixDelta;
+ if ( aPixOffset.X() < 0 &&
+ aPixOffset.X() + aTotPixSz.Width() < aOutPixSz.Width() )
+ aPixDelta.setWidth(
+ aOutPixSz.Width() - ( aPixOffset.X() + aTotPixSz.Width() ) );
+ if ( aPixOffset.Y() < 0 &&
+ aPixOffset.Y() + aTotPixSz.Height() < aOutPixSz.Height() )
+ aPixDelta.setHeight(
+ aOutPixSz.Height() - ( aPixOffset.Y() + aTotPixSz.Height() ) );
+ if ( aPixDelta.Width() || aPixDelta.Height() )
+ {
+ aPixOffset.AdjustX(aPixDelta.Width() );
+ aPixOffset.AdjustY(aPixDelta.Height() );
+ }
+
+ // for axis without scrollbar restore the origin
+ if ( !bVVisible || !bHVisible )
+ {
+ aPixOffset = Point(
+ bHVisible
+ ? aPixOffset.X()
+ : (aOutPixSz.Width()-aTotPixSz.Width()) / 2,
+ bVVisible
+ ? aPixOffset.Y()
+ : (aOutPixSz.Height()-aTotPixSz.Height()) / 2 );
+ }
+ if (bHVisible && mxScrolledWindow->get_hpolicy() == VclPolicyType::NEVER)
+ aPixOffset.setX( 0 );
+ if (bVVisible && mxScrolledWindow->get_vpolicy() == VclPolicyType::NEVER)
+ aPixOffset.setY( 0 );
+
+ // select the shifted map-mode
+ if (aPixOffset != aOldPixOffset)
+ SetGraphicMapMode(aMap);
+
+ // show or hide scrollbars
+ mxScrolledWindow->set_vpolicy(bVVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
+ mxScrolledWindow->set_hpolicy(bHVisible ? VclPolicyType::ALWAYS : VclPolicyType::NEVER);
+
+ // resize scrollbars and set their ranges
+ if ( bHVisible )
+ {
+ mxScrolledWindow->hadjustment_configure(-aPixOffset.X(), 0, aTotPixSz.Width(), nColumnPixW,
+ aOutPixSz.Width(), aOutPixSz.Width());
+ }
+ if ( bVVisible )
+ {
+ mxScrolledWindow->vadjustment_configure(-aPixOffset.Y(), 0, aTotPixSz.Height(), nLinePixH,
+ aOutPixSz.Height(), aOutPixSz.Height());
+ }
+}
+
+IMPL_LINK_NOARG(SmGraphicWindow, ScrollHdl, weld::ScrolledWindow&, void)
+{
+ MapMode aMap(GetGraphicMapMode());
+ Point aNewPixOffset(aPixOffset);
+
+ // scrolling horizontally?
+ if (mxScrolledWindow->get_hpolicy() == VclPolicyType::ALWAYS)
+ aNewPixOffset.setX(-mxScrolledWindow->hadjustment_get_value());
+
+ // scrolling vertically?
+ if (mxScrolledWindow->get_vpolicy() == VclPolicyType::ALWAYS)
+ aNewPixOffset.setY(-mxScrolledWindow->vadjustment_get_value());
+
+ // scrolling?
+ if (aPixOffset == aNewPixOffset)
+ return;
+
+ // recompute the logical scroll units
+ aPixOffset = aNewPixOffset;
+
+ SetGraphicMapMode(aMap);
+}
+
+void SmGraphicWindow::SetGraphicMapMode(const MapMode& rNewMapMode)
+{
+ OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
+ MapMode aMap( rNewMapMode );
+ aMap.SetOrigin( aMap.GetOrigin() + rDevice.PixelToLogic( aPixOffset, aMap ) );
+ rDevice.SetMapMode( aMap );
+ mxGraphic->Invalidate();
+}
+
+MapMode SmGraphicWindow::GetGraphicMapMode() const
+{
+ OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
+ MapMode aMap(rDevice.GetMapMode());
+ aMap.SetOrigin( aMap.GetOrigin() - rDevice.PixelToLogic( aPixOffset ) );
+ return aMap;
+}
+
+void SmGraphicWindow::SetTotalSize( const Size& rNewSize )
+{
+ OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
+ aTotPixSz = rDevice.LogicToPixel(rNewSize);
+ Resize();
+}
+
+Size SmGraphicWindow::GetTotalSize() const
+{
+ OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
+ return rDevice.PixelToLogic(aTotPixSz);
+}
+
+void SmGraphicWindow::ShowContextMenu(const CommandEvent& rCEvt)
+{
+ GetParent()->ToTop();
+ Point aPos(5, 5);
+ if (rCEvt.IsMouseEvent())
+ aPos = rCEvt.GetMousePosPixel();
+
+ // added for replaceability of context menus
+ SfxDispatcher::ExecutePopup( this, &aPos );
+}
+
+SmGraphicWidget::SmGraphicWidget(SmViewShell& rShell, SmGraphicWindow& rGraphicWindow)
+ : mrGraphicWindow(rGraphicWindow)
+ , bIsCursorVisible(false)
+ , bIsLineVisible(false)
+ , aCaretBlinkTimer("SmGraphicWidget aCaretBlinkTimer")
+ , mrViewShell(rShell)
+{
+}
+
+void SmGraphicWidget::SetDrawingArea(weld::DrawingArea* pDrawingArea)
+{
+ weld::CustomWidgetController::SetDrawingArea(pDrawingArea);
+
+ OutputDevice& rDevice = pDrawingArea->get_ref_device();
+
+ rDevice.SetBackground(SM_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor);
+
+ const Fraction aFraction(1, 1);
+ rDevice.SetMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
+
+ SetTotalSize();
+
+ SetHelpId(HID_SMA_WIN_DOCUMENT);
+
+ ShowLine(false);
+ CaretBlinkInit();
+}
+
+SmGraphicWidget::~SmGraphicWidget()
+{
+ if (mxAccessible.is())
+ mxAccessible->ClearWin(); // make Accessible nonfunctional
+ mxAccessible.clear();
+ CaretBlinkStop();
+}
+
+bool SmGraphicWidget::MouseButtonDown(const MouseEvent& rMEvt)
+{
+ GrabFocus();
+
+ // set formula-cursor and selection of edit window according to the
+ // position clicked at
+
+ SAL_WARN_IF( rMEvt.GetClicks() == 0, "starmath", "0 clicks" );
+ if ( !rMEvt.IsLeft() )
+ return true;
+
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ // get click position relative to formula
+ Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
+
+ const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree();
+ if (!pTree)
+ return true;
+
+ if (SmViewShell::IsInlineEditEnabled()) {
+ mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, !rMEvt.IsShift());
+ return true;
+ }
+ const SmNode *pNode = nullptr;
+ // if it was clicked inside the formula then get the appropriate node
+ if (pTree->OrientedDist(aPos) <= 0)
+ pNode = pTree->FindRectClosestTo(aPos);
+
+ if (!pNode)
+ return true;
+
+ SmEditWindow* pEdit = mrViewShell.GetEditWindow();
+ if (!pEdit)
+ return true;
+
+ // set selection to the beginning of the token
+ pEdit->SetSelection(pNode->GetSelection());
+ SetCursor(pNode);
+
+ // allow for immediate editing and
+ //! implicitly synchronize the cursor position mark in this window
+ pEdit->GrabFocus();
+
+ return true;
+}
+
+bool SmGraphicWidget::MouseMove(const MouseEvent &rMEvt)
+{
+ if (rMEvt.IsLeft() && SmViewShell::IsInlineEditEnabled())
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ Point aPos(rDevice.PixelToLogic(rMEvt.GetPosPixel()) - GetFormulaDrawPos());
+ mrViewShell.GetDoc()->GetCursor().MoveTo(&rDevice, aPos, false);
+
+ CaretBlinkStop();
+ SetIsCursorVisible(true);
+ CaretBlinkStart();
+ RepaintViewShellDoc();
+ }
+ return true;
+}
+
+void SmGraphicWidget::GetFocus()
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+ if (mrViewShell.GetEditWindow())
+ mrViewShell.GetEditWindow()->Flush();
+ //Let view shell know what insertions should be done in visual editor
+ mrViewShell.SetInsertIntoEditWindow(false);
+ SetIsCursorVisible(true);
+ ShowLine(true);
+ CaretBlinkStart();
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWidget::LoseFocus()
+{
+ if (mxAccessible.is())
+ {
+ uno::Any aOldValue, aNewValue;
+ aOldValue <<= AccessibleStateType::FOCUSED;
+ // aNewValue remains empty
+ mxAccessible->LaunchEvent( AccessibleEventId::STATE_CHANGED,
+ aOldValue, aNewValue );
+ }
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+ SetIsCursorVisible(false);
+ ShowLine(false);
+ CaretBlinkStop();
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWidget::RepaintViewShellDoc()
+{
+ SmDocShell* pDoc = mrViewShell.GetDoc();
+ if (pDoc)
+ pDoc->Repaint();
+}
+
+IMPL_LINK_NOARG(SmGraphicWidget, CaretBlinkTimerHdl, Timer *, void)
+{
+ if (IsCursorVisible())
+ SetIsCursorVisible(false);
+ else
+ SetIsCursorVisible(true);
+
+ RepaintViewShellDoc();
+}
+
+void SmGraphicWidget::CaretBlinkInit()
+{
+ aCaretBlinkTimer.SetInvokeHandler(LINK(this, SmGraphicWidget, CaretBlinkTimerHdl));
+ aCaretBlinkTimer.SetTimeout(Application::GetSettings().GetStyleSettings().GetCursorBlinkTime());
+}
+
+void SmGraphicWidget::CaretBlinkStart()
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+ if (aCaretBlinkTimer.GetTimeout() != STYLE_CURSOR_NOBLINKTIME)
+ aCaretBlinkTimer.Start();
+}
+
+void SmGraphicWidget::CaretBlinkStop()
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+ aCaretBlinkTimer.Stop();
+}
+
+// shows or hides the formula-cursor depending on 'bShow' is true or not
+void SmGraphicWidget::ShowCursor(bool bShow)
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return;
+
+ bool bInvert = bShow != IsCursorVisible();
+ if (bInvert)
+ {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ InvertFocusRect(rDevice, aCursorRect);
+ }
+
+ SetIsCursorVisible(bShow);
+}
+
+void SmGraphicWidget::ShowLine(bool bShow)
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return;
+
+ bIsLineVisible = bShow;
+}
+
+void SmGraphicWidget::SetCursor(const SmNode *pNode)
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return;
+
+ const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree();
+
+ // get appropriate rectangle
+ Point aOffset (pNode->GetTopLeft() - pTree->GetTopLeft()),
+ aTLPos (GetFormulaDrawPos() + aOffset);
+ aTLPos.AdjustX( -(pNode->GetItalicLeftSpace()) );
+ Size aSize (pNode->GetItalicSize());
+
+ SetCursor(tools::Rectangle(aTLPos, aSize));
+}
+
+void SmGraphicWidget::SetCursor(const tools::Rectangle &rRect)
+ // sets cursor to new position (rectangle) 'rRect'.
+ // The old cursor will be removed, and the new one will be shown if
+ // that is activated in the ConfigItem
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return;
+
+ SmModule *pp = SM_MOD();
+
+ if (IsCursorVisible())
+ ShowCursor(false); // clean up remainings of old cursor
+ aCursorRect = rRect;
+ if (pp->GetConfig()->IsShowFormulaCursor())
+ ShowCursor(true); // draw new cursor
+}
+
+const SmNode * SmGraphicWidget::SetCursorPos(sal_uInt16 nRow, sal_uInt16 nCol)
+ // looks for a VISIBLE node in the formula tree with its token at
+ // (or around) the position 'nRow', 'nCol' in the edit window
+ // (row and column numbering starts with 1 there!).
+ // If there is such a node the formula-cursor is set to cover that nodes
+ // rectangle. If not the formula-cursor will be hidden.
+ // In any case the search result is being returned.
+{
+ if (SmViewShell::IsInlineEditEnabled())
+ return nullptr;
+
+ // find visible node with token at nRow, nCol
+ const SmNode *pTree = mrViewShell.GetDoc()->GetFormulaTree(),
+ *pNode = nullptr;
+ if (pTree)
+ pNode = pTree->FindTokenAt(nRow, nCol);
+
+ if (pNode)
+ SetCursor(pNode);
+ else
+ ShowCursor(false);
+
+ return pNode;
+}
+
+void SmGraphicWidget::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle&)
+{
+ SmDocShell& rDoc = *mrViewShell.GetDoc();
+ Point aPoint;
+
+ rDoc.DrawFormula(rRenderContext, aPoint, true); //! modifies aPoint to be the topleft
+ //! corner of the formula
+ aFormulaDrawPos = aPoint;
+ if (SmViewShell::IsInlineEditEnabled())
+ {
+ //Draw cursor if any...
+ if (mrViewShell.GetDoc()->HasCursor() && IsLineVisible())
+ mrViewShell.GetDoc()->GetCursor().Draw(rRenderContext, aPoint, IsCursorVisible());
+ }
+ else
+ {
+ SetIsCursorVisible(false); // (old) cursor must be drawn again
+
+ const SmEditWindow* pEdit = mrViewShell.GetEditWindow();
+ if (pEdit)
+ { // get new position for formula-cursor (for possible altered formula)
+ sal_Int32 nRow;
+ sal_uInt16 nCol;
+ SmGetLeftSelectionPart(pEdit->GetSelection(), nRow, nCol);
+ const SmNode *pFound = SetCursorPos(static_cast<sal_uInt16>(nRow), nCol);
+
+ SmModule *pp = SM_MOD();
+ if (pFound && pp->GetConfig()->IsShowFormulaCursor())
+ ShowCursor(true);
+ }
+ }
+}
+
+void SmGraphicWidget::SetTotalSize()
+{
+ SmDocShell &rDoc = *mrViewShell.GetDoc();
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ const Size aTmp(rDevice.PixelToLogic(rDevice.LogicToPixel(rDoc.GetSize())));
+ if (aTmp != mrGraphicWindow.GetTotalSize())
+ mrGraphicWindow.SetTotalSize(aTmp);
+}
+
+namespace
+{
+SmBracketType BracketTypeOf(sal_uInt32 c)
+{
+ switch (c)
+ {
+ case '(':
+ case ')':
+ return SmBracketType::Round;
+ case '[':
+ case ']':
+ return SmBracketType::Square;
+ case '{':
+ case '}':
+ return SmBracketType::Curly;
+ }
+ assert(false); // Unreachable
+ return SmBracketType::Round;
+}
+
+bool CharInput(sal_uInt32 c, SmCursor& rCursor, OutputDevice& rDevice)
+{
+ switch (c)
+ {
+ case 0:
+ return false;
+ case ' ':
+ rCursor.InsertElement(BlankElement);
+ break;
+ case '!':
+ rCursor.InsertElement(FactorialElement);
+ break;
+ case '%':
+ rCursor.InsertElement(PercentElement);
+ break;
+ case '*':
+ rCursor.InsertElement(CDotElement);
+ break;
+ case '+':
+ rCursor.InsertElement(PlusElement);
+ break;
+ case '-':
+ rCursor.InsertElement(MinusElement);
+ break;
+ case '<':
+ rCursor.InsertElement(LessThanElement);
+ break;
+ case '=':
+ rCursor.InsertElement(EqualElement);
+ break;
+ case '>':
+ rCursor.InsertElement(GreaterThanElement);
+ break;
+ case '^':
+ rCursor.InsertSubSup(RSUP);
+ break;
+ case '_':
+ rCursor.InsertSubSup(RSUB);
+ break;
+ case '/':
+ rCursor.InsertFraction();
+ break;
+ case '(':
+ case '[':
+ case '{':
+ rCursor.InsertBrackets(BracketTypeOf(c));
+ break;
+ case ')':
+ case ']':
+ case '}':
+ if (rCursor.IsAtTailOfBracket(BracketTypeOf(c)))
+ {
+ rCursor.Move(&rDevice, MoveRight);
+ break;
+ }
+ [[fallthrough]];
+ default:
+ rCursor.InsertText(OUString(&c, 1));
+ break;
+ }
+ return true;
+}
+}
+
+bool SmGraphicWidget::KeyInput(const KeyEvent& rKEvt)
+{
+ if (!SmViewShell::IsInlineEditEnabled())
+ return mrViewShell.KeyInput(rKEvt);
+
+ bool bConsumed = true;
+
+ SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor();
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+ if (eFunc == KeyFuncType::COPY)
+ rCursor.Copy();
+ else if (eFunc == KeyFuncType::CUT)
+ rCursor.Cut();
+ else if (eFunc == KeyFuncType::PASTE)
+ rCursor.Paste();
+ else {
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode();
+ switch(nCode)
+ {
+ case KEY_LEFT:
+ {
+ rCursor.Move(&rDevice, MoveLeft, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_RIGHT:
+ {
+ rCursor.Move(&rDevice, MoveRight, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_UP:
+ {
+ rCursor.Move(&rDevice, MoveUp, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_DOWN:
+ {
+ rCursor.Move(&rDevice, MoveDown, !rKEvt.GetKeyCode().IsShift());
+ }break;
+ case KEY_RETURN:
+ {
+ if(!rKEvt.GetKeyCode().IsShift())
+ rCursor.InsertRow();
+ }break;
+ case KEY_DELETE:
+ {
+ if(!rCursor.HasSelection()){
+ rCursor.Move(&rDevice, MoveRight, false);
+ if(rCursor.HasComplexSelection()) break;
+ }
+ rCursor.Delete();
+ }break;
+ case KEY_BACKSPACE:
+ {
+ rCursor.DeletePrev(&rDevice);
+ }break;
+ default:
+ {
+ if (!CharInput(rKEvt.GetCharCode(), rCursor, rDevice))
+ bConsumed = mrViewShell.KeyInput(rKEvt);
+ }
+ }
+ }
+ CaretBlinkStop();
+ CaretBlinkStart();
+ SetIsCursorVisible(true);
+ RepaintViewShellDoc();
+
+ return bConsumed;
+}
+
+bool SmGraphicWidget::Command(const CommandEvent& rCEvt)
+{
+ bool bCallBase = true;
+ if (!mrViewShell.GetViewFrame()->GetFrame().IsInPlace())
+ {
+ switch ( rCEvt.GetCommand() )
+ {
+ case CommandEventId::ContextMenu:
+ // purely for "ExecutePopup" taking a vcl::Window and
+ // we assume SmGraphicWindow 0,0 is at SmEditWindow 0,0
+ mrGraphicWindow.ShowContextMenu(rCEvt);
+ bCallBase = false;
+ break;
+
+ case CommandEventId::Wheel:
+ {
+ const CommandWheelData* pWData = rCEvt.GetWheelData();
+ if ( pWData && CommandWheelMode::ZOOM == pWData->GetMode() )
+ {
+ sal_uInt16 nTmpZoom = mrGraphicWindow.GetZoom();
+ if( 0 > pWData->GetDelta() )
+ nTmpZoom -= 10;
+ else
+ nTmpZoom += 10;
+ mrGraphicWindow.SetZoom(nTmpZoom);
+ bCallBase = false;
+ }
+ }
+ break;
+
+ default: break;
+ }
+ }
+ else
+ {
+ switch (rCEvt.GetCommand())
+ {
+ case CommandEventId::ExtTextInput:
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ const CommandExtTextInputData* pData = rCEvt.GetExtTextInputData();
+ assert(pData);
+ const OUString& rText = pData->GetText();
+ SmCursor& rCursor = mrViewShell.GetDoc()->GetCursor();
+ OutputDevice& rDevice = GetDrawingArea()->get_ref_device();
+ for (sal_Int32 i = 0; i < rText.getLength();)
+ CharInput(rText.iterateCodePoints(&i), rCursor, rDevice);
+ bCallBase = false;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return !bCallBase;
+}
+
+void SmGraphicWindow::SetZoom(sal_uInt16 Factor)
+{
+ nZoom = std::clamp(Factor, MINZOOM, MAXZOOM);
+ Fraction aFraction(nZoom, 100);
+ SetGraphicMapMode(MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction));
+ mxGraphic->SetTotalSize();
+ SmViewShell& rViewSh = mxGraphic->GetView();
+ rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOM);
+ rViewSh.GetViewFrame()->GetBindings().Invalidate(SID_ATTR_ZOOMSLIDER);
+}
+
+void SmGraphicWindow::ZoomToFitInWindow()
+{
+ SmViewShell& rViewSh = mxGraphic->GetView();
+ SmDocShell& rDoc = *rViewSh.GetDoc();
+
+ // set defined mapmode before calling 'LogicToPixel' below
+ SetGraphicMapMode(MapMode(MapUnit::Map100thMM));
+
+ OutputDevice& rDevice = mxGraphic->GetDrawingArea()->get_ref_device();
+ Size aSize(rDevice.LogicToPixel(rDoc.GetSize()));
+ Size aWindowSize(GetSizePixel());
+
+ if (!aSize.IsEmpty())
+ {
+ tools::Long nVal = std::min ((85 * aWindowSize.Width()) / aSize.Width(),
+ (85 * aWindowSize.Height()) / aSize.Height());
+ SetZoom ( sal::static_int_cast< sal_uInt16 >(nVal) );
+ }
+}
+
+uno::Reference< XAccessible > SmGraphicWidget::CreateAccessible()
+{
+ if (!mxAccessible.is())
+ {
+ mxAccessible = new SmGraphicAccessible( this );
+ }
+ return mxAccessible;
+}
+
+/**************************************************************************/
+SmGraphicController::SmGraphicController(SmGraphicWidget &rSmGraphic,
+ sal_uInt16 nId_,
+ SfxBindings &rBindings) :
+ SfxControllerItem(nId_, rBindings),
+ rGraphic(rSmGraphic)
+{
+}
+
+void SmGraphicController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ rGraphic.SetTotalSize();
+ rGraphic.Invalidate();
+ SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
+}
+
+/**************************************************************************/
+SmEditController::SmEditController(SmEditWindow &rSmEdit,
+ sal_uInt16 nId_,
+ SfxBindings &rBindings) :
+ SfxControllerItem(nId_, rBindings),
+ rEdit(rSmEdit)
+{
+}
+
+void SmEditController::StateChangedAtToolBoxControl(sal_uInt16 nSID, SfxItemState eState, const SfxPoolItem* pState)
+{
+ const SfxStringItem *pItem = dynamic_cast<const SfxStringItem*>( pState);
+
+ if ((pItem != nullptr) && (rEdit.GetText() != pItem->GetValue()))
+ rEdit.SetText(pItem->GetValue());
+ SfxControllerItem::StateChangedAtToolBoxControl (nSID, eState, pState);
+}
+
+/**************************************************************************/
+SmCmdBoxWindow::SmCmdBoxWindow(SfxBindings *pBindings_, SfxChildWindow *pChildWindow,
+ vcl::Window *pParent)
+ : SfxDockingWindow(pBindings_, pChildWindow, pParent, "EditWindow", "modules/smath/ui/editwindow.ui")
+ , m_xEdit(new SmEditWindow(*this, *m_xBuilder))
+ , aController(*m_xEdit, SID_TEXT, *pBindings_)
+ , bExiting(false)
+ , aInitialFocusTimer("SmCmdBoxWindow aInitialFocusTimer")
+{
+ set_id("math_edit");
+
+ SetHelpId( HID_SMA_COMMAND_WIN );
+ SetSizePixel(LogicToPixel(Size(292 , 94), MapMode(MapUnit::MapAppFont)));
+ SetText(SmResId(STR_CMDBOXWINDOW));
+
+ Hide();
+
+ // Don't try to grab focus in LOK inline edit mode
+ if (!comphelper::LibreOfficeKit::isActive())
+ {
+ aInitialFocusTimer.SetInvokeHandler(LINK(this, SmCmdBoxWindow, InitialFocusTimerHdl));
+ aInitialFocusTimer.SetTimeout(100);
+ }
+}
+
+Point SmCmdBoxWindow::WidgetToWindowPos(const weld::Widget& rWidget, const Point& rPos)
+{
+ Point aRet(rPos);
+ int x(0), y(0), width(0), height(0);
+ rWidget.get_extents_relative_to(*m_xContainer, x, y, width, height);
+ aRet.Move(x, y);
+ aRet.Move(m_xBox->GetPosPixel().X(), m_xBox->GetPosPixel().Y());
+ return aRet;
+}
+
+void SmCmdBoxWindow::ShowContextMenu(const Point& rPos)
+{
+ ToTop();
+ SmViewShell *pViewSh = GetView();
+ if (pViewSh)
+ pViewSh->GetViewFrame()->GetDispatcher()->ExecutePopup("edit", this, &rPos);
+}
+
+void SmCmdBoxWindow::Command(const CommandEvent& rCEvt)
+{
+ if (rCEvt.GetCommand() == CommandEventId::ContextMenu)
+ {
+ ShowContextMenu(rCEvt.GetMousePosPixel());
+ return;
+ }
+
+ SfxDockingWindow::Command(rCEvt);
+}
+
+SmCmdBoxWindow::~SmCmdBoxWindow ()
+{
+ disposeOnce();
+}
+
+void SmCmdBoxWindow::dispose()
+{
+ aInitialFocusTimer.Stop();
+ bExiting = true;
+ aController.dispose();
+ m_xEdit.reset();
+ SfxDockingWindow::dispose();
+}
+
+SmViewShell * SmCmdBoxWindow::GetView()
+{
+ SfxDispatcher *pDispatcher = GetBindings().GetDispatcher();
+ SfxViewShell *pView = pDispatcher ? pDispatcher->GetFrame()->GetViewShell() : nullptr;
+ return dynamic_cast<SmViewShell*>( pView);
+}
+
+Size SmCmdBoxWindow::CalcDockingSize(SfxChildAlignment eAlign)
+{
+ switch (eAlign)
+ {
+ case SfxChildAlignment::LEFT:
+ case SfxChildAlignment::RIGHT:
+ return Size();
+ default:
+ break;
+ }
+ return SfxDockingWindow::CalcDockingSize(eAlign);
+}
+
+SfxChildAlignment SmCmdBoxWindow::CheckAlignment(SfxChildAlignment eActual,
+ SfxChildAlignment eWish)
+{
+ switch (eWish)
+ {
+ case SfxChildAlignment::TOP:
+ case SfxChildAlignment::BOTTOM:
+ case SfxChildAlignment::NOALIGNMENT:
+ return eWish;
+ default:
+ break;
+ }
+
+ return eActual;
+}
+
+void SmCmdBoxWindow::StateChanged( StateChangedType nStateChange )
+{
+ if (StateChangedType::InitShow == nStateChange)
+ {
+ Resize(); // avoid SmEditWindow not being painted correctly
+
+ // set initial position of window in floating mode
+ if (IsFloatingMode())
+ AdjustPosition(); //! don't change pos in docking-mode !
+
+ aInitialFocusTimer.Start();
+ }
+
+ SfxDockingWindow::StateChanged( nStateChange );
+}
+
+IMPL_LINK_NOARG( SmCmdBoxWindow, InitialFocusTimerHdl, Timer *, void )
+{
+ // We want to have the focus in the edit window once Math has been opened
+ // to allow for immediate typing.
+ // Problem: There is no proper way to do this
+ // Thus: this timer based solution has been implemented (see GrabFocus below)
+
+ // Follow-up problem (#i114910): grabbing the focus may bust the help system since
+ // it relies on getting the current frame which conflicts with grabbing the focus.
+ // Thus aside from the 'GrabFocus' call everything else is to get the
+ // help reliably working despite using 'GrabFocus'.
+
+ try
+ {
+ uno::Reference< frame::XDesktop2 > xDesktop = frame::Desktop::create( comphelper::getProcessComponentContext() );
+
+ m_xEdit->GrabFocus();
+
+ SmViewShell* pView = GetView();
+ assert(pView);
+ bool bInPlace = pView->GetViewFrame()->GetFrame().IsInPlace();
+ uno::Reference< frame::XFrame > xFrame( GetBindings().GetDispatcher()->GetFrame()->GetFrame().GetFrameInterface());
+ if ( bInPlace )
+ {
+ uno::Reference<container::XChild> xModel(pView->GetDoc()->GetModel(),
+ uno::UNO_QUERY_THROW);
+ uno::Reference< frame::XModel > xParent( xModel->getParent(), uno::UNO_QUERY_THROW );
+ uno::Reference< frame::XController > xParentCtrler( xParent->getCurrentController() );
+ uno::Reference< frame::XFramesSupplier > xParentFrame( xParentCtrler->getFrame(), uno::UNO_QUERY_THROW );
+ xParentFrame->setActiveFrame( xFrame );
+ }
+ else
+ {
+ xDesktop->setActiveFrame( xFrame );
+ }
+ }
+ catch (uno::Exception &)
+ {
+ SAL_WARN( "starmath", "failed to properly set initial focus to edit window" );
+ }
+}
+
+void SmCmdBoxWindow::AdjustPosition()
+{
+ const tools::Rectangle aRect( Point(), GetParent()->GetOutputSizePixel() );
+ Point aTopLeft( Point( aRect.Left(),
+ aRect.Bottom() - GetSizePixel().Height() ) );
+ Point aPos( GetParent()->OutputToScreenPixel( aTopLeft ) );
+ if (aPos.X() < 0)
+ aPos.setX( 0 );
+ if (aPos.Y() < 0)
+ aPos.setY( 0 );
+ SetPosPixel( aPos );
+}
+
+void SmCmdBoxWindow::ToggleFloatingMode()
+{
+ SfxDockingWindow::ToggleFloatingMode();
+
+ if (GetFloatingWindow())
+ GetFloatingWindow()->SetMinOutputSizePixel(Size (200, 50));
+}
+
+void SmCmdBoxWindow::GetFocus()
+{
+ if (!bExiting)
+ m_xEdit->GrabFocus();
+}
+
+SFX_IMPL_DOCKINGWINDOW_WITHID(SmCmdBoxWrapper, SID_CMDBOXWINDOW);
+
+SmCmdBoxWrapper::SmCmdBoxWrapper(vcl::Window *pParentWindow, sal_uInt16 nId,
+ SfxBindings *pBindings,
+ SfxChildWinInfo *pInfo) :
+ SfxChildWindow(pParentWindow, nId)
+{
+ VclPtrInstance<SmCmdBoxWindow> pDialog(pBindings, this, pParentWindow);
+ SetWindow(pDialog);
+ // make window docked to the bottom initially (after first start)
+ SetAlignment(SfxChildAlignment::BOTTOM);
+ pDialog->setDeferredProperties();
+ pDialog->set_border_width(CMD_BOX_PADDING);
+ pDialog->set_margin_top(CMD_BOX_PADDING_TOP);
+ pDialog->Initialize(pInfo);
+}
+
+SFX_IMPL_SUPERCLASS_INTERFACE(SmViewShell, SfxViewShell)
+
+void SmViewShell::InitInterface_Impl()
+{
+ GetStaticInterface()->RegisterObjectBar(SFX_OBJECTBAR_TOOLS,
+ SfxVisibilityFlags::Standard | SfxVisibilityFlags::FullScreen | SfxVisibilityFlags::Server,
+ ToolbarId::Math_Toolbox);
+ //Dummy-Objectbar, to avoid quiver while activating
+
+ GetStaticInterface()->RegisterChildWindow(SmCmdBoxWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
+ GetStaticInterface()->RegisterChildWindow(SfxInfoBarContainerChild::GetChildWindowId());
+
+ GetStaticInterface()->RegisterChildWindow(::sfx2::sidebar::SidebarChildWindow::GetChildWindowId());
+}
+
+SFX_IMPL_NAMED_VIEWFACTORY(SmViewShell, "Default")
+{
+ SFX_VIEW_REGISTRATION(SmDocShell);
+}
+
+void SmViewShell::InnerResizePixel(const Point &rOfs, const Size &rSize, bool)
+{
+ Size aObjSize = GetObjectShell()->GetVisArea().GetSize();
+ if ( !aObjSize.IsEmpty() )
+ {
+ Size aProvidedSize = GetWindow()->PixelToLogic(rSize, MapMode(MapUnit::Map100thMM));
+ Fraction aZoomX(aProvidedSize.Width(), aObjSize.Width());
+ Fraction aZoomY(aProvidedSize.Height(), aObjSize.Height());
+ MapMode aMap(mxGraphicWindow->GetGraphicMapMode());
+ aMap.SetScaleX(aZoomX);
+ aMap.SetScaleY(aZoomY);
+ mxGraphicWindow->SetGraphicMapMode(aMap);
+ }
+
+ SetBorderPixel( SvBorder() );
+ mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
+ GetGraphicWidget().SetTotalSize();
+}
+
+void SmViewShell::OuterResizePixel(const Point &rOfs, const Size &rSize)
+{
+ mxGraphicWindow->SetPosSizePixel(rOfs, rSize);
+ if (GetDoc()->IsPreview())
+ mxGraphicWindow->ZoomToFitInWindow();
+}
+
+void SmViewShell::QueryObjAreaPixel( tools::Rectangle& rRect ) const
+{
+ rRect.SetSize(mxGraphicWindow->GetSizePixel());
+}
+
+void SmViewShell::SetZoomFactor( const Fraction &rX, const Fraction &rY )
+{
+ const Fraction &rFrac = std::min(rX, rY);
+ mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(tools::Long(rFrac * Fraction( 100, 1 ))));
+
+ //To avoid rounding errors base class regulates crooked values too
+ //if necessary
+ SfxViewShell::SetZoomFactor( rX, rY );
+}
+
+Size SmViewShell::GetTextLineSize(OutputDevice const & rDevice, const OUString& rLine)
+{
+ Size aSize(rDevice.GetTextWidth(rLine), rDevice.GetTextHeight());
+ const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
+
+ if (nTabPos)
+ {
+ aSize.setWidth( 0 );
+ sal_Int32 nPos = 0;
+ do
+ {
+ if (nPos > 0)
+ aSize.setWidth( ((aSize.Width() / nTabPos) + 1) * nTabPos );
+
+ const OUString aText = rLine.getToken(0, '\t', nPos);
+ aSize.AdjustWidth(rDevice.GetTextWidth(aText) );
+ }
+ while (nPos >= 0);
+ }
+
+ return aSize;
+}
+
+Size SmViewShell::GetTextSize(OutputDevice const & rDevice, std::u16string_view rText, tools::Long MaxWidth)
+{
+ Size aSize;
+ Size aTextSize;
+ if (rText.empty())
+ return aTextSize;
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) );
+ aLine = aLine.replaceAll("\r", "");
+
+ aSize = GetTextLineSize(rDevice, aLine);
+
+ if (aSize.Width() > MaxWidth)
+ {
+ do
+ {
+ OUString aText;
+ sal_Int32 m = aLine.getLength();
+ sal_Int32 nLen = m;
+
+ for (sal_Int32 n = 0; n < nLen; n++)
+ {
+ sal_Unicode cLineChar = aLine[n];
+ if ((cLineChar == ' ') || (cLineChar == '\t'))
+ {
+ aText = aLine.copy(0, n);
+ if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
+ m = n;
+ else
+ break;
+ }
+ }
+
+ aText = aLine.copy(0, m);
+ aLine = aLine.replaceAt(0, m, u"");
+ aSize = GetTextLineSize(rDevice, aText);
+ aTextSize.AdjustHeight(aSize.Height() );
+ aTextSize.setWidth( std::clamp(aSize.Width(), aTextSize.Width(), MaxWidth) );
+
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ aLine = comphelper::string::stripStart(aLine, '\t');
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ }
+ while (!aLine.isEmpty());
+ }
+ else
+ {
+ aTextSize.AdjustHeight(aSize.Height() );
+ aTextSize.setWidth( std::max(aTextSize.Width(), aSize.Width()) );
+ }
+ }
+ while (nPos >= 0);
+
+ return aTextSize;
+}
+
+void SmViewShell::DrawTextLine(OutputDevice& rDevice, const Point& rPosition, const OUString& rLine)
+{
+ Point aPoint(rPosition);
+ const tools::Long nTabPos = rLine.isEmpty() ? 0 : rDevice.approximate_digit_width() * 8;
+
+ if (nTabPos)
+ {
+ sal_Int32 nPos = 0;
+ do
+ {
+ if (nPos > 0)
+ aPoint.setX( ((aPoint.X() / nTabPos) + 1) * nTabPos );
+
+ OUString aText = rLine.getToken(0, '\t', nPos);
+ rDevice.DrawText(aPoint, aText);
+ aPoint.AdjustX(rDevice.GetTextWidth(aText) );
+ }
+ while ( nPos >= 0 );
+ }
+ else
+ rDevice.DrawText(aPoint, rLine);
+}
+
+void SmViewShell::DrawText(OutputDevice& rDevice, const Point& rPosition, std::u16string_view rText, sal_uInt16 MaxWidth)
+{
+ if (rText.empty())
+ return;
+
+ Point aPoint(rPosition);
+ Size aSize;
+
+ sal_Int32 nPos = 0;
+ do
+ {
+ OUString aLine( o3tl::getToken(rText, 0, '\n', nPos) );
+ aLine = aLine.replaceAll("\r", "");
+ aSize = GetTextLineSize(rDevice, aLine);
+ if (aSize.Width() > MaxWidth)
+ {
+ do
+ {
+ OUString aText;
+ sal_Int32 m = aLine.getLength();
+ sal_Int32 nLen = m;
+
+ for (sal_Int32 n = 0; n < nLen; n++)
+ {
+ sal_Unicode cLineChar = aLine[n];
+ if ((cLineChar == ' ') || (cLineChar == '\t'))
+ {
+ aText = aLine.copy(0, n);
+ if (GetTextLineSize(rDevice, aText).Width() < MaxWidth)
+ m = n;
+ else
+ break;
+ }
+ }
+ aText = aLine.copy(0, m);
+ aLine = aLine.replaceAt(0, m, u"");
+
+ DrawTextLine(rDevice, aPoint, aText);
+ aPoint.AdjustY(aSize.Height() );
+
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ aLine = comphelper::string::stripStart(aLine, '\t');
+ aLine = comphelper::string::stripStart(aLine, ' ');
+ }
+ while (GetTextLineSize(rDevice, aLine).Width() > MaxWidth);
+
+ // print the remaining text
+ if (!aLine.isEmpty())
+ {
+ DrawTextLine(rDevice, aPoint, aLine);
+ aPoint.AdjustY(aSize.Height() );
+ }
+ }
+ else
+ {
+ DrawTextLine(rDevice, aPoint, aLine);
+ aPoint.AdjustY(aSize.Height() );
+ }
+ }
+ while ( nPos >= 0 );
+}
+
+void SmViewShell::Impl_Print(OutputDevice &rOutDev, const SmPrintUIOptions &rPrintUIOptions, tools::Rectangle aOutRect )
+{
+ const bool bIsPrintTitle = rPrintUIOptions.getBoolValue( PRTUIOPT_TITLE_ROW, true );
+ const bool bIsPrintFrame = rPrintUIOptions.getBoolValue( PRTUIOPT_BORDER, true );
+ const bool bIsPrintFormulaText = rPrintUIOptions.getBoolValue( PRTUIOPT_FORMULA_TEXT, true );
+ SmPrintSize ePrintSize( static_cast< SmPrintSize >( rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_FORMAT, PRINT_SIZE_NORMAL ) ));
+ const sal_uInt16 nZoomFactor = static_cast< sal_uInt16 >(rPrintUIOptions.getIntValue( PRTUIOPT_PRINT_SCALE, 100 ));
+
+ rOutDev.Push();
+ rOutDev.SetLineColor( COL_BLACK );
+
+ // output text on top
+ if (bIsPrintTitle)
+ {
+ Size aSize600 (0, 600);
+ Size aSize650 (0, 650);
+ vcl::Font aFont(FAMILY_DONTKNOW, aSize600);
+
+ aFont.SetAlignment(ALIGN_TOP);
+ aFont.SetWeight(WEIGHT_BOLD);
+ aFont.SetFontSize(aSize650);
+ aFont.SetColor( COL_BLACK );
+ rOutDev.SetFont(aFont);
+
+ Size aTitleSize (GetTextSize(rOutDev, GetDoc()->GetTitle(), aOutRect.GetWidth() - 200));
+
+ aFont.SetWeight(WEIGHT_NORMAL);
+ aFont.SetFontSize(aSize600);
+ rOutDev.SetFont(aFont);
+
+ Size aDescSize (GetTextSize(rOutDev, GetDoc()->GetComment(), aOutRect.GetWidth() - 200));
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(tools::Rectangle(aOutRect.TopLeft(),
+ Size(aOutRect.GetWidth(), 100 + aTitleSize.Height() + 200 + aDescSize.Height() + 100)));
+ aOutRect.AdjustTop(200 );
+
+ // output title
+ aFont.SetWeight(WEIGHT_BOLD);
+ aFont.SetFontSize(aSize650);
+ rOutDev.SetFont(aFont);
+ Point aPoint(aOutRect.Left() + (aOutRect.GetWidth() - aTitleSize.Width()) / 2,
+ aOutRect.Top());
+ DrawText(rOutDev, aPoint, GetDoc()->GetTitle(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustTop(aTitleSize.Height() + 200 );
+
+ // output description
+ aFont.SetWeight(WEIGHT_NORMAL);
+ aFont.SetFontSize(aSize600);
+ rOutDev.SetFont(aFont);
+ aPoint.setX( aOutRect.Left() + (aOutRect.GetWidth() - aDescSize.Width()) / 2 );
+ aPoint.setY( aOutRect.Top() );
+ DrawText(rOutDev, aPoint, GetDoc()->GetComment(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustTop(aDescSize.Height() + 300 );
+ }
+
+ // output text on bottom
+ if (bIsPrintFormulaText)
+ {
+ vcl::Font aFont(FAMILY_DONTKNOW, Size(0, 600));
+ aFont.SetAlignment(ALIGN_TOP);
+ aFont.SetColor( COL_BLACK );
+
+ // get size
+ rOutDev.SetFont(aFont);
+
+ Size aSize (GetTextSize(rOutDev, GetDoc()->GetText(), aOutRect.GetWidth() - 200));
+
+ aOutRect.AdjustBottom( -(aSize.Height() + 600) );
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(tools::Rectangle(aOutRect.BottomLeft(),
+ Size(aOutRect.GetWidth(), 200 + aSize.Height() + 200)));
+
+ Point aPoint (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
+ aOutRect.Bottom() + 300);
+ DrawText(rOutDev, aPoint, GetDoc()->GetText(),
+ sal::static_int_cast< sal_uInt16 >(aOutRect.GetWidth() - 200));
+ aOutRect.AdjustBottom( -200 );
+ }
+
+ if (bIsPrintFrame)
+ rOutDev.DrawRect(aOutRect);
+
+ aOutRect.AdjustTop(100 );
+ aOutRect.AdjustLeft(100 );
+ aOutRect.AdjustBottom( -100 );
+ aOutRect.AdjustRight( -100 );
+
+ Size aSize (GetDoc()->GetSize());
+
+ MapMode OutputMapMode;
+ // PDF export should always use PRINT_SIZE_NORMAL ...
+ if (!rPrintUIOptions.getBoolValue( "IsPrinter" ) )
+ ePrintSize = PRINT_SIZE_NORMAL;
+ switch (ePrintSize)
+ {
+ case PRINT_SIZE_NORMAL:
+ OutputMapMode = MapMode(MapUnit::Map100thMM);
+ break;
+
+ case PRINT_SIZE_SCALED:
+ if (!aSize.IsEmpty())
+ {
+ Size OutputSize (rOutDev.LogicToPixel(Size(aOutRect.GetWidth(),
+ aOutRect.GetHeight()), MapMode(MapUnit::Map100thMM)));
+ Size GraphicSize (rOutDev.LogicToPixel(aSize, MapMode(MapUnit::Map100thMM)));
+ sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
+ tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
+ nZ -= 10;
+ Fraction aFraction (std::clamp(nZ, MINZOOM, sal_uInt16(100)));
+
+ OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
+ }
+ else
+ OutputMapMode = MapMode(MapUnit::Map100thMM);
+ break;
+
+ case PRINT_SIZE_ZOOMED:
+ {
+ Fraction aFraction( nZoomFactor, 100 );
+
+ OutputMapMode = MapMode(MapUnit::Map100thMM, Point(), aFraction, aFraction);
+ break;
+ }
+ }
+
+ aSize = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aSize, OutputMapMode),
+ MapMode(MapUnit::Map100thMM));
+
+ Point aPos (aOutRect.Left() + (aOutRect.GetWidth() - aSize.Width()) / 2,
+ aOutRect.Top() + (aOutRect.GetHeight() - aSize.Height()) / 2);
+
+ aPos = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aPos, MapMode(MapUnit::Map100thMM)),
+ OutputMapMode);
+ aOutRect = rOutDev.PixelToLogic(rOutDev.LogicToPixel(aOutRect, MapMode(MapUnit::Map100thMM)),
+ OutputMapMode);
+
+ rOutDev.SetMapMode(OutputMapMode);
+ rOutDev.SetClipRegion(vcl::Region(aOutRect));
+ GetDoc()->DrawFormula(rOutDev, aPos);
+ rOutDev.SetClipRegion();
+
+ rOutDev.Pop();
+}
+
+SfxPrinter* SmViewShell::GetPrinter(bool bCreate)
+{
+ SmDocShell* pDoc = GetDoc();
+ if (pDoc->HasPrinter() || bCreate)
+ return pDoc->GetPrinter();
+ return nullptr;
+}
+
+sal_uInt16 SmViewShell::SetPrinter(SfxPrinter *pNewPrinter, SfxPrinterChangeFlags nDiffFlags )
+{
+ SfxPrinter *pOld = GetDoc()->GetPrinter();
+ if ( pOld && pOld->IsPrinting() )
+ return SFX_PRINTERROR_BUSY;
+
+ if ((nDiffFlags & SfxPrinterChangeFlags::PRINTER) == SfxPrinterChangeFlags::PRINTER)
+ GetDoc()->SetPrinter( pNewPrinter );
+
+ if ((nDiffFlags & SfxPrinterChangeFlags::OPTIONS) == SfxPrinterChangeFlags::OPTIONS)
+ {
+ SmModule *pp = SM_MOD();
+ pp->GetConfig()->ItemSetToConfig(pNewPrinter->GetOptions());
+ }
+ return 0;
+}
+
+bool SmViewShell::HasPrintOptionsPage() const
+{
+ return true;
+}
+
+std::unique_ptr<SfxTabPage> SmViewShell::CreatePrintOptionsPage(weld::Container* pPage, weld::DialogController* pController,
+ const SfxItemSet &rOptions)
+{
+ return SmPrintOptionsTabPage::Create(pPage, pController, rOptions);
+}
+
+SmEditWindow *SmViewShell::GetEditWindow()
+{
+ SmCmdBoxWrapper* pWrapper = static_cast<SmCmdBoxWrapper*>(
+ GetViewFrame()->GetChildWindow(SmCmdBoxWrapper::GetChildWindowId()));
+
+ if (pWrapper != nullptr)
+ {
+ SmEditWindow& rEditWin = pWrapper->GetEditWindow();
+ return &rEditWin;
+ }
+
+ return nullptr;
+}
+
+SmElementsDockingWindow* SmViewShell::GetDockingWindow()
+{
+ auto eldockwinwrap = GetViewFrame()->GetChildWindow(SmElementsDockingWindowWrapper::GetChildWindowId());
+ if(eldockwinwrap)
+ return dynamic_cast<SmElementsDockingWindow*>(eldockwinwrap->GetWindow());
+ else
+ return nullptr;
+}
+
+void SmViewShell::SetStatusText(const OUString& rText)
+{
+ maStatusText = rText;
+ GetViewFrame()->GetBindings().Invalidate(SID_TEXTSTATUS);
+}
+
+void SmViewShell::ShowError(const SmErrorDesc* pErrorDesc)
+{
+ assert(GetDoc());
+ if (pErrorDesc || nullptr != (pErrorDesc = GetDoc()->GetParser()->GetError()) )
+ {
+ SetStatusText( pErrorDesc->m_aText );
+ GetEditWindow()->MarkError( Point( pErrorDesc->m_pNode->GetColumn(),
+ pErrorDesc->m_pNode->GetRow()));
+ }
+}
+
+void SmViewShell::NextError()
+{
+ assert(GetDoc());
+ const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->NextError();
+
+ if (pErrorDesc)
+ ShowError( pErrorDesc );
+}
+
+void SmViewShell::PrevError()
+{
+ assert(GetDoc());
+ const SmErrorDesc *pErrorDesc = GetDoc()->GetParser()->PrevError();
+
+ if (pErrorDesc)
+ ShowError( pErrorDesc );
+}
+
+void SmViewShell::Insert( SfxMedium& rMedium )
+{
+ SmDocShell *pDoc = GetDoc();
+ bool bRet = false;
+
+ uno::Reference <embed::XStorage> xStorage = rMedium.GetStorage();
+ if (xStorage.is() && xStorage->getElementNames().hasElements())
+ {
+ if (xStorage->hasByName("content.xml"))
+ {
+ // is this a fabulous math package ?
+ Reference<css::frame::XModel> xModel(pDoc->GetModel());
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bRet = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bRet)
+ return;
+
+ OUString aText = pDoc->GetText();
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->InsertText( aText );
+ else
+ {
+ SAL_WARN( "starmath", "EditWindow missing" );
+ }
+
+ pDoc->Parse();
+ pDoc->SetModified();
+
+ SfxBindings &rBnd = GetViewFrame()->GetBindings();
+ rBnd.Invalidate(SID_GRAPHIC_SM);
+ rBnd.Invalidate(SID_TEXT);
+}
+
+void SmViewShell::InsertFrom(SfxMedium &rMedium)
+{
+ bool bSuccess = false;
+ SmDocShell* pDoc = GetDoc();
+ SvStream* pStream = rMedium.GetInStream();
+
+ if (pStream)
+ {
+ const OUString& rFltName = rMedium.GetFilter()->GetFilterName();
+ if ( rFltName == MATHML_XML )
+ {
+ Reference<css::frame::XModel> xModel(pDoc->GetModel());
+ SmXMLImportWrapper aEquation(xModel); //!! modifies the result of pDoc->GetText() !!
+ bSuccess = ERRCODE_NONE == aEquation.Import(rMedium);
+ }
+ }
+
+ if (!bSuccess)
+ return;
+
+ OUString aText = pDoc->GetText();
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->InsertText(aText);
+ else
+ SAL_WARN( "starmath", "EditWindow missing" );
+
+ pDoc->Parse();
+ pDoc->SetModified();
+
+ SfxBindings& rBnd = GetViewFrame()->GetBindings();
+ rBnd.Invalidate(SID_GRAPHIC_SM);
+ rBnd.Invalidate(SID_TEXT);
+}
+
+void SmViewShell::Execute(SfxRequest& rReq)
+{
+ SmEditWindow *pWin = GetEditWindow();
+
+ switch (rReq.GetSlot())
+ {
+ case SID_FORMULACURSOR:
+ {
+ SmModule *pp = SM_MOD();
+
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem *pItem;
+
+ bool bVal;
+ if ( pArgs &&
+ SfxItemState::SET == pArgs->GetItemState( SID_FORMULACURSOR, false, &pItem))
+ bVal = static_cast<const SfxBoolItem *>(pItem)->GetValue();
+ else
+ bVal = !pp->GetConfig()->IsShowFormulaCursor();
+
+ pp->GetConfig()->SetShowFormulaCursor(bVal);
+ if (!IsInlineEditEnabled())
+ GetGraphicWidget().ShowCursor(bVal);
+ break;
+ }
+ case SID_DRAW:
+ if (pWin)
+ {
+ GetDoc()->SetText( pWin->GetText() );
+ SetStatusText(OUString());
+ ShowError( nullptr );
+ GetDoc()->Repaint();
+ }
+ break;
+
+ case SID_ZOOM_OPTIMAL:
+ mxGraphicWindow->ZoomToFitInWindow();
+ break;
+
+ case SID_ZOOMIN:
+ mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() + 25);
+ break;
+
+ case SID_ZOOMOUT:
+ SAL_WARN_IF( mxGraphicWindow->GetZoom() < 25, "starmath", "incorrect sal_uInt16 argument" );
+ mxGraphicWindow->SetZoom(mxGraphicWindow->GetZoom() - 25);
+ break;
+
+ case SID_COPYOBJECT:
+ {
+ //TODO/LATER: does not work because of UNO Tunneling - will be fixed later
+ Reference< datatransfer::XTransferable > xTrans( GetDoc()->GetModel(), uno::UNO_QUERY );
+ if( xTrans.is() )
+ {
+ auto pTrans = comphelper::getFromUnoTunnel<TransferableHelper>(xTrans);
+ if (pTrans)
+ {
+ SmEditWindow *pEditWin = GetEditWindow();
+ pTrans->CopyToClipboard(pEditWin->GetClipboard());
+ }
+ }
+ }
+ break;
+
+ case SID_PASTEOBJECT:
+ {
+ SmEditWindow *pEditWin = GetEditWindow();
+ TransferableDataHelper aData(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard()));
+ uno::Reference < io::XInputStream > xStrm;
+ SotClipboardFormatId nId;
+ if( aData.GetTransferable().is() &&
+ ( aData.HasFormat( nId = SotClipboardFormatId::EMBEDDED_OBJ ) ||
+ (aData.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR ) &&
+ aData.HasFormat( nId = SotClipboardFormatId::EMBED_SOURCE ))))
+ xStrm = aData.GetInputStream(nId, OUString());
+
+ if (xStrm.is())
+ {
+ try
+ {
+ uno::Reference < embed::XStorage > xStorage =
+ ::comphelper::OStorageHelper::GetStorageFromInputStream( xStrm, ::comphelper::getProcessComponentContext() );
+ SfxMedium aMedium( xStorage, OUString() );
+ Insert( aMedium );
+ GetDoc()->UpdateText();
+ }
+ catch (uno::Exception &)
+ {
+ SAL_WARN( "starmath", "SmViewShell::Execute (SID_PASTEOBJECT): failed to get storage from input stream" );
+ }
+ }
+ }
+ break;
+
+
+ case SID_CUT:
+ if (pWin)
+ pWin->Cut();
+ break;
+
+ case SID_COPY:
+ if (pWin)
+ {
+ if (pWin->IsAllSelected())
+ {
+ GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_COPYOBJECT, SfxCallMode::RECORD,
+ { new SfxVoidItem(SID_COPYOBJECT) });
+ }
+ else
+ pWin->Copy();
+ }
+ break;
+
+ case SID_PASTE:
+ {
+ bool bCallExec = nullptr == pWin;
+ if( !bCallExec )
+ {
+ SmEditWindow *pEditWin = GetEditWindow();
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromClipboard(
+ pEditWin->GetClipboard()));
+
+ if( aDataHelper.GetTransferable().is() &&
+ aDataHelper.HasFormat( SotClipboardFormatId::STRING ))
+ pWin->Paste();
+ else
+ bCallExec = true;
+ }
+ if( bCallExec )
+ {
+ GetViewFrame()->GetDispatcher()->ExecuteList(
+ SID_PASTEOBJECT, SfxCallMode::RECORD,
+ { new SfxVoidItem(SID_PASTEOBJECT) });
+ }
+ }
+ break;
+
+ case SID_DELETE:
+ if (pWin)
+ pWin->Delete();
+ break;
+
+ case SID_SELECT:
+ if (pWin)
+ pWin->SelectAll();
+ break;
+
+ case SID_INSERTCOMMANDTEXT:
+ {
+ const SfxStringItem& rItem = static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTCOMMANDTEXT));
+
+ if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
+ {
+ pWin->InsertText(rItem.GetValue());
+ }
+ if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
+ {
+ GetDoc()->GetCursor().InsertCommandText(rItem.GetValue());
+ GetGraphicWidget().GrabFocus();
+ }
+ break;
+
+ }
+
+ case SID_INSERTSPECIAL:
+ {
+ const SfxStringItem& rItem =
+ static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_INSERTSPECIAL));
+
+ if (pWin && (mbInsertIntoEditWindow || !IsInlineEditEnabled()))
+ pWin->InsertText(rItem.GetValue());
+ if (IsInlineEditEnabled() && (GetDoc() && !mbInsertIntoEditWindow))
+ GetDoc()->GetCursor().InsertSpecial(rItem.GetValue());
+ break;
+ }
+
+ case SID_IMPORT_FORMULA:
+ {
+ mpRequest.reset(new SfxRequest( rReq ));
+ mpDocInserter.reset(new ::sfx2::DocumentInserter(pWin ? pWin->GetFrameWeld() : nullptr,
+ GetDoc()->GetFactory().GetFactoryName()));
+ mpDocInserter->StartExecuteModal( LINK( this, SmViewShell, DialogClosedHdl ) );
+ break;
+ }
+
+ case SID_IMPORT_MATHML_CLIPBOARD:
+ {
+ SmEditWindow *pEditWin = GetEditWindow();
+ TransferableDataHelper aDataHelper(TransferableDataHelper::CreateFromClipboard(pEditWin->GetClipboard()));
+ uno::Reference < io::XInputStream > xStrm;
+ if ( aDataHelper.GetTransferable().is() )
+ {
+ SotClipboardFormatId nId = SotClipboardFormatId::MATHML;
+ if (aDataHelper.HasFormat(nId))
+ {
+ xStrm = aDataHelper.GetInputStream(nId, "");
+ if (xStrm.is())
+ {
+ SfxMedium aClipboardMedium;
+ aClipboardMedium.GetItemSet(); //generate initial itemset, not sure if necessary
+ std::shared_ptr<const SfxFilter> pMathFilter =
+ SfxFilter::GetFilterByName(MATHML_XML);
+ aClipboardMedium.SetFilter(pMathFilter);
+ aClipboardMedium.setStreamToLoadFrom(xStrm, true /*bIsReadOnly*/);
+ InsertFrom(aClipboardMedium);
+ GetDoc()->UpdateText();
+ }
+ }
+ else
+ {
+ nId = SotClipboardFormatId::STRING;
+ if (aDataHelper.HasFormat(nId))
+ {
+ // In case of FORMAT_STRING no stream exists, need to generate one
+ OUString aString;
+ if (aDataHelper.GetString( nId, aString))
+ {
+ // tdf#117091 force xml declaration to exist
+ if (!aString.startsWith("<?xml"))
+ aString = "<?xml version=\"1.0\"?>\n" + aString;
+
+ SfxMedium aClipboardMedium;
+ aClipboardMedium.GetItemSet(); //generates initial itemset, not sure if necessary
+ std::shared_ptr<const SfxFilter> pMathFilter =
+ SfxFilter::GetFilterByName(MATHML_XML);
+ aClipboardMedium.SetFilter(pMathFilter);
+
+ SvMemoryStream aStrm( const_cast<sal_Unicode *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode), StreamMode::READ);
+ uno::Reference<io::XInputStream> xStrm2( new ::utl::OInputStreamWrapper(aStrm) );
+ aClipboardMedium.setStreamToLoadFrom(xStrm2, true /*bIsReadOnly*/);
+ InsertFrom(aClipboardMedium);
+ GetDoc()->UpdateText();
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ case SID_NEXTERR:
+ NextError();
+ if (pWin)
+ pWin->GrabFocus();
+ break;
+
+ case SID_PREVERR:
+ PrevError();
+ if (pWin)
+ pWin->GrabFocus();
+ break;
+
+ case SID_NEXTMARK:
+ if (pWin)
+ {
+ pWin->SelNextMark();
+ pWin->GrabFocus();
+ }
+ break;
+
+ case SID_PREVMARK:
+ if (pWin)
+ {
+ pWin->SelPrevMark();
+ pWin->GrabFocus();
+ }
+ break;
+
+ case SID_TEXTSTATUS:
+ {
+ if (rReq.GetArgs() != nullptr)
+ {
+ const SfxStringItem& rItem =
+ static_cast<const SfxStringItem&>(rReq.GetArgs()->Get(SID_TEXTSTATUS));
+
+ SetStatusText(rItem.GetValue());
+ }
+
+ break;
+ }
+
+ case SID_GETEDITTEXT:
+ if (pWin && !pWin->GetText().isEmpty())
+ GetDoc()->SetText( pWin->GetText() );
+ break;
+
+ case SID_ATTR_ZOOM:
+ {
+ if ( !GetViewFrame()->GetFrame().IsInPlace() )
+ {
+ const SfxItemSet *pSet = rReq.GetArgs();
+ if ( pSet )
+ {
+ ZoomByItemSet(pSet);
+ }
+ else
+ {
+ SfxItemSetFixed<SID_ATTR_ZOOM, SID_ATTR_ZOOM> aSet( SmDocShell::GetPool() );
+ aSet.Put( SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
+ SvxAbstractDialogFactory* pFact = SvxAbstractDialogFactory::Create();
+ ScopedVclPtr<AbstractSvxZoomDialog> xDlg(pFact->CreateSvxZoomDialog(GetViewFrame()->GetWindow().GetFrameWeld(), aSet));
+ xDlg->SetLimits( MINZOOM, MAXZOOM );
+ if (xDlg->Execute() != RET_CANCEL)
+ ZoomByItemSet(xDlg->GetOutputItemSet());
+ }
+ }
+ }
+ break;
+
+ case SID_ATTR_ZOOMSLIDER:
+ {
+ const SfxItemSet *pArgs = rReq.GetArgs();
+ const SfxPoolItem* pItem;
+
+ if ( pArgs && SfxItemState::SET == pArgs->GetItemState(SID_ATTR_ZOOMSLIDER, true, &pItem ) )
+ {
+ const sal_uInt16 nCurrentZoom = static_cast<const SvxZoomSliderItem *>(pItem)->GetValue();
+ mxGraphicWindow->SetZoom(nCurrentZoom);
+ }
+ }
+ break;
+
+ case SID_ELEMENTSDOCKINGWINDOW:
+ {
+ GetViewFrame()->ToggleChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
+ GetViewFrame()->GetBindings().Invalidate( SID_ELEMENTSDOCKINGWINDOW );
+
+ rReq.Ignore ();
+ }
+ break;
+
+ case SID_UNICODE_NOTATION_TOGGLE:
+ {
+ EditEngine* pEditEngine = nullptr;
+ if( pWin )
+ pEditEngine = pWin->GetEditEngine();
+
+ EditView* pEditView = nullptr;
+ if( pEditEngine )
+ pEditView = pEditEngine->GetView();
+
+ if( pEditView )
+ {
+ const OUString sInput = pEditView->GetSurroundingText();
+ ESelection aSel( pWin->GetSelection() );
+
+ if ( aSel.nStartPos > aSel.nEndPos )
+ aSel.nEndPos = aSel.nStartPos;
+
+ //calculate a valid end-position by reading logical characters
+ sal_Int32 nUtf16Pos=0;
+ while( (nUtf16Pos < sInput.getLength()) && (nUtf16Pos < aSel.nEndPos) )
+ {
+ sInput.iterateCodePoints(&nUtf16Pos);
+ if( nUtf16Pos > aSel.nEndPos )
+ aSel.nEndPos = nUtf16Pos;
+ }
+
+ ToggleUnicodeCodepoint aToggle;
+ while( nUtf16Pos && aToggle.AllowMoreInput( sInput[nUtf16Pos-1]) )
+ --nUtf16Pos;
+ const OUString sReplacement = aToggle.ReplacementString();
+ if( !sReplacement.isEmpty() )
+ {
+ pEditView->SetSelection( aSel );
+ pEditEngine->UndoActionStart(EDITUNDO_REPLACEALL);
+ aSel.nStartPos = aSel.nEndPos - aToggle.StringToReplace().getLength();
+ pWin->SetSelection( aSel );
+ pEditView->InsertText( sReplacement, true );
+ pEditEngine->UndoActionEnd();
+ pWin->Flush();
+ }
+ }
+ }
+ break;
+
+ case SID_SYMBOLS_CATALOGUE:
+ {
+
+ // get device used to retrieve the FontList
+ SmDocShell *pDoc = GetDoc();
+ OutputDevice *pDev = pDoc->GetPrinter();
+ if (!pDev || pDev->GetFontFaceCollectionCount() == 0)
+ pDev = &SM_MOD()->GetDefaultVirtualDev();
+ SAL_WARN_IF( !pDev, "starmath", "device for font list missing" );
+
+ SmModule *pp = SM_MOD();
+ SmSymbolDialog aDialog(pWin ? pWin->GetFrameWeld() : nullptr, pDev, pp->GetSymbolManager(), *this);
+ aDialog.run();
+ }
+ break;
+ }
+ rReq.Done();
+}
+
+
+void SmViewShell::GetState(SfxItemSet &rSet)
+{
+ SfxWhichIter aIter(rSet);
+
+ SmEditWindow *pEditWin = GetEditWindow();
+ for (sal_uInt16 nWh = aIter.FirstWhich(); nWh != 0; nWh = aIter.NextWhich())
+ {
+ switch (nWh)
+ {
+ case SID_CUT:
+ case SID_COPY:
+ case SID_DELETE:
+ if (! pEditWin || ! pEditWin->IsSelected())
+ rSet.DisableItem(nWh);
+ break;
+
+ case SID_PASTE:
+ if (pEditWin)
+ {
+ TransferableDataHelper aDataHelper(
+ TransferableDataHelper::CreateFromClipboard(
+ pEditWin->GetClipboard()) );
+
+ mbPasteState = aDataHelper.GetTransferable().is() &&
+ ( aDataHelper.HasFormat( SotClipboardFormatId::STRING ) ||
+ aDataHelper.HasFormat( SotClipboardFormatId::EMBEDDED_OBJ ) ||
+ (aDataHelper.HasFormat( SotClipboardFormatId::OBJECTDESCRIPTOR )
+ && aDataHelper.HasFormat( SotClipboardFormatId::EMBED_SOURCE )));
+ }
+ if( !mbPasteState )
+ rSet.DisableItem( nWh );
+ break;
+
+ case SID_ATTR_ZOOM:
+ rSet.Put(SvxZoomItem( SvxZoomType::PERCENT, mxGraphicWindow->GetZoom()));
+ [[fallthrough]];
+ case SID_ZOOMIN:
+ case SID_ZOOMOUT:
+ case SID_ZOOM_OPTIMAL:
+ if ( GetViewFrame()->GetFrame().IsInPlace() )
+ rSet.DisableItem( nWh );
+ break;
+
+ case SID_ATTR_ZOOMSLIDER :
+ {
+ const sal_uInt16 nCurrentZoom = mxGraphicWindow->GetZoom();
+ SvxZoomSliderItem aZoomSliderItem( nCurrentZoom, MINZOOM, MAXZOOM );
+ aZoomSliderItem.AddSnappingPoint( 100 );
+ rSet.Put( aZoomSliderItem );
+ }
+ break;
+
+ case SID_NEXTERR:
+ case SID_PREVERR:
+ case SID_NEXTMARK:
+ case SID_PREVMARK:
+ case SID_DRAW:
+ case SID_SELECT:
+ if (! pEditWin || pEditWin->IsEmpty())
+ rSet.DisableItem(nWh);
+ break;
+
+ case SID_TEXTSTATUS:
+ {
+ rSet.Put(SfxStringItem(nWh, maStatusText));
+ }
+ break;
+
+ case SID_FORMULACURSOR:
+ {
+ SmModule *pp = SM_MOD();
+ rSet.Put(SfxBoolItem(nWh, pp->GetConfig()->IsShowFormulaCursor()));
+ }
+ break;
+ case SID_ELEMENTSDOCKINGWINDOW:
+ {
+ bool bState = false;
+ SfxChildWindow *pChildWnd = GetViewFrame()->
+ GetChildWindow( SmElementsDockingWindowWrapper::GetChildWindowId() );
+ if (pChildWnd && pChildWnd->GetWindow()->IsVisible())
+ bState = true;
+ rSet.Put(SfxBoolItem(SID_ELEMENTSDOCKINGWINDOW, bState));
+ }
+ break;
+ }
+ }
+}
+
+namespace
+{
+class SmController : public SfxBaseController
+{
+public:
+ SmController(SfxViewShell& rViewShell)
+ : SfxBaseController(&rViewShell)
+ , mpSelectionChangeHandler(new svx::sidebar::SelectionChangeHandler(
+ GetContextName, this, vcl::EnumContext::Context::Math))
+ {
+ mpSelectionChangeHandler->Connect();
+ rViewShell.SetContextName(GetContextName());
+ }
+ ~SmController() { mpSelectionChangeHandler->Disconnect(); }
+
+ // css::frame::XController
+ void SAL_CALL attachFrame(const css::uno::Reference<css::frame::XFrame>& xFrame) override
+ {
+ SfxBaseController::attachFrame(xFrame);
+
+ mpSelectionChangeHandler->selectionChanged({}); // Installs the correct context
+ }
+
+private:
+ static OUString GetContextName() { return "Math"; } // Static constant for now
+
+ rtl::Reference<svx::sidebar::SelectionChangeHandler> mpSelectionChangeHandler;
+};
+}
+
+SmViewShell::SmViewShell(SfxViewFrame *pFrame_, SfxViewShell *)
+ : SfxViewShell(pFrame_, SfxViewShellFlags::HAS_PRINTOPTIONS)
+ , mxGraphicWindow(VclPtr<SmGraphicWindow>::Create(*this))
+ , maGraphicController(mxGraphicWindow->GetGraphicWidget(), SID_GRAPHIC_SM, pFrame_->GetBindings())
+ , mbPasteState(false)
+ , mbInsertIntoEditWindow(false)
+{
+ SetStatusText(OUString());
+ SetWindow(mxGraphicWindow.get());
+ SfxShell::SetName("SmView");
+ SfxShell::SetUndoManager( &GetDoc()->GetEditEngine().GetUndoManager() );
+ SetController(new SmController(*this));
+}
+
+SmViewShell::~SmViewShell()
+{
+ //!! this view shell is not active anymore !!
+ // Thus 'SmGetActiveView' will give a 0 pointer.
+ // Thus we need to supply this view as argument
+ SmEditWindow *pEditWin = GetEditWindow();
+ if (pEditWin)
+ pEditWin->DeleteEditView();
+ mxGraphicWindow.disposeAndClear();
+}
+
+void SmViewShell::Deactivate( bool bIsMDIActivate )
+{
+ SmEditWindow *pEdit = GetEditWindow();
+ if ( pEdit )
+ pEdit->Flush();
+
+ SfxViewShell::Deactivate( bIsMDIActivate );
+}
+
+void SmViewShell::Activate( bool bIsMDIActivate )
+{
+ SfxViewShell::Activate( bIsMDIActivate );
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ // In LOK, activate in-place editing
+ GetGraphicWidget().GrabFocus();
+ }
+ else if (SmEditWindow *pEdit = GetEditWindow())
+ {
+ //! Since there is no way to be informed if a "drag and drop"
+ //! event has taken place, we call SetText here in order to
+ //! synchronize the GraphicWindow display with the text in the
+ //! EditEngine.
+ SmDocShell *pDoc = GetDoc();
+ pDoc->SetText( pDoc->GetEditEngine().GetText() );
+
+ if ( bIsMDIActivate )
+ pEdit->GrabFocus();
+ }
+}
+
+IMPL_LINK( SmViewShell, DialogClosedHdl, sfx2::FileDialogHelper*, _pFileDlg, void )
+{
+ assert(_pFileDlg && "SmViewShell::DialogClosedHdl(): no file dialog");
+ assert(mpDocInserter && "ScDocShell::DialogClosedHdl(): no document inserter");
+
+ if ( ERRCODE_NONE == _pFileDlg->GetError() )
+ {
+ std::unique_ptr<SfxMedium> pMedium = mpDocInserter->CreateMedium();
+
+ if ( pMedium )
+ {
+ if ( pMedium->IsStorage() )
+ Insert( *pMedium );
+ else
+ InsertFrom( *pMedium );
+ pMedium.reset();
+
+ SmDocShell* pDoc = GetDoc();
+ pDoc->UpdateText();
+ pDoc->ArrangeFormula();
+ pDoc->Repaint();
+ // adjust window, repaint, increment ModifyCount,...
+ GetViewFrame()->GetBindings().Invalidate(SID_GRAPHIC_SM);
+ }
+ }
+
+ mpRequest->SetReturnValue( SfxBoolItem( mpRequest->GetSlot(), true ) );
+ mpRequest->Done();
+}
+
+void SmViewShell::Notify( SfxBroadcaster& , const SfxHint& rHint )
+{
+ switch( rHint.GetId() )
+ {
+ case SfxHintId::ModeChanged:
+ case SfxHintId::DocChanged:
+ GetViewFrame()->GetBindings().InvalidateAll(false);
+ break;
+ default:
+ break;
+ }
+}
+
+bool SmViewShell::IsInlineEditEnabled()
+{
+ return comphelper::LibreOfficeKit::isActive()
+ || officecfg::Office::Common::Misc::ExperimentalMode::get();
+}
+
+void SmViewShell::ZoomByItemSet(const SfxItemSet *pSet)
+{
+ assert(pSet);
+ const SvxZoomItem &rZoom = pSet->Get(SID_ATTR_ZOOM);
+ switch( rZoom.GetType() )
+ {
+ case SvxZoomType::PERCENT:
+ mxGraphicWindow->SetZoom(sal::static_int_cast<sal_uInt16>(rZoom.GetValue ()));
+ break;
+
+ case SvxZoomType::OPTIMAL:
+ mxGraphicWindow->ZoomToFitInWindow();
+ break;
+
+ case SvxZoomType::PAGEWIDTH:
+ case SvxZoomType::WHOLEPAGE:
+ {
+ const MapMode aMap( MapUnit::Map100thMM );
+ SfxPrinter *pPrinter = GetPrinter( true );
+ tools::Rectangle OutputRect(Point(), pPrinter->GetOutputSize());
+ Size OutputSize(pPrinter->LogicToPixel(Size(OutputRect.GetWidth(),
+ OutputRect.GetHeight()), aMap));
+ Size GraphicSize(pPrinter->LogicToPixel(GetDoc()->GetSize(), aMap));
+ sal_uInt16 nZ = sal::static_int_cast<sal_uInt16>(std::min(tools::Long(Fraction(OutputSize.Width() * 100, GraphicSize.Width())),
+ tools::Long(Fraction(OutputSize.Height() * 100, GraphicSize.Height()))));
+ mxGraphicWindow->SetZoom(nZ);
+ break;
+ }
+ default:
+ break;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/visitors.cxx b/starmath/source/visitors.cxx
new file mode 100644
index 000000000..980c6346c
--- /dev/null
+++ b/starmath/source/visitors.cxx
@@ -0,0 +1,2727 @@
+/* -*- 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 <rtl/math.hxx>
+#include <sal/log.hxx>
+#include <tools/gen.hxx>
+#include <vcl/lineinfo.hxx>
+#include <visitors.hxx>
+#include "tmpdevice.hxx"
+#include <cursor.hxx>
+
+#include <starmathdatabase.hxx>
+
+// SmDefaultingVisitor
+
+void SmDefaultingVisitor::Visit( SmTableNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBraceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmOperNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmAlignNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmAttributeNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmFontNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmUnHorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinHorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinVerNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmSubSupNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmMatrixNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmPlaceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmTextNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmSpecialNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmBlankNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmErrorNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmLineNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmExpressionNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmPolyLineNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRootNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmRectangleNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+void SmDefaultingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ DefaultVisit( pNode );
+}
+
+// SmCaretDrawingVisitor
+
+SmCaretDrawingVisitor::SmCaretDrawingVisitor( OutputDevice& rDevice,
+ SmCaretPos position,
+ Point offset,
+ bool caretVisible )
+ : mrDev( rDevice )
+ , maPos( position )
+ , maOffset( offset )
+ , mbCaretVisible( caretVisible )
+{
+ SAL_WARN_IF( !position.IsValid(), "starmath", "Cannot draw invalid position!" );
+ if( !position.IsValid( ) )
+ return;
+
+ //Save device state
+ mrDev.Push( vcl::PushFlags::FONT | vcl::PushFlags::MAPMODE | vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR | vcl::PushFlags::TEXTCOLOR );
+
+ maPos.pSelectedNode->Accept( this );
+ //Restore device state
+ mrDev.Pop( );
+}
+
+void SmCaretDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ tools::Long i = maPos.nIndex;
+
+ mrDev.SetFont( pNode->GetFont( ) );
+
+ //Find the line
+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
+
+ //Find coordinates
+ tools::Long left = pNode->GetLeft( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, i ) + maOffset.X( );
+ tools::Long top = pLine->GetTop( ) + maOffset.Y( );
+ tools::Long height = pLine->GetHeight( );
+ tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
+ tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
+
+ //Set color
+ mrDev.SetLineColor( COL_BLACK );
+
+ if ( mbCaretVisible ) {
+ //Draw vertical line
+ Point p1( left, top );
+ Point p2( left, top + height );
+ mrDev.DrawLine( p1, p2 );
+ }
+
+ //Underline the line
+ Point aLeft( left_line, top + height );
+ Point aRight( right_line, top + height );
+ mrDev.DrawLine( aLeft, aRight );
+}
+
+void SmCaretDrawingVisitor::DefaultVisit( SmNode* pNode )
+{
+ //Find the line
+ SmNode* pLine = SmCursor::FindTopMostNodeInLine( pNode );
+
+ //Find coordinates
+ tools::Long left = pNode->GetLeft( ) + maOffset.X( ) + ( maPos.nIndex == 1 ? pNode->GetWidth( ) : 0 );
+ tools::Long top = pLine->GetTop( ) + maOffset.Y( );
+ tools::Long height = pLine->GetHeight( );
+ tools::Long left_line = pLine->GetLeft( ) + maOffset.X( );
+ tools::Long right_line = pLine->GetRight( ) + maOffset.X( );
+
+ //Set color
+ mrDev.SetLineColor( COL_BLACK );
+
+ if ( mbCaretVisible ) {
+ //Draw vertical line
+ Point p1( left, top );
+ Point p2( left, top + height );
+ mrDev.DrawLine( p1, p2 );
+ }
+
+ //Underline the line
+ Point aLeft( left_line, top + height );
+ Point aRight( right_line, top + height );
+ mrDev.DrawLine( aLeft, aRight );
+}
+
+// SmCaretPos2LineVisitor
+
+void SmCaretPos2LineVisitor::Visit( SmTextNode* pNode )
+{
+ //Save device state
+ mpDev->Push( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR );
+
+ tools::Long i = maPos.nIndex;
+
+ mpDev->SetFont( pNode->GetFont( ) );
+
+ //Find coordinates
+ tools::Long left = pNode->GetLeft( ) + mpDev->GetTextWidth( pNode->GetText( ), 0, i );
+ tools::Long top = pNode->GetTop( );
+ tools::Long height = pNode->GetHeight( );
+
+ maLine = SmCaretLine( left, top, height );
+
+ //Restore device state
+ mpDev->Pop( );
+}
+
+void SmCaretPos2LineVisitor::DefaultVisit( SmNode* pNode )
+{
+ //Vertical line ( code from SmCaretDrawingVisitor )
+ Point p1 = pNode->GetTopLeft( );
+ if( maPos.nIndex == 1 )
+ p1.Move( pNode->GetWidth( ), 0 );
+
+ maLine = SmCaretLine( p1.X( ), p1.Y( ), pNode->GetHeight( ) );
+}
+
+
+// SmDrawingVisitor
+
+void SmDrawingVisitor::Visit( SmTableNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBraceNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmOperNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmAlignNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmAttributeNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmFontNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmUnHorNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinHorNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinVerNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmSubSupNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmMatrixNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmPlaceNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ DrawTextNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmSpecialNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmBlankNode* )
+{
+}
+
+void SmDrawingVisitor::Visit( SmErrorNode* pNode )
+{
+ DrawSpecialNode( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmLineNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmExpressionNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmRootNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ DrawChildren( pNode );
+}
+
+void SmDrawingVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ // draw root-sign itself
+ DrawSpecialNode( pNode );
+
+ SmTmpDevice aTmpDev( mrDev, true );
+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
+ mrDev.SetLineColor( );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ // since the width is always unscaled it corresponds to the _original_
+ // _unscaled_ font height to be used, we use that to calculate the
+ // bar height. Thus it is independent of the arguments height.
+ // ( see display of sqrt QQQ versus sqrt stack{Q#Q#Q#Q} )
+ tools::Long nBarHeight = pNode->GetWidth( ) * 7 / 100;
+ tools::Long nBarWidth = pNode->GetBodyWidth( ) + pNode->GetBorderWidth( );
+ Point aBarOffset( pNode->GetWidth( ), +pNode->GetBorderWidth( ) );
+ Point aBarPos( maPosition + aBarOffset );
+
+ tools::Rectangle aBar( aBarPos, Size( nBarWidth, nBarHeight ) );
+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
+ //! increasing zoomfactor.
+ // This is done by shifting its output-position to a point that
+ // corresponds exactly to a pixel on the output device.
+ Point aDrawPos( mrDev.PixelToLogic( mrDev.LogicToPixel( aBar.TopLeft( ) ) ) );
+ aBar.SetPos( aDrawPos );
+
+ mrDev.DrawRect( aBar );
+}
+
+void SmDrawingVisitor::Visit( SmPolyLineNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ tools::Long nBorderwidth = pNode->GetFont( ).GetBorderWidth( );
+
+ LineInfo aInfo;
+ aInfo.SetWidth( pNode->GetWidth( ) - 2 * nBorderwidth );
+
+ Point aOffset ( Point( ) - pNode->GetPolygon( ).GetBoundRect( ).TopLeft( )
+ + Point( nBorderwidth, nBorderwidth ) ),
+ aPos ( maPosition + aOffset );
+ pNode->GetPolygon( ).Move( aPos.X( ), aPos.Y( ) ); //Works because Polygon wraps a pointer
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetLineColor( pNode->GetFont( ).GetColor( ) );
+
+ mrDev.DrawPolyLine( pNode->GetPolygon( ), aInfo );
+}
+
+void SmDrawingVisitor::Visit( SmRectangleNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetFillColor( pNode->GetFont( ).GetColor( ) );
+ mrDev.SetLineColor( );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ sal_uLong nTmpBorderWidth = pNode->GetFont( ).GetBorderWidth( );
+
+ // get rectangle and remove borderspace
+ tools::Rectangle aTmp ( pNode->AsRectangle( ) + maPosition - pNode->GetTopLeft( ) );
+ aTmp.AdjustLeft(nTmpBorderWidth );
+ aTmp.AdjustRight( -sal_Int32(nTmpBorderWidth) );
+ aTmp.AdjustTop(nTmpBorderWidth );
+ aTmp.AdjustBottom( -sal_Int32(nTmpBorderWidth) );
+
+ SAL_WARN_IF( aTmp.IsEmpty(), "starmath", "Empty rectangle" );
+
+ //! avoid GROWING AND SHRINKING of drawn rectangle when constantly
+ //! increasing zoomfactor.
+ // This is done by shifting its output-position to a point that
+ // corresponds exactly to a pixel on the output device.
+ Point aPos ( mrDev.PixelToLogic( mrDev.LogicToPixel( aTmp.TopLeft( ) ) ) );
+ aTmp.SetPos( aPos );
+
+ mrDev.DrawRect( aTmp );
+}
+
+void SmDrawingVisitor::DrawTextNode( SmTextNode* pNode )
+{
+ if ( pNode->IsPhantom() || pNode->GetText().isEmpty() || pNode->GetText()[0] == '\0' )
+ return;
+
+ SmTmpDevice aTmpDev ( mrDev, false );
+ aTmpDev.SetFont( pNode->GetFont( ) );
+
+ Point aPos ( maPosition );
+ aPos.AdjustY(pNode->GetBaselineOffset( ) );
+ // round to pixel coordinate
+ aPos = mrDev.PixelToLogic( mrDev.LogicToPixel( aPos ) );
+
+ mrDev.DrawStretchText( aPos, pNode->GetWidth( ), pNode->GetText( ) );
+}
+
+void SmDrawingVisitor::DrawSpecialNode( SmSpecialNode* pNode )
+{
+ //! since this chars might come from any font, that we may not have
+ //! set to ALIGN_BASELINE yet, we do it now.
+ pNode->GetFont( ).SetAlignment( ALIGN_BASELINE );
+
+ DrawTextNode( pNode );
+}
+
+void SmDrawingVisitor::DrawChildren( SmStructureNode* pNode )
+{
+ if ( pNode->IsPhantom( ) )
+ return;
+
+ Point rPosition = maPosition;
+
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Point aOffset ( pChild->GetTopLeft( ) - pNode->GetTopLeft( ) );
+ maPosition = rPosition + aOffset;
+ pChild->Accept( this );
+ }
+}
+
+// SmSetSelectionVisitor
+
+SmSetSelectionVisitor::SmSetSelectionVisitor( SmCaretPos startPos, SmCaretPos endPos, SmNode* pTree)
+ : maStartPos(startPos)
+ , maEndPos(endPos)
+ , mbSelecting(false)
+{
+ //Assume that pTree is a SmTableNode
+ SAL_WARN_IF(pTree->GetType() != SmNodeType::Table, "starmath", "pTree should be a SmTableNode!");
+ //Visit root node, this is special as this node cannot be selected, but its children can!
+ if(pTree->GetType() == SmNodeType::Table){
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pTree && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pTree && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ SAL_WARN_IF(mbSelecting, "starmath", "Caret positions needed to set mbSelecting about, shouldn't be possible!");
+
+ //Visit lines
+ for( auto pChild : *static_cast<SmStructureNode*>(pTree) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ //If we started a selection in this line and it haven't ended, we do that now!
+ if(mbSelecting) {
+ mbSelecting = false;
+ SetSelectedOnAll(pChild);
+ //Set maStartPos and maEndPos to invalid positions, this ensures that an unused
+ //start or end (because we forced end above), doesn't start a new selection.
+ maStartPos = maEndPos = SmCaretPos();
+ }
+ }
+ //Check if pTree isn't selected
+ SAL_WARN_IF(pTree->IsSelected(), "starmath", "pTree should never be selected!");
+ //Discard the selection if there's a bug (it's better than crashing)
+ if(pTree->IsSelected())
+ SetSelectedOnAll(pTree, false);
+ }else //This shouldn't happen, but I don't see any reason to die if it does
+ pTree->Accept(this);
+}
+
+void SmSetSelectionVisitor::SetSelectedOnAll( SmNode* pSubTree, bool IsSelected ) {
+ pSubTree->SetSelected( IsSelected );
+
+ if(pSubTree->GetNumSubNodes() == 0)
+ return;
+ //Quick BFS to set all selections
+ for( auto pChild : *static_cast<SmStructureNode*>(pSubTree) )
+ {
+ if(!pChild)
+ continue;
+ SetSelectedOnAll( pChild, IsSelected );
+ }
+}
+
+void SmSetSelectionVisitor::DefaultVisit( SmNode* pNode ) {
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+
+ //Cache current state
+ bool WasSelecting = mbSelecting;
+ bool ChangedState = false;
+
+ //Set selected
+ pNode->SetSelected( mbSelecting );
+
+ //Visit children
+ if(pNode->GetNumSubNodes() > 0)
+ {
+ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ ChangedState = ( WasSelecting != mbSelecting ) || ChangedState;
+ }
+ }
+
+ //If state changed
+ if( ChangedState )
+ {
+ //Select this node and all of its children
+ //(Make exception for SmBracebodyNode)
+ if( pNode->GetType() != SmNodeType::Bracebody ||
+ !pNode->GetParent() ||
+ pNode->GetParent()->GetType() != SmNodeType::Brace )
+ SetSelectedOnAll( pNode );
+ else
+ SetSelectedOnAll( pNode->GetParent() );
+ /* If the equation is: sqrt{2 + 4} + 5
+ * And the selection is: sqrt{2 + [4} +] 5
+ * Where [ denotes maStartPos and ] denotes maEndPos
+ * Then the sqrt node should be selected, so that the
+ * effective selection is: [sqrt{2 + 4} +] 5
+ * The same is the case if we swap maStartPos and maEndPos.
+ */
+ }
+
+ //Change state if maStartPos is after this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
+ {
+ mbSelecting = !mbSelecting;
+ }
+ //Change state if maEndPos is after of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
+ {
+ mbSelecting = !mbSelecting;
+ }
+}
+
+void SmSetSelectionVisitor::VisitCompositionNode( SmStructureNode* pNode )
+{
+ //Change state if maStartPos is in front of this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is in front of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 0 )
+ mbSelecting = !mbSelecting;
+
+ //Cache current state
+ bool WasSelecting = mbSelecting;
+
+ //Visit children
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+
+ //Set selected, if everything was selected
+ pNode->SetSelected( WasSelecting && mbSelecting );
+
+ //Change state if maStartPos is after this node
+ if( maStartPos.pSelectedNode == pNode && maStartPos.nIndex == 1 )
+ mbSelecting = !mbSelecting;
+ //Change state if maEndPos is after of this node
+ if( maEndPos.pSelectedNode == pNode && maEndPos.nIndex == 1 )
+ mbSelecting = !mbSelecting;
+}
+
+void SmSetSelectionVisitor::Visit( SmTextNode* pNode ) {
+ tools::Long i1 = -1,
+ i2 = -1;
+ if( maStartPos.pSelectedNode == pNode )
+ i1 = maStartPos.nIndex;
+ if( maEndPos.pSelectedNode == pNode )
+ i2 = maEndPos.nIndex;
+
+ tools::Long start, end;
+ pNode->SetSelected(true);
+ if( i1 != -1 && i2 != -1 ) {
+ start = std::min(i1, i2);
+ end = std::max(i1, i2);
+ } else if( mbSelecting && i1 != -1 ) {
+ start = 0;
+ end = i1;
+ mbSelecting = false;
+ } else if( mbSelecting && i2 != -1 ) {
+ start = 0;
+ end = i2;
+ mbSelecting = false;
+ } else if( !mbSelecting && i1 != -1 ) {
+ start = i1;
+ end = pNode->GetText().getLength();
+ mbSelecting = true;
+ } else if( !mbSelecting && i2 != -1 ) {
+ start = i2;
+ end = pNode->GetText().getLength();
+ mbSelecting = true;
+ } else if( mbSelecting ) {
+ start = 0;
+ end = pNode->GetText().getLength();
+ } else {
+ pNode->SetSelected( false );
+ start = 0;
+ end = 0;
+ }
+ pNode->SetSelected( start != end );
+ pNode->SetSelectionStart( start );
+ pNode->SetSelectionEnd( end );
+}
+
+void SmSetSelectionVisitor::Visit( SmExpressionNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmLineNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmAlignNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmBinHorNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmUnHorNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+void SmSetSelectionVisitor::Visit( SmFontNode* pNode ) {
+ VisitCompositionNode( pNode );
+}
+
+// SmCaretPosGraphBuildingVisitor
+
+SmCaretPosGraphBuildingVisitor::SmCaretPosGraphBuildingVisitor( SmNode* pRootNode )
+ : mpRightMost(nullptr)
+ , mpGraph(new SmCaretPosGraph)
+{
+ //pRootNode should always be a table
+ SAL_WARN_IF( pRootNode->GetType( ) != SmNodeType::Table, "starmath", "pRootNode must be a table node");
+ //Handle the special case where SmNodeType::Table is used a rootnode
+ if( pRootNode->GetType( ) == SmNodeType::Table ){
+ //Children are SmLineNodes
+ //Or so I thought... Apparently, the children can be instances of SmExpression
+ //especially if there's an error in the formula... So here we go, a simple work around.
+ for( auto pChild : *static_cast<SmStructureNode*>(pRootNode) )
+ {
+ if(!pChild)
+ continue;
+ mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ) );
+ pChild->Accept( this );
+ }
+ }else
+ pRootNode->Accept(this);
+}
+
+SmCaretPosGraphBuildingVisitor::~SmCaretPosGraphBuildingVisitor()
+{
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmLineNode* pNode ){
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmTableNode
+ * This method covers cases where SmTableNode is used in a binom or stack,
+ * the special case where it is used as root node for the entire formula is
+ * handled in the constructor.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmTableNode* pNode ){
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1) );
+ bool bIsFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ mpRightMost = mpGraph->Add( SmCaretPos( pChild, 0 ), left);
+ if(bIsFirst)
+ left->SetRight(mpRightMost);
+ pChild->Accept( this );
+ mpRightMost->SetRight(right);
+ if(bIsFirst)
+ right->SetLeft(mpRightMost);
+ bIsFirst = false;
+ }
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmSubSupNode
+ *
+ * The child positions in a SubSupNode, where H is the body:
+ * \code
+ * CSUP
+ *
+ * LSUP H H RSUP
+ * H H
+ * HHHH
+ * H H
+ * LSUB H H RSUB
+ *
+ * CSUB
+ * \endcode
+ *
+ * Graph over these, where "left" is before the SmSubSupNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> H;
+ * H -> right;
+ * LSUP -> H;
+ * LSUB -> H;
+ * CSUP -> right;
+ * CSUB -> right;
+ * RSUP -> right;
+ * RSUB -> right;
+ * };
+ * \enddot
+ *
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmSubSupNode* pNode )
+{
+ SmCaretPosGraphEntry *left,
+ *right,
+ *bodyLeft,
+ *bodyRight;
+
+ assert(mpRightMost);
+ left = mpRightMost;
+
+ //Create bodyLeft
+ SAL_WARN_IF( !pNode->GetBody(), "starmath", "SmSubSupNode Doesn't have a body!" );
+ bodyLeft = mpGraph->Add( SmCaretPos( pNode->GetBody( ), 0 ), left );
+ left->SetRight( bodyLeft ); //TODO: Don't make this if LSUP or LSUB are NULL ( not sure??? )
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit the body, to get bodyRight
+ mpRightMost = bodyLeft;
+ pNode->GetBody( )->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //If there's an LSUP
+ SmNode* pChild = pNode->GetSubSup( LSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( bodyLeft );
+ }
+ //If there's an LSUB
+ pChild = pNode->GetSubSup( LSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( bodyLeft );
+ }
+ //If there's a CSUP
+ pChild = pNode->GetSubSup( CSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's a CSUB
+ pChild = pNode->GetSubSup( CSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's an RSUP
+ pChild = pNode->GetSubSup( RSUP );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+ //If there's an RSUB
+ pChild = pNode->GetSubSup( RSUB );
+ if( pChild ){
+ SmCaretPosGraphEntry *cLeft; //Child left
+ cLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), bodyRight );
+
+ mpRightMost = cLeft;
+ pChild->Accept( this );
+
+ mpRightMost->SetRight( right );
+ }
+
+ //Set return parameters
+ mpRightMost = right;
+}
+
+/** Build caret position for SmOperNode
+ *
+ * If first child is an SmSubSupNode we will ignore its
+ * body, as this body is a SmMathSymbol, for SUM, INT or similar
+ * that shouldn't be subject to modification.
+ * If first child is not a SmSubSupNode, ignore it completely
+ * as it is a SmMathSymbol.
+ *
+ * The child positions in a SmOperNode, where H is symbol, e.g. int, sum or similar:
+ * \code
+ * TO
+ *
+ * LSUP H H RSUP BBB BB BBB B B
+ * H H B B B B B B B B
+ * HHHH BBB B B B B B
+ * H H B B B B B B B
+ * LSUB H H RSUB BBB BB BBB B
+ *
+ * FROM
+ * \endcode
+ * Notice, CSUP, etc. are actually grandchildren, but inorder to ignore H, these are visited
+ * from here. If they are present, that is if pOper is an instance of SmSubSupNode.
+ *
+ * Graph over these, where "left" is before the SmOperNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> BODY;
+ * BODY -> right;
+ * LSUP -> BODY;
+ * LSUB -> BODY;
+ * TO -> BODY;
+ * FROM -> BODY;
+ * RSUP -> BODY;
+ * RSUB -> BODY;
+ * };
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmOperNode* pNode )
+{
+ SmNode *pOper = pNode->GetSubNode( 0 ),
+ *pBody = pNode->GetSubNode( 1 );
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *bodyLeft,
+ *bodyRight,
+ *right;
+ //Create body left
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Visit body, get bodyRight
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ), bodyRight );
+ bodyRight->SetRight( right );
+
+ //Get subsup pNode if any
+ SmSubSupNode* pSubSup = pOper->GetType( ) == SmNodeType::SubSup ? static_cast<SmSubSupNode*>(pOper) : nullptr;
+
+ if( pSubSup ) {
+ SmNode* pChild = pSubSup->GetSubSup( LSUP );
+ if( pChild ) {
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( LSUB );
+ if( pChild ) {
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( CSUP );
+ if ( pChild ) {//TO
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( CSUB );
+ if( pChild ) { //FROM
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( RSUP );
+ if ( pChild ) {
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ pChild = pSubSup->GetSubSup( RSUB );
+ if ( pChild ) {
+ //Create position in front of pChild
+ SmCaretPosGraphEntry *childLeft = mpGraph->Add( SmCaretPos( pChild, 0 ), left );
+ //Visit pChild
+ mpRightMost = childLeft;
+ pChild->Accept( this );
+ //Set right on mpRightMost from pChild
+ mpRightMost->SetRight( bodyLeft );
+ }
+ }
+
+ //Return right
+ mpRightMost = right;
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmMatrixNode* pNode )
+{
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ for (size_t i = 0; i < pNode->GetNumRows(); ++i)
+ {
+ SmCaretPosGraphEntry* r = left;
+ for (size_t j = 0; j < pNode->GetNumCols(); ++j)
+ {
+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
+
+ mpRightMost = mpGraph->Add( SmCaretPos( pSubNode, 0 ), r );
+ if( j != 0 || ( pNode->GetNumRows() - 1U ) / 2 == i )
+ r->SetRight( mpRightMost );
+
+ pSubNode->Accept( this );
+
+ r = mpRightMost;
+ }
+ mpRightMost->SetRight( right );
+ if( ( pNode->GetNumRows() - 1U ) / 2 == i )
+ right->SetLeft( mpRightMost );
+ }
+
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmTextNode
+ *
+ * Lines in an SmTextNode:
+ * \code
+ * A B C
+ * \endcode
+ * Where A B and C are characters in the text.
+ *
+ * Graph over these, where "left" is before the SmTextNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> A;
+ * A -> B
+ * B -> right;
+ * };
+ * \enddot
+ * Notice that C and right is the same position here.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmTextNode* pNode )
+{
+ SAL_WARN_IF( pNode->GetText().isEmpty(), "starmath", "Empty SmTextNode is bad" );
+
+ int size = pNode->GetText().getLength();
+ for( int i = 1; i <= size; i++ ){
+ SmCaretPosGraphEntry* pRight = mpRightMost;
+ mpRightMost = mpGraph->Add( SmCaretPos( pNode, i ), pRight );
+ pRight->SetRight( mpRightMost );
+ }
+}
+
+/** Build SmCaretPosGraph for SmBinVerNode
+ *
+ * Lines in an SmBinVerNode:
+ * \code
+ * A
+ * -----
+ * B
+ * \endcode
+ *
+ * Graph over these, where "left" is before the SmBinVerNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> A;
+ * A -> right;
+ * B -> right;
+ * };
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinVerNode* pNode )
+{
+ //None if these children can be NULL, see SmBinVerNode::Arrange
+ SmNode *pNum = pNode->GetSubNode( 0 ),
+ *pDenom = pNode->GetSubNode( 2 );
+
+ SmCaretPosGraphEntry *left,
+ *right,
+ *numLeft,
+ *denomLeft;
+
+ assert(mpRightMost);
+ //Set left
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create numLeft
+ numLeft = mpGraph->Add( SmCaretPos( pNum, 0 ), left );
+ left->SetRight( numLeft );
+
+ //Visit pNum
+ mpRightMost = numLeft;
+ pNum->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Create denomLeft
+ denomLeft = mpGraph->Add( SmCaretPos( pDenom, 0 ), left );
+
+ //Visit pDenom
+ mpRightMost = denomLeft;
+ pDenom->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return parameter
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmVerticalBraceNode
+ *
+ * Lines in an SmVerticalBraceNode:
+ * \code
+ * pScript
+ * ________
+ * / \
+ * pBody
+ * \endcode
+ *
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmNode *pBody = pNode->Body(),
+ *pScript = pNode->Script();
+ //None of these children can be NULL
+
+ SmCaretPosGraphEntry *left,
+ *bodyLeft,
+ *scriptLeft,
+ *right;
+
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create bodyLeft
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Create script
+ scriptLeft = mpGraph->Add( SmCaretPos( pScript, 0 ), left );
+ mpRightMost = scriptLeft;
+ pScript->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmBinDiagonalNode
+ *
+ * Lines in an SmBinDiagonalNode:
+ * \code
+ * A /
+ * /
+ * / B
+ * \endcode
+ * Where A and B are lines.
+ *
+ * Used in formulas such as "A wideslash B"
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmNode *A = pNode->GetSubNode( 0 ),
+ *B = pNode->GetSubNode( 1 );
+
+ SmCaretPosGraphEntry *left,
+ *leftA,
+ *rightA,
+ *leftB,
+ *right;
+ left = mpRightMost;
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Create left A
+ leftA = mpGraph->Add( SmCaretPos( A, 0 ), left );
+ left->SetRight( leftA );
+
+ //Visit A
+ mpRightMost = leftA;
+ A->Accept( this );
+ rightA = mpRightMost;
+
+ //Create left B
+ leftB = mpGraph->Add( SmCaretPos( B, 0 ), rightA );
+ rightA->SetRight( leftB );
+
+ //Visit B
+ mpRightMost = leftB;
+ B->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+//Straight forward ( I think )
+void SmCaretPosGraphBuildingVisitor::Visit( SmBinHorNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmUnHorNode* pNode )
+{
+ // Unary operator node
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmExpressionNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmFontNode* pNode )
+{
+ //Has only got one child, should act as an expression if possible
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmBracebodyNode
+ * Acts as an SmExpressionNode
+ *
+ * Below is an example of a formula tree that has multiple children for SmBracebodyNode
+ * \dot
+ * digraph {
+ * labelloc = "t";
+ * label= "Equation: \"lbrace i mline i in setZ rbrace\"";
+ * n0 [label="SmTableNode"];
+ * n0 -> n1 [label="0"];
+ * n1 [label="SmLineNode"];
+ * n1 -> n2 [label="0"];
+ * n2 [label="SmExpressionNode"];
+ * n2 -> n3 [label="0"];
+ * n3 [label="SmBraceNode"];
+ * n3 -> n4 [label="0"];
+ * n4 [label="SmMathSymbolNode: {"];
+ * n3 -> n5 [label="1"];
+ * n5 [label="SmBracebodyNode"];
+ * n5 -> n6 [label="0"];
+ * n6 [label="SmExpressionNode"];
+ * n6 -> n7 [label="0"];
+ * n7 [label="SmTextNode: i"];
+ * n5 -> n8 [label="1"];
+ * n8 [label="SmMathSymbolNode: &#124;"]; // Unicode "VERTICAL LINE"
+ * n5 -> n9 [label="2"];
+ * n9 [label="SmExpressionNode"];
+ * n9 -> n10 [label="0"];
+ * n10 [label="SmBinHorNode"];
+ * n10 -> n11 [label="0"];
+ * n11 [label="SmTextNode: i"];
+ * n10 -> n12 [label="1"];
+ * n12 [label="SmMathSymbolNode: &#8712;"]; // Unicode "ELEMENT OF"
+ * n10 -> n13 [label="2"];
+ * n13 [label="SmMathSymbolNode: &#8484;"]; // Unicode "DOUBLE-STRUCK CAPITAL Z"
+ * n3 -> n14 [label="2"];
+ * n14 [label="SmMathSymbolNode: }"];
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBracebodyNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ SmCaretPosGraphEntry* pStart = mpGraph->Add( SmCaretPos( pChild, 0), mpRightMost );
+ mpRightMost->SetRight( pStart );
+ mpRightMost = pStart;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmAlignNode
+ * Acts as an SmExpressionNode, as it only has one child this okay
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmAlignNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+/** Build SmCaretPosGraph for SmRootNode
+ *
+ * Lines in an SmRootNode:
+ * \code
+ * _________
+ * A/
+ * \/ B
+ *
+ * \endcode
+ * A: pExtra ( optional, can be NULL ),
+ * B: pBody
+ *
+ * Graph over these, where "left" is before the SmRootNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> B;
+ * B -> right;
+ * A -> B;
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmRootNode* pNode )
+{
+ SmNode *pExtra = pNode->GetSubNode( 0 ), //Argument, NULL for sqrt, and SmTextNode if cubicroot
+ *pBody = pNode->GetSubNode( 2 ); //Body of the root
+ assert(pBody);
+
+ SmCaretPosGraphEntry *left,
+ *right,
+ *bodyLeft,
+ *bodyRight;
+
+ //Get left and save it
+ assert(mpRightMost);
+ left = mpRightMost;
+
+ //Create body left
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Create right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit body
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //Visit pExtra
+ if( pExtra ){
+ mpRightMost = mpGraph->Add( SmCaretPos( pExtra, 0 ), left );
+ pExtra->Accept( this );
+ mpRightMost->SetRight( bodyLeft );
+ }
+
+ mpRightMost = right;
+}
+
+
+/** Build SmCaretPosGraph for SmPlaceNode
+ * Consider this a single character.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmPlaceNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+/** SmErrorNode is context dependent metadata, it can't be selected
+ *
+ * @remarks There's no point in deleting, copying and/or moving an instance
+ * of SmErrorNode as it may not exist in another context! Thus there are no
+ * positions to select an SmErrorNode.
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmErrorNode* )
+{
+}
+
+/** Build SmCaretPosGraph for SmBlankNode
+ * Consider this a single character, as it is only a blank space
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBlankNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmBraceNode
+ *
+ * Lines in an SmBraceNode:
+ * \code
+ * | |
+ * | B |
+ * | |
+ * \endcode
+ * B: Body
+ *
+ * Graph over these, where "left" is before the SmBraceNode and "right" is after:
+ * \dot
+ * digraph Graph{
+ * left -> B;
+ * B -> right;
+ * }
+ * \enddot
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmBraceNode* pNode )
+{
+ SmNode* pBody = pNode->Body();
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ if( pBody->GetType() != SmNodeType::Bracebody ) {
+ mpRightMost = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( mpRightMost );
+ }else
+ mpRightMost = left;
+
+ pBody->Accept( this );
+ mpRightMost->SetRight( right );
+ right->SetLeft( mpRightMost );
+
+ mpRightMost = right;
+}
+
+/** Build SmCaretPosGraph for SmAttributeNode
+ *
+ * Lines in an SmAttributeNode:
+ * \code
+ * Attr
+ * Body
+ * \endcode
+ *
+ * There's a body and an attribute, the construction is used for "widehat A", where "A" is the body
+ * and "^" is the attribute ( note GetScaleMode( ) on SmAttributeNode tells how the attribute should be
+ * scaled ).
+ */
+void SmCaretPosGraphBuildingVisitor::Visit( SmAttributeNode* pNode )
+{
+ SmNode *pAttr = pNode->Attribute(),
+ *pBody = pNode->Body();
+ assert(pAttr);
+ assert(pBody);
+
+ SmCaretPosGraphEntry *left = mpRightMost,
+ *attrLeft,
+ *bodyLeft,
+ *bodyRight,
+ *right;
+
+ //Creating bodyleft
+ bodyLeft = mpGraph->Add( SmCaretPos( pBody, 0 ), left );
+ left->SetRight( bodyLeft );
+
+ //Creating right
+ right = mpGraph->Add( SmCaretPos( pNode, 1 ) );
+
+ //Visit the body
+ mpRightMost = bodyLeft;
+ pBody->Accept( this );
+ bodyRight = mpRightMost;
+ bodyRight->SetRight( right );
+ right->SetLeft( bodyRight );
+
+ //Create attrLeft
+ attrLeft = mpGraph->Add( SmCaretPos( pAttr, 0 ), left );
+
+ //Visit attribute
+ mpRightMost = attrLeft;
+ pAttr->Accept( this );
+ mpRightMost->SetRight( right );
+
+ //Set return value
+ mpRightMost = right;
+}
+
+//Consider these single symbols
+void SmCaretPosGraphBuildingVisitor::Visit( SmSpecialNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ SmCaretPosGraphEntry* right = mpGraph->Add( SmCaretPos( pNode, 1 ), mpRightMost );
+ mpRightMost->SetRight( right );
+ mpRightMost = right;
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmRootSymbolNode* )
+{
+ //Do nothing
+}
+
+void SmCaretPosGraphBuildingVisitor::Visit( SmRectangleNode* )
+{
+ //Do nothing
+}
+void SmCaretPosGraphBuildingVisitor::Visit( SmPolyLineNode* )
+{
+ //Do nothing
+}
+
+// SmCloningVisitor
+
+SmNode* SmCloningVisitor::Clone( SmNode* pNode )
+{
+ SmNode* pCurrResult = mpResult;
+ pNode->Accept( this );
+ SmNode* pClone = mpResult;
+ mpResult = pCurrResult;
+ return pClone;
+}
+
+void SmCloningVisitor::CloneNodeAttr( SmNode const * pSource, SmNode* pTarget )
+{
+ pTarget->SetScaleMode( pSource->GetScaleMode( ) );
+ //Other attributes are set when prepare or arrange is executed
+ //and may depend on stuff not being cloned here.
+}
+
+void SmCloningVisitor::CloneKids( SmStructureNode* pSource, SmStructureNode* pTarget )
+{
+ //Cache current result
+ SmNode* pCurrResult = mpResult;
+
+ //Create array for holding clones
+ size_t nSize = pSource->GetNumSubNodes( );
+ SmNodeArray aNodes( nSize );
+
+ //Clone children
+ for (size_t i = 0; i < nSize; ++i)
+ {
+ SmNode* pKid;
+ if( nullptr != ( pKid = pSource->GetSubNode( i ) ) )
+ pKid->Accept( this );
+ else
+ mpResult = nullptr;
+ aNodes[i] = mpResult;
+ }
+
+ //Set subnodes of pTarget
+ pTarget->SetSubNodes( std::move(aNodes) );
+
+ //Restore result as where prior to call
+ mpResult = pCurrResult;
+}
+
+void SmCloningVisitor::Visit( SmTableNode* pNode )
+{
+ SmTableNode* pClone = new SmTableNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBraceNode* pNode )
+{
+ SmBraceNode* pClone = new SmBraceNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBracebodyNode* pNode )
+{
+ SmBracebodyNode* pClone = new SmBracebodyNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmOperNode* pNode )
+{
+ SmOperNode* pClone = new SmOperNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmAlignNode* pNode )
+{
+ SmAlignNode* pClone = new SmAlignNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmAttributeNode* pNode )
+{
+ SmAttributeNode* pClone = new SmAttributeNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmFontNode* pNode )
+{
+ SmFontNode* pClone = new SmFontNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->SetSizeParameter( pNode->GetSizeParameter( ), pNode->GetSizeType( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmUnHorNode* pNode )
+{
+ SmUnHorNode* pClone = new SmUnHorNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinHorNode* pNode )
+{
+ SmBinHorNode* pClone = new SmBinHorNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinVerNode* pNode )
+{
+ SmBinVerNode* pClone = new SmBinVerNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmBinDiagonalNode *pClone = new SmBinDiagonalNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->SetAscending( pNode->IsAscending( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmSubSupNode* pNode )
+{
+ SmSubSupNode *pClone = new SmSubSupNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->SetUseLimits( pNode->IsUseLimits( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmMatrixNode* pNode )
+{
+ SmMatrixNode *pClone = new SmMatrixNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->SetRowCol( pNode->GetNumRows( ), pNode->GetNumCols( ) );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmPlaceNode* pNode )
+{
+ mpResult = new SmPlaceNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmTextNode* pNode )
+{
+ SmTextNode* pClone = new SmTextNode( pNode->GetToken( ), pNode->GetFontDesc( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->ChangeText( pNode->GetText( ) );
+ CloneNodeAttr( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmSpecialNode* pNode )
+{
+ mpResult = new SmSpecialNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ mpResult = new SmGlyphSpecialNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ mpResult = new SmMathSymbolNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmBlankNode* pNode )
+{
+ SmBlankNode* pClone = new SmBlankNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ pClone->SetBlankNum( pNode->GetBlankNum( ) );
+ mpResult = pClone;
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmErrorNode* pNode )
+{
+ mpResult = new SmErrorNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmLineNode* pNode )
+{
+ SmLineNode* pClone = new SmLineNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmExpressionNode* pNode )
+{
+ SmExpressionNode* pClone = new SmExpressionNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmPolyLineNode* pNode )
+{
+ mpResult = new SmPolyLineNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmRootNode* pNode )
+{
+ SmRootNode* pClone = new SmRootNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+void SmCloningVisitor::Visit( SmRootSymbolNode* pNode )
+{
+ mpResult = new SmRootSymbolNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmRectangleNode* pNode )
+{
+ mpResult = new SmRectangleNode( pNode->GetToken( ) );
+ mpResult->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, mpResult );
+}
+
+void SmCloningVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmVerticalBraceNode* pClone = new SmVerticalBraceNode( pNode->GetToken( ) );
+ pClone->SetSelection( pNode->GetSelection() );
+ CloneNodeAttr( pNode, pClone );
+ CloneKids( pNode, pClone );
+ mpResult = pClone;
+}
+
+// SmSelectionDrawingVisitor
+
+SmSelectionDrawingVisitor::SmSelectionDrawingVisitor( OutputDevice& rDevice, SmNode* pTree, const Point& rOffset )
+ : mrDev( rDevice )
+ , mbHasSelectionArea( false )
+{
+ //Visit everything
+ SAL_WARN_IF( !pTree, "starmath", "pTree can't be null!" );
+ if( pTree )
+ pTree->Accept( this );
+
+ //Draw selection if there's any
+ if( !mbHasSelectionArea ) return;
+
+ maSelectionArea.Move( rOffset.X( ), rOffset.Y( ) );
+
+ //Save device state
+ mrDev.Push( vcl::PushFlags::LINECOLOR | vcl::PushFlags::FILLCOLOR );
+ //Change colors
+ mrDev.SetLineColor( );
+ mrDev.SetFillColor( COL_LIGHTGRAY );
+
+ //Draw rectangle
+ mrDev.DrawRect( maSelectionArea );
+
+ //Restore device state
+ mrDev.Pop( );
+}
+
+void SmSelectionDrawingVisitor::ExtendSelectionArea(const tools::Rectangle& rArea)
+{
+ if ( ! mbHasSelectionArea ) {
+ maSelectionArea = rArea;
+ mbHasSelectionArea = true;
+ } else
+ maSelectionArea.Union(rArea);
+}
+
+void SmSelectionDrawingVisitor::DefaultVisit( SmNode* pNode )
+{
+ if( pNode->IsSelected( ) )
+ ExtendSelectionArea( pNode->AsRectangle( ) );
+ VisitChildren( pNode );
+}
+
+void SmSelectionDrawingVisitor::VisitChildren( SmNode* pNode )
+{
+ if(pNode->GetNumSubNodes() == 0)
+ return;
+ for( auto pChild : *static_cast<SmStructureNode*>(pNode) )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ }
+}
+
+void SmSelectionDrawingVisitor::Visit( SmTextNode* pNode )
+{
+ if( !pNode->IsSelected())
+ return;
+
+ mrDev.Push( vcl::PushFlags::TEXTCOLOR | vcl::PushFlags::FONT );
+
+ mrDev.SetFont( pNode->GetFont( ) );
+ Point Position = pNode->GetTopLeft( );
+ tools::Long left = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionStart( ) );
+ tools::Long right = Position.getX( ) + mrDev.GetTextWidth( pNode->GetText( ), 0, pNode->GetSelectionEnd( ) );
+ tools::Long top = Position.getY( );
+ tools::Long bottom = top + pNode->GetHeight( );
+ tools::Rectangle rect( left, top, right, bottom );
+
+ ExtendSelectionArea( rect );
+
+ mrDev.Pop( );
+}
+
+// SmNodeToTextVisitor
+
+SmNodeToTextVisitor::SmNodeToTextVisitor( SmNode* pNode, OUString &rText )
+{
+ pNode->Accept( this );
+ maCmdText.stripEnd(' ');
+ rText = maCmdText.makeStringAndClear();
+}
+
+void SmNodeToTextVisitor::Visit( SmTableNode* pNode )
+{
+ if( pNode->GetToken( ).eType == TBINOM ) {
+ Append(u"{ binom");
+ LineToText( pNode->GetSubNode( 0 ) );
+ LineToText( pNode->GetSubNode( 1 ) );
+ Append(u"} ");
+ } else if( pNode->GetToken( ).eType == TSTACK ) {
+ Append(u"stack{ ");
+ bool bFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ if(bFirst)
+ bFirst = false;
+ else
+ {
+ Separate( );
+ Append(u"# ");
+ }
+ LineToText( pChild );
+ }
+ Separate( );
+ Append(u"}");
+ } else { //Assume it's a toplevel table, containing lines
+ bool bFirst = true;
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ if(bFirst)
+ bFirst = false;
+ else
+ {
+ Separate( );
+ Append(u"newline");
+ }
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBraceNode* pNode )
+{
+ if ( pNode->GetToken().eType == TEVALUATE )
+ {
+ SmNode *pBody = pNode->Body();
+ Append(u"evaluate { ");
+ pBody->Accept( this );
+ Append(u"} ");
+ }
+ else{
+ SmNode *pLeftBrace = pNode->OpeningBrace(),
+ *pBody = pNode->Body(),
+ *pRightBrace = pNode->ClosingBrace();
+ //Handle special case where it's absolute function
+ if( pNode->GetToken( ).eType == TABS ) {
+ Append(u"abs");
+ LineToText( pBody );
+ } else {
+ if( pNode->GetScaleMode( ) == SmScaleMode::Height )
+ Append(u"left ");
+ pLeftBrace->Accept( this );
+ Separate( );
+ pBody->Accept( this );
+ Separate( );
+ if( pNode->GetScaleMode( ) == SmScaleMode::Height )
+ Append(u"right ");
+ pRightBrace->Accept( this );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBracebodyNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmOperNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ Separate( );
+ if( pNode->GetToken( ).eType == TOPER ){
+ //There's an SmGlyphSpecialNode if eType == TOPER
+ if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup )
+ Append( pNode->GetSubNode( 0 )->GetSubNode( 0 )->GetToken( ).aText );
+ else
+ Append( pNode->GetSubNode( 0 )->GetToken( ).aText );
+ }
+ if( pNode->GetSubNode( 0 )->GetType( ) == SmNodeType::SubSup ) {
+ SmSubSupNode *pSubSup = static_cast<SmSubSupNode*>( pNode->GetSubNode( 0 ) );
+ SmNode* pChild = pSubSup->GetSubSup( LSUP );
+ if( pChild ) {
+ Separate( );
+ Append(u"lsup { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pSubSup->GetSubSup( LSUB );
+ if( pChild ) {
+ Separate( );
+ Append(u"lsub { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pSubSup->GetSubSup( RSUP );
+ if( pChild ) {
+ Separate( );
+ Append(u"^ { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pSubSup->GetSubSup( RSUB );
+ if( pChild ) {
+ Separate( );
+ Append(u"_ { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pSubSup->GetSubSup( CSUP );
+ if( pChild ) {
+ Separate( );
+ if (pSubSup->IsUseLimits())
+ Append(u"to { ");
+ else
+ Append(u"csup { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pSubSup->GetSubSup( CSUB );
+ if( pChild ) {
+ Separate( );
+ if (pSubSup->IsUseLimits())
+ Append(u"from { ");
+ else
+ Append(u"csub { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ }
+ LineToText( pNode->GetSubNode( 1 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmAlignNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ LineToText( pNode->GetSubNode( 0 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmAttributeNode* pNode )
+{
+ Append( pNode->GetToken( ).aText );
+ LineToText( pNode->Body() );
+}
+
+void SmNodeToTextVisitor::Visit( SmFontNode* pNode )
+{
+ sal_uInt32 nc;
+ sal_uInt8 nr, ng, nb;
+ switch ( pNode->GetToken( ).eType )
+ {
+ case TBOLD:
+ Append(u"bold ");
+ break;
+ case TNBOLD:
+ Append(u"nbold ");
+ break;
+ case TITALIC:
+ Append(u"italic ");
+ break;
+ case TNITALIC:
+ Append(u"nitalic ");
+ break;
+ case TPHANTOM:
+ Append(u"phantom ");
+ break;
+ case TSIZE:
+ {
+ Append(u"size ");
+ switch ( pNode->GetSizeType( ) )
+ {
+ case FontSizeType::PLUS:
+ Append(u"+");
+ break;
+ case FontSizeType::MINUS:
+ Append(u"-");
+ break;
+ case FontSizeType::MULTIPLY:
+ Append(u"*");
+ break;
+ case FontSizeType::DIVIDE:
+ Append(u"/");
+ break;
+ case FontSizeType::ABSOLUT:
+ default:
+ break;
+ }
+ Append( ::rtl::math::doubleToUString(
+ static_cast<double>( pNode->GetSizeParameter( ) ),
+ rtl_math_StringFormat_Automatic,
+ rtl_math_DecimalPlaces_Max, '.', true ) );
+ Separate( );
+ }
+ break;
+
+ case TDVIPSNAMESCOL:
+ Append(u"color dvip ");
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
+ break;
+ case THTMLCOL:
+ case TMATHMLCOL:
+ case TICONICCOL:
+ Append(u"color ");
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ Append( starmathdatabase::Identify_Color_Parser( nc ).aIdent );
+ break;
+ case TRGB:
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ Append(u"color rgb ");
+ nb = nc % 256;
+ nc /= 256;
+ ng = nc % 256;
+ nc /= 256;
+ nr = nc % 256;
+ Append(OUString::number(nr));
+ Separate();
+ Append(OUString::number(ng));
+ Separate();
+ Append(OUString::number(nb));
+ Separate();
+ break;
+ case TRGBA:
+ Append(u"color rgba ");
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ nb = nc % 256;
+ nc /= 256;
+ ng = nc % 256;
+ nc /= 256;
+ nr = nc % 256;
+ nc /= 256;
+ Append(OUString::number(nr));
+ Separate();
+ Append(OUString::number(ng));
+ Separate();
+ Append(OUString::number(nb));
+ Separate();
+ Append(OUString::number(nc));
+ Separate();
+ break;
+ case THEX:
+ Append(u"color hex ");
+ nc = pNode->GetToken().cMathChar.toUInt32(16);
+ Append(OUString::number(nc,16));
+ Separate();
+ break;
+ case TSANS:
+ Append(u"font sans ");
+ break;
+ case TSERIF:
+ Append(u"font serif ");
+ break;
+ case TFIXED:
+ Append(u"font fixed ");
+ break;
+ default:
+ break;
+ }
+ LineToText( pNode->GetSubNode( 1 ) );
+}
+
+void SmNodeToTextVisitor::Visit( SmUnHorNode* pNode )
+{
+ if(pNode->GetSubNode( 1 )->GetToken( ).eType == TFACT)
+ {
+ // visit children in the reverse order
+ for( auto it = pNode->rbegin(); it != pNode->rend(); ++it )
+ {
+ auto pChild = *it;
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+ else
+ {
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBinHorNode* pNode )
+{
+ const SmNode *pParent = pNode->GetParent();
+ bool bBraceNeeded = pParent && pParent->GetType() == SmNodeType::Font;
+ SmNode *pLeft = pNode->LeftOperand(),
+ *pOper = pNode->Symbol(),
+ *pRight = pNode->RightOperand();
+ Separate( );
+ if (bBraceNeeded)
+ Append(u"{ ");
+ pLeft->Accept( this );
+ Separate( );
+ pOper->Accept( this );
+ Separate( );
+ pRight->Accept( this );
+ Separate( );
+ if (bBraceNeeded)
+ Append(u"} ");
+}
+
+void SmNodeToTextVisitor::Visit( SmBinVerNode* pNode )
+{
+ if( pNode->GetToken().eType == TOVER ){
+ SmNode *pNum = pNode->GetSubNode( 0 ),
+ *pDenom = pNode->GetSubNode( 2 );
+ Append(u"{ ");
+ LineToText( pNum );
+ Append(u"over");
+ LineToText( pDenom );
+ Append(u"} ");
+ } else{
+ SmNode *pNum = pNode->GetSubNode( 0 ),
+ *pDenom = pNode->GetSubNode( 2 );
+ Append(u"frac {");
+ LineToText( pNum );
+ Append(u"} {");
+ LineToText( pDenom );
+ Append(u"}");
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBinDiagonalNode* pNode )
+{
+ SmNode *pLeftOperand = pNode->GetSubNode( 0 ),
+ *pRightOperand = pNode->GetSubNode( 1 );
+ Append(u"{ ");
+ LineToText( pLeftOperand );
+ Separate( );
+ Append(u"wideslash ");
+ LineToText( pRightOperand );
+ Append(u"} ");
+}
+
+void SmNodeToTextVisitor::Visit( SmSubSupNode* pNode )
+{
+ if( pNode->GetToken().eType == TEVALUATE )
+ {
+ Append(u"evaluate { ");
+ pNode->GetSubNode( 0 )->GetSubNode( 1 )->Accept(this);
+ Append(u"} ");
+ SmNode* pChild = pNode->GetSubSup( RSUP );
+ if( pChild ) {
+ Separate( );
+ Append(u"to { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ pChild = pNode->GetSubSup( RSUB );
+ if( pChild ) {
+ Separate( );
+ Append(u"from { ");
+ LineToText( pChild );
+ Append(u"} ");
+ }
+ }
+ else
+ {
+ LineToText( pNode->GetBody( ) );
+ SmNode *pChild = pNode->GetSubSup( LSUP );
+ if( pChild ) {
+ Separate( );
+ Append(u"lsup ");
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( LSUB );
+ if( pChild ) {
+ Separate( );
+ Append(u"lsub ");
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( RSUP );
+ if( pChild ) {
+ Separate( );
+ Append(u"^ ");
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( RSUB );
+ if( pChild ) {
+ Separate( );
+ Append(u"_ ");
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( CSUP );
+ if( pChild ) {
+ Separate( );
+ if (pNode->IsUseLimits())
+ Append(u"to ");
+ else
+ Append(u"csup ");
+ LineToText( pChild );
+ }
+ pChild = pNode->GetSubSup( CSUB );
+ if( pChild ) {
+ Separate( );
+ if (pNode->IsUseLimits())
+ Append(u"from ");
+ else
+ Append(u"csub ");
+ LineToText( pChild );
+ }
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmMatrixNode* pNode )
+{
+ Append(u"matrix{");
+ for (size_t i = 0; i < pNode->GetNumRows(); ++i)
+ {
+ for (size_t j = 0; j < pNode->GetNumCols( ); ++j)
+ {
+ SmNode* pSubNode = pNode->GetSubNode( i * pNode->GetNumCols( ) + j );
+ Separate( );
+ if (pSubNode)
+ pSubNode->Accept( this );
+ Separate( );
+ if (j != pNode->GetNumCols() - 1U)
+ Append(u"#");
+ }
+ Separate( );
+ if (i != pNode->GetNumRows() - 1U)
+ Append(u"##");
+ }
+ Append(u"} ");
+}
+
+void SmNodeToTextVisitor::Visit( SmPlaceNode* )
+{
+ Append(u"<?>");
+}
+
+void SmNodeToTextVisitor::Visit( SmTextNode* pNode )
+{
+ SmTokenType type = pNode->GetToken( ).eType;
+ switch(type){
+ case TTEXT:
+ Append(u"\"");
+ Append( pNode->GetToken().aText );
+ Append(u"\"");
+ break;
+ case TNUMBER:
+ Append( pNode->GetToken().aText );
+ break;
+ case TIDENT:
+ Append( pNode->GetToken().aText );
+ break;
+ case TFUNC:
+ Append(u"func ");
+ Append( pNode->GetToken().aText );
+ break;
+ case THEX:
+ Append(u"hex ");
+ Append( pNode->GetToken().aText );
+ break;
+ default:
+ Append( pNode->GetToken().aText );
+ }
+ Separate( );
+}
+
+void SmNodeToTextVisitor::Visit( SmSpecialNode* pNode )
+{
+ SmTokenType type = pNode->GetToken().eType;
+ switch(type){
+ case TLIMSUP:
+ Append(u"lim sup ");
+ break;
+ case TLIMINF:
+ Append(u"lim inf ");
+ break;
+ default:
+ Append( pNode->GetToken().aText );
+ break;
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmGlyphSpecialNode* pNode )
+{
+ if( pNode->GetToken( ).eType == TBOPER )
+ Append(u"boper ");
+ else
+ Append(u"uoper ");
+ Append( pNode->GetToken( ).aText );
+}
+
+//TODO to improve this it is required to improve mathmlimport.
+void SmNodeToTextVisitor::Visit( SmMathSymbolNode* pNode )
+{
+ if ( ( pNode->GetToken().nGroup & TG::LBrace )
+ || ( pNode->GetToken().nGroup & TG::RBrace )
+ || ( pNode->GetToken().nGroup & TG::Sum )
+ || ( pNode->GetToken().nGroup & TG::Product )
+ || ( pNode->GetToken().nGroup & TG::Relation )
+ || ( pNode->GetToken().nGroup & TG::UnOper )
+ || ( pNode->GetToken().nGroup & TG::Oper )
+ ) {
+ Append( pNode->GetToken().aText );
+ return;
+ }
+ sal_Unicode cChar = pNode->GetToken().cMathChar[0];
+ Separate( );
+ switch(cChar){
+ case MS_NONE:
+ Append(u"none");
+ break;
+ case '{':
+ Append(u"{");
+ break;
+ case '}':
+ Append(u"}");
+ break;
+ case MS_VERTLINE:
+ Append(u"mline");
+ break;
+ case MS_TILDE:
+ Append(u"\"~\"");
+ break;
+ case MS_RIGHTARROW:
+ if( pNode->GetToken().eType == TTOWARD ) Append(u"toward");
+ else Append(u"rightarrow");
+ break;
+ case MS_LEFTARROW:
+ Append(u"leftarrow");
+ break;
+ case MS_UPARROW:
+ Append(u"uparrow");
+ break;
+ case MS_DOWNARROW:
+ Append(u"downarrow");
+ break;
+ case MS_LAMBDABAR:
+ Append(u"lambdabar");
+ break;
+ case MS_DOTSLOW:
+ Append(u"dotslow");
+ break;
+ case MS_SETC:
+ Append(u"setC");
+ break;
+ case MS_HBAR:
+ Append(u"hbar");
+ break;
+ case MS_IM:
+ Append(u"Im");
+ break;
+ case MS_SETN:
+ Append(u"setN");
+ break;
+ case MS_WP:
+ Append(u"wp");
+ break;
+ case MS_LAPLACE:
+ Append(u"laplace");
+ break;
+ case MS_SETQ:
+ Append(u"setQ");
+ break;
+ case MS_RE:
+ Append(u"Re");
+ break;
+ case MS_SETR:
+ Append(u"setR");
+ break;
+ case MS_SETZ:
+ Append(u"setZ");
+ break;
+ case MS_ALEPH:
+ Append(u"aleph");
+ break;
+ case 0x0362:
+ Append(u"widevec");
+ break;
+ case MS_DLARROW:
+ Append(u"dlarrow");
+ break;
+ case MS_DRARROW:
+ Append(u"drarrow");
+ break;
+ case MS_DLRARROW:
+ Append(u"dlrarrow");
+ break;
+ case MS_FORALL:
+ Append(u"forall");
+ break;
+ case MS_PARTIAL:
+ Append(u"partial");
+ break;
+ case MS_EXISTS:
+ Append(u"exists");
+ break;
+ case MS_NOTEXISTS:
+ Append(u"notexists");
+ break;
+ case MS_EMPTYSET:
+ Append(u"emptyset");
+ break;
+ case MS_NABLA:
+ Append(u"nabla");
+ break;
+ case MS_BACKEPSILON:
+ Append(u"backepsilon");
+ break;
+ case MS_CIRC:
+ Append(u"circ");
+ break;
+ case MS_INFINITY:
+ Append(u"infinity");
+ break;
+ case 0x22b2: // NORMAL SUBGROUP OF
+ Append(OUStringChar(cChar));
+ break;
+ case 0x22b3: // CONTAINS AS NORMAL SUBGROUP
+ Append(OUStringChar(cChar));
+ break;
+ case MS_ORTHO:
+ Append(u"ortho");
+ break;
+ case MS_DOTSVERT:
+ Append(u"dotsvert");
+ break;
+ case MS_DOTSAXIS:
+ Append(u"dotsaxis");
+ break;
+ case MS_DOTSUP:
+ Append(u"dotsup");
+ break;
+ case MS_DOTSDOWN:
+ Append(u"dotsdown");
+ break;
+ case '^':
+ Append(u"^");
+ break;
+ case 0xe091:
+ Append(u"widehat");
+ break;
+ case 0xe096:
+ Append(u"widetilde");
+ break;
+ case 0xe098:
+ Append(u"widevec");
+ break;
+ case 0xeb01: //no space
+ case 0xeb08: //normal space
+ break;
+ case 0xef04: //tiny space
+ case 0xef05: //tiny space
+ case 0xeb02: //small space
+ case 0xeb04: //medium space
+ Append(u"`");
+ break;
+ case 0xeb05: //large space
+ Append(u"~");
+ break;
+ case 0x3a9:
+ Append(u"%OMEGA");
+ break;
+ default:
+ Append(OUStringChar(cChar));
+ break;
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmBlankNode* pNode )
+{
+ sal_uInt16 nNum = pNode->GetBlankNum();
+ if (nNum <= 0)
+ return;
+ sal_uInt16 nWide = nNum / 4;
+ sal_uInt16 nNarrow = nNum % 4;
+ for (sal_uInt16 i = 0; i < nWide; i++)
+ Append(u"~");
+ for (sal_uInt16 i = 0; i < nNarrow; i++)
+ Append(u"`");
+ Append(u" ");
+}
+
+void SmNodeToTextVisitor::Visit( SmErrorNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmLineNode* pNode )
+{
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ Separate( );
+ pChild->Accept( this );
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmExpressionNode* pNode )
+{
+ bool bracketsNeeded = pNode->GetNumSubNodes() != 1 || pNode->GetSubNode(0)->GetType() == SmNodeType::BinHor;
+ if (!bracketsNeeded)
+ {
+ const SmNode *pParent = pNode->GetParent();
+ // nested subsups
+ bracketsNeeded =
+ pParent && pParent->GetType() == SmNodeType::SubSup &&
+ pNode->GetNumSubNodes() == 1 &&
+ pNode->GetSubNode(0)->GetType() == SmNodeType::SubSup;
+ }
+
+ if (bracketsNeeded) {
+ Append(u"{ ");
+ }
+ for( auto pChild : *pNode )
+ {
+ if(!pChild)
+ continue;
+ pChild->Accept( this );
+ Separate( );
+ }
+ if (bracketsNeeded) {
+ Append(u"} ");
+ }
+}
+
+void SmNodeToTextVisitor::Visit( SmPolyLineNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmRootNode* pNode )
+{
+ SmNode *pExtra = pNode->GetSubNode( 0 ),
+ *pBody = pNode->GetSubNode( 2 );
+ if( pExtra ) {
+ Append(u"nroot");
+ LineToText( pExtra );
+ } else
+ Append(u"sqrt");
+ LineToText( pBody );
+}
+
+void SmNodeToTextVisitor::Visit( SmRootSymbolNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmRectangleNode* )
+{
+}
+
+void SmNodeToTextVisitor::Visit( SmVerticalBraceNode* pNode )
+{
+ SmNode *pBody = pNode->Body(),
+ *pScript = pNode->Script();
+ LineToText( pBody );
+ Append( pNode->GetToken( ).aText );
+ LineToText( pScript );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/wordexportbase.cxx b/starmath/source/wordexportbase.cxx
new file mode 100644
index 000000000..adf1a275c
--- /dev/null
+++ b/starmath/source/wordexportbase.cxx
@@ -0,0 +1,184 @@
+/* -*- 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 "wordexportbase.hxx"
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+SmWordExportBase::SmWordExportBase(const SmNode* pIn)
+ : m_pTree(pIn)
+{
+}
+
+SmWordExportBase::~SmWordExportBase() = default;
+
+void SmWordExportBase::HandleNode(const SmNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase",
+ "Node: " << nLevel << " " << int(pNode->GetType()) << " " << pNode->GetNumSubNodes());
+ switch (pNode->GetType())
+ {
+ case SmNodeType::Attribute:
+ HandleAttribute(static_cast<const SmAttributeNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Text:
+ HandleText(pNode, nLevel);
+ break;
+ case SmNodeType::VerticalBrace:
+ HandleVerticalBrace(static_cast<const SmVerticalBraceNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Brace:
+ HandleBrace(static_cast<const SmBraceNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Oper:
+ HandleOperator(static_cast<const SmOperNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::UnHor:
+ HandleUnaryOperation(static_cast<const SmUnHorNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::BinHor:
+ HandleBinaryOperation(static_cast<const SmBinHorNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::BinVer:
+ HandleFractions(pNode, nLevel, nullptr);
+ break;
+ case SmNodeType::Root:
+ HandleRoot(static_cast<const SmRootNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Special:
+ {
+ auto pText = static_cast<const SmTextNode*>(pNode);
+ //if the token str and the result text are the same then this
+ //is to be seen as text, else assume it's a mathchar
+ if (pText->GetText() == pText->GetToken().aText)
+ HandleText(pText, nLevel);
+ else
+ HandleMath(pText, nLevel);
+ break;
+ }
+ case SmNodeType::Math:
+ case SmNodeType::MathIdent:
+ HandleMath(pNode, nLevel);
+ break;
+ case SmNodeType::SubSup:
+ HandleSubSupScript(static_cast<const SmSubSupNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Expression:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ case SmNodeType::Table:
+ //Root Node, PILE equivalent, i.e. vertical stack
+ HandleTable(pNode, nLevel);
+ break;
+ case SmNodeType::Matrix:
+ HandleMatrix(static_cast<const SmMatrixNode*>(pNode), nLevel);
+ break;
+ case SmNodeType::Line:
+ {
+ // TODO
+ HandleAllSubNodes(pNode, nLevel);
+ }
+ break;
+#if 0
+ case SmNodeType::Align:
+ HandleMAlign(pNode,nLevel);
+ break;
+#endif
+ case SmNodeType::Place:
+ // explicitly do nothing, MSOffice treats that as a placeholder if item is missing
+ break;
+ case SmNodeType::Blank:
+ HandleBlank();
+ break;
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+//Root Node, PILE equivalent, i.e. vertical stack
+void SmWordExportBase::HandleTable(const SmNode* pNode, int nLevel)
+{
+ //The root of the starmath is a table, if
+ //we convert this them each iteration of
+ //conversion from starmath to Word will
+ //add an extra unnecessary level to the
+ //Word output stack which would grow
+ //without bound in a multi step conversion
+ if (nLevel || pNode->GetNumSubNodes() > 1)
+ HandleVerticalStack(pNode, nLevel);
+ else
+ HandleAllSubNodes(pNode, nLevel);
+}
+
+void SmWordExportBase::HandleAllSubNodes(const SmNode* pNode, int nLevel)
+{
+ int size = pNode->GetNumSubNodes();
+ for (int i = 0; i < size; ++i)
+ {
+ // TODO remove when all types of nodes are handled properly
+ if (pNode->GetSubNode(i) == nullptr)
+ {
+ SAL_WARN("starmath.wordbase", "Subnode is NULL, parent node not handled properly");
+ continue;
+ }
+ HandleNode(pNode->GetSubNode(i), nLevel + 1);
+ }
+}
+
+void SmWordExportBase::HandleUnaryOperation(const SmUnHorNode* pNode, int nLevel)
+{
+ // update HandleMath() when adding new items
+ SAL_INFO("starmath.wordbase", "Unary: " << int(pNode->GetToken().eType));
+
+ HandleAllSubNodes(pNode, nLevel);
+}
+
+void SmWordExportBase::HandleBinaryOperation(const SmBinHorNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase", "Binary: " << int(pNode->Symbol()->GetToken().eType));
+ // update HandleMath() when adding new items
+ switch (pNode->Symbol()->GetToken().eType)
+ {
+ case TDIVIDEBY:
+ return HandleFractions(pNode, nLevel, "lin");
+ default:
+ HandleAllSubNodes(pNode, nLevel);
+ break;
+ }
+}
+
+void SmWordExportBase::HandleMath(const SmNode* pNode, int nLevel)
+{
+ SAL_INFO("starmath.wordbase", "Math: " << int(pNode->GetToken().eType));
+ switch (pNode->GetToken().eType)
+ {
+ case TDIVIDEBY:
+ case TACUTE:
+ OSL_ASSERT(false);
+ [[fallthrough]]; // the above are handled elsewhere, e.g. when handling BINHOR
+ default:
+ HandleText(pNode, nLevel);
+ break;
+ }
+}
+
+void SmWordExportBase::HandleSubSupScript(const SmSubSupNode* pNode, int nLevel)
+{
+ // set flags to a bitfield of which sub/sup items exists
+ int flags = (pNode->GetSubSup(CSUB) != nullptr ? (1 << CSUB) : 0)
+ | (pNode->GetSubSup(CSUP) != nullptr ? (1 << CSUP) : 0)
+ | (pNode->GetSubSup(RSUB) != nullptr ? (1 << RSUB) : 0)
+ | (pNode->GetSubSup(RSUP) != nullptr ? (1 << RSUP) : 0)
+ | (pNode->GetSubSup(LSUB) != nullptr ? (1 << LSUB) : 0)
+ | (pNode->GetSubSup(LSUP) != nullptr ? (1 << LSUP) : 0);
+ HandleSubSupScriptInternal(pNode, nLevel, flags);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/starmath/source/wordexportbase.hxx b/starmath/source/wordexportbase.hxx
new file mode 100644
index 000000000..33a179d05
--- /dev/null
+++ b/starmath/source/wordexportbase.hxx
@@ -0,0 +1,48 @@
+/* -*- 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 <node.hxx>
+
+/**
+ Base class implementing writing of formulas to Word.
+ */
+class SmWordExportBase
+{
+public:
+ explicit SmWordExportBase(const SmNode* pIn);
+ virtual ~SmWordExportBase();
+
+protected:
+ void HandleNode(const SmNode* pNode, int nLevel);
+ void HandleAllSubNodes(const SmNode* pNode, int nLevel);
+ void HandleTable(const SmNode* pNode, int nLevel);
+ virtual void HandleVerticalStack(const SmNode* pNode, int nLevel) = 0;
+ virtual void HandleText(const SmNode* pNode, int nLevel) = 0;
+ void HandleMath(const SmNode* pNode, int nLevel);
+ virtual void HandleFractions(const SmNode* pNode, int nLevel, const char* type) = 0;
+ void HandleUnaryOperation(const SmUnHorNode* pNode, int nLevel);
+ void HandleBinaryOperation(const SmBinHorNode* pNode, int nLevel);
+ virtual void HandleRoot(const SmRootNode* pNode, int nLevel) = 0;
+ virtual void HandleAttribute(const SmAttributeNode* pNode, int nLevel) = 0;
+ virtual void HandleOperator(const SmOperNode* pNode, int nLevel) = 0;
+ void HandleSubSupScript(const SmSubSupNode* pNode, int nLevel);
+ virtual void HandleSubSupScriptInternal(const SmSubSupNode* pNode, int nLevel, int flags) = 0;
+ virtual void HandleMatrix(const SmMatrixNode* pNode, int nLevel) = 0;
+ virtual void HandleBrace(const SmBraceNode* pNode, int nLevel) = 0;
+ virtual void HandleVerticalBrace(const SmVerticalBraceNode* pNode, int nLevel) = 0;
+ virtual void HandleBlank() = 0;
+ const SmNode* GetTree() const { return m_pTree; }
+
+private:
+ const SmNode* const m_pTree;
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */